pax_global_header00006660000000000000000000000064144473403310014515gustar00rootroot0000000000000052 comment=5b212be71f6f1e0f890431a0f55f3bb773f1e28e .gitattributes000066400000000000000000000000361444734033100137530ustar00rootroot00000000000000*.hc linguist-language=HexenC README.txt000066400000000000000000000001771444734033100125640ustar00rootroot00000000000000Hexen II: Hammer of Thyrion (uHexen2) source code. http://uhexen2.sourceforge.net/ http://sourceforge.net/projects/uhexen2/ common/000077500000000000000000000000001444734033100123515ustar00rootroot00000000000000common/adivtab.h000066400000000000000000000410321444734033100141340ustar00rootroot00000000000000/* * adivtab.h * * table of quotients and remainders * for [-15...16] / [-15...16] * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* numerator = -15 */ { 1, 0 }, { 1, -1 }, { 1, -2 }, { 1, -3 }, { 1, -4 }, { 1, -5 }, { 1, -6 }, { 1, -7 }, { 2, -1 }, { 2, -3 }, { 3, 0 }, { 3, -3 }, { 5, 0 }, { 7, -1 }, { 15, 0 }, { 0, 0 }, { -15, 0 }, { -8, 1 }, { -5, 0 }, { -4, 1 }, { -3, 0 }, { -3, 3 }, { -3, 6 }, { -2, 1 }, { -2, 3 }, { -2, 5 }, { -2, 7 }, { -2, 9 }, { -2, 11 }, { -2, 13 }, { -1, 0 }, { -1, 1 }, /* numerator = -14 */ { 0, -14 }, { 1, 0 }, { 1, -1 }, { 1, -2 }, { 1, -3 }, { 1, -4 }, { 1, -5 }, { 1, -6 }, { 2, 0 }, { 2, -2 }, { 2, -4 }, { 3, -2 }, { 4, -2 }, { 7, 0 }, { 14, 0 }, { 0, 0 }, { -14, 0 }, { -7, 0 }, { -5, 1 }, { -4, 2 }, { -3, 1 }, { -3, 4 }, { -2, 0 }, { -2, 2 }, { -2, 4 }, { -2, 6 }, { -2, 8 }, { -2, 10 }, { -2, 12 }, { -1, 0 }, { -1, 1 }, { -1, 2 }, /* numerator = -13 */ { 0, -13 }, { 0, -13 }, { 1, 0 }, { 1, -1 }, { 1, -2 }, { 1, -3 }, { 1, -4 }, { 1, -5 }, { 1, -6 }, { 2, -1 }, { 2, -3 }, { 3, -1 }, { 4, -1 }, { 6, -1 }, { 13, 0 }, { 0, 0 }, { -13, 0 }, { -7, 1 }, { -5, 2 }, { -4, 3 }, { -3, 2 }, { -3, 5 }, { -2, 1 }, { -2, 3 }, { -2, 5 }, { -2, 7 }, { -2, 9 }, { -2, 11 }, { -1, 0 }, { -1, 1 }, { -1, 2 }, { -1, 3 }, /* numerator = -12 */ { 0, -12 }, { 0, -12 }, { 0, -12 }, { 1, 0 }, { 1, -1 }, { 1, -2 }, { 1, -3 }, { 1, -4 }, { 1, -5 }, { 2, 0 }, { 2, -2 }, { 3, 0 }, { 4, 0 }, { 6, 0 }, { 12, 0 }, { 0, 0 }, { -12, 0 }, { -6, 0 }, { -4, 0 }, { -3, 0 }, { -3, 3 }, { -2, 0 }, { -2, 2 }, { -2, 4 }, { -2, 6 }, { -2, 8 }, { -2, 10 }, { -1, 0 }, { -1, 1 }, { -1, 2 }, { -1, 3 }, { -1, 4 }, /* numerator = -11 */ { 0, -11 }, { 0, -11 }, { 0, -11 }, { 0, -11 }, { 1, 0 }, { 1, -1 }, { 1, -2 }, { 1, -3 }, { 1, -4 }, { 1, -5 }, { 2, -1 }, { 2, -3 }, { 3, -2 }, { 5, -1 }, { 11, 0 }, { 0, 0 }, { -11, 0 }, { -6, 1 }, { -4, 1 }, { -3, 1 }, { -3, 4 }, { -2, 1 }, { -2, 3 }, { -2, 5 }, { -2, 7 }, { -2, 9 }, { -1, 0 }, { -1, 1 }, { -1, 2 }, { -1, 3 }, { -1, 4 }, { -1, 5 }, /* numerator = -10 */ { 0, -10 }, { 0, -10 }, { 0, -10 }, { 0, -10 }, { 0, -10 }, { 1, 0 }, { 1, -1 }, { 1, -2 }, { 1, -3 }, { 1, -4 }, { 2, 0 }, { 2, -2 }, { 3, -1 }, { 5, 0 }, { 10, 0 }, { 0, 0 }, { -10, 0 }, { -5, 0 }, { -4, 2 }, { -3, 2 }, { -2, 0 }, { -2, 2 }, { -2, 4 }, { -2, 6 }, { -2, 8 }, { -1, 0 }, { -1, 1 }, { -1, 2 }, { -1, 3 }, { -1, 4 }, { -1, 5 }, { -1, 6 }, /* numerator = -9 */ { 0, -9 }, { 0, -9 }, { 0, -9 }, { 0, -9 }, { 0, -9 }, { 0, -9 }, { 1, 0 }, { 1, -1 }, { 1, -2 }, { 1, -3 }, { 1, -4 }, { 2, -1 }, { 3, 0 }, { 4, -1 }, { 9, 0 }, { 0, 0 }, { -9, 0 }, { -5, 1 }, { -3, 0 }, { -3, 3 }, { -2, 1 }, { -2, 3 }, { -2, 5 }, { -2, 7 }, { -1, 0 }, { -1, 1 }, { -1, 2 }, { -1, 3 }, { -1, 4 }, { -1, 5 }, { -1, 6 }, { -1, 7 }, /* numerator = -8 */ { 0, -8 }, { 0, -8 }, { 0, -8 }, { 0, -8 }, { 0, -8 }, { 0, -8 }, { 0, -8 }, { 1, 0 }, { 1, -1 }, { 1, -2 }, { 1, -3 }, { 2, 0 }, { 2, -2 }, { 4, 0 }, { 8, 0 }, { 0, 0 }, { -8, 0 }, { -4, 0 }, { -3, 1 }, { -2, 0 }, { -2, 2 }, { -2, 4 }, { -2, 6 }, { -1, 0 }, { -1, 1 }, { -1, 2 }, { -1, 3 }, { -1, 4 }, { -1, 5 }, { -1, 6 }, { -1, 7 }, { -1, 8 }, /* numerator = -7 */ { 0, -7 }, { 0, -7 }, { 0, -7 }, { 0, -7 }, { 0, -7 }, { 0, -7 }, { 0, -7 }, { 0, -7 }, { 1, 0 }, { 1, -1 }, { 1, -2 }, { 1, -3 }, { 2, -1 }, { 3, -1 }, { 7, 0 }, { 0, 0 }, { -7, 0 }, { -4, 1 }, { -3, 2 }, { -2, 1 }, { -2, 3 }, { -2, 5 }, { -1, 0 }, { -1, 1 }, { -1, 2 }, { -1, 3 }, { -1, 4 }, { -1, 5 }, { -1, 6 }, { -1, 7 }, { -1, 8 }, { -1, 9 }, /* numerator = -6 */ { 0, -6 }, { 0, -6 }, { 0, -6 }, { 0, -6 }, { 0, -6 }, { 0, -6 }, { 0, -6 }, { 0, -6 }, { 0, -6 }, { 1, 0 }, { 1, -1 }, { 1, -2 }, { 2, 0 }, { 3, 0 }, { 6, 0 }, { 0, 0 }, { -6, 0 }, { -3, 0 }, { -2, 0 }, { -2, 2 }, { -2, 4 }, { -1, 0 }, { -1, 1 }, { -1, 2 }, { -1, 3 }, { -1, 4 }, { -1, 5 }, { -1, 6 }, { -1, 7 }, { -1, 8 }, { -1, 9 }, { -1, 10 }, /* numerator = -5 */ { 0, -5 }, { 0, -5 }, { 0, -5 }, { 0, -5 }, { 0, -5 }, { 0, -5 }, { 0, -5 }, { 0, -5 }, { 0, -5 }, { 0, -5 }, { 1, 0 }, { 1, -1 }, { 1, -2 }, { 2, -1 }, { 5, 0 }, { 0, 0 }, { -5, 0 }, { -3, 1 }, { -2, 1 }, { -2, 3 }, { -1, 0 }, { -1, 1 }, { -1, 2 }, { -1, 3 }, { -1, 4 }, { -1, 5 }, { -1, 6 }, { -1, 7 }, { -1, 8 }, { -1, 9 }, { -1, 10 }, { -1, 11 }, /* numerator = -4 */ { 0, -4 }, { 0, -4 }, { 0, -4 }, { 0, -4 }, { 0, -4 }, { 0, -4 }, { 0, -4 }, { 0, -4 }, { 0, -4 }, { 0, -4 }, { 0, -4 }, { 1, 0 }, { 1, -1 }, { 2, 0 }, { 4, 0 }, { 0, 0 }, { -4, 0 }, { -2, 0 }, { -2, 2 }, { -1, 0 }, { -1, 1 }, { -1, 2 }, { -1, 3 }, { -1, 4 }, { -1, 5 }, { -1, 6 }, { -1, 7 }, { -1, 8 }, { -1, 9 }, { -1, 10 }, { -1, 11 }, { -1, 12 }, /* numerator = -3 */ { 0, -3 }, { 0, -3 }, { 0, -3 }, { 0, -3 }, { 0, -3 }, { 0, -3 }, { 0, -3 }, { 0, -3 }, { 0, -3 }, { 0, -3 }, { 0, -3 }, { 0, -3 }, { 1, 0 }, { 1, -1 }, { 3, 0 }, { 0, 0 }, { -3, 0 }, { -2, 1 }, { -1, 0 }, { -1, 1 }, { -1, 2 }, { -1, 3 }, { -1, 4 }, { -1, 5 }, { -1, 6 }, { -1, 7 }, { -1, 8 }, { -1, 9 }, { -1, 10 }, { -1, 11 }, { -1, 12 }, { -1, 13 }, /* numerator = -2 */ { 0, -2 }, { 0, -2 }, { 0, -2 }, { 0, -2 }, { 0, -2 }, { 0, -2 }, { 0, -2 }, { 0, -2 }, { 0, -2 }, { 0, -2 }, { 0, -2 }, { 0, -2 }, { 0, -2 }, { 1, 0 }, { 2, 0 }, { 0, 0 }, { -2, 0 }, { -1, 0 }, { -1, 1 }, { -1, 2 }, { -1, 3 }, { -1, 4 }, { -1, 5 }, { -1, 6 }, { -1, 7 }, { -1, 8 }, { -1, 9 }, { -1, 10 }, { -1, 11 }, { -1, 12 }, { -1, 13 }, { -1, 14 }, /* numerator = -1 */ { 0, -1 }, { 0, -1 }, { 0, -1 }, { 0, -1 }, { 0, -1 }, { 0, -1 }, { 0, -1 }, { 0, -1 }, { 0, -1 }, { 0, -1 }, { 0, -1 }, { 0, -1 }, { 0, -1 }, { 0, -1 }, { 1, 0 }, { 0, 0 }, { -1, 0 }, { -1, 1 }, { -1, 2 }, { -1, 3 }, { -1, 4 }, { -1, 5 }, { -1, 6 }, { -1, 7 }, { -1, 8 }, { -1, 9 }, { -1, 10 }, { -1, 11 }, { -1, 12 }, { -1, 13 }, { -1, 14 }, { -1, 15 }, /* numerator = 0 */ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, /* numerator = 1 */ { -1, -14 }, { -1, -13 }, { -1, -12 }, { -1, -11 }, { -1, -10 }, { -1, -9 }, { -1, -8 }, { -1, -7 }, { -1, -6 }, { -1, -5 }, { -1, -4 }, { -1, -3 }, { -1, -2 }, { -1, -1 }, { -1, 0 }, { 0, 0 }, { 1, 0 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, /* numerator = 2 */ { -1, -13 }, { -1, -12 }, { -1, -11 }, { -1, -10 }, { -1, -9 }, { -1, -8 }, { -1, -7 }, { -1, -6 }, { -1, -5 }, { -1, -4 }, { -1, -3 }, { -1, -2 }, { -1, -1 }, { -1, 0 }, { -2, 0 }, { 0, 0 }, { 2, 0 }, { 1, 0 }, { 0, 2 }, { 0, 2 }, { 0, 2 }, { 0, 2 }, { 0, 2 }, { 0, 2 }, { 0, 2 }, { 0, 2 }, { 0, 2 }, { 0, 2 }, { 0, 2 }, { 0, 2 }, { 0, 2 }, { 0, 2 }, /* numerator = 3 */ { -1, -12 }, { -1, -11 }, { -1, -10 }, { -1, -9 }, { -1, -8 }, { -1, -7 }, { -1, -6 }, { -1, -5 }, { -1, -4 }, { -1, -3 }, { -1, -2 }, { -1, -1 }, { -1, 0 }, { -2, -1 }, { -3, 0 }, { 0, 0 }, { 3, 0 }, { 1, 1 }, { 1, 0 }, { 0, 3 }, { 0, 3 }, { 0, 3 }, { 0, 3 }, { 0, 3 }, { 0, 3 }, { 0, 3 }, { 0, 3 }, { 0, 3 }, { 0, 3 }, { 0, 3 }, { 0, 3 }, { 0, 3 }, /* numerator = 4 */ { -1, -11 }, { -1, -10 }, { -1, -9 }, { -1, -8 }, { -1, -7 }, { -1, -6 }, { -1, -5 }, { -1, -4 }, { -1, -3 }, { -1, -2 }, { -1, -1 }, { -1, 0 }, { -2, -2 }, { -2, 0 }, { -4, 0 }, { 0, 0 }, { 4, 0 }, { 2, 0 }, { 1, 1 }, { 1, 0 }, { 0, 4 }, { 0, 4 }, { 0, 4 }, { 0, 4 }, { 0, 4 }, { 0, 4 }, { 0, 4 }, { 0, 4 }, { 0, 4 }, { 0, 4 }, { 0, 4 }, { 0, 4 }, /* numerator = 5 */ { -1, -10 }, { -1, -9 }, { -1, -8 }, { -1, -7 }, { -1, -6 }, { -1, -5 }, { -1, -4 }, { -1, -3 }, { -1, -2 }, { -1, -1 }, { -1, 0 }, { -2, -3 }, { -2, -1 }, { -3, -1 }, { -5, 0 }, { 0, 0 }, { 5, 0 }, { 2, 1 }, { 1, 2 }, { 1, 1 }, { 1, 0 }, { 0, 5 }, { 0, 5 }, { 0, 5 }, { 0, 5 }, { 0, 5 }, { 0, 5 }, { 0, 5 }, { 0, 5 }, { 0, 5 }, { 0, 5 }, { 0, 5 }, /* numerator = 6 */ { -1, -9 }, { -1, -8 }, { -1, -7 }, { -1, -6 }, { -1, -5 }, { -1, -4 }, { -1, -3 }, { -1, -2 }, { -1, -1 }, { -1, 0 }, { -2, -4 }, { -2, -2 }, { -2, 0 }, { -3, 0 }, { -6, 0 }, { 0, 0 }, { 6, 0 }, { 3, 0 }, { 2, 0 }, { 1, 2 }, { 1, 1 }, { 1, 0 }, { 0, 6 }, { 0, 6 }, { 0, 6 }, { 0, 6 }, { 0, 6 }, { 0, 6 }, { 0, 6 }, { 0, 6 }, { 0, 6 }, { 0, 6 }, /* numerator = 7 */ { -1, -8 }, { -1, -7 }, { -1, -6 }, { -1, -5 }, { -1, -4 }, { -1, -3 }, { -1, -2 }, { -1, -1 }, { -1, 0 }, { -2, -5 }, { -2, -3 }, { -2, -1 }, { -3, -2 }, { -4, -1 }, { -7, 0 }, { 0, 0 }, { 7, 0 }, { 3, 1 }, { 2, 1 }, { 1, 3 }, { 1, 2 }, { 1, 1 }, { 1, 0 }, { 0, 7 }, { 0, 7 }, { 0, 7 }, { 0, 7 }, { 0, 7 }, { 0, 7 }, { 0, 7 }, { 0, 7 }, { 0, 7 }, /* numerator = 8 */ { -1, -7 }, { -1, -6 }, { -1, -5 }, { -1, -4 }, { -1, -3 }, { -1, -2 }, { -1, -1 }, { -1, 0 }, { -2, -6 }, { -2, -4 }, { -2, -2 }, { -2, 0 }, { -3, -1 }, { -4, 0 }, { -8, 0 }, { 0, 0 }, { 8, 0 }, { 4, 0 }, { 2, 2 }, { 2, 0 }, { 1, 3 }, { 1, 2 }, { 1, 1 }, { 1, 0 }, { 0, 8 }, { 0, 8 }, { 0, 8 }, { 0, 8 }, { 0, 8 }, { 0, 8 }, { 0, 8 }, { 0, 8 }, /* numerator = 9 */ { -1, -6 }, { -1, -5 }, { -1, -4 }, { -1, -3 }, { -1, -2 }, { -1, -1 }, { -1, 0 }, { -2, -7 }, { -2, -5 }, { -2, -3 }, { -2, -1 }, { -3, -3 }, { -3, 0 }, { -5, -1 }, { -9, 0 }, { 0, 0 }, { 9, 0 }, { 4, 1 }, { 3, 0 }, { 2, 1 }, { 1, 4 }, { 1, 3 }, { 1, 2 }, { 1, 1 }, { 1, 0 }, { 0, 9 }, { 0, 9 }, { 0, 9 }, { 0, 9 }, { 0, 9 }, { 0, 9 }, { 0, 9 }, /* numerator = 10 */ { -1, -5 }, { -1, -4 }, { -1, -3 }, { -1, -2 }, { -1, -1 }, { -1, 0 }, { -2, -8 }, { -2, -6 }, { -2, -4 }, { -2, -2 }, { -2, 0 }, { -3, -2 }, { -4, -2 }, { -5, 0 }, { -10, 0 }, { 0, 0 }, { 10, 0 }, { 5, 0 }, { 3, 1 }, { 2, 2 }, { 2, 0 }, { 1, 4 }, { 1, 3 }, { 1, 2 }, { 1, 1 }, { 1, 0 }, { 0, 10 }, { 0, 10 }, { 0, 10 }, { 0, 10 }, { 0, 10 }, { 0, 10 }, /* numerator = 11 */ { -1, -4 }, { -1, -3 }, { -1, -2 }, { -1, -1 }, { -1, 0 }, { -2, -9 }, { -2, -7 }, { -2, -5 }, { -2, -3 }, { -2, -1 }, { -3, -4 }, { -3, -1 }, { -4, -1 }, { -6, -1 }, { -11, 0 }, { 0, 0 }, { 11, 0 }, { 5, 1 }, { 3, 2 }, { 2, 3 }, { 2, 1 }, { 1, 5 }, { 1, 4 }, { 1, 3 }, { 1, 2 }, { 1, 1 }, { 1, 0 }, { 0, 11 }, { 0, 11 }, { 0, 11 }, { 0, 11 }, { 0, 11 }, /* numerator = 12 */ { -1, -3 }, { -1, -2 }, { -1, -1 }, { -1, 0 }, { -2, -10 }, { -2, -8 }, { -2, -6 }, { -2, -4 }, { -2, -2 }, { -2, 0 }, { -3, -3 }, { -3, 0 }, { -4, 0 }, { -6, 0 }, { -12, 0 }, { 0, 0 }, { 12, 0 }, { 6, 0 }, { 4, 0 }, { 3, 0 }, { 2, 2 }, { 2, 0 }, { 1, 5 }, { 1, 4 }, { 1, 3 }, { 1, 2 }, { 1, 1 }, { 1, 0 }, { 0, 12 }, { 0, 12 }, { 0, 12 }, { 0, 12 }, /* numerator = 13 */ { -1, -2 }, { -1, -1 }, { -1, 0 }, { -2, -11 }, { -2, -9 }, { -2, -7 }, { -2, -5 }, { -2, -3 }, { -2, -1 }, { -3, -5 }, { -3, -2 }, { -4, -3 }, { -5, -2 }, { -7, -1 }, { -13, 0 }, { 0, 0 }, { 13, 0 }, { 6, 1 }, { 4, 1 }, { 3, 1 }, { 2, 3 }, { 2, 1 }, { 1, 6 }, { 1, 5 }, { 1, 4 }, { 1, 3 }, { 1, 2 }, { 1, 1 }, { 1, 0 }, { 0, 13 }, { 0, 13 }, { 0, 13 }, /* numerator = 14 */ { -1, -1 }, { -1, 0 }, { -2, -12 }, { -2, -10 }, { -2, -8 }, { -2, -6 }, { -2, -4 }, { -2, -2 }, { -2, 0 }, { -3, -4 }, { -3, -1 }, { -4, -2 }, { -5, -1 }, { -7, 0 }, { -14, 0 }, { 0, 0 }, { 14, 0 }, { 7, 0 }, { 4, 2 }, { 3, 2 }, { 2, 4 }, { 2, 2 }, { 2, 0 }, { 1, 6 }, { 1, 5 }, { 1, 4 }, { 1, 3 }, { 1, 2 }, { 1, 1 }, { 1, 0 }, { 0, 14 }, { 0, 14 }, /* numerator = 15 */ { -1, 0 }, { -2, -13 }, { -2, -11 }, { -2, -9 }, { -2, -7 }, { -2, -5 }, { -2, -3 }, { -2, -1 }, { -3, -6 }, { -3, -3 }, { -3, 0 }, { -4, -1 }, { -5, 0 }, { -8, -1 }, { -15, 0 }, { 0, 0 }, { 15, 0 }, { 7, 1 }, { 5, 0 }, { 3, 3 }, { 3, 0 }, { 2, 3 }, { 2, 1 }, { 1, 7 }, { 1, 6 }, { 1, 5 }, { 1, 4 }, { 1, 3 }, { 1, 2 }, { 1, 1 }, { 1, 0 }, { 0, 15 }, /* numerator = 16 */ { -2, -14 }, { -2, -12 }, { -2, -10 }, { -2, -8 }, { -2, -6 }, { -2, -4 }, { -2, -2 }, { -2, 0 }, { -3, -5 }, { -3, -2 }, { -4, -4 }, { -4, 0 }, { -6, -2 }, { -8, 0 }, { -16, 0 }, { 0, 0 }, { 16, 0 }, { 8, 0 }, { 5, 1 }, { 4, 0 }, { 3, 1 }, { 2, 4 }, { 2, 2 }, { 2, 0 }, { 1, 7 }, { 1, 6 }, { 1, 5 }, { 1, 4 }, { 1, 3 }, { 1, 2 }, { 1, 1 }, { 1, 0 }, common/anorms.h000066400000000000000000000154431444734033100140300ustar00rootroot00000000000000/* * anorms.h * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ { -0.525731, 0.000000, 0.850651 }, { -0.442863, 0.238856, 0.864188 }, { -0.295242, 0.000000, 0.955423 }, { -0.309017, 0.500000, 0.809017 }, { -0.162460, 0.262866, 0.951056 }, { 0.000000, 0.000000, 1.000000 }, { 0.000000, 0.850651, 0.525731 }, { -0.147621, 0.716567, 0.681718 }, { 0.147621, 0.716567, 0.681718 }, { 0.000000, 0.525731, 0.850651 }, { 0.309017, 0.500000, 0.809017 }, { 0.525731, 0.000000, 0.850651 }, { 0.295242, 0.000000, 0.955423 }, { 0.442863, 0.238856, 0.864188 }, { 0.162460, 0.262866, 0.951056 }, { -0.681718, 0.147621, 0.716567 }, { -0.809017, 0.309017, 0.500000 }, { -0.587785, 0.425325, 0.688191 }, { -0.850651, 0.525731, 0.000000 }, { -0.864188, 0.442863, 0.238856 }, { -0.716567, 0.681718, 0.147621 }, { -0.688191, 0.587785, 0.425325 }, { -0.500000, 0.809017, 0.309017 }, { -0.238856, 0.864188, 0.442863 }, { -0.425325, 0.688191, 0.587785 }, { -0.716567, 0.681718, -0.147621 }, { -0.500000, 0.809017, -0.309017 }, { -0.525731, 0.850651, 0.000000 }, { 0.000000, 0.850651, -0.525731 }, { -0.238856, 0.864188, -0.442863 }, { 0.000000, 0.955423, -0.295242 }, { -0.262866, 0.951056, -0.162460 }, { 0.000000, 1.000000, 0.000000 }, { 0.000000, 0.955423, 0.295242 }, { -0.262866, 0.951056, 0.162460 }, { 0.238856, 0.864188, 0.442863 }, { 0.262866, 0.951056, 0.162460 }, { 0.500000, 0.809017, 0.309017 }, { 0.238856, 0.864188, -0.442863 }, { 0.262866, 0.951056, -0.162460 }, { 0.500000, 0.809017, -0.309017 }, { 0.850651, 0.525731, 0.000000 }, { 0.716567, 0.681718, 0.147621 }, { 0.716567, 0.681718, -0.147621 }, { 0.525731, 0.850651, 0.000000 }, { 0.425325, 0.688191, 0.587785 }, { 0.864188, 0.442863, 0.238856 }, { 0.688191, 0.587785, 0.425325 }, { 0.809017, 0.309017, 0.500000 }, { 0.681718, 0.147621, 0.716567 }, { 0.587785, 0.425325, 0.688191 }, { 0.955423, 0.295242, 0.000000 }, { 1.000000, 0.000000, 0.000000 }, { 0.951056, 0.162460, 0.262866 }, { 0.850651, -0.525731, 0.000000 }, { 0.955423, -0.295242, 0.000000 }, { 0.864188, -0.442863, 0.238856 }, { 0.951056, -0.162460, 0.262866 }, { 0.809017, -0.309017, 0.500000 }, { 0.681718, -0.147621, 0.716567 }, { 0.850651, 0.000000, 0.525731 }, { 0.864188, 0.442863, -0.238856 }, { 0.809017, 0.309017, -0.500000 }, { 0.951056, 0.162460, -0.262866 }, { 0.525731, 0.000000, -0.850651 }, { 0.681718, 0.147621, -0.716567 }, { 0.681718, -0.147621, -0.716567 }, { 0.850651, 0.000000, -0.525731 }, { 0.809017, -0.309017, -0.500000 }, { 0.864188, -0.442863, -0.238856 }, { 0.951056, -0.162460, -0.262866 }, { 0.147621, 0.716567, -0.681718 }, { 0.309017, 0.500000, -0.809017 }, { 0.425325, 0.688191, -0.587785 }, { 0.442863, 0.238856, -0.864188 }, { 0.587785, 0.425325, -0.688191 }, { 0.688191, 0.587785, -0.425325 }, { -0.147621, 0.716567, -0.681718 }, { -0.309017, 0.500000, -0.809017 }, { 0.000000, 0.525731, -0.850651 }, { -0.525731, 0.000000, -0.850651 }, { -0.442863, 0.238856, -0.864188 }, { -0.295242, 0.000000, -0.955423 }, { -0.162460, 0.262866, -0.951056 }, { 0.000000, 0.000000, -1.000000 }, { 0.295242, 0.000000, -0.955423 }, { 0.162460, 0.262866, -0.951056 }, { -0.442863, -0.238856, -0.864188 }, { -0.309017, -0.500000, -0.809017 }, { -0.162460, -0.262866, -0.951056 }, { 0.000000, -0.850651, -0.525731 }, { -0.147621, -0.716567, -0.681718 }, { 0.147621, -0.716567, -0.681718 }, { 0.000000, -0.525731, -0.850651 }, { 0.309017, -0.500000, -0.809017 }, { 0.442863, -0.238856, -0.864188 }, { 0.162460, -0.262866, -0.951056 }, { 0.238856, -0.864188, -0.442863 }, { 0.500000, -0.809017, -0.309017 }, { 0.425325, -0.688191, -0.587785 }, { 0.716567, -0.681718, -0.147621 }, { 0.688191, -0.587785, -0.425325 }, { 0.587785, -0.425325, -0.688191 }, { 0.000000, -0.955423, -0.295242 }, { 0.000000, -1.000000, 0.000000 }, { 0.262866, -0.951056, -0.162460 }, { 0.000000, -0.850651, 0.525731 }, { 0.000000, -0.955423, 0.295242 }, { 0.238856, -0.864188, 0.442863 }, { 0.262866, -0.951056, 0.162460 }, { 0.500000, -0.809017, 0.309017 }, { 0.716567, -0.681718, 0.147621 }, { 0.525731, -0.850651, 0.000000 }, { -0.238856, -0.864188, -0.442863 }, { -0.500000, -0.809017, -0.309017 }, { -0.262866, -0.951056, -0.162460 }, { -0.850651, -0.525731, 0.000000 }, { -0.716567, -0.681718, -0.147621 }, { -0.716567, -0.681718, 0.147621 }, { -0.525731, -0.850651, 0.000000 }, { -0.500000, -0.809017, 0.309017 }, { -0.238856, -0.864188, 0.442863 }, { -0.262866, -0.951056, 0.162460 }, { -0.864188, -0.442863, 0.238856 }, { -0.809017, -0.309017, 0.500000 }, { -0.688191, -0.587785, 0.425325 }, { -0.681718, -0.147621, 0.716567 }, { -0.442863, -0.238856, 0.864188 }, { -0.587785, -0.425325, 0.688191 }, { -0.309017, -0.500000, 0.809017 }, { -0.147621, -0.716567, 0.681718 }, { -0.425325, -0.688191, 0.587785 }, { -0.162460, -0.262866, 0.951056 }, { 0.442863, -0.238856, 0.864188 }, { 0.162460, -0.262866, 0.951056 }, { 0.309017, -0.500000, 0.809017 }, { 0.147621, -0.716567, 0.681718 }, { 0.000000, -0.525731, 0.850651 }, { 0.425325, -0.688191, 0.587785 }, { 0.587785, -0.425325, 0.688191 }, { 0.688191, -0.587785, 0.425325 }, { -0.955423, 0.295242, 0.000000 }, { -0.951056, 0.162460, 0.262866 }, { -1.000000, 0.000000, 0.000000 }, { -0.850651, 0.000000, 0.525731 }, { -0.955423, -0.295242, 0.000000 }, { -0.951056, -0.162460, 0.262866 }, { -0.864188, 0.442863, -0.238856 }, { -0.951056, 0.162460, -0.262866 }, { -0.809017, 0.309017, -0.500000 }, { -0.864188, -0.442863, -0.238856 }, { -0.951056, -0.162460, -0.262866 }, { -0.809017, -0.309017, -0.500000 }, { -0.681718, 0.147621, -0.716567 }, { -0.681718, -0.147621, -0.716567 }, { -0.850651, 0.000000, -0.525731 }, { -0.688191, 0.587785, -0.425325 }, { -0.587785, 0.425325, -0.688191 }, { -0.425325, 0.688191, -0.587785 }, { -0.425325, -0.688191, -0.587785 }, { -0.587785, -0.425325, -0.688191 }, { -0.688191, -0.587785, -0.425325 }, common/arch_def.h000066400000000000000000000123271444734033100142620ustar00rootroot00000000000000/* * arch_def.h * platform specific definitions * - standalone header * - doesn't and must not include any other headers * - shouldn't depend on compiler.h, q_stdinc.h, or * any other headers * * Copyright (C) 2007-2016 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ARCHDEFS_H #define ARCHDEFS_H #if defined(__DJGPP__) || defined(__MSDOS__) || defined(__DOS__) || defined(_MSDOS) # if !defined(PLATFORM_DOS) # define PLATFORM_DOS 1 # endif #elif defined(__OS2__) || defined(__EMX__) # if !defined(PLATFORM_OS2) # define PLATFORM_OS2 1 # endif #elif defined(_WIN32) || defined(__WIN32__) || defined(_WIN64) || defined(__NT__) || defined(_Windows) # if !defined(PLATFORM_WINDOWS) # define PLATFORM_WINDOWS 1 # endif #elif defined(__HAIKU__) # if !defined(PLATFORM_HAIKU) # define PLATFORM_HAIKU 1 # endif #elif defined(__APPLE__) && defined(__MACH__) /* Mac OS X */ # if !defined(PLATFORM_OSX) # define PLATFORM_OSX 1 # endif #elif defined(macintosh) /* Mac OS classic */ # if !defined(PLATFORM_MAC) # define PLATFORM_MAC 1 # endif #elif defined(__MORPHOS__) || defined(__AROS__) || defined(AMIGAOS) || \ defined(__amigaos__) || defined(__amigaos4__) ||defined(__amigados__) || \ defined(AMIGA) || defined(_AMIGA) || defined(__AMIGA__) # if !defined(PLATFORM_AMIGA) # define PLATFORM_AMIGA 1 # endif #elif defined(__riscos__) # if !defined(PLATFORM_RISCOS) # define PLATFORM_RISCOS 1 # endif #else /* here goes the unix platforms */ #if defined(__unix) || defined(__unix__) || defined(unix) || \ defined(__linux__) || defined(__linux) || \ defined(__FreeBSD__) || defined(__DragonFly__) || \ defined(__FreeBSD_kernel__) /* Debian GNU/kFreeBSD */ || \ defined(__OpenBSD__) || defined(__NetBSD__) || \ defined(__hpux) || defined(__hpux__) || defined(_hpux) || \ defined(__sun) || defined(sun) || \ defined(__sgi) || defined(sgi) || defined(__sgi__) || \ defined(__GNU__) /* GNU/Hurd */ || \ defined(__QNX__) || defined(__QNXNTO__) || defined(__HAIKU__) # if !defined(PLATFORM_UNIX) # define PLATFORM_UNIX 1 # endif #endif #endif /* PLATFORM_xxx */ #if defined (PLATFORM_OSX) /* OS X is unix-based */ # if !defined(PLATFORM_UNIX) # define PLATFORM_UNIX 2 # endif #endif /* OS X -> PLATFORM_UNIX */ #if defined(__FreeBSD__) || defined(__DragonFly__) || \ defined(__FreeBSD_kernel__) /* Debian GNU/kFreeBSD */ || \ defined(__OpenBSD__) || defined(__NetBSD__) # if !defined(PLATFORM_BSD) # define PLATFORM_BSD 1 # endif #endif /* PLATFORM_BSD (for convenience) */ #if defined(PLATFORM_AMIGA) && !defined(PLATFORM_AMIGAOS3) # if !defined(__MORPHOS__) && !defined(__AROS__) && !defined(__amigaos4__) # define PLATFORM_AMIGAOS3 1 # endif #endif /* PLATFORM_AMIGAOS3 (for convenience) */ #if defined(_WIN64) # define PLATFORM_STRING "Win64" #elif defined(PLATFORM_WINDOWS) # define PLATFORM_STRING "Windows" #elif defined(PLATFORM_DOS) # define PLATFORM_STRING "DOS" #elif defined(PLATFORM_OS2) # define PLATFORM_STRING "OS/2" #elif defined(__linux__) || defined(__linux) # define PLATFORM_STRING "Linux" #elif defined(__DragonFly__) # define PLATFORM_STRING "DragonFly" #elif defined(__FreeBSD__) # define PLATFORM_STRING "FreeBSD" #elif defined(__NetBSD__) # define PLATFORM_STRING "NetBSD" #elif defined(__OpenBSD__) # define PLATFORM_STRING "OpenBSD" #elif defined(__MORPHOS__) # define PLATFORM_STRING "MorphOS" #elif defined(__AROS__) # define PLATFORM_STRING "AROS" #elif defined(__amigaos4__) # define PLATFORM_STRING "AmigaOS4" #elif defined(PLATFORM_AMIGA) # define PLATFORM_STRING "AmigaOS" #elif defined(__QNX__) || defined(__QNXNTO__) # define PLATFORM_STRING "QNX" #elif defined(PLATFORM_HAIKU) # define PLATFORM_STRING "Haiku" #elif defined(PLATFORM_OSX) # define PLATFORM_STRING "MacOSX" #elif defined(PLATFORM_MAC) # define PLATFORM_STRING "MacOS" #elif defined(__hpux) || defined(__hpux__) || defined(_hpux) # define PLATFORM_STRING "HP-UX" #elif (defined(__sun) || defined(sun)) && (defined(__svr4__) || defined(__SVR4)) # define PLATFORM_STRING "Solaris" #elif defined(__sun) || defined(sun) # define PLATFORM_STRING "SunOS" #elif defined(__sgi) || defined(sgi) || defined(__sgi__) # define PLATFORM_STRING "Irix" #elif defined(PLATFORM_RISCOS) # define PLATFORM_STRING "RiscOS" #elif defined(__GNU__) # define PLATFORM_STRING "GNU/Hurd" #elif defined(PLATFORM_UNIX) # define PLATFORM_STRING "Unix" #else # define PLATFORM_STRING "Unknown" # warning "Platform is UNKNOWN." #endif /* PLATFORM_STRING */ #endif /* ARCHDEFS_H */ common/bspfile.h000066400000000000000000000172731444734033100141600ustar00rootroot00000000000000/* bspfile.h -- common bspfile header * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef BSPFILE_H_ #define BSPFILE_H_ // upper design bounds #define MAX_MAP_HULLS 8 #define MAX_MAP_MODELS 256 #define MAX_MAP_BRUSHES 4096 #define MAX_MAP_ENTITIES 1024 // #define MAX_MAP_ENTSTRING 65536 #define MAX_MAP_ENTSTRING 81920 #define MAX_MAP_PLANES 16384 #define MAX_MAP_NODES 32767 // because negative shorts are contents #define MAX_MAP_CLIPNODES 65535 // #define MAX_MAP_LEAFS 65535 // was 8192. was 32767 before Id 1.07 update. #define MAX_MAP_VERTS 65535 #define MAX_MAP_FACES 65535 #define MAX_MAP_MARKSURFACES 65535 #define MAX_MAP_TEXINFO 4096 #define MAX_MAP_EDGES 256000 #define MAX_MAP_SURFEDGES 512000 #define MAX_MAP_TEXTURES 512 #define MAX_MAP_MIPTEX 0x200000 #define MAX_MAP_LIGHTING 0x100000 #define MAX_MAP_VISIBILITY 0x100000 #define MAX_MAP_PORTALS 65536 // key / value pair sizes #define MAX_KEY 32 #define MAX_VALUE 1024 //============================================================================= #define BSPVERSION 29 #define BSP2VERSION (('B'<<0)|('S'<<8)|('P'<<16)|('2'<<24)) typedef struct { int fileofs, filelen; } lump_t; #define LUMP_ENTITIES 0 #define LUMP_PLANES 1 #define LUMP_TEXTURES 2 #define LUMP_VERTEXES 3 #define LUMP_VISIBILITY 4 #define LUMP_NODES 5 #define LUMP_TEXINFO 6 #define LUMP_FACES 7 #define LUMP_LIGHTING 8 #define LUMP_CLIPNODES 9 #define LUMP_LEAFS 10 #define LUMP_MARKSURFACES 11 #define LUMP_EDGES 12 #define LUMP_SURFEDGES 13 #define LUMP_MODELS 14 #define HEADER_LUMPS 15 typedef struct { float mins[3], maxs[3]; float origin[3]; int headnode[MAX_MAP_HULLS]; int visleafs; // not including the solid leaf 0 int firstface, numfaces; } dmodel_t; typedef struct { int version; lump_t lumps[HEADER_LUMPS]; } dheader_t; typedef struct { int nummiptex; int dataofs[4]; // [nummiptex] } dmiptexlump_t; #define MIPLEVELS 4 typedef struct miptex_s { char name[16]; unsigned int width, height; unsigned int offsets[MIPLEVELS]; // four mip maps stored } miptex_t; typedef struct { float point[3]; } dvertex_t; // 0-2 are axial planes #define PLANE_X 0 #define PLANE_Y 1 #define PLANE_Z 2 // 3-5 are non-axial planes snapped to the nearest #define PLANE_ANYX 3 #define PLANE_ANYY 4 #define PLANE_ANYZ 5 typedef struct { float normal[3]; float dist; int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate } dplane_t; #define CONTENTS_EMPTY -1 #define CONTENTS_SOLID -2 #define CONTENTS_WATER -3 #define CONTENTS_SLIME -4 #define CONTENTS_LAVA -5 #define CONTENTS_SKY -6 #define CONTENTS_ORIGIN -7 // removed at csg time #define CONTENTS_CLIP -8 // changed to contents_solid #define CONTENTS_CURRENT_0 -9 #define CONTENTS_CURRENT_90 -10 #define CONTENTS_CURRENT_180 -11 #define CONTENTS_CURRENT_270 -12 #define CONTENTS_CURRENT_UP -13 #define CONTENTS_CURRENT_DOWN -14 // !!! if this is changed, it must be changed in asm_i386.h too !!! typedef struct { int planenum; short children[2]; // negative numbers are -(leafs+1), not nodes short mins[3]; // for sphere culling short maxs[3]; unsigned short firstface; unsigned short numfaces; // counting both sides } dnode_t; typedef struct { int planenum; int children[2]; // negative numbers are -(leafs+1), not nodes float mins[3]; // for sphere culling float maxs[3]; unsigned int firstface; unsigned int numfaces; // counting both sides } dnode2_t; typedef struct { int planenum; short children[2]; // negative numbers are contents } dclipnode_t; typedef struct { int planenum; int children[2]; // negative numbers are contents } dclipnode2_t; typedef struct texinfo_s { float vecs[2][4]; // [s/t][xyz offset] int miptex; int flags; } texinfo_t; #define TEX_SPECIAL 1 // sky or slime, no lightmap or 256 subdivision // note that edge 0 is never used, because negative edge nums are used for // counterclockwise use of the edge in a face typedef struct { unsigned short v[2]; // vertex numbers } dedge_t; typedef struct { unsigned int v[2]; // vertex numbers } dedge2_t; #define MAXLIGHTMAPS 4 typedef struct { short planenum; short side; int firstedge; // we must support > 64k edges short numedges; short texinfo; // lighting info byte styles[MAXLIGHTMAPS]; int lightofs; // start of [numstyles*surfsize] samples } dface_t; typedef struct { int planenum; int side; int firstedge; // we must support > 64k edges int numedges; int texinfo; // lighting info byte styles[MAXLIGHTMAPS]; int lightofs; // start of [numstyles*surfsize] samples } dface2_t; #define AMBIENT_WATER 0 #define AMBIENT_SKY 1 #define AMBIENT_SLIME 2 #define AMBIENT_LAVA 3 #define NUM_AMBIENTS 4 // automatic ambient sounds // leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas // all other leafs need visibility info typedef struct { int contents; int visofs; // -1 = no visibility info short mins[3]; // for frustum culling short maxs[3]; unsigned short firstmarksurface; unsigned short nummarksurfaces; byte ambient_level[NUM_AMBIENTS]; } dleaf_t; typedef struct { int contents; int visofs; // -1 = no visibility info float mins[3]; // for frustum culling float maxs[3]; unsigned int firstmarksurface; unsigned int nummarksurfaces; byte ambient_level[NUM_AMBIENTS]; } dleaf2_t; //============================================================================ #ifndef QUAKE_GAME extern int is_bsp2; #define ANGLE_UP -1 #define ANGLE_DOWN -2 // the utilities get to be lazy and just use large static arrays extern int nummodels; extern dmodel_t dmodels[MAX_MAP_MODELS]; extern int visdatasize; extern byte dvisdata[MAX_MAP_VISIBILITY]; extern int lightdatasize; extern byte dlightdata[MAX_MAP_LIGHTING]; extern int texdatasize; extern byte dtexdata[MAX_MAP_MIPTEX]; // (dmiptexlump_t) extern int entdatasize; extern char dentdata[MAX_MAP_ENTSTRING]; extern int numleafs; extern dleaf_t dleafs[MAX_MAP_LEAFS]; extern dleaf2_t dleafs2[MAX_MAP_LEAFS]; extern int numplanes; extern dplane_t dplanes[MAX_MAP_PLANES]; extern int numvertexes; extern dvertex_t dvertexes[MAX_MAP_VERTS]; extern int numnodes; extern dnode_t dnodes[MAX_MAP_NODES]; extern dnode2_t dnodes2[MAX_MAP_NODES]; extern int numtexinfo; extern texinfo_t texinfo[MAX_MAP_TEXINFO]; extern int numfaces; extern dface_t dfaces[MAX_MAP_FACES]; extern dface2_t dfaces2[MAX_MAP_FACES]; extern int numclipnodes; extern dclipnode_t dclipnodes[MAX_MAP_CLIPNODES]; extern dclipnode2_t dclipnodes2[MAX_MAP_CLIPNODES]; extern int numedges; extern dedge_t dedges[MAX_MAP_EDGES]; extern dedge2_t dedges2[MAX_MAP_EDGES]; extern int nummarksurfaces; extern unsigned short dmarksurfaces[MAX_MAP_MARKSURFACES]; extern int dmarksurfaces2[MAX_MAP_MARKSURFACES]; extern int numsurfedges; extern int dsurfedges[MAX_MAP_SURFEDGES]; void LoadBSPFile (const char *filename); void WriteBSPFile (const char *filename, int bsp2); void PrintBSPFileSizes (int bsp2); #endif /* QUAKE_GAME */ #endif /* BSPFILE_H_ */ common/compiler.h000066400000000000000000000070601444734033100143370ustar00rootroot00000000000000/* * compiler.h * compiler specific definitions and settings * used in the uhexen2 (Hammer of Thyrion) tree. * - standalone header * - doesn't and must not include any other headers * - shouldn't depend on arch_def.h, q_stdinc.h, or * any other headers * * Copyright (C) 2007-2011 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef HX2_COMPILER_H #define HX2_COMPILER_H #if defined(__GNUC__) #define FUNC_PRINTF(x,y) __attribute__((__format__(__printf__,x,y))) #else #define FUNC_PRINTF(x,y) #endif /* argument format attributes for function pointers are supported for gcc >= 3.1 */ #if defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 0)) #define FUNCP_PRINTF FUNC_PRINTF #else #define FUNCP_PRINTF(x,y) #endif /* llvm's optnone function attribute started with clang-3.5.0 */ #if defined(__clang__) && \ (__clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 5)) #define FUNC_NO_OPTIMIZE __attribute__((__optnone__)) /* function optimize attribute is added starting with gcc 4.4.0 */ #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 3)) #define FUNC_NO_OPTIMIZE __attribute__((__optimize__("0"))) #else #define FUNC_NO_OPTIMIZE #endif #if defined(__GNUC__) #define FUNC_NORETURN __attribute__((__noreturn__)) #elif defined(_MSC_VER) && (_MSC_VER >= 1200) #define FUNC_NORETURN __declspec(noreturn) #elif defined(__WATCOMC__) #define FUNC_NORETURN /* use the 'aborts' aux pragma */ #else #define FUNC_NORETURN #endif #if defined(__GNUC__) #define FUNC_UNUSED __attribute__((__unused__)) #else #define FUNC_UNUSED #endif #if defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) #define FUNC_NOINLINE __attribute__((__noinline__)) #elif defined(_MSC_VER) && (_MSC_VER >= 1300) #define FUNC_NOINLINE __declspec(noinline) #else #define FUNC_NOINLINE #endif #if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) #define FUNC_NOCLONE __attribute__((__noclone__)) #else #define FUNC_NOCLONE #endif #if !(defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8))) #define __extension__ #endif /* __GNUC__ */ #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #define __thisfunc__ __func__ #elif defined(__GNUC__) && __GNUC__ < 3 #define __thisfunc__ __FUNCTION__ #elif defined(__GNUC__) && __GNUC__ > 2 #define __thisfunc__ __func__ #elif defined(__WATCOMC__) #define __thisfunc__ __FUNCTION__ #elif defined(__LCC__) #define __thisfunc__ __func__ #elif defined(_MSC_VER) && _MSC_VER >= 1300 /* VC7++ */ #define __thisfunc__ __FUNCTION__ #else /* stupid fallback */ /*#define __thisfunc__ __FILE__*/ #error __func__ or __FUNCTION__ compiler token not supported? define one... #endif /* inline keyword: */ #if defined(_MSC_VER) && !defined(__cplusplus) #define inline __inline #endif /* _MSC_VER */ #endif /* HX2_COMPILER_H */ common/crc.c000066400000000000000000000074431444734033100132740ustar00rootroot00000000000000/* * crc.c -- crc functions * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include "crc.h" // this is a 16 bit, non-reflected CRC using the polynomial 0x1021 // and the initial and final xor values shown below... in other words, the // CCITT standard CRC used by XMODEM #define CRC_INIT_VALUE 0xffff #define CRC_XOR_VALUE 0x0000 static unsigned short crctable[256] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; void CRC_Init(unsigned short *crcvalue) { *crcvalue = CRC_INIT_VALUE; } void CRC_ProcessByte(unsigned short *crcvalue, unsigned char data) { *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; } void CRC_ProcessBlock (unsigned char *start, unsigned short *crcvalue, int count) { unsigned short crc = *crcvalue; while (count--) crc = (crc << 8) ^ crctable[(crc >> 8) ^ *start++]; *crcvalue = crc; } unsigned short CRC_Value(unsigned short crcvalue) { return crcvalue ^ CRC_XOR_VALUE; } unsigned short CRC_Block (unsigned char *start, int count) { unsigned short crc; CRC_Init (&crc); CRC_ProcessBlock (start, &crc, count); return crc; } common/crc.h000066400000000000000000000022261444734033100132730ustar00rootroot00000000000000/* * crc.h -- crc functions * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_CRC_H #define __HX2_CRC_H void CRC_Init(unsigned short *crcvalue); void CRC_ProcessByte(unsigned short *crcvalue, unsigned char data); void CRC_ProcessBlock (unsigned char *start, unsigned short *crcvalue, int count); unsigned short CRC_Value(unsigned short crcvalue); unsigned short CRC_Block (unsigned char *start, int count); #endif /* __HX2_CRC_H */ common/filenames.h000066400000000000000000000154101444734033100144660ustar00rootroot00000000000000/* Macros for taking apart, interpreting and processing file names. * * These are here because some non-Posix (a.k.a. DOSish) systems have * drive letter brain-damage at the beginning of an absolute file name, * use forward- and back-slash in path names interchangeably, and * some of them have case-insensitive file names. * * This was based on filenames.h from BFD, the Binary File Descriptor * library, Copyright (C) 2000-2016 Free Software Foundation, Inc., * and changed by O. Sezer for our needs. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef FILENAMES_H #define FILENAMES_H #include #include "compiler.h" /* for inline, etc */ /* ---------------------- Windows, DOS, OS2: ---------------------- */ #if defined(__MSDOS__) || defined(__DOS__) || defined(__DJGPP__) || \ defined(_MSDOS) || defined(__OS2__) || defined(__EMX__) || \ defined(_WIN32) || defined(_Windows) || defined(__WINDOWS__) || \ defined(__NT__) || defined(__CYGWIN__) #define HAVE_DOS_BASED_FILE_SYSTEM 1 #define HAVE_CASE_INSENSITIVE_FILE_SYSTEM 1 #define HAS_DRIVE_SPEC(f) ((f)[0] && ((f)[1] == ':')) #define STRIP_DRIVE_SPEC(f) ((f) + 2) #define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\') /* both '/' and '\\' work as dir separator. djgpp likes changing * '\\' into '/', so I define DIR_SEPARATOR_CHAR as '/' for djgpp, * '\\' otherwise. */ #ifdef __DJGPP__ #define DIR_SEPARATOR_CHAR '/' #define DIR_SEPARATOR_STR "/" #else #define DIR_SEPARATOR_CHAR '\\' #define DIR_SEPARATOR_STR "\\" #endif /* Note that IS_ABSOLUTE_PATH accepts d:foo as well, although it is only semi-absolute. This is because the users of IS_ABSOLUTE_PATH want to know whether to prepend the current working directory to a file name, which should not be done with a name like d:foo. */ #define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]) || HAS_DRIVE_SPEC((f))) #ifdef __cplusplus static inline char *FIND_FIRST_DIRSEP(char *_the_path) { /* FIXME: What about C:FOO ? */ char *p1 = strchr(_the_path, '/'); char *p2 = strchr(_the_path, '\\'); if (p1 == NULL) return p2; if (p2 == NULL) return p1; return (p1 < p2)? p1 : p2; } static inline char *FIND_LAST_DIRSEP (char *_the_path) { /* FIXME: What about C:FOO ? */ char *p1 = strrchr(_the_path, '/'); char *p2 = strrchr(_the_path, '\\'); if (p1 == NULL) return p2; if (p2 == NULL) return p1; return (p1 > p2)? p1 : p2; } static inline const char *FIND_FIRST_DIRSEP(const char *_the_path) { /* FIXME: What about C:FOO ? */ const char *p1 = strchr(_the_path, '/'); const char *p2 = strchr(_the_path, '\\'); if (p1 == NULL) return p2; if (p2 == NULL) return p1; return (p1 < p2)? p1 : p2; } static inline const char *FIND_LAST_DIRSEP (const char *_the_path) { /* FIXME: What about C:FOO ? */ const char *p1 = strrchr(_the_path, '/'); const char *p2 = strrchr(_the_path, '\\'); if (p1 == NULL) return p2; if (p2 == NULL) return p1; return (p1 > p2)? p1 : p2; } #else static inline char *FIND_FIRST_DIRSEP(const char *_the_path) { /* FIXME: What about C:FOO ? */ char *p1 = strchr(_the_path, '/'); char *p2 = strchr(_the_path, '\\'); if (p1 == NULL) return p2; if (p2 == NULL) return p1; return (p1 < p2)? p1 : p2; } static inline char *FIND_LAST_DIRSEP (const char *_the_path) { /* FIXME: What about C:FOO ? */ char *p1 = strrchr(_the_path, '/'); char *p2 = strrchr(_the_path, '\\'); if (p1 == NULL) return p2; if (p2 == NULL) return p1; return (p1 > p2)? p1 : p2; } #endif /* C++ */ /* ----------------- AmigaOS, MorphOS, AROS, etc: ----------------- */ #elif defined(__MORPHOS__) || defined(__AROS__) || defined(AMIGAOS) || \ defined(__amigaos__) || defined(__amigaos4__) ||defined(__amigados__) || \ defined(AMIGA) || defined(_AMIGA) || defined(__AMIGA__) #define HAS_DRIVE_SPEC(f) (0) /* */ #define STRIP_DRIVE_SPEC(f) (f) /* */ #define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == ':') #define DIR_SEPARATOR_CHAR '/' #define DIR_SEPARATOR_STR "/" #define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]) || (strchr((f), ':'))) #define HAVE_CASE_INSENSITIVE_FILE_SYSTEM 1 #ifdef __cplusplus static inline char *FIND_FIRST_DIRSEP(char *_the_path) { char *p = strchr(_the_path, ':'); if (p != NULL) return p; return strchr(_the_path, '/'); } static inline char *FIND_LAST_DIRSEP (char *_the_path) { char *p = strrchr(_the_path, '/'); if (p != NULL) return p; return strchr(_the_path, ':'); } static inline const char *FIND_FIRST_DIRSEP(const char *_the_path) { const char *p = strchr(_the_path, ':'); if (p != NULL) return p; return strchr(_the_path, '/'); } static inline const char *FIND_LAST_DIRSEP (const char *_the_path) { const char *p = strrchr(_the_path, '/'); if (p != NULL) return p; return strchr(_the_path, ':'); } #else static inline char *FIND_FIRST_DIRSEP(const char *_the_path) { char *p = strchr(_the_path, ':'); if (p != NULL) return p; return strchr(_the_path, '/'); } static inline char *FIND_LAST_DIRSEP (const char *_the_path) { char *p = strrchr(_the_path, '/'); if (p != NULL) return p; return strchr(_the_path, ':'); } #endif /* C++ */ /* ---------------------- assumed UNIX-ish : ---------------------- */ #else /* */ #define IS_DIR_SEPARATOR(c) ((c) == '/') #define DIR_SEPARATOR_CHAR '/' #define DIR_SEPARATOR_STR "/" #define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0])) #define HAS_DRIVE_SPEC(f) (0) #define STRIP_DRIVE_SPEC(f) (f) #ifdef __cplusplus static inline char *FIND_FIRST_DIRSEP(char *_the_path) { return strchr(_the_path, '/'); } static inline char *FIND_LAST_DIRSEP (char *_the_path) { return strrchr(_the_path, '/'); } static inline const char *FIND_FIRST_DIRSEP(const char *_the_path) { return strchr(_the_path, '/'); } static inline const char *FIND_LAST_DIRSEP (const char *_the_path) { return strrchr(_the_path, '/'); } #else static inline char *FIND_FIRST_DIRSEP(const char *_the_path) { return strchr(_the_path, '/'); } static inline char *FIND_LAST_DIRSEP (const char *_the_path) { return strrchr(_the_path, '/'); } #endif /* C++ */ #endif #endif /* FILENAMES_H */ common/genmodel.h000066400000000000000000000061111444734033100143130ustar00rootroot00000000000000/* * genmodel.h -- model structures * * This file must be identical in the genmodel project and in the H3 * project. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GENMODEL_H #define __GENMODEL_H #define ALIAS_VERSION 6 #define ALIAS_NEWVERSION 50 #define ALIAS_ONSEAM 0x0020 // Little-endian "IDPO" #define IDPOLYHEADER (('O'<<24)+('P'<<16)+('D'<<8)+'I') #define RAPOLYHEADER (('O'<<24)+('P'<<16)+('A'<<8)+'R') // must match definition in spritegn.h #ifndef SYNCTYPE_T #define SYNCTYPE_T typedef enum { ST_SYNC = 0, ST_RAND } synctype_t; #endif typedef enum { ALIAS_SINGLE = 0, ALIAS_GROUP } aliasframetype_t; typedef enum { ALIAS_SKIN_SINGLE = 0, ALIAS_SKIN_GROUP } aliasskintype_t; typedef struct { int ident; int version; vec3_t scale; vec3_t scale_origin; float boundingradius; vec3_t eyeposition; int numskins; int skinwidth; int skinheight; int numverts; int numtris; int numframes; synctype_t synctype; int flags; float size; } mdl_t; typedef struct { int ident; int version; vec3_t scale; vec3_t scale_origin; float boundingradius; vec3_t eyeposition; int numskins; int skinwidth; int skinheight; int numverts; int numtris; int numframes; synctype_t synctype; int flags; float size; int num_st_verts; } newmdl_t; typedef struct { // TODO: could be shorts int onseam; int s; int t; } stvert_t; typedef struct dtriangle_s { int facesfront; int vertindex[3]; } dtriangle_t; typedef struct dnewtriangle_s { int facesfront; unsigned short vertindex[3]; unsigned short stindex[3]; } dnewtriangle_t; #define DT_FACES_FRONT 0x0010 typedef struct { byte v[3]; byte lightnormalindex; } trivertx_t; typedef struct { trivertx_t bboxmin; // lightnormal isn't used trivertx_t bboxmax; // lightnormal isn't used char name[16]; // frame name from grabbing } daliasframe_t; typedef struct { int numframes; trivertx_t bboxmin; // lightnormal isn't used trivertx_t bboxmax; // lightnormal isn't used } daliasgroup_t; typedef struct { int numskins; } daliasskingroup_t; typedef struct { float interval; } daliasinterval_t; typedef struct { float interval; } daliasskininterval_t; typedef struct { aliasframetype_t type; } daliasframetype_t; typedef struct { aliasskintype_t type; } daliasskintype_t; #endif /* __GENMODEL_H */ common/hwal.h000066400000000000000000000031621444734033100134570ustar00rootroot00000000000000/* * hwal.h * Hexen II, .WAL texture file format * * Copyright (C) 1996-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HWAL_H #define __HWAL_H // Little-endian "HWAL" #define IDWALHEADER (('L'<<24)+('A'<<16)+('W'<<8)+'H') #define WALVERSION 1 #if !defined (MIPLEVELS) #define MIPLEVELS 4 #endif /* MIPLEVELS */ // this format, based on a quake2 WAL structure, was put together // by Jacques 'Korax' Krige. compared to miptex_t, the miptex_wal_t // structure has two extra int fields at the beginning and the name // field is 32 chars long instead of 16. the rest, ie. the offsets, // are the same. typedef struct miptex_wal_s { int ident; int version; char name[32]; unsigned int width, height; unsigned int offsets[MIPLEVELS]; // four mip maps stored } miptex_wal_t; #define WAL_EXT_DIRNAME "textures" #define WAL_REPLACE_ASTERIX '_' /* character to replace '*' in texture names. */ #endif /* __HWAL_H */ common/midifile.h000066400000000000000000000275631444734033100143210ustar00rootroot00000000000000/* * midifile.h -- common MIDI constants and structures * * Compiled using widely available information from: * - MIDIFILE: Tim Thompson, Michael Czeiszperger * - playmidi: Copyright (C) 1994-1996 Nathan I. Laredo * - TiMidity: Copyright (C) 1995 Tuukka Toivonen * - pmidi: Copyright (C) 1999 Steve Ratcliffe * - alsa-lib: Jaroslav Kysela * Abramo Bagnara * Takashi Iwai * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* -->-- Begin unmodified copy of "MIDI-FORMAT" file by Nathan Laredo -->-- MIDI File Format ================ By Nathan Laredo, last revision: 25 August 1996 NOTE: THIS IS NOT THE OFFICIAL MIDI FILE FORMAT SPECIFICATION. THESE ARE PERSONAL NOTES I WROTE WHEN I FIRST STARTED TO WRITE MY OWN MIDI FILE READING ROUTINES. THIS INFORMATION IS PROVIDED IN THE HOPE THAT IT MAY SAVE SOMEONE ELSE THE AMOUNT OF RESEARCH THAT WENT INTO IT. I MAKE NO GUARANTEES OR WARRANTIES PERTAINING TO USEFULNESS OR ACCURACY. ----------------------------------------------------------------------- (MThd = 0x4d546864) (32-bit big endian length = 6) (16-bit big endian format) (16-bit big endian tracks) (16-bit big-endian division) format is either 0 - one track, 1 - many tracks, one sequence or 2 - many tracks with one sequence per track. format 1 files should have all tempo changes in the first track (MTrk = 0x4d54726b) (32-bit big endian length of track) (variable-length encoded ticks since previous event) (Event data -- see below) ticks and event data are repeated for length bytes. MTrk block is repeated for as many tracks as indicated in header. ------------------------------------------------------------------------ Variable length quantities are a series of 7 bit values encoded from msb to lsb, with the lsb bit 7 clear, all prior have bit 7 set. ------------------------------------------------------------------------ Event data is either midi data (with running status) that is to be sent to the midi device, a sysex (two types), or a meta-event ------------------------------------------------------------------------- A sysex event contains information to be sent directly to the midi synth. It consists of (0xf0) (variable-length encoded length) (data after 0xf0) or (0xf7) (variable-length encoded length) (all data to be sent) Sysex messages may or may not be terminated with 0xf7 (EOX), EOX should not be added automagically when messages are output to a midi synth, as there may be a following 0xf7-type sysex after a delay that may be required by the synth. The 0xf7-type sysex may contain data > 0x7f so programs shouldn't stop sending when a byte value is > 0x7f. ------------------------------------------------------------------------- A meta event contaions information such as text, tempo, and key signature. It consists of (0xff) (type) (variable length encoded deta length) (data) Text is not usually null-terminated. You must depend on the length when reading it. meta event types range from 0x00 to 0x7f Type Contents ==== ======== 0x00: (16-bit big endian sequence number) 0x01: (any text) 0x02: (Copyright Message text) 0x03: (Sequence/Track Name text) 0x04: (Instrument Name text) 0x05: (Lyric text) 0x06: (Marker text) 0x07: (Cue-point text) 0x2f: End of Track -- no data for this type 0x51: (24-bit big endian tempo) -- microseconds per midi quarter note 0x54: (hour) (min) (second) (frame) (fractional-frame) - SMPTE track start 0x58: (numerator) (denominator) (clocks per metronome click) (32nd notes notated per midi quarter note) -- Time Signature denominator is a power of two, ie 0x03 = 2^3 = 8 0x59: (sharps/flats) (major/minor flag) -- Key Signature if sharp key, first byte represents number of sharps if flat key, first byte represents negative number of flats the major/minor flag is 0 if minor, 1 if major. 0x7f: (Sequencer Specific data) -- I've never used this type, I ignore it. I have never seen it used, and If someone would let me know why it exists, I would appreciate it. ----------------------------------------------------------------------------- Many thanks to the authors of the many free midi references available on ftp.ucsd.edu that I used in creating this file format specification which I use for my Playmidi package for Linux. If you find any errors in this document, please email me. Nathan Laredo -- laredo@gnu.ai.mit.edu -- (C)1995, 1996 Nathan Laredo This document may be freely distributed in its original form. <----<----<----<----<-- end of MIDI-FORMAT file ----<----<----<----<---- */ #ifndef MIDIFILE_H #define MIDIFILE_H /* MIDI status commands */ #define MIDICMD_NOTE_OFF 0x80 #define MIDICMD_NOTE_ON 0x90 #define MIDICMD_NOTE_PRESSURE 0xa0 #define MIDICMD_KEY_PRESSURE MIDICMD_NOTE_PRESSURE #define MIDICMD_KEY_AFTERTOUCH MIDICMD_NOTE_PRESSURE #define MIDICMD_POLY_PRESS MIDICMD_NOTE_PRESSURE #define MIDICMD_CONTROL 0xb0 #define MIDICMD_CTRL_CHANGE MIDICMD_CONTROL #define MIDICMD_CONTROLER MIDICMD_CONTROL #define MIDICMD_PGM_CHANGE 0xc0 #define MIDICMD_PATCH MIDICMD_PGM_CHANGE #define MIDICMD_CHANNEL_PRESSURE 0xd0 #define MIDICMD_CHANNEL_AFTERTOUCH MIDICMD_CHANNEL_PRESSURE #define MIDICMD_PITCH_BEND 0xe0 #define MIDICMD_PITCH_WHEEL MIDICMD_PITCH_BEND #define MIDICMD_BENDER MIDICMD_PITCH_BEND #define MIDICMD_SYSEX 0xf0 /* sysex (system exclusive) begin */ /* (0xf1 to 0xff from asoundef.h) */ #define MIDICMD_MTC_QUARTER 0xf1 /* MTC quarter frame */ #define MIDICMD_SONG_POS 0xf2 #define MIDICMD_SONG_SELECT 0xf3 #define MIDICMD_TUNE_REQUEST 0xf6 #define MIDICMD_SYSEX_END 0xf7 /* end of sysex */ #define MIDICMD_CLOCK 0xf8 #define MIDICMD_START 0xfa #define MIDICMD_CONTINUE 0xfb #define MIDICMD_STOP 0xfc #define MIDICMD_SENSING 0xfe /* active sensing */ #define MIDICMD_RESET 0xff /* 7 bit controllers */ #define MIDICTL_SUSTAIN 0x40 /* damper_pedal */ #define MIDICTL_PORTAMENTO 0x41 #define MIDICTL_SOSTENUTO 0x42 #define MIDICTL_SOFT_PEDAL 0x43 #define MIDICTL_LEGATO_FOOTSWITCH 0x44 /* general_4 */ #define MIDICTL_HOLD2 0x45 /* (0x46 to 0x4f from asoundef.h) */ #define MIDICTL_SC1_SOUND_VARIATION 0x46 #define MIDICTL_SC2_TIMBRE 0x47 #define MIDICTL_SC3_RELEASE_TIME 0x48 #define MIDICTL_SC4_ATTACK_TIME 0x49 #define MIDICTL_SC5_BRIGHTNESS 0x4a #define MIDICTL_SC6 0x4b #define MIDICTL_SC7 0x4c #define MIDICTL_SC8 0x4d #define MIDICTL_SC9 0x4e #define MIDICTL_SC10 0x4f #define MIDICTL_GENERAL_PURPOSE5 0x50 #define MIDICTL_GENERAL_PURPOSE6 0x51 #define MIDICTL_GENERAL_PURPOSE7 0x52 #define MIDICTL_GENERAL_PURPOSE8 0x53 /* (0x54, 0x5b from asoundef.h) */ #define MIDICTL_PORTAMENTO_CONTROL 0x54 #define MIDICTL_REVERB_DEPTH 0x5b /* E1 Reverb Depth */ #define MIDICTL_TREMOLO_DEPTH 0x5c /* E2 Tremolo Depth */ #define MIDICTL_CHORUS_DEPTH 0x5d /* E3 Chorus Depth */ #define MIDICTL_DETUNE_DEPTH 0x5e /* E4 Detune Depth */ #define MIDICTL_PHASER_DEPTH 0x5f /* E5 Phaser Depth */ /* parameter values */ #define MIDICTL_DATA_INCREMENT 0x60 #define MIDICTL_DATA_DECREMENT 0x61 /* parameter selection */ #define MIDICTL_NONREG_PARM_LSB 0x62 /* Non-registered parameter number */ #define MIDICTL_NONREG_PARM_MSB 0x63 #define MIDICTL_REGIST_PARM_LSB 0x64 /* Registered parameter number */ #define MIDICTL_REGIST_PARM_MSB 0x65 /* (0x78 to 0x7f from asoundef.h) */ #define MIDICTL_ALL_SOUNDS_OFF 0x78 #define MIDICTL_RESET_CONTROLLERS 0x79 #define MIDICTL_LOCAL_CONTROL_SWITCH 0x7a #define MIDICTL_ALL_NOTES_OFF 0x7b #define MIDICTL_OMNI_OFF 0x7c #define MIDICTL_OMNI_ON 0x7d #define MIDICTL_MONO1 0x7e #define MIDICTL_MONO2 0x7f /* Standard MIDI Files meta event definitions */ #define MIDI_META_EVENT 0xff #define MIDI_META_SEQUENCE 0x00 /* sequence_number */ #define MIDI_META_TEXT 0x01 /* text_event */ #define MIDI_META_COPYRIGHT 0x02 #define MIDI_META_TRACKNAME 0x03 /* sequence_name */ #define MIDI_META_INSTRNAME 0x04 /* instrument_name */ #define MIDI_META_LYRIC 0x05 #define MIDI_META_MARKER 0x06 #define MIDI_META_CUE_POINT 0x07 #define MIDI_META_CHANPREFIX 0x20 /* channel_prefix */ #define MIDI_META_EOT 0x2f /* end_of_track */ #define MIDI_META_TEMPO 0x51 /* set_tempo */ #define MIDI_META_SMPTE_OFFSET 0x54 /* SMPTE track start */ #define MIDI_META_TIME 0x58 /* time_signature */ #define MIDI_META_KEY 0x59 /* key_signature */ #define MIDI_META_PROP 0x7f /* sequencer_specific */ /* some more MIDI controller numbers (from asoundef.h) */ #define MIDICTL_MSB_BANK 0x00 /* bank selection */ #define MIDICTL_MSB_MODWHEEL 0x01 /* modulation */ #define MIDICTL_MSB_BREATH 0x02 #define MIDICTL_MSB_FOOT 0x04 #define MIDICTL_MSB_PORTAMENTO_TIME 0x05 #define MIDICTL_MSB_DATA_ENTRY 0x06 #define MIDICTL_MSB_MAIN_VOLUME 0x07 #define MIDICTL_MSB_BALANCE 0x08 #define MIDICTL_MSB_PAN 0x0a /* panpot */ #define MIDICTL_MSB_EXPRESSION 0x0b #define MIDICTL_MSB_EFFECT1 0x0c #define MIDICTL_MSB_EFFECT2 0x0d #define MIDICTL_MSB_GENERAL_PURPOSE1 0x10 #define MIDICTL_MSB_GENERAL_PURPOSE2 0x11 #define MIDICTL_MSB_GENERAL_PURPOSE3 0x12 #define MIDICTL_MSB_GENERAL_PURPOSE4 0x13 #define MIDICTL_LSB_BANK 0x20 /* bank selection */ #define MIDICTL_LSB_MODWHEEL 0x21 /* modulation */ #define MIDICTL_LSB_BREATH 0x22 #define MIDICTL_LSB_FOOT 0x24 #define MIDICTL_LSB_PORTAMENTO_TIME 0x25 #define MIDICTL_LSB_DATA_ENTRY 0x26 #define MIDICTL_LSB_MAIN_VOLUME 0x27 #define MIDICTL_LSB_BALANCE 0x28 #define MIDICTL_LSB_PAN 0x2a /* panpot */ #define MIDICTL_LSB_EXPRESSION 0x2b #define MIDICTL_LSB_EFFECT1 0x2c #define MIDICTL_LSB_EFFECT2 0x2d #define MIDICTL_LSB_GENERAL_PURPOSE1 0x30 #define MIDICTL_LSB_GENERAL_PURPOSE2 0x31 #define MIDICTL_LSB_GENERAL_PURPOSE3 0x32 #define MIDICTL_LSB_GENERAL_PURPOSE4 0x33 /* miscellaneous definitions */ #define MIDI_LOWERBYTE(x) ((unsigned char)(x & 0xff)) #define MIDI_UPPERBYTE(x) ((unsigned char)((x & 0xff00)>>8)) /* header magic */ #define MIDI_MAGIC_MTHD 0x4d546864 /* MIDI header magic "MThd" */ #define MIDI_MAGIC_MTRK 0x4d54726b /* Track header magic "MTrk" */ /* Non-standard MIDI file formats */ #define MIDI_MAGIC_CTMF 0x43544d46 /* Creative Music File (CMF) */ #define MIDI_MAGIC_RMID 0x524d4944 /* "RMID" (Microsoft's RMI) */ #define MIDI_MAGIC_RIFF 0x52494646 /* "RIFF", for Microsoft RMID */ /* Constants for MIDI v1.0. (from asoundef.h) */ #define MIDI_CHANNELS 16 /* Number of channels per port/cable. */ #define MIDI_GM_DRUM_CHANNEL (10-1) /* Channel number for GM drums. */ /* Pack these structures to byte granularity, because they are data * stored in midi files. With GCC, use __attribute__((__packed__)). * With MS Visual C and others, a #pragma pack() equivalent is needed. */ #if !defined(__GNUC__) #define __midi_packed #else #define __midi_packed __attribute__((__packed__)) #endif #if !defined(__GNUC__) #pragma pack(push,1) #endif struct _midichunk_s { int tag; /* MThd or MTrk */ int len; /* for MThd: must be 6, i.e. sizeof midihdr_t */ /* for MTrk: specifies the size of the track. */ } __midi_packed; struct _midihdr_s { short format; short numtracks; short timediv; } __midi_packed; typedef struct _midichunk_s midichunk_t; typedef struct _midihdr_s midihdr_t; #if !defined(__GNUC__) #pragma pack(pop) #endif typedef int _check_midichunk_size[(sizeof(struct _midichunk_s) == 8) * 2 - 1]; typedef int _check_midi_header_size[(sizeof(struct _midihdr_s) == 6) * 2 - 1]; #endif /* MIDIFILE_H */ common/net_sys.h000066400000000000000000000221151444734033100142070ustar00rootroot00000000000000/* * net_sys.h -- common network system header. * - depends on arch_def.h * - may depend on q_stdinc.h * * Copyright (C) 2007-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __NET_SYS_H__ #define __NET_SYS_H__ #include #include #include #include #if defined(PLATFORM_BSD) || defined(PLATFORM_OSX) || \ defined(PLATFORM_AMIGA) /* bsdsocket.library */ || \ (defined(PLATFORM_OS2) && !defined(__EMX__)) || \ defined(__GNU__) /* GNU/Hurd */ || \ defined(__riscos__) || defined(__HAIKU__) /* struct sockaddr has unsigned char sa_len as the first member in BSD * variants and the family member is also an unsigned char instead of an * unsigned short. This should matter only when PLATFORM_UNIX is defined, * however, checking for the offset of sa_family in every platform that * provide a struct sockaddr doesn't hurt either (see down below for the * compile time asserts.) */ /* FIXME : GET RID OF THIS ABOMINATION !!! */ #define HAVE_SA_LEN 1 #define SA_FAM_OFFSET 1 #else #undef HAVE_SA_LEN #define SA_FAM_OFFSET 0 #endif /* BSD, sockaddr */ /* unix includes and compatibility macros */ #if defined(PLATFORM_UNIX) || defined(PLATFORM_RISCOS) || defined(PLATFORM_HAIKU) #include #include #if defined(__sun) || defined(sun) #include #include #endif /* __sunos__ */ #include #include #include #include #include #include typedef int sys_socket_t; #define INVALID_SOCKET (-1) #define SOCKET_ERROR (-1) #if defined(__APPLE__) && defined(SO_NKE) && !defined(SO_NOADDRERR) /* ancient Mac OS X SDKs 10.2 and older are missing socklen_t */ typedef int socklen_t; /* defining as signed int to match the old api */ #endif /* ancient OSX SDKs */ #define SOCKETERRNO errno #define ioctlsocket ioctl #define closesocket close #define selectsocket select #define IOCTLARG_P(x) /* (char *) */ x #define NET_EWOULDBLOCK EWOULDBLOCK #define NET_ECONNREFUSED ECONNREFUSED #define socketerror(x) strerror((x)) /* Verify that we defined HAVE_SA_LEN correctly: */ COMPILE_TIME_ASSERT(sockaddr, offsetof(struct sockaddr, sa_family) == SA_FAM_OFFSET); #endif /* end of unix stuff */ /* os/2 includes and compatibility macros */ #if defined(PLATFORM_OS2) #ifdef __EMX__ #define _EMX_TCPIP #endif #include #include #include #include #include #include #include #include typedef int sys_socket_t; #define INVALID_SOCKET (-1) #define SOCKET_ERROR (-1) typedef u_long in_addr_t; /* u_int32_t */ typedef int socklen_t; #ifdef __EMX__ #include #define SOCKETERRNO errno #define socketerror(x) strerror((x)) #if !(defined(__INNOTEK_LIBC__) || defined(__KLIBC__)) /* no hstrerror() in old EMX */ #define hstrerror(x) strerror((x)) #endif #define closesocket close #else #include #define SOCKETERRNO sock_errno() #define socketerror(x) sock_strerror((x)) #define closesocket soclose #endif #define ioctlsocket ioctl #define selectsocket select #define IOCTLARG_P(x) /* (char *) */ x #define NET_EWOULDBLOCK EWOULDBLOCK #define NET_ECONNREFUSED ECONNREFUSED /* Verify that we defined HAVE_SA_LEN correctly: */ COMPILE_TIME_ASSERT(sockaddr, offsetof(struct sockaddr, sa_family) == SA_FAM_OFFSET); #endif /* end of os/2 stuff */ /* amiga includes and compatibility macros */ #if defined(PLATFORM_AMIGA) /* Amiga bsdsocket.library */ #ifndef PLATFORM_AMIGAOS3 #include #else #define __NO_NET_API #endif #include #ifdef PLATFORM_AMIGAOS3 #include /* struct timeval */ #else #include #endif #ifndef PLATFORM_AMIGAOS3 #include #endif #include #include #include #include #include #include typedef int sys_socket_t; #define INVALID_SOCKET (-1) #define SOCKET_ERROR (-1) #if defined(__AROS__) || defined(__amigaos4__) # define HAVE_SOCKLEN_T #elif defined(PLATFORM_AMIGAOS3) && defined(_SYS_NETINCLUDE_TYPES_H) /* Roadshow-SDK: identified by sys/netinclude_types.h header guard */ # define HAVE_SOCKLEN_T #endif #if defined(__amigaos4__) # define HAVE_IN_ADDR_T #elif defined(__AROS__) && defined(INET_ADDRSTRLEN) /* AROS ABI_V2 */ # define HAVE_IN_ADDR_T #elif defined(PLATFORM_AMIGAOS3) && defined(_SYS_NETINCLUDE_TYPES_H) /* Roadshow-SDK: identified by sys/netinclude_types.h header guard */ # define HAVE_IN_ADDR_T #endif #if !defined(HAVE_SOCKLEN_T) typedef LONG socklen_t; /* int32_t */ #endif #if !defined(HAVE_IN_ADDR_T) #if (LONG_MAX <= 2147483647L) typedef unsigned long in_addr_t; /* u_int32_t */ #else typedef unsigned int in_addr_t; /* u_int32_t */ #endif #endif #define SOCKETERRNO Errno() #define ioctlsocket IoctlSocket #define closesocket CloseSocket #define selectsocket(_N,_R,_W,_E,_T) \ WaitSelect((_N),(_R),(_W),(_E),(_T),NULL) #define IOCTLARG_P(x) (char *) x #if defined(__amigaos4__) || defined(PLATFORM_AMIGAOS3) #define inet_ntoa(x) Inet_NtoA(x.s_addr) /* Inet_NtoA(*(ULONG*)&x) */ #define h_errno Errno() #endif #define NET_EWOULDBLOCK EWOULDBLOCK #define NET_ECONNREFUSED ECONNREFUSED #define socketerror(x) strerror((x)) /* there is h_errno but no hstrerror() */ #define hstrerror(x) strerror((x)) /* Verify that we defined HAVE_SA_LEN correctly: */ COMPILE_TIME_ASSERT(sockaddr, offsetof(struct sockaddr, sa_family) == SA_FAM_OFFSET); #endif /* end of amiga bsdsocket.library stuff */ /* windows includes and compatibility macros */ #if defined(PLATFORM_WINDOWS) /* NOTE: winsock[2].h already includes windows.h */ #if !defined(_USE_WINSOCK2) #include #else #include #include #endif /* there is no in_addr_t on windows: define it as the type of the S_addr of in_addr structure */ typedef u_long in_addr_t; /* uint32_t */ /* on windows, socklen_t is to be a winsock2 thing */ #if !defined(IP_MSFILTER_SIZE) typedef int socklen_t; #endif /* socklen_t type */ typedef SOCKET sys_socket_t; #define selectsocket select #define IOCTLARG_P(x) /* (u_long *) */ x #define SOCKETERRNO WSAGetLastError() #define NET_EWOULDBLOCK WSAEWOULDBLOCK #define NET_ECONNREFUSED WSAECONNREFUSED /* must #include "wsaerror.h" for this : */ #define socketerror(x) __WSAE_StrError((x)) /* Verify that we defined HAVE_SA_LEN correctly: */ COMPILE_TIME_ASSERT(sockaddr, offsetof(struct sockaddr, sa_family) == SA_FAM_OFFSET); #endif /* end of windows stuff */ /* dos includes and compatibility macros */ #if defined(PLATFORM_DOS) #if defined(USE_WATT32) /* Waterloo TCP defines INVALID_SOCKET and SOCKET_ERROR. * It uses ioctlsocket and closesocket, similar to WinSock. * Unlike WinSock, ioctl argument is char*, NOT u_long*. * Unlike WinSock, SOCKET type is signed, NOT unsigned. * It still doesn't define socklen_t or in_addr_t types. */ #include #include #include #include #include #include #include #include #include /* for select_s(), sock_init() & friends. */ #ifdef __cplusplus extern "C" { #endif /* For controlling whether to terminate the app if a PKT-DRVR is not found: */ extern int _watt_do_exit; /* in sock_ini.h, but not in public headers. */ #ifdef __cplusplus } #endif #define selectsocket select_s #define IOCTLARG_P(x) (char *)x #define SOCKETERRNO errno #define socketerror(x) strerror((x)) #define NET_EWOULDBLOCK EWOULDBLOCK #define NET_ECONNREFUSED ECONNREFUSED typedef int socklen_t; typedef u_long in_addr_t; typedef int sys_socket_t; /* Verify that we defined HAVE_SA_LEN correctly: */ COMPILE_TIME_ASSERT(sockaddr, offsetof(struct sockaddr, sa_family) == SA_FAM_OFFSET); #else /* local headers: */ #include "dos/dos_sock.h" #endif /* USE_WATT32 */ #endif /* end of dos stuff. */ /* macros which may still be missing */ #if !defined(INADDR_NONE) #define INADDR_NONE ((in_addr_t) 0xffffffff) #endif /* INADDR_NONE */ #if !defined(INADDR_LOOPBACK) #define INADDR_LOOPBACK ((in_addr_t) 0x7f000001) /* 127.0.0.1 */ #endif /* INADDR_LOOPBACK */ #if !defined(MAXHOSTNAMELEN) /* SUSv2 guarantees that `Host names are limited to 255 bytes'. POSIX 1003.1-2001 guarantees that `Host names (not including the terminating NUL) are limited to HOST_NAME_MAX bytes'. */ #define MAXHOSTNAMELEN 256 #endif /* MAXHOSTNAMELEN */ #endif /* __NET_SYS_H__ */ common/pakfile.h000066400000000000000000000024141444734033100141360ustar00rootroot00000000000000/* * pakfile.h -- on-disk pak file structures * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __PAKFILE_H #define __PAKFILE_H // Little-endian "PACK" #define IDPAKHEADER (('K'<<24)+('C'<<16)+('A'<<8)+'P') #define PAK_PATH_LENGTH 56 typedef struct { char name[PAK_PATH_LENGTH]; int filepos, filelen; } dpackfile_t; typedef struct { // int ident; // == IDPAKHEADER char id[4]; int dirofs; int dirlen; } dpackheader_t; #define packfile_t dpackfile_t #define packheader_t dpackheader_t #define MAX_FILES_IN_PACK 2048 #endif /* __PAKFILE_H */ common/pr_comp.h000066400000000000000000000110451444734033100141620ustar00rootroot00000000000000/* * pr_comp.h * PROGS structures: This file is shared by Hexen II and HCC * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __PR_COMP_H #define __PR_COMP_H typedef int func_t; typedef int string_t; typedef enum { ev_bad = -1, ev_void = 0, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer } etype_t; #define OFS_NULL 0 #define OFS_RETURN 1 #define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors #define OFS_PARM1 7 #define OFS_PARM2 10 #define OFS_PARM3 13 #define OFS_PARM4 16 #define OFS_PARM5 19 #define OFS_PARM6 22 #define OFS_PARM7 25 #define RESERVED_OFS 28 #define IMMEDIATE_NAME "I+" /* was "IMMEDIATE" in qcc */ enum { OP_DONE, OP_MUL_F, OP_MUL_V, OP_MUL_FV, OP_MUL_VF, OP_DIV_F, OP_ADD_F, OP_ADD_V, OP_SUB_F, OP_SUB_V, OP_EQ_F, OP_EQ_V, OP_EQ_S, OP_EQ_E, OP_EQ_FNC, OP_NE_F, OP_NE_V, OP_NE_S, OP_NE_E, OP_NE_FNC, OP_LE, OP_GE, OP_LT, OP_GT, OP_LOAD_F, OP_LOAD_V, OP_LOAD_S, OP_LOAD_ENT, OP_LOAD_FLD, OP_LOAD_FNC, OP_ADDRESS, OP_STORE_F, OP_STORE_V, OP_STORE_S, OP_STORE_ENT, OP_STORE_FLD, OP_STORE_FNC, OP_STOREP_F, OP_STOREP_V, OP_STOREP_S, OP_STOREP_ENT, OP_STOREP_FLD, OP_STOREP_FNC, OP_RETURN, OP_NOT_F, OP_NOT_V, OP_NOT_S, OP_NOT_ENT, OP_NOT_FNC, OP_IF, OP_IFNOT, OP_CALL0, OP_CALL1, OP_CALL2, OP_CALL3, OP_CALL4, OP_CALL5, OP_CALL6, OP_CALL7, OP_CALL8, OP_STATE, OP_GOTO, OP_AND, OP_OR, OP_BITAND, OP_BITOR, OP_MULSTORE_F, OP_MULSTORE_V, OP_MULSTOREP_F, OP_MULSTOREP_V, OP_DIVSTORE_F, OP_DIVSTOREP_F, OP_ADDSTORE_F, OP_ADDSTORE_V, OP_ADDSTOREP_F, OP_ADDSTOREP_V, OP_SUBSTORE_F, OP_SUBSTORE_V, OP_SUBSTOREP_F, OP_SUBSTOREP_V, OP_FETCH_GBL_F, OP_FETCH_GBL_V, OP_FETCH_GBL_S, OP_FETCH_GBL_E, OP_FETCH_GBL_FNC, OP_CSTATE, OP_CWSTATE, OP_THINKTIME, OP_BITSET, OP_BITSETP, OP_BITCLR, OP_BITCLRP, OP_RAND0, OP_RAND1, OP_RAND2, OP_RANDV0, OP_RANDV1, OP_RANDV2, OP_SWITCH_F, OP_SWITCH_V, OP_SWITCH_S, OP_SWITCH_E, OP_SWITCH_FNC, OP_CASE, OP_CASERANGE }; typedef struct statement_v6_s { unsigned short op; short a, b, c; } dstatement_v6_t; typedef struct statement_v7_s { unsigned short pad; unsigned short op; int a, b, c; } dstatement_v7_t; typedef struct { unsigned short type; // if DEF_SAVEGLOBAL bit is set // the variable needs to be saved in savegames unsigned short ofs; int s_name; } ddef_v6_t; typedef struct { unsigned short pad; unsigned short type; // if DEF_SAVEGLOBAL bit is set // the variable needs to be saved in savegames int ofs; int s_name; } ddef_v7_t; COMPILE_TIME_ASSERT(dstatement_v6_t, sizeof(dstatement_v6_t) == 8); COMPILE_TIME_ASSERT(dstatement_v7_t, sizeof(dstatement_v7_t) == 16); COMPILE_TIME_ASSERT(ddef_v6_t, sizeof(ddef_v6_t) == 8); COMPILE_TIME_ASSERT(ddef_v7_t, sizeof(ddef_v7_t) == 12); #define DEF_SAVEGLOBAL (1<<15) #define MAX_PARMS 8 typedef struct { int first_statement; // negative numbers are builtins int parm_start; int locals; // total ints of parms + locals int profile; // runtime int s_name; int s_file; // source file defined in int numparms; byte parm_size[MAX_PARMS]; } dfunction_t; #define PROG_VERSION_V6 6 #define PROG_VERSION_V7 7 // process the progs internally as version 7: #define PROG_VERSION (PROG_VERSION_V7) typedef ddef_v7_t ddef_t; typedef dstatement_v7_t dstatement_t; typedef struct { int version; int crc; // check of header file int ofs_statements; int numstatements; // statement 0 is an error int ofs_globaldefs; int numglobaldefs; int ofs_fielddefs; int numfielddefs; int ofs_functions; int numfunctions; // function 0 is an empty int ofs_strings; int numstrings; // first string is a null string int ofs_globals; int numglobals; int entityfields; } dprograms_t; #endif /* __PR_COMP_H */ common/q_ctype.h000066400000000000000000000040441444734033100141700ustar00rootroot00000000000000/* Locale insensitive ctype.h functions taken from the RPM library - * RPM is Copyright (c) 1998 by Red Hat Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef Q_CTYPE_H #define Q_CTYPE_H static inline int q_isascii(int c) { return ((c & ~0x7f) == 0); } static inline int q_islower(int c) { return (c >= 'a' && c <= 'z'); } static inline int q_isupper(int c) { return (c >= 'A' && c <= 'Z'); } static inline int q_isalpha(int c) { return (q_islower(c) || q_isupper(c)); } static inline int q_isdigit(int c) { return (c >= '0' && c <= '9'); } static inline int q_isxdigit(int c) { return (q_isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); } static inline int q_isalnum(int c) { return (q_isalpha(c) || q_isdigit(c)); } static inline int q_isblank(int c) { return (c == ' ' || c == '\t'); } static inline int q_isspace(int c) { switch(c) { case ' ': case '\t': case '\n': case '\r': case '\f': case '\v': return 1; } return 0; } static inline int q_isgraph(int c) { return (c > 0x20 && c <= 0x7e); } static inline int q_isprint(int c) { return (c >= 0x20 && c <= 0x7e); } static inline int q_toascii(int c) { return (c & 0x7f); } static inline int q_tolower(int c) { return ((q_isupper(c)) ? (c | ('a' - 'A')) : c); } static inline int q_toupper(int c) { return ((q_islower(c)) ? (c & ~('a' - 'A')) : c); } #endif /* Q_CTYPE_H */ common/q_endian.c000066400000000000000000000063561444734033100143050ustar00rootroot00000000000000/* q_endian.c -- byte order functions * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2007-2016 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include "compiler.h" #include "q_endian.h" #if ENDIAN_RUNTIME_DETECT #define __byteswap_func static #else #define __byteswap_func #endif int host_byteorder; int host_bigendian; /* qboolean */ FUNC_NOINLINE FUNC_NOCLONE uint32_t get_0x12345678 (void) { return 0x12345678; /* U N I X */ } int DetectByteorder (void) { volatile union { uint32_t i; uint8_t c[4]; } bint; bint.i = get_0x12345678 (); /* BE_ORDER: 12 34 56 78 U N I X LE_ORDER: 78 56 34 12 X I N U PDP_ORDER: 34 12 78 56 N U X I */ if (bint.c[0] == 0x12) return BIG_ENDIAN; if (bint.c[0] == 0x78) return LITTLE_ENDIAN; if (bint.c[0] == 0x34) return PDP_ENDIAN; return -1; } #if ENDIAN_RUNTIME_DETECT || !(defined(USE_M68K_ASM) || (defined(__VBCC__) && defined(__M68K__))) __byteswap_func short ShortSwap (short l) { unsigned char b1, b2; b1 = l & 255; b2 = (l>>8) & 255; return (b1<<8) + b2; } __byteswap_func int LongSwap (int l) { unsigned char b1, b2, b3, b4; b1 = l & 255; b2 = (l>>8 ) & 255; b3 = (l>>16) & 255; b4 = (l>>24) & 255; return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; } __byteswap_func float FloatSwap (float f) { union { float f; unsigned char b[4]; } dat1, dat2; dat1.f = f; dat2.b[0] = dat1.b[3]; dat2.b[1] = dat1.b[2]; dat2.b[2] = dat1.b[1]; dat2.b[3] = dat1.b[0]; return dat2.f; } #endif #if ENDIAN_RUNTIME_DETECT __byteswap_func short ShortNoSwap (short l) { return l; } __byteswap_func int LongNoSwap (int l) { return l; } __byteswap_func float FloatNoSwap (float f) { return f; } short (*BigShort) (short); short (*LittleShort) (short); int (*BigLong) (int); int (*LittleLong) (int); float (*BigFloat) (float); float (*LittleFloat) (float); #endif /* ENDIAN_RUNTIME_DETECT */ void ByteOrder_Init (void) { host_byteorder = DetectByteorder (); host_bigendian = (host_byteorder == BIG_ENDIAN); #if ENDIAN_RUNTIME_DETECT switch (host_byteorder) { case BIG_ENDIAN: BigShort = ShortNoSwap; LittleShort = ShortSwap; BigLong = LongNoSwap; LittleLong = LongSwap; BigFloat = FloatNoSwap; LittleFloat = FloatSwap; break; case LITTLE_ENDIAN: BigShort = ShortSwap; LittleShort = ShortNoSwap; BigLong = LongSwap; LittleLong = LongNoSwap; BigFloat = FloatSwap; LittleFloat = FloatNoSwap; break; default: break; } #endif /* ENDIAN_RUNTIME_DETECT */ } common/q_endian.h000066400000000000000000000212431444734033100143020ustar00rootroot00000000000000/* q_endian.h -- endianness detection / handling * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2007-2016 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef QENDIAN_H #define QENDIAN_H /* to detect byte order at runtime instead of compile time, define as 1 */ #undef ENDIAN_RUNTIME_DETECT #define ENDIAN_RUNTIME_DETECT 0 /* try the system headers first: with BSD and derivatives, * sys/types.h can pull in the correct header and defs. */ #if !defined __VBCC__ #include #endif #if !defined(BYTE_ORDER) /* include more if it didn't work */ # if defined(__linux__) || defined(__linux) # include # elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \ defined(__FreeBSD_kernel__) /* Debian GNU/kFreeBSD */ || \ (defined(__APPLE__) && defined(__MACH__)) /* Mac OS X */ || \ defined(__DragonFly__) # include # elif defined(__sun) || defined(__svr4__) # include # elif defined(_AIX) # include # elif defined(sgi) # include # elif defined(__MINGW32__) # include # elif defined(__DJGPP__) # include # elif defined(__EMX__) # include # elif defined(__OS2__) && defined(__WATCOMC__) # include # elif defined(__MORPHOS__) # include # elif defined(__amigaos__) && defined(__NEWLIB__) # include # elif defined(__amigaos__) && defined(__CLIB2__) # if !defined(__amigaos4__) # define __NO_NET_API /* have netinclude/ instead */ # endif # include # endif #endif /* endian includes */ /* alternative macro names: */ #if defined(__BYTE_ORDER) && !defined(BYTE_ORDER) #define BYTE_ORDER __BYTE_ORDER #endif #if defined(__LITTLE_ENDIAN) && !defined(LITTLE_ENDIAN) #define LITTLE_ENDIAN __LITTLE_ENDIAN #endif #if defined(__BIG_ENDIAN) && !defined(BIG_ENDIAN) #define BIG_ENDIAN __BIG_ENDIAN #endif /* new gcc and clang versions pre-define the following: */ #if defined(__BYTE_ORDER__) && !defined(BYTE_ORDER) #define BYTE_ORDER __BYTE_ORDER__ #endif #if defined(__ORDER_LITTLE_ENDIAN__) && !defined(LITTLE_ENDIAN) #define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ #endif #if defined(__ORDER_BIG_ENDIAN__) && !defined(BIG_ENDIAN) #define BIG_ENDIAN __ORDER_BIG_ENDIAN__ #endif #if !defined(PDP_ENDIAN) #if defined(__ORDER_PDP_ENDIAN__) #define PDP_ENDIAN __ORDER_PDP_ENDIAN__ #elif defined(__PDP_ENDIAN) #define PDP_ENDIAN __PDP_ENDIAN #else #define PDP_ENDIAN 3412 #endif #endif /* NUXI endian -- unsupported */ #if defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN) # if (BYTE_ORDER != LITTLE_ENDIAN) && (BYTE_ORDER != BIG_ENDIAN) # error "Unsupported endianness." # endif #else /* one of the definitions is mising. */ # undef BYTE_ORDER # undef LITTLE_ENDIAN # undef BIG_ENDIAN # define LITTLE_ENDIAN 1234 # define BIG_ENDIAN 4321 #endif /* byte order defs */ #if !defined(BYTE_ORDER) /* good assumptions: */ /* try the compiler-predefined endianness macros. * Ref.: https://sf.net/p/predef/wiki/Endianness/ */ # if defined(__BIG_ENDIAN__) || defined(_BIG_ENDIAN) # define BYTE_ORDER BIG_ENDIAN # elif defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \ defined(_MIPSEB) || defined(__MIPSEB) || defined(__MIPSEB__) # define BYTE_ORDER BIG_ENDIAN # elif defined(__LITTLE_ENDIAN__) || defined(_LITTLE_ENDIAN) # define BYTE_ORDER LITTLE_ENDIAN # elif defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || \ defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) # define BYTE_ORDER LITTLE_ENDIAN /* assumptions based on OS and/or architecture macros. Some refs: * predef/other/endian.h from the boost library * http://labs.hoffmanlabs.com/node/544 * https://blogs.msdn.com/larryosterman/archive/2005/06/07/426334.aspx */ # elif defined(__i386) || defined(__i386__) || defined(__386__) || defined(_M_IX86) || defined(__I386__) # define BYTE_ORDER LITTLE_ENDIAN /* any x86 */ # elif defined(__amd64) || defined(__x86_64__) || defined(_M_X64) # define BYTE_ORDER LITTLE_ENDIAN /* any x64 */ # elif defined(__mc68000__) || defined(__M68K__) || defined(__m68k__) || defined(__MC68K__) # define BYTE_ORDER BIG_ENDIAN /* Motorola 68k */ # elif defined(__sun) || defined(__svr4__) /* solaris */ # if defined(_LITTLE_ENDIAN) /* x86 */ # define BYTE_ORDER LITTLE_ENDIAN # elif defined(_BIG_ENDIAN) /* sparc */ # define BYTE_ORDER BIG_ENDIAN # endif # elif defined(__hppa) || defined(__hppa__) # define BYTE_ORDER BIG_ENDIAN /* PARISC */ # elif defined(__sparc) || defined(__sparc__) # define BYTE_ORDER BIG_ENDIAN /* SPARC -- cf. boost/predef/other/endian.h */ # elif defined(__DJGPP__) || defined(__MSDOS__) || defined(__DOS__) || defined(_DOS) # define BYTE_ORDER LITTLE_ENDIAN /* DOS */ # elif defined(__OS2__) || defined(__EMX__) # define BYTE_ORDER LITTLE_ENDIAN /* OS2 */ # elif defined(_WIN32) || defined(__WIN32__) || defined(_WIN64) || defined(__NT__) || defined(_Windows) # define BYTE_ORDER LITTLE_ENDIAN /* Windows */ # endif #endif /* good assumptions */ #if !defined(BYTE_ORDER) /* [un]safe assumptions: */ /* assumptions based on OS and/or architecture macros. * proceed carefully: many of these are bi-endian CPUs. */ # if defined(__ppc__) || defined(__powerpc__) || defined(__POWERPC__) || defined(__PPC__) # define BYTE_ORDER BIG_ENDIAN # elif defined(__alpha__) || defined(__alpha) # define BYTE_ORDER LITTLE_ENDIAN # elif defined(__mips__) || defined(__MIPS__) # define BYTE_ORDER BIG_ENDIAN # endif # if defined(BYTE_ORDER) && 0 /* change to 1 to for warnings */ # if (BYTE_ORDER == LITTLE_ENDIAN) # warning "Using LIL endian as a SAFE default." # else /*(BYTE_ORDER == BIG_ENDIAN)*/ # warning "Using BIG endian as a SAFE default." # endif # endif #endif /* [un]safe assumptions */ #if !ENDIAN_RUNTIME_DETECT #if !defined(BYTE_ORDER) # error Could not determine BYTE_ORDER #endif /* for autotools compatibility */ #if (BYTE_ORDER == BIG_ENDIAN) && !defined(WORDS_BIGENDIAN) # define WORDS_BIGENDIAN 1 #endif #if (BYTE_ORDER != BIG_ENDIAN) && defined(WORDS_BIGENDIAN) #error WORDS_BIGENDIAN defined for non-BIG_ENDIAN #endif #endif /* ! ENDIAN_RUNTIME_DETECT */ #ifdef __cplusplus extern "C" { #endif extern int host_byteorder; extern int host_bigendian; /* bool */ extern int DetectByteorder (void); extern void ByteOrder_Init (void); /* byte swapping. most times we want to convert to * little endian: our data files are written in LE * format. sometimes, such as when writing to net, * we also convert to big endian. */ #if ENDIAN_RUNTIME_DETECT extern short (*BigShort) (short); extern short (*LittleShort) (short); extern int (*BigLong) (int); extern int (*LittleLong) (int); extern float (*BigFloat) (float); extern float (*LittleFloat) (float); #else /* ! ENDIAN_RUNTIME_DETECT */ extern short ShortSwap (short); extern int LongSwap (int); extern float FloatSwap (float); #if defined(__VBCC__) && defined(__M68K__) int m68k_swap32(__reg("d0") int) = "\trol.w\t#8,d0\n" "\tswap\td0\n" "\trol.w\t#8,d0"; short m68k_swap16(__reg("d0") short) = "\trol.w\t#8,d0"; #define LongSwap(s) m68k_swap32((s)) #define ShortSwap(s) m68k_swap16((s)) float m68k_swap32f(__reg("fp0") float) = "\tfmove.s\tfp0,d0\n" "\trol.w\t#8,d0\n" "\tswap\td0\n" "\trol.w\t#8,d0\n" "\tfmove.s\td0,fp0"; #define FloatSwap(s) m68k_swap32f((s)) #endif #if (BYTE_ORDER == BIG_ENDIAN) #define BigShort(s) (s) #define LittleShort(s) ShortSwap((s)) #define BigLong(l) (l) #define LittleLong(l) LongSwap((l)) #define BigFloat(f) (f) #define LittleFloat(f) FloatSwap((f)) #else /* BYTE_ORDER == LITTLE_ENDIAN */ #define BigShort(s) ShortSwap((s)) #define LittleShort(s) (s) #define BigLong(l) LongSwap((l)) #define LittleLong(l) (l) #define BigFloat(f) FloatSwap((f)) #define LittleFloat(f) (f) #endif /* swap macros */ #endif /* runtime det */ #ifdef __cplusplus } #endif /* */ #endif /* QENDIAN_H */ common/q_stdinc.h000066400000000000000000000134161444734033100143330ustar00rootroot00000000000000/* q_stdinc.h - includes the minimum necessary stdc headers, * defines common and / or missing types. * * NOTE: for net stuff use net_sys.h, * for byte order use q_endian.h, * for math stuff use mathlib.h, * for locale-insensitive ctype.h functions use q_ctype.h. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2007-2011 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __QSTDINC_H #define __QSTDINC_H #include #include #include #if !(defined(_WIN32)||defined(__DOS__)||defined(MSDOS)||defined(__MSDOS__)||defined(__OS2__)) #include #endif #include /* NOTES on TYPE SIZES: Quake/Hexen II engine relied on 32 bit int type size with ILP32 (not LP32) model in mind. We now support LP64 and LLP64, too. We expect: sizeof (char) == 1 sizeof (short) == 2 sizeof (int) == 4 sizeof (float) == 4 sizeof (long) == 4 / 8 sizeof (pointer *) == 4 / 8 For this, we need stdint.h (or inttypes.h) FIXME: On some platforms, only inttypes.h is available. FIXME: Properly replace certain short and int usage with int16_t and int32_t. */ #include "q_stdint.h" #include #include #include /*==========================================================================*/ #ifndef NULL #if defined(__cplusplus) #define NULL 0 #else #define NULL ((void *)0) #endif #endif #define Q_MAXCHAR ((char)0x7f) #define Q_MAXSHORT ((short)0x7fff) #define Q_MAXINT ((int)0x7fffffff) #define Q_MAXLONG ((int)0x7fffffff) #define Q_MAXFLOAT ((int)0x7fffffff) #define Q_MINCHAR ((char)0x80) #define Q_MINSHORT ((short)0x8000) #define Q_MININT ((int)0x80000000) #define Q_MINLONG ((int)0x80000000) #define Q_MINFLOAT ((int)0x7fffffff) /* Make sure the types really have the right * sizes: These macros are from SDL headers. */ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) #define COMPILE_TIME_ASSERT(name, x) _Static_assert(x, #x) #elif defined(__cplusplus) && (__cplusplus >= 201103L) #define COMPILE_TIME_ASSERT(name, x) static_assert(x, #x) #else /* universal, but may trigger -Wunused-local-typedefs */ #define COMPILE_TIME_ASSERT(name, x) \ typedef int dummy_ ## name[(x) * 2 - 1] #endif COMPILE_TIME_ASSERT(char, sizeof(char) == 1); COMPILE_TIME_ASSERT(float, sizeof(float) == 4); COMPILE_TIME_ASSERT(long, sizeof(long) >= 4); COMPILE_TIME_ASSERT(int, sizeof(int) == 4); COMPILE_TIME_ASSERT(short, sizeof(short) == 2); /* make sure enums are the size of ints for structure packing */ typedef enum { THE_DUMMY_VALUE } THE_DUMMY_ENUM; COMPILE_TIME_ASSERT(enum, sizeof(THE_DUMMY_ENUM) == sizeof(int)); /* Provide a substitute for offsetof() if we don't have one. * This variant works on most (but not *all*) systems... */ #ifndef offsetof #define offsetof(t,m) ((intptr_t)&(((t *)0)->m)) #endif /*==========================================================================*/ typedef unsigned char byte; #undef true #undef false #if defined(__cplusplus) /* some structures have qboolean members and the x86 asm code expect * those members to be 4 bytes long. therefore, qboolean must be 32 * bits and it can NOT be binary compatible with the 8 bit C++ bool. */ typedef int qboolean; COMPILE_TIME_ASSERT(falsehood, (0 == false)); COMPILE_TIME_ASSERT(truth, (1 == true)); #else typedef enum { false = 0, true = 1 } qboolean; COMPILE_TIME_ASSERT(falsehood, ((1 != 1) == false)); COMPILE_TIME_ASSERT(truth, ((1 == 1) == true)); #endif COMPILE_TIME_ASSERT(qboolean, sizeof(qboolean) == 4); /*==========================================================================*/ /* math */ #if defined(DOUBLEVEC_T) typedef double vec_t; #else /* float type */ typedef float vec_t; #endif /* DOUBLEVEC_T */ typedef vec_t vec3_t[3]; typedef vec_t vec4_t[4]; typedef vec_t vec5_t[5]; typedef int fixed4_t; typedef int fixed8_t; typedef int fixed16_t; /*==========================================================================*/ /* MAX_OSPATH (max length of a filesystem pathname, i.e. PATH_MAX) * Note: See GNU Hurd and others' notes about brokenness of this: * http://www.gnu.org/software/hurd/community/gsoc/project_ideas/maxpath.html * http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html */ #if defined(__DJGPP__) || defined(_MSDOS) || defined(__MSDOS__) || defined(__DOS__) /* 256 is more than enough */ #if !defined(PATH_MAX) #define PATH_MAX 256 #endif #define MAX_OSPATH 256 #else #if !defined(PATH_MAX) /* equivalent values? */ #if defined(MAXPATHLEN) #define PATH_MAX MAXPATHLEN #elif defined(_WIN32) && defined(_MAX_PATH) #define PATH_MAX _MAX_PATH #elif defined(_WIN32) && defined(MAX_PATH) #define PATH_MAX MAX_PATH #elif defined(__OS2__) && defined(CCHMAXPATH) #define PATH_MAX CCHMAXPATH #else /* fallback */ #define PATH_MAX 1024 #endif #endif /* PATH_MAX */ #define MAX_OSPATH PATH_MAX #endif /* MAX_OSPATH */ /*==========================================================================*/ /* missing types: */ #if defined(_MSC_VER) typedef ptrdiff_t ssize_t; #endif /*==========================================================================*/ #endif /* __QSTDINC_H */ common/q_stdint.h000066400000000000000000000013301444734033100143440ustar00rootroot00000000000000/* q_stdint.h: include a proper stdint.h header */ #ifndef __QSTDINT_H #define __QSTDINT_H #if defined(_MSC_VER) && (_MSC_VER < 1600) /* MS Visual Studio provides stdint.h only starting with * version 2010. Even in VS2010, there is no inttypes.h.. */ #include "msinttypes/stdint.h" #elif defined(__DJGPP__) && \ (!defined(__DJGPP_MINOR__) || __DJGPP_MINOR__ < 4) /* DJGPP < v2.04 doesn't have stdint.h and inttypes.h. */ /* to ensure a proper version check, include stdio.h * or go32.h which includes sys/version.h since djgpp * versions >= 2.02 and defines __DJGPP_MINOR__ */ #include "djstdint/stdint.h" #else /* assume presence of a stdint.h from the SDK. */ #include #endif #endif /* __QSTDINT_H */ common/qsnprint.c000066400000000000000000000040021444734033100143670ustar00rootroot00000000000000/* qsnprint.c -- (v)snprintf wrappers * Copyright (C) 2007 O. Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "arch_def.h" #include "compiler.h" #include "qsnprint.h" /* platform dependant (v)snprintf function names: */ #if defined(PLATFORM_WINDOWS) #define snprintf_func _snprintf #define vsnprintf_func _vsnprintf #else #define snprintf_func snprintf #define vsnprintf_func vsnprintf #endif #if defined(__DJGPP__) && \ (!defined(__DJGPP_MINOR__) || __DJGPP_MINOR__ < 4) /* DJGPP < v2.04 doesn't have [v]snprintf(). */ /* to ensure a proper version check, include stdio.h * or go32.h which includes sys/version.h since djgpp * versions >= 2.02 and defines __DJGPP_MINOR__ */ #include "djlib/vsnprntf.c" #endif /* __DJGPP_MINOR__ < 4 */ int q_vsnprintf(char *str, size_t size, const char *format, va_list args) { int ret; ret = vsnprintf_func (str, size, format, args); if (ret < 0) ret = (int)size; if (size == 0) /* no buffer */ return ret; if ((size_t)ret >= size) str[size - 1] = '\0'; return ret; } int q_snprintf (char *str, size_t size, const char *format, ...) { int ret; va_list argptr; va_start (argptr, format); ret = q_vsnprintf (str, size, format, argptr); va_end (argptr); return ret; } common/qsnprint.h000066400000000000000000000022041444734033100143760ustar00rootroot00000000000000/* qsnprint.h-- (v)snprintf wrappers * Copyright (C) 2007 O. Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __Q_SNPRINF_H #define __Q_SNPRINF_H #ifdef __cplusplus extern "C" { #endif extern int q_snprintf (char *str, size_t size, const char *format, ...) FUNC_PRINTF(3,4); extern int q_vsnprintf(char *str, size_t size, const char *format, va_list args) FUNC_PRINTF(3,0); #ifdef __cplusplus } #endif #endif /* __Q_SNPRINF_H */ common/spritegn.h000066400000000000000000000056271444734033100143670ustar00rootroot00000000000000/* * spritegn.h * header file for sprite generation program * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __SPRITEGEN_H #define __SPRITEGEN_H // ********************************************************** // * This file must be identical in the spritegen directory * // * and in the Quake directory, because it's used to * // * pass data from one to the other via .spr files. * // ********************************************************** //------------------------------------------------------- // This program generates .spr sprite package files. // The format of the files is as follows: // // dsprite_t file header structure // // // dspriteframe_t frame header structure // sprite bitmap // // dspriteframe_t frame header structure // sprite bitmap // //------------------------------------------------------- #ifdef INCLUDELIBS #include #include #include #include #include "cmdlib.h" #include "scriplib.h" #include "dictlib.h" #include "trilib.h" #include "lbmlib.h" #include "mathlib.h" #endif #define SPRITE_VERSION 1 // Little-endian "IDSP" #define IDSPRITEHEADER (('P'<<24)+('S'<<16)+('D'<<8)+'I') // must match definition in genmodel.h #ifndef SYNCTYPE_T #define SYNCTYPE_T typedef enum { ST_SYNC = 0, ST_RAND } synctype_t; #endif // TODO: shorten these? typedef struct { int ident; int version; int type; float boundingradius; int width; int height; int numframes; float beamlength; synctype_t synctype; } dsprite_t; #define SPR_VP_PARALLEL_UPRIGHT 0 #define SPR_FACING_UPRIGHT 1 #define SPR_VP_PARALLEL 2 #define SPR_ORIENTED 3 #define SPR_VP_PARALLEL_ORIENTED 4 typedef struct { int origin[2]; int width; int height; } dspriteframe_t; typedef struct { int numframes; } dspritegroup_t; typedef struct { float interval; } dspriteinterval_t; typedef enum { SPR_SINGLE = 0, SPR_GROUP } spriteframetype_t; typedef struct { spriteframetype_t type; } dspriteframetype_t; #endif /* __SPRITEGEN_H */ common/strl_fn.h000066400000000000000000000006211444734033100141700ustar00rootroot00000000000000/* strl_fn.h - header file for BSD strlcat and strlcpy */ #ifndef __STRLFUNCS_H #define __STRLFUNCS_H /* use our own copies of strlcpy and strlcat taken from OpenBSD */ #ifdef __cplusplus extern "C" { #endif extern size_t q_strlcpy (char *dst, const char *src, size_t size); extern size_t q_strlcat (char *dst, const char *src, size_t size); #ifdef __cplusplus } #endif #endif /* __STRLFUNCS_H */ common/strlcat.c000066400000000000000000000036141444734033100141750ustar00rootroot00000000000000/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "strl_fn.h" /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless siz <= strlen(dst)). * Returns strlen(src) + MIN(siz, strlen(initial dst)). * If retval >= siz, truncation occurred. */ size_t q_strlcat (char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = siz - dlen; if (n == 0) return(dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return(dlen + (s - src)); /* count does not include NUL */ } #ifdef __DJGPP__ /* override stock DJGPP versions of strlc?? */ #ifdef __cplusplus extern "C" { #endif size_t strlcat (char *, const char *, size_t) __attribute__((alias("q_strlcat"))); #ifdef __cplusplus } #endif #endif common/strlcpy.c000066400000000000000000000034221444734033100142160ustar00rootroot00000000000000/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "strl_fn.h" /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t q_strlcpy (char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0) { while (--n != 0) { if ((*d++ = *s++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } #ifdef __DJGPP__ /* override stock DJGPP versions of strlc?? */ #ifdef __cplusplus extern "C" { #endif size_t strlcpy (char *, const char *, size_t) __attribute__((alias("q_strlcpy"))); #ifdef __cplusplus } #endif #endif common/swap_68k.s000066400000000000000000000013451444734033100142020ustar00rootroot00000000000000; Quake for AMIGA ; byte swap assembler implementations by Frank Wille XDEF _ShortSwap XDEF _LongSwap XDEF _FloatSwap ; vasm extensions machine 68020 fpu 1 ; short _ShortSwap (short l) ; swap word (LE->BE) ; cnop 0,4 _ShortSwap rsreset rs.l 1 rs.w 1 .data rs.w 1 move .data(sp),d0 ror #8,d0 rts ; int _LongSwap (int l) ; swap longword (LE->BE) ; cnop 0,4 _LongSwap rsreset rs.l 1 .data rs.l 1 move.l .data(sp),d0 ror #8,d0 swap d0 ror #8,d0 rts ; float _FloatSwap (float f) ; swap float (LE->BE) ; cnop 0,4 _FloatSwap rsreset rs.l 1 .data rs.s 1 move.l .data(sp),d0 ror #8,d0 swap d0 ror #8,d0 fmove.s d0,fp0 rts docs/000077500000000000000000000000001444734033100120115ustar00rootroot00000000000000docs/ABOUT000066400000000000000000000013641444734033100126120ustar00rootroot00000000000000Hexen II: Hammer of Thyrion (uHexen2) - version 1.5.10 http://uhexen2.sourceforge.net/ http://sourceforge.net/projects/uhexen2/ Hammer of Thyrion is a cross-platform port of Raven Software's Hexen II source to support Linux, FreeBSD, OpenBSD, QNX, other unices, Mac OS X, Windows, and DOS. It is based on an old linux port Anvil of Thyrion. Hexen II was created by Raven Software Corp., published by Id Software Inc and distributed by Activision, Inc. Hexen II and Portal of Praevus are trademarks of Raven Software Corporation. Hammer of Thyrion is *not* made, and is *not* supported by any of these software companies in any way. Copyright --------- This code is copyright the repective authors and it is released under the GNU Public License v2. docs/AUTHORS000066400000000000000000000050431444734033100130630ustar00rootroot00000000000000Hexen II: Hammer of Thyrion (uHexen2) http://uhexen2.sourceforge.net/ http://sourceforge.net/projects/uhexen2/ Maintainers / Coding: --------------------- O. Sezer Original Engine: ---------------- id Software - http://www.idsoftware.com/ Raven Software - http://www.ravensoft.com/ Initial port to Linux (the Anvil of Thyrion engine) : ----------------------------------------------------- Dan Olson Clément Bourdarias Contributors (coding): ---------------------- Steven Atkinson Levent Yavas Thomas Freundt Sander van Dijk Szilárd Biró Juraj Styk Siggi Eric Wasylishen Michal Wozniak Keith Rozett Chris Gransden Jacques Krige - https://www.jacqueskrige.site/ Julien Langer Ricardo Garcia Jonathan Bergeron Spike Shanjaq Astraljam Credits: Miscellaneous Quake/Hexen II projects and/or their authors that we used code from: ----------------------------------------------------- QuakeForge - http://sourceforge.net/projects/quake/ Bill Currie ioquake3 - http://ioquake3.org/ QIP project - https://www.quake-info-pool.net/ Matthias Buecher Kevin Shanahan Forest Hale "Pa3PyX" Juraj Styk Eric Hobbs Ryan C. Gordon Michael Hiney ("MH" - parts of the DIB code from mhquake) Mark Olsen Marc Allaire More contributors (documentation & testing): -------------------------------------------- plusminus Davide Cendron Anthony Williams Frank Sapone Oleg Moiseyev Ronald Eijkman Sean Stach "VB" Burçak Öztürk Dustin Samko If you think your name is missing and should be mentioned here, please contact us so we can add it. docs/BUGS000066400000000000000000000034001444734033100124710ustar00rootroot00000000000000Hexen II: Hammer of Thyrion (uHexen2) Known issues: ------------------------------------- - Unix (SDL) client: Hitting the ESC key to open the menu several times or opening and closing the game console several times may result in a freeze or an endless loop. This is NOT a uhexen2 bug, but an SDL-1.2 bug in its X11 backend: https://github.com/libsdl-org/SDL-1.2/issues/744 The issue triggers if you have an IME running and XMODIFIERS is set. The bug is already fixed in the mainstream SDL repo: https://github.com/libsdl-org/SDL-1.2/commit/0332e2bb18dc68d6892c3b653b2547afe323854b To solve the problem: either recompile SDL to incorporate the fix, or unset XMODIFIERS to work around it. - h2ded (dedicated server) coop mode issues: sf.net/p/uhexen2/bugs/52/ - Skull wizards may become non-solid and invincible after they blink away and come back. - Dark Pharaoh's staff would disappear after pushing the switch that lowers it. - Loading saves may often result in bugs. Issues don't seem to happen with "h2 -dedicated". To be analyzed. - HexenWorld: Coop mode is broken. Known problem since Raven's latest beta versions. - OpenGL: Shadows have always been broken (they need a major rework to fix) and are disabled by default. - 3dfx Voodoo: Palettized textures are broken with the old unmodified Mesa 3.x drivers. The new Mesa-6.2 drivers work fine. Windows people should use the latest "MesaFX" driver. - 3dfx Voodoo: May not work correctly with multitexturing when running with gl_luminance lightmaps. This is a 3dfx limitation. - 3dfx Voodoo, Windows: 3dfx minigl drivers shall not work properly, because their glGenTextures() implementation is broken and we rely on that function. Use 3dfx OpenGL ICD or "MesaFX" drivers, instead. docs/Features000066400000000000000000000060131444734033100135120ustar00rootroot00000000000000Hexen II: Hammer of Thyrion (uHexen2) Features: ------------------------------------- * Support for Hexen II, Hexen II demo, Portal of Praevus mission pack, and HexenWorld. * Supported operating systems / platforms: Linux, FreeBSD, Mac OS X, Windows, AROS, MorphOS, DOS, and OS/2. Any other platforms with SDL support should also work fine. Known to build and run just fine for QNX, and Nokia internet tablets N770, N800 and N810 using Maemo. There are separate ports to AmigaOS4, RISC OS, PalmOS, GP2X and Pandora. * Well tested on both 32 bit and 64 bit platforms. Well tested on both little endian and big endian platforms. * In-game resolution changing for both opengl and software renderers. Instant fullscreen/windowed toggling on X11 (unix). * BSP2 map format support. * Widescreen FOV (field of view) support and automatic Hor+ FOV scaling based on resoluton in both opengl and software renderer. * OpenGL enhancements including multitexturing, glow effects, working brightness control, text and hud size changing on the fly, translucent console background, console background stretching, colored lights and support for external lit files, anisotropic texture filtering, fixes for texture cache and model mesh cache mismatches. * Fully network-compatible with Raven's 1.11 windows version as well as with other Hexen II ports. Can load and play games saved with Raven's 1.11 windows version. * The same binary handles both original hexen2 and the mission pack. * The same binary transparently handles progs.dat files of both v6 and v7 specs. * The same hexen2 binary transparently handles all v1.03-v1.11 (hexen2) and v1.12 (mission pack) style progs.dat files. * The same hexenworld binary transparently handles all v0.11, v0.14 and v0.15 style progs.dat files. * Fullscreen intermissions and help dialogs. * MouseWheel support. * Improvements and lots of fixes to the HexenC game code. * Improved sound support: A selection of three different sound drivers (ALSA, OSS and SDL) for Linux. FreeBSD is supported with OSS and SDL, OpenBSD and NetBSD are experimentally supported with sunaudio and SDL. * Support for ogg, mp3, opus, flac and wav external music files to be played instead of the original midi files. support for mod (tracker) music, including unreal's umx music files. * Working music volume controls and on the fly selection of CD or MIDI music. * Full user directories support under unix: all users have their own configuration files and game saves. * Improved console functionality (inline editing and tab completion), and a maplist command. * Ability to delete saved games from within the game. * Several hexen2 utilities (hcc: the hexenc compiler, and other mapping tools) are maintained and are being kept up to date. * Several hexenworld utilities (hwmaster: hexenworld master server, hwmquery: tool for querying hw master servers, hwrcon and hwterm: rcon tools) are maintained and are being kept up to date. * Countless bug fixes, security fixes, and other improvements. docs/README000066400000000000000000000676651444734033100127150ustar00rootroot00000000000000 Hexen II: Hammer of Thyrion (uHexen2) - v1.5.10 ----------------------------------------------- 1. Installation 1.1 Getting Started 1.2 Software Requirements 1.3 Running the Game 1.4 Sound and Music 1.5 Networking/Multiplayer 1.6 Game Saves and Configuration files 1.7 Fullscreen/Windowed modes and mouse 1.8 Joystick (gamepad) support 2. Known Problems 2.1 Sound Problems 2.2 Lines on Screen 2.3 Fluxbox 3. General Info 3.1 Mods 3.2 Game Console 3.3 Hardware Acceleration 3.4 Thanks 3.5 Links 4. Appendix ---------------------------------------------------------------- 1. INSTALLATION --------------- 1.1 Getting Started ------------------- Hexen II source and the Hammer of Thyrion source port are free, but the game itself is not: You must have an original copy of the game released by Raven Software and Activision. If you don't, then download the demo version. Installing the demo version doesn't require any media and/or any extra effort: our demo packages contain all requirements. To install the retail version of the game, you need your original cdroms: the windows discs released by Raven and Activision back in 1997. The less common "Xplosive" cdrom published in UK is also supported. To install the "Portal of Praevus" mission pack, you need your original mission pack cdrom. To install the Matrox m3D bundled (oem) version of the game (also known as "Continent of Blackmarsh"), you need your M3D_2 cdrom. Download the base binary tarball (e.g., hexen2-1.5.10.i586.tgz) and extract it. Copy the two files pak0.pak and pak1.pak from your cdrom into the "data1" directory in your own installation. The pak file names should be lower-cased, if not already. To fix the permissions: chmod 644 data1/pak*.pak Hexen II requires its game data to be updated to version 1.11, too. To patch the pak files, run the included "h2patch" program. Here are the md5sums, just in case: c9675191e75dd25a3b9ed81ee7e05eff data1/pak0.pak c2ac5b0640773eed9ebe1cda2eca2ad0 data1/pak1.pak If you have the Portal of Praevus mission pack, copy the pak3.pak file from your cdrom into the "portals" directory in your own installation. To fix the permissions: chmod 644 portals/pak* The mission pack data doesn't need patching. Here is the md5sum of pak3.pak, just in case: 77ae298dd0dcd16ab12f4a68067ff2c3 portals/pak3.pak If you want to install HexenWorld, download the HexenWorld binary package (e.g., hexenworld-1.5.10.i586.tgz) and extract it in the same directory you extracted the base uhexen2 tarball. HexenWorld data doesn't need patching. Here are the game binaries' names: hexen2 - Hexen II (software rendering) glhexen2 - Hexen II (opengl rendering) h2ded - Hexen II Dedicated server hwcl - HexenWorld Client (software rendering) glhwcl - HexenWorld Client (opengl rendering) hwsv - HexenWorld Server hwmaster - HexenWorld Master Server 1.2 Software Requirements ------------------------- SDL (min.: 1.2.4) : Simple DirectMedia Layer libraries. 1.2.7 and newer highly recommended. If run on systems with SDL < 1.2.6, anti-aliasing support will be auto- matically disabled. To compile your own binaries, you'll need a standard unix build environment. See the file COMPILE for details. 1.3 Running the Game: ---------------------- You can run the game from a terminal window: "cd" into the installation directory and run the binary. For example: cd /home/myname/hexen2 ./glhexen2 [possible options] The game starts in windowed mode by default: use the "-f" command line switch to start in fullscreen. To play the Portals of Praevus mission pack, use the "-portals" or "-h2mp" command line switch. Important command-line switches: -portals | -h2mp Run with Portal of Praevus mission pack support (only for registered version of Hexen2.) -f | -fullscreen Start fullscreen (can also be set from the menu) -w | -window Start windowed (can also be set from the menu) -g | -gllibrary Override the OpenGL library to use -width N Select screen width (can also use the menu) -height N Optional. Must be used with -width -bpp N Color depth for GL fullscreen mode -conwidth N Enables a bigger hud display and readable fonts at high resolutions. Valid conwidth values are values equal or less than the scr width above: Smaller the number, bigger the text. 640 is the sanest. Can also be adjusted from the menu. -nomouse Disables mouse usage in game -nojoy Disables joystick (gamepad) probing and usage -heapsize N Heapsize (memory to allocate, in KB) -fsaa N Enable N sample anti-aliasing (N: 0,2,4) (can also be set from the menu) -sync | -vsync Enable syncing with monitor refresh (GL, Nvidia) -lm_4 Set lightmap format to RGBA, which is actually the default format. RGBA lightmaps are required for colored lights. Can be set from the menu. -lm_1 Set lightmap format to LUMINANCE. (this was the old format that hexen2 originally used and it is a little faster than RGBA lightmaps). Can be set from the menu. -paltex Enable 8 bit (palettized) textures: saves video memory on *very* old low-memory 3D accelerators if the proper GL extension is supported. (can be set from the menu.) -nomtex Disable multitexture detection/usage -no3dfxgamma Disable special gamma support for Voodoo1/2/Rush (if compiled with 3dfx gamma hacks support.) -sndoss Use OSS for audio (default) -sndalsa Use ALSA for audio -sndsdl Use SDL for audio -sndbsd Use sunaudio (for openbsd, netbsd, sun) -ossdev OSS Audio device to use (default: /dev/dsp) -alsadev ALSA Audio device to use (default is "default") -sndspeed N Sampling rate of sound playback (eg 22050,44100) -cddev CD Audio device to use (default is /dev/cdrom for linux, /dev/acd0 for FreeBSD.) For windows, use a single drive letter, like: glh2 -cddev E -nocdaudio Disable cdrom music -nomidi Disable MIDI driver -s | -nosound Run the game without sound -nolan Disable networking (incompatible with -listen) -listen N Enable multiplayer with maximum N players -protocol N Run the server using protocol version N, instead of the default. Valid values: 18 (the old hexen2 v1.11 protocol), 19 (default, h2mp protocol). -port Change the default port. Default port is 26900 for hexen2, 26950 for hexenworld server, 26900 for hexenworld master server. hexenworld client always uses 26901. -localip
For Hexen2 only. Changes the ip address embedded in the response packets for the serverinfo and connect requests to use the cmdline-provided ip address. Server still binds to INADDR_ANY and it can see the broadcast requests. -ip
Enables the server admins to bind to a specific IP address on a multi-homed host. Note: using this option will prevent the us from receiving broadcast packets, therefore server discovery on the LAN will not work if the server is started this way. -bindip
Same as the -ip option -noifscan Hexen II only. Disables local address detection through network interface scan. -developer Enable developer mode early during init phase. -condebug | -debuglog Logs the console output. -devlog Enable full logging even when not in developer mode. -v | -version Display the game version -h | -help Display short help message If you wish to brighten the display, simply use the game's menu entry. In case that it doesn't work for you, you can use the "xgamma" utility: Here is the script I use for xgamma: xgamma -q -gamma 1.3; # brighten the display ./glhexen2 xgamma -q -gamma 1; # restore old brightness 3dfx Voodoo1 and Voodoo2 users can employ the -3dfxgamma command line switch to activate native 3dfx gamma controls (remember to enable that option in the Makefile when compiling). 1.4 Sound and Music ------------------- uHexen2 includes several choices of sound drivers for linux/unix users: SDL, OSS and ALSA. The engine tries them and uses the first one which successfully initializes. You can make the engine to use only a specific driver by command line arguments, too. a. SDL sound code ----------------- SDL audio isolates you from many compatibility issues and is usable on most Linux distributions. The "-sndsdl" command line switch tells the game engine to use SDL audio, but it isn't necessary because SDL audio is the default. ./glhexen2 -sndsdl [other possible arguments] If you want to force SDL to use a different driver backend than its default setting, you can export SDL_AUDIODRIVER=drv where "drv" may be alsa, dsp, pulse, esd or something similar depending on your own setup. (Note that SDL_AUDIODRIVER=esd can be laggy in my own experience.) b. OSS sound code ----------------- The OSS (Open Sound System) code is compatible with most of the Linux distributions and FreeBSD, but it is known not to work for a few people. The "-sndoss" command line switch tells the game engine to use OSS: ./glhexen2 -sndoss -sndspeed 44100 c. ALSA sound code ------------------ The ALSA driver is for Linux and is based on the ALSA 1.0.x libraries. Start the game with "-sndalsa" command line switch: ./glhexen2 -sndalsa -sndspeed 44100 If ALSA gives you trouble, try using "plughw:0" or "hw:0,0" instead of the default device. A lot of ALSA installations have a misconfigured default device. Example: ./glhexen2 -sndalsa -alsadev plughw:0 -sndspeed 44100 d. BSD/SUN sound code --------------------- The sunaudio driver can be used in combination with our experimental OpenBSD and NetBSD support. The "-sndbsd" command line switch tells the game engine to use sunaudio. If you can't get sound working, the "-nosound" option will disable it, send a bug report on the project page. 1.4.1 Midi and Music support ---------------------------- On linux/unix, midi background music is implemented using timidity, so, you'll need GUS-compatible instrument patch files along with a properly configured timidity.cfg. You can get a complete set of GUS patches by downloading the timidity_patches.tar.gz from our site. Extract it into your uHexen2 installation directory and midi should be ready to play: http://sourceforge.net/project/downloading.php?group_id=124987&filename=timidity_patches.tar.gz Note to the curious: Configuration file timidity.cfg is searched first under the user directory, i.e.: ~/.hexen2, then under the installation directory, then under the common system locations /etc, /etc/timidity, /usr/share/timidity, /usr/local/share/timidity, /usr/local/lib/timidity in this order. Full absolute path of timidity.cfg can be specified by setting the TIMIDITY_CFG environment variable as an override too, e.g.: export TIMIDITY_CFG=/usr/local/share/timidity/timidity.cfg Hammer of Thyrion supports OGG, MP3 and WAV external music files to be played instead of the original midi files, so you can enjoy the hexen2 music in good quality like they are from the hexen2 cdrom audio tracks. Ogg playback requires libogg and libvorbis, mp3 playback requires libmad or libmpg123 depending on your compile-time decision (see the Makefile). See README.music for more information. 1.5 Networking / Multiplayer ---------------------------- Since version 1.4.2 (engine version 1.19), Hammer of Thyrion binaries are fully compatible Raven's Windows Hexen II-1.11 binaries. When run as a client, it can connect to Raven-1.11 servers and play just fine. It can playback the demos created with 1.11 version, as well. On the other hand if a 1.11 client wants to connect to a uhexen2 server, the server must be started with "-protocol 18" command line argument. There is no network compatibility with Hexen II protocols older than 18. Mission pack is, of course, fully network-compatible across platforms. As for the other Hexen II ports, uHexen2 is compatible with them, too. 1.6 Game Saves and Configuration files -------------------------------------- Your game saves and configuration files are stored under .hexen2 in your home folder: ~/.hexen2 (for demo version, it's ~/.hexen2demo) Saves and configs for Hexen2 are stored under ~/.hexen2/data1 For the Mission Pack, they are stored under ~/.hexen2/portals 1.7 Fullscreen/Windowed modes and mouse --------------------------------------- While playing the game, you can switch between fullscreen and windowed modes by pressing ALT + Enter combination. Alternatively, you can use the relevant menu option. The last setting will be saved to the config and will be remembered by the game. You can also change the resolution from the menu system. Again, the last setting will be saved to the config and will be remembered by the game. The menu option "Use Mouse" enables/disables mouse usage on the fly. While in windowed mode, disabling the mouse ungrabs the mouse pointer, so that you can easily use your desktop. You can also ungrab/regrab the mouse pointer by pressing the CTRL + G key combination whenever you want. 1.8 Joystick (gamepad) support ------------------------------ Hammer of Thyrion supports joysticks (gamepads) through SDL. The feature can be enabled or disabled on the fly by setting joystick to 1 or 0 from the console. It is disabled by default. On the other hand, the -nojoy command line switch disables all joystick probing and usage. The "joy_index" cvar defines which joystick to use if more than one are attached to the computer. The default value of joy_index is 0, i.e. the first one. You can setup which axis to query for which movement or look by using the following cvars from the console. -1 means 'none', i.e. if you set any of them to -1, then the corresponding movement or look will not be queried on the joystick: joy_axisside Axis for left/right movement 0 joy_axisforward Axis for forward/backward movement 1 joy_axisyaw Axis for looking left/right 2 joy_axispitch Axis for looking up/down 3 joy_axisup Axis for up/down movement. -1 (none) The following cvars the axis deadzone tolerance values for the above, all defaulting to 0. suggested values range between 0 to 0.01: joy_deadzoneside, joy_deadzoneforward, joy_deadzoneyaw, joy_deadzonepitch, joy_deadzoneup The following cvars are the axis movement multiplier (sensitivity) for the above, all defaulting to 1: joy_sensitivityside, joy_sensitivityforward, joy_sensitivityyaw, joy_sensitivitypitch, joy_sensitivityup ---------------------------------------------------------------- 2. KNOWN PROBLEMS ----------------- 2.1 Sound Problems ------------------ First, we recommend using 44100 or 48000 as the sampling rate with recent sound chipsets: use a command line argument like "-sndspeed 44100". If you have problems making sound to work correctly, using the arts sound server may help you: artsd & ; artsdsp -m glhexen2 -sndoss You may notice some lag in sound but if all fails this may be a good enough workaround. We have reports that some i8x0 sound chips not working correctly with hexen2, and this is overcome either by using alsa driver of hexen2 at 48000 sampling rate, or by the arts workaround mentioned above. The ALSA driver may complain about a non-power-of-two buffer size and unsatisfactory sound may result. The following was once reported for an onboard VIA82xx audiocard: ALSA: Using device: default ALSA: 14867 bytes buffer with mmap interleaved access ALSA: WARNING: non-power of 2 buffer size. sound may be unsatisfactory. Recommend using either the plughw or hw devices or adjusting dmix to have a power of 2 buf size ALSA Audio initialized (16 bit, stereo, 44100 Hz) You may resolve this by using a .asoundrc like the following, pcm.alsa_fixh2 { type dmix ipc_key 2048 slave { pcm "hw:0" rate 48000 period_time 0 period_size 1024 buffer_size 16384 channels 4 } } and starting uhexen2 with: $ glhexen2 -sndalsa -alsadev alsa_fixh2 -sndspeed 48000 (Notice the -sndalsa -alsadev alsa_fixh2 parameters. Tested and documented by Davide Cendron.) 2.2 Lines on Screen ------------------- A common problem, especially with 3dfx cards, is flickering lines across the screen caused by a GL hack which isn't supported today. To fix this, bring down the console and enter 'gl_ztrick 0'. With fresh installations of uhexen2, this option is disabled by default. 2.3 Fluxbox ----------- Though it's a great window manager, older versions of fluxbox have issues with some games in fullscreen mode. If this is the case, hit "ALT + Enter" twice to switch into windowed mode and then back into fullscreen mode. ---------------------------------------------------------------- 3. GENERAL INFO --------------- 3.1 Mods -------- Running a Hexen II mod is the same as running a Quake mod: You need to specify its directory name after a -game argument on the command line. For example, in order to run Fortress of Four Doors: ./glhexen2 -game fo4d If you want to run some mod with mission pack support enabled, use a command line with -portals argument included. For example: ./glhexen2 -portals -game somemod Fortress of Four Doors is one of the few large mods for Hexen II. It has a confusing level near the middle, but is otherwise great. Look around the web for the file fo4d.zip or fo4d.tgz (also available on our downloads page.) Also, make sure to check out the botmatch mod "hcbots" available at our project page. Rino recently made his two mods available: mpbyrino and mpbyrino2. The former works fine with Hexen II, whereas the latter needs the Portal of Praevus expansion pack. Rino's mods are at: https://www.facebook.com/pages/RINO/265889100246732 The "Game of Tomes" mod by peewee_rota (aka ThePunKing) is another new hexen2 mod: https://github.com/cabbruzzese/gameoftomes/wiki And then, there is the somewhat incomplete Project Peanut: Developed by Shanjaq, Project Peanut introduces RPG elements, such as a massive array of spells, in a very interesting way. Its homepage is at: http://www.geocities.ws/shanjaq/index.html Demo note: You need the full retail version of the game in order to play modified games: mods are not supported in the demo version. 3.2 Game Console ---------------- Games based on the Quake engine have an in game console - toggled by the '~' key or by the Shift-Escape key combination - from which variables can be changed. Some of these affect performance, others enable features. A brief list follows but for more info see: http://www.quakewiki.net/archives/console/commands/quake.html variable valid values -------- ------------ fov_adapt 0 or 1 : Disable/enable Hor+ style field of view (FOV) scaling. Default is enabled: your fov will be scaled automatically according to the screen resolution, so that manual fov changing isn't needed. Opengl and software renderers both supported. _snd_mixahead 0.1-0.9 : 0.1 = less latency, 0.9 = better performance. Don't play with the default 0.1 value unless you really need to. snow_active 0 - 255 : 0 = none, 1 = normal amount designated by the map. Higher acts as a multiplier, very high values may cause massive performance loss in opengl. 3.2.1 Some opengl options ------------------------------- gl_multitexture 0 or 1 : Disable/enable multitexture support. 0 is default. Faster performance if the hardware supports it. Menu can be used to change the setting (Video Modes -> Multitexturing.) gl_texture_NPOT 0 or 1 : Disable/enable non-power-of-two textures. If the hardware supports it, it'd make a better job with textures, most noticibly with the 2D elements. 0 is default. Menu can be used to change the setting (Video -> NPOT textures.) NOTE: Some old graphic cards, such as R300 to R500 class ATI Radeons or NVIDIA GeForce FX, may lie about fully supporting this feature: enabling this on such hardware may result in an unplayable slowdown. gl_texturemode : GL_NEAREST (Point sample, least quality) GL_LINEAR (Bilinear, no mipmaps) GL_NEAREST_MIPMAP_NEAREST GL_NEAREST_MIPMAP_LINEAR GL_LINEAR_MIPMAP_NEAREST (Bilinear: default) GL_LINEAR_MIPMAP_LINEAR (Trilinear: best) Also adjustable from the menu. Bilinear is recommended. If you don't have an ancient graphics card, you should have no trouble running with the trilinear filtering. gl_texturemode_anisotropy: 1 is the minimum value with no anisotropic filtering. Values >= 2 will take effect if the hardware has support for it. gl_constretch 0 or 1 : Disable/enable console background eye candy. 3.2.2 Software renderer options ------------------------------- sbtrans 0, 1, 2 : HUD (status bar) transparency. 0: none (solid, default setting), 1: slightly transparent, 2: very transparent. dmtrans 0, 1, 2 : Same as the sbtrans above, but used for the small deathmatch overlay in multiplayer. contrans 0, 1, 2 : Same as the sbtrans above, but used for the console background transparency. 3.2.3 Other OpenGL options ------------------------------- gl_coloredlight 0, 1, 2: Static colored lights. 0 is the white light that hexen2 traditionally uses. Mode 1 is colored lighting. Mode 2 is experimental, which combines white and colored lighting (if available), and can give better results then just colored lighting sometimes. It is also slightly brighter. 0 is the default mode. Can be adjusted from the menu system. Changing this option requires the level to be reloaded. Colored lighting requires RGBA lightmaps (also selectable from the menu.) gl_colored_dynamic_lights 0 or 1: Disable/enable colored dynamic lights. 0 is the default. Also adjustable from the menu. gl_extra_dynamic_lights 0 or 1: Disable/enable extra dynamic lights. 0 is the default. Also adjustable from the menu. gl_purge_maptex 0 or 1: Purge opengl textures upon map changes. The textures won't keep accumulating from map to map, but the levels will load slower. Default is 1. Adjustable from the menu. 3.2.4 Nvidia tweaks ------------------------------- These are only valid for supported hardware (Geforce and better) with Nvidia's proprietary linux video drivers installed. The -sync or -vsync command line switch forces GL redraws to sync with the monitor refresh for a more stable image. This is equivalent to setting the environment variable __GL_SYNC_TO_VBLANK to 1. You can override our -fsaa switch if you set Nvidia-specific environment variable __GL_FSAA_MODE to an appropriate value. The details for this can usually be found in Nvidia's readme file. Briefly: Geforce 1 and 2 use mode 3 or 4, others 1, 2, 4 or 5. 3.3 Hardware Acceleration ------------------------- Setting up hardware OpenGL acceleration under Linux used to be a big deal in the past. Modern linux distros do this automatically now. Nvidia's drivers for all of their modern video cards are not open source, and because of this many distributions do not include them. If your Nvidia card is running slowly, this is probably the cause, you should visit Nvidia.com to download the linux installer. In my experience these drivers are great, but not all versions work 100% with all cards. If you have a misbehaving Nvidia video card, try a different driver version. Linux kernel 2.6.8 / Nvidia driver version 6111 /TnT card and possibly other cards and kernels have issues with the GL game: upgrade to 7167 to fix these problems. If you don't want to change your nvidia driver and the game seems to halt, try using the alt-enter key combination to switch to windowed mode and redraw the screen. Behavior is the same for glquake. 3dfx Voodoo1/2 and Voodoo Rush are no longer hardware accelerated under XFree-4.x and X.org releases. You need Glide, and Mesa compiled against glide. For some more detailed info, consult the document README.3dfx. 3.4 Thanks ---------- Thanks to ID Software, Raven Games and Activision for a great game and for supporting open source software, to Dan Olson and Clément Bourdarias for the initial Linux port, and to sourceforge.net for hosting us. Many thanks to all of our contributors: see the AUTHORS file for a complete list of them. 3.5 Links --------- 3.5.1 Hammer of Thyrion pages: ------------------------------- Home page : http://uhexen2.sourceforge.net/ Project page : https://sourceforge.net/projects/uhexen2/ Git Repository : https://sourceforge.net/p/uhexen2/uhexen2/ 3.5.2 Hammer of Thyrion ports: ------------------------------- AmigaOS4 port: http://os4depot.net/?function=showfile&file=game/roleplaying/uhexen2.lha Pandora (GP2X) port: http://dl.openhandhelds.org/cgi-bin/pandora.cgi?0,0,0,0,30,66 PalmOS port: http://www.metaviewsoft.de/ RISC OS port: http://www.riscository.com/2012/hexen-ii-port-released/ http://www.iconbar.com/forums/viewthread.php?threadid=8455&page=1#120298 3.5.3 Hammer of Thyrion packages / builds: ------------------------------------------- FreeBSD packages: http://www.freshports.org/games/uhexen2/ Arch Linux packages: https://aur.archlinux.org/packages/hexen2/ Gentoo ebuilds: http://bugs.gentoo.org/show_bug.cgi?id=105780 3.5.4 Other Hexen II ports: ---------------------------- Hexen II PSP port: http://jurajstyk.host.sk/ JLQuake, a Quake1/2/3 and H2/HW engine: http://sourceforge.net/projects/vquake/ FTEQW, a Q1/QW engine with H2 support: http://sourceforge.net/projects/fteqw/ Korax's UQE Hexen II: https://www.jacqueskrige.site/ 3.5.5 Older, currently inactive Hexen II ports: ------------------------------------------------ Hexen II Mac port : http://macglquake.sourceforge.net/ Hexen II Dreamcast: http://dcquake.sourceforge.net/ JSHexen II : http://jurajstyk.host.sk/ Pa3PyX's Hexen II : http://pa3pyx.dnsalias.org/ Kiero's old MorphOS port of Hammer of Thyrion: http://www.binaryriot.org/users/kiero/ 3.5.6 Other links: ------------------- Hexen II resources at gamers.org: http://www.gamers.org/pub/idgames2/hexen2/ http://www.gamers.org/pub/idgames2/planetquake/hexenworld/ Rino's Hexen II mods: https://www.facebook.com/pages/RINO/265889100246732 Game of Tomes mod by peewee_rota (aka ThePunKing): https://github.com/cabbruzzese/gameoftomes/wiki DarkRavager's Hexen II resources: http://pages.citenet.net/users/danielm/hexen/hexen.html Linux Quake HOWTO: http://tldp.org/HOWTO/Quake-HOWTO.html id Software : http://www.idsoftware.com/ Raven Software: http://www.ravensoft.com/ SDL homepage : http://www.libsdl.org/ ALSA homepage : http://www.alsa-project.org/ --------------------------------- APPENDIX A. Some Legacy Options --------------------------------- If, while running around, the screen occasionally twitches up and down, then disable lookspring by typing "lookspring 0" in the game console. Lookspring, lookstrafe and keyboard look (+klook) are legacy options from DOS times for systems without a mouse, which we didn't kill only for historical reasons. They are pretty much of no use today and we recommend disabling them if they aren't already. Those options and/or key bindings are removed from the menu system of Hammer of Thyrion. For reference, here are what they are meant to do, as written in the old manuals: lookspring: 0 or 1. Returns your view immediately to straight ahead when you release the look up / down key. Otherwise, you must move forward for a step or two before your view snaps back. lookstrafe: 0 or 1. If you are using the look up / down key, then this option causes you to sidestep instead of turn when you try to move left or right. keyboard look: If +klook is bound to a key, press that key to use your movement keys to look up or down. mouse look: If +mlook is bound to a key, press that key to allow your mouse to look up or down (by sliding it forward and back), and to remain looking up or down even if you move forward. And here are some legacy opengl options: gl_picmip (0, 1, 2): 0 is default. 1 and 2 scale down the textures (low quality), but the help screens and some small fonts become unreadable and ugly. Was useful with old hardware with low memory back at the time. Keep it as 0. gl_playermip: similar to gl_picmip, but was for player skins. gl_ztrick (0 or 1): 3% performance hack mostly unsupported today. (See: http://forums.insideqc.com/viewtopic.php?p=36082#p36082) Keep it as 0. docs/README.3dfx000066400000000000000000000211651444734033100135410ustar00rootroot00000000000000Setting up 3dfx Voodoo Graphics, Voodoo 2 and Voodoo Rush 3d accelerator boards to work with Hexen II under Linux: --------------------------------------------------------- This doco here is provided for convenience; there certainly are many documentation on the Internet. For your Voodoo Graphics or Voodoo2 board to work with OpenGL, and Hexen II, you need to install Glide2 or Glide3 libraries specific to your board. Then you must compile Mesa3d against glide and optionally install it. Mesa-3.x.x uses glide2, and optionally glide3. Mesa-6.x.x only uses glide3 and this is the fastest and recommended option. 1a. Downloading and installing glide2x : ---------------------------------------- There are two ways of installing glide2x under linux: - Download and install proper 3dfx rpm packages for your voodoo card (you need to search them on the internet) - Or, better: Check-out the latest cvs version of glide from sourceforge: # cvs -d:pserver:anonymous@glide.cvs.sourceforge.net:/cvsroot/glide co glide2x swlibs Alternatively you can download the package "glide2x.tgz" from our page. To compile for voodoo2: # mv swlibs ./glide2x/ # cd glide2x # make -f makefile.linux FX_GLIDE_HW=cvg (replace cvg with sst1 in order to compile for voodoo graphics) The compiled library filename is libglide.so.2.53 for voodoo2, or libglide.so.2.46 for voodoo graphics. Become root, copy either of these libraries into /usr/lib, then create some symlinks, the final picture should be like: libglide.so -> libglide.so.2 libglide2x.so -> libglide.so.2 libglide.so.2 -> libglide.so.2.53 (libglide.so.2.46 for Voodoo1) Finally, while still as the super user, run /sbin/ldconfig To install the glide2 headers, create a directory named glide under /usr/include. Download the file "glide2_headers.tgz" from our page and extract it into /usr/include/glide. This finalizes glide2 installation. 1b. Downloading and installing glide3x : ---------------------------------------- Check-out the latest cvs-development version of glide3 from sourceforge: # cvs -d:pserver:anonymous@glide.cvs.sourceforge.net:/cvsroot/glide co -r glide-devel-branch glide3x swlibs Alternatively you can download the package "glide3x.tgz" from our page. To compile for Voodoo2: # mv swlibs ./glide3x/ # cd glide3x # make -f makefile.linux FX_GLIDE_HW=cvg (replace cvg with sst1 in order to compile for voodoo graphics) The compiled library filename is libglide3x.so, become root, copy this library into /usr/lib. Finally, while still as the super user, run /sbin/ldconfig To install the glide3 headers, create a directory named glide under /usr/include. Download the file "glide3_headers_v5.tgz" from our page and extract it into /usr/include/glide. This finalizes glide3 installation. 2. Installing Device3Dfx kernel module : ---------------------------------------- You need this kernel module in order to use glide and access /dev/3dfx as a normal user. Check-out the latest sources from sourceforge cvs: # cvs -d:pserver:anonymous@glide.cvs.sourceforge.net:/cvsroot/glide co -r glide-devel-branch Device3Dfx (The glide2x.tgz and glide3x.tgz packages on our page already include this module's sources.) Compile it: # cd Device3Dfx # make KSRC=/path/to/kernelsource Install it: # su (enter root password) # make KSRC=/path/to/kernelsource install If the compilation errors-out with a message about remap_page_range this means you're using a non-standard kernel (like a RedHat kernel). In this case you may try editing 3dfx_driver.c and changing the kernel version to match yours at line 181. 3. Downloading and installing Mesa : ------------------------------------ This may be a tricky process depending on what kind of a system you are on: 3.a. RedHat-7.3 and newer : ---------------------------- This represents a system with XFree86-4.x which, in a way, already includes mesa libraries and includes a working opengl installation through DRI. You must NOT do any "make install" after compiling mesa. Here is a procedure which is tested and works on an up-to-date RedHat-7.3 system: Download Mesa-6.2.1 (or Mesa-3.4.2 for glide2) from mesa3d downloads page on sf.net into an empty directory. # tar xvfz MesaLib-6.2.1.tar.gz # tar xvfz MesaDemos-6.2.1.tar.gz # cd Mesa-6.2.1 # make linux-x86-glide # this compiles against glide # using all possible x86 asm # cd lib # cp libGL.so.1.5.060201 libVoodooGL.so.6.2 or, for mesa-3.4.2: # cp libGL.so.1.2.030402 libVoodooGL.so.3.4 The last command creates a copy of Mesa OpenGL library, we will use it with Hexen2. Copy this new library to the hexen2 installation directory. Now run the game with the command: # export MESA_GLX_FX=f # ./glhexen2 -f -width 640 -gllibrary libVoodooGL.so.6.2 (you may use additional arguments) If you copied the library to the hexen2 installation directory, you needn't specify any path to it on the command line: HoT-1.4.0 and later versions will find it in its basedir. Alternatively, you can copy the library to any place you want: in that case you have to specify the full path to it on the command line for hexen2 to find it. Example: # ./glhexen2 -f -width 640 -g /usr/local/lib/libVoodooGL.so.6.2 Mesa-3.0 and Mesa-3.2.1 should also work fine. In my experience, Mesa-4.0.4 and 5.0.2 didn't work, they may or may not work for you. The only issue I had with Mesa-6.2.1 is SLI was not put into any action (no fps beef-up.) Otherwise, it worked fine. Note: The maximum resolution for a voodoo1 is 640. For a single voodoo2 it is 800, for a voodoo2/SLI setup it is 1024. This actually depends on how much fb memory your board have: if you have a 8mb Skywell Magic3D plus, you can do 800x600 with a voodoo graphics board, as well. For performance reasons, 640x480 is recommended for a Voodoo2, 512x384 is recommended for a Voodoo Graphics. 3.b. RedHat-6.2, Slackware-7.1 : (anyone still with these oldies? ;) ---------------------------------------- These ones represent systems which use XFree86-3.x and no default installations of OpenGL/DRI. # tar xvfz MesaLib-3.4.2.tar.gz # tar xvfz MesaDemos-3.4.2.tar.gz # cd Mesa-3.4.2 # make linux-x86-glide (or better: use the configure / make / make install sequence) You can also try using the other mentioned mesa versions. After the installation, you will have an opengl implementation that depends on 3dfx glide. If you perform a system-wide mesa installation, you don't need the -gllibrary argument for running hexen2. Otherwise use a command line like: # ./glhexen2 -f -width 640 -gllibrary /home/NAME/lib/libVoodooGL.so.3.4 (you may use additional arguments) 4. 8-bit Texture extensions: ----------------------------- If you use the -paltex argument with HoT-1.3.0 and newer versions, 8-bit texture extensions will be searched and used with very little quality loss. This helps saving video memory. This options works with Mesa-6.x but not with Mesa-3.x. It may or may not work correctly for you. 5. Gamma (brightness) support: ------------------------------- You can compile uHexen2 for linux to include some 3dfx specific hacks for brightness control. (make sure that you enable that option in the Makefile when compiling). The -no3dfxgamma argument disables it. 6. Using the fxMesa opengl library for other games: ---------------------------------------------------- Hexen2 / Hammer of Thyrion port does have support for user specified opengl library usage: See the sample command lines above employing the -g or -gllibrary argument. If any other game you want to play uses opengl but does not have support for user specified opengl libraries, it will try to open the system opengl library, namely libGL.so or libGL.so.1. To cheat such a game to use the fxMesa library, copy the library to where the game is installed and rename the library to libGL.so or libGL.so.1. Then run the game with a command line like: # cd # LD_LIBRARY_PATH=. MESA_GLX_FX=f Notice the dot immediately after the equal sign. With some luck, you may be able to use your Voodoo Graphics or Voodoo^2 board with that game. 7. SAGE OpenGL driver: ---------------------- This is an experimental 3dfx OpenGL driver by Daniel Borca (website: http://www.geocities.ws/dborca/opengl/sage.html ) SAGE is an OpenGL work-alike. It requires 3dfx glide3x libraries to be installed. The v0.9.2 binary provided by the author seems to work well enough with uHexen2, but it is noticably slower than Mesa. It doesn't have any software fallbacks, so multitextured rendering with 8 bit lightmaps (GL_LUMINANCE) doesn't work: you must run in GL_RGBA mode. SAGE source has recently been GPL'ed: https://github.com/dborca/sage A fork of it by a uHexen2 author, see: https://github.com/sezero/sage docs/README.hwcl000066400000000000000000000053031444734033100136260ustar00rootroot00000000000000Hammer of Thyrion (uHexen2) - HexenWorld Client Client modes: -------------- This is not a complete listing, more may be found on the net. Deathmatch overlay: ------------------- dm_mode 0 Overlay will not appear on your screen at all dm_mode 1 Overlay will be at upper left side dm_mode 2 Overlay will be at bottom left side Viewing pings and player info: ------------------------------ +showdm Q is the default. Bind to any key. Player names: ------------- +shownames If bound to a key, activates name only when key is pressed. When added to autoexec.cfg, then it will stay permanently on. Team dptions: ------------- team [name] Sets the team for each player. Takes advantage of team options. messagemode 2 Binding this command will allow you to send messages that only your team can read. r_teamcolor This command controls what color of tint that you see your teammates. Value is 1-900. Mission Pack support: --------------------- HexenWorld looks for the Portal of Praevus mission pack and automatically activates it if it finds it. If, for some reason, you want to disable the mission pack support, run the client with -noportals commandline switch. Key Bindings: ------------- This small document assumes you know what a key binding and making an alias mean. The basic default bindings can be adjusted from the options menu, other less common or more advanced bindings can be set by editing the config.cfg file in your hw directory. Among such custom binds is the disk jump: DISK JUMP : ----------- This is similar to Quake's rocket jump and it uses the Disc of Repulsion artifact, allowing you to leap almost 3 times higher than a normal jump. Here is an alias setup for doing a disc jump. Bind the alias djump1 to a key of your choice: alias djump "+jump; wait; impulse 110; wait; -attack; -jump; force_centerview" alias aimdown "cl_pitchspeed 999999; +lookdown; wait; cl_pitchspeed 150; -lookdown" alias djump1 "aimdown; djump" Some other useful actions: impulse 44 : Throws an item. This is good for passing off goodies to your teammates. impulse 32 : Panic Button. Activates one of everything in your inventory. Taunts : ------------ Press your "talk" (chat) key, the default is T. You'll get the "say: " prompt on your screen. Now hit the toggleconsole key (the tilde, which is located just above the tab key on US keyboards) and type a number from 1 to 39. I won't list the wordings here, try them yourself. Note: Raven seems to have forgotten to put in the sound files for taunt numbers 8, 12, 21, 22, 23, 29 and 37, so you'll hear nothing with them. Binding the taunts to keys is also possible but you must edit your config.cfg file. A simple bind such as: bind "m" "say`10" will do the trick. docs/README.hwmaster000066400000000000000000000021151444734033100145210ustar00rootroot00000000000000Hammer of Thyrion (uHexen2) - HexenWorld Master Server - version 1.2.7 ---------------------------- This is a small, commandline-only program. It doesn't need any Hexen II or HexenWorld installation. It uses 26900 as the default port. Command line parameters: -port xxxxx select a port other than default (26900) Console commands: quit shuts down the master server list lists all registered servers clear clears the list of registered servers filter lists all current IP filters. When the program receives packets from an IP matching a filter, the incomming IP is changed to the value set in the filter filter add xxx.xxx.xxx.xxx:port xxx.xxx.xxx.xxx:port adds a filter filter remove xxx.xxx.xxx.xxx:port removes a filter filter clear removes all filters The source is based on previous works by Marc Allaire (aka. Kor Skarn) and QuakeForge authors. HWMASTER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. docs/README.hwsv000066400000000000000000000124111444734033100136560ustar00rootroot00000000000000Hammer of Thyrion (uHexen2) - HexenWorld Server Server Modes: --------------------- There are different modes that you can run your HexenWorld server in. These are only for unmodified hexenworld. Some community created mods, such as the Ghost mod, may modify the meaning and behavior of these options. This is not a complete listing, more may be found on the net. Mission Pack support: --------------------- HexenWorld looks for the Portal of Praevus mission pack and automatically activates it if it finds it. If, for some reason, you want to disable the mission pack support, run the server with -noportals commandline switch. Setting up Map Cycling: ----------------------- Only the server needs to do this. As of gamedata-v1.28 (uHexen2-v1.5.6), hw and siege map cyling is done using the localinfo variables just like in quakeworld. The following example shows how to rotate four maps on a hw server: localinfo demo2 romeric5 localinfo romeric5 village2 localinfo village2 meso3 localinfo meso3 demo2 The first line tells that demo2 will be followed by romeric5, the second line says romeric5 will be followed by village2, and so on. You can save those following lines in hw/mapcycle.cfg, and start your server like: hwsv +exec mapcycle.cfg +map demo2 and the specified map rotation will be in effect. While the server is running, you can change the map rotation, too: edit your mapcycle.cfg to clear the old rotation rules and specify a new one, e.g: // these clear the nextmap info for the previous ones, because you // probably are not interested in them anymore localinfo demo2 "" localinfo romeric5 "" localinfo village2 "" localinfo meso3 "" // and these specify new rules localinfo village3 hwdm2 localinfo hwdm2 ravdm4 localinfo ravdm4 demo3 localinfo demo3 tower localinfo tower village3 Now on the server console you can do "exec mapcycle.cfg" which refreshes the map rotation info, and then do "map village3" which starts the first map in the rotation. We provide sample mapcyle.cfg files for the hw and siege mods in our packages. Remember that map cycling is mod-specific: some other mod may require doing different things. Tome Mode: ---------- tomemode 0 Default tomemode 1 Each player can select the tome of power version of a weapon by pressing the impulse for the weapon twice. (ie.: if your 3rd weapon is bound to your 3 key, press it twice.) tomemode 2 Players gain the ability at any point to turn on a powered up version of a weapon once a tome of power has been acquired. The weapon will stay tomed until the player dies. Damage Scaling: --------------- damagescale 1.0 This is the default setting. This will scale all damage by the scale entered. Spawning: --------- tomerespawn 1 Players will respawn with a tome of power active. 0 or 1. w2respawn 1 Players will respawn with their second weapon already in their inventory and selected. 0 or 1. altrespawn 1 When a player dies, he will drop his selected weapon, drop 1/2 of his remaining weapons, lose half of his mana, and drop half of his inventory. He will keep the rest when he respawns. 0 or 1. shyRespawn 1 Artifacts taken from a place will be shy to respawn unless there is no one around them. Items affected: Mystic Urn, Krater of Might, Tome of Power, Invisibility Sphere, Seal of the Ovinomancer, Ring of Flight, Icon of the Defender, and the Purifier Pieces. 0 or 1. Default: 0. patternrunner 1 Some respawn times will be scaled with the rumber of players. 0 or 1. Default 0. Item Usage: ----------- autoitems 1 Artifacts taken will be automatically used. Affected items: Invisibility Sphere, Boots of Speed, Force Cube, Mystic Urn, Krater of Might, Icon of the Defender. 0 or 1. Default is 0. Not functional in dmmode 1. easyfourth 1 If a player gets a purifier piece, the complementary piece will be automatically added and he will "easily" have the fourth weapon. 0 or 1. Default is 0. Player Levels: -------------- fixedlevel # Forces all players to stay at one particualr level. No experience is accumulated in this mode. The number entered is the level number that each player will stay at. Deathmatch Modes: ----------------- dmmode 0 Normal deathmatch. Default. dmmode 1 Hoard the Icon. The goal is to capture the icon. The only person that can score a frag is the person with the icon. In this mode, the icon allows the carrier to do double damage and take half damage. The icon has no time limit and you can not pick up any weapons or items while you have it. When the player with icon is killed, it will randomly respawn somewhere else in the level. dmmode 2 Hunter. Each player has a specific target that they must kill. If you kill your target, you will gain your target's old target and the person you killed will respawn with you as his target. You gain a frag for killing your target, lose a frag for killing any one not your target, and you gain nothing if you kill a player who is targeting you. Teamplay Modes: --------------- teamplay 0 Default teamplay 1 Cannot hurt yourself or teammates teamplay 2 If you kill yourself or teammate, you lose a frag teamplay 3 Cannot hurt your teammates, but you can hurt yourself. Mesage broadcasting: -------------------- spartanPrint 1 Only sends chat and relevant death messages to clients. 0 or 1. Default is 1. docs/README.music000066400000000000000000000151431444734033100140140ustar00rootroot00000000000000Hexen II: Hammer of Thyrion (uHexen2) - Music support ----------------------------------------------------- Hammer of Thyrion supports OGG, MP3 and WAV external music files to be played instead of the original midi files, so you can enjoy the hexen2 music in good quality like they are from the hexen2 cdrom audio tracks. Actually the midi music of hexen2 and its mission pack are the same songs as the hexen2 cdaudio, so you can just rip your hexen2 cdaudio tracks and rename them properly to match the midi file name. What you should do is simple: 1. Use your favorite cd-ripper application and rip your hexen2 cdrom audio tracks, convert them to ogg or mp3 so they occupy less space, like track02.ogg, track03.ogg, etc. (Notice that there is no such thing as track01: The first tracks of both Hexen II and Portal of Praevus cdroms are always "data" tracks) 2. Go into your hexen2 installation directory (unix/linux users: you can do this in your $HOME/.hexen2 directory, too.) 3. Create directory: data1/music (for windows users data1\music). If you have the mission pack, then create another directory portals/music . 4. Take the ripped music files from step1, place them under data1/music. If you have the mission pack, repeat step1 for the mission pack, too, and place the ripped music files under portals/music. 5. Make sure that the music type is chosen as "all codecs" in the game's options menu. (For the curious+techies: this internally sets bgm_type as "midi" and bgm_extmusic as 1.) 6. Let's do some file renaming: the trackXX files should match the midi names. Linux/unix users can do these easily using our cdrip_hexen2.sh and cdrip_missionpack.sh shell scripts. Note to the curious: this file renaming is necessary. Unlike quake, the hexen2 music does have its own file name which is the midi file name. One may try to be clever and code this cdaudio track->midiname mapping into the game engine, however there are more than one corner cases where such a thing is doomed to fail. Here are the corresponding midi names for cdrom audio tracks: data1/music : ------------- track02 -> casa1 track08 -> egyp3 track14 -> roma3 track03 -> casa2 track09 -> meso1 track15 -> casb1 track04 -> casa3 track10 -> meso2 track16 -> casb2 track05 -> casa4 track11 -> meso3 track17 -> casb3 track06 -> egyp1 track12 -> roma1 track07 -> egyp2 track13 -> roma2 portals/music : --------------- track02 -> tulku7 track06 -> tulku9 track09 -> tulku5 track03 -> tulku1 track07 -> tulku10 track10 -> tulku8 track04 -> tulku4 track08 -> tulku6 track11 -> tulku3 track05 -> tulku2 (track12 won't match to anything, leave as is) Notice that there is no such thing as track01: The first tracks of both Hexen II and Portal of Praevus cdroms are always "data" tracks. 7. All things are ready to go. Notes on less common Hexen II cdrom media: ------------------------------------------ 8. A note for the owners of Hexen II/xplosiv release: Xplosiv is (was) a publishing label that did budget or re-releases of quite some games, one of them Hexen II. Audio tracks on the Xplosiv release of Hexen II is the exact opposite of the order on the original release, like so: track02 -> casb3 track08 -> meso3 track14 -> casa4 track03 -> casb2 track09 -> meso2 track15 -> casa3 track04 -> casb1 track10 -> meso1 track16 -> casa2 track05 -> roma3 track11 -> egyp3 track17 -> casa1 track06 -> roma2 track12 -> egyp2 track07 -> roma1 track13 -> egyp1 (Information was kindly provided by Sander van Dijk. You can use the cdrip_hexen2_xplosiv.sh script with it, if you are on linux.) 9. Another note, for the owners of Hexen II Matrox m3D bundled version, also known as "Continent of Blackmarsh": That disc has tracks 2 and 4 in reversed order, like so: track02 -> casa3 track03 -> casa2 track04 -> casa1 track05 -> casa4 (Information noted on M3D_2 disc with Hexen II v1.10. You can use the cdrip_hexen2_matroxm3d.sh script with it, if you are on linux.) New console commands: --------------------- - music Start playing the requested music file. Example: music meso3 Notice that you don't type the file extension: The requested music will be searched with ogg, mp3, wav and then with a mid extension, automatically. If you do specify the file extension, like "music meso3.wav", then it will honor your your wish and try only the given type: this is good for testing/comparing the same music in different formats, and mappers can use this feature and specify the "midi" name in their maps with a specific extension. - music_stop Stops the playing music. - music_pause Pauses the playing music - music_resume Resumes playing the music if it was paused - music_loop 1 Makes the background music to loop (default behavior) - music_loop 0 Makes the background music to play once and then stop - music_jump Jump to a given order in music (only for module (tracker) music) Removed console commands: ------------------------- midi_play, midi_stop, midi_pause and midi_loop commands of the original game are removed in favor of the new music commands above. New cvars: ------------------------- - bgm_extmusic (0 or 1): Disable or enable playback of external music files instead of original midi files. default is 1 (enabled). New command line options: ------------------------- - -noextmusic: Disables the playback of external music files instead of the original midi files. Music files in PAK files: ------------------------- PAK-contained music files are fully supported. Music file directories: ------------------------- - The midi music files are always searched under the "midi" directory, as usual, for compatibility. - Other kinds of music files, i.e. anything other than midi, are always searched under the "music" directory. Music file search order: ------------------------- The engine can handle multiple audio formats. The map-dictated music, i.e. the music from the svc_midi_name server message, is always searched by the order of searchpath priority: the file from the searchpath with the highest priority is chosen, because it is most likely the one from our own game directory itself. This way, if a game mod has egyp1 as a mp3 or a midi, which is below egyp1.ogg in the music_handler order, the mp3 or midi will still have priority over egyp1.ogg from the data1 game directory. docs/ReleaseNotes000066400000000000000000001020771444734033100143340ustar00rootroot00000000000000Hammer of Thyrion (uHexen2) release notes: ======================================================================== Changes in v1.5.10: (Oct. 25, 2022.) ======================================================================== This is a minor bug fix and maintenance release: - Added BSP2 format support, thanks to an initial patch by Spike. - Fixed possible crash in effects code (thanks to Jonathan Bergeron). - Fixed getting stuck with loading plaque upon attempting to load a bad save from the menu. - OpenGL: Fixed dark dynamic lights -- e.g. when Necromancer uses invulnerability. (thanks to Shanjaq Astraljam, sf.net bug 48.) - OpenGL: Fixed graphical artifacts with scaled UI (sf.net bug 59.) - OpenGL: gl_clear now defaults to 1 and glClearColor is black. The new cvar r_clearcolor (palette index from 0 to 255, default is 0) can be used to change the clear color (from fitzquake.) - OpenGL: compensate viewmodel distortion at fov > 90. - Software renderer: replaced client sin/cos calls with precomputed table lookups for better performance especially on old hardware. - More performance tweaks mostly targeting the software renderer. - Windows, midi: Fixed volume reseting after loop (sf.net bug 66.) - Improvements to mp3 tag detection / skipping. - New console command music_jump: Jump to given order in music, like Unreal's music change. Only for module (tracker) music. - Map utils: BSP2 format support: qbsp must be run with the "-bsp2" command line switch in order to output in bsp2 format. - Utils: light, vis, and jsh2color now default to auto-detecting the max number of threads. - Windows: removed the long-obsoleted Scitech MGL video driver. - DOS: removed the long-obsoleted MPATH and BW-TCP network drivers. - Amiga (m68k): multiple updates/optimizations thanks to Szilard Biro. - Amiga (m68k): support for PSX controller, cdplayer.library and Paula audio support, thanks to Szilard Biro. - Amiga (m68k): added support for Bebbo's gcc6/libnix based toolchain, dropped support for VBCC compiler. - AROS: initial x86_64 support. - All platforms: Several HexenC (hcode) tweaks and clean-ups. - Multiple minor fixes, tidy-ups, and protability tweaks, and updates to third-party libraries. - Source repository moved to git. ======================================================================== Changes in v1.5.9: (May 31, 2018.) ======================================================================== This is a minor bug fix and maintenance release: - OS/2 support using SDL (should work on eComStation and ArcaOS too.) - Updated third-party libraries (music codecs, SDL, etc.) - Support for Watcom compiler targeting OS/2 and Windows. - Support for libxmp for tracker music decoding (disabled by default.) - AHI audio no longer fails if the library returns 32 bit format (e.g. in AROS v0 nightly builds.) - New AmigaOS MIDI driver using CAMD library. - Several optimized assembler sources for m68k-amigaos. - GCC is now supported to target m68k-amigaos. - Software renderer: Reduced some 2D overdraw. Faster model lighting. - Recursion optimizations to server world query procedures. - New 'viewpos' command to display client's position and angles (based on FitzQuake version.) - config.cfg is no longer written in case of a Sys_Error. - Utils, windows: Change threads creation to use _beginthreadex(). - All platforms, gameplay: Necromancer soul devour no longer prints a stray number (bug #65.) - Other minor fixes, tidy-ups, and protability tweaks. ======================================================================== Changes in v1.5.8a (Aug. 28, 2016) ======================================================================== This is a post-release patch against v1.5.8 fixing a few Amiga issues: - Amiga sound (AHI backend): fix sample rates lower than 11025 Hz. - Amiga sound (AHI backend): fix bad sound with sample rates which are not a multiple of 11025. - Amiga input: limit lowlevel.library joystick ports to 0-3. - AmigaOS3, MiniGL: smart and automatic locking support. New cvar: gl_lockmode, valid values: "manual" (default), "auto", and "smart". ======================================================================== Changes in v1.5.8: (Aug. 12, 2016.) ======================================================================== This is a minor bug fix and maintenance release: - Software renderer: Fixed a reported crash if the engine is compiled with certain Visual Studio versions (bug #62.) - Software renderer: Fixed sky scaling issues, thanks to Szilard Biro. (fix found in 'ToChriS' quake source.) - Software renderer: Fixed scaling console backgrounds whose dimensions are not the default 320x200. One such example is the "Game of Tomes" mod which has a 1024x768 conback.lmp. - Hexenworld server: Added "-protocol x" command line option to force the server to use a specific protocol (25 or 26), if so required. - Amiga: Video fixes. On AROS, the game does not 'freeze' in fullscreen mode anymore if the 'Timidity:' assign is not present. - Amiga: m68k-amigaos support. (Use VBCC as the compiler, gcc has code generation issues.) - Windows: Visual Studio builds no longer require yasm or nasm. Tested compilation using VS2015-update/2. - DOS, PCI sound driver: Updates, more Intel HDA chipsets recognized. - DOS, 3dfx opengl: Macronix (MX86251) variants of Voodoo Rush boards are now functional. - DOS, 3dfx opengl: Further updates/fixes to opengl and glide drivers. Updates to DXE exports so that more opengl driver builds would work without requiring an engine recompile. - All platforms, gameplay: Fixed an issue with the rider bosses Death, Pestilence and War in coop mode. (as reported by Spike and Korax.) - Other minor fixes/tidy-ups elsewhere in the source. ======================================================================== Changes in v1.5.7: (Jan. 10, 2016.) ======================================================================== - All platforms, gameplay, mission pack: fixed an hcode bug which would make the spiders seemingly invincible for a difficulty-depending time. - All platforms, gameplay: Crusader partial heal at certain experience thresholds now starts at level 3 to be consistent with the manual. - All platforms, gameplay: Many small HexenC fixes and clean-ups. - All platforms, hexen2: Better cross-map demo playback support. - OpenGL: fix screenshots when screen width isn't a multiple of 4. - OpenGL: minor gamma updates. - Windows: fixed DPI scaling issues. - Windows, CD audio: fixed the "-cddev" command line option. - Music: Support for FLAC codec and tracker (MOD) music including unreal umx files. (edit the makefiles to enable when compiling.) - Utils, hcc: Added a command line option "-os" to compact the strings heap by eliminating duplicates. some minor revisions. - Utils, jsh2color: ignore minlight values from the -light command line switch: eliminates the majority of the ugly black patches from eidolon and rider2c maps. bumped its version to 1.2.6. - All platforms: External wal texture loading is now a compile time option, disabled by default. - HexenWorld: Support for a new protocol 26: if the client sends '*cap' userinfo with a 'c', the server returns protocol 26 instead of 25 and sends the sound and model lists in small chunks like quakeworld does. Old clients versus new servers and new clients versus old servers are not affected and run using the original protocol 25 as they used to. The new protocol 26 connection sequence avoids packet fragmentation. - DOS: HexenWorld is now supported on DOS. requires uhexen2 v1.5.7 or newer hexenworld servers to connect. (see above for hw protocol 26.) - DOS: OpenGL support on 3Dfx Voodoo cards. - DOS: Experimental support for several PCI sound cards (see README.dos) - Several small fixes, tidy-ups, safeguards elsewhere in the sources. - Added a shell script to run different versions of the game which can be useful for system-wide installations. (see scripts/hexen2-run.sh) - Removed the gtk launcher. - Updated third-party libraries (music codecs and SDL). ======================================================================== Changes in v1.5.6: (Mar. 15, 2013.) ======================================================================== - Screen: implemented Hor+ style field of view (FOV) scaling, useful for widescreen resolutions. configured by new cvar fov_adapt: set it to 1 and your fov will be scaled automatically according to the resolution. enabled by default. opengl and software renderers both supported. - Screen: viewsize greater than 120 now disables the hud (use the screen size slider in the options menu.) - Progs VM: Fixed a corner case thinko in version6 progs execution. - Server: changelevel failures, e.g. in case of non-existent maps, are now considered as error, so that the clients do not get stuck in the connected state anymore. - Server: accept maplist.txt only if it is from the same game directory as progs.dat itself or from a searchpath with a higher priority. - Timidity (Unix MIDI): Multiple fixes, including memory errors found by valgrind. - Mac OS X: A new icon. - Music: Support for Opus codec. (edit the makefiles to enable it when compiling.) - Software renderer: fixed color shifts from area contents or power-up items usage staying persistent after a disconnect. - OpenGL: worked around weird mods, e.g.: project peanut, where certain models are sometimes drawn "white" because the mod's hcode initializes those entities with non-existant skins. - OpenGL: gamma fixes for several platforms. - HexenWorld: opengl fixes for the shownames feature, other gl updates. - Hexenworld, gamecode: hw and siege map cycling setup is done using the localinfo variables just like in quakeworld, strings.txt abusing is no more. See docs/README.hwsv for brief instructions. - Verified correct compilation by upcoming gcc-4.8 and by clang-3.2. - Fixed several other minor bugs, some found by valgrind. Incremented engine versions for hexen2 to 1.28, hexenworld to 0.28, and gamecode version to 1.28. ======================================================================== Changes in v1.5.5: (Jan. 02, 2013.) ======================================================================== - Utils (light, vis, jsh2color): Added unix pthreads support for running multi-threaded. Enabled windows multi-threads on all platforms instead of alpha-only. Default is single-threaded. Use "-threads #" to specify the required number of threads. (max: 32. -1 tries to autodetect.) - Utils, qbsp: Added a command line option "-oldhullsize" to replicate the old h2utils/qbsp behavior of using the original hexen2 sizes for hulls #5 and #6, not H2MP ones, if so needed. - Utils, hcc: Added a command line option "-old" to replicate old hcc versions' behavior of allowing the STR_ constants be saved globals and letting precache_file() calls go into progs.dat, if so needed. - Utils, dcc: Some small fixes. Added new command line options -fields, -functions, -globaldefs, -prglobals, -statements and -strings, which dump the related information. - Utils, all: Some fixes/improvements to command line options handling. - Support for the rarely used progs version 7 spec with 32 bit offsets instead of 16 bit of the original version 6: The engine can run both with v6 as well as with v7 progs. The hcc and dhcc tools are changed to compile the progs as v6 whenever possible for compatibility, or as v7 otherwise. they can also be made to compile specifically v6 or v7 progs by the new command line options -v6, -v7, or -version . The dhcc tool can now decompile both v6 and v7 progs. - Fixed a respawn-after-death issue which sometimes rendered the player unable to move backwards, or unable to use certain inventory items, or several other weird effects (bug #2176023.) The bug was there since the original hexen2 source release. - New quake2-style noclip movement. (from Fitzquake; configured by new cvar sv_altnoclip, enabled by default.) - Client: Fixed a demo playback failure when certain conditions are met. - Client: Fixed a minor and rare intermission glitch. - Client: unbindall before loading stored bindings (configurable by new cvar cfg_unbindall, enabled by default.) - SDL, keyboard input: Fixed control-character handling in unicode mode. - All, keyboard input: Several tweaks. - Software renderer: Disabled progress bars drawing, as it was reported to cause excessively long load times on some systems. Disabled the rotating skull drawing during file i/o. - Software renderer: Fixed occasional crash when sprites rendered very close to the camera origin, such as the teleportation puff. (used to happen mostly in 64 bit builds. bug #3562290.) - OpenGL: Fixed a crash in texture resampling code with 3d hardware max texture size greater than 1024 and wide-enough textures. - Windows: Removed hooks for QHOST, which is (was) a proprietery server administation tool. - Mac OS X: packages now have ogg/vorbis and mp3 music playback support. - Mac OS X: New MIDI driver. - Mac OS X: Support for text paste from OS clipboard to console. Support for the Apple (Command) key. - Mac OS X: Support for building using the makefiles and cross-compiling from Linux. - MorphOS builds: added missing -noixemul to the compiler flags. - MorphOS, AROS, Amiga: Support text paste from OS clipboard to console. - All platforms, gameplay: fixed bug in original hexen2 hcode which used to cause weapon switching to get stuck if several weapons were picked up too fast. fixed a minor bug in assassin's fourth weapon in powered mode. cleaned up weapon cycling code, minor fixes/clean-ups in weapon selection. fixed cube of force, so that it doesn't attack the player's own summoned imp anymore. several other hcode cleanups. - All platforms, hexenworld: pak4.pak from the ancient beta 0.11 version is now recognized properly. - All platforms, h2patch: Simplifications. - Windows: support for Visual Studio 2012. - All: Incremented versions for hexen2 engine to 1.27, hexenworld engine to 0.27, and gamecode version to 1.27. ======================================================================== Changes in v1.5.4: (July 01, 2012.) ======================================================================== - OpenGL: Fixed screen flickering resulting from progress bars drawing with some drivers (bug #3519666.) uHexen2 no longer draws the loading progress bars during level load in opengl mode. - OpenGL (Windows): Fixed a bug which would prevent running on Windows8 consumer preview versions, unless 32 bit color depth was specified on the command line. Works just fine now. - Music playback: Made sure that the file's channels count is supported. - Unix MIDI (Timidity): Configuration file timidity.cfg is now searched first under the user directory and the installation directory before the common system locations. Full absolute path of timidity.cfg can be specified by setting the TIMIDITY_CFG environment variable, too. - Client, all: Revised the intermissions setup code. - Client, all: Fixed a few effect abslight and scale flags, which were another bunch of original hexen2source bugs. - All platforms: Majorly revised path name handling and filesystem code. - Support for Solaris. Support for Amiga, AROS, MorphOS. Added missing RISCOS defines. - Keyboard input: Made the keypad keys to send separate key events in game mode. (for Windows and SDL-using builds, e.g. Linux/Unix, OSX.) - Joystick: Added joystick (gamepad) support for all SDL-using builds, e.g. Linux/Unix, MacOSX. The new cvars are documented in the README. - Mouse (DOS): Wheel is now detected and used by default. Use "-nowheel" command line switch to disable it. - Mouse (DOS): Pausing the game, moving the mouse and then unpausing it doesn't change the view angle anymore. - All platforms, gameplay: Fixed a rare crash in the ambient fish hcode. - All platforms, h2patch: More detailed output and better reporting in case of incompatible or corrupted pak files. Same with the pak-patch functionality of the gtk-launcher application. - Utils, dcc (progs decompiler): Fixed an infinite recursion issue when decompiling the hcbots progs.dat. Fixed decompiling of rival kingdoms progs. Changed decompiler memory usage. Made -src command line option of dhcc to behave the same as it does with hcc. Added -name command line option either for specifying a name other than progs.src for the compiler like hcc itself or for specifying a name other than progs.dat for the decompiler. - Utils, hcc (HexenC compiler): Cleaned up the -src command line option handling. - All: Incremented versions for the hexen2 engine to 1.26 and hexenworld engine to 0.26. Incremented gamecode version to 1.26. - Several source code cleanups and documentation updates. ======================================================================== Changes in v1.5.3: (Apr. 10, 2012.) ======================================================================== - OpenGL: Made non-power-of-two textures support configurable by a cvar, gl_texture_NPOT, disabled by default: Fixes serious slowdown on some old graphics hardware, such as R300 to R500 class Radeons on Mac OS X, which the driver is lying about its capability. Added a menu entry to enable/disable the feature (OpenGL features -> NPOT textures.) - Sound: The client no longer tries updating the ambient sounds when not connected, ie. when no map is active. - Several updates to the documentation. ======================================================================== Changes in v1.5.2: (Apr. 04, 2012.) ======================================================================== - All platforms: Incremented versions for the hexen2 engine to 1.25, hexenworld engine to 0.25, gamecode to 1.25 and the gtk-launcher to 1.0.7. - Verified correct compilation by clang (using v3.0) and by the new gcc version 4.7.x. - All platforms, gamecode: Fixed occasional crashes in the original hexen2 game with the Paladin's axe. - All platforms, hexen2 net play: fixed connection getting stuck sometimes after displaying the "Connection accepted" message. - All platforms: Ported server model code optimizations and cleanups from h2ded to hwsv (hexenworld server). - All platforms: Added support for transparent console in the software renderer, controlled by new cvar "contrans". The valid values for contrans are 0 (solid), 1 (transparent) and 2 (very transparent). - All platforms, software renderer: Fixed wrong transparency issue with certain models when not using the x86 assembler drawing routines. - All platforms: Added on-screen fps counter to hexen2 (do "showfps 1" from the console). Updated existing showfps code of hexenworld. - All platforms, OpenGL: Added anisotropic texture filtering support (gl_texture_anisotropy). Revised existing texture filters. The texture filtering options now saved to the config. - All platforms, OpenGL: Added support for non-power-of-two-textures extension. - All platforms: Implemented fov in opengl hexen2, useful for widescreen resolutions. Revised the implementations in hexenworld, as well as the software renderer. - All platforms, OpenGL: Fixed shadows spot which was broken back in 2007 during v1.4.2 development. - All platforms, input: Discard mouse/joystick motion when in camera mode. Discard the accumulated mouse motion when starting a game or when loading a saved game to prevent any unintended viewangle changes. - Windows input: Disabled GDI mouse acceleration flags for better behavior on XP and newer versions. - SDL / Unix: Revised SDL unicode and dead keys support. - SDL / Mac OS X: Fixed console and message mode backspace key (bug #2688151.) - Unix / SVGAlib mouse: Fixed missing camera mode and ideal roll handling. - Unix / SVGAlib video: Fixed planar modes when using the svgalib_helper kernel module. Removed the need for x86 assembly for compilation and the linux-only limitation. - Unix / SVGAlib video: Fixed messed up colors upon coming back from a vt switching. - Unix: Fixed compilation on GNU/kFreeBSD (Debian, #657793) - Unix, MIDI: Several updates to the Timidity backend. - Timidity, Windows MIDI: Support for Microsoft RMID format. - Windows MIDI: Fixed broken tempo with certain midi files (problem was introduced in version 1.5.1) - Windows software renderer: Fixed restoring of the saved video mode on startup (problem was introduced in v1.4.4.) - Windows software renderer: Disabled MGL DirectDraw support by default (causes more trouble than it's actually worth.) Can be enabled by -useddraw or -usedirectdraw command line switches, if required. - All platforms: Revised the cross-episode demo recording and playback behavior, allowing the intermission screens to be displayed. - All platforms: Removed code that prevented deathmatch and coop cvars to be set at the same time, which was reported for possibility of causing compatibility issues with mods. - All platforms: Majorly revised the support mechanism for different versions of progs. Added old v1.03 progs support to hexen2, added old v0.11 and v0.14 progs support to hw. - All platforms, h2ded: Removed error when neither coop nor deathmatch is set and rely on server admin. - All platforms, hexenworld client: Fixed a crash in the credits display when one waited long enough to see all the text to be printed. - Unix / gtk-launcher: Added support for gtk3. Several bug fixes and code cleanups. - Mac OS X: Fixed compilation failure due to a typo in MIDI code introduced in v1.5.1. - DOS: Added WatTCP (WATT-32) networking support for hexen2 internet play. - DOS: Added experimental DOS support for hexenworld using the WatTCP library. (doesn't work yet due to WatTCP bugs.) - DOS: Added "-nogus" command line option so that UltraSound initializa- tion can be skipped by the user if necessary. - Miscellaneous source code cleanups. ======================================================================== Changes in v1.5.1: (Nov. 21, 2011.) ======================================================================== - All platforms: Fixed an inventory icon scrolling bug. - All platforms: Removed a needless limitation on config and save names. - Linux (Unix/SDL): Fixed SDL software renderer bug that the image was not stretched to fill the screen. - Linux: Fixed the gtk-launcher crash with new glibc. - Windows: Updates to the midi playback code. - DOS: Fixed failure on computers with more than 2GB memory. - DOS: Fixed a stack corruption bug in VESA initialization. - DOS: Added support for the -cddev command line arguments. - Unix: Added some paranoid checks for string buffer sizes. - Linux/Unix: Provided cd-rip scripts for less common game media, i.e. the Xplosiv re-release and the Matrox m3D oem versions. - All platforms: Fixed logic in the hwmquery tool that would cause less number of servers to be displayed than what had been received from hwmaster. - Bumped versions for the hexen2 engine to 1.24, hexenworld to 0.24, hwmaster, hwrcon and hwterm to 1.2.7, hwmquery to 0.2.3 and the gtk launcher to 1.0.6. - Build system updates and source code cleanups. ======================================================================== Changes in v1.5.0: (Sep. 15, 2011.) ======================================================================== - bumped hexen2 engine version to 1.23, and hexenworld engine version to 0.23. bumped the gamecode version to 1.20. - added support for ogg, mp3 and wav external music files to be played instead of the original midi files. - added interface for music streams to the sound layer and dropped the SDL_mixer dependency. many changes in sound and midi layer. imported libtimidity into the source tree and applied several fixes. - sound: changed internal driver interface to provide a better chance of engine startup with sound, especially for linux (unix) users. - unix midi: reimplemented through the new music streams interface using timidity. - windows midi: ignore midi hardware volume adjustment capability under windows vista/7 which fixes music volume control messing with the main volume. - fixed a bug in multiple zones management, which wasn't easy to hit but was deadly when it was hit. - fixed an old game load/save corruption bug. - fixed an old load bug where items dropped by monsters not being visible or getting lost upon saving and loading the game. - fixed an old load bug where prize artifacts in the Temple of Mars not being visible or getting lost upon saving and loading the game or by changing the level and reentering. - fixed an old load bug where killed monsters didnt respawn in nightmare difficulty in the mission pack upon saving and loading the game or by changing the level and reentering. - software renderer (C-only): fixed a segmentation fault with Crusader's ice mace. - software renderer: fixed crashes at high resolutions with r_waterwarp enabled. - added new cvar sys_throttle in order to throttle the game loop just a little bit and run cooler on new fast systems. the default value of sys_throttle is 0.02, acceptable values are between 0 and 1. changing it to 0 disables throttling. - opengl: fixed overlapping flickering textures, i.e. the infamous brush model Z-fighting. the fix is controlled by gl_zfix: default value is 1 (enabled). setting it to 0 disables the fix and brings back the old behaviour. - opengl: always use GL_LINEAR as lightmap filter. made gl_texturemode command not affect lightmaps. - opengl: fixed an issue in net games where joining players would appear untextured (bug #3288081). - opengl: updated code to use glGenTextures(). - joystick, windows: added support for 6th joystick axis for up movement controlled by joyupthreshold and joyupsensitivity. - fixed hexenworld server issue which used to crash servers upon running the romeric5 (Temple of Mars) map, which now loads fine. - fixed h2ded (hexen2 dedicated server application) bug that the swords on the weapon racks in the demo2 and the village1 levels would be placed incorrectly (bug #3344613). - hexenworld server: fixed segmentation fault in case of bad pointers in NUM_FOR_EDICT() - fixed HexenC bug where, if there were a summoned imp around, a blinked away wizard wouldn't reappear even after imp went away (bug #3314808). - fixed a HexenC bug where the summoned imp didn't pick the Egypt snake boss as an enemy (bug #3314810). - improved soul spheres' HexenC code. - fixed a HexenC bug where medusa's gaze attack sound would persist when she is dead. - fixed an Eidolon/imp HexenC bug which used to render Eidolon idle. - fixed a HexenC bug which would prevent a yakman from appearing during the 'Trial of Strength' in the 'Temple of Phurbu' (tibet7) level of the mission pack, rendering the level not completable (bug #1112533). - fixed a mission pack HexenC bug where a pentacle monster in the tibet1 map became invulnerable when it got crushed by a door. - fixed a wheel of ages message to report 360 degrees instead of 30 in the egypt2 map. - fixed a HexenC bug which might prevent Eidolon to land. - fixed an Eidolon HexenC bug which might prevent the finale screen to trigger. - fixed a HexenC bug where werepanthers became "undead" when an assassin killed them by her bombs. - fixed mission pack HexenC bug which used to prevent the finale screen to trigger if Praevus were killed too quickly. - handled several map quirks thanks to careful works by Thomas Freundt and Keith Rozett. - fixed trigger_crosslevel issue with spawnflag 8 which used to prevent one of the prizes in Temple of Mars to appear. - removed the ugly hack of reducing teleport push speed to 225 in order to overcome the problem of reaching the Cathedral's balcony and the holy cross, and added a modified entities file for the Cathedral map instead. - added support for external entity file loading which makes it easy to handle map quirks. ent files are accepted only if they come from the same game directory as the map itself or from a searchpath with a higher priority. external entities loading is controlled by the cvar external_ents: default value is 1 (enabled). setting it to 0 disables external entities. - revised lit file loading. lit files are now accepted only if they come from the same game directory as the map itself or from a searchpath with a higher priority. - added new h2patch tool, an easy to use xdelta3-based standalone pak patch tool for purposes of updating to hexen2 v1.11 data files. updated the gtk-launcher's patch facility to use the same backend. - utils, hcc: fixed broken switch statement compilation when there is an implicit break in the last case. - utils, dcc: fixed a wrong decompilation of an "if" opcode into a "while". some source code revision. - utils, hcc: dropped support for the old version of hcc tool. - tested compilation using Visual Studio 2010. - source directory layout reworked, repository moved from cvs to svn. * Changes since v1.4.3 mostly during the unreleased 1.4.4-beta cycle: - fixed broken software renderer binaries from C++ compilers with x86 assembly enabled (do not use the bool keyword of C++). - compilations using the new gcc-4.4, 4.5 and 4.6 versions are tested and supported. - added support for 64 bit windows versions (x64). - added support for mingw-w64 compilers. - added support for Microsoft Visual Studio 2005 and Visual Studio 2008. - filesystem: fixed an oversight with builds without user directories, where the "-basedir" command line argument failed to change the internal userdir variables. - fixed stuffcmds so that arguments to commands can have '-' or '+' within their names. A command like "hexen2 +map mymap-01" works properly now. - increased default zone memory size to 384kb. - fixed a rare segmentation fault with certain command line arguments (bug was introduced sometime between version 1.3.0 and 1.4.0). - fixed a problem in sound effects code where some teleportation sounds might have got lost. - alsa audio: added workarouds for better behavior on some linux setups. - hexen2 client: pause/resume midi (background music) along with cdaudio upon pause message. - windows cdaudio: added new command line option -cddev for choosing the cdrom drive to use. syntax: -cddev E - windows cdaudio: fixed MCI errors when resuming the last track of a cdrom. - net: made the socket api usage compatible with windows especially with relation to 64 bit windows. - net (hexen2): fixed BSD and Mac OS X socket api usage. - opengl: reworked text and HUD scaling menu option. - opengl: fixed an issue that changing resolution from within the game might crash on Windows Vista. - software renderer: fixed a long standing stack corruption bug which used to cause alias models not to be drawn at all. - stricter checking on the values pulled out of .wav file chunk headers. - fixed rare game crash when battling bosses with assassin class, using the tome of power and launching at the enemy a lot of projectiles. - renamed model_t to qmodel_t in order to avoid conflicts on solaris. - fixed the "mirrored prints" problem of hexen2 console at high resolutions. - fixed the colored prints of hexenworld console. - fixed 'array going out-of-bounds' (utils/map.c) and bogus 'maybe used uninitialized' warnings (sbar.c) from gcc-4.3. - fixes to the SVGALib driver. it is functional now, both for hexen2 and for hexenworld clients. - fixed bug #2176384: in windowed mode, when you save using the menus, the game used to quit the menu without grabbing the mouse properly. - windows mouse: fixed behavior upon minimize/restore in windowed mode. - slightly increased the buffersize for sdl audio. made it to print a little more detailed info at startup. - fixed hexenworld client message parser so that midi and cdaudio don't play at the same time. - launcher, patching: rewritten the inter-threads log printing and fixed the erratic crashes on smp systems. launcher version is 1.0.5 now. - hexenworld master server: unknown packets are properly output as hexdump. hwmaster version is 1.2.6 now. - 3dfx gamma hacks are disabled by default at compile time (see the Makefile). - killed compatibility with user directories from HoT-1.4.0 and earlier which didn't operate with a data1 subdirectory. they are ancient history now. - added initial support for standalone free contents (mods) as an extra patch. - utils, jsh2color: worked around a stack corruption resulting in crashes due to the code's way of dealing with TEX_SPECIAL cases. - utils, lmp2pcx: Tweaked and documented palette file usage, allowed the embedded palette again. - utils, qbsp: accept more than one wad file specified in the value of the "wad" key. the wad file names must be separated by a semicolon. spaces are allowed in the file names. quoted paths aren't allowed. - utils, qbsp: the absolute paths in wad values are not touched, ie. the paths beginning with a '/' on unix or with a drive specifying string like "C:\foo\bar.wad" on windows. all other values with no path information or relative path information are prefixed with the project path. this fixes the issues reported for qbsp usage from within worldcraft. - added back some unused code for future reference. removed some truly dead code. - other minor fixes. docs/ReleaseNotes.old000066400000000000000000001072021444734033100151040ustar00rootroot00000000000000Hammer of Thyrion (uHexen2) release notes: ======================================================================== Changes in v1.4.3: (Apr. 04, 2008.) ======================================================================== - Fixed compilation with gcc-2.x and gcc-3.0.x series. - Re-enabled the success check during gfx.wad loading. - Fixed some errors in a few endianness macros. - Fixed a bug in key events handler which prevented the pause key events on some platforms. - Added back the use of password file on unix systems. - Passed NULL as the timezone argument to gettimeofday(). - Sound: Buffer lock fixes in SDL audio output. - Sound: internal changes in the driver interface. - Video/Input: The game now blocks/unblocks sound upon window focus loss or gain (SDL input driver). - Software renderer: Fixed fullscreen intermission and help picture size mismatches after resolution changes which would cause errors under certain conditions. - Fixed a glitch in hexenworld class selection menu. - Fixed invalid class handling glitches in hexen2. - Fixed a buffer overrun in dhcc (the hexenc decompiler) - Fixed a buffer overrun vulnerability in the hexenworld huffman decoder (Secunia advisory SA28124). - Fixed a theoretical buffer overrun in console input. - Several other fixes elsewhere for buffer size safety. - Replaced some technically dangerous memcpy calls with memmove calls. - Some protocol related clean-ups and documentation. - Fixed the paths in texutils cross compilation scripts. - Console: added ctrl-right and ctrl-left key handling. - Console line editing: made insert mode the default. - Launcher: added a progress bar for the patch operation. - Added support for the oem (Matrox m3D bundle) version. - Added a new compile time option to disable the user directories even on multi-user platforms. - Added a new compile time option to detect byte order at runtime (off by default, see q_endian.h). - Always use MAXHOSTNAMELEN sized buffers with gethostname - Added DOS support for the software renderer (we already have dosquake, why not have a dos-hexen2 ;) - Added an experimental svgalib client (software renderer, based on quake1 code) - not tested or supported, yet. - Updates in platform headers for better portability. - Completed the const correctness effort. - Incremented the engine versions to 1.21 for Hexen II and 0.21 for HexenWorld. - Incremented the gamecode version number to 1.19a. - Fixed and updated several build and patch scripts. - A few documentation updates and corrections. - Added PDP (NUXI) endian stuff (for reference, actually, neithed tested nor supported..) ======================================================================== Changes in v1.4.2: (Oct. 03, 2007.) ======================================================================== Many thanks to Juraj Styk for his help and for the code he provided for the non-intel software renderer support, to Davide Cendron for 64 bit testing and to Kevin Shanahan (aka. Tyrann) for useful discussions and ideas. - Engine version is now at 1.19 for Hexen II and 0.19 for HexenWorld. - Added support for 64 bit architectures: crashes on amd64 are fixed. - Added support for protocol 18: Hammer of Thyrion is now fully network- compatible with Raven's 1.11 windows version, as it has been with other Hexen II ports. - The net compatibility works both ways: When H.o.T is run as a client, it can connect to Raven-1.11 servers and can play just fine. On the other hand, if a 1.11 client wants to connect to a H.o.T. server, the server must be started with "-protocol 18" command line argument. - You can also load and play games saved with the 1.11 windows version and can playback the demos recorded with the 1.11 version. - Fixed and improved parsing of puzzles.txt: This fixes the bug where the names used to be listed as Unknown in the puzzle pieces inventory display (you can access the puzzle pieces inventory display using a key bound to the +showdm command, the default is q). - Updated the loading of strings.txt, infolist.txt and puzzles.txt. - Added ability of deleting saved games from within the game: press the DEL key in the load or save menu and press y at the dialog. The new console command deletesave does the same thing. - Updates to SDL input code. Added the Shift-Escape key combination as a new means for opening the in-game console. - Fixed wheelmouse for direct input mode in the windows version. Added the direct input support of HexenWorld to Hexen II. DirectInput is activated by the "-dinput" command line argument. - Revised pak data checking. - Added experimental support for using unpatched, off-the-cdrom 1.03 version pak files as a compile time option. Disabled by default. - Several cvar tweaks. - Made the load and connect commands to get rid of the menu or the console. - Made the item inventory bar to wrap around. - Fixed multiplayer episode selection in the menu system. - Updates to zone memory usage: a 256 KB secondary zone for static textures (sec_zone) is now initialized, which is especially useful for the software renderer and have its merits for the opengl version, too. - Revised several unsafe LoadStackFile and LoadBufFile usage. - Removed the useless net_vcr facility. - Tweaked the init sequence. - Changed the remnants of malloc usage into hunk or zone allocations. - Worked around crashes in gcc-4.0.x unit-at-a-time mode compilations by allocating several sizebufs dynamically instead of pointing to a static buffer. - Mac OS X: Ensured that the basedir always stays the same. - The gamecode is now at version 1.19. - Gamecode: Origin fixes for tomed fires of vorpal sword and purifier: paladin used to fire them always from the non-crouched eye position, and it was even the worst when he was looking up (and down). You won't be shooting your back when standing just in front of a wall and fire your tomed purifier to the sky, from now on. - Gamecode: Fixed an obscure bug where the assasin with her 4th weapon uses the tome of power and can't fire it (gamecode version 1.16a.) - Console: Added prefix matching support to the maplist command. - Console: A bug in duplicate name detection in the maplist command was fixed. - Console: Updated and sanitized the command lister procedures. - Console: Added a -developer command line argument which sets the developer cvar to 1 during the init phase (ie. before the config is read). - Console: Combined all Con_Printf and Sys_Printf family of procedures into a single flag based procedure. Added macros for compatibility with the old api. - Console: Revised logging system, made all Con_Printf and Sys_Printf output to be written into the logs from the start. Added a -devlog command line switch to enable logging of developer message even when the developer cvar isn't set. - Hexen II, UDP: added network interface scanning to overcome cases where the host address resolves to 127.0.0.1 (loopback) using the old hostent method. If, for some reason, you want to disable this feature, use the command line argument -noifscan. - Hexen II, UDP/Winsock: Added -localip command line option to hexen2, usage: -localip . This changes the ip address embedded in response packets for serverinfo and connect requests to use command- line-provided ip address. The server still binds to INADDR_ANY and it can see the broadcast requests. - Hexen II, UDP/Winsock: Added hexenworld (quakeworld) style ip binding option, usage: -ip or -bindip . This enables the server admins to bind to a specific IP address on a multi-homed host. Note that using this option will prevent the us from receiving broadcast packets, so the server discovery on the LAN will not work if the server is started this way. - Hexenworld: added -bindip as an alternative for the -ip option to follow the similar hexen2 change (both options are the same, -ip is checked first). - Fixed net driver checks in Hexen II dedicated server: It was broken in the 1.4.1 release and works properly now. - Fixed Datagram_Init() returning success even if it couldn't initialize any of the lan drivers. - Fixed a long-standing 0 byte UDP packet DoS bug in Hexen II. - Fixed a bug in NET_SendToAll (mainly used for reconnecting clients upon changing levels) where it didn't skip non-connected clients. - Fixed a crash when changelevel2 is requested for a non-existant map. - Fixed mission pack's doWhiteFlash server message: it used to affect the local client only. Naturally, the recorded demos didn't carry the message and the flash didn't appear, either. It works properly now. - Made the Hexen II dedicated server to exec not hexen.rc or config.cfg, but server.cfg if available. As an alternative, any config file can be exec'ed when starting the server, such as: "h2ded +exec myconfig.cfg" - Hexen II dedicated server now starts the demo1 map, if the server is still not active when initialization is complete. - Video: Added 'apply changes' and 'reset changes' items to the video options menu. - Software Renderer: Added support for non-intel cpus: x86 assembly is not needed anymore. - Software Renderer: Fixed a few endianness issues. - Software Renderer: r_transwater now always acts as a toggle for water translucency: it used to fail if a map was loaded with r_transwater disabled. works now. - OpenGL: Some speed-ups in the alias model drawing. - OpenGL: Fixed RGBA lighting. - OpenGL: Fixed sprite translucencies, such as punch hits, demoness' fireball and powered-up acidball attacks. - OpenGL: Fixed an invalid glTexParameterf call which used to result in extra lines being drawn on the screen. - OpenGL: Fixed bug that the array size for playertextures must be MAX_CLIENTS, but was hardcoded as 16. - Added ability to load external wal texture files instead of internal pixel data of the brush model (depends on the new r_texture_external cvar). - Sound: Added a new version of SDL audio driver which fixes a rare bug of game randomly erroring out with the message 'Cache_MakeLRU: active link'. - Sound: Made 22050 Hz the default sample rate. - Added external music file support via SDL_mixer as an optional patch. - CDAudio: Updated the volume setting procedures. - HexenWorld OpenGL: Fixed bug that netgraphtexture was not reset during reinitialization of the draw subsystem (used to affect the packet loss status graph by r_netgraph). - HexenWorld, OpenGL: Fixed bug that dynamic lights had a problem with RGBA lightmaps, such as torches didn't illuminate, but luminance mode was fine. Lightmaps in RGBA mode are working now. - HexenWorld, OpenGL: Added colored dynamic lights and extra dynamic lights (stuff copied over from hexen2). The gl_colored_dynamic_lights and gl_extra_dynamic_lights cvars and their menu items are functional now. - Launcher: Added integrated ability to patch the pak files. - The launcher now scans the installation directory against known game versions for pak file health and pops up the patch window with a short report if it finds something bad. It now disables the mission pack or hexenworld options according to the results it took from the installation scanning. - Launcher: Added ability to specify a different data installation directory. - Launcher: Added ability to specify extra command line arguments which aren't directly supported by the graphical interface. - Launcher: Many clean-ups. Now at version 1.0.2. - Utilities: Added bsp2wal which extracts all texture data into hwal files. - Utilities: Added lmp2pcx which converts hexen2 texture data into pcx and tga. - Utilities: Fixed the broken project path decision in qbsp. it doesn't scan for a maps directory anymore. - Utilities: Added a findfirst/findnext implementation to the common library. - Utilities: Several fixes in dcc. - Utilities: Minor updates to the hexenworld utilities hwmquery, hwrcon and hwterm. Added extra patches against xqf and qstat for similar functionality. - Imported the latest xdelta-1.1 sources into the tree. - A lot of code clean-up all over the tree. Updated inline commentaries, fixed many printf format string errors, started a large scale attempt at improving const-correctness. ======================================================================== Changes in v1.4.1: (Oct. 30, 2006.) ======================================================================== - Added support for QNX. - Added support for Mac OS X. - A few MorphOS fixes (some of which are probably correct for Amiga, as well.) - Added a hexen2 dedicated server application (h2ded). - The data1 subdirectory is now used in the user directories. Users' data from older versions are automatically updated. - Made console and menu to disappear for playdemo like it does in timedemo. - Fixed demos freezing upon changing maps. - Fixed demo recording and playback covering multiple episodes: The intermissions are now skipped while recording demos across multiple levels, and recording is stopped at ending scenes. - Made background music to stop upon disconnect. - Disabled the forced centerview (pitch drifting) when flying in hexen2. The behavior now matches that of hexenworld. - Removed unintended viewmodel movement upon setting viewsize to maximum - Fixed client segmentation faults in heterogenous client/server situations, such as pure hexen2 client vs. mission pack server: we now Host_Error upon missing models when connecting to a server. - Added network compatibility support for Korax's UQE-Hexen2 v1.13 - No erroring out if we can't bind to a network interface at init phase. - Made the alias command to display the value of the given alias instead of clearing its value. - Added new console commands: unalias, which deletes the given alias command, and unaliasall, which deletes all aliases. - Unbinding a key now frees its memory. - Disabled the sys_memory, sys_cache and sys_stats console commands for server-only binaries except for debug builds. - Disabled the VCR facility by default. After all, it is not an ordinary demo recording but a server only feature for debugging server bugs. - Fixed and improved parsing of maplist.txt and config.cfg. - Engine version is now 1.16 for Hexen II, and 0.18 for HexenWorld. Gamecode is now at version 1.16. - Gameplay: Fixed long-standing hexenc bug in mezzo_reflect_trig_touch which used to crash the game with an additional message about fangel/deflect.wav when changing levels. - Gamecode: Disabled impulse 11 which was a developer-only command but could be issued from any client and might clobber the server. - Gamecode: Cleaned up and tweaked hexenworld and siege map cycling. - Gamecode: Fixed hexen2 and hexenworld server crashes upon deathmatch level change in cases of a custom map being run and there is no map cycling. The mission pack actually didn't used to crash but it always failed changing into a new level. it is now fixed, as well. - Video: Enabled fullscreen/windowed switching through the menu system in cases where instant SDL toggling doesn't work. - Video: Added procedures to re-initialize the draw subsystem upon game directory changes. Hexenworld already uses it. Hexen II can use it in future versions. - OpenGL: Made scaling of the text and hud size adjustable from the menu system. - OpenGL: Fixed the light level being incorrectly set when the game is run with rgba lightmaps which used to prevent the cloaking ability of the assasin. - OpenGL: Fixed some endianness issues. - OpenGL: Added console background stretching (cvar: gl_constretch, 0 or 1). - Sound: Added mute, volumeup and volumedown console commands. - Sound, OSS driver: Endianness fixes. - Sound, OSS driver: The GETOSPACE ioctl must be issued after setting the parameters. - Sound: Added 48000, 24000 and 16000 Hz to the array of sampling rates. Should help with 48khz AC97 chips. - Sound: Added an experimental SUN audio driver for native audio in OpenBSD, NetBSD and SUN. - CDAudio: The case when the user changes track without using the "cd" command should now be handled correctly on linux and bsd. - CDAudio: Added -cddev commandline argument support to sdl cd driver. - CDAudio: Merged a better sdl cdaudio driver which doesn't cause slow- downs. - HexenWorld: Fixed a buffer overflow vulnerability in Huffman packet decompression. - HexenWorld: Fixed wrong charset and conback display problem upon gamedir changes. - HexenWorld: Cleaned-up hexenworld master server. Added the "clear" console command to clear the list of registered servers. - HexenWorld: Added hwmquery, a tiny utility for querying hexenworld master servers. - HexenWorld: Fixed hexenworld gamecode so that it can be played using the demo version with crusader and necromancer classes. - Utilities: Added big-endian support to the hcc (the hexenc compiler), dcc (dhcc: progs decompiler) and jsh2color (the litfile generator). - Utilities, qbsp: Do the name search in a case insensitive way in the bsp compiler: some wad files doesn't have the texture name in expected case. - Utilities, dhcc: Several fixes including a workaround for crashes when compiled by gcc-4.x, and a fix for bad decompilation of the mission pack progs. - Launcher: Fixed finding of actual command path. - Launcher: Fixed broken display of launcher binary name when run using a symlink. - Launcher: Updated for engine-side changes, version 0.8.0. - Ensured that string operations are safe regarding their buffer size in many places. - Added support for yasm (v0.5.0) as an alternative assembler - Fixes and improvements in the build system - Many compile and linkage fixes. - Many other bug fixes, clean-ups and small improvements. ======================================================================== Changes in v1.4.0: (Apr. 28, 2006.) ======================================================================== - Engine version is now 1.15 for Hexen II, and 0.17 for HexenWorld. Gamecode is now at version 1.15. - The same binary now handles both original hexen2 and the mission pack. No need for two different files. Mission pack is simply activated by the -portals or -missionpack commandline switch. - The same hexen2 binary now transparently handles both hexen2-1.11 and h2mp-v1.12 progs.dat files. - Unix version now yields the CPU when the game is paused, minimized or doesn't have focus. - Better error checking when saving games on unix - Increased minimum heapsize to 16 mb to prevent hunk allocation errors - host_hunklevel and gl_texlevel are now set before execing hexen.rc to prevent "bad things" (TM) from happening - Added flags support to the cvar system. - Added support for the pak files in users' directories. - Several fixes and updates to the filesystem handling. - Video: Added support for on the fly resolution change for both opengl and software renderers. The video modes menu is now fully functional. - OpenGL: Suffort for colored lights and external lit files - OpenGL: A new opengl features menu to control most opengl options. - OpenGL: Fixes in palettized texture uploading code - OpenGL: Switched back to original h2 code for palettized textures and texture resampling for better quality, left the hw code as a compile time option - OpenGL: Moved sdl multisampling capability check from compile time to runtime for much better compatibility - OpenGL: Fixed glows of floating models (eg. mana) not floating along with the model - OpenGL: Fixed win32 version to use dlsymmed opengl function calls - OpenGL: Took care of flickering problem while drawing the loading plaque - OpenGL: Fixed the loading plaque not being displayed when antialiasing was in effect - OpenGL: Fixed the loading screen not being displayed correctly on win32 when antialiasing was in effect - OpenGL: Added stencil buffered shadows - OpenGL: Added the lightmaps portion of adjustable filters which have been left missing since v1.2.3 - OpenGL: Disabled model data caching (*.ms2) which may lead to problems - OpenGL: Disabled multitexture for 3dfx sky rendering, which was broken - OpenGL: Built-in support for 3dfx specific gamma - Software Renderer: Updated sdl software video driver and made the loading plaque and the rotating skull to work - Software Renderer: Fixed mgl library linkage for win32 software renderer - Software Renderer: Fixed win32 software renderer not switching to the resolution saved in config.cfg - Software Renderer: Ported win32 software renderer changes from hexen2 to hexenworld - Console: Improved insert/overwrite mode console editing and command completion - Console: Made keys autorepeating work in unix - Console: Added new cvarlist and aliaslist commands, changed the old command lister name from list to cmdlist. - Console: Added maplist command to list maps in pakfiles and in game searchpath - Sound: Several small driver updates - Sound: Added support for systems without OSS sound - Fixed objectives display in the expansion pack which was broken during the 1.3.0 development cycle - Added checks for coop and deathmatch not be set at the same time - Made noexit, timelimit and fraglimit to matter only for deathmatch, not for coop and single player, as they should. - Fixed a bug where with viewsize (scr_viewsize) being set to 120, the game wouldn't start with mini status bar unless the user did a size-up - Set the default playerclass to paladin: fresh installations of original hexen2 without a config shall no longer fail when run first time with a +map XXX commandline argument - The menu/console now automatically disappears when playing a timedemo - The panels now properly disappear upon starting a new game through the menu system - Unix: Minimum required SDL (and SDL_mixer) version is now 1.2.4 - Win32: Fixed to compile with start-up splash screens enabled - Made udp to quietly absorb empty packets - Fixed glh2 and glh2mp segfaults when run as dedicated server - HexenWorld: Fixed ability string indexes for the demoness and dwarf classes and armor class display - HexenWorld: Fixed the crash when the server is spawned with dmmode 1 - HexenWorld: Fixed server not correctly updating its gamedir serverinfo under some conditions - HexenWorld: Fixed window restore bug that prevented the screen from being updated - HexenWorld: User directory information now properly updated upon gamedir changes - HexenWorld: Added ip interface binding option (-ip xxx.xxx.xxx.xxx) to the server - HexenWorld: Added sanity checking to and fixed the off by one error in -ip option of the master server - Hexenworld: server shall now flush its logfiles upon every print - HexenWorld: Added -noportals command line switch to skip mission pack support if required - HexenWorld: Changed server log filename from qconsole.log to hwsv.log - Utilities: Big tidy-up. - Utilities: Added jsh2color (lit file generator for colored lights) - Utilities: Added hexenworld standalone rcon and terminal tools (hwrcon and hwterm) which make it possible to remotely administer servers without installing a full client. (See in: hw_utils) - Major build system update - Added math optimization flags to utilities and hwsv - Added cross compilation support on unix for the win32 version - Several other fixes and lots of code clean-up and improvements ======================================================================== Changes in v1.3.1: (Apr. 20, 2006.) ======================================================================== This release contains only strict bug-fixes to v1.3.0: - fixed objectives in the expansion pack which was broken during the 1.3.0 development cycle by a bad gcc4 fix. - fixed the snd_scaletable init breakage with gcc-4.1 at -O1 and higher. From Kevin Shanahan, aka Tyrann. - fixed win32 window restore bug that prevented the screen from being updated in hexenworld. - fixed a typo in the utilities' makefiles ======================================================================== Changes in v1.3.0: (Aug. 29, 2005.) ======================================================================== - Engine version is now 1.14 for hexen2, 0.16 for hexenworld. Gamecode is now at version 1.14. - Gameplay: Teleportation push-speed is now the same as Raven's original except for single player game in the Cathedral level. - Gameplay: Added Pa3PyX' fire delay bits for paladin's staff. - Gameplay: Disallowed cycling to a weapon without proper mana. - Gameplay: Enabled impulse 34, (puzzle piece inventory listing) - Gameplay: Speed key now acts as slow key when "always run" is chosen. - Mouse: mlook and lookspring fixes. - Menu: Multiplayer episode selection fixed. - Mini-status is now allowed when hud transparency is set. - Several pointer fixes which would lead to endless loops, bad movement and such. - Fixed a sneaky demo playback bug which caused a fake freeze-like appearance when no next demos were found. - ChaseCam clipping and pitch fixes (still not perfect, but much better than what we previously had.) - Endianness detection fixes (now decided at compile time not in runtime.) - We now allocate on the hunk (or zone) and don't malloc system memory unless necessary. - Increased hexen2 minimum zone size to 256 kb which now matches that of hexenworld - Fixed ALSA minimum required version detection. - Added missing console input code for dedicated server cases. - Added lower/upper boundaries (8mb/96mb) for -heapsize argument. - Added lower (256kb)/upper (1mb) boundaries for -zone argument. - Many build system / makefile fixes. - Huge code cleanup. The source should be gcc4-ready now. Many dead or broken code are removed, as well. - Other smallish fixes such as out-of-bound arrays, etc. - Video: Added support for window manager icon using xbm format. - OpenGL: Improved support for 3dfx Voodoo Graphics and Voodoo2 accelerators including native gamma support through the lib3dfxgamma library. A small HOWTO for these boards is provided, as well. - OpenGL: Added 2-TMU multitexturing support (doesn't work correctly for 3dfx cards, yet.) - OpenGL: Added 2 and 4 sample FSAA support (SDL >= 1.2.6 is required) - OpenGL: Revived palettized textures support which helps saving video memory on old low-end cards, activated by -paltex command line switch. - OpenGL: Added texture checksumming which fixes "white" textures when gl_purge_maptex is not set. - OpenGL: Added support for the -conwidth argument which enables larger, thus readable text and bigger hud in high resolutions. - OpenGL: mesh cache is now searched directly under com_userdir/glhexen, not in search_path, fixing the crazy polygons in mods bug. - OpenGL: Prevent Z fighting in shadows. - OpenGL: Added a gl-gamma trick by LordHavoc which is activated if all other methods of brightness control fail. - OpenGL: Added and fixed (worked-around) "crosshair 2" support of hexenworld to hexen2, which now uses 32x32 pixmaps. pixmap loading procedure stolen from the twilight project. Also merged crosshaircolor (0-255) support of original quake. - Software Renderer: Added hexenworld's "crosshair 2" support to hexen2. - Software Renderer: Added hexenworld's transparent statusbar to hexen2. Cvar controlled: "sbtrans N", N = 0: solid, 1: slightly transparent, 2: very transparent. - Sound: Added device selection support (-ossdev) to OSS sound. - Sound: OSS sound now mmaps /dev/dsp with PROT_READ flag set. - Sound, CDAudio: Documented the device selection options. - CDAudio: Updated code so that it doesn't completely fail when there is no disc in the drive during initialization. - CDAudio: Fixed a volume update bug. - Music: Fixed music volume from config.cfg not taken into action on startup - CDAudio: Added FreeBSD and SDL cdrom code. - MIDI: Fixed midi not playing when midifile exists in the game's search path but not in /.midi - MIDI: Fixed an oversight in reinit_music which resulted in a needless restart of midi music upon pressing Esc twice. - Filesystem: Userdir support is expanded to cover the whole code including server logs, recorded demos, downloaded files and all others. - Filesystem: COM_InitFilesystem is updated and demo/retail detection is improved. - Mouse: code re-visited with bug-fixes and cleaned-ups. - Mouse: -nomouse option is revived to completely disable mouse usage upon request. - Hexenworld: Fixed a longstanding client crash with lightning streams. - Hexenworld: Ported all features and fixes (such as glow code, model recycling, opengl texture flushing, hud fixes, etc) which resided in hexen2. The two trees are now in sync feature-wise. - Hexenworld: Added hexenworld master server (by Marc Allaire, aka. Kor Skarn). - Hexenworld: Connection is no longer refused upon client/server version mismatches. Such things should rely on protocol version, not simply version. - Hexenworld: Fixed Dwarf class was never being enabled in Siege game in the menu system because of a com_gamedir oversight. - Hexenworld: Fixed server sending the wrong message for cdrom track which resulted in cd music not playing on the client side. - Tiny update for failure cases of NET_SendPacket and NET_GetPacket. - Launcher: Cleaned-up, bug-fixed and and updated to version 0.6.1 to support more options. - FreeBSD: Updated the code to support FreeBSD ( >= 5.3 ). - Win32: Restored code's compilability on Windows platform (using MinGW and MSYS.) HoT tree is now functional on Win32. ======================================================================== Changes in v1.2.4: (Mar. 06, 2005.) ======================================================================== The primary focus of this new release may be considered as sound and music changes: Two new sound drivers, SDL and ALSA, were added. People who used to have issues with sound should now play the game with sound. Volume adjustment issues for music are also addressed. A more detailed list of changes is below. Many thanks to Levent Yavas for his remarkable patience in testing of this release. - SDL and ALSA sound drivers are added. These should help people having sound problems with the OSS driver. Sound drivers can be chosen either by using the game launcher or by command line switches. ALSA requires 1.0.X versions of alsa kernel modules and ALSA libraries. Midi music doesn't work with SDL sound driver due SDL and SDL_mixer conflicts: to be addressed in future versions. - Fixed cd music stopping upon changing volume. - Added working volume control for cdrom and midi music. - Midi music now works for HexenWorld. - Further small fixes to the core sound code and to OSS driver. - Fixed bug where demos played without models when the -nosound command line switch was in effect. - Added support for mouse buttons 4 and 5 (thanks Julien Langer) - Fixed statusbar being displayed with an ugly brown-colored band - Added sky alpha (transparency for lower sky layer) - Added transparency for console background (from hexenworld). - Fixed hexenworld client bug causing mouse not being activated after connecting to a server. - Fixed "flush_textures" decision which used to be always true. Loading the same map should now be faster on old systems. - Ported many features/fixes from Hexen2 to HexenWorld. There should be less difference between the two now. - The game launcher now scans for known hexenworld game-types and hexen2 botmatch mods, and provides a list menu. - The launcher interface now has two modes, basic and detailed. - Many internal changes to the source code (see the CHANGES file for such details.) * HexenWorld Gamecode changes/fixes: - Server no longer crashes on maps rider1a, rider2c, romeric6, meso9 and eidolon - Added map cycling for HexenWorld server * Hexen2 Gameplay changes/fixes (merged from Pa3PyX' sources): - Fps-independent crossbow, setstaff and flameorb fire rates - No more 20 tornadoes per second from meteor staff at 200 fps - Fix for ravenstaff bug where splitting missiles would collide with each other. - Assasin cloak ability re-write. - Necromancer soul steal adjustments to make it really useful. - Fixes to demoness abilities according to the manual - Crusader's full-mana ability starts at level3, assasin's backstab and crusader's holy strength start at level6. ======================================================================== Changes in v1.2.3: (Jan. 20, 2005.) ======================================================================== - The user directory is ~/.hexen2 for the registered version and ~/.hexen2demo for the demo version, from now on. - The demo ending screen message is fixed. - Added build options for the demo version. - Source code cleanup. - More merges of fixes/features from hexen2 to hexenworld. - Documentation clarifications on network compatibility. - Launcher: Fixed, tweaked, added support for GTK+-2.x. - Gameplay: Typo fixes in strings.txt of Hexen2. - Gameplay: Holy item in the Cathedral balcony wasn't reachable (reduced game's teleport-pushing speed.) - Gameplay: Fixed Eidolon losing his hostility under certain conditions (merged newer eidolon code of the mission pack.) - Gameplay: Fixed Eidolon failing to landing after his initial jump. - Fixed GL Glows menu item, made it control gl_other_glows, too. - New menu item for "Previous Weapon". - New menu item for Fullscreen/Windowed toggling. - Wheelmouse support added. - Mouse2/Mouse3 buttons behavior is fixed. - _windowed_mouse also defaults to 1 for opengl version. - Mouse can now be disabled for fullscreen modes. - Filesystem: The -game argument is now correctly honored for the user directories. - Intermission screens are drawn fullscreen even on high resolutions. Same for the Help screens. - OpenGL: Texture delete/reload support, no more dying on texture cache mismatches. Fixed textures going missing sometimes. - OpenGL: gl_ztrick also disabled by default in HexenWorld. - OpenGL: Fixed gl_other_glows option not being written into config. - OpenGL: Maximum texture size is now detected using glGetIntegerv, not vendor string. - OpenGL: Removed second arg from the function EmitSkyPolys(). - OpenGL: Several other cleanups. ======================================================================== Changes in v1.2.2a/b: (Jan. 15, 2005.) ======================================================================== - Can now toggle between fullscreen and windowed modes. - Transparent console background. ======================================================================== Changes in v1.2.2: (Dec. 19, 2004.) ======================================================================== - Heapsize is now 32768 by default - Centered the end of game messages - Bugfix: in window mode, mouse can now be bound in the Options menu. - Bugfix: in window mode, "-nomouse" option works. - Music automaticly restarts after changing it in Options menu. - Partial fix for Demoness fire-weapon OpenGL bug. - HexenWorld: dlsym'ed all calls to GL functions. - Added an experimental SDL sound driver. Very buggy for now. - Revived the GTK launcher from AoT: fully functional, works fine with fullscreen opengl modes. ======================================================================== Changes in v1.2.1: (Nov. 26, 2004.) ======================================================================== First release of Hammer of Thyrion based on Anvil of Thyrion v1.2.0. Changes from AoT-1.2.0: - The "No Boss" bug is fixed. - Proper fullscreen mode(s) for OpenGL. Screen sizes are selectable with "-width" and "-height" options. - Mouse grab in window modes, which is released when menus appear. - Interactive video modes in software game disabled. Replaced Video Mode menu with a helpful message. - Merged the OpenGL glows patch by Juraj Styk. - New menu items for GL Glow, Chase mode, Draw Shadows. - Changes to initial cvar_t variables r_shadows and _windowed_mouse - Two general overflow bug-fixes by Pa3PyX. docs/SrcNotes.txt000066400000000000000000000151631444734033100143200ustar00rootroot00000000000000 ------------------------------------------------------------------------- Hexen II: Hammer of Thyrion (uHexen2) This is a pretty messy draft of notes I gathered while working on the Hexen II, HexenWorld and utilities code. For known bugs, see the file named BUGS. This file lists several issues in the code which mostly are not actually bugs, but still issues, anyway. I also provided some documentation and some info about hexen2-specific data files, although very incomplete at the moment. ------------------------------------------------------------------------- Client to Server / Server to Client code intrusions: -------------------------------------------------------------- host.c and host_cmd.c already hold code shared by both the client and the server, they are quake standart. Aside from them, here are the specific intrusions in the hexen2 code: - sv_main.c :: SV_SpawnServer() : Loading Progress Bar data (server operations are progress bar'red during the loading stage) vars: total_loading_size, current_loading_size, loading_stage functions: D_ShowLoadingSize() - sv_main.c :: SV_SpawnServer() vars: scr_centertime_off - sv_main.c :: SV_SpawnServer() : gl texture flush data vars: flush_textures (if the new map name is different we'll flush the textures from opengl. can't detect map name change anywhere else.) - sv_kingofhill : sv_main.c, cl_parse.c, sbar.c King of the Hill status (mission pack specific). Broken by design: the server message svc_update_kingofhill is sent by the progs and is read by cl_parse.c, therefore if you are running a dedicated server it will never be read and always be reset at every new map start. - cl_playerclass : host_cmd.c, pr_edict.c For the mission pack, this value is sent to the progs as soon as the progs is loaded. For single player games, it makes worldspawn() to load the specific player model stuff and not all of the player models. It can't be acquired by the server early enough by way of the client connection messages.. - sv_gravity : r_part.c, in function: R_UpdateParticles() Used for particle velocity calculations. Hexen II only. SV_SpawnServer, Host_ShutdownServer and SCR_UpdateScreen: -------------------------------------------------------------- The timeout in SCR_UpdateScreen(), and more (actually a bug) : When in a map, suppose that you touched a level exit but the map file for the changelevel target doesn't exist: SV_SpawnServer() will issue an error message and return, SCR_UpdateScreen() will wait for its timeout (it will feel like the game has crashed/frozen because the screen will not be updated due to the boolean scr_disabled_for_loading being true) and then spit the stupid 'load failed' message. Here, SV_SpawnServer() didn't do any Host_ShutDown(), didn't drop any of its clients and didn't decrement the active connections. Now if you try loading a saved game, Loop_Connect() naturally fails with a message 'no qsocket available'. The problem is inherited from the Quake engine. As of uHexen2 v1.5.6, this issue can happen with clients connected to old servers only. Protocol 18 vs 19, soundnum message size, MAX_SOUNDS: -------------------------------------------------------------- Quake1 protocol sends the soundnum as a byte through its net protocol, it has MAX_SOUND defined as 256. Although we don't have the source code to it, Hexen2-1.11 obviously did the same thing. On the other hand, the mission pack, Portal of Praevus, for which we have the source code, has a different behavior: H2MP defines MAX_SOUNDS as 512 which is not directly sendable as a byte. SV_StartSound() checks if the number of the sound is more than 256, if yes, then it sets a flag, SND_OVERFLOW, to the field_mask and substracts 256 from the number; the client code checks accordingly. By this way, numbers larger than 256 ARE actually sent as a byte. On the other hand, the SV_StartSound code as released by Raven was buggy: it used to set SND_ATTENUATION flag instead of SND_OVERFLOW; what a mess.. And here is even more mess: While SV_StartSound() is using this mechanism and sending the soundnum as a byte, PF_ambientsound() is sending it as a short! Did the 1.11 version actually use 256 sounds as quake1, or did it use 512 sounds and used this mechanism in order to send it as a byte, we do NOT know that by direct evidence. However, hexenworld sends that number as a byte (ie. quake1/quakeworld way) and defines MAX_SOUNDS as 256, it does not use the SND_OVERFLOW mechanism. Since hexenworld seems to have forked from hexen2-v1.11, it is plausible that protocol 18 of hexen2-1.11 didn't use the SND_OVERFLOW thing.. Also see the notes at the end of protocol.h in the original code. Intermission indices: -------------------------------------------------------------- Accepted range: 1-12 num: strings.txt index: --------------------------------- ------------------- 1: famine ending (episode 1 to 2) 395 2: death ending (episode 2 to 3) 396 3: pestilence ending (episode 3 to 4) 397 4: war ending (episode 4 to last) 398 5: finale for the demo version 408 (410 for the mission pack.) 6: eidolon, finale, part 1/3 392 7: eidolon, finale, part 2/3 393 8: eidolon, finale, part 3/3 394 9: finale for the bundle version 391 10: mission pack, praevus ending 538 11: mission pack, eps. change to tibet 545 12: mission pack, opening 561 #12 is not a 'real' intermission: - running a new single player mission pack game through the menu system starts an 'intermission screen' (#12) first: some scrolling text giving a story background. - this intermission screen #12 is only started by this menu system: unlike the other intermissions, it isn't progs (server) controlled. - there is no svc_intermission message for this, so, we set cl.completed_time and cl.intermission by hand. - since the user may just have started the game, we must load strings.txt for the scrolling text before setting cl.intermission, if necessary. - running the keep1 map is also a client-side thing: it is triggered by Key_Event() when the user hits a key. Specific Hexen II data files: (This is, and will be, far from being a complete list. They are just notes of mine when I felt like making notes...) -------------------------------------------------------------- gfx/invpal.lmp : 262144 bytes, 8 bit pallette for the opengl version. Can be created from the palette during start-up, too. gfx/menu/bigfont.lmp gfx/menu/bigfont2.lmp : a set of 27 characters with large font, A-Z and '/', for main menu titles. 20x20. bigfont2 is the one actually in use now. gfx/menu/fontsize.lmp : 729 bytes. font size data for the bigfont characters. Can be created from the bigfont file during start-up, too. docs/TODO000066400000000000000000000021121444734033100124750ustar00rootroot00000000000000Hexen II: Hammer of Thyrion (uHexen2) TODO: ------------------------------------- - Add a pak-patch application and a launcher for Mac OS X. - Port code to SDL2. - Re-visit the messy mouse grab code and clean it up. - Clean-up the opengl code, modernize it. - Proper vsync support for opengl. - Multisampling support for windows opengl. - Support for several image formats (jpeg, png, etc) - Maybe model interpolation stuff? - Increase several engine limits to allow for larger maps, as in Fitzquake and QuakeSpasm engines. - Software renderer: maybe add 16 bit support (r_pixbytes = 2, and even 4 for 32 bit)? The current code is largely missing translucency pieces and the d_8to16 and d_8to24 translation tables. - Net: Find some way to workaround the infamous router troubles - Add sound font support to Timidity. - ALSA, OSS: Add sequencer support for MIDI playback. - DOS: Implement MIDI music playback (MPU-401, AdLib/FM synth.) - utils: qbsp, light and vis: improvements? - utils, texutils: add more texture tools (pcx2wal, etc.)? - More unification of hexen2 and hexenworld trees engine/000077500000000000000000000000001444734033100123265ustar00rootroot00000000000000engine/h2shared/000077500000000000000000000000001444734033100140265ustar00rootroot00000000000000engine/h2shared/SDLMain.h000066400000000000000000000005661444734033100154350ustar00rootroot00000000000000/* SDLMain.m - main entry point for our Cocoa-ized SDL app Initial Version: Darrell Walisser Non-NIB-Code & other changes: Max Horn Feel free to customize this file to suit your needs */ #ifndef _SDLMain_h_ #define _SDLMain_h_ #import @interface SDLMain : NSObject @end #endif /* _SDLMain_h_ */ engine/h2shared/SDLMain.m000066400000000000000000000272131444734033100154400ustar00rootroot00000000000000/* SDLMain.m - main entry point for our Cocoa-ized SDL app Initial Version: Darrell Walisser Non-NIB-Code & other changes: Max Horn Feel free to customize this file to suit your needs */ #if defined(SDL_FRAMEWORK) || defined(NO_SDL_CONFIG) #include #else #include "SDL.h" #endif #include "SDLMain.h" #include /* for MAXPATHLEN */ #include /* For some reaon, Apple removed setAppleMenu from the headers in 10.4, but the method still is there and works. To avoid warnings, we declare it ourselves here. */ @interface NSApplication(SDL_Missing_Methods) - (void)setAppleMenu:(NSMenu *)menu; @end /* Use this flag to determine whether we use SDLMain.nib or not */ #define SDL_USE_NIB_FILE 0 /* Use this flag to determine whether we use CPS (docking) or not */ #define SDL_USE_CPS 1 #ifdef SDL_USE_CPS /* Portions of CPS.h */ typedef struct CPSProcessSerNum { UInt32 lo; UInt32 hi; } CPSProcessSerNum; extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn); extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn); #endif /* SDL_USE_CPS */ static int gArgc; static char **gArgv; static BOOL gFinderLaunch; static BOOL gCalledAppMainline = FALSE; static NSString *getApplicationName(void) { const NSDictionary *dict; NSString *appName = 0; /* Determine the application name */ dict = (const NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); if (dict) appName = [dict objectForKey: @"CFBundleName"]; if (![appName length]) appName = [[NSProcessInfo processInfo] processName]; return appName; } #if SDL_USE_NIB_FILE /* A helper category for NSString */ @interface NSString (ReplaceSubString) - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString; @end #endif @interface NSApplication (SDLApplication) @end @implementation NSApplication (SDLApplication) /* Invoked from the Quit menu item */ - (void)terminate:(id)sender { /* Post a SDL_QUIT event */ SDL_Event event; event.type = SDL_QUIT; SDL_PushEvent(&event); } @end /* The main class of the application, the application's delegate */ @implementation SDLMain /* Set the working directory to the .app's parent directory */ - (void) setupWorkingDirectory:(BOOL)shouldChdir { if (shouldChdir) { char parentdir[MAXPATHLEN]; CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle()); CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url); if (CFURLGetFileSystemRepresentation(url2, 1, (UInt8 *)parentdir, MAXPATHLEN)) { chdir(parentdir); /* chdir to the binary app's parent */ } CFRelease(url); CFRelease(url2); } } #if SDL_USE_NIB_FILE /* Fix menu to contain the real app name instead of "SDL App" */ - (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName { NSRange aRange; NSEnumerator *enumerator; NSMenuItem *menuItem; aRange = [[aMenu title] rangeOfString:@"SDL App"]; if (aRange.length != 0) [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]]; enumerator = [[aMenu itemArray] objectEnumerator]; while ((menuItem = [enumerator nextObject])) { aRange = [[menuItem title] rangeOfString:@"SDL App"]; if (aRange.length != 0) [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]]; if ([menuItem hasSubmenu]) [self fixMenu:[menuItem submenu] withAppName:appName]; } } #else static void setApplicationMenu(void) { /* warning: this code is very odd */ NSMenu *appleMenu; NSMenuItem *menuItem; NSString *title; NSString *appName; appName = getApplicationName(); appleMenu = [[NSMenu alloc] initWithTitle:@""]; /* Add menu items */ title = [@"About " stringByAppendingString:appName]; [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; [appleMenu addItem:[NSMenuItem separatorItem]]; title = [@"Hide " stringByAppendingString:appName]; [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; [appleMenu addItem:[NSMenuItem separatorItem]]; title = [@"Quit " stringByAppendingString:appName]; [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; /* Put menu into the menubar */ menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; [menuItem setSubmenu:appleMenu]; [[NSApp mainMenu] addItem:menuItem]; /* Tell the application object that this is now the application menu */ [NSApp setAppleMenu:appleMenu]; /* Finally give up our references to the objects */ [appleMenu release]; [menuItem release]; } /* Create a window menu */ static void setupWindowMenu(void) { NSMenu *windowMenu; NSMenuItem *windowMenuItem; NSMenuItem *menuItem; windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; /* "Minimize" item */ menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; [windowMenu addItem:menuItem]; [menuItem release]; /* Put menu into the menubar */ windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; [windowMenuItem setSubmenu:windowMenu]; [[NSApp mainMenu] addItem:windowMenuItem]; /* Tell the application object that this is now the window menu */ [NSApp setWindowsMenu:windowMenu]; /* Finally give up our references to the objects */ [windowMenu release]; [windowMenuItem release]; } /* Replacement for NSApplicationMain */ static void CustomApplicationMain (int argc, char **argv) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; SDLMain *sdlMain; /* Ensure the application object is initialised */ [NSApplication sharedApplication]; #ifdef SDL_USE_CPS { CPSProcessSerNum PSN; /* Tell the dock about us */ if (!CPSGetCurrentProcess(&PSN)) if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103)) if (!CPSSetFrontProcess(&PSN)) [NSApplication sharedApplication]; } #endif /* SDL_USE_CPS */ /* Set up the menubar */ [NSApp setMainMenu:[[NSMenu alloc] init]]; setApplicationMenu(); setupWindowMenu(); /* Create SDLMain and make it the app delegate */ sdlMain = [[SDLMain alloc] init]; [NSApp setDelegate:sdlMain]; /* Start the main event loop */ [NSApp run]; [sdlMain release]; [pool release]; } #endif /* * Catch document open requests...this lets us notice files when the app * was launched by double-clicking a document, or when a document was * dragged/dropped on the app's icon. You need to have a * CFBundleDocumentsType section in your Info.plist to get this message, * apparently. * * Files are added to gArgv, so to the app, they'll look like command line * arguments. Previously, apps launched from the finder had nothing but * an argv[0]. * * This message may be received multiple times to open several docs on launch. * * This message is ignored once the app's mainline has been called. */ - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename { const char *temparg; size_t arglen; char *arg; char **newargv; if (!gFinderLaunch) /* MacOS is passing command line args. */ return FALSE; if (gCalledAppMainline) /* app has started, ignore this document. */ return FALSE; temparg = [filename UTF8String]; arglen = SDL_strlen(temparg) + 1; arg = (char *) SDL_malloc(arglen); if (arg == NULL) return FALSE; newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2)); if (newargv == NULL) { SDL_free(arg); return FALSE; } gArgv = newargv; SDL_strlcpy(arg, temparg, arglen); gArgv[gArgc++] = arg; gArgv[gArgc] = NULL; return TRUE; } /* Called when the internal event loop has just started running */ - (void) applicationDidFinishLaunching: (NSNotification *) note { int status; /* Set the working directory to the .app's parent directory */ [self setupWorkingDirectory:gFinderLaunch]; #if SDL_USE_NIB_FILE /* Set the main menu to contain the real app name instead of "SDL App" */ [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()]; #endif /* Hand off to main application code */ gCalledAppMainline = TRUE; status = SDL_main (gArgc, gArgv); /* We're done, thank you for playing */ exit(status); } @end @implementation NSString (ReplaceSubString) - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString { unsigned int bufferSize; unsigned int selfLen = [self length]; unsigned int aStringLen = [aString length]; unichar *buffer; NSRange localRange; NSString *result; bufferSize = selfLen + aStringLen - aRange.length; buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar)); /* Get first part into buffer */ localRange.location = 0; localRange.length = aRange.location; [self getCharacters:buffer range:localRange]; /* Get middle part into buffer */ localRange.location = 0; localRange.length = aStringLen; [aString getCharacters:(buffer+aRange.location) range:localRange]; /* Get last part into buffer */ localRange.location = aRange.location + aRange.length; localRange.length = selfLen - localRange.location; [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange]; /* Build output string */ result = [NSString stringWithCharacters:buffer length:bufferSize]; NSDeallocateMemoryPages(buffer, bufferSize); return result; } @end #ifdef main # undef main #endif static int IsRootCwd() { char buf[MAXPATHLEN]; char *cwd = getcwd(buf, sizeof (buf)); return (cwd && (strcmp(cwd, "/") == 0)); } static int IsFinderLaunch(const int argc, char **argv) { /* -psn_XXX is passed if we are launched from Finder, SOMETIMES */ if ( (argc >= 2) && (strncmp(argv[1], "-psn", 4) == 0) ) { return 1; } else if ((argc == 1) && IsRootCwd()) { /* we might still be launched from the Finder; on 10.9+, you might not get the -psn command line anymore. If there's no command line, and if our current working directory is "/", it might as well be a Finder launch. */ return 1; } return 0; /* not a Finder launch. */ } /* Main entry point to executable - should *not* be SDL_main! */ int main (int argc, char **argv) { /* Copy the arguments into a global variable */ if (IsFinderLaunch(argc, argv)) { gArgv = (char **) SDL_malloc(sizeof (char *) * 2); gArgv[0] = argv[0]; gArgv[1] = NULL; gArgc = 1; gFinderLaunch = YES; } else { int i; gArgc = argc; gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1)); for (i = 0; i <= argc; i++) gArgv[i] = argv[i]; gFinderLaunch = NO; } #if SDL_USE_NIB_FILE NSApplicationMain (argc, argv); #else CustomApplicationMain (argc, argv); #endif return 0; } engine/h2shared/SDLMain.patch000066400000000000000000000051611444734033100163010ustar00rootroot00000000000000[ this was reverted after the breakage reported at https://bugzilla.libsdl.org/show_bug.cgi?id=4114#c5 ] # HG changeset patch # User Ozkan Sezer # Date 1529690620 -10800 # Branch SDL-1.2 # Node ID 67f391c74aee22627731ff31ac84dfd8b2575c08 # Parent d684a767e2400582f82d3e5d2bb2cc3ed8cd59b1 SDLMain.m: replace CPS* stuff with activateIgnoringOtherApps (bug #4114) CPS stuff are deprecated and emit warnings at runtime, e.g.: warning: CPSGetCurrentProcess(): This call is deprecated and should not be called anymore warning: CPSSetForegroundOperationState() (as above) warning: capture_display; Captured mirror master 0x350000c1 (unit 1) on behalf of slave 0x042716c0 (unit 0) http://lists.libsdl.org/pipermail/sdl-libsdl.org/2006-April/055749.html suggests doing something like: ProcessSerialNumber psn = { 0, kCurrentProcess}; TransformProcessType (&psn, kProcessTransformToForegroundApplication); SetFrontProcess (&psn); And SetFrontProcess() is deprecated in 10.9. Processes.h suggests [[NSApplication sharedApplication] activateIgnoringOtherApps: YES] to make one's own application frontmost. diff --git a/src/main/macosx/SDLMain.m b/src/main/macosx/SDLMain.m --- a/src/main/macosx/SDLMain.m +++ b/src/main/macosx/SDLMain.m @@ -20,22 +20,6 @@ /* Use this flag to determine whether we use SDLMain.nib or not */ #define SDL_USE_NIB_FILE 0 -/* Use this flag to determine whether we use CPS (docking) or not */ -#define SDL_USE_CPS 1 -#ifdef SDL_USE_CPS -/* Portions of CPS.h */ -typedef struct CPSProcessSerNum -{ - UInt32 lo; - UInt32 hi; -} CPSProcessSerNum; - -extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn); -extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); -extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn); - -#endif /* SDL_USE_CPS */ - static int gArgc; static char **gArgv; static BOOL gFinderLaunch; @@ -201,18 +185,7 @@ SDLMain *sdlMain; /* Ensure the application object is initialised */ - [NSApplication sharedApplication]; - -#ifdef SDL_USE_CPS - { - CPSProcessSerNum PSN; - /* Tell the dock about us */ - if (!CPSGetCurrentProcess(&PSN)) - if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103)) - if (!CPSSetFrontProcess(&PSN)) - [NSApplication sharedApplication]; - } -#endif /* SDL_USE_CPS */ + [[NSApplication sharedApplication] activateIgnoringOtherApps: YES]; /* Set up the menubar */ [NSApp setMainMenu:[[NSMenu alloc] init]]; @@ -345,7 +318,6 @@ @end - #ifdef main # undef main #endif engine/h2shared/alsa_funcs.h000066400000000000000000000101371444734033100163170ustar00rootroot00000000000000/* * alsa_funcs.h -- ALSA function list * Author: Bill Currie, 2002/4/19 * make sure NOT to protect this file against multiple inclusions! * * Copyright (C) 2001 Bill Currie * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* We require alsa-lib 0.9.8 or newer to function. Refuse otherwise. */ #if SND_LIB_MAJOR < 1 && (SND_LIB_MINOR < 9 || (SND_LIB_MINOR == 9 && SND_LIB_SUBMINOR < 8)) #error Alsa libraries v0.9.8 or newer are required #endif #ifndef ALSA_FUNC #define ALSA_FUNC(ret, func, params) #define UNDEF_ALSAFUNC #endif ALSA_FUNC (int, snd_pcm_close, (snd_pcm_t *pcmdev)) ALSA_FUNC (int, snd_pcm_hw_params, (snd_pcm_t *pcmdev, snd_pcm_hw_params_t *params)) ALSA_FUNC (int, snd_pcm_hw_params_any, (snd_pcm_t *pcmdev, snd_pcm_hw_params_t *params)) ALSA_FUNC (int, snd_pcm_hw_params_get_buffer_size, (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)) ALSA_FUNC (int, snd_pcm_hw_params_get_period_size, (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir)) ALSA_FUNC (int, snd_pcm_hw_params_set_access, (snd_pcm_t *pcmdev, snd_pcm_hw_params_t *params, snd_pcm_access_t acc)) ALSA_FUNC (int, snd_pcm_hw_params_set_buffer_size_near, (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)) ALSA_FUNC (int, snd_pcm_hw_params_set_period_size_near, (snd_pcm_t *pcmdev, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir)) ALSA_FUNC (int, snd_pcm_hw_params_set_rate_near, (snd_pcm_t *pcmdev, snd_pcm_hw_params_t *params, unsigned int *val, int *dir)) ALSA_FUNC (int, snd_pcm_hw_params_set_channels, (snd_pcm_t *pcmdev, snd_pcm_hw_params_t *params, unsigned int val)) ALSA_FUNC (int, snd_pcm_hw_params_set_format, (snd_pcm_t *pcmdev, snd_pcm_hw_params_t *params, snd_pcm_format_t val)) ALSA_FUNC (int, snd_pcm_mmap_begin, (snd_pcm_t *pcmdev, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames)) ALSA_FUNC (int, snd_pcm_avail_update, (snd_pcm_t *pcmdev)) ALSA_FUNC (snd_pcm_sframes_t, snd_pcm_mmap_commit, (snd_pcm_t *pcmdev, snd_pcm_uframes_t offset, snd_pcm_uframes_t frames)) ALSA_FUNC (int, snd_pcm_open, (snd_pcm_t **pcmdev, const char *name, snd_pcm_stream_t stream, int mode)) ALSA_FUNC (int, snd_pcm_start, (snd_pcm_t *pcmdev)) ALSA_FUNC (snd_pcm_state_t, snd_pcm_state, (snd_pcm_t *pcmdev)) ALSA_FUNC (int, snd_pcm_sw_params, (snd_pcm_t *pcmdev, snd_pcm_sw_params_t *params)) ALSA_FUNC (int, snd_pcm_sw_params_current, (snd_pcm_t *pcmdev, snd_pcm_sw_params_t *params)) ALSA_FUNC (int, snd_pcm_sw_params_set_start_threshold, (snd_pcm_t *pcmdev, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val)) ALSA_FUNC (int, snd_pcm_sw_params_set_stop_threshold, (snd_pcm_t *pcmdev, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val)) ALSA_FUNC (const char *, snd_strerror, (int errnum)) ALSA_FUNC (int, snd_pcm_drop, (snd_pcm_t *pcmdev)) ALSA_FUNC (int, snd_pcm_hw_params_malloc, (snd_pcm_hw_params_t **ptr)) ALSA_FUNC (int, snd_pcm_sw_params_malloc, (snd_pcm_sw_params_t **ptr)) ALSA_FUNC (void, snd_pcm_hw_params_free, (snd_pcm_hw_params_t *obj)) ALSA_FUNC (void, snd_pcm_sw_params_free, (snd_pcm_sw_params_t *obj)) ALSA_FUNC (int, snd_pcm_pause, (snd_pcm_t *pcmdev, int enable)) /* ALSA_FUNC (int, snd_pcm_delay, (snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)) ALSA_FUNC (int, snd_pcm_prepare, (snd_pcm_t *pcm)) ALSA_FUNC (snd_pcm_sframes_t, snd_pcm_writei, (snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)) */ #ifdef UNDEF_ALSAFUNC #undef ALSA_FUNC #undef UNDEF_ALSAFUNC #endif engine/h2shared/amiga_r_68k.s000066400000000000000000000266151444734033100163130ustar00rootroot00000000000000; 680x0 optimised Quake render routines by John Selck. ; r_alias.c: XDEF _R_AliasTransformAndProjectFinalVerts ; r_draw.c: XDEF _R_ClipEdge ; r_misc.c: XDEF _TransformVector XDEF @TransformVector ; r_surf.c: XDEF _R_DrawSurfaceBlock16 ; external defs: XREF _vright XREF _vup XREF _vpn XREF _modelorg XREF _xscale XREF _yscale XREF _xcenter XREF _ycenter XREF _r_refdef XREF _r_framecount XREF _edge_p XREF _surface_p XREF _surfaces XREF _newedges XREF _removeedges XREF _cacheoffset XREF _r_pedge XREF _r_leftclipped XREF _r_rightclipped XREF _r_nearzionly XREF _r_leftenter XREF _r_leftexit XREF _r_rightenter XREF _r_rightexit XREF _r_emitted XREF _r_nearzi XREF _r_u1 XREF _r_v1 XREF _r_lzi1 XREF _r_ceilv1 XREF _r_lastvertvalid XREF _aliasxcenter XREF _aliasycenter XREF _r_avertexnormals ;XREF _tmatrix ; $00 ;XREF _viewmatrix ; $30 ;XREF _ptriangles ; $60 ;XREF _r_affinetridesc ; $64 ;XREF _acolormap ; $88 XREF _r_apverts ; $8c ;XREF _pmdl ; $90 XREF _r_plightvec ; $94 XREF _r_ambientlight ; $a0 XREF _r_shadelight ; $a4 ;XREF _paliashdr ; $a8 ;XREF _pfinalverts ; $ac ;XREF _pauxverts ; $b0 ;XREF _ziscale ; $b4 ;XREF _pmodel ; $b8 ;XREF _alias_forward ; $bc ;XREF _alias_right ; $c8 ;XREF _alias_up ; $d4 ;XREF _pskindesc ; $e0 ;XREF _r_amodels_drawn ; $e4 ;XREF _a_skinwidth ; $e8 ;XREF _r_anumverts ; $ec XREF _aliastransform ; $f0 -> $120 __farbss equ _aliastransform-$f0 SECTION CODE,CODE ; void R_AliasTransformAndProjectFinalVerts (finalvert_t *fv, stvert_t *pstverts) ; point transformation of entities _R_AliasTransformAndProjectFinalVerts: move.l 8(sp),a1 move.l 4(sp),a0 sub.w #$4c,sp movem.l d6-d7/a2-a3/a5,-(sp) fmovem.x fp2-fp7,-(sp) move.l a0,a5 moveq #0,d7 lea $64(a7),a3 move.l __farbss+$118,(a3)+ move.l __farbss+$114,(a3)+ move.l __farbss+$110,(a3)+ move.l __farbss+$11c,(a3)+ move.l __farbss+$f8,(a3)+ move.l __farbss+$f4,(a3)+ move.l __farbss+$f0,(a3)+ move.l __farbss+$fc,(a3)+ move.l _aliasxcenter,(a3)+ move.l __farbss+$108,(a3)+ move.l __farbss+$104,(a3)+ move.l __farbss+$100,(a3)+ move.l __farbss+$10c,(a3)+ move.l _aliasycenter,(a3)+ move.l _r_plightvec+8,(a3)+ move.l _r_plightvec+4,(a3)+ move.l _r_plightvec+0,(a3) move.l _r_apverts,a2 bra.w .atin .atl0 moveq #0,d0 move.b (a2),d0 fmove.w d0,fp7 move.b 1(a2),d0 fmove.w d0,fp6 move.b 2(a2),d0 fmove.w d0,fp5 lea $64(a7),a3 fmove.s (a3)+,fp1 fmul.x fp5,fp1 move.b 3(a2),d0 fmove.s (a3)+,fp2 fmul.x fp6,fp2 mulu.w #12,d0 fmove.s (a3)+,fp3 fmul.x fp7,fp3 lea _r_avertexnormals,a0 fadd.s (a3)+,fp3 fadd.x fp1,fp3 fadd.x fp2,fp3 fmove.s #1,fp0 fdiv.x fp3,fp0 move.l 4(a1),8(a5) move.l 8(a1),12(a5) move.l (a1),$18(a5) fmove.l fp0,$14(a5) fmove.s (a3)+,fp1 fmul.x fp5,fp1 add.l d0,a0 fmove.s (a3)+,fp2 fmul.x fp6,fp2 move.l _r_ambientlight,d6 fmove.s (a3)+,fp3 fmul.x fp7,fp3 addq.l #1,d7 fadd.s (a3)+,fp3 fadd.x fp1,fp3 fadd.x fp2,fp3 fmul.x fp0,fp3 addq.l #4,a2 fadd.s (a3)+,fp3 fmove.l fp3,(a5) fmove.s (a3)+,fp1 fmul.x fp5,fp1 add.w #12,a1 fmove.s (a3)+,fp2 fmul.x fp6,fp2 fmove.s (a3)+,fp3 fmul.x fp7,fp3 fadd.s (a3)+,fp3 fadd.x fp1,fp3 fadd.x fp2,fp3 fmul.x fp0,fp3 fadd.s (a3)+,fp3 fmove.l fp3,4(a5) fmove.s 8(a0),fp2 fmul.s (a3)+,fp2 fmove.s 4(a0),fp3 fmul.s (a3)+,fp3 fmove.s (a0),fp0 fmul.s (a3),fp0 add.w #$20,a5 fadd.x fp2,fp0 fadd.x fp3,fp0 ftst.x fp0 fboge.w .s0 fmul.s _r_shadelight,fp0 fmove.l fp0,d0 add.l d0,d6 bge.b .s0 moveq #0,d6 .s0 move.l d6,-$10(a5) .atin cmp.l _r_anumverts,d7 blt.w .atl0 fmovem.x (sp)+,fp2-fp7 movem.l (sp)+,d6-d7/a2-a3/a5 add.w #$4c,sp rts ; void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip); ; clipping cnop 0,4 _R_ClipEdge: movem.l a2-a3,-(sp) move.l 20(sp),a2 move.l 16(sp),a1 move.l 12(sp),a0 fmovem.x fp2-fp4,-(sp) bsr.b R_ClipEdge_inner fmovem.x (sp)+,fp2-fp4 movem.l (sp)+,a2-a3 rts cnop 0,4 R_ClipEdge_inner: sub.w #12,sp move.l a2,d0 beq.w .emit .next fmove.s (a0),fp0 fmul.s (a2),fp0 fmove.s 4(a0),fp1 fmul.s 4(a2),fp1 fmove.s 8(a0),fp2 fmul.s 8(a2),fp2 fadd.x fp1,fp0 fadd.x fp2,fp0 fsub.s 12(a2),fp0 fmove.s (a1),fp1 fmul.s (a2),fp1 fmove.s 4(a1),fp2 fmul.s 4(a2),fp2 fmove.s 8(a1),fp3 fmul.s 8(a2),fp3 fadd.x fp2,fp1 fadd.x fp3,fp1 fsub.s 12(a2),fp1 ftst.x fp0 fbolt.w .enter ftst.x fp1 fbuge.w .loop move.l #$7fffffff,_cacheoffset fmove.x fp0,fp2 fsub.x fp1,fp2 fdiv.x fp2,fp0 fmove.s (a0),fp1 fmove.s (a1),fp2 fsub.x fp1,fp2 fmul.x fp0,fp2 fadd.x fp1,fp2 fmove.s fp2,(sp) fmove.s 4(a0),fp1 fmove.s 4(a1),fp2 fsub.x fp1,fp2 fmul.x fp0,fp2 fadd.x fp1,fp2 fmove.s fp2,4(sp) fmove.s 8(a0),fp1 fmove.s 8(a1),fp2 fsub.x fp1,fp2 fmul.x fp0,fp2 fadd.x fp1,fp2 fmove.s fp2,8(sp) tst.b $14(a2) beq.b .ces0 moveq #1,d0 move.l d0,_r_leftclipped lea _r_leftexit,a3 bra.b .s1 .ces0 tst.b $15(a2) beq.b .s2 moveq #1,d0 move.l d0,_r_rightclipped lea _r_rightexit,a3 .s1 move.l sp,a1 move.l (a1)+,(a3)+ move.l (a1)+,(a3)+ move.l (a1),(a3) .s2 move.l sp,a1 move.l 16(a2),a2 bsr.w R_ClipEdge_inner bra.w .end .enter ftst.x fp1 fboge.w .s3 tst.l _r_leftclipped bne.w .end move.l _r_framecount,d0 or.l #$80000000,d0 move.l d0,_cacheoffset bra.w .end .s3 clr.l _r_lastvertvalid move.l #$7fffffff,_cacheoffset fmove.x fp0,fp2 fsub.x fp1,fp2 fdiv.x fp2,fp0 fmove.s (a0),fp1 fmove.s (a1),fp2 fsub.x fp1,fp2 fmul.x fp0,fp2 fadd.x fp1,fp2 fmove.s fp2,(sp) fmove.s 4(a0),fp1 fmove.s 4(a1),fp2 fsub.x fp1,fp2 fmul.x fp0,fp2 fadd.x fp1,fp2 fmove.s fp2,4(sp) fmove.s 8(a0),fp1 fmove.s 8(a1),fp2 fsub.x fp1,fp2 fmul.x fp0,fp2 fadd.x fp1,fp2 fmove.s fp2,8(sp) tst.b $14(a2) beq.b .s4 moveq #1,d0 move.l d0,_r_leftclipped lea _r_leftenter,a3 bra.b .s5 .s4 tst.b $15(a2) beq.b .s6 moveq #1,d0 move.l d0,_r_rightclipped lea _r_rightenter,a3 .s5 move.l sp,a0 move.l (a0)+,(a3)+ move.l (a0)+,(a3)+ move.l (a0),(a3) .s6 move.l sp,a0 move.l 16(a2),a2 bsr.w R_ClipEdge_inner bra.b .end .loop move.l 16(a2),a2 move.l a2,d0 bne.w .next .emit add.w #12,sp bra.b @R_EmitEdge .end add.w #12,sp rts ; void R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1) ; u0 = FP2 ; v0 = FP3 ; lzi0 = FP4 cnop 0,4 @R_EmitEdge: movem.l d4-d5/d7/a5,-(sp) move.l a1,a2 tst.l _r_lastvertvalid beq.b .notval fmove.s _r_u1,fp2 fmove.s _r_v1,fp3 fmove.s _r_lzi1,fp4 move.l _r_ceilv1,d7 bra.w .valid .notval lea local(pc),a3 fmove.s (a0)+,fp0 fsub.s _modelorg,fp0 fmove.s fp0,(a3)+ fmove.s (a0)+,fp0 fsub.s _modelorg+4,fp0 fmove.s fp0,(a3)+ fmove.s (a0),fp0 fsub.s _modelorg+8,fp0 fmove.s fp0,(a3) lea local(pc),a0 lea transformed(pc),a1 bsr @TransformVector fmove.s transformed+8(pc),fp1 fcmp.s #1.000000e-02,fp1 fboge.w .res0 fmove.s #1.000000e-02,fp1 .res0 fmove.s #1,fp4 fdiv.x fp1,fp4 fmove.s _xscale,fp2 fmul.x fp4,fp2 fmul.s transformed(pc),fp2 fadd.s _xcenter,fp2 fcmp.s _r_refdef+$44,fp2 fboge.w .res1 fmove.s _r_refdef+$44,fp2 .res1 fcmp.s _r_refdef+$54,fp2 fbole.w .res2 fmove.s _r_refdef+$54,fp2 .res2 fmove.s _yscale,fp3 fmul.x fp4,fp3 fmul.s transformed+4(pc),fp3 fneg.x fp3 fadd.s _ycenter,fp3 fcmp.s _r_refdef+$48,fp3 fboge.w .res3 fmove.s _r_refdef+$48,fp3 .res3 fcmp.s _r_refdef+$58,fp3 fbole.w .res4 fmove.s _r_refdef+$58,fp3 .res4 fmove.l fp3,d7 fcmp.l d7,fp3 fbule.w .rc0 addq.l #1,d7 .rc0 .valid lea local(pc),a3 fmove.s (a2)+,fp0 fsub.s _modelorg,fp0 fmove.s fp0,(a3)+ fmove.s (a2)+,fp0 fsub.s _modelorg+4,fp0 fmove.s fp0,(a3)+ fmove.s (a2),fp0 fsub.s _modelorg+8,fp0 fmove.s fp0,(a3) lea local(pc),a0 lea transformed(pc),a1 bsr @TransformVector fmove.s transformed+8(pc),fp1 fcmp.s #1.000000e-02,fp1 fboge.w .res5 fmove.s #1.000000e-02,fp1 .res5 fmove.s #1,fp0 fdiv.x fp1,fp0 fmove.s fp0,_r_lzi1 fmove.s _xscale,fp0 fmul.s _r_lzi1,fp0 fmul.s transformed(pc),fp0 fadd.s _xcenter,fp0 fcmp.s _r_refdef+$44,fp0 fboge.w .res6 fmove.s _r_refdef+$44,fp0 .res6 fcmp.s _r_refdef+$54,fp0 fbole.w .res7 fmove.s _r_refdef+$54,fp0 .res7 fmove.s fp0,_r_u1 fmove.s _yscale,fp0 fmul.s _r_lzi1,fp0 fmul.s transformed+4(pc),fp0 fneg.x fp0 fadd.s _ycenter,fp0 fcmp.s _r_refdef+$48,fp0 fboge.w .res8 fmove.s _r_refdef+$48,fp0 .res8 fcmp.s _r_refdef+$58,fp0 fbole.w .res9 fmove.s _r_refdef+$58,fp0 .res9 fmove.s fp0,_r_v1 fmove.s _r_lzi1,fp1 fcmp.x fp4,fp1 fbole.w .res10 fmove.x fp1,fp4 .res10 fmove.x fp4,fp1 fcmp.s _r_nearzi,fp1 fbole.w .res11 fmove.s fp1,_r_nearzi .res11 tst.l _r_nearzionly bne.w .endemit move.l #1,_r_emitted fmove.l fp0,d0 fcmp.l d0,fp0 fbule.w .rc1 addq.l #1,d0 .rc1 move.l d0,_r_ceilv1 cmp.l d7,d0 bne.b .nocreate cmp.l #$7fffffff,_cacheoffset beq.w .endemit move.l _r_framecount,d0 or.l #$80000000,d0 move.l d0,_cacheoffset bra.w .endemit .nocreate fmove.s _r_u1,fp0 fsub.x fp2,fp0 fmove.s _r_v1,fp1 fsub.x fp3,fp1 fdiv.x fp1,fp0 move.l _edge_p,a5 add.l #$20,_edge_p fmove.s fp4,$18(a5) move.l _r_pedge,$1c(a5) move.l _surface_p,d1 sub.l _surfaces,d1 asr.l #6,d1 cmp.l d0,d7 bgt.b .notrail move.l d7,d5 fmove.l d5,fp1 fsub.x fp3,fp1 fmul.x fp0,fp1 move.l _r_ceilv1,d4 move.w d1,$10(a5) clr.w $12(a5) fadd.x fp2,fp1 bra.b .nolead .notrail move.l _r_ceilv1,d5 fmove.l d5,fp1 fsub.s _r_v1,fp1 fmul.x fp0,fp1 move.l d7,d4 clr.w $10(a5) move.w d1,$12(a5) fadd.s _r_u1,fp1 .nolead fmul.s #1048576,fp1 lea _newedges,a0 subq.l #1,d4 lea 0(a0,d5.l*4),a1 move.l (a1),a0 fadd.s #1048575,fp1 fmove.l fp1,d0 fmul.s #1048576,fp0 cmp.l _r_refdef+$4c,d0 bge.b .rer0 move.l _r_refdef+$4c,d0 .rer0 cmp.l _r_refdef+$50,d0 ble.b .rer1 move.l _r_refdef+$50,d0 .rer1 move.l d0,(a5) fmove.l fp0,4(a5) tst.w $10(a5) beq.b .rer2 addq.l #1,d0 .rer2 move.l d0,d7 move.l a0,d0 beq.b .re43 move.l (a0),d1 cmp.l d7,d1 blt.b .re44 .re43: move.l d0,12(a5) move.l a5,(a1) bra.b .re49 .re44: move.l d0,a3 dc.w $0c40 .re45: move.l a2,a3 move.l 12(a3),a2 move.l a2,d0 beq.b .re48 cmp.l (a2),d7 bge.b .re45 .re48: move.l a2,12(a5) move.l a5,12(a3) .re49: lea _removeedges,a0 lea 0(a0,d4.l*4),a1 move.l (a1),$14(a5) move.l a5,(a1) .endemit movem.l (sp)+,d4-d5/d7/a5 rts cnop 0,8 local: dc.l 0,0,0 transformed: dc.l 0,0,0 ; void TransformVector (vec3_t in, vec3_t out); ; general vector transformation _TransformVector: move.l 8(sp),a1 move.l 4(sp),a0 @TransformVector: move.l a2,-(sp) lea _vright,a2 fmove.s (a2)+,fp0 fmul.s (a0)+,fp0 fmove.s (a2)+,fp1 fmul.s (a0)+,fp1 fadd.x fp1,fp0 fmove.s (a2),fp1 fmul.s (a0),fp1 subq.l #8,a0 lea _vup,a2 fadd.x fp1,fp0 fmove.s fp0,(a1)+ fmove.s (a2)+,fp0 fmul.s (a0)+,fp0 fmove.s (a2)+,fp1 fmul.s (a0)+,fp1 fadd.x fp1,fp0 fmove.s (a2),fp1 fmul.s (a0),fp1 subq.l #8,a0 lea _vpn,a2 fadd.x fp1,fp0 fmove.s fp0,(a1)+ fmove.s (a2)+,fp0 fmul.s (a0)+,fp0 fmove.s (a2)+,fp1 fmul.s (a0)+,fp1 fadd.x fp1,fp0 fmove.s (a2),fp1 fmul.s (a0),fp1 move.l (sp)+,a2 fadd.x fp1,fp0 fmove.s fp0,(a1) rts ; void R_DrawSurfaceBlock16 (void) ; dummy _R_DrawSurfaceBlock16: ; not needed in quake 1 rts END engine/h2shared/anorm_dots.h000066400000000000000000000637001444734033100163520ustar00rootroot00000000000000/* * anorm_dots.h * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // 0 { 1.23, 1.30, 1.47, 1.35, 1.56, 1.71, 1.37, 1.38, 1.59, 1.60, 1.79, 1.97, 1.88, 1.92, 1.79, 1.02, 0.93, 1.07, 0.82, 0.87, 0.88, 0.94, 0.96, 1.14, 1.11, 0.82, 0.83, 0.89, 0.89, 0.86, 0.94, 0.91, 1.00, 1.21, 0.98, 1.48, 1.30, 1.57, 0.96, 1.07, 1.14, 1.60, 1.61, 1.40, 1.37, 1.72, 1.78, 1.79, 1.93, 1.99, 1.90, 1.68, 1.71, 1.86, 1.60, 1.68, 1.78, 1.86, 1.93, 1.99, 1.97, 1.44, 1.22, 1.49, 0.93, 0.99, 0.99, 1.23, 1.22, 1.44, 1.49, 0.89, 0.89, 0.97, 0.91, 0.98, 1.19, 0.82, 0.76, 0.82, 0.71, 0.72, 0.73, 0.76, 0.79, 0.86, 0.83, 0.72, 0.76, 0.76, 0.89, 0.82, 0.89, 0.82, 0.89, 0.91, 0.83, 0.96, 1.14, 0.97, 1.40, 1.19, 0.98, 0.94, 1.00, 1.07, 1.37, 1.21, 1.48, 1.30, 1.57, 1.61, 1.37, 0.86, 0.83, 0.91, 0.82, 0.82, 0.88, 0.89, 0.96, 1.14, 0.98, 0.87, 0.93, 0.94, 1.02, 1.30, 1.07, 1.35, 1.38, 1.11, 1.56, 1.92, 1.79, 1.79, 1.59, 1.60, 1.72, 1.90, 1.79, 0.80, 0.85, 0.79, 0.93, 0.80, 0.85, 0.77, 0.74, 0.72, 0.77, 0.74, 0.72, 0.70, 0.70, 0.71, 0.76, 0.73, 0.79, 0.79, 0.73, 0.76, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 1 { 1.26, 1.26, 1.48, 1.23, 1.50, 1.71, 1.14, 1.19, 1.38, 1.46, 1.64, 1.94, 1.87, 1.84, 1.71, 1.02, 0.92, 1.00, 0.79, 0.85, 0.84, 0.91, 0.90, 0.98, 0.99, 0.77, 0.77, 0.83, 0.82, 0.79, 0.86, 0.84, 0.92, 0.99, 0.91, 1.24, 1.03, 1.33, 0.88, 0.94, 0.97, 1.41, 1.39, 1.18, 1.11, 1.51, 1.61, 1.59, 1.80, 1.91, 1.76, 1.54, 1.65, 1.76, 1.70, 1.70, 1.85, 1.85, 1.97, 1.99, 1.93, 1.28, 1.09, 1.39, 0.92, 0.97, 0.99, 1.18, 1.26, 1.52, 1.48, 0.83, 0.85, 0.90, 0.88, 0.93, 1.00, 0.77, 0.73, 0.78, 0.72, 0.71, 0.74, 0.75, 0.79, 0.86, 0.81, 0.75, 0.81, 0.79, 0.96, 0.88, 0.94, 0.86, 0.93, 0.92, 0.85, 1.08, 1.33, 1.05, 1.55, 1.31, 1.01, 1.05, 1.27, 1.31, 1.60, 1.47, 1.70, 1.54, 1.76, 1.76, 1.57, 0.93, 0.90, 0.99, 0.88, 0.88, 0.95, 0.97, 1.11, 1.39, 1.20, 0.92, 0.97, 1.01, 1.10, 1.39, 1.22, 1.51, 1.58, 1.32, 1.64, 1.97, 1.85, 1.91, 1.77, 1.74, 1.88, 1.99, 1.91, 0.79, 0.86, 0.80, 0.94, 0.84, 0.88, 0.74, 0.74, 0.71, 0.82, 0.77, 0.76, 0.70, 0.73, 0.72, 0.73, 0.70, 0.74, 0.85, 0.77, 0.82, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 2 { 1.34, 1.27, 1.53, 1.17, 1.46, 1.71, 0.98, 1.05, 1.20, 1.34, 1.48, 1.86, 1.82, 1.71, 1.62, 1.09, 0.94, 0.99, 0.79, 0.85, 0.82, 0.90, 0.87, 0.93, 0.96, 0.76, 0.74, 0.79, 0.76, 0.74, 0.79, 0.78, 0.85, 0.92, 0.85, 1.00, 0.93, 1.06, 0.81, 0.86, 0.89, 1.16, 1.12, 0.97, 0.95, 1.28, 1.38, 1.35, 1.60, 1.77, 1.57, 1.33, 1.50, 1.58, 1.69, 1.63, 1.82, 1.74, 1.91, 1.92, 1.80, 1.04, 0.97, 1.21, 0.90, 0.93, 0.97, 1.05, 1.21, 1.48, 1.37, 0.77, 0.80, 0.84, 0.85, 0.88, 0.92, 0.73, 0.71, 0.74, 0.74, 0.71, 0.75, 0.73, 0.79, 0.84, 0.78, 0.79, 0.86, 0.81, 1.05, 0.94, 0.99, 0.90, 0.95, 0.92, 0.86, 1.24, 1.44, 1.14, 1.59, 1.34, 1.02, 1.27, 1.50, 1.49, 1.80, 1.69, 1.86, 1.72, 1.87, 1.80, 1.69, 1.00, 0.98, 1.23, 0.95, 0.96, 1.09, 1.16, 1.37, 1.63, 1.46, 0.99, 1.10, 1.25, 1.24, 1.51, 1.41, 1.67, 1.77, 1.55, 1.72, 1.95, 1.89, 1.98, 1.91, 1.86, 1.97, 1.99, 1.94, 0.81, 0.89, 0.85, 0.98, 0.90, 0.94, 0.75, 0.78, 0.73, 0.89, 0.83, 0.82, 0.72, 0.77, 0.76, 0.72, 0.70, 0.71, 0.91, 0.83, 0.89, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 3 { 1.46, 1.34, 1.60, 1.16, 1.46, 1.71, 0.94, 0.99, 1.05, 1.26, 1.33, 1.74, 1.76, 1.57, 1.54, 1.23, 0.98, 1.05, 0.83, 0.89, 0.84, 0.92, 0.87, 0.91, 0.96, 0.78, 0.74, 0.79, 0.72, 0.72, 0.75, 0.76, 0.80, 0.88, 0.83, 0.94, 0.87, 0.95, 0.76, 0.80, 0.82, 0.97, 0.96, 0.89, 0.88, 1.08, 1.11, 1.10, 1.37, 1.59, 1.37, 1.07, 1.27, 1.34, 1.57, 1.45, 1.69, 1.55, 1.77, 1.79, 1.60, 0.93, 0.90, 0.99, 0.86, 0.87, 0.93, 0.96, 1.07, 1.35, 1.18, 0.73, 0.76, 0.77, 0.81, 0.82, 0.85, 0.70, 0.71, 0.72, 0.78, 0.73, 0.77, 0.73, 0.79, 0.82, 0.76, 0.83, 0.90, 0.84, 1.18, 0.98, 1.03, 0.92, 0.95, 0.90, 0.86, 1.32, 1.45, 1.15, 1.53, 1.27, 0.99, 1.42, 1.65, 1.58, 1.93, 1.83, 1.94, 1.81, 1.88, 1.74, 1.70, 1.19, 1.17, 1.44, 1.11, 1.15, 1.36, 1.41, 1.61, 1.81, 1.67, 1.22, 1.34, 1.50, 1.42, 1.65, 1.61, 1.82, 1.91, 1.75, 1.80, 1.89, 1.89, 1.98, 1.99, 1.94, 1.98, 1.92, 1.87, 0.86, 0.95, 0.92, 1.14, 0.98, 1.03, 0.79, 0.84, 0.77, 0.97, 0.90, 0.89, 0.76, 0.82, 0.82, 0.74, 0.72, 0.71, 0.98, 0.89, 0.97, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 4 { 1.60, 1.44, 1.68, 1.22, 1.49, 1.71, 0.93, 0.99, 0.99, 1.23, 1.22, 1.60, 1.68, 1.44, 1.49, 1.40, 1.14, 1.19, 0.89, 0.96, 0.89, 0.97, 0.89, 0.91, 0.98, 0.82, 0.76, 0.82, 0.71, 0.72, 0.73, 0.76, 0.79, 0.86, 0.83, 0.91, 0.83, 0.89, 0.72, 0.76, 0.76, 0.89, 0.89, 0.82, 0.82, 0.98, 0.96, 0.97, 1.14, 1.40, 1.19, 0.94, 1.00, 1.07, 1.37, 1.21, 1.48, 1.30, 1.57, 1.61, 1.37, 0.86, 0.83, 0.91, 0.82, 0.82, 0.88, 0.89, 0.96, 1.14, 0.98, 0.70, 0.72, 0.73, 0.77, 0.76, 0.79, 0.70, 0.72, 0.71, 0.82, 0.77, 0.80, 0.74, 0.79, 0.80, 0.74, 0.87, 0.93, 0.85, 1.23, 1.02, 1.02, 0.93, 0.93, 0.87, 0.85, 1.30, 1.35, 1.07, 1.38, 1.11, 0.94, 1.47, 1.71, 1.56, 1.97, 1.88, 1.92, 1.79, 1.79, 1.59, 1.60, 1.30, 1.35, 1.56, 1.37, 1.38, 1.59, 1.60, 1.79, 1.92, 1.79, 1.48, 1.57, 1.72, 1.61, 1.78, 1.79, 1.93, 1.99, 1.90, 1.86, 1.78, 1.86, 1.93, 1.99, 1.97, 1.90, 1.79, 1.72, 0.94, 1.07, 1.00, 1.37, 1.21, 1.30, 0.86, 0.91, 0.83, 1.14, 0.98, 0.96, 0.82, 0.88, 0.89, 0.79, 0.76, 0.73, 1.07, 0.94, 1.11, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 5 { 1.74, 1.57, 1.76, 1.33, 1.54, 1.71, 0.94, 1.05, 0.99, 1.26, 1.16, 1.46, 1.60, 1.34, 1.46, 1.59, 1.37, 1.37, 0.97, 1.11, 0.96, 1.10, 0.95, 0.94, 1.08, 0.89, 0.82, 0.88, 0.72, 0.76, 0.75, 0.80, 0.80, 0.88, 0.87, 0.91, 0.83, 0.87, 0.72, 0.76, 0.74, 0.83, 0.84, 0.78, 0.79, 0.96, 0.89, 0.92, 0.98, 1.23, 1.05, 0.86, 0.92, 0.95, 1.11, 0.98, 1.22, 1.03, 1.34, 1.42, 1.14, 0.79, 0.77, 0.84, 0.78, 0.76, 0.82, 0.82, 0.89, 0.97, 0.90, 0.70, 0.71, 0.71, 0.73, 0.72, 0.74, 0.73, 0.76, 0.72, 0.86, 0.81, 0.82, 0.76, 0.79, 0.77, 0.73, 0.90, 0.95, 0.86, 1.18, 1.03, 0.98, 0.92, 0.90, 0.83, 0.84, 1.19, 1.17, 0.98, 1.15, 0.97, 0.89, 1.42, 1.65, 1.44, 1.93, 1.83, 1.81, 1.67, 1.61, 1.36, 1.41, 1.32, 1.45, 1.58, 1.57, 1.53, 1.74, 1.70, 1.88, 1.94, 1.81, 1.69, 1.77, 1.87, 1.79, 1.89, 1.92, 1.98, 1.99, 1.98, 1.89, 1.65, 1.80, 1.82, 1.91, 1.94, 1.75, 1.61, 1.50, 1.07, 1.34, 1.27, 1.60, 1.45, 1.55, 0.93, 0.99, 0.90, 1.35, 1.18, 1.07, 0.87, 0.93, 0.96, 0.85, 0.82, 0.77, 1.15, 0.99, 1.27, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 6 { 1.86, 1.71, 1.82, 1.48, 1.62, 1.71, 0.98, 1.20, 1.05, 1.34, 1.17, 1.34, 1.53, 1.27, 1.46, 1.77, 1.60, 1.57, 1.16, 1.38, 1.12, 1.35, 1.06, 1.00, 1.28, 0.97, 0.89, 0.95, 0.76, 0.81, 0.79, 0.86, 0.85, 0.92, 0.93, 0.93, 0.85, 0.87, 0.74, 0.78, 0.74, 0.79, 0.82, 0.76, 0.79, 0.96, 0.85, 0.90, 0.94, 1.09, 0.99, 0.81, 0.85, 0.89, 0.95, 0.90, 0.99, 0.94, 1.10, 1.24, 0.98, 0.75, 0.73, 0.78, 0.74, 0.72, 0.77, 0.76, 0.82, 0.89, 0.83, 0.73, 0.71, 0.71, 0.71, 0.70, 0.72, 0.77, 0.80, 0.74, 0.90, 0.85, 0.84, 0.78, 0.79, 0.75, 0.73, 0.92, 0.95, 0.86, 1.05, 0.99, 0.94, 0.90, 0.86, 0.79, 0.81, 1.00, 0.98, 0.91, 0.96, 0.89, 0.83, 1.27, 1.50, 1.23, 1.80, 1.69, 1.63, 1.46, 1.37, 1.09, 1.16, 1.24, 1.44, 1.49, 1.69, 1.59, 1.80, 1.69, 1.87, 1.86, 1.72, 1.82, 1.91, 1.94, 1.92, 1.95, 1.99, 1.98, 1.91, 1.97, 1.89, 1.51, 1.72, 1.67, 1.77, 1.86, 1.55, 1.41, 1.25, 1.33, 1.58, 1.50, 1.80, 1.63, 1.74, 1.04, 1.21, 0.97, 1.48, 1.37, 1.21, 0.93, 0.97, 1.05, 0.92, 0.88, 0.84, 1.14, 1.02, 1.34, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 7 { 1.94, 1.84, 1.87, 1.64, 1.71, 1.71, 1.14, 1.38, 1.19, 1.46, 1.23, 1.26, 1.48, 1.26, 1.50, 1.91, 1.80, 1.76, 1.41, 1.61, 1.39, 1.59, 1.33, 1.24, 1.51, 1.18, 0.97, 1.11, 0.82, 0.88, 0.86, 0.94, 0.92, 0.99, 1.03, 0.98, 0.91, 0.90, 0.79, 0.84, 0.77, 0.79, 0.84, 0.77, 0.83, 0.99, 0.85, 0.91, 0.92, 1.02, 1.00, 0.79, 0.80, 0.86, 0.88, 0.84, 0.92, 0.88, 0.97, 1.10, 0.94, 0.74, 0.71, 0.74, 0.72, 0.70, 0.73, 0.72, 0.76, 0.82, 0.77, 0.77, 0.73, 0.74, 0.71, 0.70, 0.73, 0.83, 0.85, 0.78, 0.92, 0.88, 0.86, 0.81, 0.79, 0.74, 0.75, 0.92, 0.93, 0.85, 0.96, 0.94, 0.88, 0.86, 0.81, 0.75, 0.79, 0.93, 0.90, 0.85, 0.88, 0.82, 0.77, 1.05, 1.27, 0.99, 1.60, 1.47, 1.39, 1.20, 1.11, 0.95, 0.97, 1.08, 1.33, 1.31, 1.70, 1.55, 1.76, 1.57, 1.76, 1.70, 1.54, 1.85, 1.97, 1.91, 1.99, 1.97, 1.99, 1.91, 1.77, 1.88, 1.85, 1.39, 1.64, 1.51, 1.58, 1.74, 1.32, 1.22, 1.01, 1.54, 1.76, 1.65, 1.93, 1.70, 1.85, 1.28, 1.39, 1.09, 1.52, 1.48, 1.26, 0.97, 0.99, 1.18, 1.00, 0.93, 0.90, 1.05, 1.01, 1.31, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 8 { 1.97, 1.92, 1.88, 1.79, 1.79, 1.71, 1.37, 1.59, 1.38, 1.60, 1.35, 1.23, 1.47, 1.30, 1.56, 1.99, 1.93, 1.90, 1.60, 1.78, 1.61, 1.79, 1.57, 1.48, 1.72, 1.40, 1.14, 1.37, 0.89, 0.96, 0.94, 1.07, 1.00, 1.21, 1.30, 1.14, 0.98, 0.96, 0.86, 0.91, 0.83, 0.82, 0.88, 0.82, 0.89, 1.11, 0.87, 0.94, 0.93, 1.02, 1.07, 0.80, 0.79, 0.85, 0.82, 0.80, 0.87, 0.85, 0.93, 1.02, 0.93, 0.77, 0.72, 0.74, 0.71, 0.70, 0.70, 0.71, 0.72, 0.77, 0.74, 0.82, 0.76, 0.79, 0.72, 0.73, 0.76, 0.89, 0.89, 0.82, 0.93, 0.91, 0.86, 0.83, 0.79, 0.73, 0.76, 0.91, 0.89, 0.83, 0.89, 0.89, 0.82, 0.82, 0.76, 0.72, 0.76, 0.86, 0.83, 0.79, 0.82, 0.76, 0.73, 0.94, 1.00, 0.91, 1.37, 1.21, 1.14, 0.98, 0.96, 0.88, 0.89, 0.96, 1.14, 1.07, 1.60, 1.40, 1.61, 1.37, 1.57, 1.48, 1.30, 1.78, 1.93, 1.79, 1.99, 1.92, 1.90, 1.79, 1.59, 1.72, 1.79, 1.30, 1.56, 1.35, 1.38, 1.60, 1.11, 1.07, 0.94, 1.68, 1.86, 1.71, 1.97, 1.68, 1.86, 1.44, 1.49, 1.22, 1.44, 1.49, 1.22, 0.99, 0.99, 1.23, 1.19, 0.98, 0.97, 0.97, 0.98, 1.19, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 9 { 1.94, 1.97, 1.87, 1.91, 1.85, 1.71, 1.60, 1.77, 1.58, 1.74, 1.51, 1.26, 1.48, 1.39, 1.64, 1.99, 1.97, 1.99, 1.70, 1.85, 1.76, 1.91, 1.76, 1.70, 1.88, 1.55, 1.33, 1.57, 0.96, 1.08, 1.05, 1.31, 1.27, 1.47, 1.54, 1.39, 1.20, 1.11, 0.93, 0.99, 0.90, 0.88, 0.95, 0.88, 0.97, 1.32, 0.92, 1.01, 0.97, 1.10, 1.22, 0.84, 0.80, 0.88, 0.79, 0.79, 0.85, 0.86, 0.92, 1.02, 0.94, 0.82, 0.76, 0.77, 0.72, 0.73, 0.70, 0.72, 0.71, 0.74, 0.74, 0.88, 0.81, 0.85, 0.75, 0.77, 0.82, 0.94, 0.93, 0.86, 0.92, 0.92, 0.86, 0.85, 0.79, 0.74, 0.79, 0.88, 0.85, 0.81, 0.82, 0.83, 0.77, 0.78, 0.73, 0.71, 0.75, 0.79, 0.77, 0.74, 0.77, 0.73, 0.70, 0.86, 0.92, 0.84, 1.14, 0.99, 0.98, 0.91, 0.90, 0.84, 0.83, 0.88, 0.97, 0.94, 1.41, 1.18, 1.39, 1.11, 1.33, 1.24, 1.03, 1.61, 1.80, 1.59, 1.91, 1.84, 1.76, 1.64, 1.38, 1.51, 1.71, 1.26, 1.50, 1.23, 1.19, 1.46, 0.99, 1.00, 0.91, 1.70, 1.85, 1.65, 1.93, 1.54, 1.76, 1.52, 1.48, 1.26, 1.28, 1.39, 1.09, 0.99, 0.97, 1.18, 1.31, 1.01, 1.05, 0.90, 0.93, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 10 { 1.86, 1.95, 1.82, 1.98, 1.89, 1.71, 1.80, 1.91, 1.77, 1.86, 1.67, 1.34, 1.53, 1.51, 1.72, 1.92, 1.91, 1.99, 1.69, 1.82, 1.80, 1.94, 1.87, 1.86, 1.97, 1.59, 1.44, 1.69, 1.05, 1.24, 1.27, 1.49, 1.50, 1.69, 1.72, 1.63, 1.46, 1.37, 1.00, 1.23, 0.98, 0.95, 1.09, 0.96, 1.16, 1.55, 0.99, 1.25, 1.10, 1.24, 1.41, 0.90, 0.85, 0.94, 0.79, 0.81, 0.85, 0.89, 0.94, 1.09, 0.98, 0.89, 0.82, 0.83, 0.74, 0.77, 0.72, 0.76, 0.73, 0.75, 0.78, 0.94, 0.86, 0.91, 0.79, 0.83, 0.89, 0.99, 0.95, 0.90, 0.90, 0.92, 0.84, 0.86, 0.79, 0.75, 0.81, 0.85, 0.80, 0.78, 0.76, 0.77, 0.73, 0.74, 0.71, 0.71, 0.73, 0.74, 0.74, 0.71, 0.76, 0.72, 0.70, 0.79, 0.85, 0.78, 0.98, 0.92, 0.93, 0.85, 0.87, 0.82, 0.79, 0.81, 0.89, 0.86, 1.16, 0.97, 1.12, 0.95, 1.06, 1.00, 0.93, 1.38, 1.60, 1.35, 1.77, 1.71, 1.57, 1.48, 1.20, 1.28, 1.62, 1.27, 1.46, 1.17, 1.05, 1.34, 0.96, 0.99, 0.90, 1.63, 1.74, 1.50, 1.80, 1.33, 1.58, 1.48, 1.37, 1.21, 1.04, 1.21, 0.97, 0.97, 0.93, 1.05, 1.34, 1.02, 1.14, 0.84, 0.88, 0.92, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 11 { 1.74, 1.89, 1.76, 1.98, 1.89, 1.71, 1.93, 1.99, 1.91, 1.94, 1.82, 1.46, 1.60, 1.65, 1.80, 1.79, 1.77, 1.92, 1.57, 1.69, 1.74, 1.87, 1.88, 1.94, 1.98, 1.53, 1.45, 1.70, 1.18, 1.32, 1.42, 1.58, 1.65, 1.83, 1.81, 1.81, 1.67, 1.61, 1.19, 1.44, 1.17, 1.11, 1.36, 1.15, 1.41, 1.75, 1.22, 1.50, 1.34, 1.42, 1.61, 0.98, 0.92, 1.03, 0.83, 0.86, 0.89, 0.95, 0.98, 1.23, 1.14, 0.97, 0.89, 0.90, 0.78, 0.82, 0.76, 0.82, 0.77, 0.79, 0.84, 0.98, 0.90, 0.98, 0.83, 0.89, 0.97, 1.03, 0.95, 0.92, 0.86, 0.90, 0.82, 0.86, 0.79, 0.77, 0.84, 0.81, 0.76, 0.76, 0.72, 0.73, 0.70, 0.72, 0.71, 0.73, 0.73, 0.72, 0.74, 0.71, 0.78, 0.74, 0.72, 0.75, 0.80, 0.76, 0.94, 0.88, 0.91, 0.83, 0.87, 0.84, 0.79, 0.76, 0.82, 0.80, 0.97, 0.89, 0.96, 0.88, 0.95, 0.94, 0.87, 1.11, 1.37, 1.10, 1.59, 1.57, 1.37, 1.33, 1.05, 1.08, 1.54, 1.34, 1.46, 1.16, 0.99, 1.26, 0.96, 1.05, 0.92, 1.45, 1.55, 1.27, 1.60, 1.07, 1.34, 1.35, 1.18, 1.07, 0.93, 0.99, 0.90, 0.93, 0.87, 0.96, 1.27, 0.99, 1.15, 0.77, 0.82, 0.85, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 12 { 1.60, 1.78, 1.68, 1.93, 1.86, 1.71, 1.97, 1.99, 1.99, 1.97, 1.93, 1.60, 1.68, 1.78, 1.86, 1.61, 1.57, 1.79, 1.37, 1.48, 1.59, 1.72, 1.79, 1.92, 1.90, 1.38, 1.35, 1.60, 1.23, 1.30, 1.47, 1.56, 1.71, 1.88, 1.79, 1.92, 1.79, 1.79, 1.30, 1.56, 1.35, 1.37, 1.59, 1.38, 1.60, 1.90, 1.48, 1.72, 1.57, 1.61, 1.79, 1.21, 1.00, 1.30, 0.89, 0.94, 0.96, 1.07, 1.14, 1.40, 1.37, 1.14, 0.96, 0.98, 0.82, 0.88, 0.82, 0.89, 0.83, 0.86, 0.91, 1.02, 0.93, 1.07, 0.87, 0.94, 1.11, 1.02, 0.93, 0.93, 0.82, 0.87, 0.80, 0.85, 0.79, 0.80, 0.85, 0.77, 0.72, 0.74, 0.71, 0.70, 0.70, 0.71, 0.72, 0.77, 0.74, 0.72, 0.76, 0.73, 0.82, 0.79, 0.76, 0.73, 0.79, 0.76, 0.93, 0.86, 0.91, 0.83, 0.89, 0.89, 0.82, 0.72, 0.76, 0.76, 0.89, 0.82, 0.89, 0.82, 0.89, 0.91, 0.83, 0.96, 1.14, 0.97, 1.40, 1.44, 1.19, 1.22, 0.99, 0.98, 1.49, 1.44, 1.49, 1.22, 0.99, 1.23, 0.98, 1.19, 0.97, 1.21, 1.30, 1.00, 1.37, 0.94, 1.07, 1.14, 0.98, 0.96, 0.86, 0.91, 0.83, 0.88, 0.82, 0.89, 1.11, 0.94, 1.07, 0.73, 0.76, 0.79, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 13 { 1.46, 1.65, 1.60, 1.82, 1.80, 1.71, 1.93, 1.91, 1.99, 1.94, 1.98, 1.74, 1.76, 1.89, 1.89, 1.42, 1.34, 1.61, 1.11, 1.22, 1.36, 1.50, 1.61, 1.81, 1.75, 1.15, 1.17, 1.41, 1.18, 1.19, 1.42, 1.44, 1.65, 1.83, 1.67, 1.94, 1.81, 1.88, 1.32, 1.58, 1.45, 1.57, 1.74, 1.53, 1.70, 1.98, 1.69, 1.87, 1.77, 1.79, 1.92, 1.45, 1.27, 1.55, 0.97, 1.07, 1.11, 1.34, 1.37, 1.59, 1.60, 1.35, 1.07, 1.18, 0.86, 0.93, 0.87, 0.96, 0.90, 0.93, 0.99, 1.03, 0.95, 1.15, 0.90, 0.99, 1.27, 0.98, 0.90, 0.92, 0.78, 0.83, 0.77, 0.84, 0.79, 0.82, 0.86, 0.73, 0.71, 0.73, 0.72, 0.70, 0.73, 0.72, 0.76, 0.81, 0.76, 0.76, 0.82, 0.77, 0.89, 0.85, 0.82, 0.75, 0.80, 0.80, 0.94, 0.88, 0.94, 0.87, 0.95, 0.96, 0.88, 0.72, 0.74, 0.76, 0.83, 0.78, 0.84, 0.79, 0.87, 0.91, 0.83, 0.89, 0.98, 0.92, 1.23, 1.34, 1.05, 1.16, 0.99, 0.96, 1.46, 1.57, 1.54, 1.33, 1.05, 1.26, 1.08, 1.37, 1.10, 0.98, 1.03, 0.92, 1.14, 0.86, 0.95, 0.97, 0.90, 0.89, 0.79, 0.84, 0.77, 0.82, 0.76, 0.82, 0.97, 0.89, 0.98, 0.71, 0.72, 0.74, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 14 { 1.34, 1.51, 1.53, 1.67, 1.72, 1.71, 1.80, 1.77, 1.91, 1.86, 1.98, 1.86, 1.82, 1.95, 1.89, 1.24, 1.10, 1.41, 0.95, 0.99, 1.09, 1.25, 1.37, 1.63, 1.55, 0.96, 0.98, 1.16, 1.05, 1.00, 1.27, 1.23, 1.50, 1.69, 1.46, 1.86, 1.72, 1.87, 1.24, 1.49, 1.44, 1.69, 1.80, 1.59, 1.69, 1.97, 1.82, 1.94, 1.91, 1.92, 1.99, 1.63, 1.50, 1.74, 1.16, 1.33, 1.38, 1.58, 1.60, 1.77, 1.80, 1.48, 1.21, 1.37, 0.90, 0.97, 0.93, 1.05, 0.97, 1.04, 1.21, 0.99, 0.95, 1.14, 0.92, 1.02, 1.34, 0.94, 0.86, 0.90, 0.74, 0.79, 0.75, 0.81, 0.79, 0.84, 0.86, 0.71, 0.71, 0.73, 0.76, 0.73, 0.77, 0.74, 0.80, 0.85, 0.78, 0.81, 0.89, 0.84, 0.97, 0.92, 0.88, 0.79, 0.85, 0.86, 0.98, 0.92, 1.00, 0.93, 1.06, 1.12, 0.95, 0.74, 0.74, 0.78, 0.79, 0.76, 0.82, 0.79, 0.87, 0.93, 0.85, 0.85, 0.94, 0.90, 1.09, 1.27, 0.99, 1.17, 1.05, 0.96, 1.46, 1.71, 1.62, 1.48, 1.20, 1.34, 1.28, 1.57, 1.35, 0.90, 0.94, 0.85, 0.98, 0.81, 0.89, 0.89, 0.83, 0.82, 0.75, 0.78, 0.73, 0.77, 0.72, 0.76, 0.89, 0.83, 0.91, 0.71, 0.70, 0.72, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 }, // 15 { 1.26, 1.39, 1.48, 1.51, 1.64, 1.71, 1.60, 1.58, 1.77, 1.74, 1.91, 1.94, 1.87, 1.97, 1.85, 1.10, 0.97, 1.22, 0.88, 0.92, 0.95, 1.01, 1.11, 1.39, 1.32, 0.88, 0.90, 0.97, 0.96, 0.93, 1.05, 0.99, 1.27, 1.47, 1.20, 1.70, 1.54, 1.76, 1.08, 1.31, 1.33, 1.70, 1.76, 1.55, 1.57, 1.88, 1.85, 1.91, 1.97, 1.99, 1.99, 1.70, 1.65, 1.85, 1.41, 1.54, 1.61, 1.76, 1.80, 1.91, 1.93, 1.52, 1.26, 1.48, 0.92, 0.99, 0.97, 1.18, 1.09, 1.28, 1.39, 0.94, 0.93, 1.05, 0.92, 1.01, 1.31, 0.88, 0.81, 0.86, 0.72, 0.75, 0.74, 0.79, 0.79, 0.86, 0.85, 0.71, 0.73, 0.75, 0.82, 0.77, 0.83, 0.78, 0.85, 0.88, 0.81, 0.88, 0.97, 0.90, 1.18, 1.00, 0.93, 0.86, 0.92, 0.94, 1.14, 0.99, 1.24, 1.03, 1.33, 1.39, 1.11, 0.79, 0.77, 0.84, 0.79, 0.77, 0.84, 0.83, 0.90, 0.98, 0.91, 0.85, 0.92, 0.91, 1.02, 1.26, 1.00, 1.23, 1.19, 0.99, 1.50, 1.84, 1.71, 1.64, 1.38, 1.46, 1.51, 1.76, 1.59, 0.84, 0.88, 0.80, 0.94, 0.79, 0.86, 0.82, 0.77, 0.76, 0.74, 0.74, 0.71, 0.73, 0.70, 0.72, 0.82, 0.77, 0.85, 0.74, 0.70, 0.73, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00 } engine/h2shared/asm_nasm.inc000066400000000000000000000026411444734033100163220ustar00rootroot00000000000000; asm_nasm.inc -- common header for NASM format x86-assembly sources ; Copyright (C) 2008-2012 O.Sezer ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; ; ; underscore prefix handling: ; unless we receive _NO_PREFIX, we shall prefix C-shared ; symbols with '_'. for ELF targets, or whenever necessary, ; you must specifically add -D_NO_PREFIX to your NASMFLAGS. ; %ifndef _NO_PREFIX %macro _sym_prefix 1 %define %1 _ %+ %1 %endmacro %endif ; ; other definitions / macros ; %idefine offset ; ; OS/2 special handlings ;(see the nasm documentation for obj format.) ; %ifdef __OS2__ BITS 32 ; declare segments with proper attributes for OS/2 builds SEGMENT .data CLASS=DATA ALIGN=16 USE32 FLAT SEGMENT .text CLASS=CODE ALIGN=16 USE32 FLAT %endif engine/h2shared/bgmnull.c000066400000000000000000000005141444734033100156320ustar00rootroot00000000000000/* BGM code for when we are configured for no sound : */ /* _NO_SOUND should have been defined by the Makefile. */ #ifdef _NO_MIDIDRV /* no sound driver && codecs, no midi driver, nada... */ #include "bgmnull_none.c" #else /* perversely, we have no sound, but have midi.. */ #include "bgmnull_midi.c" #endif /* _NO_MIDIDRV */ engine/h2shared/bgmnull_midi.c000066400000000000000000000104041444734033100166330ustar00rootroot00000000000000/* * Background music handling for Hexen II: Hammer of Thyrion (uHexen2) * Handle cases when we are configured for no sound BUT with a midi driver * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2010-2011 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "bgmusic.h" #include "cdaudio.h" #include "midi_drv.h" #define MIDI_DIRNAME "midi" qboolean bgmloop; cvar_t bgm_extmusic = {"bgm_extmusic", "0", CVAR_ARCHIVE}; static float old_volume = -1.0f; typedef struct midi_handle_s { void *handle; midi_driver_t *driver; } midi_handle_t; static midi_driver_t *midi_drivers = NULL; static midi_handle_t midi_handle; static void BGM_Play_f (void) { if (Cmd_Argc() == 2) { /* BGM_Play (Cmd_Argv(1));*/ BGM_PlayMIDIorMusic (Cmd_Argv(1)); } else { Con_Printf ("music \n"); return; } } static void BGM_Pause_f (void) { BGM_Pause (); } static void BGM_Resume_f (void) { BGM_Resume (); } static void BGM_Loop_f (void) { if (Cmd_Argc() == 2) { if (q_strcasecmp(Cmd_Argv(1), "0") == 0 || q_strcasecmp(Cmd_Argv(1),"off") == 0) bgmloop = false; else if (q_strcasecmp(Cmd_Argv(1), "1") == 0 || q_strcasecmp(Cmd_Argv(1),"on") == 0) bgmloop = true; else if (q_strcasecmp(Cmd_Argv(1),"toggle") == 0) bgmloop = !bgmloop; } if (bgmloop) Con_Printf("Music will be looped\n"); else Con_Printf("Music will not be looped\n"); } static void BGM_Stop_f (void) { BGM_Stop(); } void BGM_RegisterMidiDRV (void *drv) { midi_driver_t *driver = (midi_driver_t *) drv; driver->next = midi_drivers; midi_drivers = driver; } qboolean BGM_Init (void) { memset (&midi_handle, 0, sizeof(midi_handle_t)); Cvar_RegisterVariable(&bgm_extmusic); Cmd_AddCommand("music", BGM_Play_f); Cmd_AddCommand("music_pause", BGM_Pause_f); Cmd_AddCommand("music_resume", BGM_Resume_f); Cmd_AddCommand("music_loop", BGM_Loop_f); Cmd_AddCommand("music_stop", BGM_Stop_f); bgmloop = true; return true; } void BGM_Shutdown (void) { BGM_Stop(); /* sever our connections to * midi_drv and snd_codec */ midi_drivers = NULL; } void BGM_PlayMIDIorMusic (const char *filename) { midi_driver_t *drv; char tmp[MAX_QPATH]; if (midi_drivers == NULL) return; drv = midi_drivers; q_snprintf(tmp, sizeof(tmp), "%s/%s.%s", MIDI_DIRNAME, filename, "mid"); while (drv) { if (drv->available) { midi_handle.handle = drv->mididrv_open (filename); if (midi_handle.handle != NULL) { midi_handle.driver = drv; return; } } drv = drv->next; } Con_Printf("Couldn't open music file %s\n", filename); } void BGM_Stop (void) { if (midi_handle.handle) { midi_handle.driver->mididrv_close (& midi_handle.handle); midi_handle.handle = NULL; midi_handle.driver = NULL; } } void BGM_Pause (void) { if (midi_handle.handle) midi_handle.driver->mididrv_pause (& midi_handle.handle); } void BGM_Resume (void) { if (midi_handle.handle) midi_handle.driver->mididrv_resume (& midi_handle.handle); } void BGM_Update (void) { if (old_volume != bgmvolume.value) { if (bgmvolume.value < 0) Cvar_SetQuick (&bgmvolume, "0"); else if (bgmvolume.value > 1) Cvar_SetQuick (&bgmvolume, "1"); old_volume = bgmvolume.value; if (midi_handle.handle) { midi_handle.driver->mididrv_setvol (& midi_handle.handle, bgmvolume.value); if (bgmvolume.value == 0.0f) /* don't bother advancing */ midi_handle.driver->mididrv_pause (& midi_handle.handle); else midi_handle.driver->mididrv_resume(& midi_handle.handle); } } if (midi_handle.handle) midi_handle.driver->mididrv_advance (& midi_handle.handle); } engine/h2shared/bgmnull_none.c000066400000000000000000000032021444734033100166460ustar00rootroot00000000000000/* * Background music handling for Hexen II: Hammer of Thyrion (uHexen2) * Handle cases when we are configured for no sound and no midi driver, * nada... * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2010-2011 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "bgmusic.h" #include "cdaudio.h" qboolean bgmloop = true; cvar_t bgm_extmusic = {"bgm_extmusic", "0", CVAR_ARCHIVE}; static float old_volume = -1.0f; qboolean BGM_Init (void) { Cvar_RegisterVariable(&bgm_extmusic); return false; } void BGM_Shutdown (void) { } void BGM_PlayMIDIorMusic (const char *filename) { } void BGM_Stop (void) { } void BGM_Pause (void) { } void BGM_Resume (void) { } void BGM_Update (void) { if (old_volume != bgmvolume.value) { if (bgmvolume.value < 0) Cvar_SetQuick (&bgmvolume, "0"); else if (bgmvolume.value > 1) Cvar_SetQuick (&bgmvolume, "1"); old_volume = bgmvolume.value; } } engine/h2shared/bgmusic.c000066400000000000000000000366241444734033100156360ustar00rootroot00000000000000/* * Background music handling for Hexen II: Hammer of Thyrion (uHexen2) * Handles streaming music as raw sound samples and runs the midi driver * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2010-2018 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" #include "snd_codec.h" #include "bgmusic.h" #include "cdaudio.h" #include "midi_drv.h" #define MIDI_DIRNAME "midi" #define MUSIC_DIRNAME "music" qboolean bgmloop; cvar_t bgm_extmusic = {"bgm_extmusic", "1", CVAR_ARCHIVE}; static qboolean no_extmusic= false; static float old_volume = -1.0f; static midi_driver_t *midi_drivers = NULL; typedef enum _bgm_player { BGM_NONE = -1, BGM_MIDIDRV = 1, BGM_STREAMER } bgm_player_t; typedef struct music_handler_s { unsigned int type; /* 1U << n (see snd_codec.h) */ bgm_player_t player; /* Enumerated bgm player type */ int is_available; /* -1 means not present */ const char *ext; /* Expected file extension */ const char *dir; /* Where to look for music file */ struct music_handler_s *next; } music_handler_t; static music_handler_t wanted_handlers[] = { { CODECTYPE_VORBIS,BGM_STREAMER,-1, "ogg", MUSIC_DIRNAME, NULL }, { CODECTYPE_OPUS, BGM_STREAMER, -1, "opus", MUSIC_DIRNAME, NULL }, { CODECTYPE_MP3, BGM_STREAMER, -1, "mp3", MUSIC_DIRNAME, NULL }, { CODECTYPE_FLAC, BGM_STREAMER, -1, "flac", MUSIC_DIRNAME, NULL }, { CODECTYPE_WAV, BGM_STREAMER, -1, "wav", MUSIC_DIRNAME, NULL }, { CODECTYPE_MOD, BGM_STREAMER, -1, "it", MUSIC_DIRNAME, NULL }, { CODECTYPE_MOD, BGM_STREAMER, -1, "s3m", MUSIC_DIRNAME, NULL }, { CODECTYPE_MOD, BGM_STREAMER, -1, "xm", MUSIC_DIRNAME, NULL }, { CODECTYPE_MOD, BGM_STREAMER, -1, "mod", MUSIC_DIRNAME, NULL }, { CODECTYPE_UMX, BGM_STREAMER, -1, "umx", MUSIC_DIRNAME, NULL }, /* midi must be last before NULL terminator. */ #define MIDIDRIVER_MID (1U << 31) /* special, comes before CODECTYPE_MID */ { MIDIDRIVER_MID, BGM_MIDIDRV, -1, "mid", MIDI_DIRNAME, NULL }, { CODECTYPE_MID, BGM_STREAMER, -1, "mid", MIDI_DIRNAME, NULL }, { CODECTYPE_NONE, BGM_NONE, -1, NULL, NULL, NULL } }; static music_handler_t *music_handlers = NULL; #define ANY_CODECTYPE 0xFFFFFFFF #define MIDI_TYPES (CODECTYPE_MID | MIDIDRIVER_MID) #define MIDITYPE(x) (((x) & MIDI_TYPES) != 0) #define CDRIP_TYPES (CODECTYPE_VORBIS | CODECTYPE_MP3 | CODECTYPE_FLAC | CODECTYPE_WAV | CODECTYPE_OPUS) #define CDRIPTYPE(x) (((x) & CDRIP_TYPES) != 0) typedef struct midi_handle_s { void *handle; midi_driver_t *driver; } midi_handle_t; /* only one of bgmstream and mid_handle is supposed to be * active, not both. */ static midi_handle_t midi_handle; static snd_stream_t *bgmstream = NULL; static void BGM_Play_f (void) { if (Cmd_Argc() == 2) { BGM_Play (Cmd_Argv(1)); } else { Con_Printf ("music \n"); } } static void BGM_Pause_f (void) { BGM_Pause (); } static void BGM_Resume_f (void) { BGM_Resume (); } static void BGM_Loop_f (void) { if (Cmd_Argc() == 2) { if (q_strcasecmp(Cmd_Argv(1), "0") == 0 || q_strcasecmp(Cmd_Argv(1),"off") == 0) bgmloop = false; else if (q_strcasecmp(Cmd_Argv(1), "1") == 0 || q_strcasecmp(Cmd_Argv(1),"on") == 0) bgmloop = true; else if (q_strcasecmp(Cmd_Argv(1),"toggle") == 0) bgmloop = !bgmloop; if (bgmstream) bgmstream->loop = bgmloop; } if (bgmloop) Con_Printf("Music will be looped\n"); else Con_Printf("Music will not be looped\n"); } static void BGM_Stop_f (void) { BGM_Stop(); } static void BGM_Jump_f (void) { if (Cmd_Argc() != 2) { Con_Printf ("music_jump \n"); } else if (bgmstream) { S_CodecJumpToOrder(bgmstream, atoi(Cmd_Argv(1))); } } void BGM_RegisterMidiDRV (void *drv) { midi_driver_t *driver = (midi_driver_t *) drv; driver->next = midi_drivers; midi_drivers = driver; } qboolean BGM_Init (void) { music_handler_t *handlers = NULL; int i; memset (&midi_handle, 0, sizeof(midi_handle_t)); Cvar_RegisterVariable(&bgm_extmusic); Cmd_AddCommand("music", BGM_Play_f); Cmd_AddCommand("music_pause", BGM_Pause_f); Cmd_AddCommand("music_resume", BGM_Resume_f); Cmd_AddCommand("music_loop", BGM_Loop_f); Cmd_AddCommand("music_stop", BGM_Stop_f); Cmd_AddCommand("music_jump", BGM_Jump_f); if (COM_CheckParm("-noextmusic") != 0) no_extmusic = true; bgmloop = true; for (i = 0; wanted_handlers[i].type != CODECTYPE_NONE; i++) { switch (wanted_handlers[i].player) { case BGM_MIDIDRV: wanted_handlers[i].is_available = (midi_drivers == NULL) ? -1 : 1; break; case BGM_STREAMER: wanted_handlers[i].is_available = S_CodecIsAvailable(wanted_handlers[i].type); break; case BGM_NONE: default: break; } if (wanted_handlers[i].is_available != -1) { if (handlers) { handlers->next = &wanted_handlers[i]; handlers = handlers->next; } else { music_handlers = &wanted_handlers[i]; handlers = music_handlers; } } } return true; } void BGM_Shutdown (void) { BGM_Stop(); /* sever our connections to * midi_drv and snd_codec */ music_handlers = NULL; midi_drivers = NULL; } static int BGM_Play_mididrv (const char *filename) { midi_driver_t *drv = midi_drivers; while (drv) { if (drv->available) { midi_handle.handle = drv->mididrv_open (filename); if (midi_handle.handle != NULL) { midi_handle.driver = drv; if (bgmvolume.value == 0.0f) /* don't bother advancing */ drv->mididrv_pause(&midi_handle.handle); return 0; } } drv = drv->next; } return -1; } static void BGM_Play_noext (const char *filename, unsigned int allowed_types) { char tmp[MAX_QPATH]; music_handler_t *handler; handler = music_handlers; while (handler) { if (! (handler->type & allowed_types)) { handler = handler->next; continue; } if (!handler->is_available) { handler = handler->next; continue; } q_snprintf(tmp, sizeof(tmp), "%s/%s.%s", handler->dir, filename, handler->ext); switch (handler->player) { case BGM_MIDIDRV: if (BGM_Play_mididrv(tmp) == 0) return; /* success */ /* BGM_MIDIDRV is followed by CODECTYPE_MID streamer. * Even if the midi driver failed, we may still have * a chance with the streamer if it's available... */ if (! (handler->next && handler->next->is_available)) break; handler = handler->next; case BGM_STREAMER: bgmstream = S_CodecOpenStreamType(tmp, handler->type, bgmloop); if (bgmstream) return; /* success */ break; case BGM_NONE: default: break; } handler = handler->next; } Con_Printf("Couldn't handle music file %s\n", filename); } void BGM_Play (const char *filename) { char tmp[MAX_QPATH]; const char *ext; music_handler_t *handler; BGM_Stop(); if (music_handlers == NULL) return; if (!filename || !*filename) { Con_DPrintf("null music file name\n"); return; } ext = COM_FileGetExtension(filename); if (! *ext) /* try all things */ { BGM_Play_noext(filename, ANY_CODECTYPE); return; } /* use the filename as is */ handler = music_handlers; while (handler) { if (handler->is_available && !q_strcasecmp(ext, handler->ext)) break; handler = handler->next; } if (!handler) { Con_Printf("Unhandled extension for %s\n", filename); return; } q_snprintf(tmp, sizeof(tmp), "%s/%s", handler->dir, filename); switch (handler->player) { case BGM_MIDIDRV: if (BGM_Play_mididrv(tmp) == 0) return; /* success */ /* BGM_MIDIDRV is followed by CODECTYPE_MID streamer. * Even if the midi driver failed, we may still have * a chance with the streamer if it's available... */ if (! (handler->next && handler->next->is_available)) break; handler = handler->next; case BGM_STREAMER: bgmstream = S_CodecOpenStreamType(tmp, handler->type, bgmloop); if (bgmstream) return; /* success */ break; case BGM_NONE: default: break; } Con_Printf("Couldn't handle music file %s\n", filename); } void BGM_PlayMIDIorMusic (const char *filename) { /* instead of searching by the order of music_handlers, do so by * the order of searchpath priority: the file from the searchpath * with the highest path_id is most likely from our own gamedir * itself. this way, if a mod has egyp1 as a mp3 or a midi, which * is below *.ogg in the music_handler order, the mp3 or midi will * still have priority over egyp1.ogg from, say, data1. */ char tmp[MAX_QPATH]; const char *ext, *dir; unsigned int path_id, prev_id, type; qboolean try_midi_stream; music_handler_t *handler; if (music_handlers == NULL) return; BGM_Stop(); if (!filename || !*filename) { Con_DPrintf("null music file name\n"); return; } ext = COM_FileGetExtension(filename); if (*ext != '\0') { BGM_Play(filename); return; } prev_id = 0; type = 0; dir = ext = NULL; handler = music_handlers; try_midi_stream = false; while (handler) { if (! handler->is_available) goto _next; if (! MIDITYPE(handler->type) && (no_extmusic || !bgm_extmusic.value)) goto _next; q_snprintf(tmp, sizeof(tmp), "%s/%s.%s", handler->dir, filename, handler->ext); if (! FS_FileExists(tmp, &path_id)) { if (handler->type == MIDIDRIVER_MID) break; goto _next; } if (path_id > prev_id) { prev_id = path_id; type = handler->type; ext = handler->ext; dir = handler->dir; if (handler->type == MIDIDRIVER_MID) { if (handler->next && handler->next->is_available) try_midi_stream = true; break; } } _next: handler = handler->next; } if (ext == NULL) Con_Printf("Couldn't handle music file %s\n", filename); else { q_snprintf(tmp, sizeof(tmp), "%s/%s.%s", dir, filename, ext); switch (type) { case MIDIDRIVER_MID: if (BGM_Play_mididrv(tmp) == 0) return; /* success */ /* BGM_MIDIDRV is followed by CODECTYPE_MID streamer. * Even if the midi driver failed, we may still have * a chance with the streamer if it's available... */ if (!try_midi_stream) break; type = CODECTYPE_MID; default: bgmstream = S_CodecOpenStreamType(tmp, type, bgmloop); if (bgmstream) return; /* success */ } Con_Printf("Couldn't handle music file %s\n", tmp); } } void BGM_PlayCDtrack (byte track, qboolean looping) { /* instead of searching by the order of music_handlers, do so by * the order of searchpath priority: the file from the searchpath * with the highest path_id is most likely from our own gamedir * itself. This way, if a mod has track02 as a *.mp3 file, which * is below *.ogg in the music_handler order, the mp3 will still * have priority over track02.ogg from, say, data1. */ char tmp[MAX_QPATH]; const char *ext; unsigned int path_id, prev_id, type; music_handler_t *handler; BGM_Stop(); if (CDAudio_Play(track, looping) == 0) return; /* success */ if (music_handlers == NULL) return; if (no_extmusic || !bgm_extmusic.value) return; prev_id = 0; type = 0; ext = NULL; handler = music_handlers; while (handler) { if (! handler->is_available) goto _next; if (! CDRIPTYPE(handler->type)) goto _next; q_snprintf(tmp, sizeof(tmp), "%s/track%02d.%s", MUSIC_DIRNAME, (int)track, handler->ext); if (! FS_FileExists(tmp, &path_id)) goto _next; if (path_id > prev_id) { prev_id = path_id; type = handler->type; ext = handler->ext; } _next: handler = handler->next; } if (ext == NULL) Con_Printf("Couldn't find a cdrip for track %d\n", (int)track); else { q_snprintf(tmp, sizeof(tmp), "%s/track%02d.%s", MUSIC_DIRNAME, (int)track, ext); bgmstream = S_CodecOpenStreamType(tmp, type, bgmloop); if (! bgmstream) Con_Printf("Couldn't handle music file %s\n", tmp); } } void BGM_Stop (void) { if (midi_handle.handle) { midi_handle.driver->mididrv_close (& midi_handle.handle); midi_handle.handle = NULL; midi_handle.driver = NULL; } if (bgmstream) { bgmstream->status = STREAM_NONE; S_CodecCloseStream(bgmstream); bgmstream = NULL; s_rawend = 0; } } void BGM_Pause (void) { if (midi_handle.handle) midi_handle.driver->mididrv_pause (& midi_handle.handle); if (bgmstream) { if (bgmstream->status == STREAM_PLAY) bgmstream->status = STREAM_PAUSE; } } void BGM_Resume (void) { if (midi_handle.handle) midi_handle.driver->mididrv_resume (& midi_handle.handle); if (bgmstream) { if (bgmstream->status == STREAM_PAUSE) bgmstream->status = STREAM_PLAY; } } static void BGM_UpdateStream (void) { qboolean did_rewind = false; int res; /* Number of bytes read. */ int bufferSamples; int fileSamples; int fileBytes; byte raw[16384]; if (bgmstream->status != STREAM_PLAY) return; /* don't bother playing anything if musicvolume is 0 */ if (bgmvolume.value <= 0) return; /* see how many samples should be copied into the raw buffer */ if (s_rawend < paintedtime) s_rawend = paintedtime; while (s_rawend < paintedtime + MAX_RAW_SAMPLES) { bufferSamples = MAX_RAW_SAMPLES - (s_rawend - paintedtime); /* decide how much data needs to be read from the file */ fileSamples = bufferSamples * bgmstream->info.rate / shm->speed; if (!fileSamples) return; /* our max buffer size */ fileBytes = fileSamples * (bgmstream->info.width * bgmstream->info.channels); if (fileBytes > (int) sizeof(raw)) { fileBytes = (int) sizeof(raw); fileSamples = fileBytes / (bgmstream->info.width * bgmstream->info.channels); } /* Read */ res = S_CodecReadStream(bgmstream, fileBytes, raw); if (res < fileBytes) { fileBytes = res; fileSamples = res / (bgmstream->info.width * bgmstream->info.channels); } if (res > 0) /* data: add to raw buffer */ { S_RawSamples(fileSamples, bgmstream->info.rate, bgmstream->info.width, bgmstream->info.channels, raw, bgmvolume.value); did_rewind = false; } else if (res == 0) /* EOF */ { if (bgmloop) { if (did_rewind) { Con_Printf("Stream keeps returning EOF.\n"); BGM_Stop(); return; } res = S_CodecRewindStream(bgmstream); if (res != 0) { Con_Printf("Stream seek error (%i), stopping.\n", res); BGM_Stop(); return; } did_rewind = true; } else { BGM_Stop(); return; } } else /* res < 0: some read error */ { Con_Printf("Stream read error (%i), stopping.\n", res); BGM_Stop(); return; } } } void BGM_Update (void) { if (old_volume != bgmvolume.value) { if (bgmvolume.value < 0) Cvar_SetQuick (&bgmvolume, "0"); else if (bgmvolume.value > 1) Cvar_SetQuick (&bgmvolume, "1"); old_volume = bgmvolume.value; if (midi_handle.handle) { midi_handle.driver->mididrv_setvol (& midi_handle.handle, bgmvolume.value); if (bgmvolume.value == 0.0f) /* don't bother advancing */ midi_handle.driver->mididrv_pause (& midi_handle.handle); else midi_handle.driver->mididrv_resume(& midi_handle.handle); } } if (midi_handle.handle) midi_handle.driver->mididrv_advance (& midi_handle.handle); if (bgmstream) BGM_UpdateStream (); } engine/h2shared/bgmusic.h000066400000000000000000000030041444734033100156250ustar00rootroot00000000000000/* Background music handling for Hexen II: Hammer of Thyrion (uHexen2) * Handles streaming music as raw sound samples and runs the midi driver * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2010-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _BGMUSIC_H_ #define _BGMUSIC_H_ extern qboolean bgmloop; extern cvar_t bgm_extmusic; qboolean BGM_Init (void); void BGM_Shutdown (void); void BGM_RegisterMidiDRV (void *drv); void BGM_Play (const char *filename); void BGM_Stop (void); void BGM_Update (void); void BGM_Pause (void); void BGM_Resume (void); void BGM_PlayCDtrack (byte track, qboolean looping); void BGM_PlayMIDIorMusic (const char *filename); /* init and shutdown procedures for midi driver: */ qboolean MIDI_Init (void); void MIDI_Cleanup (void); #endif /* _BGMUSIC_H_ */ engine/h2shared/bgmusic.old000066400000000000000000000123311444734033100161570ustar00rootroot00000000000000/* * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2010-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /* removed support for cdtrack to midiname mapping */ /* * These mappings apply only to original Hexen II and its * expansion pack Portal of Praevus: community maps need * not, and in many examples do not, follow this table. * Even some of the maps from Raven itself are quirky: * - tower.bsp of the original game wants "casb2" as midi * but wants cd track 3 instead of 16. * - keep5.bsp of the expansion pack wants "casb1" as midi * but wants cd track 6 instead of 15. */ static const char *data1_tracks[] = { "", /* track01 is data */ "casa1", /* 02 */ "casa2", /* 03 */ "casa3", /* 04 */ "casa4", /* 05 */ "egyp1", /* 06 */ "egyp2", /* 07 */ "egyp3", /* 08 */ "meso1", /* 09 */ "meso2", /* 10 */ "meso3", /* 11 */ "roma1", /* 12 */ "roma2", /* 13 */ "roma3", /* 14 */ "casb1", /* 15 */ "casb2", /* 16 */ "casb3" /* track 17 is last */ }; static const char *portals_tracks[] = { "", /* track01 is data */ "tulku7", /* 02 */ "tulku1", /* 03 */ "tulku4", /* 04 */ "tulku2", /* 05 */ "tulku9", /* 06 */ "tulku10", /* 07 */ "tulku6", /* 08 */ "tulku5", /* 09 */ "tulku8", /* 10 */ "tulku3" /* 11, last. */ /* track12 is last but is not associated * with a midi/music name. it is only used * by the menu during credits display. * therefore, it is not included here. */ }; #define num_data1_tracks (sizeof(data1_tracks) / sizeof(data1_tracks[0])) #define num_portals_tracks (sizeof(portals_tracks) / sizeof(portals_tracks[0])) static int map_cdtrack (const char *midiname) /* crapola: see notes above */ { if (cls.state < ca_connected) /* no have a cd track number yet. */ return 0; #if !defined(H2W) if ((cls.demoplayback || cls.demorecording) && cls.forcetrack != -1) { /* if (cls.forcetrack < 2) return 0; else if (gameflags & GAME_PORTALS && cls.forcetrack <= num_portals_tracks) return cls.forcetrack; else if (cls.forcetrack <= num_data1_tracks) return cls.forcetrack; else return 0; */ /* There is forcetrack for cdaudio, but there is * no corresponding "forcemidi", therefore there * is no meaning in mapping forcetrack to a name. * If you really want your forced track, you use * cdaudio. */ if (cl.cdtrack != cls.forcetrack) return 0; } #endif /* H2W */ if (cl.cdtrack < 2) return 0; /* track01 is always data */ if (cl.cdtrack <= num_portals_tracks && q_strcasecmp(midiname, portals_tracks[cl.cdtrack - 1]) == 0) { return cl.cdtrack; } if (cl.cdtrack <= num_data1_tracks && q_strcasecmp(midiname, data1_tracks[cl.cdtrack - 1]) == 0) { return cl.cdtrack; } return 0; } void BGM_PlayMIDIorMusic (const char *filename) { char tmp[MAX_QPATH]; const char *ext; music_handler_t *handler; int cdtrack = 0; if (music_handlers == NULL) return; BGM_Stop(); if (!filename || !*filename) { Con_DPrintf("null music file name\n"); return; } ext = COM_FileGetExtension(filename); if (*ext != '\0') { BGM_Play(filename); return; } handler = music_handlers; while (handler) { if (MIDITYPE(handler->type)) { if (cdtrack == 0) { cdtrack = map_cdtrack(filename); if (cdtrack != 0) { handler = music_handlers; /* reset */ Con_DPrintf ("searching a cd-rip for track %d\n", cdtrack); continue; } } else { cdtrack = 0; /* we couldn't find a track file */ } } if (!handler->is_available) { handler = handler->next; continue; } if (cdtrack != 0) { if (! CDRIPTYPE(handler->type)) { handler = handler->next; continue; } q_snprintf(tmp, sizeof(tmp), "%s/track%02d.%s", handler->dir, cdtrack, handler->ext); } else { if (MIDITYPE(handler->type)) Con_DPrintf("trying a midi file\n"); q_snprintf(tmp, sizeof(tmp), "%s/%s.%s", handler->dir, filename, handler->ext); } switch (handler->player) { case BGM_MIDIDRV: if (BGM_Play_mididrv(tmp) == 0) return; /* success */ /* BGM_MIDIDRV is followed by CODECTYPE_MID streamer. * Even if the midi driver failed, we may still have * a chance with the streamer if it's available... */ if (! (handler->next && handler->next->is_available)) break; handler = handler->next; case BGM_STREAMER: bgmstream = S_CodecOpenStreamType(tmp, handler->type); if (bgmstream) return; /* success */ break; case BGM_NONE: default: break; } handler = handler->next; } Con_Printf("Couldn't handle music file %s\n", filename); } engine/h2shared/c2p1x1_8_c5_030.s000066400000000000000000000156171444734033100164420ustar00rootroot00000000000000; ; 2000-04-17 ; ; c2p1x1_8_c5_030 ; ; 1.31vbl [all dma off] on Blizzard1230-IV@50MHz ; ; 2000-04-17: added bplsize modifying init (smcinit) ; 1999-01-08: initial version ; ; bplsize must be less than or equal to 16kB! ; IFND BPLX BPLX EQU 320 ENDC IFND BPLY BPLY EQU 256 ENDC IFND BPLSIZE BPLSIZE EQU BPLX*BPLY/8 ENDC IFND CHUNKYXMAX CHUNKYXMAX EQU BPLX ENDC IFND CHUNKYYMAX CHUNKYYMAX EQU BPLY ENDC xdef _c2p1x1_8_c5_030_smcinit xdef _c2p1x1_8_c5_030_init xdef _c2p1x1_8_c5_030 ; incdir include: include lvo/exec_lib.i section code,code ; d0.w chunkyx [chunky-pixels] ; d1.w chunkyy [chunky-pixels] ; d2.w (scroffsx) [screen-pixels] ; d3.w scroffsy [screen-pixels] ; d4.l (rowlen) [bytes] -- offset between one row and the next in a bpl ; d5.l bplsize [bytes] -- offset between one row in one bpl and the next bpl ; d6.l (chunkylen) [bytes] -- offset between one row and the next in chunkybuf _c2p1x1_8_c5_030_smcinit c2p1x1_8_c5_030_smcinit movem.l d2-d3/d5/a6,-(sp) andi.l #$ffff,d0 mulu.w d0,d3 lsr.l #3,d3 move.l d3,c2p1x1_8_c5_030_scroffs mulu.w d0,d1 move.l d1,c2p1x1_8_c5_030_pixels move.w d5,c2p1x1_8_c5_030_smc1 move.w d5,c2p1x1_8_c5_030_smc2 move.w d5,c2p1x1_8_c5_030_smc5 move.w d5,c2p1x1_8_c5_030_smc8 move.w d5,c2p1x1_8_c5_030_smc11 move.w d5,d0 neg.w d0 subq.w #4,d0 move.w d0,c2p1x1_8_c5_030_smc3 move.w d0,c2p1x1_8_c5_030_smc6 move.w d0,c2p1x1_8_c5_030_smc9 move.w d0,c2p1x1_8_c5_030_smc12 add.l d5,d5 move.w d5,c2p1x1_8_c5_030_smc4 move.w d5,c2p1x1_8_c5_030_smc10 add.l d5,d5 move.l d5,c2p1x1_8_c5_030_smc7 move.l $4.w,a6 jsr _LVOCacheClearU(a6) movem.l (sp)+,d2-d3/d5/a6 rts ; d0.w chunkyx [chunky-pixels] ; d1.w chunkyy [chunky-pixels] ; d2.w (scroffsx) [screen-pixels] ; d3.w scroffsy [screen-pixels] ; d4.l (rowlen) [bytes] -- offset between one row and the next in a bpl ; d5.l (bplsize) [bytes] -- offset between one row in one bpl and the next bpl ; d6.l (chunkylen) [bytes] -- offset between one row and the next in chunkybuf _c2p1x1_8_c5_030_init c2p1x1_8_c5_030_init movem.l d2-d3,-(sp) andi.l #$ffff,d0 mulu.w d0,d3 lsr.l #3,d3 move.l d3,c2p1x1_8_c5_030_scroffs mulu.w d0,d1 move.l d1,c2p1x1_8_c5_030_pixels movem.l (sp)+,d2-d3 rts ; a0 c2pscreen ; a1 bitplanes _c2p1x1_8_c5_030 c2p1x1_8_c5_030 movem.l d2-d7/a2-a6,-(sp) move.l #$33333333,d5 move.l #$55555555,d6 move.l #$00ff00ff,a6 add.w #BPLSIZE,a1 c2p1x1_8_c5_030_smc1 EQU *-2 add.l c2p1x1_8_c5_030_scroffs,a1 move.l c2p1x1_8_c5_030_pixels,a2 add.l a0,a2 cmp.l a0,a2 beq .none addq.l #4,a2 movem.l a0-a1,-(sp) move.l (a0)+,d0 move.l (a0)+,d2 move.l (a0)+,d1 move.l (a0)+,d3 move.l #$0f0f0f0f,d4 ; Merge 4x1, part 1 and.l d4,d0 and.l d4,d2 lsl.l #4,d0 or.l d2,d0 and.l d4,d1 and.l d4,d3 move.l (a0)+,d2 lsl.l #4,d1 or.l d3,d1 move.l d1,a3 move.l (a0)+,d1 move.l (a0)+,d3 move.l (a0)+,d7 and.l d4,d2 ; Merge 4x1, part 2 and.l d4,d1 lsl.l #4,d2 or.l d1,d2 and.l d4,d3 and.l d4,d7 lsl.l #4,d3 or.l d7,d3 move.l a3,d1 move.w d2,d7 ; Swap 16x2 move.w d0,d2 swap d2 move.w d2,d0 move.w d7,d2 move.w d3,d7 move.w d1,d3 swap d3 move.w d3,d1 move.w d7,d3 bra.s .start1 .x1 move.l (a0)+,d2 move.l (a0)+,d1 move.l (a0)+,d3 move.l d7,BPLSIZE(a1) c2p1x1_8_c5_030_smc2 EQU *-2 move.l #$0f0f0f0f,d4 ; Merge 4x1, part 1 and.l d4,d0 and.l d4,d2 lsl.l #4,d0 or.l d2,d0 and.l d4,d1 and.l d4,d3 lsl.l #4,d1 or.l d3,d1 move.l d1,a3 move.l (a0)+,d2 and.l d4,d2 ; Merge 4x1, part 2 lsl.l #4,d2 move.l (a0)+,d1 move.l (a0)+,d3 move.l (a0)+,d7 move.l a4,(a1)+ and.l d4,d1 or.l d1,d2 and.l d4,d3 and.l d4,d7 lsl.l #4,d3 or.l d7,d3 move.l a3,d1 move.w d2,d7 ; Swap 16x2 move.w d0,d2 swap d2 move.w d2,d0 move.w d7,d2 move.w d3,d7 move.w d1,d3 swap d3 move.w d3,d1 move.w d7,d3 move.l a5,-BPLSIZE-4(a1) c2p1x1_8_c5_030_smc3 EQU *-2 .start1 move.l a6,d4 move.l d2,d7 ; Swap 2x2 lsr.l #2,d7 eor.l d0,d7 and.l d5,d7 eor.l d7,d0 lsl.l #2,d7 eor.l d7,d2 move.l d3,d7 lsr.l #2,d7 eor.l d1,d7 and.l d5,d7 eor.l d7,d1 lsl.l #2,d7 eor.l d7,d3 move.l d1,d7 lsr.l #8,d7 eor.l d0,d7 and.l d4,d7 eor.l d7,d0 lsl.l #8,d7 eor.l d7,d1 move.l d1,d7 lsr.l #1,d7 eor.l d0,d7 and.l d6,d7 eor.l d7,d0 move.l d0,BPLSIZE*2(a1) c2p1x1_8_c5_030_smc4 EQU *-2 add.l d7,d7 eor.l d1,d7 move.l d3,d1 lsr.l #8,d1 eor.l d2,d1 and.l d4,d1 eor.l d1,d2 lsl.l #8,d1 eor.l d1,d3 move.l d3,d1 lsr.l #1,d1 eor.l d2,d1 and.l d6,d1 eor.l d1,d2 add.l d1,d1 eor.l d1,d3 move.l (a0)+,d0 move.l d2,a4 move.l d3,a5 cmpa.l a0,a2 bne .x1 move.l d7,BPLSIZE(a1) c2p1x1_8_c5_030_smc5 EQU *-2 move.l a4,(a1)+ move.l a5,-BPLSIZE-4(a1) c2p1x1_8_c5_030_smc6 EQU *-2 movem.l (sp)+,a0-a1 move.l (a0)+,d0 add.l #BPLSIZE*4,a1 c2p1x1_8_c5_030_smc7 EQU *-4 move.l (a0)+,d2 move.l (a0)+,d1 move.l (a0)+,d3 move.l #$f0f0f0f0,d4 ; Merge 4x1, part 1 and.l d4,d0 and.l d4,d2 lsr.l #4,d2 or.l d2,d0 and.l d4,d1 and.l d4,d3 move.l (a0)+,d2 lsr.l #4,d3 or.l d3,d1 move.l d1,a3 move.l (a0)+,d1 move.l (a0)+,d3 move.l (a0)+,d7 and.l d4,d2 ; Merge 4x1, part 2 and.l d4,d1 lsr.l #4,d1 or.l d1,d2 and.l d4,d3 and.l d4,d7 lsr.l #4,d7 or.l d7,d3 move.l a3,d1 move.w d2,d7 ; Swap 16x2 move.w d0,d2 swap d2 move.w d2,d0 move.w d7,d2 move.w d3,d7 move.w d1,d3 swap d3 move.w d3,d1 move.w d7,d3 bra.s .start2 .x2 move.l (a0)+,d2 move.l (a0)+,d1 move.l (a0)+,d3 move.l d7,BPLSIZE(a1) c2p1x1_8_c5_030_smc8 EQU *-2 move.l #$f0f0f0f0,d4 ; Merge 4x1, part 1 and.l d4,d0 and.l d4,d2 lsr.l #4,d2 or.l d2,d0 and.l d4,d1 and.l d4,d3 lsr.l #4,d3 or.l d3,d1 move.l d1,a3 move.l (a0)+,d2 and.l d4,d2 ; Merge 4x1, part 2 move.l (a0)+,d1 move.l (a0)+,d3 move.l (a0)+,d7 move.l a4,(a1)+ and.l d4,d1 lsr.l #4,d1 or.l d1,d2 and.l d4,d3 and.l d4,d7 lsr.l #4,d7 or.l d7,d3 move.l a3,d1 move.w d2,d7 ; Swap 16x2 move.w d0,d2 swap d2 move.w d2,d0 move.w d7,d2 move.w d3,d7 move.w d1,d3 swap d3 move.w d3,d1 move.w d7,d3 move.l a5,-BPLSIZE-4(a1) c2p1x1_8_c5_030_smc9 EQU *-2 .start2 move.l a6,d4 move.l d2,d7 ; Swap 2x2 lsr.l #2,d7 eor.l d0,d7 and.l d5,d7 eor.l d7,d0 lsl.l #2,d7 eor.l d7,d2 move.l d3,d7 lsr.l #2,d7 eor.l d1,d7 and.l d5,d7 eor.l d7,d1 lsl.l #2,d7 eor.l d7,d3 move.l d1,d7 lsr.l #8,d7 eor.l d0,d7 and.l d4,d7 eor.l d7,d0 lsl.l #8,d7 eor.l d7,d1 move.l d1,d7 lsr.l #1,d7 eor.l d0,d7 and.l d6,d7 eor.l d7,d0 move.l d0,BPLSIZE*2(a1) c2p1x1_8_c5_030_smc10 EQU *-2 add.l d7,d7 eor.l d1,d7 move.l d3,d1 lsr.l #8,d1 eor.l d2,d1 and.l d4,d1 eor.l d1,d2 lsl.l #8,d1 eor.l d1,d3 move.l d3,d1 lsr.l #1,d1 eor.l d2,d1 and.l d6,d1 eor.l d1,d2 add.l d1,d1 eor.l d1,d3 move.l (a0)+,d0 move.l d2,a4 move.l d3,a5 cmpa.l a0,a2 bne .x2 move.l d7,BPLSIZE(a1) c2p1x1_8_c5_030_smc11 EQU *-2 move.l a4,(a1)+ move.l a5,-BPLSIZE-4(a1) c2p1x1_8_c5_030_smc12 EQU *-2 .none movem.l (sp)+,d2-d7/a2-a6 rts section bss,bss c2p1x1_8_c5_030_scroffs ds.l 1 c2p1x1_8_c5_030_pixels ds.l 1 engine/h2shared/c2p1x1_8_c5_040.s000066400000000000000000000154411444734033100164360ustar00rootroot00000000000000; ; ; Date: 2000-04-11 Mikael Kalms (Scout/C-Lous & more) ; Email: mikael@kalms.org ; ; About: ; 1x1 8bpl cpu5 C2P for contigous bitplanes and no horizontal modulo ; ; This routine is intended for use on all 68040 and 68060 based systems. ; It is not designed to perform well on 68020-030. ; ; This routine is released into the public domain. It may be freely used ; for non-commercial as well as commercial purposes. A short notice via ; email is always appreciated, though. ; ; Timings: ; Estimated to run at copyspeed on 040-40 and 060 ; ; Features: ; Handles bitplanes of virtually any size (4GB) ; ; Restrictions: ; Chunky-buffer must be an even multiple of 32 pixels wide ; If incorrect/invalid parameters are specified, the routine will ; most probably crash. ; ; c2p1x1_8_c5_040_init sets chunkybuffer size/pos & bplsize ; c2p1x1_8_c5_040 performs the actual c2p conversion ; XDEF _c2p1x1_8_c5_040_init XDEF _c2p1x1_8_c5_040 section code,code ; d0.w chunkyx [chunky-pixels] ; d1.w chunkyy [chunky-pixels] ; d2.w (scroffsx) [screen-pixels] ; d3.w scroffsy [screen-pixels] ; d4.l (rowlen) [bytes] -- offset between one row and the next in a bpl ; d5.l bplsize [bytes] -- offset between one row in one bpl and the next bpl ; d6.l (chunkylen) [bytes] -- offset between one row and the next in chunkybuf _c2p1x1_8_c5_040_init c2p1x1_8_c5_040_init move.l d3,-(sp) mulu.w d0,d3 lsr.l #3,d3 move.l d3,c2p1x1_8_c5_040_scroffs mulu.w d0,d1 move.l d1,c2p1x1_8_c5_040_pixels move.l d5,d0 lsl.l #3,d0 sub.l d5,d0 move.l d0,c2p1x1_8_c5_040_delta0 addq.l #4,d0 move.l d0,c2p1x1_8_c5_040_delta4 move.l d5,d0 lsl.l #2,d0 move.l d0,c2p1x1_8_c5_040_delta1 move.l d0,c2p1x1_8_c5_040_delta3 move.l d0,c2p1x1_8_c5_040_delta5 move.l d0,c2p1x1_8_c5_040_delta7 sub.l d5,d0 move.l d0,c2p1x1_8_c5_040_delta2 move.l d0,c2p1x1_8_c5_040_delta6 move.l d0,c2p1x1_8_c5_040_delta8 move.l (sp)+,d3 rts ; a0 c2pscreen ; a1 bitplanes _c2p1x1_8_c5_040 c2p1x1_8_c5_040 movem.l d2-d7/a2-a6,-(sp) add.l c2p1x1_8_c5_040_delta0(pc),a1 add.l c2p1x1_8_c5_040_scroffs(pc),a1 move.l c2p1x1_8_c5_040_pixels(pc),d0 beq .none add.l a0,d0 move.l d0,-(sp) tst.b 16(a0) move.l (a0)+,d0 move.l (a0)+,d1 move.l (a0)+,d2 move.l (a0)+,d3 tst.b 16(a0) move.l (a0)+,d4 move.l (a0)+,d5 move.l (a0)+,a5 move.l (a0)+,a6 swap d4 ; Swap 16x4, part 1 swap d5 eor.w d0,d4 eor.w d1,d5 eor.w d4,d0 eor.w d5,d1 eor.w d0,d4 eor.w d1,d5 swap d4 swap d5 move.l d4,d6 ; Swap 2x4, part 1 move.l d5,d7 lsr.l #2,d6 lsr.l #2,d7 eor.l d0,d6 eor.l d1,d7 and.l #$33333333,d6 and.l #$33333333,d7 eor.l d6,d0 eor.l d7,d1 lsl.l #2,d6 lsl.l #2,d7 eor.l d6,d4 eor.l d7,d5 exg d4,a5 exg d5,a6 swap d4 ; Swap 16x4, part 2 swap d5 eor.w d2,d4 eor.w d3,d5 eor.w d4,d2 eor.w d5,d3 eor.w d2,d4 eor.w d3,d5 swap d4 swap d5 move.l d4,d6 ; Swap 2x4, part 1 move.l d5,d7 lsr.l #2,d6 lsr.l #2,d7 eor.l d2,d6 eor.l d3,d7 and.l #$33333333,d6 and.l #$33333333,d7 eor.l d6,d2 eor.l d7,d3 lsl.l #2,d6 lsl.l #2,d7 eor.l d6,d4 eor.l d7,d5 move.l d1,d6 ; Swap 4x1, part 1 move.l d3,d7 lsr.l #4,d6 lsr.l #4,d7 eor.l d0,d6 eor.l d2,d7 and.l #$0f0f0f0f,d6 and.l #$0f0f0f0f,d7 eor.l d6,d0 eor.l d7,d2 lsl.l #4,d6 lsl.l #4,d7 eor.l d6,d1 eor.l d7,d3 move.l d2,d6 ; Swap 8x2, part 1 move.l d3,d7 lsr.l #8,d6 lsr.l #8,d7 eor.l d0,d6 eor.l d1,d7 and.l #$00ff00ff,d6 and.l #$00ff00ff,d7 eor.l d6,d0 eor.l d7,d1 lsl.l #8,d6 lsl.l #8,d7 eor.l d6,d2 eor.l d7,d3 bra .start cnop 0,4 .x tst.b 32(a0) move.l (a0)+,d0 move.l (a0)+,d1 move.l (a0)+,d2 move.l (a0)+,d3 tst.b 32(a0) move.l (a0)+,d4 move.l (a0)+,d5 move.l (a0)+,a5 move.l (a0)+,a6 move.l d6,(a1) swap d4 ; Swap 16x4, part 1 swap d5 eor.w d0,d4 eor.w d1,d5 eor.w d4,d0 eor.w d5,d1 eor.w d0,d4 sub.l c2p1x1_8_c5_040_delta1(pc),a1 eor.w d1,d5 swap d4 swap d5 move.l d4,d6 ; Swap 2x4, part 1 move.l d7,(a1) move.l d5,d7 lsr.l #2,d6 lsr.l #2,d7 eor.l d0,d6 eor.l d1,d7 and.l #$33333333,d6 and.l #$33333333,d7 eor.l d6,d0 eor.l d7,d1 lsl.l #2,d6 lsl.l #2,d7 eor.l d6,d4 eor.l d7,d5 exg d4,a5 add.l c2p1x1_8_c5_040_delta2(pc),a1 exg d5,a6 swap d4 ; Swap 16x4, part 2 swap d5 eor.w d2,d4 eor.w d3,d5 eor.w d4,d2 eor.w d5,d3 eor.w d2,d4 eor.w d3,d5 swap d4 swap d5 move.l a3,(a1) move.l d4,d6 ; Swap 2x4, part 2 move.l d5,d7 lsr.l #2,d6 lsr.l #2,d7 eor.l d2,d6 eor.l d3,d7 and.l #$33333333,d6 and.l #$33333333,d7 eor.l d6,d2 eor.l d7,d3 lsl.l #2,d6 lsl.l #2,d7 eor.l d6,d4 eor.l d7,d5 move.l d1,d6 ; Swap 4x1, part 1 move.l d3,d7 lsr.l #4,d6 lsr.l #4,d7 eor.l d0,d6 eor.l d2,d7 and.l #$0f0f0f0f,d6 and.l #$0f0f0f0f,d7 sub.l c2p1x1_8_c5_040_delta3(pc),a1 eor.l d6,d0 eor.l d7,d2 lsl.l #4,d6 lsl.l #4,d7 eor.l d6,d1 move.l a4,(a1) eor.l d7,d3 move.l d2,d6 ; Swap 8x2, part 1 move.l d3,d7 lsr.l #8,d6 lsr.l #8,d7 eor.l d0,d6 eor.l d1,d7 and.l #$00ff00ff,d6 and.l #$00ff00ff,d7 eor.l d6,d0 eor.l d7,d1 lsl.l #8,d6 lsl.l #8,d7 eor.l d6,d2 add.l c2p1x1_8_c5_040_delta4(pc),a1 eor.l d7,d3 .start move.l d2,d6 ; Swap 1x2, part 1 move.l d3,d7 lsr.l #1,d6 lsr.l #1,d7 eor.l d0,d6 eor.l d1,d7 and.l #$55555555,d6 and.l #$55555555,d7 eor.l d6,d0 eor.l d7,d1 move.l d0,(a1) add.l d6,d6 add.l d7,d7 eor.l d6,d2 eor.l d7,d3 move.l a5,d6 move.l a6,d7 move.l d2,a3 move.l d3,a4 move.l d5,d2 ; Swap 4x1, part 2 move.l d7,d3 lsr.l #4,d2 lsr.l #4,d3 sub.l c2p1x1_8_c5_040_delta5(pc),a1 eor.l d4,d2 eor.l d6,d3 and.l #$0f0f0f0f,d2 and.l #$0f0f0f0f,d3 eor.l d2,d4 move.l d1,(a1) eor.l d3,d6 lsl.l #4,d2 lsl.l #4,d3 eor.l d2,d5 eor.l d3,d7 move.l d4,d2 ; Swap 8x2, part 2 move.l d5,d3 lsr.l #8,d2 lsr.l #8,d3 add.l c2p1x1_8_c5_040_delta6(pc),a1 eor.l d6,d2 eor.l d7,d3 and.l #$00ff00ff,d2 and.l #$00ff00ff,d3 eor.l d2,d6 move.l a3,(a1) eor.l d3,d7 lsl.l #8,d2 lsl.l #8,d3 eor.l d2,d4 eor.l d3,d5 move.l d4,d2 ; Swap 1x2, part 2 move.l d5,d3 sub.l c2p1x1_8_c5_040_delta7(pc),a1 lsr.l #1,d2 lsr.l #1,d3 eor.l d6,d2 eor.l d7,d3 and.l #$55555555,d2 move.l a4,(a1) and.l #$55555555,d3 eor.l d2,d6 eor.l d3,d7 add.l d2,d2 add.l d3,d3 eor.l d2,d4 eor.l d3,d5 add.l c2p1x1_8_c5_040_delta8(pc),a1 move.l d4,a3 move.l d5,a4 cmp.l (sp),a0 bne .x move.l d6,(a1) sub.l c2p1x1_8_c5_040_delta1(pc),a1 move.l d7,(a1) add.l c2p1x1_8_c5_040_delta2(pc),a1 move.l a3,(a1) sub.l c2p1x1_8_c5_040_delta3(pc),a1 move.l a4,(a1) addq.l #4,sp .none movem.l (sp)+,d2-d7/a2-a6 rts cnop 0,4 c2p1x1_8_c5_040_data c2p1x1_8_c5_040_scroffs ds.l 1 c2p1x1_8_c5_040_pixels ds.l 1 c2p1x1_8_c5_040_delta0 ds.l 1 c2p1x1_8_c5_040_delta1 ds.l 1 c2p1x1_8_c5_040_delta2 ds.l 1 c2p1x1_8_c5_040_delta3 ds.l 1 c2p1x1_8_c5_040_delta4 ds.l 1 c2p1x1_8_c5_040_delta5 ds.l 1 c2p1x1_8_c5_040_delta6 ds.l 1 c2p1x1_8_c5_040_delta7 ds.l 1 c2p1x1_8_c5_040_delta8 ds.l 1 engine/h2shared/c2p1x1_8_c5_bm.s000066400000000000000000000306011444734033100165240ustar00rootroot00000000000000 ; ; Date: 1999-03-07 Mikael Kalms (Scout/C-Lous & more) ; Email: mikael@kalms.org ; ; 1x1 8bpl cpu5 C2P for arbitrary BitMaps ; ; Features: ; Performs CPU-only C2P conversion using rather state-of-the-art (as of ; the creation date, anyway) techniques ; Different routines for non-modulo and modulo C2P conversions ; Handles bitmaps of virtually any size (>4096x4096) ; Position-independent (PC-relative) code ; ; Restrictions: ; Chunky-buffer must be an even multiple of 32 pixels wide ; X-Offset must be set to an even multiple of 8 ; If these conditions not are met, the routine will abort. ; If incorrect/invalid parameters are specified, the routine will ; most probably crash. ; ; c2p1x1_8_c5_bm xdef _c2p1x1_8_c5_bm xdef c2p1x1_8_c5_bm incdir include: include graphics/gfx.i rsreset C2P1X1_8_C5_BM_CHUNKYX rs.w 1 C2P1X1_8_C5_BM_CHUNKYY rs.w 1 C2P1X1_8_C5_BM_ROWMOD rs.l 1 C2P1X1_8_C5_BM_SIZEOF rs.b 0 section code,code ; d0.w chunkyx [chunky-pixels] ; d1.w chunkyy [chunky-pixels] ; d2.w offsx [screen-pixels] ; d3.w offsy [screen-pixels] ; a0 chunkyscreen ; a1 BitMap _c2p1x1_8_c5_bm c2p1x1_8_c5_bm movem.l d2-d7/a2-a6,-(sp) subq.l #C2P1X1_8_C5_BM_SIZEOF,sp ; A few sanity checks cmpi.b #8,bm_Depth(a1) ; At least 8 valid bplptrs? blo .exit move.w d0,d4 move.w d2,d5 andi.w #$1f,d4 ; Even 32-pixel width? bne .exit andi.w #$7,d5 ; Even 8-pixel xoffset? bne .exit moveq #0,d4 move.w bm_BytesPerRow(a1),d4 move.w d0,C2P1X1_8_C5_BM_CHUNKYX(sp) ; Skip if 0 pixels to convert beq .exit move.w d1,C2P1X1_8_C5_BM_CHUNKYY(sp) beq .exit ext.l d2 ; Offs to first pixel to draw in bpl mulu.w d4,d3 lsr.l #3,d2 add.l d2,d3 lsl.w #3,d4 ; Modulo c2p required? sub.w d0,d4 bmi .exit bne .c2p_mod mulu.w d0,d1 add.l a0,d1 move.l d1,a2 ; Ptr to end of chunkybuffer movem.l a0-a1/d3,-(sp) movem.l bm_Planes(a1),a3-a6 ; Setup ptrs to bpl0-3 add.l d3,a3 add.l d3,a4 add.l d3,a5 add.l d3,a6 move.l (a0)+,d0 ; Convert lower 4 bpls move.l (a0)+,d2 move.l (a0)+,d1 move.l (a0)+,d3 move.l #$0f0f0f0f,d6 ; Merge 4x1, part 1 and.l d6,d0 and.l d6,d1 and.l d6,d2 and.l d6,d3 lsl.l #4,d0 lsl.l #4,d1 or.l d2,d0 or.l d3,d1 move.l (a0)+,d2 move.l (a0)+,d6 move.l (a0)+,d3 move.l (a0)+,d7 move.l #$0f0f0f0f,d4 ; Merge 4x1, part 2 and.l d4,d2 and.l d4,d6 and.l d4,d3 and.l d4,d7 lsl.l #4,d2 lsl.l #4,d3 or.l d6,d2 or.l d7,d3 move.w d2,d6 ; Swap 16x2 move.w d3,d7 move.w d0,d2 move.w d1,d3 swap d2 swap d3 move.w d2,d0 move.w d3,d1 move.w d6,d2 move.w d7,d3 move.l #$33333333,d4 move.l d2,d6 ; Swap 2x2 move.l d3,d7 lsr.l #2,d6 lsr.l #2,d7 eor.l d0,d6 eor.l d1,d7 and.l d4,d6 and.l d4,d7 eor.l d6,d0 eor.l d7,d1 lsl.l #2,d6 lsl.l #2,d7 eor.l d6,d2 eor.l d7,d3 move.l #$00ff00ff,d4 move.l d1,d6 ; Swap 8x1 move.l d3,d7 lsr.l #8,d6 lsr.l #8,d7 eor.l d0,d6 eor.l d2,d7 bra .x1start .x1 move.l (a0)+,d0 move.l (a0)+,d2 move.l (a0)+,d1 move.l (a0)+,d3 move.l d7,(a3)+ move.l #$0f0f0f0f,d6 ; Merge 4x1, part 1 and.l d6,d0 and.l d6,d1 and.l d6,d2 and.l d6,d3 lsl.l #4,d0 lsl.l #4,d1 or.l d2,d0 or.l d3,d1 move.l (a0)+,d2 move.l (a0)+,d6 move.l (a0)+,d3 move.l (a0)+,d7 move.l d4,(a4)+ move.l #$0f0f0f0f,d4 ; Merge 4x1, part 2 and.l d4,d2 and.l d4,d6 and.l d4,d3 and.l d4,d7 lsl.l #4,d2 lsl.l #4,d3 or.l d6,d2 or.l d7,d3 move.w d2,d6 ; Swap 16x2 move.w d3,d7 move.w d0,d2 move.w d1,d3 swap d2 swap d3 move.w d2,d0 move.w d3,d1 move.w d6,d2 move.w d7,d3 move.l d5,(a5)+ move.l #$33333333,d4 move.l d2,d6 ; Swap 2x2 move.l d3,d7 lsr.l #2,d6 lsr.l #2,d7 eor.l d0,d6 eor.l d1,d7 and.l d4,d6 and.l d4,d7 eor.l d6,d0 eor.l d7,d1 lsl.l #2,d6 lsl.l #2,d7 eor.l d6,d2 eor.l d7,d3 move.l #$00ff00ff,d4 move.l d1,d6 ; Swap 8x1 move.l d3,d7 lsr.l #8,d6 lsr.l #8,d7 eor.l d0,d6 eor.l d2,d7 move.l a1,(a6)+ .x1start and.l d4,d6 and.l d4,d7 eor.l d6,d0 eor.l d7,d2 lsl.l #8,d6 lsl.l #8,d7 eor.l d6,d1 eor.l d7,d3 move.l #$55555555,d4 move.l d1,d5 ; Swap 1x1 move.l d3,d7 lsr.l #1,d5 lsr.l #1,d7 eor.l d0,d5 eor.l d2,d7 and.l d4,d5 and.l d4,d7 eor.l d5,d0 eor.l d7,d2 add.l d5,d5 add.l d7,d7 eor.l d1,d5 eor.l d3,d7 move.l d0,a1 move.l d2,d4 cmpa.l a0,a2 bne .x1 move.l d7,(a3)+ move.l d4,(a4)+ move.l d5,(a5)+ move.l a1,(a6)+ movem.l (sp)+,a0-a1/d3 movem.l bm_Planes+4*4(a1),a3-a6 ; Setup ptrs to bpl4-7 add.l d3,a3 add.l d3,a4 add.l d3,a5 add.l d3,a6 move.l (a0)+,d0 ; Convert upper 4 bpls move.l (a0)+,d2 move.l (a0)+,d1 move.l (a0)+,d3 move.l #$f0f0f0f0,d6 ; Merge 4x1, part 1 and.l d6,d0 and.l d6,d1 and.l d6,d2 and.l d6,d3 lsr.l #4,d2 lsr.l #4,d3 or.l d2,d0 or.l d3,d1 move.l (a0)+,d2 move.l (a0)+,d6 move.l (a0)+,d3 move.l (a0)+,d7 move.l #$f0f0f0f0,d4 ; Merge 4x1, part 2 and.l d4,d2 and.l d4,d6 and.l d4,d3 and.l d4,d7 lsr.l #4,d6 lsr.l #4,d7 or.l d6,d2 or.l d7,d3 move.w d2,d6 ; Swap 16x2 move.w d3,d7 move.w d0,d2 move.w d1,d3 swap d2 swap d3 move.w d2,d0 move.w d3,d1 move.w d6,d2 move.w d7,d3 move.l #$33333333,d4 move.l d2,d6 ; Swap 2x2 move.l d3,d7 lsr.l #2,d6 lsr.l #2,d7 eor.l d0,d6 eor.l d1,d7 and.l d4,d6 and.l d4,d7 eor.l d6,d0 eor.l d7,d1 lsl.l #2,d6 lsl.l #2,d7 eor.l d6,d2 eor.l d7,d3 move.l #$00ff00ff,d4 move.l d1,d6 ; Swap 8x1 move.l d3,d7 lsr.l #8,d6 lsr.l #8,d7 eor.l d0,d6 eor.l d2,d7 bra .x2start .x2 move.l (a0)+,d0 move.l (a0)+,d2 move.l (a0)+,d1 move.l (a0)+,d3 move.l d7,(a3)+ move.l #$f0f0f0f0,d6 ; Merge 4x1, part 1 and.l d6,d0 and.l d6,d1 and.l d6,d2 and.l d6,d3 lsr.l #4,d2 lsr.l #4,d3 or.l d2,d0 or.l d3,d1 move.l (a0)+,d2 move.l (a0)+,d6 move.l (a0)+,d3 move.l (a0)+,d7 move.l d4,(a4)+ move.l #$f0f0f0f0,d4 ; Merge 4x1, part 2 and.l d4,d2 and.l d4,d6 and.l d4,d3 and.l d4,d7 lsr.l #4,d6 lsr.l #4,d7 or.l d6,d2 or.l d7,d3 move.w d2,d6 ; Swap 16x2 move.w d3,d7 move.w d0,d2 move.w d1,d3 swap d2 swap d3 move.w d2,d0 move.w d3,d1 move.w d6,d2 move.w d7,d3 move.l d5,(a5)+ move.l #$33333333,d4 move.l d2,d6 ; Swap 2x2 move.l d3,d7 lsr.l #2,d6 lsr.l #2,d7 eor.l d0,d6 eor.l d1,d7 and.l d4,d6 and.l d4,d7 eor.l d6,d0 eor.l d7,d1 lsl.l #2,d6 lsl.l #2,d7 eor.l d6,d2 eor.l d7,d3 move.l #$00ff00ff,d4 move.l d1,d6 ; Swap 8x1 move.l d3,d7 lsr.l #8,d6 lsr.l #8,d7 eor.l d0,d6 eor.l d2,d7 move.l a1,(a6)+ .x2start and.l d4,d6 and.l d4,d7 eor.l d6,d0 eor.l d7,d2 lsl.l #8,d6 lsl.l #8,d7 eor.l d6,d1 eor.l d7,d3 move.l #$55555555,d4 move.l d1,d5 ; Swap 1x1 move.l d3,d7 lsr.l #1,d5 lsr.l #1,d7 eor.l d0,d5 eor.l d2,d7 and.l d4,d5 and.l d4,d7 eor.l d5,d0 eor.l d7,d2 add.l d5,d5 add.l d7,d7 eor.l d1,d5 eor.l d3,d7 move.l d0,a1 move.l d2,d4 cmpa.l a0,a2 bne .x2 move.l d7,(a3)+ move.l d4,(a4)+ move.l d5,(a5)+ move.l a1,(a6)+ .exit addq.l #C2P1X1_8_C5_BM_SIZEOF,sp movem.l (sp)+,d2-d7/a2-a6 .earlyexit rts .c2p_mod lsr.w #3,d4 move.l d4,C2P1X1_8_C5_BM_ROWMOD(sp) ; Modulo between two rows move.l a0,a2 ; Ptr to end of line + 1 iter add.w C2P1X1_8_C5_BM_CHUNKYX(sp),a2 add.w #32,a2 movem.l a0-a2/d1/d3,-(sp) movem.l bm_Planes(a1),a3-a6 ; Setup ptrs to bpl0-3 add.l d3,a3 add.l d3,a4 add.l d3,a5 add.l d3,a6 move.l (a0)+,d0 ; Convert lower 4 bpls move.l (a0)+,d2 move.l (a0)+,d1 move.l (a0)+,d3 move.l #$0f0f0f0f,d6 ; Merge 4x1, part 1 and.l d6,d0 and.l d6,d1 and.l d6,d2 and.l d6,d3 lsl.l #4,d0 lsl.l #4,d1 or.l d2,d0 or.l d3,d1 move.l (a0)+,d2 move.l (a0)+,d6 move.l (a0)+,d3 move.l (a0)+,d7 move.l #$0f0f0f0f,d4 ; Merge 4x1, part 2 and.l d4,d2 and.l d4,d6 and.l d4,d3 and.l d4,d7 lsl.l #4,d2 lsl.l #4,d3 or.l d6,d2 or.l d7,d3 move.w d2,d6 ; Swap 16x2 move.w d3,d7 move.w d0,d2 move.w d1,d3 swap d2 swap d3 move.w d2,d0 move.w d3,d1 move.w d6,d2 move.w d7,d3 move.l #$33333333,d4 move.l d2,d6 ; Swap 2x2 move.l d3,d7 lsr.l #2,d6 lsr.l #2,d7 eor.l d0,d6 eor.l d1,d7 and.l d4,d6 and.l d4,d7 eor.l d6,d0 eor.l d7,d1 lsl.l #2,d6 lsl.l #2,d7 eor.l d6,d2 eor.l d7,d3 move.l #$00ff00ff,d4 move.l d1,d6 ; Swap 8x1 move.l d3,d7 lsr.l #8,d6 lsr.l #8,d7 eor.l d0,d6 eor.l d2,d7 bra .modx1start .modx1y add.w C2P1X1_8_C5_BM_CHUNKYX+20(sp),a2 ; Skip to end of next ; line + 1 iter move.l C2P1X1_8_C5_BM_ROWMOD+20(sp),d0 ; Skip to beginning of add.l d0,a3 ; next line add.l d0,a4 add.l d0,a5 add.l d0,a6 .modx1 move.l (a0)+,d0 move.l (a0)+,d2 move.l (a0)+,d1 move.l (a0)+,d3 move.l d7,(a3)+ move.l #$0f0f0f0f,d6 ; Merge 4x1, part 1 and.l d6,d0 and.l d6,d1 and.l d6,d2 and.l d6,d3 lsl.l #4,d0 lsl.l #4,d1 or.l d2,d0 or.l d3,d1 move.l (a0)+,d2 move.l (a0)+,d6 move.l (a0)+,d3 move.l (a0)+,d7 move.l d4,(a4)+ move.l #$0f0f0f0f,d4 ; Merge 4x1, part 2 and.l d4,d2 and.l d4,d6 and.l d4,d3 and.l d4,d7 lsl.l #4,d2 lsl.l #4,d3 or.l d6,d2 or.l d7,d3 move.w d2,d6 ; Swap 16x2 move.w d3,d7 move.w d0,d2 move.w d1,d3 swap d2 swap d3 move.w d2,d0 move.w d3,d1 move.w d6,d2 move.w d7,d3 move.l d5,(a5)+ move.l #$33333333,d4 move.l d2,d6 ; Swap 2x2 move.l d3,d7 lsr.l #2,d6 lsr.l #2,d7 eor.l d0,d6 eor.l d1,d7 and.l d4,d6 and.l d4,d7 eor.l d6,d0 eor.l d7,d1 lsl.l #2,d6 lsl.l #2,d7 eor.l d6,d2 eor.l d7,d3 move.l #$00ff00ff,d4 move.l d1,d6 ; Swap 8x1 move.l d3,d7 lsr.l #8,d6 lsr.l #8,d7 eor.l d0,d6 eor.l d2,d7 move.l a1,(a6)+ .modx1start and.l d4,d6 and.l d4,d7 eor.l d6,d0 eor.l d7,d2 lsl.l #8,d6 lsl.l #8,d7 eor.l d6,d1 eor.l d7,d3 move.l #$55555555,d4 move.l d1,d5 ; Swap 1x1 move.l d3,d7 lsr.l #1,d5 lsr.l #1,d7 eor.l d0,d5 eor.l d2,d7 and.l d4,d5 and.l d4,d7 eor.l d5,d0 eor.l d7,d2 add.l d5,d5 add.l d7,d7 eor.l d1,d5 eor.l d3,d7 move.l d0,a1 move.l d2,d4 cmpa.l a0,a2 bne .modx1 subq.w #1,C2P1X1_8_C5_BM_CHUNKYY+20(sp) bne .modx1y movem.l (sp)+,a0-a2/d1/d3 move.w d1,C2P1X1_8_C5_BM_CHUNKYY(sp) movem.l bm_Planes+4*4(a1),a3-a6 ; Setup ptrs to bpl4-7 add.l d3,a3 add.l d3,a4 add.l d3,a5 add.l d3,a6 move.l (a0)+,d0 ; Convert lower 4 bpls move.l (a0)+,d2 move.l (a0)+,d1 move.l (a0)+,d3 move.l #$f0f0f0f0,d6 ; Merge 4x1, part 1 and.l d6,d0 and.l d6,d1 and.l d6,d2 and.l d6,d3 lsr.l #4,d2 lsr.l #4,d3 or.l d2,d0 or.l d3,d1 move.l (a0)+,d2 move.l (a0)+,d6 move.l (a0)+,d3 move.l (a0)+,d7 move.l #$f0f0f0f0,d4 ; Merge 4x1, part 2 and.l d4,d2 and.l d4,d6 and.l d4,d3 and.l d4,d7 lsr.l #4,d6 lsr.l #4,d7 or.l d6,d2 or.l d7,d3 move.w d2,d6 ; Swap 16x2 move.w d3,d7 move.w d0,d2 move.w d1,d3 swap d2 swap d3 move.w d2,d0 move.w d3,d1 move.w d6,d2 move.w d7,d3 move.l #$33333333,d4 move.l d2,d6 ; Swap 2x2 move.l d3,d7 lsr.l #2,d6 lsr.l #2,d7 eor.l d0,d6 eor.l d1,d7 and.l d4,d6 and.l d4,d7 eor.l d6,d0 eor.l d7,d1 lsl.l #2,d6 lsl.l #2,d7 eor.l d6,d2 eor.l d7,d3 move.l #$00ff00ff,d4 move.l d1,d6 ; Swap 8x1 move.l d3,d7 lsr.l #8,d6 lsr.l #8,d7 eor.l d0,d6 eor.l d2,d7 bra .modx2start .modx2y add.w C2P1X1_8_C5_BM_CHUNKYX(sp),a2 ; Skip to end of next line + 1 ; iter move.l C2P1X1_8_C5_BM_ROWMOD(sp),d0 ; Skip to beginning of add.l d0,a3 ; next line add.l d0,a4 add.l d0,a5 add.l d0,a6 .modx2 move.l (a0)+,d0 move.l (a0)+,d2 move.l (a0)+,d1 move.l (a0)+,d3 move.l d7,(a3)+ move.l #$f0f0f0f0,d6 ; Merge 4x1, part 1 and.l d6,d0 and.l d6,d1 and.l d6,d2 and.l d6,d3 lsr.l #4,d2 lsr.l #4,d3 or.l d2,d0 or.l d3,d1 move.l (a0)+,d2 move.l (a0)+,d6 move.l (a0)+,d3 move.l (a0)+,d7 move.l d4,(a4)+ move.l #$f0f0f0f0,d4 ; Merge 4x1, part 2 and.l d4,d2 and.l d4,d6 and.l d4,d3 and.l d4,d7 lsr.l #4,d6 lsr.l #4,d7 or.l d6,d2 or.l d7,d3 move.w d2,d6 ; Swap 16x2 move.w d3,d7 move.w d0,d2 move.w d1,d3 swap d2 swap d3 move.w d2,d0 move.w d3,d1 move.w d6,d2 move.w d7,d3 move.l d5,(a5)+ move.l #$33333333,d4 move.l d2,d6 ; Swap 2x2 move.l d3,d7 lsr.l #2,d6 lsr.l #2,d7 eor.l d0,d6 eor.l d1,d7 and.l d4,d6 and.l d4,d7 eor.l d6,d0 eor.l d7,d1 lsl.l #2,d6 lsl.l #2,d7 eor.l d6,d2 eor.l d7,d3 move.l #$00ff00ff,d4 move.l d1,d6 ; Swap 8x1 move.l d3,d7 lsr.l #8,d6 lsr.l #8,d7 eor.l d0,d6 eor.l d2,d7 move.l a1,(a6)+ .modx2start and.l d4,d6 and.l d4,d7 eor.l d6,d0 eor.l d7,d2 lsl.l #8,d6 lsl.l #8,d7 eor.l d6,d1 eor.l d7,d3 move.l #$55555555,d4 move.l d1,d5 ; Swap 1x1 move.l d3,d7 lsr.l #1,d5 lsr.l #1,d7 eor.l d0,d5 eor.l d2,d7 and.l d4,d5 and.l d4,d7 eor.l d5,d0 eor.l d7,d2 add.l d5,d5 add.l d7,d7 eor.l d1,d5 eor.l d3,d7 move.l d0,a1 move.l d2,d4 cmpa.l a0,a2 bne .modx2 subq.w #1,C2P1X1_8_C5_BM_CHUNKYY(sp) bne .modx2y bra .exit engine/h2shared/c2p1x1_8_c5_bm_040.s000066400000000000000000000170551444734033100171170ustar00rootroot00000000000000; ; File: c2p1x1_8_c5_bm_040.s ; Author: Mikael Kalms ; Date: 17 April 2000 ; Title: C2P - 1x1, 8bpl, BitMap output, 040+ optimized ; ; Description: ; Performs CPU-only C2P conversion ; Outputs to any BitMap (interlaced, large, ...) ; Position-independent (PC-relative) code ; 68040+ optimized -- performs badly on 020/030 ; No selfmodifying code used (relies on datacache instead) ; For best performance, align chunkybuffer and destination window ; on even 4byte boundary ; ; Restrictions: ; Chunky-buffer must be an even multiple of 32 pixels wide ; X-Offset must be set to an even multiple of 8 ; xdef _c2p1x1_8_c5_bm_040 xdef c2p1x1_8_c5_bm_040 incdir include: include graphics/gfx.i rsreset C2P1X1_8_C5_BM_040_DELTA1 rs.l 1 C2P1X1_8_C5_BM_040_DELTA2 rs.l 1 C2P1X1_8_C5_BM_040_DELTA3 rs.l 1 C2P1X1_8_C5_BM_040_DELTA4 rs.l 1 C2P1X1_8_C5_BM_040_DELTA5 rs.l 1 C2P1X1_8_C5_BM_040_DELTA6 rs.l 1 C2P1X1_8_C5_BM_040_DELTA7 rs.l 1 C2P1X1_8_C5_BM_040_DELTA8 rs.l 1 C2P1X1_8_C5_BM_040_CHUNKYX rs.w 1 C2P1X1_8_C5_BM_040_CHUNKYY rs.w 1 C2P1X1_8_C5_BM_040_ROWMOD rs.l 1 C2P1X1_8_C5_BM_040_SIZEOF rs.b 0 section code,code ; d0.w chunkyx [chunky-pixels] ; d1.w chunkyy [chunky-pixels] ; d2.w offsx [screen-pixels] ; d3.w offsy [screen-pixels] ; a0 chunkyscreen ; a1 BitMap _c2p1x1_8_c5_bm_040 c2p1x1_8_c5_bm_040 movem.l d2-d7/a2-a6,-(sp) sub.l #C2P1X1_8_C5_BM_040_SIZEOF,sp ; A few sanity checks cmpi.b #8,bm_Depth(a1) ; At least 8 valid bplptrs? blo .exit move.w d0,d4 move.w d2,d5 andi.w #$1f,d4 ; Even 32-pixel width? bne .exit andi.w #$7,d5 ; Even 8-pixel xoffset? bne .exit moveq #0,d4 move.w bm_BytesPerRow(a1),d4 move.w d0,C2P1X1_8_C5_BM_040_CHUNKYX(sp) ; Skip if 0 pixels to convert beq .exit move.w d1,C2P1X1_8_C5_BM_040_CHUNKYY(sp) beq .exit and.l #$ffff,d0 and.l #$ffff,d2 mulu.w d4,d3 ; Offs to first pixel to draw in bpl lsr.l #3,d2 add.l d2,d3 lsl.l #3,d4 ; Positive modulo? sub.l d0,d4 bmi .exit lsr.l #3,d4 move.l d4,C2P1X1_8_C5_BM_040_ROWMOD(sp) ; Modulo between two rows move.l a0,a2 ; Ptr to end of line add.w C2P1X1_8_C5_BM_040_CHUNKYX(sp),a2 move.l bm_Planes+1*4(a1),a3 sub.l bm_Planes+5*4(a1),a3 move.l a3,C2P1X1_8_C5_BM_040_DELTA1(sp) move.l bm_Planes+4*4(a1),a3 sub.l bm_Planes+1*4(a1),a3 move.l a3,C2P1X1_8_C5_BM_040_DELTA2(sp) move.l bm_Planes+0*4(a1),a3 sub.l bm_Planes+4*4(a1),a3 move.l a3,C2P1X1_8_C5_BM_040_DELTA3(sp) move.l bm_Planes+7*4(a1),a3 sub.l bm_Planes+0*4(a1),a3 addq.l #4,a3 move.l a3,C2P1X1_8_C5_BM_040_DELTA4(sp) move.l bm_Planes+3*4(a1),a3 sub.l bm_Planes+7*4(a1),a3 move.l a3,C2P1X1_8_C5_BM_040_DELTA5(sp) move.l bm_Planes+6*4(a1),a3 sub.l bm_Planes+3*4(a1),a3 move.l a3,C2P1X1_8_C5_BM_040_DELTA6(sp) move.l bm_Planes+2*4(a1),a3 sub.l bm_Planes+6*4(a1),a3 move.l a3,C2P1X1_8_C5_BM_040_DELTA7(sp) move.l bm_Planes+5*4(a1),a3 sub.l bm_Planes+2*4(a1),a3 move.l a3,C2P1X1_8_C5_BM_040_DELTA8(sp) move.l bm_Planes+7*4(a1),a1 add.l d3,a1 tst.b 32(a0) move.l (a0)+,d0 move.l (a0)+,d1 move.l (a0)+,d2 move.l (a0)+,d3 tst.b 32(a0) move.l (a0)+,d4 move.l (a0)+,d5 move.l (a0)+,a5 move.l (a0)+,a6 swap d4 ; Swap 16x4, part 1 swap d5 eor.w d0,d4 eor.w d1,d5 eor.w d4,d0 eor.w d5,d1 eor.w d0,d4 eor.w d1,d5 swap d4 swap d5 move.l d4,d6 ; Swap 2x4, part 1 move.l d5,d7 lsr.l #2,d6 lsr.l #2,d7 eor.l d0,d6 eor.l d1,d7 and.l #$33333333,d6 and.l #$33333333,d7 eor.l d6,d0 eor.l d7,d1 lsl.l #2,d6 lsl.l #2,d7 eor.l d6,d4 eor.l d7,d5 exg d4,a5 exg d5,a6 swap d4 ; Swap 16x4, part 2 swap d5 eor.w d2,d4 eor.w d3,d5 eor.w d4,d2 eor.w d5,d3 eor.w d2,d4 eor.w d3,d5 swap d4 swap d5 move.l d4,d6 ; Swap 2x4, part 2 move.l d5,d7 lsr.l #2,d6 lsr.l #2,d7 eor.l d2,d6 eor.l d3,d7 and.l #$33333333,d6 and.l #$33333333,d7 eor.l d6,d2 eor.l d7,d3 lsl.l #2,d6 lsl.l #2,d7 eor.l d6,d4 eor.l d7,d5 move.l d1,d6 ; Swap 4x1, part 1 move.l d3,d7 lsr.l #4,d6 lsr.l #4,d7 eor.l d0,d6 eor.l d2,d7 and.l #$0f0f0f0f,d6 and.l #$0f0f0f0f,d7 eor.l d6,d0 eor.l d7,d2 lsl.l #4,d6 lsl.l #4,d7 eor.l d6,d1 eor.l d7,d3 move.l d2,d6 ; Swap 8x2, part 1 move.l d3,d7 lsr.l #8,d6 lsr.l #8,d7 eor.l d0,d6 eor.l d1,d7 and.l #$00ff00ff,d6 and.l #$00ff00ff,d7 eor.l d6,d0 eor.l d7,d1 lsl.l #8,d6 lsl.l #8,d7 eor.l d6,d2 eor.l d7,d3 bra .xstart .y .x tst.b 32(a0) move.l (a0)+,d0 move.l (a0)+,d1 move.l (a0)+,d2 move.l (a0)+,d3 tst.b 32(a0) move.l (a0)+,d4 move.l (a0)+,d5 move.l (a0)+,a5 move.l (a0)+,a6 move.l d6,(a1) swap d4 ; Swap 16x4, part 1 swap d5 eor.w d0,d4 eor.w d1,d5 eor.w d4,d0 eor.w d5,d1 eor.w d0,d4 add.l C2P1X1_8_C5_BM_040_DELTA1(sp),a1 eor.w d1,d5 swap d4 swap d5 move.l d4,d6 ; Swap 2x4, part 1 move.l d7,(a1) move.l d5,d7 lsr.l #2,d6 lsr.l #2,d7 eor.l d0,d6 eor.l d1,d7 and.l #$33333333,d6 and.l #$33333333,d7 eor.l d6,d0 eor.l d7,d1 lsl.l #2,d6 lsl.l #2,d7 eor.l d6,d4 eor.l d7,d5 exg d4,a5 add.l C2P1X1_8_C5_BM_040_DELTA2(sp),a1 exg d5,a6 swap d4 ; Swap 16x4, part 2 swap d5 eor.w d2,d4 eor.w d3,d5 eor.w d4,d2 eor.w d5,d3 eor.w d2,d4 eor.w d3,d5 swap d4 swap d5 move.l a3,(a1) move.l d4,d6 ; Swap 2x4, part 2 move.l d5,d7 lsr.l #2,d6 lsr.l #2,d7 eor.l d2,d6 eor.l d3,d7 and.l #$33333333,d6 and.l #$33333333,d7 eor.l d6,d2 eor.l d7,d3 lsl.l #2,d6 lsl.l #2,d7 eor.l d6,d4 eor.l d7,d5 move.l d1,d6 ; Swap 4x1, part 1 move.l d3,d7 lsr.l #4,d6 lsr.l #4,d7 eor.l d0,d6 eor.l d2,d7 and.l #$0f0f0f0f,d6 and.l #$0f0f0f0f,d7 add.l C2P1X1_8_C5_BM_040_DELTA3(sp),a1 eor.l d6,d0 eor.l d7,d2 lsl.l #4,d6 lsl.l #4,d7 eor.l d6,d1 move.l a4,(a1) eor.l d7,d3 move.l d2,d6 ; Swap 8x2, part 1 move.l d3,d7 lsr.l #8,d6 lsr.l #8,d7 eor.l d0,d6 eor.l d1,d7 and.l #$00ff00ff,d6 and.l #$00ff00ff,d7 eor.l d6,d0 eor.l d7,d1 lsl.l #8,d6 lsl.l #8,d7 add.l C2P1X1_8_C5_BM_040_DELTA4(sp),a1 eor.l d6,d2 eor.l d7,d3 cmp.l a0,a2 ; End of line passed? bhs.s .xstart add.w C2P1X1_8_C5_BM_040_CHUNKYX(sp),a2 ; Skip to end of next ; line add.l C2P1X1_8_C5_BM_040_ROWMOD(sp),a1 ; Skip to beginning of ; next line .xstart move.l d2,d6 ; Swap 1x2, part 1 move.l d3,d7 lsr.l #1,d6 lsr.l #1,d7 eor.l d0,d6 eor.l d1,d7 and.l #$55555555,d6 and.l #$55555555,d7 eor.l d6,d0 eor.l d7,d1 move.l d0,(a1) add.l d6,d6 add.l d7,d7 eor.l d6,d2 eor.l d7,d3 move.l a5,d6 move.l a6,d7 move.l d2,a3 move.l d3,a4 move.l d5,d2 ; Swap 4x1, part 2 move.l d7,d3 lsr.l #4,d2 lsr.l #4,d3 add.l C2P1X1_8_C5_BM_040_DELTA5(sp),a1 eor.l d4,d2 eor.l d6,d3 and.l #$0f0f0f0f,d2 and.l #$0f0f0f0f,d3 eor.l d2,d4 move.l d1,(a1) eor.l d3,d6 lsl.l #4,d2 lsl.l #4,d3 eor.l d2,d5 eor.l d3,d7 move.l d4,d2 ; Swap 8x2, part 2 move.l d5,d3 lsr.l #8,d2 lsr.l #8,d3 add.l C2P1X1_8_C5_BM_040_DELTA6(sp),a1 eor.l d6,d2 eor.l d7,d3 and.l #$00ff00ff,d2 and.l #$00ff00ff,d3 eor.l d2,d6 move.l a3,(a1) eor.l d3,d7 lsl.l #8,d2 lsl.l #8,d3 eor.l d2,d4 eor.l d3,d5 move.l d4,d2 ; Swap 1x2, part 2 move.l d5,d3 add.l C2P1X1_8_C5_BM_040_DELTA7(sp),a1 lsr.l #1,d2 lsr.l #1,d3 eor.l d6,d2 eor.l d7,d3 and.l #$55555555,d2 move.l a4,(a1) and.l #$55555555,d3 eor.l d2,d6 eor.l d3,d7 add.l d2,d2 add.l d3,d3 add.l C2P1X1_8_C5_BM_040_DELTA8(sp),a1 eor.l d2,d4 eor.l d3,d5 move.l d4,a3 move.l d5,a4 cmp.l a0,a2 bne .x subq.w #1,C2P1X1_8_C5_BM_040_CHUNKYY(sp) bne .y move.l d6,(a1) add.l C2P1X1_8_C5_BM_040_DELTA1(sp),a1 move.l d7,(a1) add.l C2P1X1_8_C5_BM_040_DELTA2(sp),a1 move.l a3,(a1) add.l C2P1X1_8_C5_BM_040_DELTA3(sp),a1 move.l a4,(a1) .exit add.l #C2P1X1_8_C5_BM_040_SIZEOF,sp movem.l (sp)+,d2-d7/a2-a6 .earlyexit rts engine/h2shared/cd_amiga.c000066400000000000000000000236131444734033100157230ustar00rootroot00000000000000/* cd_amiga.c * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2021 Szilard Biro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "cdaudio.h" #include #include #include #include #include #include #include static qboolean cdValid = false; static qboolean playing = false; static qboolean wasPlaying = false; static qboolean initialized = false; static qboolean enabled = true; static qboolean playLooping = false; static byte remap[100]; static byte playTrack; static byte maxTrack; struct Library *CDPlayerBase = NULL; static struct IOStdReq *cdRequest = NULL; static struct MsgPort *cdPort = NULL; static BYTE cdDevice = -1; static struct CD_TOC cdTOC; static const char default_dev[] = "CD0:"; /* user can always do -cddev */ static const char *cd_dev = default_dev; static float old_cdvolume; static qboolean hw_vol_works = true; static struct CD_Volume orig_vol; /* original setting to be restored upon exit */ static struct CD_Volume drv_vol; /* the volume setting we'll be using */ static void CDAudio_Eject(void) { BYTE error; if (cdDevice == -1 || !enabled) return; error = CDEject(cdRequest); if (error) Con_DPrintf("CDEject failed (%d)\n", (int)error); } static int CDAudio_GetAudioDiskInfo(void) { BYTE error; if (cdDevice == -1) return -1; cdValid = false; error = CDReadTOC(&cdTOC, cdRequest); if (error) { Con_DPrintf("CDReadTOC failed (%d)\n", (int)error); return -1; } if (cdTOC.cdc_NumTracks < 1) { Con_DPrintf("CDAudio: no music tracks\n"); return -1; } cdValid = true; maxTrack = cdTOC.cdc_NumTracks; return 0; } int CDAudio_Play(byte track, qboolean looping) { BYTE error; if (cdDevice == -1 || !enabled) return -1; if (!cdValid) { CDAudio_GetAudioDiskInfo(); if (!cdValid) return -1; } track = remap[track]; if (track < 1 || track > maxTrack) { Con_DPrintf("CDAudio: Bad track number %u.\n", track); return -1; } /* don't try to play a non-audio track */ if (cdTOC.cdc_Flags[track]) { Con_Printf("CDAudio: track %i is not audio\n", track); return -1; } if (playing) { if (playTrack == track) return 0; CDAudio_Stop(); } error = CDPlay(track, track, cdRequest); if (error) { Con_DPrintf("CDPlay failed (%d)\n", (int)error); return -1; } playLooping = looping; playTrack = track; playing = true; if (bgmvolume.value == 0) /* don't bother advancing */ CDAudio_Pause (); return 0; } void CDAudio_Stop(void) { BYTE error; if (cdDevice == -1 || !enabled) return; if (!playing) return; error = CDStop(cdRequest); if (error) Con_DPrintf("CDStop failed (%d)\n", (int)error); wasPlaying = false; playing = false; } void CDAudio_Pause(void) { BYTE error; if (cdDevice == -1 || !enabled) return; if (!playing) return; error = CDResume(TRUE, cdRequest); if (error) Con_DPrintf("CDResume failed (%d)\n", (int)error); wasPlaying = playing; playing = false; } void CDAudio_Resume(void) { BYTE error; if (cdDevice == -1 || !enabled) return; if (!cdValid) return; if (!wasPlaying) return; error = CDResume(FALSE, cdRequest); if (error) Con_DPrintf("CDResume failed (%d)\n", (int)error); playing = true; } static void CD_f (void) { const char *command; int ret, n; if (Cmd_Argc() < 2) { Con_Printf("commands:"); Con_Printf("on, off, reset, remap, \n"); Con_Printf("play, stop, loop, pause, resume\n"); Con_Printf("eject, close, info\n"); return; } command = Cmd_Argv (1); if (q_strcasecmp(command, "on") == 0) { enabled = true; return; } if (q_strcasecmp(command, "off") == 0) { if (playing) CDAudio_Stop(); enabled = false; return; } if (q_strcasecmp(command, "reset") == 0) { enabled = true; if (playing) CDAudio_Stop(); for (n = 0; n < 100; n++) remap[n] = n; CDAudio_GetAudioDiskInfo(); return; } if (q_strcasecmp(command, "remap") == 0) { ret = Cmd_Argc() - 2; if (ret <= 0) { for (n = 1; n < 100; n++) if (remap[n] != n) Con_Printf(" %u -> %u\n", n, remap[n]); return; } for (n = 1; n <= ret; n++) remap[n] = atoi(Cmd_Argv (n+1)); return; } if (!cdValid) { CDAudio_GetAudioDiskInfo(); if (!cdValid) { Con_Printf("No CD in player.\n"); return; } } if (q_strcasecmp(command, "play") == 0) { CDAudio_Play((byte)atoi(Cmd_Argv (2)), false); return; } if (q_strcasecmp(command, "loop") == 0) { CDAudio_Play((byte)atoi(Cmd_Argv (2)), true); return; } if (q_strcasecmp(command, "stop") == 0) { CDAudio_Stop(); return; } if (q_strcasecmp(command, "pause") == 0) { CDAudio_Pause(); return; } if (q_strcasecmp(command, "resume") == 0) { CDAudio_Resume(); return; } if (q_strcasecmp(command, "eject") == 0) { if (playing) CDAudio_Stop(); CDAudio_Eject(); cdValid = false; return; } if (q_strcasecmp(command, "info") == 0) { Con_Printf("%u tracks\n", maxTrack); if (playing) Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack); else if (wasPlaying) Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack); Con_Printf("Volume is %f\n", bgmvolume.value); return; } } static qboolean CD_GetVolume (struct CD_Volume *vol) { BYTE error; error = CDGetVolume(vol, cdRequest); if (error) { Con_DPrintf("CDGetVolume failed (%d)\n", (int)error); return false; } return true; } static qboolean CD_SetVolume (struct CD_Volume *vol) { BYTE error; error = CDSetVolume(vol, cdRequest); if (error) { Con_DPrintf("CDSetVolume failed (%d)\n", (int)error); return false; } return true; } static qboolean CDAudio_SetVolume (float value) { if (cdDevice == -1 || !enabled) return false; old_cdvolume = value; if (value == 0.0f) CDAudio_Pause (); else CDAudio_Resume(); if (!hw_vol_works) { return false; } else { drv_vol.cdv_Chan0 = drv_vol.cdv_Chan2 = drv_vol.cdv_Chan1 = drv_vol.cdv_Chan3 = value * 255.0f; return CD_SetVolume (&drv_vol); } } void CDAudio_Update(void) { BOOL status; static double lastCheck; if (cdDevice == -1 || !enabled) return; if (old_cdvolume != bgmvolume.value) CDAudio_SetVolume (bgmvolume.value); if (playing && lastCheck < realtime) { lastCheck = realtime + 2.0; /* two seconds between chks */ status = CDActive(cdRequest); if (!status) { playing = false; if (playLooping) CDAudio_Play(playTrack, true); } } } static void CD_CloseDevice(void) { if (cdRequest) { if (!cdDevice) { CloseDevice((struct IORequest *)cdRequest); cdDevice = -1; } DeleteIORequest(cdRequest); cdRequest = NULL; } if (cdPort) { DeleteMsgPort(cdPort); cdPort = NULL; } } static int CD_OpenDevice(const char *volume) { struct FileLock *fl; struct DosList *dol = NULL; struct DosList *doslist; BPTR lock; struct Process *me; APTR oldwindow; me = (struct Process *)FindTask(NULL); oldwindow = me->pr_WindowPtr; me->pr_WindowPtr = (APTR)-1; lock = Lock((STRPTR)volume, ACCESS_READ); me->pr_WindowPtr = oldwindow; if (!lock) return -1; // IoErr() UnLock(lock); // look for the device fl = (struct FileLock *)BADDR(lock); if ((doslist = LockDosList(LDF_DEVICES | LDF_READ))) { while ((doslist = NextDosEntry(doslist, LDF_DEVICES))) { if (doslist->dol_Task == fl->fl_Task) { dol = doslist; break; } } UnLockDosList(LDF_DEVICES | LDF_READ); } if (dol) { struct FileSysStartupMsg *fssm = (struct FileSysStartupMsg *)BADDR(dol->dol_misc.dol_handler.dol_Startup); if ((ULONG)fssm > 0x400 && TypeOfMem(fssm) && fssm->fssm_Unit <= 0x00ffffff) { STRPTR device = (STRPTR)BADDR(fssm->fssm_Device); if (device && TypeOfMem(device) && device[0] != 0 && device[1] != '\0') { if ((cdPort = CreateMsgPort())) { if ((cdRequest = (struct IOStdReq *)CreateIORequest(cdPort, sizeof(struct IOStdReq)))) { cdDevice = OpenDevice(device + 1, fssm->fssm_Unit, (struct IORequest *)cdRequest, 0); return cdDevice; } } } } } CD_CloseDevice(); return -1; } int CDAudio_Init(void) { int i; if (safemode || COM_CheckParm("-nocdaudio")) return -1; if (!(CDPlayerBase = OpenLibrary((STRPTR)CDPLAYERNAME, CDPLAYERVERSION))) { Con_Printf ("%s: can't open cdplayer.library, CD Audio is disabled.\n", __thisfunc__); return -1; } if ((i = COM_CheckParm("-cddev")) != 0 && i < com_argc - 1) cd_dev = com_argv[i + 1]; if ((cdDevice = CD_OpenDevice(cd_dev)) == -1) { i = cdDevice; Con_Printf("%s: open of \"%s\" failed (%d)\n", __thisfunc__, cd_dev, i); return -1; } for (i = 0; i < 100; i++) remap[i] = i; initialized = true; enabled = true; old_cdvolume = bgmvolume.value; Con_Printf("CDAudio initialized (using cdplayer.library)\n"); if (CDAudio_GetAudioDiskInfo()) { Con_Printf("%s: No CD in drive\n", __thisfunc__); cdValid = false; } Cmd_AddCommand ("cd", CD_f); hw_vol_works = CD_GetVolume (&orig_vol); if (hw_vol_works) hw_vol_works = CDAudio_SetVolume (bgmvolume.value); return 0; } void CDAudio_Shutdown(void) { if (!initialized) return; CDAudio_Stop(); if (hw_vol_works) CD_SetVolume (&orig_vol); CD_CloseDevice(); if (CDPlayerBase) { CloseLibrary(CDPlayerBase); CDPlayerBase = NULL; } } engine/h2shared/cd_bsd.c000066400000000000000000000226251444734033100154170ustar00rootroot00000000000000/* * cd_bsd.c * * Copyright (C) 1996-1997 Id Software, Inc. * A few BSD bits taken from the darkplaces project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cd_unix.h" #ifdef __USE_BSD_CDROM__ #include "quakedef.h" #include "cdaudio.h" #include #include #include #include #include #include #include #include static qboolean cdValid = false; static qboolean playing = false; static qboolean wasPlaying = false; static qboolean initialized = false; static qboolean enabled = true; static qboolean playLooping = false; static byte remap[100]; static byte playTrack; static byte maxTrack; static int cdfile = -1; /* default path to cdrom device. user can always do -cddev */ #if !defined(__FreeBSD__) static const char default_dev[] = _PATH_DEV "cd0"; #else static const char default_dev[] = _PATH_DEV "acd0"; #endif static const char *cd_dev = default_dev; static float old_cdvolume; static qboolean hw_vol_works = true; static struct ioc_vol orig_vol; /* original setting to be restored upon exit */ static struct ioc_vol drv_vol; /* the volume setting we'll be using */ #define IOCTL_FAILURE(__name) do { \ int __err = errno; \ Con_DPrintf("ioctl %s failed (%d: %s)\n", #__name, __err, strerror(__err)); \ } while (0) static void CDAudio_Eject(void) { if (cdfile == -1 || !enabled) return; ioctl(cdfile, CDIOCALLOW); if (ioctl(cdfile, CDIOCEJECT) == -1) IOCTL_FAILURE(CDIOCEJECT); } static void CDAudio_CloseDoor(void) { if (cdfile == -1 || !enabled) return; ioctl(cdfile, CDIOCALLOW); if (ioctl(cdfile, CDIOCCLOSE) == -1) IOCTL_FAILURE(CDIOCCLOSE); } static int CDAudio_GetAudioDiskInfo(void) { struct ioc_toc_header tochdr; if (cdfile == -1) return -1; cdValid = false; if (ioctl(cdfile, CDIOREADTOCHEADER, &tochdr) == -1) { IOCTL_FAILURE(CDIOREADTOCHEADER); return -1; } if (tochdr.starting_track < 1) { Con_DPrintf("CDAudio: no music tracks\n"); return -1; } cdValid = true; maxTrack = tochdr.ending_track; return 0; } int CDAudio_Play(byte track, qboolean looping) { struct ioc_read_toc_entry entry; struct cd_toc_entry toc_buffer; struct ioc_play_track ti; if (cdfile == -1 || !enabled) return -1; if (!cdValid) { CDAudio_GetAudioDiskInfo(); if (!cdValid) return -1; } track = remap[track]; if (track < 1 || track > maxTrack) { Con_DPrintf("CDAudio: Bad track number %u.\n", track); return -1; } /* don't try to play a non-audio track */ # define CDROM_DATA_TRACK 4 memset((char *)&toc_buffer, 0, sizeof(toc_buffer)); entry.data_len = sizeof(toc_buffer); entry.data = &toc_buffer; entry.starting_track = track; entry.address_format = CD_MSF_FORMAT; if (ioctl(cdfile, CDIOREADTOCENTRYS, &entry) == -1) { IOCTL_FAILURE(CDIOREADTOCENTRYS); return -1; } if (toc_buffer.control & CDROM_DATA_TRACK) { Con_Printf("CDAudio: track %i is not audio\n", track); return -1; } if (playing) { if (playTrack == track) return 0; CDAudio_Stop(); } ti.start_track = track; ti.end_track = track; ti.start_index = 1; ti.end_index = 99; if (ioctl(cdfile, CDIOCPLAYTRACKS, &ti) == -1) { IOCTL_FAILURE(CDIOCPLAYTRACKS); return -1; } if (ioctl(cdfile, CDIOCRESUME) == -1) { IOCTL_FAILURE(CDIOCRESUME); return -1; } playLooping = looping; playTrack = track; playing = true; if (bgmvolume.value == 0) /* don't bother advancing */ CDAudio_Pause (); return 0; } void CDAudio_Stop(void) { if (cdfile == -1 || !enabled) return; if (!playing) return; if (ioctl(cdfile, CDIOCSTOP) == -1) { IOCTL_FAILURE(CDIOCSTOP); return; } ioctl(cdfile, CDIOCALLOW); wasPlaying = false; playing = false; } void CDAudio_Pause(void) { if (cdfile == -1 || !enabled) return; if (!playing) return; if (ioctl(cdfile, CDIOCPAUSE) == -1) IOCTL_FAILURE(CDIOCPAUSE); wasPlaying = playing; playing = false; } void CDAudio_Resume(void) { if (cdfile == -1 || !enabled) return; if (!cdValid) return; if (!wasPlaying) return; if (ioctl(cdfile, CDIOCRESUME) == -1) IOCTL_FAILURE(CDIOCRESUME); playing = true; } static void CD_f (void) { const char *command; int ret, n; if (Cmd_Argc() < 2) { Con_Printf("commands:"); Con_Printf("on, off, reset, remap, \n"); Con_Printf("play, stop, loop, pause, resume\n"); Con_Printf("eject, close, info\n"); return; } command = Cmd_Argv (1); if (q_strcasecmp(command, "on") == 0) { enabled = true; return; } if (q_strcasecmp(command, "off") == 0) { if (playing) CDAudio_Stop(); enabled = false; return; } if (q_strcasecmp(command, "reset") == 0) { enabled = true; if (playing) CDAudio_Stop(); for (n = 0; n < 100; n++) remap[n] = n; CDAudio_GetAudioDiskInfo(); return; } if (q_strcasecmp(command, "remap") == 0) { ret = Cmd_Argc() - 2; if (ret <= 0) { for (n = 1; n < 100; n++) if (remap[n] != n) Con_Printf(" %u -> %u\n", n, remap[n]); return; } for (n = 1; n <= ret; n++) remap[n] = atoi(Cmd_Argv (n+1)); return; } if (q_strcasecmp(command, "close") == 0) { CDAudio_CloseDoor(); return; } if (!cdValid) { CDAudio_GetAudioDiskInfo(); if (!cdValid) { Con_Printf("No CD in player.\n"); return; } } if (q_strcasecmp(command, "play") == 0) { CDAudio_Play((byte)atoi(Cmd_Argv (2)), false); return; } if (q_strcasecmp(command, "loop") == 0) { CDAudio_Play((byte)atoi(Cmd_Argv (2)), true); return; } if (q_strcasecmp(command, "stop") == 0) { CDAudio_Stop(); return; } if (q_strcasecmp(command, "pause") == 0) { CDAudio_Pause(); return; } if (q_strcasecmp(command, "resume") == 0) { CDAudio_Resume(); return; } if (q_strcasecmp(command, "eject") == 0) { if (playing) CDAudio_Stop(); CDAudio_Eject(); cdValid = false; return; } if (q_strcasecmp(command, "info") == 0) { Con_Printf("%u tracks\n", maxTrack); if (playing) Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack); else if (wasPlaying) Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack); Con_Printf("Volume is %f\n", bgmvolume.value); return; } } static qboolean CD_GetVolume (struct ioc_vol *vol) { if (ioctl(cdfile, CDIOCGETVOL, vol) == -1) { IOCTL_FAILURE(CDIOCGETVOL); return false; } return true; } static qboolean CD_SetVolume (struct ioc_vol *vol) { if (ioctl(cdfile, CDIOCSETVOL, vol) == -1) { IOCTL_FAILURE(CDIOCSETVOL); return false; } return true; } static qboolean CDAudio_SetVolume (float value) { if (cdfile == -1 || !enabled) return false; old_cdvolume = value; if (value == 0.0f) CDAudio_Pause (); else CDAudio_Resume(); if (!hw_vol_works) { return false; } else { drv_vol.vol[0] = drv_vol.vol[2] = drv_vol.vol[1] = drv_vol.vol[3] = value * 255.0f; return CD_SetVolume (&drv_vol); } } void CDAudio_Update(void) { struct ioc_read_subchannel subchnl; struct cd_sub_channel_info data; static time_t lastchk; if (cdfile == -1 || !enabled) return; if (old_cdvolume != bgmvolume.value) CDAudio_SetVolume (bgmvolume.value); if (playing && lastchk < time(NULL)) { lastchk = time(NULL) + 2; /* two seconds between chks */ memset (&subchnl, 0, sizeof(subchnl)); subchnl.data = &data; subchnl.data_len = sizeof(data); subchnl.address_format = CD_MSF_FORMAT; subchnl.data_format = CD_CURRENT_POSITION; subchnl.track = playTrack; if (ioctl(cdfile, CDIOCREADSUBCHANNEL, &subchnl) == -1) { IOCTL_FAILURE(CDIOCREADSUBCHANNEL); playing = false; return; } if (data.header.audio_status != CD_AS_PLAY_IN_PROGRESS && data.header.audio_status != CD_AS_PLAY_PAUSED) { playing = false; if (playLooping) CDAudio_Play(playTrack, true); } else { playTrack = data.what.position.track_number; } } } int CDAudio_Init(void) { int i; if (safemode || COM_CheckParm("-nocdaudio")) return -1; if ((i = COM_CheckParm("-cddev")) != 0 && i < com_argc - 1) cd_dev = com_argv[i + 1]; if ((cdfile = open(cd_dev, O_RDONLY | O_NONBLOCK)) == -1) { i = errno; Con_Printf("%s: open of \"%s\" failed (%d: %s)\n", __thisfunc__, cd_dev, i, strerror(i)); cdfile = -1; return -1; } for (i = 0; i < 100; i++) remap[i] = i; initialized = true; enabled = true; old_cdvolume = bgmvolume.value; Con_Printf("CDAudio initialized (using BSD ioctls)\n"); if (CDAudio_GetAudioDiskInfo()) { Con_Printf("%s: No CD in drive\n", __thisfunc__); cdValid = false; } Cmd_AddCommand ("cd", CD_f); hw_vol_works = CD_GetVolume (&orig_vol); if (hw_vol_works) hw_vol_works = CDAudio_SetVolume (bgmvolume.value); return 0; } void CDAudio_Shutdown(void) { if (!initialized) return; CDAudio_Stop(); if (hw_vol_works) CD_SetVolume (&orig_vol); close(cdfile); cdfile = -1; } #endif /* __USE_BSD_CDROM__ */ engine/h2shared/cd_dos.c000066400000000000000000000474061444734033100154400ustar00rootroot00000000000000/* * cd_audio.c -- dos cdaudio support. * from quake1 source with minor adaptations for uhexen2. * * Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. * All rights reserved. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "quakedef.h" #include "cdaudio.h" #include "dosisms.h" #define ADDRESS_MODE_HSG 0 #define ADDRESS_MODE_RED_BOOK 1 #define STATUS_ERROR_BIT 0x8000 #define STATUS_BUSY_BIT 0x0200 #define STATUS_DONE_BIT 0x0100 #define STATUS_ERROR_MASK 0x00ff #define ERROR_WRITE_PROTECT 0 #define ERROR_UNKNOWN_UNIT 1 #define ERROR_DRIVE_NOT_READY 2 #define ERROR_UNKNOWN_COMMAND 3 #define ERROR_CRC_ERROR 4 #define ERROR_BAD_REQUEST_LEN 5 #define ERROR_SEEK_ERROR 6 #define ERROR_UNKNOWN_MEDIA 7 #define ERROR_SECTOR_NOT_FOUND 8 #define ERROR_OUT_OF_PAPER 9 #define ERROR_WRITE_FAULT 10 #define ERROR_READ_FAULT 11 #define ERROR_GENERAL_FAILURE 12 #define ERROR_RESERVED_13 13 #define ERROR_RESERVED_14 14 #define ERROR_BAD_DISK_CHANGE 15 #define COMMAND_READ 3 #define COMMAND_WRITE 12 #define COMMAND_PLAY_AUDIO 132 #define COMMAND_STOP_AUDIO 133 #define COMMAND_RESUME_AUDIO 136 #define READ_REQUEST_AUDIO_CHANNEL_INFO 4 #define READ_REQUEST_DEVICE_STATUS 6 #define READ_REQUEST_MEDIA_CHANGE 9 #define READ_REQUEST_AUDIO_DISK_INFO 10 #define READ_REQUEST_AUDIO_TRACK_INFO 11 #define READ_REQUEST_AUDIO_STATUS 15 #define WRITE_REQUEST_EJECT 0 #define WRITE_REQUEST_RESET 2 #define WRITE_REQUEST_AUDIO_CHANNEL_INFO 3 #define STATUS_DOOR_OPEN 0x00000001 #define STATUS_DOOR_UNLOCKED 0x00000002 #define STATUS_RAW_SUPPORT 0x00000004 #define STATUS_READ_WRITE 0x00000008 #define STATUS_AUDIO_SUPPORT 0x00000010 #define STATUS_INTERLEAVE_SUPPORT 0x00000020 #define STATUS_BIT_6_RESERVED 0x00000040 #define STATUS_PREFETCH_SUPPORT 0x00000080 #define STATUS_AUDIO_MANIPLUATION_SUPPORT 0x00000100 #define STATUS_RED_BOOK_ADDRESS_SUPPORT 0x00000200 #define MEDIA_NOT_CHANGED 1 #define MEDIA_STATUS_UNKNOWN 0 #define MEDIA_CHANGED (-1) #define AUDIO_CONTROL_MASK 0xd0 #define AUDIO_CONTROL_DATA_TRACK 0x40 #define AUDIO_CONTROL_AUDIO_2_TRACK 0x00 #define AUDIO_CONTROL_AUDIO_2P_TRACK 0x10 #define AUDIO_CONTROL_AUDIO_4_TRACK 0x80 #define AUDIO_CONTROL_AUDIO_4P_TRACK 0x90 #define AUDIO_STATUS_PAUSED 0x0001 #pragma pack(1) struct playAudioRequest { char addressingMode; int startLocation; int sectors; }; struct readRequest { char mediaDescriptor; short bufferOffset; short bufferSegment; short length; short startSector; int volumeID; }; struct writeRequest { char mediaDescriptor; short bufferOffset; short bufferSegment; short length; short startSector; int volumeID; }; struct cd_request { char headerLength; char unit; char command; short status; char reserved[8]; union { struct playAudioRequest playAudio; struct readRequest read; struct writeRequest write; } x; }; struct audioChannelInfo_s { char code; char channel0input; char channel0volume; char channel1input; char channel1volume; char channel2input; char channel2volume; char channel3input; char channel3volume; }; struct deviceStatus_s { char code; int status; }; struct mediaChange_s { char code; char status; }; struct audioDiskInfo_s { char code; char lowTrack; char highTrack; int leadOutStart; }; struct audioTrackInfo_s { char code; char track; int start; char control; }; struct audioStatus_s { char code; short status; int PRstartLocation; int PRendLocation; }; struct reset_s { char code; }; union readInfo_u { struct audioChannelInfo_s audioChannelInfo; struct deviceStatus_s deviceStatus; struct mediaChange_s mediaChange; struct audioDiskInfo_s audioDiskInfo; struct audioTrackInfo_s audioTrackInfo; struct audioStatus_s audioStatus; struct reset_s reset; }; #pragma pack() #define MAXIMUM_TRACKS 100 typedef struct { int start; int length; qboolean isData; } track_info; typedef struct { qboolean valid; int leadOutAddress; track_info track[MAXIMUM_TRACKS]; byte lowTrack; byte highTrack; } cd_info; static struct cd_request *cdRequest; static union readInfo_u *readInfo; static cd_info cd; static qboolean playing = false; static qboolean wasPlaying = false; static qboolean mediaCheck = false; static qboolean initialized = false; static qboolean enabled = true; static qboolean playLooping = false; static short cdRequestSegment; static short cdRequestOffset; static short readInfoSegment; static short readInfoOffset; static byte remap[256]; static byte cdrom; static byte firstcdrom, numcdroms; static byte cdroms_list[32]; static byte playTrack; static float old_cdvolume; static int RedBookToSector (int rb) { byte minute; byte second; byte frame; minute = (rb >> 16) & 0xff; second = (rb >> 8) & 0xff; frame = rb & 0xff; return minute * 60 * 75 + second * 75 + frame; } static void CDAudio_Reset (void) { cdRequest->headerLength = 13; cdRequest->unit = 0; cdRequest->command = COMMAND_WRITE; cdRequest->status = 0; cdRequest->x.write.mediaDescriptor = 0; cdRequest->x.write.bufferOffset = readInfoOffset; cdRequest->x.write.bufferSegment = readInfoSegment; cdRequest->x.write.length = sizeof(struct reset_s); cdRequest->x.write.startSector = 0; cdRequest->x.write.volumeID = 0; readInfo->reset.code = WRITE_REQUEST_RESET; regs.x.ax = 0x1510; regs.x.cx = cdrom; regs.x.es = cdRequestSegment; regs.x.bx = cdRequestOffset; dos_int86 (0x2f); } static void CDAudio_Eject (void) { cdRequest->headerLength = 13; cdRequest->unit = 0; cdRequest->command = COMMAND_WRITE; cdRequest->status = 0; cdRequest->x.write.mediaDescriptor = 0; cdRequest->x.write.bufferOffset = readInfoOffset; cdRequest->x.write.bufferSegment = readInfoSegment; cdRequest->x.write.length = sizeof(struct reset_s); cdRequest->x.write.startSector = 0; cdRequest->x.write.volumeID = 0; readInfo->reset.code = WRITE_REQUEST_EJECT; regs.x.ax = 0x1510; regs.x.cx = cdrom; regs.x.es = cdRequestSegment; regs.x.bx = cdRequestOffset; dos_int86 (0x2f); } static int CDAudio_GetAudioTrackInfo (byte track, int *start) { byte control; cdRequest->headerLength = 13; cdRequest->unit = 0; cdRequest->command = COMMAND_READ; cdRequest->status = 0; cdRequest->x.read.mediaDescriptor = 0; cdRequest->x.read.bufferOffset = readInfoOffset; cdRequest->x.read.bufferSegment = readInfoSegment; cdRequest->x.read.length = sizeof(struct audioTrackInfo_s); cdRequest->x.read.startSector = 0; cdRequest->x.read.volumeID = 0; readInfo->audioTrackInfo.code = READ_REQUEST_AUDIO_TRACK_INFO; readInfo->audioTrackInfo.track = track; regs.x.ax = 0x1510; regs.x.cx = cdrom; regs.x.es = cdRequestSegment; regs.x.bx = cdRequestOffset; dos_int86 (0x2f); if (cdRequest->status & STATUS_ERROR_BIT) { Con_DPrintf("%s %04x\n", __thisfunc__, cdRequest->status & 0xffff); return -1; } *start = readInfo->audioTrackInfo.start; control = readInfo->audioTrackInfo.control & AUDIO_CONTROL_MASK; return (control & AUDIO_CONTROL_DATA_TRACK); } static int CDAudio_GetAudioDiskInfo (void) { int n; cdRequest->headerLength = 13; cdRequest->unit = 0; cdRequest->command = COMMAND_READ; cdRequest->status = 0; cdRequest->x.read.mediaDescriptor = 0; cdRequest->x.read.bufferOffset = readInfoOffset; cdRequest->x.read.bufferSegment = readInfoSegment; cdRequest->x.read.length = sizeof(struct audioDiskInfo_s); cdRequest->x.read.startSector = 0; cdRequest->x.read.volumeID = 0; readInfo->audioDiskInfo.code = READ_REQUEST_AUDIO_DISK_INFO; regs.x.ax = 0x1510; regs.x.cx = cdrom; regs.x.es = cdRequestSegment; regs.x.bx = cdRequestOffset; dos_int86 (0x2f); if (cdRequest->status & STATUS_ERROR_BIT) { Con_DPrintf("%s %04x\n", __thisfunc__, cdRequest->status & 0xffff); return -1; } cd.valid = true; cd.lowTrack = readInfo->audioDiskInfo.lowTrack; cd.highTrack = readInfo->audioDiskInfo.highTrack; cd.leadOutAddress = readInfo->audioDiskInfo.leadOutStart; for (n = cd.lowTrack; n <= cd.highTrack; n++) { cd.track[n].isData = CDAudio_GetAudioTrackInfo (n, &cd.track[n].start); if (n > cd.lowTrack) { cd.track[n-1].length = RedBookToSector(cd.track[n].start) - RedBookToSector(cd.track[n-1].start); if (n == cd.highTrack) cd.track[n].length = RedBookToSector(cd.leadOutAddress) - RedBookToSector(cd.track[n].start); } } return 0; } static int CDAudio_GetAudioStatus (void) { cdRequest->headerLength = 13; cdRequest->unit = 0; cdRequest->command = COMMAND_READ; cdRequest->status = 0; cdRequest->x.read.mediaDescriptor = 0; cdRequest->x.read.bufferOffset = readInfoOffset; cdRequest->x.read.bufferSegment = readInfoSegment; cdRequest->x.read.length = sizeof(struct audioStatus_s); cdRequest->x.read.startSector = 0; cdRequest->x.read.volumeID = 0; readInfo->audioDiskInfo.code = READ_REQUEST_AUDIO_STATUS; regs.x.ax = 0x1510; regs.x.cx = cdrom; regs.x.es = cdRequestSegment; regs.x.bx = cdRequestOffset; dos_int86 (0x2f); if (cdRequest->status & STATUS_ERROR_BIT) return -1; return 0; } static int CDAudio_MediaChange (void) { cdRequest->headerLength = 13; cdRequest->unit = 0; cdRequest->command = COMMAND_READ; cdRequest->status = 0; cdRequest->x.read.mediaDescriptor = 0; cdRequest->x.read.bufferOffset = readInfoOffset; cdRequest->x.read.bufferSegment = readInfoSegment; cdRequest->x.read.length = sizeof(struct mediaChange_s); cdRequest->x.read.startSector = 0; cdRequest->x.read.volumeID = 0; readInfo->mediaChange.code = READ_REQUEST_MEDIA_CHANGE; regs.x.ax = 0x1510; regs.x.cx = cdrom; regs.x.es = cdRequestSegment; regs.x.bx = cdRequestOffset; dos_int86 (0x2f); return readInfo->mediaChange.status; } // we set the volume to 0 first and then to the desired volume // some cd-rom drivers seem to need it done this way static qboolean CD_SetVolume (byte volume) { if (!initialized || !enabled) return false; cdRequest->headerLength = 13; cdRequest->unit = 0; cdRequest->command = COMMAND_WRITE; cdRequest->status = 0; cdRequest->x.read.mediaDescriptor = 0; cdRequest->x.read.bufferOffset = readInfoOffset; cdRequest->x.read.bufferSegment = readInfoSegment; cdRequest->x.read.length = sizeof(struct audioChannelInfo_s); cdRequest->x.read.startSector = 0; cdRequest->x.read.volumeID = 0; readInfo->audioChannelInfo.code = WRITE_REQUEST_AUDIO_CHANNEL_INFO; readInfo->audioChannelInfo.channel0input = 0; readInfo->audioChannelInfo.channel0volume = 0; readInfo->audioChannelInfo.channel1input = 1; readInfo->audioChannelInfo.channel1volume = 0; readInfo->audioChannelInfo.channel2input = 2; readInfo->audioChannelInfo.channel2volume = 0; readInfo->audioChannelInfo.channel3input = 3; readInfo->audioChannelInfo.channel3volume = 0; regs.x.ax = 0x1510; regs.x.cx = cdrom; regs.x.es = cdRequestSegment; regs.x.bx = cdRequestOffset; dos_int86 (0x2f); readInfo->audioChannelInfo.channel0volume = volume; readInfo->audioChannelInfo.channel1volume = volume; regs.x.ax = 0x1510; regs.x.cx = cdrom; regs.x.es = cdRequestSegment; regs.x.bx = cdRequestOffset; dos_int86 (0x2f); return true; } static qboolean CDAudio_SetVolume (float value) { byte volume; if (!initialized || !enabled) return false; old_cdvolume = value; /* should I pause if value == 0 ? */ volume = (int)(value * 255.0f); return CD_SetVolume (volume); } int CDAudio_Play (byte track, qboolean looping) { if (!initialized || !enabled) return -1; if (!cd.valid) return -1; track = remap[track]; if (playing) { if (playTrack == track) return 0; CDAudio_Stop(); } playLooping = looping; if (track < cd.lowTrack || track > cd.highTrack) { Con_DPrintf("%s: Bad track number %u.\n", __thisfunc__, track); return -1; } playTrack = track; if (cd.track[track].isData) { Con_DPrintf("%s: Can not play data.\n", __thisfunc__); return -1; } CDAudio_SetVolume (bgmvolume.value); cdRequest->headerLength = 13; cdRequest->unit = 0; cdRequest->command = COMMAND_PLAY_AUDIO; cdRequest->status = 0; cdRequest->x.playAudio.addressingMode = ADDRESS_MODE_RED_BOOK; cdRequest->x.playAudio.startLocation = cd.track[track].start; cdRequest->x.playAudio.sectors = cd.track[track].length; regs.x.ax = 0x1510; regs.x.cx = cdrom; regs.x.es = cdRequestSegment; regs.x.bx = cdRequestOffset; dos_int86 (0x2f); if (cdRequest->status & STATUS_ERROR_BIT) { Con_DPrintf("%s: track %u failed\n", __thisfunc__, track); cd.valid = false; playing = false; return -1; } playing = true; /* should I pause if bgmvolume.value == 0 ? */ return 0; } void CDAudio_Stop (void) { if (!initialized || !enabled) return; cdRequest->headerLength = 13; cdRequest->unit = 0; cdRequest->command = COMMAND_STOP_AUDIO; cdRequest->status = 0; regs.x.ax = 0x1510; regs.x.cx = cdrom; regs.x.es = cdRequestSegment; regs.x.bx = cdRequestOffset; dos_int86 (0x2f); wasPlaying = playing; playing = false; } void CDAudio_Pause (void) { CDAudio_Stop(); } void CDAudio_Resume (void) { if (!initialized || !enabled) return; if (!cd.valid) return; if (!wasPlaying) return; cdRequest->headerLength = 13; cdRequest->unit = 0; cdRequest->command = COMMAND_RESUME_AUDIO; cdRequest->status = 0; regs.x.ax = 0x1510; regs.x.cx = cdrom; regs.x.es = cdRequestSegment; regs.x.bx = cdRequestOffset; dos_int86 (0x2f); playing = true; } static void CD_f (void) { const char *command; int ret, n; int startAddress; if (Cmd_Argc() < 2) return; command = Cmd_Argv (1); if (q_strcasecmp(command, "on") == 0) { enabled = true; return; } if (q_strcasecmp(command, "off") == 0) { if (playing) CDAudio_Stop(); enabled = false; return; } if (q_strcasecmp(command, "reset") == 0) { enabled = true; if (playing) CDAudio_Stop(); for (n = 0; n < 256; n++) remap[n] = n; CDAudio_Reset(); CDAudio_GetAudioDiskInfo(); return; } if (q_strcasecmp(command, "remap") == 0) { ret = Cmd_Argc() - 2; if (ret <= 0) { for (n = 1; n < 256; n++) if (remap[n] != n) Con_Printf(" %u -> %u\n", n, remap[n]); return; } for (n = 1; n <= ret; n++) remap[n] = atoi(Cmd_Argv (n+1)); return; } if (!cd.valid) { Con_Printf("No CD in player.\n"); return; } if (q_strcasecmp(command, "play") == 0) { CDAudio_Play(atoi(Cmd_Argv (2)), false); return; } if (q_strcasecmp(command, "loop") == 0) { CDAudio_Play(atoi(Cmd_Argv (2)), true); return; } if (q_strcasecmp(command, "stop") == 0) { CDAudio_Stop(); return; } if (q_strcasecmp(command, "pause") == 0) { CDAudio_Pause(); return; } if (q_strcasecmp(command, "resume") == 0) { CDAudio_Resume(); return; } if (q_strcasecmp(command, "eject") == 0) { if (playing) CDAudio_Stop(); CDAudio_Eject(); cd.valid = false; return; } if (q_strcasecmp(command, "info") == 0) { Con_Printf("%u tracks\n", cd.highTrack - cd.lowTrack + 1); for (n = cd.lowTrack; n <= cd.highTrack; n++) { ret = CDAudio_GetAudioTrackInfo (n, &startAddress); Con_Printf("Track %2u: %s at %2u:%02u\n", n, ret ? "data " : "music", (startAddress >> 16) & 0xff, (startAddress >> 8) & 0xff); } if (playing) Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack); Con_Printf("Volume is %f\n", bgmvolume.value); CDAudio_MediaChange(); Con_Printf("Status %04x\n", cdRequest->status & 0xffff); return; } } void CDAudio_Update (void) { int ret; static double lastUpdate; if (!initialized || !enabled) return; if ((realtime - lastUpdate) < 0.25) return; lastUpdate = realtime; if (mediaCheck) { static double lastCheck; if ((realtime - lastCheck) < 5.0) return; lastCheck = realtime; ret = CDAudio_MediaChange(); if (ret == MEDIA_CHANGED) { Con_DPrintf("CDAudio: media changed\n"); playing = false; wasPlaying = false; cd.valid = false; CDAudio_GetAudioDiskInfo(); return; } } if (old_cdvolume != bgmvolume.value) CDAudio_SetVolume (bgmvolume.value); if (playing) { CDAudio_GetAudioStatus(); if ((cdRequest->status & STATUS_BUSY_BIT) == 0) { playing = false; if (playLooping) CDAudio_Play(playTrack, true); } } } static byte get_cddev_arg (const char *arg) { /* arg should be like "D", "D:" or "D:\", make * sure it is so. Also check if this is really * a CDROM drive. */ byte drivenum; __dpmi_regs r; if (!arg || ! *arg) return 0xff; if (arg[1] != '\0') { if (arg[1] != ':') return 0xff; if (arg[2] != '\0') { if (arg[2] != '\\' && arg[2] != '/') return 0xff; if (arg[3] != '\0') return 0xff; } } drivenum = *arg; if (drivenum >= 'A' && drivenum <= 'Z') drivenum -= 'A'; else if (drivenum >= 'a' && drivenum <= 'z') drivenum -= 'a'; else return 0xff; r.x.ax = 0x150b; r.x.cx = drivenum; /* 0 = A: */ __dpmi_int(0x2f, &r); if (r.x.ax != 0 /*&& r.x.bx == 0xadad*/) return drivenum; Con_Printf("%c: is not a CDROM drive\n", drivenum + 'A'); return 0xff; } static const byte *get_cdroms_list (void) { __dpmi_regs r; memset (cdroms_list, 0, sizeof(cdroms_list)); r.x.ax = 0x150d; r.x.bx = __tb & 0x0f; r.x.es = (__tb >> 4) & 0xffff; if (__dpmi_int(0x2f, &r) != 0) return NULL; dosmemget(__tb, numcdroms, cdroms_list); return cdroms_list; } int CDAudio_Init (void) { char *memory; int n; if (safemode || COM_CheckParm("-nocdaudio") || COM_CheckParm("-nocd")) return -1; if (COM_CheckParm("-cdmediacheck")) mediaCheck = true; regs.x.ax = 0x1500; regs.x.bx = 0; dos_int86 (0x2f); if (regs.x.bx == 0) { Con_Printf ("MSCDEX not loaded, CD Audio is disabled.\n"); return -1; } numcdroms = regs.x.bx; firstcdrom = regs.x.cx; cdrom = firstcdrom; regs.x.ax = 0x150c; regs.x.bx = 0; dos_int86 (0x2f); if (regs.x.bx == 0) { Con_Printf("%s: MSCDEX version 2.00 or later required.\n", __thisfunc__); return -1; } if (!get_cdroms_list ()) { cdroms_list[0] = firstcdrom; Con_Printf("%s: Couldn't get available CD drive letters\n", __thisfunc__); } Con_DPrintf("%s: %u CD-ROM drive(s) available:", __thisfunc__, numcdroms); for (n = 0; n < numcdroms && cdroms_list[n]; n++) Con_DPrintf (" %c", cdroms_list[n] + 'A'); Con_DPrintf(".\n"); if ((n = COM_CheckParm("-cddev")) != 0 && n < com_argc - 1) { cdrom = get_cddev_arg(com_argv[n + 1]); if (cdrom == 0xff) { Con_Printf("Invalid argument to -cddev\n"); return -1; } } Con_Printf("%s: Using CD-ROM drive %c:\n", __thisfunc__, cdrom + 'A'); memory = (char *) dos_getmemory(sizeof(struct cd_request) + sizeof(union readInfo_u)); if (memory == NULL) { Con_DPrintf("%s: Unable to allocate low memory.\n", __thisfunc__); return -1; } cdRequest = (struct cd_request *)memory; cdRequestSegment = ptr2real(cdRequest) >> 4; cdRequestOffset = ptr2real(cdRequest) & 0xf; readInfo = (union readInfo_u *)(memory + sizeof(struct cd_request)); readInfoSegment = ptr2real(readInfo) >> 4; readInfoOffset = ptr2real(readInfo) & 0xf; for (n = 0; n < 256; n++) remap[n] = n; initialized = true; old_cdvolume = bgmvolume.value; CD_SetVolume (255); if (CDAudio_GetAudioDiskInfo()) { Con_Printf("%s: No CD in player.\n", __thisfunc__); enabled = false; } Cmd_AddCommand ("cd", CD_f); Con_Printf("CD Audio Initialized\n"); return 0; } void CDAudio_Shutdown (void) { if (!initialized) return; CDAudio_Stop(); CD_SetVolume (255); } engine/h2shared/cd_linux.c000066400000000000000000000213311444734033100157770ustar00rootroot00000000000000/* cd_linux.c * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cd_unix.h" #ifdef __USE_LINUX_CDROM__ #include "quakedef.h" #include "cdaudio.h" #include #include #include #include #include #include #include #include static qboolean cdValid = false; static qboolean playing = false; static qboolean wasPlaying = false; static qboolean initialized = false; static qboolean enabled = true; static qboolean playLooping = false; static byte remap[100]; static byte playTrack; static byte maxTrack; static int cdfile = -1; static const char default_dev[] = _PATH_DEV "cdrom"; /* user can always do -cddev */ static const char *cd_dev = default_dev; static float old_cdvolume; static qboolean hw_vol_works = true; static struct cdrom_volctrl orig_vol; /* original setting to be restored upon exit */ static struct cdrom_volctrl drv_vol; /* the volume setting we'll be using */ #define IOCTL_FAILURE(__name) do { \ int __err = errno; \ Con_DPrintf("ioctl %s failed (%d: %s)\n", #__name, __err, strerror(__err)); \ } while (0) static void CDAudio_Eject(void) { if (cdfile == -1 || !enabled) return; if (ioctl(cdfile, CDROMEJECT) == -1) IOCTL_FAILURE(CDROMEJECT); } static void CDAudio_CloseDoor(void) { if (cdfile == -1 || !enabled) return; if (ioctl(cdfile, CDROMCLOSETRAY) == -1) IOCTL_FAILURE(CDROMCLOSETRAY); } static int CDAudio_GetAudioDiskInfo(void) { struct cdrom_tochdr tochdr; if (cdfile == -1) return -1; cdValid = false; if (ioctl(cdfile, CDROMREADTOCHDR, &tochdr) == -1) { IOCTL_FAILURE(CDROMREADTOCHDR); return -1; } if (tochdr.cdth_trk0 < 1) { Con_DPrintf("CDAudio: no music tracks\n"); return -1; } cdValid = true; maxTrack = tochdr.cdth_trk1; return 0; } int CDAudio_Play(byte track, qboolean looping) { struct cdrom_tocentry entry; struct cdrom_ti ti; if (cdfile == -1 || !enabled) return -1; if (!cdValid) { CDAudio_GetAudioDiskInfo(); if (!cdValid) return -1; } track = remap[track]; if (track < 1 || track > maxTrack) { Con_DPrintf("CDAudio: Bad track number %u.\n", track); return -1; } /* don't try to play a non-audio track */ entry.cdte_track = track; entry.cdte_format = CDROM_MSF; if (ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1) { IOCTL_FAILURE(CDROMREADTOCENTRY); return -1; } if (entry.cdte_ctrl == CDROM_DATA_TRACK) { Con_Printf("CDAudio: track %i is not audio\n", track); return -1; } if (playing) { if (playTrack == track) return 0; CDAudio_Stop(); } ti.cdti_trk0 = track; ti.cdti_trk1 = track; ti.cdti_ind0 = 1; ti.cdti_ind1 = 99; if (ioctl(cdfile, CDROMPLAYTRKIND, &ti) == -1) { IOCTL_FAILURE(CDROMPLAYTRKIND); return -1; } if (ioctl(cdfile, CDROMRESUME) == -1) { IOCTL_FAILURE(CDROMRESUME); return -1; } playLooping = looping; playTrack = track; playing = true; if (bgmvolume.value == 0) /* don't bother advancing */ CDAudio_Pause (); return 0; } void CDAudio_Stop(void) { if (cdfile == -1 || !enabled) return; if (!playing) return; if (ioctl(cdfile, CDROMSTOP) == -1) IOCTL_FAILURE(CDROMSTOP); wasPlaying = false; playing = false; } void CDAudio_Pause(void) { if (cdfile == -1 || !enabled) return; if (!playing) return; if (ioctl(cdfile, CDROMPAUSE) == -1) IOCTL_FAILURE(CDROMPAUSE); wasPlaying = playing; playing = false; } void CDAudio_Resume(void) { if (cdfile == -1 || !enabled) return; if (!cdValid) return; if (!wasPlaying) return; if (ioctl(cdfile, CDROMRESUME) == -1) IOCTL_FAILURE(CDROMRESUME); playing = true; } static void CD_f (void) { const char *command; int ret, n; if (Cmd_Argc() < 2) { Con_Printf("commands:"); Con_Printf("on, off, reset, remap, \n"); Con_Printf("play, stop, loop, pause, resume\n"); Con_Printf("eject, close, info\n"); return; } command = Cmd_Argv (1); if (q_strcasecmp(command, "on") == 0) { enabled = true; return; } if (q_strcasecmp(command, "off") == 0) { if (playing) CDAudio_Stop(); enabled = false; return; } if (q_strcasecmp(command, "reset") == 0) { enabled = true; if (playing) CDAudio_Stop(); for (n = 0; n < 100; n++) remap[n] = n; CDAudio_GetAudioDiskInfo(); return; } if (q_strcasecmp(command, "remap") == 0) { ret = Cmd_Argc() - 2; if (ret <= 0) { for (n = 1; n < 100; n++) if (remap[n] != n) Con_Printf(" %u -> %u\n", n, remap[n]); return; } for (n = 1; n <= ret; n++) remap[n] = atoi(Cmd_Argv (n+1)); return; } if (q_strcasecmp(command, "close") == 0) { CDAudio_CloseDoor(); return; } if (!cdValid) { CDAudio_GetAudioDiskInfo(); if (!cdValid) { Con_Printf("No CD in player.\n"); return; } } if (q_strcasecmp(command, "play") == 0) { CDAudio_Play((byte)atoi(Cmd_Argv (2)), false); return; } if (q_strcasecmp(command, "loop") == 0) { CDAudio_Play((byte)atoi(Cmd_Argv (2)), true); return; } if (q_strcasecmp(command, "stop") == 0) { CDAudio_Stop(); return; } if (q_strcasecmp(command, "pause") == 0) { CDAudio_Pause(); return; } if (q_strcasecmp(command, "resume") == 0) { CDAudio_Resume(); return; } if (q_strcasecmp(command, "eject") == 0) { if (playing) CDAudio_Stop(); CDAudio_Eject(); cdValid = false; return; } if (q_strcasecmp(command, "info") == 0) { Con_Printf("%u tracks\n", maxTrack); if (playing) Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack); else if (wasPlaying) Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack); Con_Printf("Volume is %f\n", bgmvolume.value); return; } } static qboolean CD_GetVolume (struct cdrom_volctrl *vol) { if (ioctl(cdfile, CDROMVOLREAD, vol) == -1) { IOCTL_FAILURE(CDROMVOLREAD); return false; } return true; } static qboolean CD_SetVolume (struct cdrom_volctrl *vol) { if (ioctl(cdfile, CDROMVOLCTRL, vol) == -1) { IOCTL_FAILURE(CDROMVOLCTRL); return false; } return true; } static qboolean CDAudio_SetVolume (float value) { if (cdfile == -1 || !enabled) return false; old_cdvolume = value; if (value == 0.0f) CDAudio_Pause (); else CDAudio_Resume(); if (!hw_vol_works) { return false; } else { drv_vol.channel0 = drv_vol.channel2 = drv_vol.channel1 = drv_vol.channel3 = value * 255.0f; return CD_SetVolume (&drv_vol); } } void CDAudio_Update(void) { struct cdrom_subchnl subchnl; static time_t lastchk; if (cdfile == -1 || !enabled) return; if (old_cdvolume != bgmvolume.value) CDAudio_SetVolume (bgmvolume.value); if (playing && lastchk < time(NULL)) { lastchk = time(NULL) + 2; /* two seconds between chks */ subchnl.cdsc_format = CDROM_MSF; if (ioctl(cdfile, CDROMSUBCHNL, &subchnl) == -1) { IOCTL_FAILURE(CDROMSUBCHNL); playing = false; return; } if (subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY && subchnl.cdsc_audiostatus != CDROM_AUDIO_PAUSED) { playing = false; if (playLooping) CDAudio_Play(playTrack, true); } else { playTrack = subchnl.cdsc_trk; } } } int CDAudio_Init(void) { int i; if (safemode || COM_CheckParm("-nocdaudio")) return -1; if ((i = COM_CheckParm("-cddev")) != 0 && i < com_argc - 1) cd_dev = com_argv[i + 1]; if ((cdfile = open(cd_dev, O_RDONLY | O_NONBLOCK)) == -1) { i = errno; Con_Printf("%s: open of \"%s\" failed (%d: %s)\n", __thisfunc__, cd_dev, i, strerror(i)); cdfile = -1; return -1; } for (i = 0; i < 100; i++) remap[i] = i; initialized = true; enabled = true; old_cdvolume = bgmvolume.value; Con_Printf("CDAudio initialized (using Linux ioctls)\n"); if (CDAudio_GetAudioDiskInfo()) { Con_Printf("%s: No CD in drive\n", __thisfunc__); cdValid = false; } Cmd_AddCommand ("cd", CD_f); hw_vol_works = CD_GetVolume (&orig_vol); if (hw_vol_works) hw_vol_works = CDAudio_SetVolume (bgmvolume.value); return 0; } void CDAudio_Shutdown(void) { if (!initialized) return; CDAudio_Stop(); if (hw_vol_works) CD_SetVolume (&orig_vol); close(cdfile); cdfile = -1; } #endif /* __USE_LINUX_CDROM__ */ engine/h2shared/cd_null.c000066400000000000000000000021051444734033100156100ustar00rootroot00000000000000/* cd_null.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "cdaudio.h" int CDAudio_Play(byte track, qboolean looping) { return -1; } void CDAudio_Stop(void) { } void CDAudio_Pause(void) { } void CDAudio_Resume(void) { } void CDAudio_Update(void) { } int CDAudio_Init(void) { Con_Printf("CDAudio disabled at compile time\n"); return -1; } void CDAudio_Shutdown(void) { } engine/h2shared/cd_sdl.c000066400000000000000000000255031444734033100154270ustar00rootroot00000000000000/* cd_sdl.c * * Copyright (C) 1996-1997 Id Software, Inc. * Taken from the Twilight project with modifications * to make it work with Hexen II: Hammer of Thyrion. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cd_unix.h" #ifdef __USE_SDL_CDROM__ #include "sdl_inc.h" #ifndef SDL_INIT_CDROM /* SDL dropped support for cd audio since v1.3.0 */ #pragma message("Warning: SDL CDAudio support disabled") #include "cd_null.c" #else /* SDL_INIT_CDROM */ #include "quakedef.h" #include "cdaudio.h" static qboolean cdValid = false; static qboolean playing = false; static qboolean wasPlaying = false; static qboolean enabled = true; static qboolean playLooping = false; static byte remap[100]; static byte playTrack; static double endOfTrack = -1.0, pausetime = -1.0; static SDL_CD *cd_handle; static int cd_dev = -1; static float old_cdvolume; static qboolean hw_vol_works = true; static void CDAudio_Eject(void) { if (!cd_handle || !enabled) return; if (SDL_CDEject(cd_handle) < 0) Con_Printf ("Unable to eject CD-ROM: %s\n", SDL_GetError ()); } static int CDAudio_GetAudioDiskInfo(void) { cdValid = false; if (!cd_handle) return -1; if ( ! CD_INDRIVE(SDL_CDStatus(cd_handle)) ) return -1; cdValid = true; return 0; } int CDAudio_Play(byte track, qboolean looping) { int len_m, len_s, len_f; if (!cd_handle || !enabled) return -1; if (!cdValid) { CDAudio_GetAudioDiskInfo(); if (!cdValid) return -1; } track = remap[track]; if (track < 1 || track > cd_handle->numtracks) { Con_Printf ("%s: Bad track number %d.\n", __thisfunc__, track); return -1; } if (cd_handle->track[track-1].type == SDL_DATA_TRACK) { Con_Printf ("%s: track %d is not audio\n", __thisfunc__, track); return -1; } if (playing) { if (playTrack == track) return 0; CDAudio_Stop(); } if (SDL_CDPlay(cd_handle, cd_handle->track[track-1].offset, cd_handle->track[track-1].length) < 0) { Con_Printf ("%s: Unable to play track %d: %s\n", __thisfunc__, track, SDL_GetError ()); return -1; } playLooping = looping; playTrack = track; playing = true; FRAMES_TO_MSF(cd_handle->track[track-1].length, &len_m, &len_s, &len_f); endOfTrack = realtime + ((double)len_m * 60.0) + (double)len_s + (double)len_f / (double)CD_FPS; /* Add the pregap for the next track. This means that disc-at-once CDs * won't loop smoothly, but they wouldn't anyway so it doesn't really * matter. SDL doesn't give us pregap information anyway, so you'll * just have to live with it. */ endOfTrack += 2.0; pausetime = -1.0; if (bgmvolume.value == 0) /* don't bother advancing */ CDAudio_Pause (); return 0; } void CDAudio_Stop(void) { if (!cd_handle || !enabled) return; if (!playing) return; if (SDL_CDStop(cd_handle) < 0) Con_Printf ("%s: Unable to stop CD-ROM (%s)\n", __thisfunc__, SDL_GetError()); wasPlaying = false; playing = false; pausetime = -1.0; endOfTrack = -1.0; } void CDAudio_Pause(void) { if (!cd_handle || !enabled) return; if (!playing) return; if (SDL_CDPause(cd_handle) < 0) Con_Printf ("Unable to pause CD-ROM: %s\n", SDL_GetError()); wasPlaying = playing; playing = false; pausetime = realtime; } void CDAudio_Resume(void) { if (!cd_handle || !enabled) return; if (!cdValid) return; if (!wasPlaying) return; if (SDL_CDResume(cd_handle) < 0) Con_Printf ("Unable to resume CD-ROM: %s\n", SDL_GetError()); playing = true; endOfTrack += realtime - pausetime; pausetime = -1.0; } static void CD_f (void) { const char *command; int ret, n; if (Cmd_Argc() < 2) { Con_Printf("commands:"); Con_Printf("on, off, reset, remap, \n"); Con_Printf("play, stop, loop, pause, resume\n"); Con_Printf("eject, info\n"); return; } command = Cmd_Argv (1); if (q_strcasecmp(command, "on") == 0) { enabled = true; return; } if (q_strcasecmp(command, "off") == 0) { if (playing) CDAudio_Stop(); enabled = false; return; } if (q_strcasecmp(command, "reset") == 0) { enabled = true; if (playing) CDAudio_Stop(); for (n = 0; n < 100; n++) remap[n] = n; CDAudio_GetAudioDiskInfo(); return; } if (q_strcasecmp(command, "remap") == 0) { ret = Cmd_Argc() - 2; if (ret <= 0) { for (n = 1; n < 100; n++) if (remap[n] != n) Con_Printf(" %u -> %u\n", n, remap[n]); return; } for (n = 1; n <= ret; n++) remap[n] = atoi(Cmd_Argv (n + 1)); return; } if (!cdValid) { CDAudio_GetAudioDiskInfo(); if (!cdValid) { Con_Printf("No CD in player.\n"); return; } } if (q_strcasecmp(command, "play") == 0) { CDAudio_Play((byte)atoi(Cmd_Argv (2)), false); return; } if (q_strcasecmp(command, "loop") == 0) { CDAudio_Play((byte)atoi(Cmd_Argv (2)), true); return; } if (q_strcasecmp(command, "stop") == 0) { CDAudio_Stop(); return; } if (q_strcasecmp(command, "pause") == 0) { CDAudio_Pause(); return; } if (q_strcasecmp(command, "resume") == 0) { CDAudio_Resume(); return; } if (q_strcasecmp(command, "eject") == 0) { if (playing) CDAudio_Stop(); CDAudio_Eject(); cdValid = false; return; } if (q_strcasecmp(command, "info") == 0) { int current_min, current_sec, current_frame; int length_min, length_sec, length_frame; Con_Printf ("%u tracks\n", cd_handle->numtracks); if (playing) Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack); else if (wasPlaying) Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack); if (playing || wasPlaying) { SDL_CDStatus(cd_handle); FRAMES_TO_MSF(cd_handle->cur_frame, ¤t_min, ¤t_sec, ¤t_frame); FRAMES_TO_MSF(cd_handle->track[playTrack-1].length, &length_min, &length_sec, &length_frame); Con_Printf ("Current position: %d:%02d.%02d (of %d:%02d.%02d)\n", current_min, current_sec, current_frame * 60 / CD_FPS, length_min, length_sec, length_frame * 60 / CD_FPS); } Con_Printf("Volume is %f\n", bgmvolume.value); return; } } static qboolean CD_GetVolume (void *unused) { /* FIXME: write proper code in here when SDL supports cdrom volume control some day. */ return false; } static qboolean CD_SetVolume (void *unused) { /* FIXME: write proper code in here when SDL supports cdrom volume control some day. */ return false; } static qboolean CDAudio_SetVolume (float value) { if (!cd_handle || !enabled) return false; old_cdvolume = value; if (value == 0.0f) CDAudio_Pause (); else CDAudio_Resume(); if (!hw_vol_works) { return false; } else { /* FIXME: write proper code in here when SDL supports cdrom volume control some day. */ return CD_SetVolume (NULL); } } void CDAudio_Update(void) { CDstatus curstat; /* static double lastchk;*/ if (!cd_handle || !enabled) return; if (old_cdvolume != bgmvolume.value) CDAudio_SetVolume (bgmvolume.value); /* if (playing && realtime > lastchk)*/ if (playing && realtime > endOfTrack) { /* lastchk = realtime + 2;*/ /* two seconds between chks */ curstat = SDL_CDStatus(cd_handle); if (curstat != CD_PLAYING && curstat != CD_PAUSED) { playing = false; endOfTrack = -1.0; if (playLooping) CDAudio_Play(playTrack, true); } } } static const char *get_cddev_arg (const char *arg) { #if defined(_WIN32) /* arg should be like "D:\", make sure it is so, * but tolerate args like "D" or "D:", as well. */ static char drive[4]; if (!arg || ! *arg) return NULL; if (arg[1] != '\0') { if (arg[1] != ':') return NULL; if (arg[2] != '\0') { if (arg[2] != '\\' && arg[2] != '/') return NULL; if (arg[3] != '\0') return NULL; } } if (*arg >= 'A' && *arg <= 'Z') { drive[0] = *arg; drive[1] = ':'; drive[2] = '\\'; drive[3] = '\0'; return drive; } else if (*arg >= 'a' && *arg <= 'z') { /* make it uppercase for SDL */ drive[0] = *arg - ('a' - 'A'); drive[1] = ':'; drive[2] = '\\'; drive[3] = '\0'; return drive; } return NULL; #else if (!arg || ! *arg) return NULL; return arg; #endif } static void export_cddev_arg (void) { /* Bad ugly hack to workaround SDL's cdrom device detection. * not needed for windows due to the way SDL_cdrom works. */ #if !defined(_WIN32) && !defined(__MORPHOS__) int i = COM_CheckParm("-cddev"); if (i != 0 && i < com_argc - 1 && com_argv[i+1][0] != '\0') { static char arg[64]; q_snprintf(arg, sizeof(arg), "SDL_CDROM=%s", com_argv[i+1]); putenv(arg); } #endif } int CDAudio_Init(void) { int i, sdl_num_drives; if (safemode || COM_CheckParm("-nocdaudio")) return -1; export_cddev_arg(); if (SDL_InitSubSystem(SDL_INIT_CDROM) < 0) { Con_Printf("Couldn't init SDL cdrom: %s\n", SDL_GetError()); return -1; } sdl_num_drives = SDL_CDNumDrives (); Con_Printf ("SDL detected %d CD-ROM drive%c\n", sdl_num_drives, sdl_num_drives == 1 ? ' ' : 's'); if (sdl_num_drives < 1) return -1; if ((i = COM_CheckParm("-cddev")) != 0 && i < com_argc - 1) { const char *userdev = get_cddev_arg(com_argv[i+1]); if (!userdev) { Con_Printf("Invalid argument to -cddev\n"); return -1; } for (i = 0; i < sdl_num_drives; i++) { if (!q_strcasecmp(SDL_CDName(i), userdev)) { cd_dev = i; break; } } if (cd_dev == -1) { Con_Printf("SDL couldn't find cdrom device %s\n", userdev); return -1; } } if (cd_dev == -1) cd_dev = 0; /* default drive */ cd_handle = SDL_CDOpen(cd_dev); if (!cd_handle) { Con_Printf ("%s: Unable to open CD-ROM drive %s (%s)\n", __thisfunc__, SDL_CDName(cd_dev), SDL_GetError()); return -1; } for (i = 0; i < 100; i++) remap[i] = i; enabled = true; old_cdvolume = bgmvolume.value; Con_Printf("CDAudio initialized (SDL, using %s)\n", SDL_CDName(cd_dev)); if (CDAudio_GetAudioDiskInfo()) { Con_Printf("%s: No CD in drive\n", __thisfunc__); cdValid = false; } Cmd_AddCommand ("cd", CD_f); hw_vol_works = CD_GetVolume (NULL); /* no SDL support at present. */ if (hw_vol_works) hw_vol_works = CDAudio_SetVolume (bgmvolume.value); return 0; } void CDAudio_Shutdown(void) { if (!cd_handle) return; CDAudio_Stop(); if (hw_vol_works) CD_SetVolume (NULL); /* no SDL support at present. */ SDL_CDClose(cd_handle); cd_handle = NULL; cd_dev = -1; SDL_QuitSubSystem(SDL_INIT_CDROM); } #endif /* SDL_INIT_CDROM */ #endif /* __USE_SDL_CDROM__ */ engine/h2shared/cd_unix.h000066400000000000000000000027201444734033100156310ustar00rootroot00000000000000/* cd_unix.h -- header for Unix builds to pick the correct cdaudio code * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __CD_UNIX_H #define __CD_UNIX_H #undef __USE_BSD_CDROM__ #undef __USE_LINUX_CDROM__ #undef __USE_SDL_CDROM__ #if defined (WITH_SDLCD) /* This means that the makefile is edited for USE_SDLCD */ # if defined(SDLQUAKE) # define __USE_SDL_CDROM__ 1 # else # error "cd_unix.h: WITH_SDLCD is defined but SDLQUAKE is not." # endif #elif defined (__linux) || defined (__linux__) # define __USE_LINUX_CDROM__ 1 #elif defined (__FreeBSD__) || defined (__OpenBSD__) || defined (__NetBSD__) # define __USE_BSD_CDROM__ 1 #elif defined (SDLQUAKE) # define __USE_SDL_CDROM__ 1 #else # error "no cdaudio module defined. edit cd_unix.h or your makefile.." #endif #endif /* __CD_UNIX_H */ engine/h2shared/cd_win.c000066400000000000000000000316601444734033100154430ustar00rootroot00000000000000/* cd_win.c -- Windows cdaudio code * * Copyright (C) 1996-1997 Id Software, Inc. * Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All * rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "cdaudio.h" #include "winquake.h" #include /* * You just can't set the volume of CD playback via MCI : * http://blogs.msdn.com/larryosterman/archive/2005/10/06/477874.aspx * OTOH, using the aux APIs to control the CD audio volume is broken. */ #undef USE_AUX_API static qboolean cdValid = false; static qboolean playing = false; static qboolean wasPlaying = false; static qboolean initialized = false; static qboolean enabled = false; static qboolean playLooping = false; static byte remap[100]; static byte playTrack; static byte maxTrack; static float old_cdvolume; static UINT wDeviceID; static DWORD end_pos; #if defined(USE_AUX_API) static UINT CD_ID; static unsigned long CD_OrigVolume; static void CD_SetVolume(unsigned long Volume); #endif /* USE_AUX_API */ static void CDAudio_Eject(void) { DWORD dwReturn; dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_OPEN, (DWORD_PTR)NULL); if (dwReturn) Con_DPrintf("MCI_SET_DOOR_OPEN failed (%u)\n", (unsigned int)dwReturn); } static void CDAudio_CloseDoor(void) { DWORD dwReturn; dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, (DWORD_PTR)NULL); if (dwReturn) Con_DPrintf("MCI_SET_DOOR_CLOSED failed (%u)\n", (unsigned int)dwReturn); } static int CDAudio_GetAudioDiskInfo(void) { DWORD dwReturn; MCI_STATUS_PARMS mciStatusParms; cdValid = false; mciStatusParms.dwItem = MCI_STATUS_READY; dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR) (LPVOID) &mciStatusParms); if (dwReturn) { Con_DPrintf("CDAudio: drive ready test - get status failed\n"); return -1; } if (!mciStatusParms.dwReturn) { Con_DPrintf("CDAudio: drive not ready\n"); return -1; } mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR) (LPVOID) &mciStatusParms); if (dwReturn) { Con_DPrintf("CDAudio: get tracks - status failed\n"); return -1; } if (mciStatusParms.dwReturn < 1) { Con_DPrintf("CDAudio: no music tracks\n"); return -1; } cdValid = true; maxTrack = mciStatusParms.dwReturn; return 0; } int CDAudio_Play(byte track, qboolean looping) { DWORD dwReturn; MCI_PLAY_PARMS mciPlayParms; MCI_STATUS_PARMS mciStatusParms; if (!enabled) return -1; if (!cdValid) { CDAudio_GetAudioDiskInfo(); if (!cdValid) return -1; } track = remap[track]; if (track < 1 || track > maxTrack) { Con_DPrintf("CDAudio: Bad track number %u.\n", track); return -1; } // don't try to play a non-audio track mciStatusParms.dwItem = MCI_CDA_STATUS_TYPE_TRACK; mciStatusParms.dwTrack = track; dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD_PTR) (LPVOID) &mciStatusParms); if (dwReturn) { Con_DPrintf("MCI_STATUS failed (%u)\n", (unsigned int)dwReturn); return -1; } if (mciStatusParms.dwReturn != MCI_CDA_TRACK_AUDIO) { Con_Printf("CDAudio: track %i is not audio\n", track); return -1; } // get the length of the track to be played mciStatusParms.dwItem = MCI_STATUS_LENGTH; mciStatusParms.dwTrack = track; dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD_PTR) (LPVOID) &mciStatusParms); if (dwReturn) { Con_DPrintf("MCI_STATUS failed (%u)\n", (unsigned int)dwReturn); return -1; } if (playing) { if (playTrack == track) return 0; CDAudio_Stop(); } mciPlayParms.dwFrom = MCI_MAKE_TMSF(track, 0, 0, 0); mciPlayParms.dwTo = (mciStatusParms.dwReturn << 8) | track; end_pos = mciPlayParms.dwTo; mciPlayParms.dwCallback = (DWORD_PTR)mainwindow; dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY | MCI_FROM | MCI_TO, (DWORD_PTR)(LPVOID) &mciPlayParms); if (dwReturn) { Con_DPrintf("CDAudio: MCI_PLAY failed (%u)\n", (unsigned int)dwReturn); return -1; } playLooping = looping; playTrack = track; playing = true; if (bgmvolume.value == 0) /* don't bother advancing */ CDAudio_Pause (); return 0; } void CDAudio_Stop(void) { DWORD dwReturn; if (!enabled) return; if (!playing) return; dwReturn = mciSendCommand(wDeviceID, MCI_STOP, 0, (DWORD_PTR)NULL); if (dwReturn) Con_DPrintf("MCI_STOP failed (%u)", (unsigned int)dwReturn); wasPlaying = false; playing = false; } void CDAudio_Pause(void) { DWORD dwReturn; MCI_GENERIC_PARMS mciGenericParms; if (!enabled) return; if (!playing) return; mciGenericParms.dwCallback = (DWORD_PTR)mainwindow; dwReturn = mciSendCommand(wDeviceID, MCI_PAUSE, 0, (DWORD_PTR)(LPVOID) &mciGenericParms); if (dwReturn) Con_DPrintf("MCI_PAUSE failed (%u)", (unsigned int)dwReturn); wasPlaying = playing; playing = false; } void CDAudio_Resume(void) { DWORD dwReturn; MCI_STATUS_PARMS mciStatusParms; MCI_PLAY_PARMS mciPlayParms; if (!enabled) return; if (!cdValid) return; if (!wasPlaying) return; #if 0 /* dwReturn = mciSendCommand(wDeviceID, MCI_RESUME, MCI_WAIT, NULL); */ mciPlayParms.dwFrom = MCI_MAKE_TMSF(playTrack, 0, 0, 0); mciPlayParms.dwTo = MCI_MAKE_TMSF(playTrack + 1, 0, 0, 0); mciPlayParms.dwCallback = (DWORD_PTR)mainwindow; dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_TO | MCI_NOTIFY, (DWORD_PTR)(LPVOID) &mciPlayParms); #endif mciStatusParms.dwItem = MCI_STATUS_POSITION; dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR) (LPVOID) &mciStatusParms); if (dwReturn) { Con_DPrintf("MCI_STATUS failed (%u)\n", (unsigned int)dwReturn); return; } mciPlayParms.dwFrom = mciStatusParms.dwReturn; mciPlayParms.dwTo = end_pos; /* set in CDAudio_Play() */ mciPlayParms.dwCallback = (DWORD_PTR)mainwindow; dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_FROM | MCI_TO | MCI_NOTIFY, (DWORD_PTR)(LPVOID) &mciPlayParms); if (dwReturn) { Con_DPrintf("CDAudio: MCI_PLAY failed (%u)\n", (unsigned int)dwReturn); return; } playing = true; } static void CD_f (void) { const char *command; int ret, n; if (Cmd_Argc() < 2) { Con_Printf("commands:"); Con_Printf("on, off, reset, remap, \n"); Con_Printf("play, stop, loop, pause, resume\n"); Con_Printf("eject, close, info\n"); return; } command = Cmd_Argv (1); if (q_strcasecmp(command, "on") == 0) { enabled = true; return; } if (q_strcasecmp(command, "off") == 0) { if (playing) CDAudio_Stop(); enabled = false; return; } if (q_strcasecmp(command, "reset") == 0) { enabled = true; if (playing) CDAudio_Stop(); for (n = 0; n < 100; n++) remap[n] = n; CDAudio_GetAudioDiskInfo(); return; } if (q_strcasecmp(command, "remap") == 0) { ret = Cmd_Argc() - 2; if (ret <= 0) { for (n = 1; n < 100; n++) if (remap[n] != n) Con_Printf(" %u -> %u\n", n, remap[n]); return; } for (n = 1; n <= ret; n++) remap[n] = atoi(Cmd_Argv (n+1)); return; } if (q_strcasecmp(command, "close") == 0) { CDAudio_CloseDoor(); return; } if (!cdValid) { CDAudio_GetAudioDiskInfo(); if (!cdValid) { Con_Printf("No CD in player.\n"); return; } } if (q_strcasecmp(command, "play") == 0) { CDAudio_Play((byte)atoi(Cmd_Argv (2)), false); return; } if (q_strcasecmp(command, "loop") == 0) { CDAudio_Play((byte)atoi(Cmd_Argv (2)), true); return; } if (q_strcasecmp(command, "stop") == 0) { CDAudio_Stop(); return; } if (q_strcasecmp(command, "pause") == 0) { CDAudio_Pause(); return; } if (q_strcasecmp(command, "resume") == 0) { CDAudio_Resume(); return; } if (q_strcasecmp(command, "eject") == 0) { if (playing) CDAudio_Stop(); CDAudio_Eject(); cdValid = false; return; } if (q_strcasecmp(command, "info") == 0) { Con_Printf("%u tracks\n", maxTrack); if (playing) Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack); else if (wasPlaying) Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack); Con_Printf("Volume is %f\n", bgmvolume.value); return; } } LRESULT CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (lParam != (LONG)wDeviceID) return 1; switch (wParam) { case MCI_NOTIFY_SUCCESSFUL: if (playing) { playing = false; if (playLooping) CDAudio_Play(playTrack, true); } break; case MCI_NOTIFY_ABORTED: case MCI_NOTIFY_SUPERSEDED: break; case MCI_NOTIFY_FAILURE: Con_DPrintf("MCI_NOTIFY_FAILURE\n"); CDAudio_Stop (); cdValid = false; break; default: Con_DPrintf("Unexpected MM_MCINOTIFY type (%Iu)\n", wParam); return 1; } return 0; } static void CDAudio_SetVolume (float value) { old_cdvolume = value; if (value == 0.0f) CDAudio_Pause (); else CDAudio_Resume(); #if defined(USE_AUX_API) CD_SetVolume (value * 0xffff); #endif /* USE_AUX_API */ } void CDAudio_Update(void) { if (!enabled) return; if (old_cdvolume != bgmvolume.value) CDAudio_SetVolume (bgmvolume.value); } #if defined(USE_AUX_API) static void CD_FindCDAux(void) { UINT NumDevs, counter; MMRESULT Result; AUXCAPS Caps; CD_ID = -1; if (!COM_CheckParm("-usecdvolume")) return; NumDevs = auxGetNumDevs(); for (counter = 0; counter < NumDevs; counter++) { Result = auxGetDevCaps(counter,&Caps,sizeof(Caps)); if (!Result) // valid { if (Caps.wTechnology == AUXCAPS_CDAUDIO) { CD_ID = counter; auxGetVolume(CD_ID, &CD_OrigVolume); return; } } } } static void CD_SetVolume(unsigned long Volume) { if (CD_ID != -1) auxSetVolume(CD_ID, (Volume<<16) + Volume); } #endif /* USE_AUX_API */ static const char *get_cddev_arg (const char *arg) { /* arg should be like "D", "D:" or "D:\", make * sure it is so. Also check if this is really * a CDROM drive. */ static char drive[4]; if (!arg || ! *arg) return NULL; if (arg[1] != '\0') { if (arg[1] != ':') return NULL; if (arg[2] != '\0') { if (arg[2] != '\\' && arg[2] != '/') return NULL; if (arg[3] != '\0') return NULL; } } if (*arg >= 'A' && *arg <= 'Z') { drive[0] = *arg; drive[1] = ':'; drive[2] = '\\'; drive[3] = '\0'; } else if (*arg >= 'a' && *arg <= 'z') { /* make it uppercase */ drive[0] = *arg - ('a' - 'A'); drive[1] = ':'; drive[2] = '\\'; drive[3] = '\0'; } else { return NULL; } if (GetDriveType(drive) != DRIVE_CDROM) { Con_Printf("%c is not a CDROM drive\n", drive[0]); return NULL; } drive[2] = '\0'; return drive; } int CDAudio_Init(void) { DWORD dwReturn, flags; MCI_OPEN_PARMS mciOpenParms; MCI_SET_PARMS mciSetParms; const char *userdev = NULL; int n; if (safemode || COM_CheckParm("-nocdaudio")) return -1; flags = MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE; if ((n = COM_CheckParm("-cddev")) != 0 && n < com_argc - 1) { userdev = get_cddev_arg(com_argv[n + 1]); if (!userdev) { Con_Printf("Invalid argument to -cddev\n"); return -1; } mciOpenParms.lpstrElementName = userdev; flags |= MCI_OPEN_ELEMENT; } mciOpenParms.lpstrDeviceType = "cdaudio"; dwReturn = mciSendCommand(0, MCI_OPEN, flags, (DWORD_PTR) (LPVOID) &mciOpenParms); if (!userdev) userdev = "default cdrom"; if (dwReturn) { Con_Printf("%s: MCI_OPEN failed for %s (%u)\n", __thisfunc__, userdev, (unsigned int)dwReturn); return -1; } wDeviceID = mciOpenParms.wDeviceID; // Set the time format to track/minute/second/frame (TMSF). mciSetParms.dwTimeFormat = MCI_FORMAT_TMSF; dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)(LPVOID) &mciSetParms); if (dwReturn) { Con_Printf("MCI_SET_TIME_FORMAT failed (%u)\n", (unsigned int)dwReturn); mciSendCommand(wDeviceID, MCI_CLOSE, 0, (DWORD_PTR)NULL); return -1; } for (n = 0; n < 100; n++) remap[n] = n; initialized = true; enabled = true; old_cdvolume = bgmvolume.value; if (CDAudio_GetAudioDiskInfo()) { Con_Printf("%s: No CD in player.\n", __thisfunc__); cdValid = false; } Cmd_AddCommand ("cd", CD_f); #if defined(USE_AUX_API) CD_FindCDAux(); #endif /* USE_AUX_API */ Con_Printf("CD Audio Initialized\n"); return 0; } void CDAudio_Shutdown(void) { if (!initialized) return; CDAudio_Stop(); if (mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD_PTR)NULL)) Con_DPrintf("%s: MCI_CLOSE failed\n", __thisfunc__); } engine/h2shared/cdaudio.h000066400000000000000000000021761444734033100156150ustar00rootroot00000000000000/* * cdaudio.h -- client cd audio functions * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __CDAUDIO_H #define __CDAUDIO_H int CDAudio_Init (void); int CDAudio_Play (byte track, qboolean looping); /* returns 0 for success, -1 for failure. */ void CDAudio_Stop (void); void CDAudio_Pause (void); void CDAudio_Resume (void); void CDAudio_Shutdown (void); void CDAudio_Update (void); #endif /* __CDAUDIO_H */ engine/h2shared/cfgfile.c000066400000000000000000000074071444734033100156010ustar00rootroot00000000000000/* * cfgfile.c -- misc reads from the config file * * Copyright (C) 2008-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" static fshandle_t *cfg_file; /* =================== CFG_ReadCvars used for doing early reads from config.cfg searching the list of given cvar names for the user-set values. a temporary solution until we merge a better cvar system. the num_vars argument must be the exact number of strings in the array, otherwise I have nothing against going out of bounds. =================== */ void CFG_ReadCvars (const char **vars, int num_vars) { char buff[1024], *tmp; int i, j; if (!cfg_file || num_vars < 1) return; j = 0; do { i = 0; memset (buff, 0, sizeof(buff)); // we expect a line in the format that Cvar_WriteVariables // writes to the config file. although I'm trying to be as // much cautious as possible, if the user screws it up by // editing it, it's his fault. if (FS_fgets(buff, sizeof(buff), cfg_file)) { // remove end-of-line characters while (buff[i]) { if (buff[i] == '\r' || buff[i] == '\n') buff[i] = '\0'; // while we're here, replace tabs with spaces if (buff[i] == '\t') buff[i] = ' '; i++; } // go to the last character while (buff[i] == 0 && i > 0) i--; // remove trailing spaces while (i > 0) { if (buff[i] == ' ') { buff[i] = '\0'; i--; } else break; } // the line must end with a quotation mark if (buff[i] != '\"') continue; buff[i] = '\0'; for (i = 0; i < num_vars && vars[i]; i++) { // look for the cvar name + one space tmp = strstr(buff, va("%s ",vars[i])); if (tmp != buff) continue; // locate the first quotation mark tmp = strchr(buff, '\"'); if (tmp) { Cvar_Set (vars[i], tmp + 1); j++; break; } } } if (j == num_vars) break; } while (!FS_feof(cfg_file) && !FS_ferror(cfg_file)); FS_rewind (cfg_file); } /* =================== CFG_ReadCvarOverrides convenience function, reading the "+" command line override values of cvars in the given list. doesn't do anything with the config file. =================== */ void CFG_ReadCvarOverrides (const char **vars, int num_vars) { char buff[64]; int i, j; if (num_vars < 1) return; buff[0] = '+'; for (i = 0; i < num_vars; i++) { q_strlcpy (&buff[1], vars[i], sizeof(buff) - 1); j = COM_CheckParm(buff); if (j != 0 && j < com_argc - 1) { if (com_argv[j + 1][0] != '-' && com_argv[j + 1][0] != '+') Cvar_Set(vars[i], com_argv[j + 1]); } } } void CFG_CloseConfig (void) { if (cfg_file) { FS_fclose(cfg_file); Z_Free(cfg_file); cfg_file = NULL; } } int CFG_OpenConfig (const char *cfg_name) { FILE *f; long length; qboolean pak; CFG_CloseConfig (); length = FS_OpenFile (cfg_name, &f, NULL); pak = file_from_pak; if (length < 0) return -1; cfg_file = (fshandle_t *) Z_Malloc(sizeof(fshandle_t), Z_MAINZONE); cfg_file->file = f; cfg_file->start = ftell(f); cfg_file->pos = 0; cfg_file->length = length; cfg_file->pak = pak; return 0; } engine/h2shared/cfgfile.h000066400000000000000000000030241444734033100155750ustar00rootroot00000000000000/* * cfgfile.h -- misc reads from the config file * * Copyright (C) 2008-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __CFGFILE_H #define __CFGFILE_H int CFG_OpenConfig (const char *cfg_name); // opens the given config file. only one open config file is // kept: previosly opened one, if any, will be closed. void CFG_CloseConfig (void); // closes the currently open config file. void CFG_ReadCvars (const char **vars, int num_vars); // reads the values of cvars in the given list from the opened // config file. void CFG_ReadCvarOverrides (const char **vars, int num_vars); // convenience function, reading the "+" command line override // values of cvars in the given list. doesn't do anything with // the config file. call this after CFG_ReadCvars() and before // locking your cvars. #endif /* __CFGFILE_H */ engine/h2shared/cmd.c000066400000000000000000000416171444734033100147460ustar00rootroot00000000000000/* * cmd.c - Quake script command processing module * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #define MAX_ALIAS_NAME 32 #define MAX_ARGS 80 static sizebuf_t cmd_text; //static byte cmd_text_buf[8192]; cmd_source_t cmd_source; typedef struct cmdalias_s { struct cmdalias_s *next; char name[MAX_ALIAS_NAME]; char *value; } cmdalias_t; typedef struct cmd_function_s { struct cmd_function_s *next; const char *name; xcommand_t function; } cmd_function_t; static cmdalias_t *cmd_alias = NULL; static cmd_function_t *cmd_functions = NULL; static int cmd_argc; static char *cmd_argv[MAX_ARGS]; static char cmd_null_string[] = ""; static const char *cmd_args = NULL; static qboolean cmd_wait; //============================================================================= /* ============ Cmd_Wait_f Causes execution of the remainder of the command buffer to be delayed until next frame. This allows commands like: bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2" ============ */ static void Cmd_Wait_f (void) { cmd_wait = true; } /* ============================================================================= COMMAND BUFFER ============================================================================= */ /* ============ Cbuf_Init ============ */ void Cbuf_Init (void) { // SZ_Init (&cmd_text, cmd_text_buf, sizeof(cmd_text_buf)); SZ_Init (&cmd_text, NULL, 8192); } /* ============ Cbuf_AddText Adds command text at the end of the buffer ============ */ void Cbuf_AddText (const char *text) { int l; l = strlen (text); if (cmd_text.cursize + l >= cmd_text.maxsize) { Con_Printf ("%s: overflow\n", __thisfunc__); return; } SZ_Write (&cmd_text, text, l); } /* ============ Cbuf_InsertText Adds command text immediately after the current command Adds a \n to the text FIXME: actually change the command buffer to do less copying ============ */ void Cbuf_InsertText (const char *text) { char *temp; int templen; // copy off any commands still remaining in the exec buffer templen = cmd_text.cursize; if (templen) { temp = (char *) Z_Malloc (templen, Z_MAINZONE); memcpy (temp, cmd_text.data, templen); SZ_Clear (&cmd_text); } else temp = NULL; // shut up compiler // add the entire text of the file Cbuf_AddText (text); SZ_Write (&cmd_text, "\n", 1); // add the copied off data if (templen) { SZ_Write (&cmd_text, temp, templen); Z_Free (temp); } } /* ============ Cbuf_Execute ============ */ void Cbuf_Execute (void) { int i; char *text; char line[1024]; int quotes; while (cmd_text.cursize) { // find a \n or ; line break text = (char *)cmd_text.data; quotes = 0; for (i = 0; i < cmd_text.cursize; i++) { if (text[i] == '"') quotes++; if ( !(quotes&1) && text[i] == ';') break; // don't break if inside a quoted string if (text[i] == '\n') break; } if (i > (int)sizeof(line) - 1) { memcpy (line, text, sizeof(line) - 1); line[sizeof(line) - 1] = 0; } else { memcpy (line, text, i); line[i] = 0; } // delete the text from the command buffer and move remaining commands down // this is necessary because commands (exec, alias) can insert data at the // beginning of the text buffer if (i == cmd_text.cursize) cmd_text.cursize = 0; else { i++; cmd_text.cursize -= i; memmove (text, text + i, cmd_text.cursize); } // execute the command line Cmd_ExecuteString (line, src_command); if (cmd_wait) { // skip out while text still remains in buffer, leaving it // for next frame cmd_wait = false; break; } } } /* ============================================================================== SCRIPT COMMANDS ============================================================================== */ /* =============== Cmd_StuffCmds_f Adds command line parameters as script statements Commands lead with a +, and continue until a - or another + quake +prog jctest.qp +cmd amlev1 quake -nosound +cmd amlev1 =============== */ void Cmd_StuffCmds_f (void) { int i; int s; char *text; s = 0; for (i = 1; i < com_argc; i++) { if (!com_argv[i]) continue; // NEXTSTEP nulls out -NXHost s += strlen (com_argv[i]) + 1; } if (!s) return; text = (char *) Z_Malloc (s+1, Z_MAINZONE); text[0] = '\0'; for (i = 1; i < com_argc; i++) { if (!com_argv[i]) continue; // NEXTSTEP nulls out -NXHost if (com_argv[i][0] != '+') continue; // found a command if (text[0] != '\0') strcat (text, " "); // separate it from previous one strcat (text, &com_argv[i][1]); if (i == com_argc - 1) { strcat (text, "\n"); // finished all args break; } // add the arguments of the command ++i; for ( ; i < com_argc; i++) { if (com_argv[i][0] == '+' || com_argv[i][0] == '-') { // found a new command or a new command-line switch strcat (text, "\n"); --i; break; } strcat (text, " "); strcat (text, com_argv[i]); if (i == com_argc - 1) strcat (text, "\n"); // finished all args } } if (text[0] != '\0') Cbuf_InsertText (text); Z_Free (text); } /* =============== Cmd_Exec_f =============== */ static void Cmd_Exec_f (void) { char *f; int mark; if (Cmd_Argc () != 2) { Con_Printf ("exec : execute a script file\n"); return; } // FIXME: is this safe freeing the hunk here??? mark = Hunk_LowMark (); f = (char *)FS_LoadHunkFile (Cmd_Argv(1), NULL); if (!f) { Con_Printf ("couldn't exec %s\n",Cmd_Argv(1)); return; } Con_Printf ("execing %s\n", Cmd_Argv(1)); Cbuf_InsertText (f); Hunk_FreeToLowMark (mark); } /* =============== Cmd_Echo_f Just prints the rest of the line to the console =============== */ static void Cmd_Echo_f (void) { int i; for (i = 1; i < Cmd_Argc(); i++) Con_Printf ("%s ", Cmd_Argv(i)); Con_Printf ("\n"); } /* =============== Cmd_Alias_f Creates a new command that executes a command string (possibly ; seperated) =============== */ static void Cmd_Alias_f (void) { cmdalias_t *a; char cmd[1024]; int i, c; const char *s; if (Cmd_Argc() == 1) { Con_Printf ("Current alias commands:\n"); for (a = cmd_alias ; a ; a = a->next) Con_Printf ("%s : %s\n", a->name, a->value); return; } s = Cmd_Argv(1); if (Cmd_Argc() == 2) { for (a = cmd_alias ; a ; a = a->next) { if ( !strcmp(s, a->name) ) { Con_Printf ("%s : %s\n", s, a->value); return; } } Con_Printf ("No alias named %s\n", s); return; } if (strlen(s) >= MAX_ALIAS_NAME) { Con_Printf ("Alias name is too long\n"); return; } // if the alias already exists, reuse it for (a = cmd_alias ; a ; a = a->next) { if ( !strcmp(s, a->name) ) { Z_Free (a->value); break; } } if (!a) { a = (cmdalias_t *) Z_Malloc (sizeof(cmdalias_t), Z_MAINZONE); a->next = cmd_alias; cmd_alias = a; } strcpy (a->name, s); // copy the rest of the command line cmd[0] = 0; // start out with a null string c = Cmd_Argc(); for (i = 2; i < c; i++) { q_strlcat (cmd, Cmd_Argv(i), sizeof(cmd)); if (i != c - 1) q_strlcat (cmd, " ", sizeof(cmd)); } if (q_strlcat(cmd, "\n", sizeof(cmd)) >= sizeof(cmd)) { Con_Printf("alias value too long!\n"); cmd[0] = '\n'; // nullify the string cmd[1] = 0; } a->value = Z_Strdup (cmd); } /* =============== Cmd_Unalias_f Delete an alias =============== */ void Cmd_Unalias_f (void) { cmdalias_t *prev = NULL, *a; if (Cmd_Argc() != 2) { Con_Printf("unalias : delete alias\n" "unaliasall : delete all aliases\n"); return; } for (a = cmd_alias ; a ; a=a->next) { if ( !strcmp(Cmd_Argv(1), a->name) ) { if (prev) prev->next = a->next; else cmd_alias = a->next; Z_Free (a->value); Z_Free (a); return; } prev = a; } Con_Printf ("No alias named %s\n", Cmd_Argv(1)); } /* =============== Cmd_Unaliasall_f Delete all aliases =============== */ void Cmd_Unaliasall_f (void) { cmdalias_t *a; while (cmd_alias) { a = cmd_alias->next; Z_Free(cmd_alias->value); Z_Free(cmd_alias); cmd_alias = a; } } /* ============================================================================= COMMAND EXECUTION ============================================================================= */ /* ============ Cmd_Argc ============ */ int Cmd_Argc (void) { return cmd_argc; } /* ============ Cmd_Argv ============ */ const char *Cmd_Argv (int arg) { if (arg < 0 || arg >= cmd_argc) return cmd_null_string; return cmd_argv[arg]; } /* ============ Cmd_Args Returns a single string containing argv(1) to argv(argc()-1) ============ */ const char *Cmd_Args (void) { if (!cmd_args) return cmd_null_string; return cmd_args; } /* ============ Cmd_TokenizeString Parses the given string into command line tokens. ============ */ void Cmd_TokenizeString (const char *text) { int i; // clear the args from the last string for (i = 0; i < cmd_argc; i++) Z_Free (cmd_argv[i]); cmd_argc = 0; cmd_args = NULL; while (1) { // skip whitespace up to a /n while (*text && *text <= ' ' && *text != '\n') { text++; } if (*text == '\n') { // a newline seperates commands in the buffer text++; break; } if (!*text) return; if (cmd_argc == 1) cmd_args = text; text = COM_Parse (text); if (!text) return; if (cmd_argc < MAX_ARGS) { cmd_argv[cmd_argc] = Z_Strdup (com_token); cmd_argc++; } } } /* ============ Cmd_AddCommand ============ */ void Cmd_AddCommand (const char *cmd_name, xcommand_t function) { cmd_function_t *cmd; if (host_initialized) // because hunk allocation would get stomped Sys_Error ("Cmd_AddCommand after host_initialized"); // fail if the command is a variable name if (Cvar_VariableString(cmd_name)[0]) { Con_Printf ("%s: %s already defined as a var\n", __thisfunc__, cmd_name); return; } // fail if the command already exists for (cmd = cmd_functions ; cmd ; cmd = cmd->next) { if ( !strcmp(cmd_name, cmd->name) ) { Con_Printf ("%s: %s already defined\n", __thisfunc__, cmd_name); return; } } cmd = (cmd_function_t *) Hunk_AllocName (sizeof(cmd_function_t), "commands"); cmd->name = cmd_name; cmd->function = function; cmd->next = cmd_functions; cmd_functions = cmd; } /* ============ Cmd_Exists ============ */ qboolean Cmd_Exists (const char *cmd_name) { cmd_function_t *cmd; for (cmd = cmd_functions ; cmd ; cmd = cmd->next) { if ( !strcmp(cmd_name, cmd->name) ) return true; } return false; } /* ============ Cmd_CheckCommand ============ */ qboolean Cmd_CheckCommand (const char *partial) { cmd_function_t *cmd; cmdalias_t *a; cvar_t *var; if (!partial || !partial[0]) return false; for (cmd = cmd_functions ; cmd ; cmd = cmd->next) { if ( !strcmp(partial, cmd->name) ) return true; } var = Cvar_FindVarAfter ("", CVAR_NONE); for ( ; var ; var = var->next) { if ( !strcmp(partial, var->name) ) return true; } for (a = cmd_alias ; a ; a = a->next) { if ( !strcmp(partial, a->name) ) return true; } return false; } /* ============ Cmd_MoveToFront ============ */ void Cmd_MoveToFront (const char *name) { cmd_function_t *cmd, *next; for (cmd = cmd_functions; cmd; cmd = cmd->next) { next = cmd->next; if (next && !strcmp(name, next->name)) { // remove from the list cmd->next = next->next; // move to the front next->next = cmd_functions; cmd_functions = next; break; } } } /* ============ Cmd_ExecuteString A complete command line has been parsed, so try to execute it FIXME: lookupnoadd the token to speed search? ============ */ void Cmd_ExecuteString (const char *text, cmd_source_t src) { cmd_function_t *cmd; cmdalias_t *a; cmd_source = src; Cmd_TokenizeString (text); // execute the command line if (!Cmd_Argc()) return; // no tokens // check functions for (cmd = cmd_functions ; cmd ; cmd = cmd->next) { if ( !q_strcasecmp(cmd_argv[0], cmd->name) ) { #if defined(H2W) if (!cmd->function) # ifndef SERVERONLY Cmd_ForwardToServer (); # else Sys_Printf ("FIXME: command %s has NULL handler function\n", cmd->name); # endif else #endif cmd->function (); return; } } // check alias for (a = cmd_alias ; a ; a = a->next) { if ( !q_strcasecmp(cmd_argv[0], a->name) ) { Cbuf_InsertText (a->value); return; } } // check cvars if (!Cvar_Command()) Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0)); } /* ================ Cmd_CheckParm Returns the position (1 to argc-1) in the command's argument list where the given parameter apears, or 0 if not present ================ */ int Cmd_CheckParm (const char *parm) { int i; if (!parm) Sys_Error ("%s: null input\n", __thisfunc__); for (i = 1; i < Cmd_Argc (); i++) if ( !q_strcasecmp(parm, Cmd_Argv (i)) ) return i; return 0; } #ifndef SERVERONLY /* =============== Cmd_List_f Lists the commands to the console =============== */ int ListCommands (const char *prefix, const char **buf, int pos) { cmd_function_t *cmd; int i = 0; int preLen = (prefix == NULL) ? 0 : strlen(prefix); for (cmd = cmd_functions ; cmd ; cmd = cmd->next) { if (!preLen) // completion procedures always send a prefix { Con_Printf (" %s\n", cmd->name); continue; } if ( !q_strncasecmp(prefix, cmd->name, preLen) ) { if (!buf) // completion procedures always send a buf { Con_Printf (" %s\n", cmd->name); continue; } if (pos+i < MAX_MATCHES) buf[pos+i] = cmd->name; else break; i++; } } return i; } static void Cmd_List_f(void) { if ( Cmd_Argc() > 1 ) ListCommands (Cmd_Argv(1), NULL, 0); else ListCommands (NULL, NULL, 0); } /* =============== Cmd_ListCvar_f Lists the cvars to the console =============== */ int ListCvars (const char *prefix, const char **buf, int pos) { cvar_t *var; int i = 0; int preLen = (prefix == NULL) ? 0 : strlen(prefix); var = Cvar_FindVarAfter ("", CVAR_NONE); for ( ; var ; var = var->next) { if (!preLen) // completion procedures always send a prefix { Con_Printf (" %s\n", var->name); continue; } if ( !q_strncasecmp(prefix, var->name, preLen) ) { if (!buf) // completion procedures always send a buf { Con_Printf (" %s\n", var->name); continue; } if (pos+i < MAX_MATCHES) buf[pos+i] = var->name; else break; i++; } } return i; } static void Cmd_ListCvar_f(void) { if ( Cmd_Argc() > 1 ) ListCvars (Cmd_Argv(1), NULL, 0); else ListCvars (NULL, NULL, 0); } /* =============== Cmd_ListAlias_f Lists the cvars to the console =============== */ int ListAlias (const char *prefix, const char **buf, int pos) { cmdalias_t *a; int i = 0; int preLen = (prefix == NULL) ? 0 : strlen(prefix); for (a = cmd_alias ; a ; a = a->next) { if (!preLen) // completion procedures always send a prefix { Con_Printf (" %s\n", a->name); continue; } if ( !q_strncasecmp(prefix, a->name, preLen) ) { if (!buf) // completion procedures always send a buf { Con_Printf (" %s\n", a->name); continue; } if (pos+i < MAX_MATCHES) buf[pos+i] = a->name; else break; i++; } } return i; } static void Cmd_ListAlias_f(void) { if ( Cmd_Argc() > 1 ) ListAlias (Cmd_Argv(1), NULL, 0); else ListAlias (NULL, NULL, 0); } static void Cmd_WriteCommands_f (void) { FILE *FH; cmd_function_t *cmd; cvar_t *var; cmdalias_t *a; FH = fopen(FS_MakePath(FS_USERDIR,NULL,"commands.txt"), "w"); if (!FH) { return; } fprintf(FH,"\n\nConsole Commands:\n"); for (cmd = cmd_functions ; cmd ; cmd = cmd->next) fprintf(FH, " %s\n", cmd->name); fprintf(FH,"\n\nAlias Commands:\n"); for (a = cmd_alias ; a ; a = a->next) fprintf(FH, " %s :\n\t%s\n", a->name, a->value); fprintf(FH,"\n\nConsole Variables:\n"); var = Cvar_FindVarAfter ("", CVAR_NONE); for ( ; var ; var = var->next) fprintf(FH, " %s\n", var->name); fclose(FH); } #endif /* ============ Cmd_Init ============ */ void Cmd_Init (void) { // // register our commands // Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f); Cmd_AddCommand ("exec",Cmd_Exec_f); Cmd_AddCommand ("echo",Cmd_Echo_f); Cmd_AddCommand ("alias",Cmd_Alias_f); Cmd_AddCommand ("unalias",Cmd_Unalias_f); Cmd_AddCommand ("unaliasall",Cmd_Unaliasall_f); Cmd_AddCommand ("wait", Cmd_Wait_f); #ifndef SERVERONLY Cmd_AddCommand ("commands", Cmd_WriteCommands_f); Cmd_AddCommand ("cmdlist", Cmd_List_f); Cmd_AddCommand ("cvarlist", Cmd_ListCvar_f); Cmd_AddCommand ("aliaslist", Cmd_ListAlias_f); #endif } engine/h2shared/cmd.h000066400000000000000000000117271444734033100147520ustar00rootroot00000000000000/* cmd.h - command buffer and command execution * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_CMD_H #define __HX2_CMD_H /* Any number of commands can be added in a frame, from several different sources. Most commands come from either keybindings or console line input, but remote servers can also send across commands and entire text files can be execed. The + command line options are also added to the command buffer. The game starts with a Cbuf_AddText ("exec quake.rc\n"); Cbuf_Execute (); */ void Cbuf_Init (void); // allocates an initial text buffer that will grow as needed void Cbuf_AddText (const char *text); // as new commands are generated from the console or keybindings, // the text is added to the end of the command buffer. void Cbuf_InsertText (const char *text); // when a command wants to issue other commands immediately, the text is // inserted at the beginning of the buffer, before any remaining unexecuted // commands. void Cbuf_Execute (void); // Pulls off \n terminated lines of text from the command buffer and sends // them through Cmd_ExecuteString. Stops when the buffer is empty. // Normally called once per frame, but may be explicitly invoked. // Do not call inside a command function! /* Command execution takes a null terminated string, breaks it into tokens, then searches for a command or variable that matches the first token. For Quake/Hexen2, commands can come from three sources, but the handler functions may choose to dissallow the action or forward it to a remote server if the source is not apropriate. On the other hand, QuakeWorld and HexenWorld forward the command to the server when the handler function is NULL. */ typedef void (*xcommand_t) (void); typedef enum { src_client, // came in over a net connection as a clc_stringcmd // host_client will be valid during this state. src_command // from the command buffer } cmd_source_t; extern cmd_source_t cmd_source; void Cmd_Init (void); void Cmd_AddCommand (const char *cmd_name, xcommand_t function); // called by the init functions of other parts of the program to // register commands and functions to call for them. // The cmd_name is referenced later, so it should not be in temp memory // QuakeWorld/HexenWorld allows that the function be NULL and in that // case the command will be forwarded to the server as a clc_stringcmd // instead of being executed locally qboolean Cmd_Exists (const char *cmd_name); // used by the cvar code to check for cvar / command name overlap qboolean Cmd_CheckCommand (const char *partial); // attempts to match a given text to known commands, cvars or aliases // returns true if there is an exact match, false otherwise void Cmd_MoveToFront (const char *cmd_name); // move commands to the head of the list for faster access int Cmd_Argc (void); const char *Cmd_Argv (int arg); const char *Cmd_Args (void); // The functions that execute commands get their parameters with these // functions. Cmd_Argv () will return an empty string, not a NULL // if arg > argc, so string operations are always safe. int Cmd_CheckParm (const char *parm); // Returns the position (1 to argc-1) in the command's argument list // where the given parameter apears, or 0 if not present void Cmd_TokenizeString (const char *text); // Takes a null terminated string. Does not need to be /n terminated. // breaks the string up into arg tokens. void Cmd_ExecuteString (const char *text, cmd_source_t src); // Parses a single line of text into arguments and tries to execute it // as if it was typed at the console // Quake/Hexen II behaves differently to command text coming from the // command buffer, a remote client, or stdin (the src argument). For QW // and H2W, the src argument is for compatibility only. void Cmd_StuffCmds_f (void); // Executes the commandline parameters with a leading "+" as script // statements. int ListCommands (const char *prefix, const char **buf, int pos); int ListCvars (const char *prefix, const char **buf, int pos); int ListAlias (const char *prefix, const char **buf, int pos); // These three listers are either used privately in the respective // list commands, or by keys.c in console tab-completion. They // return the number of matches. #define MAX_MATCHES 128 // maximum number of matches for console tab completion #endif /* __HX2_CMD_H */ engine/h2shared/common.c000066400000000000000000000257371444734033100155000ustar00rootroot00000000000000/* * common.c -- misc utility functions used in client and server * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2008-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "q_ctype.h" #include "filenames.h" int safemode; /* ============================================================================ REPLACEMENT FUNCTIONS ============================================================================ */ int q_strcasecmp(const char * s1, const char * s2) { const char * p1 = s1; const char * p2 = s2; char c1, c2; if (p1 == p2) return 0; do { c1 = q_tolower (*p1++); c2 = q_tolower (*p2++); if (c1 == '\0') break; } while (c1 == c2); return (int)(c1 - c2); } int q_strncasecmp(const char *s1, const char *s2, size_t n) { const char * p1 = s1; const char * p2 = s2; char c1, c2; if (p1 == p2 || n == 0) return 0; do { c1 = q_tolower (*p1++); c2 = q_tolower (*p2++); if (c1 == '\0' || c1 != c2) break; } while (--n > 0); return (int)(c1 - c2); } char *q_strlwr (char *str) { char *c; c = str; while (*c) { *c = q_tolower(*c); c++; } return str; } char *q_strupr (char *str) { char *c; c = str; while (*c) { *c = q_toupper(*c); c++; } return str; } #ifdef __DJGPP__ /* override stock DJGPP versions of str[n]icmp by our q_str[n]casecmp: */ #ifdef __cplusplus extern "C" { #endif int __stricmp(const char *, const char *) __attribute__((alias("q_strcasecmp"))); int stricmp(const char *, const char *) __attribute__((alias("q_strcasecmp"))); int strcasecmp(const char *, const char *) __attribute__((alias("q_strcasecmp"))); int __strnicmp(const char *, const char *, size_t) __attribute__((alias("q_strncasecmp"))); int strnicmp(const char *, const char *, size_t) __attribute__((alias("q_strncasecmp"))); int strncasecmp(const char *, const char *, size_t) __attribute__((alias("q_strncasecmp"))); char *strlwr(char *) __attribute__((alias("q_strlwr"))); char *strupr(char *) __attribute__((alias("q_strlwr"))); #ifdef __cplusplus } #endif #endif size_t qerr_strlcat (const char *caller, int linenum, char *dst, const char *src, size_t size) { size_t ret = q_strlcat (dst, src, size); if (ret >= size) Sys_Error("%s: %d: string buffer overflow!", caller, linenum); return ret; } size_t qerr_strlcpy (const char *caller, int linenum, char *dst, const char *src, size_t size) { size_t ret = q_strlcpy (dst, src, size); if (ret >= size) Sys_Error("%s: %d: string buffer overflow!", caller, linenum); return ret; } int qerr_snprintf (const char *caller, int linenum, char *str, size_t size, const char *format, ...) { int ret; va_list argptr; va_start (argptr, format); ret = q_vsnprintf (str, size, format, argptr); va_end (argptr); if ((size_t)ret >= size) Sys_Error("%s: %d: string buffer overflow!", caller, linenum); return ret; } /* ============================================================================ MISC UTILITY FUNCTIONS ============================================================================ */ /* ============ va does a varargs printf into a temp buffer. cycles between 4 different static buffers. the number of buffers cycled is defined in VA_NUM_BUFFS. ============ */ #define VA_NUM_BUFFS 4 #define VA_BUFFERLEN 1024 static char *get_va_buffer(void) { static char va_buffers[VA_NUM_BUFFS][VA_BUFFERLEN]; static int buffer_idx = 0; buffer_idx = (buffer_idx + 1) & (VA_NUM_BUFFS - 1); return va_buffers[buffer_idx]; } char *va (const char *format, ...) { va_list argptr; char *va_buf; va_buf = get_va_buffer (); va_start (argptr, format); if (q_vsnprintf(va_buf, VA_BUFFERLEN, format, argptr) >= VA_BUFFERLEN) Con_DPrintf("%s: overflow (string truncated)\n", __thisfunc__); va_end (argptr); return va_buf; } /*============================================================================ quick'n'dirty string comparison function for use with qsort ============================================================================*/ int COM_StrCompare (const void *arg1, const void *arg2) { return q_strcasecmp ( *(char **) arg1, *(char **) arg2); } /*============================================================================ FileName Processing utilities ============================================================================*/ /* ============ COM_SkipPath ============ */ const char *COM_SkipPath (const char *pathname) { const char *last; last = pathname; while (*pathname) { if (IS_DIR_SEPARATOR(*pathname)) last = pathname + 1; pathname++; } return last; } /* ============ COM_StripExtension ============ */ void COM_StripExtension (const char *in, char *out, size_t outsize) { int length; if (!*in) { *out = '\0'; return; } if (in != out) /* copy when not in-place editing */ q_strlcpy (out, in, outsize); length = (int)strlen(out) - 1; while (length > 0 && out[length] != '.') { --length; if (IS_DIR_SEPARATOR(out[length])) return; /* no extension */ } if (length > 0) out[length] = '\0'; } /* ============ COM_FileGetExtension - doesn't return NULL ============ */ const char *COM_FileGetExtension (const char *in) { const char *src; size_t len; len = strlen(in); if (len < 2) /* nothing meaningful */ return ""; src = in + len - 1; while (src != in && src[-1] != '.') src--; if (src == in || FIND_FIRST_DIRSEP(src) != NULL || HAS_DRIVE_SPEC(src)) return ""; /* no extension, or parent directory has a dot */ return src; } /* ============ COM_ExtractExtension ============ */ void COM_ExtractExtension (const char *in, char *out, size_t outsize) { const char *ext = COM_FileGetExtension (in); if (! *ext) *out = '\0'; else q_strlcpy (out, ext, outsize); } /* ============ COM_FileBase take 'somedir/otherdir/filename.ext', write only 'filename' to the output ============ */ void COM_FileBase (const char *in, char *out, size_t outsize) { COM_StripExtension (COM_SkipPath(in), out, outsize); } /* ================== COM_DefaultExtension if path doesn't have a .EXT, append extension (extension should include the leading ".") ================== */ #if 0 /* can be dangerous */ void COM_DefaultExtension (char *path, const char *extension, size_t len) { char *src; if (!*path) return; src = path + strlen(path) - 1; while (!IS_DIR_SEPARATOR(*src) && src != path) { if (*src == '.') return; // it has an extension src--; } qerr_strlcat(__thisfunc__, __LINE__, path, extension, len); } #endif /* ================== COM_AddExtension if path extension doesn't match .EXT, append it (extension should include the leading ".") ================== */ void COM_AddExtension (char *path, const char *extension, size_t len) { if (strcmp(COM_FileGetExtension(path), extension + 1) != 0) qerr_strlcat(__thisfunc__, __LINE__, path, extension, len); } /* ============================================================================ STRING PARSING FUNCTIONS ============================================================================ */ char com_token[1024]; /* ============== COM_Parse Parse a token out of a string ============== */ const char *COM_Parse (const char *data) { int c; int len; len = 0; com_token[0] = 0; if (!data) return NULL; // skip whitespace skipwhite: while ((c = *data) <= ' ') { if (c == 0) return NULL; // end of file data++; } // skip // comments if (c == '/' && data[1] == '/') { while (*data && *data != '\n') data++; goto skipwhite; } // skip /*..*/ comments if (c == '/' && data[1] == '*') { data += 2; while (*data && !(*data == '*' && data[1] == '/')) data++; if (*data) data += 2; goto skipwhite; } // handle quoted strings specially if (c == '\"') { data++; while (1) { if ((c = *data) != 0) ++data; if (c == '\"' || !c) { com_token[len] = 0; return data; } com_token[len] = c; len++; } } #if 0 // parse single characters if (c == '{' || c == '}' || c == '(' || c == ')' || c == '\'' || c == ':') { com_token[len] = c; len++; com_token[len] = 0; return data+1; } #endif // parse a regular word do { com_token[len] = c; data++; len++; c = *data; #if 0 if (c == '{' || c == '}' || c == '(' || c == ')' || c == '\'' || c == ':') break; #endif } while (c > 32); com_token[len] = 0; return data; } /* ============================================================================ COMMAND LINE PROCESSING FUNCTIONS ============================================================================ */ /* ================ COM_CheckParm Returns the position (1 to argc-1) in the program's argument list where the given parameter apears, or 0 if not present ================ */ int COM_CheckParm (const char *parm) { int i; for (i = 1; i < com_argc; i++) { if (!com_argv[i]) continue; // NEXTSTEP sometimes clears appkit vars. if (!strcmp (parm,com_argv[i])) return i; } return 0; } static void COM_Cmdline_f (void) { int i; Con_Printf ("cmdline was:"); for (i = 0; (i < MAX_NUM_ARGVS) && (i < com_argc); i++) { if (com_argv[i]) Con_Printf (" %s", com_argv[i]); } Con_Printf ("\n"); } /* ============================================================================ INIT, ETC.. ============================================================================ */ void COM_ValidateByteorder (void) { const char *endianism[] = { "BE", "LE", "PDP", "Unknown" }; const char *tmp; ByteOrder_Init (); switch (host_byteorder) { case BIG_ENDIAN: tmp = endianism[0]; break; case LITTLE_ENDIAN: tmp = endianism[1]; break; case PDP_ENDIAN: tmp = endianism[2]; host_byteorder = -1; /* not supported */ break; default: tmp = endianism[3]; break; } if (host_byteorder < 0) Sys_Error ("%s: Unsupported byte order [%s]", __thisfunc__, tmp); Sys_Printf("Detected byte order: %s\n", tmp); #if !ENDIAN_RUNTIME_DETECT if (host_byteorder != BYTE_ORDER) { const char *tmp2; switch (BYTE_ORDER) { case BIG_ENDIAN: tmp2 = endianism[0]; break; case LITTLE_ENDIAN: tmp2 = endianism[1]; break; case PDP_ENDIAN: tmp2 = endianism[2]; break; default: tmp2 = endianism[3]; break; } Sys_Error ("Detected byte order %s doesn't match compiled %s order!", tmp, tmp2); } #endif /* ENDIAN_RUNTIME_DETECT */ } /* ================ COM_Init ================ */ void COM_Init (void) { Cmd_AddCommand ("cmdline", COM_Cmdline_f); safemode = COM_CheckParm ("-safe"); } engine/h2shared/common.h000066400000000000000000000104441444734033100154720ustar00rootroot00000000000000/* * common.h -- misc utilities used in client and server * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2008-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_COMMON_H #define __HX2_COMMON_H #undef min #undef max #define q_min(a, b) (((a) < (b)) ? (a) : (b)) #define q_max(a, b) (((a) > (b)) ? (a) : (b)) #if defined(PLATFORM_WINDOWS) && !defined(F_OK) /* constants for access() mode argument. MS does not define them. * Note that X_OK (0x01) must not be used in windows code. */ #define R_OK 4 /* Test for read permission. */ #define W_OK 2 /* Test for write permission. */ #define X_OK 1 /* Test for execute permission. */ #define F_OK 0 /* Test for existence. */ #endif #ifdef _MSC_VER /* MS Visual C */ /* disable some silent conversion warnings */ # pragma warning(disable:4244) /* 'argument' : conversion from 'type1' to 'type2', possible loss of data */ # pragma warning(disable:4305) /* 'identifier' : truncation from 'type1' to 'type2' */ /* in our case, truncation from 'double' to 'float' */ # pragma warning(disable:4267) /* 'var' : conversion from 'size_t' to 'type', possible loss of data (/Wp64 warning) */ #endif #ifdef __cplusplus extern "C" { #endif /* these qerr_ versions of functions error out if they detect, well, an error. * their first two arguments must the name of the caller function (see compiler.h * for the __thisfunc__ macro) and the line number, which should be __LINE__ . */ extern size_t qerr_strlcat (const char *caller, int linenum, char *dst, const char *src, size_t size); extern size_t qerr_strlcpy (const char *caller, int linenum, char *dst, const char *src, size_t size); extern int qerr_snprintf (const char *caller, int linenum, char *str, size_t size, const char *format, ...) FUNC_PRINTF(5,6); /* locale-insensitive strcasecmp replacement functions: */ extern int q_strcasecmp (const char * s1, const char * s2); extern int q_strncasecmp (const char *s1, const char *s2, size_t n); /* locale-insensitive strlwr/upr replacement functions: */ extern char *q_strlwr (char *str); extern char *q_strupr (char *str); #ifdef __cplusplus } #endif /*============================================================================*/ extern char com_token[1024]; extern qboolean com_eof; const char *COM_Parse (const char *data); extern int safemode; /* safe mode: if true, the engine will behave as if one of these arguments were actually on the command line: -nosound, -nocdaudio, -nomidi, -stdvid, -dibonly, -nomouse, -nojoy, -nolan */ void COM_Init (void); int COM_CheckParm (const char *parm); /* macros for compatibility with quake api */ #define com_argc host_parms->argc #define com_argv host_parms->argv void COM_ValidateByteorder (void); const char *COM_SkipPath (const char *pathname); void COM_StripExtension (const char *in, char *out, size_t outsize); const char *COM_FileGetExtension (const char *in); /* doesn't return NULL */ void COM_ExtractExtension (const char *in, char *out, size_t outsize); void COM_FileBase (const char *in, char *out, size_t outsize); void COM_AddExtension (char *path, const char *extension, size_t len); #if 0 /* COM_DefaultExtension can be dangerous */ void COM_DefaultExtension (char *path, const char *extension, size_t len); #endif char *va (const char *format, ...) FUNC_PRINTF(1,2); /* does a varargs printf into a temp buffer. cycles between * 4 different static buffers. the number of buffers cycled * is defined in VA_NUM_BUFFS. */ int COM_StrCompare (const void *arg1, const void *arg2); /* quick'n'dirty string comparison function for use with qsort */ #endif /* __HX2_COMMON_H */ engine/h2shared/console.h000066400000000000000000000034041444734033100156420ustar00rootroot00000000000000/* console.h -- the game console * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __CONSOLE_H #define __CONSOLE_H #define CON_TEXTSIZE 16384 typedef struct { short text[CON_TEXTSIZE]; int current; // line where next message will be printed int x; // offset in current line for next print int display; // bottom of console displays this line } console_t; extern console_t *con; extern int con_ormask; extern int con_totallines; extern qboolean con_forcedup; // because no entities to refresh extern qboolean con_initialized; extern byte *con_chars; extern int con_notifylines; // scan lines to clear for notify lines void Con_DrawCharacter (int cx, int line, int num); void Con_CheckResize (void); void Con_Init (void); void Con_DrawConsole (int lines); void Con_ShowList (int , const char **); void Con_DrawNotify (void); void Con_ClearNotify (void); void Con_ToggleConsole_f (void); void Con_NotifyBox (const char *text); // during startup for sound / cd warnings #endif /* __CONSOLE_H */ engine/h2shared/cvar.c000066400000000000000000000167621444734033100151410ustar00rootroot00000000000000/* * cvar.c -- dynamic variable tracking * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2008-2010 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" static cvar_t *cvar_vars; static char cvar_null_string[] = ""; /* ============ Cvar_FindVar ============ */ cvar_t *Cvar_FindVar (const char *var_name) { cvar_t *var; for (var = cvar_vars ; var ; var = var->next) { if (!strcmp (var_name, var->name)) return var; } return NULL; } cvar_t *Cvar_FindVarAfter (const char *prev_name, unsigned int with_flags) { cvar_t *var; if (*prev_name) { var = Cvar_FindVar (prev_name); if (!var) return NULL; var = var->next; } else var = cvar_vars; // search for the next cvar matching the needed flags while (var) { if ((var->flags & with_flags) || !with_flags) break; var = var->next; } return var; } /* ============ Cvar_LockVar used for preventing the early-read cvar values to get over- written by the actual final read of config.cfg (which will be the case when commandline overrides were used): mark them as locked until Host_Init() completely finishes its job. this is a temporary solution until we adopt a better init sequence employing the +set arguments like those in quake2/3. ============ */ void Cvar_LockVar (const char *var_name) { cvar_t *var = Cvar_FindVar (var_name); if (var) var->flags |= CVAR_LOCKED; } void Cvar_UnlockVar (const char *var_name) { cvar_t *var = Cvar_FindVar (var_name); if (var) var->flags &= ~CVAR_LOCKED; } void Cvar_UnlockAll (void) { cvar_t *var; for (var = cvar_vars ; var ; var = var->next) { var->flags &= ~CVAR_LOCKED; } } /* ============ Cvar_VariableValue ============ */ float Cvar_VariableValue (const char *var_name) { cvar_t *var; var = Cvar_FindVar (var_name); if (!var) return 0; return atof (var->string); } /* ============ Cvar_VariableString ============ */ const char *Cvar_VariableString (const char *var_name) { cvar_t *var; var = Cvar_FindVar (var_name); if (!var) return cvar_null_string; return var->string; } void Cvar_SetQuick (cvar_t *var, const char *value) { if (var->flags & (CVAR_ROM|CVAR_LOCKED)) return; if (!(var->flags & CVAR_REGISTERED)) return; if (!var->string) var->string = Z_Strdup (value); else { size_t len; // If no change, then DON'T do anything at all. // Some, if not all, of the cvar callbacks may // actually rely on this behavior!!! if (!strcmp(var->string, value)) return; var->flags |= CVAR_CHANGED; len = strlen (value); if (len != strlen(var->string)) { Z_Free ((void *)var->string); var->string = (char *) Z_Malloc (len + 1, Z_MAINZONE); } memcpy ((char *)var->string, value, len + 1); } var->value = atof (var->string); var->integer = (int) var->value; if (var->callback) var->callback (var); } void Cvar_SetValueQuick (cvar_t *var, const float value) { char val[32], *ptr = val; if (value == (float)((int)value)) q_snprintf (val, sizeof(val), "%i", (int)value); else { q_snprintf (val, sizeof(val), "%f", value); // kill trailing zeroes while (*ptr) ptr++; while (--ptr > val && *ptr == '0' && ptr[-1] != '.') *ptr = '\0'; } Cvar_SetQuick (var, val); } /* ============ Cvar_Set ============ */ void Cvar_Set (const char *var_name, const char *value) { cvar_t *var; var = Cvar_FindVar (var_name); if (!var) { // there is an error in C code if this happens Con_Printf ("%s: variable %s not found\n", __thisfunc__, var_name); return; } Cvar_SetQuick (var, value); } /* ============ Cvar_SetValue ============ */ void Cvar_SetValue (const char *var_name, const float value) { char val[32], *ptr = val; if (value == (float)((int)value)) q_snprintf (val, sizeof(val), "%i", (int)value); else { q_snprintf (val, sizeof(val), "%f", value); // kill trailing zeroes while (*ptr) ptr++; while (--ptr > val && *ptr == '0' && ptr[-1] != '.') *ptr = '\0'; } Cvar_Set (var_name, val); } /* ============ Cvar_SetROM ============ */ void Cvar_SetROM (const char *var_name, const char *value) { cvar_t *var = Cvar_FindVar (var_name); if (var) { var->flags &= ~CVAR_ROM; Cvar_SetQuick (var, value); var->flags |= CVAR_ROM; } } /* ============ Cvar_SetValueROM ============ */ void Cvar_SetValueROM (const char *var_name, const float value) { cvar_t *var = Cvar_FindVar (var_name); if (var) { var->flags &= ~CVAR_ROM; Cvar_SetValueQuick (var, value); var->flags |= CVAR_ROM; } } /* ============ Cvar_RegisterVariable Adds a freestanding variable to the variable list. ============ */ void Cvar_RegisterVariable (cvar_t *variable) { char value[512]; qboolean set_rom; // first check to see if it has already been defined if (Cvar_FindVar (variable->name)) { Con_Printf ("Can't register variable %s, already defined\n", variable->name); return; } // check for overlap with a command if (Cmd_Exists (variable->name)) { Con_Printf ("%s: %s is a command\n", __thisfunc__, variable->name); return; } // link the variable in variable->next = cvar_vars; cvar_vars = variable; variable->flags |= CVAR_REGISTERED; // copy the value off, because future sets will Z_Free it q_strlcpy (value, variable->string, sizeof(value)); variable->string = NULL; if (!(variable->flags & CVAR_CALLBACK)) variable->callback = NULL; // set it through the function to be consistent set_rom = (variable->flags & CVAR_ROM); variable->flags &= ~CVAR_ROM; Cvar_SetQuick (variable, value); if (set_rom) variable->flags |= CVAR_ROM; } /* ============ Cvar_SetCallback Set a callback function to the var ============ */ void Cvar_SetCallback (cvar_t *var, cvarcallback_t func) { var->callback = func; if (func) var->flags |= CVAR_CALLBACK; else var->flags &= ~CVAR_CALLBACK; } /* ============ Cvar_Command Handles variable inspection and changing from the console ============ */ qboolean Cvar_Command (void) { cvar_t *v; // check variables v = Cvar_FindVar (Cmd_Argv(0)); if (!v) return false; // perform a variable print or set if (Cmd_Argc() == 1) { Con_Printf ("\"%s\" is \"%s\"\n", v->name, v->string); return true; } Cvar_Set (v->name, Cmd_Argv(1)); return true; } /* ============ Cvar_MoveToFront ============ */ void Cvar_MoveToFront (const char *name) { cvar_t *var, *next; for (var = cvar_vars; var; var = var->next) { next = var->next; if (next && !strcmp(name, next->name)) { // remove from the list var->next = next->next; // move to the front next->next = cvar_vars; cvar_vars = next; break; } } } /* ============ Cvar_WriteVariables Writes lines containing "set variable value" for all variables with the archive flag set to true. ============ */ void Cvar_WriteVariables (FILE *f) { cvar_t *var; for (var = cvar_vars ; var ; var = var->next) { if (var->flags & CVAR_ARCHIVE) fprintf (f, "%s \"%s\"\n", var->name, var->string); } } engine/h2shared/cvar.h000066400000000000000000000114651444734033100151410ustar00rootroot00000000000000/* * cvar.h -- dynamic variable tracking * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2008-2010 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __CVAR_H__ #define __CVAR_H__ /* cvar_t variables are used to hold scalar or string variables that can be changed or displayed at the console or prog code as well as accessed directly in C code. it is sufficient to initialize a cvar_t with just the first two fields, or you can add a ,true flag for variables that you want saved to the configuration file when the game is quit: cvar_t r_draworder = {"r_draworder","1"}; cvar_t scr_screensize = {"screensize","1",true}; Cvars must be registered before use, or they will have a 0 value instead of the float interpretation of the string. Generally, all cvar_t declarations should be registered in the apropriate init function before any console commands are executed: Cvar_RegisterVariable (&host_framerate); C code usually just references a cvar in place: if ( r_draworder.value ) It could optionally ask for the value to be looked up for a string name: if (Cvar_VariableValue ("r_draworder")) Interpreted prog code can access cvars with the cvar(name) or cvar_set (name, value) internal functions: teamplay = cvar("teamplay"); cvar_set ("registered", "1"); The user can access cvars from the console in two ways: r_draworder prints the current value r_draworder 0 sets the current value to 0 Cvars are restricted from having the same names as commands to keep this interface from being ambiguous. */ #define CVAR_NONE 0 #define CVAR_ARCHIVE (1U << 0) // if set, causes it to be saved to config #define CVAR_NOTIFY (1U << 1) // changes will be broadcasted to all players (H2) #define CVAR_SERVERINFO (1U << 2) // added to serverinfo will be sent to clients (H2/net_dgrm.c and H2W) #define CVAR_USERINFO (1U << 3) // added to userinfo, will be sent to server (H2W) #define CVAR_CHANGED (1U << 4) #define CVAR_ROM (1U << 6) #define CVAR_LOCKED (1U << 8) // locked temporarily #define CVAR_REGISTERED (1U << 10) // the var is added to the list of variables #define CVAR_CALLBACK (1U << 16) // var has a callback typedef void (*cvarcallback_t) (struct cvar_s *); typedef struct cvar_s { const char *name; const char *string; unsigned int flags; float value; int integer; cvarcallback_t callback; struct cvar_s *next; } cvar_t; void Cvar_RegisterVariable (cvar_t *variable); // registers a cvar that already has the name, string, and optionally // the archive elements set. void Cvar_SetCallback (cvar_t *var, cvarcallback_t func); // set a callback function to the var void Cvar_Set (const char *var_name, const char *value); // equivelant to " " typed at the console void Cvar_SetValue (const char *var_name, const float value); // expands value to a string and calls Cvar_Set void Cvar_SetROM (const char *var_name, const char *value); void Cvar_SetValueROM (const char *var_name, const float value); // sets a CVAR_ROM variable from within the engine void Cvar_SetQuick (cvar_t *var, const char *value); void Cvar_SetValueQuick (cvar_t *var, const float value); // these two accept a cvar pointer instead of a var name, // but are otherwise identical to the "non-Quick" versions. // the cvar MUST be registered. float Cvar_VariableValue (const char *var_name); // returns 0 if not defined or non numeric const char *Cvar_VariableString (const char *var_name); // returns an empty string if not defined qboolean Cvar_Command (void); // called by Cmd_ExecuteString when Cmd_Argv(0) doesn't match a known // command. Returns true if the command was a variable reference that // was handled. (print or change) void Cvar_MoveToFront (const char *var_name); // move variables to the head of the list for faster access void Cvar_WriteVariables (FILE *f); // Writes lines containing "set variable value" for all variables // with the CVAR_ARCHIVE flag set cvar_t *Cvar_FindVar (const char *var_name); cvar_t *Cvar_FindVarAfter (const char *prev_name, unsigned int with_flags); void Cvar_LockVar (const char *var_name); void Cvar_UnlockVar (const char *var_name); void Cvar_UnlockAll (void); #endif /* __CVAR_H__ */ engine/h2shared/d_copy.asm000066400000000000000000000072251444734033100160130ustar00rootroot00000000000000; ; d_copy.asm ; x86 assembly-language screen copying code. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix VGA_bufferrowbytes _sym_prefix VGA_rowbytes _sym_prefix VGA_pagebase _sym_prefix VGA_height _sym_prefix VGA_width ; C-shared globals: _sym_prefix VGA_UpdatePlanarScreen _sym_prefix VGA_UpdateLinearScreen %endif ; _sym_prefix ; externs from C code ; extern VGA_bufferrowbytes extern VGA_rowbytes extern VGA_pagebase extern VGA_height extern VGA_width ; externs from ASM-only code ; SEGMENT .data LCopyWidth dd 0 LBlockSrcStep dd 0 LBlockDestStep dd 0 LSrcDelta dd 0 LDestDelta dd 0 ; copies 16 rows per plane at a pop; idea is that 16*512 = 8k, and since ; no Mode X mode is wider than 360, all the data should fit in the cache for ; the passes for the next 3 planes SEGMENT .text ; void VGA_UpdatePlanarScreen(void *bufptr); global VGA_UpdatePlanarScreen VGA_UpdatePlanarScreen: push ebp ; preserve caller's stack frame push edi push esi ; preserve register variables push ebx mov eax, [VGA_bufferrowbytes] shl eax,1 mov [LBlockSrcStep],eax mov eax, [VGA_rowbytes] shl eax,1 mov [LBlockDestStep],eax mov edx,03C4h mov al,2 out dx,al ; point the SC to the Map Mask inc edx mov esi, [esp+4+16] ; bufptr mov edi, [VGA_pagebase] mov ebp, [VGA_height] shr ebp,1 mov ecx, [VGA_width] mov eax, [VGA_bufferrowbytes] sub eax,ecx mov [LSrcDelta],eax mov eax, [VGA_rowbytes] shl eax,2 sub eax,ecx mov [LDestDelta],eax shr ecx,4 mov [LCopyWidth],ecx LRowLoop: mov al,1 LPlaneLoop: out dx,al mov ah,2 push esi push edi LRowSetLoop: mov ecx, [LCopyWidth] LColumnLoop: mov bh, [esi+12] mov bl, [esi+8] shl ebx,16 mov bh, [esi+4] mov bl, [esi] mov [edi],ebx add esi,16 add edi,4 dec ecx jnz LColumnLoop add edi, [LDestDelta] add esi, [LSrcDelta] dec ah jnz LRowSetLoop pop edi pop esi inc esi shl al,1 cmp al,16 jnz LPlaneLoop sub esi,4 add esi, [LBlockSrcStep] add edi, [LBlockDestStep] dec ebp jnz LRowLoop pop ebx ; restore register variables pop esi pop edi pop ebp ; restore the caller's stack frame ret ; void VGA_UpdateLinearScreen( void *srcptr, void *destptr, ; int width, int height, ; int srcrowbytes, int destrowbytes); global VGA_UpdateLinearScreen VGA_UpdateLinearScreen: push ebp ; preserve caller's stack frame push edi push esi ; preserve register variables push ebx cld mov esi, [esp+4+16] ; srcptr mov edi, [esp+8+16] ; destptr mov ebx, [esp+12+16] ; width mov eax, [esp+20+16] ; srcrowbytes sub eax,ebx mov edx, [esp+24+16] ; destrowbytes sub edx,ebx shr ebx,2 mov ebp, [esp+16+16] ; height LLRowLoop: mov ecx,ebx rep movsd ; rep/movsl (%esi),(%edi) add esi,eax add edi,edx dec ebp jnz LLRowLoop pop ebx ; restore register variables pop esi pop edi pop ebp ; restore the caller's stack frame ret engine/h2shared/d_draw.asm000066400000000000000000000425111444734033100157730ustar00rootroot00000000000000; ; d_draw.asm ; x86 assembly-language horizontal 8-bpp span-drawing code. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix d_zistepu _sym_prefix d_pzbuffer _sym_prefix d_zistepv _sym_prefix d_zrowbytes _sym_prefix d_ziorigin _sym_prefix d_sdivzstepu _sym_prefix d_tdivzstepu _sym_prefix d_sdivzstepv _sym_prefix d_tdivzstepv _sym_prefix d_sdivzorigin _sym_prefix d_tdivzorigin _sym_prefix sadjust _sym_prefix tadjust _sym_prefix bbextents _sym_prefix bbextentt _sym_prefix cacheblock _sym_prefix d_viewbuffer _sym_prefix cachewidth _sym_prefix d_scantable _sym_prefix scanList _sym_prefix ZScanCount ; C-shared globals: _sym_prefix D_DrawSpans8 _sym_prefix D_DrawZSpans _sym_prefix D_DrawSingleZSpans %endif ; _sym_prefix ; externs from C code extern d_zistepu extern d_pzbuffer extern d_zistepv extern d_zrowbytes extern d_ziorigin extern d_sdivzstepu extern d_tdivzstepu extern d_sdivzstepv extern d_tdivzstepv extern d_sdivzorigin extern d_tdivzorigin extern sadjust extern tadjust extern bbextents extern bbextentt extern cacheblock extern d_viewbuffer extern cachewidth extern d_scantable extern scanList extern ZScanCount ; externs from ASM-only code extern float_point5 extern Float2ToThe31nd extern izistep extern izi extern FloatMinus2ToThe31nd extern float_1 extern float_particle_z_clip extern float_minus_1 extern float_0 extern fp_16 extern fp_64k extern fp_1m extern fp_1m_minus_1 extern fp_8 extern entryvec_table extern advancetable extern sstep extern tstep extern pspantemp extern counttemp extern jumptemp extern reciprocal_table extern pbase extern s extern t extern sfracf extern tfracf extern snext extern tnext extern spancountminus1 extern zi16stepu extern sdivz16stepu extern tdivz16stepu extern zi8stepu extern sdivz8stepu extern tdivz8stepu extern reciprocal_table_16 extern entryvec_table_16 extern fp_64kx64k SEGMENT .text ;;;;;;;;;;;;;;;;;;;;;;;; ; D_DrawSpans8 ;;;;;;;;;;;;;;;;;;;;;;;; LClampHigh0: mov esi, dword [bbextents] jmp LClampReentry0 LClampHighOrLow0: jg LClampHigh0 xor esi,esi jmp LClampReentry0 LClampHigh1: mov edx, dword [bbextentt] jmp LClampReentry1 LClampHighOrLow1: jg LClampHigh1 xor edx,edx jmp LClampReentry1 LClampLow2: mov ebp,2048 jmp LClampReentry2 LClampHigh2: mov ebp, dword [bbextents] jmp LClampReentry2 LClampLow3: mov ecx,2048 jmp LClampReentry3 LClampHigh3: mov ecx, dword [bbextentt] jmp LClampReentry3 LClampLow4: mov eax,2048 jmp LClampReentry4 LClampHigh4: mov eax, dword [bbextents] jmp LClampReentry4 LClampLow5: mov ebx,2048 jmp LClampReentry5 LClampHigh5: mov ebx, dword [bbextentt] jmp LClampReentry5 ALIGN 4 global D_DrawSpans8 D_DrawSpans8: push ebp push edi push esi push ebx fld dword [d_sdivzstepu] fmul dword [fp_8] mov edx, dword [cacheblock] fld dword [d_tdivzstepu] fmul dword [fp_8] mov ebx, dword [4+16+esp] fld dword [d_zistepu] fmul dword [fp_8] mov dword [pbase],edx fstp dword [zi8stepu] fstp dword [tdivz8stepu] fstp dword [sdivz8stepu] LSpanLoop: fild dword [4+ebx] fild dword [0+ebx] fld st1 fmul dword [d_sdivzstepv] fld st1 fmul dword [d_sdivzstepu] fld st2 fmul dword [d_tdivzstepu] fxch st1 faddp st2,st0 fxch st1 fld st3 fmul dword [d_tdivzstepv] fxch st1 fadd dword [d_sdivzorigin] fxch st4 fmul dword [d_zistepv] fxch st1 faddp st2,st0 fxch st2 fmul dword [d_zistepu] fxch st1 fadd dword [d_tdivzorigin] fxch st2 faddp st1,st0 fld dword [fp_64k] fxch st1 fadd dword [d_ziorigin] fdiv st1,st0 mov ecx, dword [d_viewbuffer] mov eax, dword [4+ebx] mov dword [pspantemp],ebx mov edx, dword [tadjust] mov esi, dword [sadjust] mov edi, dword [d_scantable+eax*4] add edi,ecx mov ecx, dword [0+ebx] add edi,ecx mov ecx, dword [8+ebx] cmp ecx,8 ja LSetupNotLast1 dec ecx jz LCleanup1 mov dword [spancountminus1],ecx fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] fild dword [spancountminus1] fld dword [d_tdivzstepu] fld dword [d_zistepu] fmul st0,st2 fxch st1 fmul st0,st2 fxch st2 fmul dword [d_sdivzstepu] fxch st1 faddp st3,st0 fxch st1 faddp st3,st0 faddp st3,st0 fld dword [fp_64k] fdiv st0,st1 jmp LFDIVInFlight1 LCleanup1: fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] jmp LFDIVInFlight1 ALIGN 4 LSetupNotLast1: fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] fadd dword [zi8stepu] fxch st2 fadd dword [sdivz8stepu] fxch st2 fld dword [tdivz8stepu] faddp st2,st0 fld dword [fp_64k] fdiv st0,st1 LFDIVInFlight1: add esi, dword [s] add edx, dword [t] mov ebx, dword [bbextents] mov ebp, dword [bbextentt] cmp esi,ebx ja near LClampHighOrLow0 LClampReentry0: mov dword [s],esi mov ebx, dword [pbase] shl esi,16 cmp edx,ebp mov dword [sfracf],esi ja near LClampHighOrLow1 LClampReentry1: mov dword [t],edx mov esi, dword [s] shl edx,16 mov eax, dword [t] sar esi,16 mov dword [tfracf],edx sar eax,16 mov edx, dword [cachewidth] imul eax,edx add esi,ebx add esi,eax cmp ecx,8 jna near LLastSegment LNotLastSegment: fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [snext] fistp dword [tnext] mov eax, dword [snext] mov edx, dword [tnext] mov bl, byte [esi] sub ecx,8 mov ebp, dword [sadjust] mov dword [counttemp],ecx mov ecx, dword [tadjust] mov byte [edi],bl add ebp,eax add ecx,edx mov eax, dword [bbextents] mov edx, dword [bbextentt] cmp ebp,2048 jl near LClampLow2 cmp ebp,eax ja near LClampHigh2 LClampReentry2: cmp ecx,2048 jl near LClampLow3 cmp ecx,edx ja near LClampHigh3 LClampReentry3: mov dword [snext],ebp mov dword [tnext],ecx sub ebp, dword [s] sub ecx, dword [t] mov eax,ecx mov edx,ebp sar eax,19 jz LZero sar edx,19 mov ebx, dword [cachewidth] imul eax,ebx jmp LSetUp1 LZero: sar edx,19 mov ebx, dword [cachewidth] LSetUp1: add eax,edx mov edx, dword [tfracf] mov dword [advancetable+4],eax add eax,ebx shl ebp,13 mov ebx, dword [sfracf] shl ecx,13 mov dword [advancetable],eax mov dword [tstep],ecx add edx,ecx sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov al, byte [esi] add ebx,ebp mov byte [1+edi],al adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [2+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [3+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] mov ecx, dword [counttemp] cmp ecx,8 ja LSetupNotLast2 dec ecx jz LFDIVInFlight2 mov dword [spancountminus1],ecx fild dword [spancountminus1] fld dword [d_zistepu] fmul st0,st1 fld dword [d_tdivzstepu] fmul st0,st2 fxch st1 faddp st3,st0 fxch st1 fmul dword [d_sdivzstepu] fxch st1 faddp st3,st0 fld dword [fp_64k] fxch st1 faddp st4,st0 fdiv st0,st1 jmp LFDIVInFlight2 ALIGN 4 LSetupNotLast2: fadd dword [zi8stepu] fxch st2 fadd dword [sdivz8stepu] fxch st2 fld dword [tdivz8stepu] faddp st2,st0 fld dword [fp_64k] fdiv st0,st1 LFDIVInFlight2: mov dword [counttemp],ecx add edx, dword [tstep] sbb ecx,ecx mov byte [4+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [5+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [6+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edi,8 mov dword [tfracf],edx mov edx, dword [snext] mov dword [sfracf],ebx mov ebx, dword [tnext] mov dword [s],edx mov dword [t],ebx mov ecx, dword [counttemp] cmp ecx,8 mov byte [-1+edi],al ja near LNotLastSegment LLastSegment: test ecx,ecx jz near LNoSteps fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [snext] fistp dword [tnext] mov al, byte [esi] mov ebx, dword [tadjust] mov byte [edi],al mov eax, dword [sadjust] add eax, dword [snext] add ebx, dword [tnext] mov ebp, dword [bbextents] mov edx, dword [bbextentt] cmp eax,2048 jl near LClampLow4 cmp eax,ebp ja near LClampHigh4 LClampReentry4: mov dword [snext],eax cmp ebx,2048 jl near LClampLow5 cmp ebx,edx ja near LClampHigh5 LClampReentry5: cmp ecx,1 je near LOnlyOneStep sub eax, dword [s] sub ebx, dword [t] add eax,eax add ebx,ebx imul dword [reciprocal_table-8+ecx*4] mov ebp,edx mov eax,ebx imul dword [reciprocal_table-8+ecx*4] LSetEntryvec: mov ebx, dword [entryvec_table+ecx*4] mov eax,edx mov dword [jumptemp],ebx mov ecx,ebp sar edx,16 mov ebx, dword [cachewidth] sar ecx,16 imul edx,ebx add edx,ecx mov ecx, dword [tfracf] mov dword [advancetable+4],edx add edx,ebx shl ebp,16 mov ebx, dword [sfracf] shl eax,16 mov dword [advancetable],edx mov dword [tstep],eax mov edx,ecx add edx,eax sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] jmp dword[jumptemp] LNoSteps: mov al, byte [esi] sub edi,7 jmp LEndSpan LOnlyOneStep: sub eax, dword [s] sub ebx, dword [t] mov ebp,eax mov edx,ebx jmp LSetEntryvec ;;;;;;;;;;;;;;;;;;;;;;;; ; globals Entry*_8 ;;;;;;;;;;;;;;;;;;;;;;;; global Entry2_8 Entry2_8: sub edi,6 mov al, byte [esi] jmp LLEntry2_8 global Entry3_8 Entry3_8: sub edi,5 add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] jmp LLEntry3_8 global Entry4_8 Entry4_8: sub edi,4 add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LLEntry4_8 global Entry5_8 Entry5_8: sub edi,3 add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LLEntry5_8 global Entry6_8 Entry6_8: sub edi,2 add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LLEntry6_8 global Entry7_8 Entry7_8: dec edi add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LLEntry7_8 global Entry8_8 Entry8_8: add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [1+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LLEntry7_8: sbb ecx,ecx mov byte [2+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LLEntry6_8: sbb ecx,ecx mov byte [3+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LLEntry5_8: sbb ecx,ecx mov byte [4+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LLEntry4_8: sbb ecx,ecx mov byte [5+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] LLEntry3_8: mov byte [6+edi],al mov al, byte [esi] LLEntry2_8: LEndSpan: fstp st0 fstp st0 fstp st0 mov ebx, dword [pspantemp] mov ebx, dword [12+ebx] test ebx,ebx mov byte [7+edi],al jnz near LSpanLoop pop ebx pop esi pop edi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_DrawZSpans ;;;;;;;;;;;;;;;;;;;;;;;; LClamp: mov edx,040000000h xor ebx,ebx fstp st0 jmp LZDraw LClampNeg: mov edx,040000000h xor ebx,ebx fstp st0 jmp LZDrawNeg global D_DrawZSpans D_DrawZSpans: push ebp push edi push esi push ebx fld dword [d_zistepu] mov eax, dword [d_zistepu] mov esi, dword [4+16+esp] test eax,eax jz near LFNegSpan fmul dword [Float2ToThe31nd] fistp dword [izistep] mov ebx, dword [izistep] LFSpanLoop: fild dword [4+esi] fild dword [0+esi] mov ecx, dword [4+esi] mov edi, dword [d_pzbuffer] fmul dword [d_zistepu] fxch st1 fmul dword [d_zistepv] fxch st1 fadd dword [d_ziorigin] imul ecx, dword [d_zrowbytes] faddp st1,st0 fcom dword [float_point5] add edi,ecx mov edx, dword [0+esi] add edx,edx mov ecx, dword [8+esi] add edi,edx push esi fnstsw ax test ah,045h jz near LClamp fmul dword [Float2ToThe31nd] fistp dword [izi] mov edx, dword [izi] LZDraw: test edi,2 jz LFMiddle mov eax,edx add edx,ebx shr eax,16 dec ecx mov word [edi],ax add edi,2 LFMiddle: push ecx shr ecx,1 jz LFLast shr ecx,1 jnc LFMiddleLoop mov eax,edx add edx,ebx shr eax,16 mov esi,edx add edx,ebx and esi,0FFFF0000h or eax,esi mov dword [edi],eax add edi,4 and ecx,ecx jz LFLast LFMiddleLoop: mov eax,edx add edx,ebx shr eax,16 mov esi,edx add edx,ebx and esi,0FFFF0000h or eax,esi mov ebp,edx mov dword [edi],eax add edx,ebx shr ebp,16 mov esi,edx add edx,ebx and esi,0FFFF0000h or ebp,esi mov dword [4+edi],ebp add edi,8 dec ecx jnz LFMiddleLoop LFLast: pop ecx pop esi and ecx,1 jz LFSpanDone shr edx,16 mov word [edi],dx LFSpanDone: mov esi, dword [12+esi] test esi,esi jnz near LFSpanLoop jmp LFDone LFNegSpan: fmul dword [FloatMinus2ToThe31nd] fistp dword [izistep] mov ebx, dword [izistep] LFNegSpanLoop: fild dword [4+esi] fild dword [0+esi] mov ecx, dword [4+esi] mov edi, dword [d_pzbuffer] fmul dword [d_zistepu] fxch st1 fmul dword [d_zistepv] fxch st1 fadd dword [d_ziorigin] imul ecx, dword [d_zrowbytes] faddp st1,st0 fcom dword [float_point5] add edi,ecx mov edx, dword [0+esi] add edx,edx mov ecx, dword [8+esi] add edi,edx push esi fnstsw ax test ah,045h jz near LClampNeg fmul dword [Float2ToThe31nd] fistp dword [izi] mov edx, dword [izi] LZDrawNeg: test edi,2 jz LFNegMiddle mov eax,edx sub edx,ebx shr eax,16 dec ecx mov word [edi],ax add edi,2 LFNegMiddle: push ecx shr ecx,1 jz LFNegLast shr ecx,1 jnc LFNegMiddleLoop mov eax,edx sub edx,ebx shr eax,16 mov esi,edx sub edx,ebx and esi,0FFFF0000h or eax,esi mov dword [edi],eax add edi,4 and ecx,ecx jz LFNegLast LFNegMiddleLoop: mov eax,edx sub edx,ebx shr eax,16 mov esi,edx sub edx,ebx and esi,0FFFF0000h or eax,esi mov ebp,edx mov dword [edi],eax sub edx,ebx shr ebp,16 mov esi,edx sub edx,ebx and esi,0FFFF0000h or ebp,esi mov dword [4+edi],ebp add edi,8 dec ecx jnz LFNegMiddleLoop LFNegLast: pop ecx pop esi and ecx,1 jz LFNegSpanDone shr edx,16 mov word [edi],dx LFNegSpanDone: mov esi, dword [12+esi] test esi,esi jnz near LFNegSpanLoop LFDone: pop ebx pop esi pop edi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_DrawSingleZSpans ;;;;;;;;;;;;;;;;;;;;;;;; LClamp2: mov edx,040000000h xor ebx,ebx fstp st0 jmp LZDraw2 LClampNeg2: mov edx,040000000h xor ebx,ebx fstp st0 jmp LZDrawNeg2 global D_DrawSingleZSpans D_DrawSingleZSpans: push ebp push edi push esi push ebx mov dword [ZScanCount],0 fld dword [d_zistepu] mov eax, dword [d_zistepu] mov esi, dword [4+16+esp] test eax,eax jz near LFNegSpan2 fmul dword [Float2ToThe31nd] fistp dword [izistep] mov ebx, dword [izistep] LFSpanLoop2: fild dword [4+esi] fild dword [0+esi] mov ecx, dword [4+esi] mov edi, dword [d_pzbuffer] fmul dword [d_zistepu] fxch st1 fmul dword [d_zistepv] fxch st1 fadd dword [d_ziorigin] imul ecx, dword [d_zrowbytes] faddp st1,st0 fcom dword [float_point5] add edi,ecx mov edx, dword [0+esi] add edx,edx mov ecx, dword [8+esi] add edi,edx push esi fnstsw ax test ah,045h jz near LClamp2 fmul dword [Float2ToThe31nd] fistp dword [izi] mov edx, dword [izi] LZDraw2: mov eax,edx add edx,ebx shr eax,16 mov byte [scanList + ecx - 1], 1 cmp word [edi],ax jle LZSkip mov byte [scanList + ecx - 1], 0 add dword [ZScanCount],1 LZSkip: add edi,2 dec ecx jnz LZDraw2 pop esi jmp LFDone2 LFNegSpan2: fmul dword [FloatMinus2ToThe31nd] fistp dword [izistep] mov ebx, dword [izistep] LFNegSpanLoop2: fild dword [4+esi] fild dword [0+esi] mov ecx, dword [4+esi] mov edi, dword [d_pzbuffer] fmul dword [d_zistepu] fxch st1 fmul dword [d_zistepv] fxch st1 fadd dword [d_ziorigin] imul ecx, dword [d_zrowbytes] faddp st1,st0 fcom dword [float_point5] add edi,ecx mov edx, dword [0+esi] add edx,edx mov ecx, dword [8+esi] add edi,edx push esi fnstsw ax test ah,045h jz near LClampNeg2 fmul dword [Float2ToThe31nd] fistp dword [izi] mov edx, dword [izi] LZDrawNeg2: mov eax,edx sub edx,ebx shr eax,16 mov byte [scanList + ecx - 1], 1 cmp word [edi],ax jle LZSkip2 mov byte [scanList + ecx - 1], 0 LZSkip2: add edi,2 dec ecx jnz near LZDraw2 pop esi LFDone2: pop ebx pop esi pop edi pop ebp ret engine/h2shared/d_draw16.asm000066400000000000000000000371171444734033100161500ustar00rootroot00000000000000; ; d_draw16.asm ; x86 assembly-language horizontal 8-bpp span-drawing code, with 16-pixel ; subdivision. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix d_zistepu _sym_prefix d_pzbuffer _sym_prefix d_zistepv _sym_prefix d_zrowbytes _sym_prefix d_ziorigin _sym_prefix d_sdivzstepu _sym_prefix d_tdivzstepu _sym_prefix d_sdivzstepv _sym_prefix d_tdivzstepv _sym_prefix d_sdivzorigin _sym_prefix d_tdivzorigin _sym_prefix sadjust _sym_prefix tadjust _sym_prefix bbextents _sym_prefix bbextentt _sym_prefix cacheblock _sym_prefix d_viewbuffer _sym_prefix cachewidth _sym_prefix d_scantable ; C-shared globals: _sym_prefix D_DrawSpans16 %endif ; _sym_prefix ; externs from C code extern d_zistepu extern d_pzbuffer extern d_zistepv extern d_zrowbytes extern d_ziorigin extern d_sdivzstepu extern d_tdivzstepu extern d_sdivzstepv extern d_tdivzstepv extern d_sdivzorigin extern d_tdivzorigin extern sadjust extern tadjust extern bbextents extern bbextentt extern cacheblock extern d_viewbuffer extern cachewidth extern d_scantable ; externs from ASM-only code extern float_point5 extern Float2ToThe31nd extern izistep extern izi extern FloatMinus2ToThe31nd extern float_1 extern float_particle_z_clip extern float_minus_1 extern float_0 extern fp_16 extern fp_64k extern fp_1m extern fp_1m_minus_1 extern fp_8 extern entryvec_table extern advancetable extern sstep extern tstep extern pspantemp extern counttemp extern jumptemp extern reciprocal_table extern pbase extern s extern t extern sfracf extern tfracf extern snext extern tnext extern spancountminus1 extern zi16stepu extern sdivz16stepu extern tdivz16stepu extern zi8stepu extern sdivz8stepu extern tdivz8stepu extern reciprocal_table_16 extern entryvec_table_16 extern fp_64kx64k SEGMENT .data SEGMENT .text ;;;;;;;;;;;;;;;;;;;;;;;; ; D_DrawSpans16 ;;;;;;;;;;;;;;;;;;;;;;;; LClampHigh0: mov esi, dword [bbextents] jmp LClampReentry0 LClampHighOrLow0: jg LClampHigh0 xor esi,esi jmp LClampReentry0 LClampHigh1: mov edx, dword [bbextentt] jmp LClampReentry1 LClampHighOrLow1: jg LClampHigh1 xor edx,edx jmp LClampReentry1 LClampLow2: mov ebp,4096 jmp LClampReentry2 LClampHigh2: mov ebp, dword [bbextents] jmp LClampReentry2 LClampLow3: mov ecx,4096 jmp LClampReentry3 LClampHigh3: mov ecx, dword [bbextentt] jmp LClampReentry3 LClampLow4: mov eax,4096 jmp LClampReentry4 LClampHigh4: mov eax, dword [bbextents] jmp LClampReentry4 LClampLow5: mov ebx,4096 jmp LClampReentry5 LClampHigh5: mov ebx, dword [bbextentt] jmp LClampReentry5 ALIGN 4 global D_DrawSpans16 D_DrawSpans16: push ebp push edi push esi push ebx fld dword [d_sdivzstepu] fmul dword [fp_16] mov edx, dword [cacheblock] fld dword [d_tdivzstepu] fmul dword [fp_16] mov ebx, dword [4+16+esp] fld dword [d_zistepu] fmul dword [fp_16] mov dword [pbase],edx fstp dword [zi16stepu] fstp dword [tdivz16stepu] fstp dword [sdivz16stepu] LSpanLoop: fild dword [4+ebx] fild dword [0+ebx] fld st1 fmul dword [d_sdivzstepv] fld st1 fmul dword [d_sdivzstepu] fld st2 fmul dword [d_tdivzstepu] fxch st1 faddp st2,st0 fxch st1 fld st3 fmul dword [d_tdivzstepv] fxch st1 fadd dword [d_sdivzorigin] fxch st4 fmul dword [d_zistepv] fxch st1 faddp st2,st0 fxch st2 fmul dword [d_zistepu] fxch st1 fadd dword [d_tdivzorigin] fxch st2 faddp st1,st0 fld dword [fp_64k] fxch st1 fadd dword [d_ziorigin] fdiv st1,st0 mov ecx, dword [d_viewbuffer] mov eax, dword [4+ebx] mov dword [pspantemp],ebx mov edx, dword [tadjust] mov esi, dword [sadjust] mov edi, dword [d_scantable+eax*4] add edi,ecx mov ecx, dword [0+ebx] add edi,ecx mov ecx, dword [8+ebx] cmp ecx,16 ja LSetupNotLast1 dec ecx jz LCleanup1 mov dword [spancountminus1],ecx fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] fild dword [spancountminus1] fld dword [d_tdivzstepu] fld dword [d_zistepu] fmul st0,st2 fxch st1 fmul st0,st2 fxch st2 fmul dword [d_sdivzstepu] fxch st1 faddp st3,st0 fxch st1 faddp st3,st0 faddp st3,st0 fld dword [fp_64k] fdiv st0,st1 jmp LFDIVInFlight1 LCleanup1: fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] jmp LFDIVInFlight1 ALIGN 4 LSetupNotLast1: fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] fadd dword [zi16stepu] fxch st2 fadd dword [sdivz16stepu] fxch st2 fld dword [tdivz16stepu] faddp st2,st0 fld dword [fp_64k] fdiv st0,st1 LFDIVInFlight1: add esi, dword [s] add edx, dword [t] mov ebx, dword [bbextents] mov ebp, dword [bbextentt] cmp esi,ebx ja near LClampHighOrLow0 LClampReentry0: mov dword [s],esi mov ebx, dword [pbase] shl esi,16 cmp edx,ebp mov dword [sfracf],esi ja near LClampHighOrLow1 LClampReentry1: mov dword [t],edx mov esi, dword [s] shl edx,16 mov eax, dword [t] sar esi,16 mov dword [tfracf],edx sar eax,16 mov edx, dword [cachewidth] imul eax,edx add esi,ebx add esi,eax cmp ecx,16 jna near LLastSegment LNotLastSegment: fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [snext] fistp dword [tnext] mov eax, dword [snext] mov edx, dword [tnext] mov bl, byte [esi] sub ecx,16 mov ebp, dword [sadjust] mov dword [counttemp],ecx mov ecx, dword [tadjust] mov byte [edi],bl add ebp,eax add ecx,edx mov eax, dword [bbextents] mov edx, dword [bbextentt] cmp ebp,4096 jl near LClampLow2 cmp ebp,eax ja near LClampHigh2 LClampReentry2: cmp ecx,4096 jl near LClampLow3 cmp ecx,edx ja near LClampHigh3 LClampReentry3: mov dword [snext],ebp mov dword [tnext],ecx sub ebp, dword [s] sub ecx, dword [t] mov eax,ecx mov edx,ebp sar eax,20 jz LZero sar edx,20 mov ebx, dword [cachewidth] imul eax,ebx jmp LSetUp1 LZero: sar edx,20 mov ebx, dword [cachewidth] LSetUp1: add eax,edx mov edx, dword [tfracf] mov dword [advancetable+4],eax add eax,ebx shl ebp,12 mov ebx, dword [sfracf] shl ecx,12 mov dword [advancetable],eax mov dword [tstep],ecx add edx,ecx sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov al, byte [esi] add ebx,ebp mov byte [1+edi],al adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [2+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [3+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [4+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [5+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [6+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [7+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] mov ecx, dword [counttemp] cmp ecx,16 ja LSetupNotLast2 dec ecx jz LFDIVInFlight2 mov dword [spancountminus1],ecx fild dword [spancountminus1] fld dword [d_zistepu] fmul st0,st1 fld dword [d_tdivzstepu] fmul st0,st2 fxch st1 faddp st3,st0 fxch st1 fmul dword [d_sdivzstepu] fxch st1 faddp st3,st0 fld dword [fp_64k] fxch st1 faddp st4,st0 fdiv st0,st1 jmp LFDIVInFlight2 ALIGN 4 LSetupNotLast2: fadd dword [zi16stepu] fxch st2 fadd dword [sdivz16stepu] fxch st2 fld dword [tdivz16stepu] faddp st2,st0 fld dword [fp_64k] fdiv st0,st1 LFDIVInFlight2: mov dword [counttemp],ecx add edx, dword [tstep] sbb ecx,ecx mov byte [8+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [9+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [10+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [11+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [12+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [13+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [14+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edi,16 mov dword [tfracf],edx mov edx, dword [snext] mov dword [sfracf],ebx mov ebx, dword [tnext] mov dword [s],edx mov dword [t],ebx mov ecx, dword [counttemp] cmp ecx,16 mov byte [-1+edi],al ja near LNotLastSegment LLastSegment: test ecx,ecx jz near LNoSteps fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [snext] fistp dword [tnext] mov al, byte [esi] mov ebx, dword [tadjust] mov byte [edi],al mov eax, dword [sadjust] add eax, dword [snext] add ebx, dword [tnext] mov ebp, dword [bbextents] mov edx, dword [bbextentt] cmp eax,4096 jl near LClampLow4 cmp eax,ebp ja near LClampHigh4 LClampReentry4: mov dword [snext],eax cmp ebx,4096 jl near LClampLow5 cmp ebx,edx ja near LClampHigh5 LClampReentry5: cmp ecx,1 je near LOnlyOneStep sub eax, dword [s] sub ebx, dword [t] add eax,eax add ebx,ebx imul dword [reciprocal_table_16-8+ecx*4] mov ebp,edx mov eax,ebx imul dword [reciprocal_table_16-8+ecx*4] LSetEntryvec: mov ebx, dword [entryvec_table_16+ecx*4] mov eax,edx mov dword [jumptemp],ebx mov ecx,ebp sar edx,16 mov ebx, dword [cachewidth] sar ecx,16 imul edx,ebx add edx,ecx mov ecx, dword [tfracf] mov dword [advancetable+4],edx add edx,ebx shl ebp,16 mov ebx, dword [sfracf] shl eax,16 mov dword [advancetable],edx mov dword [tstep],eax mov edx,ecx add edx,eax sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] jmp dword[jumptemp] LNoSteps: mov al, byte [esi] sub edi,15 jmp LEndSpan LOnlyOneStep: sub eax, dword [s] sub ebx, dword [t] mov ebp,eax mov edx,ebx jmp LSetEntryvec ;;;;;;;;;;;;;;;;;;;;;;;; ; globals Entry*_16 ;;;;;;;;;;;;;;;;;;;;;;;; global Entry2_16, Entry3_16, Entry4_16, Entry5_16 global Entry6_16, Entry7_16, Entry8_16, Entry9_16 global Entry10_16, Entry11_16, Entry12_16, Entry13_16 global Entry14_16, Entry15_16, Entry16_16 Entry2_16: sub edi,14 mov al, byte [esi] jmp LEntry2_16 Entry3_16: sub edi,13 add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] jmp LEntry3_16 Entry4_16: sub edi,12 add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry4_16 Entry5_16: sub edi,11 add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry5_16 Entry6_16: sub edi,10 add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry6_16 Entry7_16: sub edi,9 add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry7_16 Entry8_16: sub edi,8 add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry8_16 Entry9_16: sub edi,7 add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry9_16 Entry10_16: sub edi,6 add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry10_16 Entry11_16: sub edi,5 add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry11_16 Entry12_16: sub edi,4 add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry12_16 Entry13_16: sub edi,3 add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry13_16 Entry14_16: sub edi,2 add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry14_16 Entry15_16: dec edi add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry15_16 Entry16_16: add edx,eax mov al, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov byte [1+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry15_16: sbb ecx,ecx mov byte [2+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry14_16: sbb ecx,ecx mov byte [3+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry13_16: sbb ecx,ecx mov byte [4+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry12_16: sbb ecx,ecx mov byte [5+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry11_16: sbb ecx,ecx mov byte [6+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry10_16: sbb ecx,ecx mov byte [7+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry9_16: sbb ecx,ecx mov byte [8+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry8_16: sbb ecx,ecx mov byte [9+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry7_16: sbb ecx,ecx mov byte [10+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry6_16: sbb ecx,ecx mov byte [11+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry5_16: sbb ecx,ecx mov byte [12+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry4_16: sbb ecx,ecx mov byte [13+edi],al add ebx,ebp mov al, byte [esi] adc esi, dword [advancetable+4+ecx*4] LEntry3_16: mov byte [14+edi],al mov al, byte [esi] LEntry2_16: LEndSpan: fstp st0 fstp st0 fstp st0 mov ebx, dword [pspantemp] mov ebx, dword [12+ebx] test ebx,ebx mov byte [15+edi],al jnz near LSpanLoop pop ebx pop esi pop edi pop ebp ret engine/h2shared/d_draw16t.asm000066400000000000000000000557361444734033100163430ustar00rootroot00000000000000; ; d_draw16t.asm ; x86 assembly-language horizontal 8-bpp span-drawing code, with 16-pixel ; subdivision and translucency handling. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix d_zistepu _sym_prefix d_pzbuffer _sym_prefix d_zistepv _sym_prefix d_zrowbytes _sym_prefix d_ziorigin _sym_prefix d_sdivzstepu _sym_prefix d_tdivzstepu _sym_prefix d_sdivzstepv _sym_prefix d_tdivzstepv _sym_prefix d_sdivzorigin _sym_prefix d_tdivzorigin _sym_prefix sadjust _sym_prefix tadjust _sym_prefix bbextents _sym_prefix bbextentt _sym_prefix cacheblock _sym_prefix d_viewbuffer _sym_prefix cachewidth _sym_prefix d_scantable _sym_prefix mainTransTable _sym_prefix scanList _sym_prefix D_DrawSingleZSpans ; C-shared globals: _sym_prefix D_Draw16StartT _sym_prefix D_DrawSpans16T _sym_prefix D_Draw16EndT _sym_prefix R_TranPatch3 %endif ; _sym_prefix ; externs from C code extern d_zistepu extern d_pzbuffer extern d_zistepv extern d_zrowbytes extern d_ziorigin extern d_sdivzstepu extern d_tdivzstepu extern d_sdivzstepv extern d_tdivzstepv extern d_sdivzorigin extern d_tdivzorigin extern sadjust extern tadjust extern bbextents extern bbextentt extern cacheblock extern d_viewbuffer extern cachewidth extern d_scantable extern mainTransTable extern scanList extern D_DrawSingleZSpans ; externs from ASM-only code extern float_point5 extern Float2ToThe31nd extern izistep extern izi extern FloatMinus2ToThe31nd extern float_1 extern float_particle_z_clip extern float_minus_1 extern float_0 extern fp_16 extern fp_64k extern fp_1m extern fp_1m_minus_1 extern fp_8 extern entryvec_table extern advancetable extern sstep extern tstep extern pspantemp extern counttemp extern jumptemp extern reciprocal_table extern pbase extern s extern t extern sfracf extern tfracf extern snext extern tnext extern spancountminus1 extern zi16stepu extern sdivz16stepu extern tdivz16stepu extern zi8stepu extern sdivz8stepu extern tdivz8stepu extern reciprocal_table_16 extern entryvec_table_16T extern fp_64kx64k SEGMENT .data masktemp dw 0 SEGMENT .text ;;;;;;;;;;;;;;;;;;;;;;;; ; D_Draw16StartT ;;;;;;;;;;;;;;;;;;;;;;;; global D_Draw16StartT D_Draw16StartT: LClampHigh0: mov esi, dword [bbextents] jmp LClampReentry0 LClampHighOrLow0: jg LClampHigh0 xor esi,esi jmp LClampReentry0 LClampHigh1: mov edx, dword [bbextentt] jmp LClampReentry1 LClampHighOrLow1: jg LClampHigh1 xor edx,edx jmp LClampReentry1 LClampLow2: mov ebp,4096 jmp LClampReentry2 LClampHigh2: mov ebp, dword [bbextents] jmp LClampReentry2 LClampLow3: mov ecx,4096 jmp LClampReentry3 LClampHigh3: mov ecx, dword [bbextentt] jmp LClampReentry3 LClampLow4: mov eax,4096 jmp LClampReentry4 LClampHigh4: mov eax, dword [bbextents] jmp LClampReentry4 LClampLow5: mov ebx,4096 jmp LClampReentry5 LClampHigh5: mov ebx, dword [bbextentt] jmp LClampReentry5 ;;;;;;;;;;;;;;;;;;;;;;;; ; D_DrawSpans16T ;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 4 global D_DrawSpans16T D_DrawSpans16T: push ebp push edi push esi push ebx fld dword [d_sdivzstepu] fmul dword [fp_16] mov edx, dword [cacheblock] fld dword [d_tdivzstepu] fmul dword [fp_16] mov ebx, dword [4+16+esp] fld dword [d_zistepu] fmul dword [fp_16] mov dword [pbase],edx fstp dword [zi16stepu] fstp dword [tdivz16stepu] fstp dword [sdivz16stepu] LSpanLoop: fild dword [4+ebx] fild dword [0+ebx] fld st1 fmul dword [d_sdivzstepv] fld st1 fmul dword [d_sdivzstepu] fld st2 fmul dword [d_tdivzstepu] fxch st1 faddp st2,st0 fxch st1 fld st3 fmul dword [d_tdivzstepv] fxch st1 fadd dword [d_sdivzorigin] fxch st4 fmul dword [d_zistepv] fxch st1 faddp st2,st0 fxch st2 fmul dword [d_zistepu] fxch st1 fadd dword [d_tdivzorigin] fxch st2 faddp st1,st0 fld dword [fp_64k] fxch st1 fadd dword [d_ziorigin] fdiv st1,st0 mov ecx, dword [d_viewbuffer] mov eax, dword [4+ebx] mov dword [pspantemp],ebx push eax push ecx push edx push ebx call D_DrawSingleZSpans ; call near D_DrawSingleZSpans pop ebx pop edx pop ecx pop eax mov edx, dword [tadjust] mov esi, dword [sadjust] mov edi, dword [d_scantable+eax*4] add edi,ecx mov ecx, dword [0+ebx] add edi,ecx mov ecx, dword [8+ebx] cmp ecx,16 ja LSetupNotLast1 dec ecx jz LCleanup1 mov dword [spancountminus1],ecx fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] fild dword [spancountminus1] fld dword [d_tdivzstepu] fld dword [d_zistepu] fmul st0,st2 fxch st1 fmul st0,st2 fxch st2 fmul dword [d_sdivzstepu] fxch st1 faddp st3,st0 fxch st1 faddp st3,st0 faddp st3,st0 fld dword [fp_64k] fdiv st0,st1 jmp LFDIVInFlight1 LCleanup1: fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] jmp LFDIVInFlight1 ALIGN 4 LSetupNotLast1: fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] fadd dword [zi16stepu] fxch st2 fadd dword [sdivz16stepu] fxch st2 fld dword [tdivz16stepu] faddp st2,st0 fld dword [fp_64k] fdiv st0,st1 LFDIVInFlight1: add esi, dword [s] add edx, dword [t] mov ebx, dword [bbextents] mov ebp, dword [bbextentt] cmp esi,ebx ja near LClampHighOrLow0 LClampReentry0: mov dword [s],esi mov ebx, dword [pbase] shl esi,16 cmp edx,ebp mov dword [sfracf],esi ja near LClampHighOrLow1 LClampReentry1: mov dword [t],edx mov esi, dword [s] shl edx,16 mov eax, dword [t] sar esi,16 mov dword [tfracf],edx sar eax,16 mov edx, dword [cachewidth] imul eax,edx add esi,ebx add esi,eax cmp ecx,16 jna near LLastSegment LNotLastSegment: fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [snext] fistp dword [tnext] mov eax, dword [snext] mov edx, dword [tnext] xor ebx,ebx add bl, byte [scanList + ecx - 1] shl ebx,1 add bl, byte [scanList + ecx - 2] shl ebx,1 add bl, byte [scanList + ecx - 3] shl ebx,1 add bl, byte [scanList + ecx - 4] shl ebx,1 add bl, byte [scanList + ecx - 5] shl ebx,1 add bl, byte [scanList + ecx - 6] shl ebx,1 add bl, byte [scanList + ecx - 7] shl ebx,1 add bl, byte [scanList + ecx - 8] shl ebx,1 add bl, byte [scanList + ecx - 9] shl ebx,1 add bl, byte [scanList + ecx - 10] shl ebx,1 add bl, byte [scanList + ecx - 11] shl ebx,1 add bl, byte [scanList + ecx - 12] shl ebx,1 add bl, byte [scanList + ecx - 13] shl ebx,1 add bl, byte [scanList + ecx - 14] shl ebx,1 add bl, byte [scanList + ecx - 15] shl ebx,1 add bl, byte [scanList + ecx - 16] ;mov bx, 8000h ; mov bx, 0ffffh ; mov bx, 0h mov [masktemp], bx mov bh, byte [esi] sub ecx,16 mov ebp, dword [sadjust] mov dword [counttemp],ecx mov ecx, dword [tadjust] ;and masktemp, 8000h bt word [masktemp], 15 jnc SkipTran1 ;rj mov bl, byte [edi] and ebx, 0ffffh mov bl, byte [12345678h + ebx] TranPatch1: mov byte [edi],bl SkipTran1: add ebp,eax add ecx,edx mov eax, dword [bbextents] mov edx, dword [bbextentt] cmp ebp,4096 jl near LClampLow2 cmp ebp,eax ja near LClampHigh2 LClampReentry2: cmp ecx,4096 jl near LClampLow3 cmp ecx,edx ja near LClampHigh3 LClampReentry3: mov dword [snext],ebp mov dword [tnext],ecx sub ebp, dword [s] sub ecx, dword [t] mov eax,ecx mov edx,ebp sar eax,20 jz LZero sar edx,20 mov ebx, dword [cachewidth] imul eax,ebx jmp LSetUp1 LZero: sar edx,20 mov ebx, dword [cachewidth] LSetUp1: add eax,edx mov edx, dword [tfracf] mov dword [advancetable+4],eax add eax,ebx shl ebp,12 mov ebx, dword [sfracf] shl ecx,12 mov dword [advancetable],eax mov dword [tstep],ecx add edx,ecx sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] mov ah, byte [esi] ; and masktemp, 4000h bt word [masktemp], 14 jnc SkipTran2 ;rj mov al, byte [1+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch2: mov byte [1+edi],al SkipTran2: add edx, dword [tstep] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov ah, byte [esi] ; and masktemp, 2000h bt word [masktemp], 13 jnc SkipTran3 ;rj mov al, byte [2+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch3: mov byte [2+edi],al SkipTran3: add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov ah, byte [esi] ; and masktemp, 1000h bt word [masktemp], 12 jnc SkipTran4 ;rj mov al, byte [3+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch4: mov byte [3+edi],al SkipTran4: add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov ah, byte [esi] ; and masktemp, 0800h bt word [masktemp], 11 jnc SkipTran5 ;rj mov al, byte [4+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch5: mov byte [4+edi],al SkipTran5: add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov ah, byte [esi] ; and masktemp, 0400h bt word [masktemp], 10 jnc SkipTran6 ;rj mov al, byte [5+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch6: mov byte [5+edi],al SkipTran6: ; rj speed test add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov ah, byte [esi] ; and masktemp, 0200h bt word [masktemp], 9 jnc SkipTran7 ;rj mov al, byte [6+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch7: mov byte [6+edi],al ; add ebx,ebp ; adc esi, dword [advancetable+4+ecx*4] ; xor eax, eax ; add edx, dword [tstep] ; mov al, byte [6+edi] ; sbb ecx,ecx ; mov ah, byte [esi] ;rj ; add eax, dword [mainTransTable] ; mov al, byte [eax + mainTransTable] ; mov byte [6+edi],al SkipTran7: add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov ah, byte [esi] ; and masktemp, 0100h bt word [masktemp], 8 jnc SkipTran8 ;rj mov al, byte [7+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch8: mov byte [7+edi],al SkipTran8: add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx add ebx,ebp mov ah, byte [esi] adc esi, dword [advancetable+4+ecx*4] mov ecx, dword [counttemp] cmp ecx,16 ja LSetupNotLast2 dec ecx jz LFDIVInFlight2 mov dword [spancountminus1],ecx fild dword [spancountminus1] fld dword [d_zistepu] fmul st0,st1 fld dword [d_tdivzstepu] fmul st0,st2 fxch st1 faddp st3,st0 fxch st1 fmul dword [d_sdivzstepu] fxch st1 faddp st3,st0 fld dword [fp_64k] fxch st1 faddp st4,st0 fdiv st0,st1 jmp LFDIVInFlight2 ALIGN 4 LSetupNotLast2: fadd dword [zi16stepu] fxch st2 fadd dword [sdivz16stepu] fxch st2 fld dword [tdivz16stepu] faddp st2,st0 fld dword [fp_64k] fdiv st0,st1 LFDIVInFlight2: mov dword [counttemp],ecx add edx, dword [tstep] sbb ecx,ecx bt word [masktemp], 7 jnc SkipTran9 ;rj mov al, byte [8+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch9: mov byte [8+edi],al SkipTran9: mov ah, byte [esi] bt word [masktemp], 6 jnc SkipTran10 ;rj mov al, byte [9+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch10: mov byte [9+edi],al SkipTran10: add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov ah, byte [esi] bt word [masktemp], 5 jnc SkipTran11 ;rj mov al, byte [10+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch11: mov byte [10+edi],al SkipTran11: add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov ah, byte [esi] bt word [masktemp], 4 jnc SkipTran12 ;rj mov al, byte [11+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch12: mov byte [11+edi],al SkipTran12: add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov ah, byte [esi] bt word [masktemp], 3 jnc SkipTran13 ;rj mov al, byte [12+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch13: mov byte [12+edi],al SkipTran13: add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov ah, byte [esi] bt word [masktemp], 2 jnc SkipTran14 ;rj mov al, byte [13+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch14: mov byte [13+edi],al SkipTran14: add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx mov ah, byte [esi] bt word [masktemp], 1 jnc SkipTran15 ;rj mov al, byte [14+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch15: mov byte [14+edi],al SkipTran15: add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx add ebx,ebp mov ah, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edi,16 mov dword [tfracf],edx mov edx, dword [snext] mov dword [sfracf],ebx mov ebx, dword [tnext] mov dword [s],edx mov dword [t],ebx mov ecx, dword [counttemp] bt word [masktemp], 0 jnc SkipTran16 ;rj mov al, byte [-1+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch16: mov byte [-1+edi],al SkipTran16: cmp ecx,16 ja near LNotLastSegment LLastSegment: test ecx,ecx jz near LNoSteps fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [snext] fistp dword [tnext] mov ah, byte [esi] mov ebx, dword [tadjust] cmp byte [scanList + ecx - 1], 1 jnz SkipTran17 ;rj mov al, byte [edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch17: mov byte [edi],al SkipTran17: mov eax, dword [sadjust] add eax, dword [snext] add ebx, dword [tnext] mov ebp, dword [bbextents] mov edx, dword [bbextentt] cmp eax,4096 jl near LClampLow4 cmp eax,ebp ja near LClampHigh4 LClampReentry4: mov dword [snext],eax cmp ebx,4096 jl near LClampLow5 cmp ebx,edx ja near LClampHigh5 LClampReentry5: cmp ecx,1 je near LOnlyOneStep sub eax, dword [s] sub ebx, dword [t] add eax,eax add ebx,ebx imul dword [reciprocal_table_16-8+ecx*4] mov ebp,edx mov eax,ebx imul dword [reciprocal_table_16-8+ecx*4] LSetEntryvec: mov ebx, dword [entryvec_table_16T+ecx*4] mov eax,edx mov dword [jumptemp],ebx mov ecx,ebp sar edx,16 mov ebx, dword [cachewidth] sar ecx,16 imul edx,ebx add edx,ecx mov ecx, dword [tfracf] mov dword [advancetable+4],edx add edx,ebx shl ebp,16 mov ebx, dword [sfracf] shl eax,16 mov dword [advancetable],edx mov dword [tstep],eax mov edx,ecx add edx,eax sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] jmp dword[jumptemp] LNoSteps: mov ah, byte [esi] sub edi,15 jmp LEndSpan LOnlyOneStep: sub eax, dword [s] sub ebx, dword [t] mov ebp,eax mov edx,ebx jmp LSetEntryvec ;;;;;;;;;;;;;;;;;;;;;;;; ; globals Entry*_16T ;;;;;;;;;;;;;;;;;;;;;;;; global Entry2_16T, Entry3_16T, Entry4_16T, Entry5_16T global Entry6_16T, Entry7_16T, Entry8_16T, Entry9_16T global Entry10_16T, Entry11_16T, Entry12_16T, Entry13_16T global Entry14_16T, Entry15_16T, Entry16_16T Entry2_16T: sub edi,14 mov ah, byte [esi] jmp LEntry2_16 Entry3_16T: sub edi,13 add edx,eax mov ah, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] jmp LEntry3_16 Entry4_16T: sub edi,12 add edx,eax mov ah, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry4_16 Entry5_16T: sub edi,11 add edx,eax mov ah, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry5_16 Entry6_16T: sub edi,10 add edx,eax mov ah, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry6_16 Entry7_16T: sub edi,9 add edx,eax mov ah, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry7_16 Entry8_16T: sub edi,8 add edx,eax mov ah, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry8_16 Entry9_16T: sub edi,7 add edx,eax mov ah, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry9_16 Entry10_16T: sub edi,6 add edx,eax mov ah, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry10_16 Entry11_16T: sub edi,5 add edx,eax mov ah, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry11_16 Entry12_16T: sub edi,4 add edx,eax mov ah, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry12_16 Entry13_16T: sub edi,3 add edx,eax mov ah, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry13_16 Entry14_16T: sub edi,2 add edx,eax mov ah, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry14_16 Entry15_16T: dec edi add edx,eax mov ah, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] jmp LEntry15_16 Entry16_16T: add edx,eax mov ah, byte [esi] sbb ecx,ecx add ebx,ebp adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] sbb ecx,ecx cmp byte [scanList + 14], 1 jnz SkipTran18 ;rj mov al, byte [1+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch18: mov byte [1+edi],al SkipTran18: add ebx,ebp mov ah, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry15_16: sbb ecx,ecx cmp byte [scanList + 13], 1 jnz SkipTran19 ;rj mov al, byte [2+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch19: mov byte [2+edi],al SkipTran19: add ebx,ebp mov ah, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry14_16: sbb ecx,ecx cmp byte [scanList + 12], 1 jnz SkipTran20 ;rj mov al, byte [3+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch20: mov byte [3+edi],al SkipTran20: add ebx,ebp mov ah, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry13_16: sbb ecx,ecx cmp byte [scanList + 11], 1 jnz SkipTran21 ;rj mov al, byte [4+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch21: mov byte [4+edi],al SkipTran21: add ebx,ebp mov ah, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry12_16: sbb ecx,ecx cmp byte [scanList + 10], 1 jnz SkipTran22 ;rj mov al, byte [5+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch22: mov byte [5+edi],al SkipTran22: add ebx,ebp mov ah, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry11_16: sbb ecx,ecx cmp byte [scanList + 9], 1 jnz SkipTran23 ;rj mov al, byte [6+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch23: mov byte [6+edi],al SkipTran23: add ebx,ebp mov ah, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry10_16: sbb ecx,ecx cmp byte [scanList + 8], 1 jnz SkipTran24 ;rj mov al, byte [7+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch24: mov byte [7+edi],al SkipTran24: add ebx,ebp mov ah, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry9_16: sbb ecx,ecx cmp byte [scanList + 7], 1 jnz SkipTran25 ;rj mov al, byte [8+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch25: mov byte [8+edi],al SkipTran25: add ebx,ebp mov ah, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry8_16: sbb ecx,ecx cmp byte [scanList + 6], 1 jnz SkipTran26 ;rj mov al, byte [9+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch26: mov byte [9+edi],al SkipTran26: add ebx,ebp mov ah, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry7_16: sbb ecx,ecx cmp byte [scanList + 5], 1 jnz SkipTran27 ;rj mov al, byte [10+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch27: mov byte [10+edi],al SkipTran27: add ebx,ebp mov ah, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry6_16: sbb ecx,ecx cmp byte [scanList + 4], 1 jnz SkipTran28 ;rj mov al, byte [11+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch28: mov byte [11+edi],al SkipTran28: add ebx,ebp mov ah, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry5_16: sbb ecx,ecx cmp byte [scanList + 3], 1 jnz SkipTran29 ;rj mov al, byte [12+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch29: mov byte [12+edi],al SkipTran29: add ebx,ebp mov ah, byte [esi] adc esi, dword [advancetable+4+ecx*4] add edx, dword [tstep] LEntry4_16: sbb ecx,ecx cmp byte [scanList + 2], 1 jnz SkipTran30 ;rj mov al, byte [13+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch30: mov byte [13+edi],al SkipTran30: add ebx,ebp mov ah, byte [esi] adc esi, dword [advancetable+4+ecx*4] LEntry3_16: cmp byte [scanList + 1], 1 jnz SkipTran31 ;rj mov al, byte [14+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch31: mov byte [14+edi],al SkipTran31: mov ah, byte [esi] LEntry2_16: LEndSpan: fstp st0 fstp st0 fstp st0 mov ebx, dword [pspantemp] mov ebx, dword [12+ebx] cmp byte [scanList + 0], 1 jnz SkipTran32 ;rj mov al, byte [15+edi] and eax, 0ffffh mov al, byte [12345678h + eax] TranPatch32: mov byte [15+edi],al SkipTran32: test ebx,ebx jnz near LSpanLoop pop ebx pop esi pop edi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_Draw16EndT ;;;;;;;;;;;;;;;;;;;;;;;; global D_Draw16EndT D_Draw16EndT: SEGMENT .data ALIGN 4 LPatchTable: dd TranPatch1-4 dd TranPatch2-4 dd TranPatch3-4 dd TranPatch4-4 dd TranPatch5-4 dd TranPatch6-4 dd TranPatch7-4 dd TranPatch8-4 dd TranPatch9-4 dd TranPatch10-4 dd TranPatch11-4 dd TranPatch12-4 dd TranPatch13-4 dd TranPatch14-4 dd TranPatch15-4 dd TranPatch16-4 dd TranPatch17-4 dd TranPatch18-4 dd TranPatch19-4 dd TranPatch20-4 dd TranPatch21-4 dd TranPatch22-4 dd TranPatch23-4 dd TranPatch24-4 dd TranPatch25-4 dd TranPatch26-4 dd TranPatch27-4 dd TranPatch28-4 dd TranPatch29-4 dd TranPatch30-4 dd TranPatch31-4 dd TranPatch32-4 ;;;;;;;;;;;;;;;;;;;;;;;; ; R_TranPatch3 ;;;;;;;;;;;;;;;;;;;;;;;; SEGMENT .text ALIGN 4 global R_TranPatch3 R_TranPatch3: push ebx mov eax, dword [mainTransTable] mov ebx,offset LPatchTable mov ecx,32 LPatchLoop: mov edx, dword [ebx] add ebx,4 mov dword [edx],eax dec ecx jnz LPatchLoop pop ebx ret engine/h2shared/d_edge.c000066400000000000000000000337151444734033100154120ustar00rootroot00000000000000/* d_edge.c * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "d_local.h" #include "r_local.h" static int miplevel; float scale_for_mip; int ubasestep, errorterm, erroradjustup, erroradjustdown; int vstartscan; // FIXME: should go away extern void R_RotateBmodel (void); vec3_t transformed_modelorg; #if 0 /* ============== D_DrawPoly ============== */ void D_DrawPoly (void) { // this driver takes spans, not polygons } #endif /* ============= D_MipLevelForScale ============= */ static int D_MipLevelForScale (float scale) { int lmiplevel; if (scale >= d_scalemip[0] ) lmiplevel = 0; else if (scale >= d_scalemip[1] ) lmiplevel = 1; else if (scale >= d_scalemip[2] ) lmiplevel = 2; else lmiplevel = 3; if (lmiplevel < d_minmip) lmiplevel = d_minmip; return lmiplevel; } /* ============== D_DrawSolidSurface // FIXME: clean this up ============== */ static void D_DrawSolidSurface (surf_t *surf, int color) { espan_t *span; byte *pdest; int u, u2, pix; pix = (color<<24) | (color<<16) | (color<<8) | color; for (span = surf->spans ; span ; span = span->pnext) { pdest = (byte *)d_viewbuffer + screenwidth*span->v; u = span->u; u2 = span->u + span->count - 1; ((byte *)pdest)[u] = pix; if (u2 - u < 8) { for (u++ ; u <= u2 ; u++) ((byte *)pdest)[u] = pix; } else { for (u++ ; u & 3 ; u++) ((byte *)pdest)[u] = pix; u2 -= 4; for ( ; u <= u2 ; u += 4) *(int *)((byte *)pdest + u) = pix; u2 += 4; for ( ; u <= u2 ; u++) ((byte *)pdest)[u] = pix; } } } /* ============== D_DrawSolidSurfaceT ============== */ static void D_DrawSolidSurfaceT (surf_t *surf, int color) { espan_t *pspan; byte *pdest; short *pz; int izi, izistep, count; float zi, dv, du; izistep = (int)(d_zistepu * 0x8000 * 0x10000); for (pspan = surf->spans ; pspan ; pspan = pspan->pnext) { pdest = (byte *)d_viewbuffer + screenwidth*pspan->v + pspan->u; pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; count = pspan->count; du = (float)pspan->u; dv = (float)pspan->v; zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; izi = (int)(zi * 0x8000 * 0x10000); do { if (*pz <= (izi >> 16)) { *pdest = mainTransTable[(color<<8) + (*pdest)]; } izi += izistep; pdest++; pz++; } while (--count > 0); } } /* ============== D_CalcGradients ============== */ static void D_CalcGradients (msurface_t *pface) { float mipscale; vec3_t p_temp1; vec3_t p_saxis, p_taxis; float t; mipscale = 1.0 / (float)(1 << miplevel); TransformVector (pface->texinfo->vecs[0], p_saxis); TransformVector (pface->texinfo->vecs[1], p_taxis); t = xscaleinv * mipscale; d_sdivzstepu = p_saxis[0] * t; d_tdivzstepu = p_taxis[0] * t; t = yscaleinv * mipscale; d_sdivzstepv = -p_saxis[1] * t; d_tdivzstepv = -p_taxis[1] * t; d_sdivzorigin = p_saxis[2] * mipscale - xcenter * d_sdivzstepu - ycenter * d_sdivzstepv; d_tdivzorigin = p_taxis[2] * mipscale - xcenter * d_tdivzstepu - ycenter * d_tdivzstepv; VectorScale (transformed_modelorg, mipscale, p_temp1); t = 0x10000*mipscale; sadjust = ((fixed16_t)(DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - ((pface->texturemins[0] << 16) >> miplevel) + pface->texinfo->vecs[0][3]*t; tadjust = ((fixed16_t)(DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) - ((pface->texturemins[1] << 16) >> miplevel) + pface->texinfo->vecs[1][3]*t; // // -1 (-epsilon) so we never wander off the edge of the texture // bbextents = ((pface->extents[0] << 16) >> miplevel) - 1; bbextentt = ((pface->extents[1] << 16) >> miplevel) - 1; } #if defined (H2W) //color for sky given a certain light level 0 - 25 static const int SiegeFlatSkyFadeTable[25] = { 0, // 1 0, 0, 1, 2, // 5 2, 3, 3, 3, 4, // 10 4, 4, 32, 32, 32, // 15 33, 33, 33, 34, 34, // 20 35, 35, 36, 36, 37 // 25 }; #endif /* H2W */ /* ============== D_DrawSurfaces ============== */ void D_DrawSurfaces (qboolean Translucent) { surf_t *s; msurface_t *pface; surfcache_t *pcurrentcache; vec3_t world_transformed_modelorg; vec3_t local_modelorg; int count; // Restore the settings currententity = &r_worldentity; // VectorCopy (world_transformed_modelorg, // transformed_modelorg); VectorCopy (base_vpn, vpn); VectorCopy (base_vup, vup); VectorCopy (base_vright, vright); VectorCopy (base_modelorg, modelorg); R_TransformFrustum (); TransformVector (modelorg, transformed_modelorg); VectorCopy (transformed_modelorg, world_transformed_modelorg); // TODO: could preset a lot of this at mode set time if (r_drawflat.integer) { if (Translucent) return; for (s = &surfaces[1] ; s < surface_p ; s++) { if (!s->spans) continue; d_zistepu = s->d_zistepu; d_zistepv = s->d_zistepv; d_ziorigin = s->d_ziorigin; D_DrawSolidSurface (s, (intptr_t)s->data & 0xFF); D_DrawZSpans (s->spans); } } else { if (!Translucent) { for (s = &surfaces[1] ; s < surface_p ; s++) { if (!s->spans) continue; r_drawnpolycount++; if (s->flags & SURF_TRANSLUCENT) continue; d_zistepu = s->d_zistepu; d_zistepv = s->d_zistepv; d_ziorigin = s->d_ziorigin; // if (!strncmp(pface->texinfo->texture->name,"*BLACK",6)) if (s->flags & SURF_DRAWBLACK) // black vis-breaker, no turb { # if defined (H2W) if (cl_siege) D_DrawSolidSurface (s, SiegeFlatSkyFadeTable[(int)floor(d_lightstylevalue[0]/22)]); else # endif /* H2W */ D_DrawSolidSurface (s, 0); D_DrawZSpans (s->spans); continue; } if (s->flags & SURF_DRAWSKY) { if (!r_skymade) { R_MakeSky (); } D_DrawSkyScans8 (s->spans); D_DrawZSpans (s->spans); } else if (s->flags & SURF_DRAWBACKGROUND) { // set up a gradient for the background surface that places it // effectively at infinity distance from the viewpoint d_zistepu = 0; d_zistepv = 0; d_ziorigin = -0.9; D_DrawSolidSurface (s, r_clearcolor.integer & 0xFF); D_DrawZSpans (s->spans); } else if (s->flags & SURF_DRAWTURB) { pface = (msurface_t *) s->data; miplevel = 0; cacheblock = (pixel_t *) ((byte *)pface->texinfo->texture + pface->texinfo->texture->offsets[0]); cachewidth = 64; if (s->insubmodel) { // FIXME: we don't want to do all this for every polygon! // TODO: store once at start of frame currententity = s->entity; //FIXME: make this passed in to // R_RotateBmodel () VectorSubtract (r_origin, currententity->origin, local_modelorg); TransformVector (local_modelorg, transformed_modelorg); R_RotateBmodel (); // FIXME: don't mess with the frustum, // make entity passed in } D_CalcGradients (pface); Turbulent8 (s); D_DrawZSpans (s->spans); if (s->insubmodel) { // // restore the old drawing state // FIXME: we don't want to do this every time! // TODO: speed up // currententity = &r_worldentity; VectorCopy (world_transformed_modelorg, transformed_modelorg); VectorCopy (base_vpn, vpn); VectorCopy (base_vup, vup); VectorCopy (base_vright, vright); VectorCopy (base_modelorg, modelorg); R_TransformFrustum (); } } else { if (s->insubmodel) { // FIXME: we don't want to do all this for every polygon! // TODO: store once at start of frame currententity = s->entity; //FIXME: make this passed in to // R_RotateBmodel () VectorSubtract (r_origin, currententity->origin, local_modelorg); TransformVector (local_modelorg, transformed_modelorg); R_RotateBmodel (); // FIXME: don't mess with the frustum, // make entity passed in } pface = (msurface_t *) s->data; if ((s->flags & SURF_DRAWSOLID) && ((s->entity->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT || !pface->samples)) { byte *pixels = ((byte *)pface->texinfo->texture + pface->texinfo->texture->offsets[0]); int light; if (!r_fullbright.integer) { if ((s->entity->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT) light = s->entity->abslight; else light = r_refdef.ambientlight; } else light = 255; D_DrawSolidSurface (s, ((unsigned char *)vid.colormap)[(((255-light)<nearzi * scale_for_mip * pface->texinfo->mipadjust); // FIXME: make this passed in to D_CacheSurface pcurrentcache = D_CacheSurface (pface, miplevel); cacheblock = (pixel_t *)pcurrentcache->data; cachewidth = pcurrentcache->width; D_CalcGradients (pface); (*d_drawspans) (s->spans); } D_DrawZSpans (s->spans); if (s->insubmodel) { // // restore the old drawing state // FIXME: we don't want to do this every time! // TODO: speed up // currententity = &r_worldentity; VectorCopy (world_transformed_modelorg, transformed_modelorg); VectorCopy (base_vpn, vpn); VectorCopy (base_vup, vup); VectorCopy (base_vright, vright); VectorCopy (base_modelorg, modelorg); R_TransformFrustum (); } } } } else { count = 0; for (s = &surfaces[1] ; s < surface_p ; s++) { if (!s->spans || !(s->flags & SURF_TRANSLUCENT)) continue; count++; d_zistepu = s->d_zistepu; d_zistepv = s->d_zistepv; d_ziorigin = s->d_ziorigin; // if (!strncmp(pface->texinfo->texture->name, "*BLACK", 6)) if (s->flags & SURF_DRAWBLACK) // black vis-breaker, no turb { # if defined (H2W) if (cl_siege) D_DrawSolidSurface (s, SiegeFlatSkyFadeTable[(int)floor(d_lightstylevalue[0]/22)]); else # endif /* H2W */ D_DrawSolidSurface (s, 0); D_DrawZSpans (s->spans); continue; } if (s->flags & SURF_DRAWTURB) { pface = (msurface_t *) s->data; miplevel = 0; cacheblock = (pixel_t *) ((byte *)pface->texinfo->texture + pface->texinfo->texture->offsets[0]); cachewidth = 64; if (s->insubmodel) { // FIXME: we don't want to do all this for every polygon! // TODO: store once at start of frame currententity = s->entity; //FIXME: make this passed in to // R_RotateBmodel () VectorSubtract (r_origin, currententity->origin, local_modelorg); TransformVector (local_modelorg, transformed_modelorg); R_RotateBmodel (); // FIXME: don't mess with the frustum, // make entity passed in } D_CalcGradients (pface); Turbulent8 (s); // D_DrawZSpans (s->spans); if (s->insubmodel) { // // restore the old drawing state // FIXME: we don't want to do this every time! // TODO: speed up // currententity = &r_worldentity; VectorCopy (world_transformed_modelorg, transformed_modelorg); VectorCopy (base_vpn, vpn); VectorCopy (base_vup, vup); VectorCopy (base_vright, vright); VectorCopy (base_modelorg, modelorg); R_TransformFrustum (); } } else { if (s->insubmodel) { // FIXME: we don't want to do all this for every polygon! // TODO: store once at start of frame currententity = s->entity; //FIXME: make this passed in to // R_RotateBmodel () VectorSubtract (r_origin, currententity->origin, local_modelorg); TransformVector (local_modelorg, transformed_modelorg); R_RotateBmodel (); // FIXME: don't mess with the frustum, // make entity passed in } pface = (msurface_t *) s->data; if ((s->flags & SURF_DRAWSOLID) && ((s->entity->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT || !pface->samples)) { byte *pixels = ((byte *)pface->texinfo->texture + pface->texinfo->texture->offsets[0]); int light; if (!r_fullbright.integer) { if ((s->entity->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT) light = s->entity->abslight; else light = r_refdef.ambientlight; } else light = 255; D_DrawSolidSurfaceT (s, ((unsigned char *)vid.colormap)[(((255-light)<nearzi * scale_for_mip * pface->texinfo->mipadjust); // FIXME: make this passed in to D_CacheSurface pcurrentcache = D_CacheSurface (pface, miplevel); cacheblock = (pixel_t *)pcurrentcache->data; cachewidth = pcurrentcache->width; D_CalcGradients (pface); // (*d_drawspans) (s->spans); #if id386 || id68k D_DrawSpans16T(s->spans); #else D_DrawSpans8T(s->spans); #endif } // D_DrawZSpans (s->spans); if (s->insubmodel) { // // restore the old drawing state // FIXME: we don't want to do this every time! // TODO: speed up // currententity = &r_worldentity; VectorCopy (world_transformed_modelorg, transformed_modelorg); VectorCopy (base_vpn, vpn); VectorCopy (base_vup, vup); VectorCopy (base_vright, vright); VectorCopy (base_modelorg, modelorg); R_TransformFrustum (); } } } // Con_Printf(" Surf is %d\n", count); } } } engine/h2shared/d_fill.c000066400000000000000000000036651444734033100154350ustar00rootroot00000000000000/* * d_clear.c -- clears a specified rectangle to the specified color * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" /* ================ D_FillRect ================ */ void D_FillRect (vrect_t *rect, int color) { int rx, ry, rwidth, rheight; unsigned char *dest; unsigned int *ldest; rx = rect->x; ry = rect->y; rwidth = rect->width; rheight = rect->height; if (rx < 0) { rwidth += rx; rx = 0; } if (ry < 0) { rheight += ry; ry = 0; } if (rx+rwidth > vid.width) rwidth = vid.width - rx; if (ry+rheight > vid.height) rheight = vid.height - rx; if (rwidth < 1 || rheight < 1) return; dest = ((byte *)vid.buffer + ry*vid.rowbytes + rx); if (((rwidth & 0x03) == 0) && (((intptr_t)dest & 0x03) == 0)) { // faster aligned dword clear ldest = (unsigned int *)dest; color += color << 16; rwidth >>= 2; color += color << 8; for (ry = 0; ry < rheight; ry++) { for (rx = 0; rx < rwidth; rx++) ldest[rx] = color; ldest = (unsigned int *)((byte*)ldest + vid.rowbytes); } } else { // slower byte-by-byte clear for unaligned cases for (ry = 0; ry < rheight; ry++) { for (rx = 0; rx < rwidth; rx++) dest[rx] = color; dest += vid.rowbytes; } } } engine/h2shared/d_iface.h000066400000000000000000000173611444734033100155610ustar00rootroot00000000000000/* * d_iface.h -- interface header file for rasterization driver modules * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef D_IFACE_H #define D_IFACE_H #define WARP_WIDTH 320 #define WARP_HEIGHT 200 #define MAX_SKIN_HEIGHT 480 typedef struct { float u, v; float s, t; float zi; } emitpoint_t; /* particle enums and types: note that hexen2 and hexenworld versions of these are different!! */ #include "particle.h" typedef struct polyvert_s { float u, v, zi, s, t; } polyvert_t; typedef struct polydesc_s { int numverts; float nearzi; msurface_t *pcurrentface; polyvert_t *pverts; } polydesc_t; // !!! if this is changed, it must be changed in d_ifacea.h too !!! typedef struct finalvert_s { int v[6]; // u, v, s, t, l, 1/z int flags; float reserved; } finalvert_t; // !!! if this is changed, it must be changed in d_ifacea.h too !!! typedef struct { void *pskin; maliasskindesc_t *pskindesc; int skinwidth; int skinheight; mtriangle_t *ptriangles; finalvert_t *pfinalverts; int numtriangles; int drawtype; int seamfixupX16; } affinetridesc_t; // !!! if this is changed, it must be changed in d_ifacea.h too !!! typedef struct { float u, v, zi, color; } screenpart_t; typedef struct { int nump; emitpoint_t *pverts; // there's room for an extra element at [nump], // if the driver wants to duplicate element [0] at // element [nump] to avoid dealing with wrapping mspriteframe_t *pspriteframe; vec3_t vup, vright, vpn; // in worldspace float nearzi; } spritedesc_t; typedef struct { int u, v; float zi; int color; } zpointdesc_t; extern cvar_t r_drawflat; extern int d_spanpixcount; ASM_LINKAGE_BEGIN extern int r_framecount; // sequence # of current frame since Quake // started ASM_LINKAGE_END extern qboolean r_drawpolys; // 1 if driver wants clipped polygons // rather than a span list extern qboolean r_drawculledpolys; // 1 if driver wants clipped polygons that // have been culled by the edge list extern qboolean r_worldpolysbacktofront; // 1 if driver wants polygons // delivered back to front rather // than front to back extern qboolean r_recursiveaffinetriangles; // true if a driver wants to use // recursive triangular subdivison // and vertex drawing via // D_PolysetDrawFinalVerts() past // a certain distance (normally // only used by the software // driver) extern float r_aliasuvscale; // scale-up factor for screen u and v // on Alias vertices passed to driver extern int r_pixbytes; extern qboolean r_dowarp; extern int d_con_indirect; // if 0, Quake will draw console directly // to vid.buffer; if 1, Quake will // draw console via D_DrawRect. Must be // defined by the driver (vid_*.c) ASM_LINKAGE_BEGIN extern affinetridesc_t r_affinetridesc; extern spritedesc_t r_spritedesc; extern zpointdesc_t r_zpointdesc; extern polydesc_t r_polydesc; extern vec3_t r_pright, r_pup, r_ppn; ASM_LINKAGE_END void D_DrawPoly (void); void D_DrawSprite (void); void D_DrawSurfaces (qboolean Translucent); ASM_LINKAGE_BEGIN void D_PolysetDraw (void); void D_PolysetDrawT (void); void D_PolysetDrawT2 (void); void D_PolysetDrawT3 (void); void D_PolysetDrawT5 (void); void D_PolysetDrawFinalVerts (finalvert_t *p1, finalvert_t *p2, finalvert_t *p3); void D_PolysetDrawFinalVertsT (finalvert_t *p1, finalvert_t *p2, finalvert_t *p3); void D_PolysetDrawFinalVertsT2 (finalvert_t *p1, finalvert_t *p2, finalvert_t *p3); void D_PolysetDrawFinalVertsT3 (finalvert_t *p1, finalvert_t *p2, finalvert_t *p3); void D_PolysetDrawFinalVertsT5 (finalvert_t *p1, finalvert_t *p2, finalvert_t *p3); #if id386 void D_DrawNonSubdiv (void); void D_PolysetCalcGradients (int skinwidth); void D_PolysetCalcGradientsT (int skinwidth); void D_PolysetCalcGradientsT2 (int skinwidth); void D_PolysetCalcGradientsT3 (int skinwidth); void D_PolysetCalcGradientsT5 (int skinwidth); void D_PolysetRecursiveTriangle (int *p1, int *p2, int *p3); void D_PolysetScanLeftEdge (int height); void D_PolysetScanLeftEdgeT (int height); void D_PolysetScanLeftEdgeT2 (int height); void D_PolysetScanLeftEdgeT3 (int height); void D_PolysetScanLeftEdgeT5 (int height); void D_DrawParticle1x1b (particle_t *pparticle); #endif #if id68k void D_DrawNonSubdiv (void); void D_PolysetCalcGradients (int skinwidth); void D_PolysetRecursiveTriangle (int *p1, int *p2, int *p3); void D_PolysetRecursiveTriangleT (int *p1, int *p2, int *p3); void D_PolysetRecursiveTriangleT2 (int *p1, int *p2, int *p3); void D_PolysetRecursiveTriangleT3 (int *p1, int *p2, int *p3); void D_PolysetRecursiveTriangleT5 (int *p1, int *p2, int *p3); //void D_PolysetScanLeftEdge (int height); #endif void D_DrawParticle (particle_t *pparticle); ASM_LINKAGE_END void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height); void D_EndDirectRect (int x, int y, int width, int height); void D_EnableBackBufferAccess (void); void D_DisableBackBufferAccess (void); void D_DrawZPoint (void); void D_Init (void); void D_ViewChanged (void); void D_SetupFrame (void); void D_TurnZOn (void); void D_WarpScreen (void); void D_FillRect (vrect_t *vrect, int color); void D_DrawRect (void); void D_UpdateRects (vrect_t *prect); void D_StartParticles (void); void D_EndParticles (void); // currently for internal use only, and should be a do-nothing function in // hardware drivers // FIXME: this should go away void D_PolysetUpdateTables (void); // these are currently for internal use only, and should not be used by drivers // FIXME: amiga m68k asm references this. ASM_LINKAGE_BEGIN extern byte *r_skysource; // !!! must be kept the same as in quakeasm.h !!! #define TRANSPARENT_COLOR 0xFF extern void *acolormap; // FIXME: should go away ASM_LINKAGE_END //=======================================================================// // callbacks to Quake typedef struct { pixel_t *surfdat; // destination for generated surface int rowbytes; // destination logical width in bytes msurface_t *surf; // description for surface to generate fixed8_t lightadj[MAXLIGHTMAPS]; // adjust for lightmap levels for dynamic lighting texture_t *texture; // corrected for animating textures int surfmip; // mipmapped ratio of surface texels / world pixels int surfwidth; // in mipmapped texels int surfheight; // in mipmapped texels } drawsurf_t; extern drawsurf_t r_drawsurf; void R_DrawSurface (void); void R_GenTile (msurface_t *psurf, void *pdest); // !!! if this is changed, it must be changed in d_ifacea.h too !!! #define TURB_TEX_SIZE 64 // base turbulent texture size // !!! if this is changed, it must be changed in d_ifacea.h too !!! #define CYCLE 128 // turbulent cycle size #define TILE_SIZE 128 // size of textures generated by R_GenTiledSurf #define SKYSHIFT 7 #define SKYSIZE (1 << SKYSHIFT) #define SKYMASK (SKYSIZE - 1) ASM_LINKAGE_BEGIN // amiga m68k asm references these : extern float skyspeed, skyspeed2; extern float skytime; ASM_LINKAGE_END extern int c_surf; extern vrect_t scr_vrect; extern byte *r_warpbuffer; #endif /* D_IFACE_H */ engine/h2shared/d_init.c000066400000000000000000000064551444734033100154520ustar00rootroot00000000000000/* d_init.c -- rasterization driver initialization * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "d_local.h" #define NUM_MIPS 4 static cvar_t d_subdiv16 = {"d_subdiv16", "1", CVAR_NONE}; static cvar_t d_mipcap = {"d_mipcap", "0", CVAR_NONE}; static cvar_t d_mipscale = {"d_mipscale", "1", CVAR_NONE}; surfcache_t *d_initial_rover; qboolean d_roverwrapped; int d_minmip; float d_scalemip[NUM_MIPS-1]; static float basemip[NUM_MIPS-1] = {1.0, 0.5*0.8, 0.25*0.8}; void (*d_drawspans) (espan_t *pspan); /* =============== D_Init =============== */ void D_Init (void) { Cvar_RegisterVariable (&d_subdiv16); Cvar_RegisterVariable (&d_mipcap); Cvar_RegisterVariable (&d_mipscale); #if 0 r_drawpolys = false; r_worldpolysbacktofront = false; #endif r_recursiveaffinetriangles = true; r_pixbytes = 1; r_aliasuvscale = 1.0; } /* =============== D_CopyRects =============== */ void D_CopyRects (vrect_t *prects, int transparent) { // this function is only required if the CPU doesn't have direct access to the // back buffer, and there's some driver interface function that the driver // doesn't support and requires Quake to do in software (such as drawing the // console); Quake will then draw into wherever the driver points vid.buffer // and will call this function before swapping buffers Q_UNUSED(prects); Q_UNUSED(transparent); } /* =============== D_EnableBackBufferAccess =============== */ void D_EnableBackBufferAccess (void) { VID_LockBuffer (); } /* =============== D_TurnZOn =============== */ void D_TurnZOn (void) { // not needed for software version } /* =============== D_DisableBackBufferAccess =============== */ void D_DisableBackBufferAccess (void) { VID_UnlockBuffer (); } /* =============== D_SetupFrame =============== */ void D_SetupFrame (void) { int i; if (r_dowarp) d_viewbuffer = r_warpbuffer; else d_viewbuffer = vid.buffer; if (r_dowarp) screenwidth = WARP_WIDTH; else screenwidth = vid.rowbytes; d_roverwrapped = false; d_initial_rover = sc_rover; d_minmip = d_mipcap.integer; if (d_minmip > 3) d_minmip = 3; else if (d_minmip < 0) d_minmip = 0; for (i = 0; i < (NUM_MIPS-1); i++) d_scalemip[i] = basemip[i] * d_mipscale.value; #if id386 || id68k if (d_subdiv16.integer) d_drawspans = D_DrawSpans16; else d_drawspans = D_DrawSpans8; #else d_drawspans = D_DrawSpans8; #endif d_aflatcolor = 0; } /* =============== D_UpdateRects =============== */ void D_UpdateRects (vrect_t *prect) { // the software driver draws these directly to the vid buffer Q_UNUSED(prect); } engine/h2shared/d_local.h000066400000000000000000000140531444734033100155770ustar00rootroot00000000000000/* * d_local.h -- private rasterization driver defs * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef D_LOCAL_H #define D_LOCAL_H #include "r_shared.h" // // TODO: fine-tune this; it's based on providing some overage even if there // is a 2k-wide scan, with subdivision every 8, for 256 spans of 12 bytes each // #define SCANBUFFERPAD 0x1000 #define R_SKY_SMASK 0x007F0000 #define R_SKY_TMASK 0x007F0000 #define DS_SPAN_LIST_END -128 //#define SURFCACHE_SIZE_AT_320X200 600*1024 #define SURFCACHE_SIZE_AT_320X200 768*1024 typedef struct surfcache_s { struct surfcache_s *next; struct surfcache_s **owner; // NULL is an empty chunk of memory int lightadj[MAXLIGHTMAPS]; // checked for strobe flush int dlight; int size; // including header unsigned int width; unsigned int height; // DEBUG only needed for debug float mipscale; struct texture_s *texture; // checked for animating textures int drawflags; int abslight; byte data[4]; // width*height elements } surfcache_t; // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct sspan_s { int u, v, count; } sspan_t; // TODO: put in span spilling to shrink list size // !!! if this is changed, it must be changed in d_polysa.s too !!! #define DPS_MAXSPANS (MAXHEIGHT + 1) // 1 extra for spanpackage that marks end // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct { void *pdest; short *pz; int count; byte *ptex; int sfrac, tfrac, light, zi; } spanpackage_t; typedef struct { int isflattop; int numleftedges; int *pleftedgevert0; int *pleftedgevert1; int *pleftedgevert2; int numrightedges; int *prightedgevert0; int *prightedgevert1; int *prightedgevert2; } edgetable_t; extern float scale_for_mip; extern qboolean d_roverwrapped; extern surfcache_t *sc_rover; extern surfcache_t *d_initial_rover; ASM_LINKAGE_BEGIN extern float d_sdivzstepu, d_tdivzstepu, d_zistepu; extern float d_sdivzstepv, d_tdivzstepv, d_zistepv; extern float d_sdivzorigin, d_tdivzorigin, d_ziorigin; extern fixed16_t sadjust, tadjust; extern fixed16_t bbextents, bbextentt; ASM_LINKAGE_END extern void (*d_drawspans) (espan_t *pspan); ASM_LINKAGE_BEGIN void D_DrawSpans8 (espan_t *pspans); void D_DrawSpans8T(espan_t *pspans); void D_DrawZSpans (espan_t *pspans); void D_DrawSingleZSpans (espan_t *pspans); #if id386 void D_DrawSpans16 (espan_t *pspans); void D_DrawSpans16T (espan_t *pspans); void D_SpriteDrawSpans (sspan_t *pspan); void D_SpriteDrawSpansT (sspan_t *pspan); void D_SpriteDrawSpansT2 (sspan_t *pspan); void D_DrawTurbulent8Span (void); void D_DrawTurbulent8TSpan (void); void D_DrawTurbulent8TQuickSpan (void); void D_PolysetDrawSpans8 (spanpackage_t *pspanpackage); void D_PolysetDrawSpans8T (spanpackage_t *pspanpackage); void D_PolysetDrawSpans8T2 (spanpackage_t *pspanpackage); void D_PolysetDrawSpans8T3 (spanpackage_t *pspanpackage); void D_PolysetDrawSpans8T5 (spanpackage_t *pspanpackage); void D_Draw16StartT (void); void D_Draw16EndT (void); void D_DrawTurbulent8TSpanEnd (void); void D_PolysetAff8Start (void); void D_PolysetAff8StartT (void); void D_PolysetAff8StartT2 (void); void D_PolysetAff8StartT3 (void); void D_PolysetAff8StartT5 (void); void D_PolysetAff8End (void); void D_PolysetAff8EndT (void); void D_PolysetAff8EndT2 (void); void D_PolysetAff8EndT3 (void); void D_PolysetAff8EndT5 (void); void D_SpriteSpansStartT (void); void D_SpriteSpansEndT (void); void D_SpriteSpansStartT2 (void); void D_SpriteSpansEndT2 (void); void D_Aff8Patch (void *pcolormap); void D_Aff8PatchT (void *pcolormap); void D_Aff8PatchT2 (void *pcolormap); void D_Aff8PatchT3 (void *pcolormap); void D_Aff8PatchT5 (void *pcolormap); void R_TranPatch1 (void); void R_TranPatch2 (void); void R_TranPatch3 (void); void R_TranPatch4 (void); void R_TranPatch5 (void); void R_TranPatch6 (void); void R_TranPatch7 (void); #endif /* id386 */ #if id68k void D_DrawTurbulent8 (espan_t *pspan); void D_DrawTurbulent8T (espan_t *pspan); void D_DrawSpans16 (espan_t *pspans); void D_DrawSpans16T (espan_t *pspans); void D_SpriteDrawSpans (sspan_t *pspan); void D_SpriteDrawSpansT (sspan_t *pspan); void D_SpriteDrawSpansT2 (sspan_t *pspan); void D_PolysetDrawSpans8 (spanpackage_t *pspanpackage); void D_PolysetDrawSpans8T (spanpackage_t *pspanpackage); void D_PolysetDrawSpans8T2 (spanpackage_t *pspanpackage); void D_PolysetDrawSpans8T3 (spanpackage_t *pspanpackage); void D_PolysetDrawSpans8T5 (spanpackage_t *pspanpackage); #endif /* id68k */ /* C funcs called from asm code: */ void D_PolysetSetEdgeTable (void); void D_RasterizeAliasPolySmooth (void); ASM_LINKAGE_END void Turbulent8 (surf_t *s); void D_DrawSkyScans8 (espan_t *pspan); void D_DrawSkyScans16 (espan_t *pspan); surfcache_t *D_CacheSurface (msurface_t *surface, int miplevel); void D_Patch (void); ASM_LINKAGE_BEGIN extern short *d_pzbuffer; extern int d_zrowbytes, d_zwidth; extern int d_scantable[MAXHEIGHT]; extern int d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle; extern int d_y_aspect_shift, d_y_aspect_rshift, d_pix_min, d_pix_max, d_pix_shift; extern pixel_t *d_viewbuffer; extern short *zspantable[MAXHEIGHT]; #define SCAN_SIZE 2048 extern byte scanList[SCAN_SIZE]; extern int ZScanCount; extern int d_aflatcolor; ASM_LINKAGE_END extern int d_minmip; extern float d_scalemip[3]; #endif /* D_LOCAL_H */ engine/h2shared/d_modech.c000066400000000000000000000061111444734033100157330ustar00rootroot00000000000000/* * d_modech.c -- called when mode has just changed. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "d_local.h" int d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle; int d_y_aspect_shift, d_y_aspect_rshift, d_pix_min, d_pix_max, d_pix_shift; int d_scantable[MAXHEIGHT]; short *zspantable[MAXHEIGHT]; /* ================ D_Patch ================ */ #if id386 void D_Patch (void) { static qboolean protectset8 = false; if (!protectset8) { Sys_MakeCodeWriteable ((int)D_PolysetAff8Start, (int)D_PolysetAff8End - (int)D_PolysetAff8Start); Sys_MakeCodeWriteable ((int)D_PolysetAff8StartT, (int)D_PolysetAff8EndT - (int)D_PolysetAff8StartT); Sys_MakeCodeWriteable ((int)D_PolysetAff8StartT2, (int)D_PolysetAff8EndT2 - (int)D_PolysetAff8StartT2); Sys_MakeCodeWriteable ((int)D_PolysetAff8StartT3, (int)D_PolysetAff8EndT3 - (int)D_PolysetAff8StartT3); Sys_MakeCodeWriteable ((int)D_PolysetAff8StartT5, (int)D_PolysetAff8EndT5 - (int)D_PolysetAff8StartT5); protectset8 = true; } } #endif /* id386 */ /* ================ D_ViewChanged ================ */ void D_ViewChanged (void) { int i, rowbytes; if (r_dowarp) rowbytes = WARP_WIDTH; else rowbytes = vid.rowbytes; scale_for_mip = xscale; if (yscale > xscale) scale_for_mip = yscale; d_zrowbytes = vid.width * 2; d_zwidth = vid.width; d_pix_min = r_refdef.vrect.width / 320; if (d_pix_min < 1) d_pix_min = 1; d_pix_max = (int)((float)r_refdef.vrect.width / (320.0 / 4.0) + 0.5); d_pix_shift = 8 - (int)((float)r_refdef.vrect.width / 320.0 + 0.5); if (d_pix_max < 1) d_pix_max = 1; if (pixelAspect > 1.4) { d_y_aspect_shift = 1; d_y_aspect_rshift = 0; } else { d_y_aspect_shift = 0; if (pixelAspect < 0.27) d_y_aspect_rshift = 2; else if (pixelAspect < 0.54) d_y_aspect_rshift = 1; else d_y_aspect_rshift = 0; } d_vrectx = r_refdef.vrect.x; d_vrecty = r_refdef.vrect.y; d_vrectright_particle = r_refdef.vrectright - d_pix_max; #if id386 d_vrectbottom_particle = r_refdef.vrectbottom - (d_pix_max << d_y_aspect_shift); #else d_vrectbottom_particle = r_refdef.vrectbottom - ((d_pix_max << d_y_aspect_shift) >> d_y_aspect_rshift); #endif for (i = 0; i < vid.height; i++) { d_scantable[i] = i*rowbytes; zspantable[i] = d_pzbuffer + i*d_zwidth; } #if id386 D_Patch (); #endif } engine/h2shared/d_part.c000066400000000000000000000112761444734033100154520ustar00rootroot00000000000000/* * d_part.c -- software driver module for drawing particles. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2007-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "d_local.h" /* ============== D_EndParticles ============== */ void D_EndParticles (void) { // not used by software driver } /* ============== D_StartParticles ============== */ void D_StartParticles (void) { // not used by software driver } #if !id386 && !id68k /* ============== D_DrawParticle ============== */ void D_DrawParticle (particle_t *pparticle) { vec3_t local, transformed; float zi; byte *pdest; short *pz; int i, izi, pix, count, u, v; int color; qboolean is_trans; // transform point VectorSubtract (pparticle->org, r_origin, local); transformed[0] = DotProduct(local, r_pright); transformed[1] = DotProduct(local, r_pup); transformed[2] = DotProduct(local, r_ppn); if (transformed[2] < PARTICLE_Z_CLIP) return; // project the point // FIXME: preadjust xcenter and ycenter zi = 1.0 / transformed[2]; u = (int)(xcenter + zi * transformed[0] + 0.5); v = (int)(ycenter - zi * transformed[1] + 0.5); if ((v > d_vrectbottom_particle) || (u > d_vrectright_particle) || (v < d_vrecty) || (u < d_vrectx)) { return; } pz = d_pzbuffer + (d_zwidth * v) + u; pdest = d_viewbuffer + d_scantable[v] + u; izi = (int)(zi * 0x8000); pix = izi >> d_pix_shift; if (pix < d_pix_min) pix = d_pix_min; else if (pix > d_pix_max) pix = d_pix_max; /* clamp color to 0-511: particle->type 10 and 11 (pt_c_explode * and pt_c_explode2, e.g. Crusader's ice particles hitting a * wall) lead to negative values, because R_UpdateParticles () * decrements their color against time. */ color = ((int) pparticle->color) & 0x01ff; if ((is_trans = (color >= 256))) color = (color - 256) << 8; /* << 8 for transTable idx */ switch (pix) { case 1: count = 1 << d_y_aspect_shift; count >>= d_y_aspect_rshift; for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) { if (pz[0] <= izi) { pz[0] = izi; pdest[0] = (!is_trans) ? color : transTable[color + pdest[0]]; } } break; case 2: count = 2 << d_y_aspect_shift; count >>= d_y_aspect_rshift; for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) { if (pz[0] <= izi) { pz[0] = izi; pdest[0] = (!is_trans) ? color : transTable[color + pdest[0]]; } if (pz[1] <= izi) { pz[1] = izi; pdest[1] = (!is_trans) ? color : transTable[color + pdest[1]]; } } break; case 3: count = 3 << d_y_aspect_shift; count >>= d_y_aspect_rshift; for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) { if (pz[0] <= izi) { pz[0] = izi; pdest[0] = (!is_trans) ? color : transTable[color + pdest[0]]; } if (pz[1] <= izi) { pz[1] = izi; pdest[1] = (!is_trans) ? color : transTable[color + pdest[1]]; } if (pz[2] <= izi) { pz[2] = izi; pdest[2] = (!is_trans) ? color : transTable[color + pdest[2]]; } } break; case 4: count = 4 << d_y_aspect_shift; count >>= d_y_aspect_rshift; for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) { if (pz[0] <= izi) { pz[0] = izi; pdest[0] = (!is_trans) ? color : transTable[color + pdest[0]]; } if (pz[1] <= izi) { pz[1] = izi; pdest[1] = (!is_trans) ? color : transTable[color + pdest[1]]; } if (pz[2] <= izi) { pz[2] = izi; pdest[2] = (!is_trans) ? color : transTable[color + pdest[2]]; } if (pz[3] <= izi) { pz[3] = izi; pdest[3] = (!is_trans) ? color : transTable[color + pdest[3]]; } } break; default: count = pix << d_y_aspect_shift; count >>= d_y_aspect_rshift; for ( ; count ; count--, pz += d_zwidth, pdest += screenwidth) { for (i = 0 ; i < pix ; i++) { if (pz[i] <= izi) { pz[i] = izi; pdest[i] = (!is_trans) ? color : transTable[color + pdest[i]]; } } } break; } } #endif /* !id386 */ engine/h2shared/d_part68k.s000066400000000000000000000261041444734033100160170ustar00rootroot00000000000000** ** Quake for AMIGA ** d_part.c assembler implementations by Frank Wille ** Transparent version for Hexen II by Szilard Biro ** ; INCLUDE "quakedef68k.i" PART_COLOR equ 12 XREF _r_origin XREF _r_pright XREF _r_pup XREF _r_ppn XREF _xcenter XREF _ycenter XREF _d_vrectbottom_particle XREF _d_vrectright_particle XREF _d_vrecty XREF _d_vrectx XREF _d_pzbuffer XREF _d_zwidth XREF _d_viewbuffer XREF _d_scantable XREF _d_y_aspect_shift XREF _d_pix_min XREF _d_pix_max XREF _d_pix_shift XREF _screenwidth XREF _transTable; XDEF _D_DrawParticle PARTICLE_Z_CLIP equ.s 8.0 ;must match the values in d_iface.h! ****************************************************************************** * * void _D_DrawParticle (particle_t *pparticle) * ****************************************************************************** cnop 0,4 _D_DrawParticle rsreset .fpuregs rs.x 4 .regs rs.l 9 rs.l 1 .pparticle rs.l 1 movem.l d2-d7/a2-a4,-(sp) fmovem.x fp2-fp5,-(sp) move.l .pparticle(sp),a2 lea _r_origin,a0 fmove.s (a2)+,fp0 fsub.s (a0)+,fp0 ;fp0 = local[0] fmove.s (a2)+,fp1 fsub.s (a0)+,fp1 ;fp1 = local[1] fmove.s (a2)+,fp2 fsub.s (a0)+,fp2 ;fp2 = local[2] lea -12(a2),a2 lea _r_pright,a1 fmove.s (a1)+,fp3 fmul fp0,fp3 fmove.s (a1)+,fp4 fmul fp1,fp4 fadd fp4,fp3 fmove.s (a1)+,fp4 fmul fp2,fp4 fadd fp4,fp3 ;fp3 = transformed[0] lea _r_pup,a1 fmove.s (a1)+,fp4 fmul fp0,fp4 fmove.s (a1)+,fp5 fmul fp1,fp5 fadd fp5,fp4 fmove.s (a1)+,fp5 fmul fp2,fp5 fadd fp5,fp4 ;fp4 = transformed[1] lea _r_ppn,a1 fmul.s (a1)+,fp0 fmul.s (a1)+,fp1 fadd fp0,fp1 fmul.s (a1)+,fp2 fadd fp1,fp2 ;fp2 = transformed[2] fcmp.s #PARTICLE_Z_CLIP,fp2 fblt.w .exit fmove.s #1,fp0 fdiv fp2,fp0 ;zi = 1.0 / transformed[2] fmove.s #0.5,fp1 fmul fp0,fp3 fmul fp0,fp4 fadd fp1,fp3 fadd fp1,fp4 fadd.s _xcenter,fp3 fmove.l fp3,d0 ;d0 = u fsub.s _ycenter,fp4 fneg fp4 fmove.l fp4,d1 ;d1 = v cmp.l _d_vrectbottom_particle,d1 bgt.w .exit cmp.l _d_vrectright_particle,d0 bgt.w .exit cmp.l _d_vrecty,d1 blt.w .exit cmp.l _d_vrectx,d0 blt.w .exit move.l _d_pzbuffer,a0 lea _d_scantable,a1 move.l 0(a1,d1.l*4),a1 move.l _d_zwidth,d2 ;d2 = d_zwidth muls d2,d1 add.l d0,d1 lea 0(a0,d1.l*2),a0 ;a0 = pz add.l d0,a1 add.l _d_viewbuffer,a1 ;a1 = pdest fmul.s #32768,fp0 fmove.l fp0,d0 ;izi = (int)(zi * 0x8000) move.l d0,d5 ;d5 = izi move.l _d_pix_shift,d1 asr.l d1,d0 ;d0 = pix move.l _d_pix_min,d1 cmp.l d1,d0 bgt.b .cont move.l d1,d0 .cont move.l _d_pix_max,d1 cmp.l d1,d0 blt.b .cont2 move.l d1,d0 .cont2 fmove.s PART_COLOR(a2),fp0 fmove.l fp0,d4 move.l _screenwidth,d1 move.l _d_y_aspect_shift,d3 and.l #511,d4 ; clamp color to 0-511 cmp.l #255,d4 bgt.w .is_trans cmp.l #4,d0 ;switch (pix) bgt.b .more beq.b .four cmp.l #2,d0 bgt.b .three beq.b .two .one ;case 1 lsl d3,d0 subq #1,d0 .one_loop cmp (a0),d5 ;if (pz[0] <= izi) blt.b .one_next move d5,(a0) ;pz[0] = izi move.b d4,(a1) ;pdest[0] = pparticle->color .one_next add.l d1,a1 ;pdest += screenwidth lea 0(a0,d2.l*2),a0 ;pz += d_zwidth dbra d0,.one_loop ;count-- bra.w .exit .two ;case 2 lsl d3,d0 subq #1,d0 .two_loop cmp (a0),d5 ;if (pz[0] <= izi) blt.b .two_2 move d5,(a0) ;pz[0] = izi move.b d4,(a1) ;pdest[0] = pparticle->color .two_2 cmp 2(a0),d5 ;if (pz[1] <= izi) blt.b .two_next move d5,2(a0) ;pz[1] = izi move.b d4,1(a1) ;pdest[1] = pparticle->color .two_next add.l d1,a1 ;pdest += screenwidth lea 0(a0,d2.l*2),a0 ;pz += d_zwidth dbra d0,.two_loop ;count-- bra.w .exit .three ;case3 lsl d3,d0 subq #1,d0 .three_loop cmp (a0),d5 ;if (pz[0] <= izi) blt.b .three_2 move d5,(a0) ;pz[0] = izi move.b d4,(a1) ;pdest[0] = pparticle->color .three_2 cmp 2(a0),d5 ;if (pz[1] <= izi) blt.b .three_3 move d5,2(a0) ;pz[1] = izi move.b d4,1(a1) ;pdest[1] = pparticle->color .three_3 cmp 4(a0),d5 ;if (pz[2] <= izi) blt.b .three_next move d5,4(a0) ;pz[2] = izi move.b d4,2(a1) ;pdest[2] = pparticle->color .three_next add.l d1,a1 ;pdest += screenwidth lea 0(a0,d2.l*2),a0 ;pz += d_zwidth dbra d0,.three_loop ;count-- bra.b .exit .four ;case4 lsl d3,d0 subq #1,d0 .four_loop cmp (a0),d5 ;if (pz[0] <= izi) blt.b .four_2 move d5,(a0) ;pz[0] = izi move.b d4,(a1) ;pdest[0] = pparticle->color .four_2 cmp 2(a0),d5 ;if (pz[1] <= izi) blt.b .four_3 move d5,2(a0) ;pz[1] = izi move.b d4,1(a1) ;pdest[1] = pparticle->color .four_3 cmp 4(a0),d5 ;if (pz[2] <= izi) blt.b .four_4 move d5,4(a0) ;pz[2] = izi move.b d4,2(a1) ;pdest[2] = pparticle->color .four_4 cmp 6(a0),d5 ;if (pz[3] <= izi) blt.b .four_next move d5,6(a0) ;pz[3] = izi move.b d4,3(a1) ;pdest[3] = pparticle->color .four_next add.l d1,a1 ;pdest += screenwidth lea 0(a0,d2.l*2),a0 ;pz += d_zwidth dbra d0,.four_loop ;count-- bra.b .exit .more move d0,d6 subq #1,d6 lsl d3,d0 subq #1,d0 .more_loop move.l a0,a2 move.l a1,a3 move d6,d7 .more_loop2 addq.l #1,a3 cmp (a2)+,d5 ;if (pz[i] <= izi) blt.b .more_next move d5,-2(a2) ;pz[i] = izi move.b d4,-1(a3) ;pdest[i] = pparticle->color .more_next dbra d7,.more_loop2 add.l d1,a1 ;pdest += screenwidth lea 0(a0,d2.l*2),a0 ;pz += d_zwidth dbra d0,.more_loop .is_trans add.w #-256,d4 lsl.w #8,d4 ; << 8 for transTable idx move.l _transTable,a4 cmp.l #4,d0 ;switch (pix) bgt.b .t_more beq.b .t_four cmp.l #2,d0 bgt.b .t_three beq.b .t_two .t_one ;case 1 lsl d3,d0 subq #1,d0 .t_one_loop cmp (a0),d5 ;if (pz[0] <= izi) blt.b .t_one_next move d5,(a0) ;pz[0] = izi move.b (a1),d4 move.b (a4,d4.l),(a1) ;pdest[0] = transTable[pparticle->color + pdest[0]]; .t_one_next add.l d1,a1 ;pdest += screenwidth lea 0(a0,d2.l*2),a0 ;pz += d_zwidth dbra d0,.t_one_loop ;count-- bra.w .exit .t_two ;case 2 lsl d3,d0 subq #1,d0 .t_two_loop cmp (a0),d5 ;if (pz[0] <= izi) blt.b .t_two_2 move d5,(a0) ;pz[0] = izi move.b (a1),d4 move.b (a4,d4.l),(a1) ;pdest[0] = transTable[pparticle->color + pdest[0]]; .t_two_2 cmp 2(a0),d5 ;if (pz[1] <= izi) blt.b .t_two_next move d5,2(a0) ;pz[1] = izi move.b 1(a1),d4 move.b (a4,d4.l),1(a1) ;pdest[1] = transTable[pparticle->color + pdest[1]]; .t_two_next add.l d1,a1 ;pdest += screenwidth lea 0(a0,d2.l*2),a0 ;pz += d_zwidth dbra d0,.t_two_loop ;count-- bra.w .exit .t_three ;case3 lsl d3,d0 subq #1,d0 .t_three_loop cmp (a0),d5 ;if (pz[0] <= izi) blt.b .t_three_2 move d5,(a0) ;pz[0] = izi move.b (a1),d4 move.b (a4,d4.l),(a1) ;pdest[0] = transTable[pparticle->color + pdest[0]]; .t_three_2 cmp 2(a0),d5 ;if (pz[1] <= izi) blt.b .t_three_3 move d5,2(a0) ;pz[1] = izi move.b 1(a1),d4 move.b (a4,d4.l),1(a1) ;pdest[1] = transTable[pparticle->color + pdest[1]]; .t_three_3 cmp 4(a0),d5 ;if (pz[2] <= izi) blt.b .t_three_next move d5,4(a0) ;pz[2] = izi move.b 2(a1),d4 move.b (a4,d4.l),2(a1) ;pdest[2] = transTable[pparticle->color + pdest[2]]; .t_three_next add.l d1,a1 ;pdest += screenwidth lea 0(a0,d2.l*2),a0 ;pz += d_zwidth dbra d0,.t_three_loop ;count-- bra.b .exit .t_four ;case4 lsl d3,d0 subq #1,d0 .t_four_loop cmp (a0),d5 ;if (pz[0] <= izi) blt.b .t_four_2 move d5,(a0) ;pz[0] = izi move.b (a1),d4 move.b (a4,d4.l),(a1) ;pdest[0] = transTable[pparticle->color + pdest[0]]; .t_four_2 cmp 2(a0),d5 ;if (pz[1] <= izi) blt.b .t_four_3 move d5,2(a0) ;pz[1] = izi move.b 1(a1),d4 move.b (a4,d4.l),1(a1) ;pdest[1] = transTable[pparticle->color + pdest[1]]; .t_four_3 cmp 4(a0),d5 ;if (pz[2] <= izi) blt.b .t_four_4 move d5,4(a0) ;pz[2] = izi move.b 2(a1),d4 move.b (a4,d4.l),2(a1) ;pdest[2] = transTable[pparticle->color + pdest[2]]; .t_four_4 cmp 6(a0),d5 ;if (pz[3] <= izi) blt.b .t_four_next move d5,6(a0) ;pz[3] = izi move.b 3(a1),d4 move.b (a4,d4.l),3(a1) ;pdest[3] = transTable[pparticle->color + pdest[3]]; .t_four_next add.l d1,a1 ;pdest += screenwidth lea 0(a0,d2.l*2),a0 ;pz += d_zwidth dbra d0,.t_four_loop ;count-- bra.b .exit .t_more move d0,d6 subq #1,d6 lsl d3,d0 subq #1,d0 .t_more_loop move.l a0,a2 move.l a1,a3 move d6,d7 .t_more_loop2 addq.l #1,a3 cmp (a2)+,d5 ;if (pz[i] <= izi) blt.b .t_more_next move d5,-2(a2) ;pz[i] = izi move.b -1(a3),d4 move.b (a4,d4.l),-1(a3) ;pdest[i] = transTable[pparticle->color + pdest[i]]; .t_more_next dbra d7,.t_more_loop2 add.l d1,a1 ;pdest += screenwidth lea 0(a0,d2.l*2),a0 ;pz += d_zwidth dbra d0,.t_more_loop .exit fmovem.x (sp)+,fp2-fp5 movem.l (sp)+,d2-d7/a2-a4 rts engine/h2shared/d_parta.asm000066400000000000000000000331671444734033100161540ustar00rootroot00000000000000; ; d_parta.asm ; x86 assembly-language 8-bpp particle-drawing code. ; ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix d_pzbuffer _sym_prefix d_zrowbytes _sym_prefix d_viewbuffer _sym_prefix d_scantable _sym_prefix r_origin _sym_prefix r_ppn _sym_prefix r_pup _sym_prefix r_pright _sym_prefix ycenter _sym_prefix xcenter _sym_prefix d_vrectbottom_particle _sym_prefix d_vrectright_particle _sym_prefix d_vrecty _sym_prefix d_vrectx _sym_prefix d_pix_shift _sym_prefix d_pix_min _sym_prefix d_pix_max _sym_prefix d_y_aspect_shift _sym_prefix screenwidth _sym_prefix transTable ; C-shared globals: _sym_prefix D_DrawParticle %endif ; _sym_prefix ; externs from C code extern d_pzbuffer extern d_zrowbytes extern d_viewbuffer extern d_scantable extern r_origin extern r_ppn extern r_pup extern r_pright extern ycenter extern xcenter extern d_vrectbottom_particle extern d_vrectright_particle extern d_vrecty extern d_vrectx extern d_pix_shift extern d_pix_min extern d_pix_max extern d_y_aspect_shift extern screenwidth extern transTable ; externs from ASM-only code extern float_point5 extern izistep extern izi extern float_1 extern float_particle_z_clip extern float_minus_1 extern float_0 extern DP_Count extern DP_u extern DP_v extern DP_32768 extern DP_Color extern DP_Pix extern DP_EntryTable extern DP_EntryTransTable SEGMENT .text ALIGN 4 ;;;;;;;;;;;;;;;;;;;;;;;; ; D_DrawParticle ;;;;;;;;;;;;;;;;;;;;;;;; global D_DrawParticle D_DrawParticle: push ebp push edi push ebx mov edi, dword [12+4+esp] fld dword [r_origin] fsubr dword [0+edi] fld dword [0+4+edi] fsub dword [r_origin+4] fld dword [0+8+edi] fsub dword [r_origin+8] fxch st2 fld dword [r_ppn] fmul st0,st1 fld dword [r_ppn+4] fmul st0,st3 fld dword [r_ppn+8] fmul st0,st5 fxch st2 faddp st1,st0 faddp st1,st0 fld st0 fdivr dword [float_1] fxch st1 fcomp dword [float_particle_z_clip] fxch st3 fld dword [r_pup] fmul st0,st2 fld dword [r_pup+4] fnstsw ax test ah,1 jnz near LPop6AndDone fmul st0,st4 fld dword [r_pup+8] fmul st0,st3 fxch st2 faddp st1,st0 faddp st1,st0 fxch st3 fmul dword [r_pright+4] fxch st2 fmul dword [r_pright] fxch st1 fmul dword [r_pright+8] fxch st2 faddp st1,st0 faddp st1,st0 fxch st1 fmul st0,st2 fxch st1 fmul st0,st2 fxch st1 fsubr dword [ycenter] fxch st1 fadd dword [xcenter] fxch st1 fadd dword [float_point5] fxch st1 fadd dword [float_point5] fxch st2 fmul dword [DP_32768] fxch st2 fistp dword [DP_u] fistp dword [DP_v] mov eax, dword [DP_u] mov edx, dword [DP_v] mov ebx, dword [d_vrectbottom_particle] mov ecx, dword [d_vrectright_particle] cmp edx,ebx jg near LPop1AndDone cmp eax,ecx jg near LPop1AndDone mov ebx, dword [d_vrecty] mov ecx, dword [d_vrectx] cmp edx,ebx jl near LPop1AndDone cmp eax,ecx jl near LPop1AndDone fld dword [12+edi] fistp dword [DP_Color] mov ebx, dword [d_viewbuffer] add ebx,eax mov edi, dword [d_scantable+edx*4] imul edx, dword [d_zrowbytes] lea edx, [edx+eax*2] mov eax, dword [d_pzbuffer] fistp dword [izi] add edi,ebx add edx,eax mov eax, dword [izi] mov ecx, dword [d_pix_shift] shr eax,cl mov ebp, dword [izi] mov ebx, dword [d_pix_min] mov ecx, dword [d_pix_max] cmp eax,ebx jnl LTestPixMax mov eax,ebx jmp LTestDone LTestPixMax: cmp eax,ecx jng LTestDone mov eax,ecx LTestDone: mov cx, word [DP_Color] mov ebx, dword [d_y_aspect_shift] test ebx,ebx jnz near LDefault cmp eax,4 ja near LDefault test ch,ch jnz Trans jmp dword[DP_EntryTable-4+eax*4] Trans: and ecx, 0ffh mov ch, cl jmp dword[DP_EntryTransTable-4+eax*4] ;;;;;;;;;;;;;;;;;;;;;;;; ; globals DP_?x? ;;;;;;;;;;;;;;;;;;;;;;;; global DP_1x1 DP_1x1: cmp word [edx],bp jg near LDone mov word [edx],bp mov byte [edi],cl jmp LDone global DP_2x2 DP_2x2: push esi mov ebx, dword [screenwidth] mov esi, dword [d_zrowbytes] cmp word [edx],bp jg L2x2_1 mov word [edx],bp mov byte [edi],cl L2x2_1: cmp word [2+edx],bp jg L2x2_2 mov word [2+edx],bp mov byte [1+edi],cl L2x2_2: cmp word [edx+esi*1],bp jg L2x2_3 mov word [edx+esi*1],bp mov byte [edi+ebx*1],cl L2x2_3: cmp word [2+edx+esi*1],bp jg L2x2_4 mov word [2+edx+esi*1],bp mov byte [1+edi+ebx*1],cl L2x2_4: pop esi jmp LDone global DP_3x3 DP_3x3: push esi mov ebx, dword [screenwidth] mov esi, dword [d_zrowbytes] cmp word [edx],bp jg L3x3_1 mov word [edx],bp mov byte [edi],cl L3x3_1: cmp word [2+edx],bp jg L3x3_2 mov word [2+edx],bp mov byte [1+edi],cl L3x3_2: cmp word [4+edx],bp jg L3x3_3 mov word [4+edx],bp mov byte [2+edi],cl L3x3_3: cmp word [edx+esi*1],bp jg L3x3_4 mov word [edx+esi*1],bp mov byte [edi+ebx*1],cl L3x3_4: cmp word [2+edx+esi*1],bp jg L3x3_5 mov word [2+edx+esi*1],bp mov byte [1+edi+ebx*1],cl L3x3_5: cmp word [4+edx+esi*1],bp jg L3x3_6 mov word [4+edx+esi*1],bp mov byte [2+edi+ebx*1],cl L3x3_6: cmp word [edx+esi*2],bp jg L3x3_7 mov word [edx+esi*2],bp mov byte [edi+ebx*2],cl L3x3_7: cmp word [2+edx+esi*2],bp jg L3x3_8 mov word [2+edx+esi*2],bp mov byte [1+edi+ebx*2],cl L3x3_8: cmp word [4+edx+esi*2],bp jg L3x3_9 mov word [4+edx+esi*2],bp mov byte [2+edi+ebx*2],cl L3x3_9: pop esi jmp LDone global DP_4x4 DP_4x4: push esi mov ebx, dword [screenwidth] mov esi, dword [d_zrowbytes] cmp word [edx],bp jg L4x4_1 mov word [edx],bp mov byte [edi],cl L4x4_1: cmp word [2+edx],bp jg L4x4_2 mov word [2+edx],bp mov byte [1+edi],cl L4x4_2: cmp word [4+edx],bp jg L4x4_3 mov word [4+edx],bp mov byte [2+edi],cl L4x4_3: cmp word [6+edx],bp jg L4x4_4 mov word [6+edx],bp mov byte [3+edi],cl L4x4_4: cmp word [edx+esi*1],bp jg L4x4_5 mov word [edx+esi*1],bp mov byte [edi+ebx*1],cl L4x4_5: cmp word [2+edx+esi*1],bp jg L4x4_6 mov word [2+edx+esi*1],bp mov byte [1+edi+ebx*1],cl L4x4_6: cmp word [4+edx+esi*1],bp jg L4x4_7 mov word [4+edx+esi*1],bp mov byte [2+edi+ebx*1],cl L4x4_7: cmp word [6+edx+esi*1],bp jg L4x4_8 mov word [6+edx+esi*1],bp mov byte [3+edi+ebx*1],cl L4x4_8: lea edx, [edx+esi*2] lea edi, [edi+ebx*2] cmp word [edx],bp jg L4x4_9 mov word [edx],bp mov byte [edi],cl L4x4_9: cmp word [2+edx],bp jg L4x4_10 mov word [2+edx],bp mov byte [1+edi],cl L4x4_10: cmp word [4+edx],bp jg L4x4_11 mov word [4+edx],bp mov byte [2+edi],cl L4x4_11: cmp word [6+edx],bp jg L4x4_12 mov word [6+edx],bp mov byte [3+edi],cl L4x4_12: cmp word [edx+esi*1],bp jg L4x4_13 mov word [edx+esi*1],bp mov byte [edi+ebx*1],cl L4x4_13: cmp word [2+edx+esi*1],bp jg L4x4_14 mov word [2+edx+esi*1],bp mov byte [1+edi+ebx*1],cl L4x4_14: cmp word [4+edx+esi*1],bp jg L4x4_15 mov word [4+edx+esi*1],bp mov byte [2+edi+ebx*1],cl L4x4_15: cmp word [6+edx+esi*1],bp jg L4x4_16 mov word [6+edx+esi*1],bp mov byte [3+edi+ebx*1],cl L4x4_16: pop esi jmp LDone ;;;;;;;;;;;;;;;;;;;;;;;; ; globals DP_T?x? ;;;;;;;;;;;;;;;;;;;;;;;; global DP_T1x1 DP_T1x1: mov eax, dword [transTable] cmp word [edx],bp jg near LDone mov word [edx],bp mov cl, byte [edi] mov cl, byte [eax+ecx] mov byte [edi],cl jmp LDone global DP_T2x2 DP_T2x2: mov eax, dword [transTable] push esi mov ebx, dword [screenwidth] mov esi, dword [d_zrowbytes] cmp word [edx],bp jg LT2x2_1 mov word [edx],bp mov cl, byte [edi] mov cl, byte [eax+ecx] mov byte [edi],cl LT2x2_1: cmp word [2+edx],bp jg LT2x2_2 mov word [2+edx],bp mov cl, byte [1+edi] mov cl, byte [eax+ecx] mov byte [1+edi],cl LT2x2_2: cmp word [edx+esi*1],bp jg LT2x2_3 mov word [edx+esi*1],bp mov cl, byte [edi+ebx*1] mov cl, byte [eax+ecx] mov byte [edi+ebx*1],cl LT2x2_3: cmp word [2+edx+esi*1],bp jg LT2x2_4 mov word [2+edx+esi*1],bp mov cl, byte [1+edi+ebx*1] mov cl, byte [eax+ecx] mov byte [1+edi+ebx*1],cl LT2x2_4: pop esi jmp LDone global DP_T3x3 DP_T3x3: mov eax, dword [transTable] push esi mov ebx, dword [screenwidth] mov esi, dword [d_zrowbytes] cmp word [edx],bp jg LT3x3_1 mov word [edx],bp mov cl, byte [edi] mov cl, byte [eax+ecx] mov byte [edi],cl LT3x3_1: cmp word [2+edx],bp jg LT3x3_2 mov word [2+edx],bp mov cl, byte [1+edi] mov cl, byte [eax+ecx] mov byte [1+edi],cl LT3x3_2: cmp word [4+edx],bp jg LT3x3_3 mov word [4+edx],bp mov cl, byte [2+edi] mov cl, byte [eax+ecx] mov byte [2+edi],cl LT3x3_3: cmp word [edx+esi*1],bp jg LT3x3_4 mov word [edx+esi*1],bp mov cl, byte [edi+ebx*1] mov cl, byte [eax+ecx] mov byte [edi+ebx*1],cl LT3x3_4: cmp word [2+edx+esi*1],bp jg LT3x3_5 mov word [2+edx+esi*1],bp mov cl, byte [1+edi+ebx*1] mov cl, byte [eax+ecx] mov byte [1+edi+ebx*1],cl LT3x3_5: cmp word [4+edx+esi*1],bp jg LT3x3_6 mov word [4+edx+esi*1],bp mov cl, byte [2+edi+ebx*1] mov cl, byte [eax+ecx] mov byte [2+edi+ebx*1],cl LT3x3_6: cmp word [edx+esi*2],bp jg LT3x3_7 mov word [edx+esi*2],bp mov cl, byte [edi+ebx*2] mov cl, byte [eax+ecx] mov byte [edi+ebx*2],cl LT3x3_7: cmp word [2+edx+esi*2],bp jg LT3x3_8 mov word [2+edx+esi*2],bp mov cl, byte [1+edi+ebx*2] mov cl, byte [eax+ecx] mov byte [1+edi+ebx*2],cl LT3x3_8: cmp word [4+edx+esi*2],bp jg LT3x3_9 mov word [4+edx+esi*2],bp mov cl, byte [2+edi+ebx*2] mov cl, byte [eax+ecx] mov byte [2+edi+ebx*2],cl LT3x3_9: pop esi jmp LDone global DP_T4x4 DP_T4x4: mov eax, dword [transTable] push esi mov ebx, dword [screenwidth] mov esi, dword [d_zrowbytes] cmp word [edx],bp jg LT4x4_1 mov word [edx],bp mov cl, byte [edi] mov cl, byte [eax+ecx] mov byte [edi],cl LT4x4_1: cmp word [2+edx],bp jg LT4x4_2 mov word [2+edx],bp mov cl, byte [1+edi] mov cl, byte [eax+ecx] mov byte [1+edi],cl LT4x4_2: cmp word [4+edx],bp jg LT4x4_3 mov word [4+edx],bp mov cl, byte [2+edi] mov cl, byte [eax+ecx] mov byte [2+edi],cl LT4x4_3: cmp word [6+edx],bp jg LT4x4_4 mov word [6+edx],bp mov cl, byte [3+edi] mov cl, byte [eax+ecx] mov byte [3+edi],cl LT4x4_4: cmp word [edx+esi*1],bp jg LT4x4_5 mov word [edx+esi*1],bp mov cl, byte [edi+ebx*1] mov cl, byte [eax+ecx] mov byte [edi+ebx*1],cl LT4x4_5: cmp word [2+edx+esi*1],bp jg LT4x4_6 mov word [2+edx+esi*1],bp mov cl, byte [1+edi+ebx*1] mov cl, byte [eax+ecx] mov byte [1+edi+ebx*1],cl LT4x4_6: cmp word [4+edx+esi*1],bp jg LT4x4_7 mov word [4+edx+esi*1],bp mov cl, byte [2+edi+ebx*1] mov cl, byte [eax+ecx] mov byte [2+edi+ebx*1],cl LT4x4_7: cmp word [6+edx+esi*1],bp jg LT4x4_8 mov word [6+edx+esi*1],bp mov cl, byte [3+edi+ebx*1] mov cl, byte [eax+ecx] mov byte [3+edi+ebx*1],cl LT4x4_8: lea edx,[edx+esi*2] lea edi, [edi+ebx*2] cmp word [edx],bp jg LT4x4_9 mov word [edx],bp mov cl, byte [edi] mov cl, byte [eax+ecx] mov byte [edi],cl LT4x4_9: cmp word [2+edx],bp jg LT4x4_10 mov word [2+edx],bp mov cl, byte [1+edi] mov cl, byte [eax+ecx] mov byte [1+edi],cl LT4x4_10: cmp word [4+edx],bp jg LT4x4_11 mov word [4+edx],bp mov cl, byte [2+edi] mov cl, byte [eax+ecx] mov byte [2+edi],cl LT4x4_11: cmp word [6+edx],bp jg LT4x4_12 mov word [6+edx],bp mov cl, byte [3+edi] mov cl, byte [eax+ecx] mov byte [3+edi],cl LT4x4_12: cmp word [edx+esi*1],bp jg LT4x4_13 mov word [edx+esi*1],bp mov cl, byte [edi+ebx*1] mov cl, byte [eax+ecx] mov byte [edi+ebx*1],cl LT4x4_13: cmp word [2+edx+esi*1],bp jg LT4x4_14 mov word [2+edx+esi*1],bp mov cl, byte [1+edi+ebx*1] mov cl, byte [eax+ecx] mov byte [1+edi+ebx*1],cl LT4x4_14: cmp word [4+edx+esi*1],bp jg LT4x4_15 mov word [4+edx+esi*1],bp mov cl, byte [2+edi+ebx*1] mov cl, byte [eax+ecx] mov byte [2+edi+ebx*1],cl LT4x4_15: cmp word [6+edx+esi*1],bp jg LT4x4_16 mov word [6+edx+esi*1],bp mov cl, byte [3+edi+ebx*1] mov cl, byte [eax+ecx] mov byte [3+edi+ebx*1],cl LT4x4_16: pop esi jmp LDone LDefault: test ch,ch jnz LTDefault mov ebx,eax mov dword [DP_Pix],eax push cx mov cl, byte [d_y_aspect_shift] shl ebx,cl pop cx LGenRowLoop: mov eax, dword [DP_Pix] LGenColLoop: cmp word [-2+edx+eax*2],bp jg LGSkip mov word [-2+edx+eax*2],bp mov byte [-1+edi+eax*1],cl LGSkip: dec eax jnz LGenColLoop add edx, dword [d_zrowbytes] add edi, dword [screenwidth] dec ebx jnz LGenRowLoop jmp LDone LTDefault: push esi and ecx, 0ffh mov ch, cl mov esi, dword [transTable] mov ebx,eax mov dword [DP_Pix],eax mov cl, byte [d_y_aspect_shift] shl ebx,cl LTGenRowLoop: mov eax, dword [DP_Pix] LTGenColLoop: cmp word [-2+edx+eax*2],bp jg LTGSkip mov word [-2+edx+eax*2],bp mov cl, byte [-1+edi+eax*1] mov cl, byte [esi+ecx] mov byte [-1+edi+eax*1],cl LTGSkip: dec eax jnz LTGenColLoop add edx, dword [d_zrowbytes] add edi, dword [screenwidth] dec ebx jnz LTGenRowLoop pop esi LDone: pop ebx pop edi pop ebp ret LPop6AndDone: fstp st0 fstp st0 fstp st0 fstp st0 fstp st0 LPop1AndDone: fstp st0 jmp LDone engine/h2shared/d_partb.asm000066400000000000000000000133461444734033100161520ustar00rootroot00000000000000; ; d_partb.asm ; x86 assembly-language 8-bpp particle-drawing code. ; ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix d_pzbuffer _sym_prefix d_zrowbytes _sym_prefix d_viewbuffer _sym_prefix d_scantable _sym_prefix r_origin _sym_prefix r_ppn _sym_prefix r_pup _sym_prefix r_pright _sym_prefix ycenter _sym_prefix xcenter _sym_prefix d_vrectbottom_particle _sym_prefix d_vrectright_particle _sym_prefix d_vrecty _sym_prefix d_vrectx _sym_prefix d_pix_shift _sym_prefix d_pix_min _sym_prefix d_pix_max _sym_prefix d_y_aspect_shift _sym_prefix screenwidth _sym_prefix transTable ; C-shared globals: _sym_prefix D_DrawParticle1x1b %endif ; _sym_prefix ; externs from C code extern d_pzbuffer extern d_zrowbytes extern d_viewbuffer extern d_scantable extern r_origin extern r_ppn extern r_pup extern r_pright extern ycenter extern xcenter extern d_vrectbottom_particle extern d_vrectright_particle extern d_vrecty extern d_vrectx extern d_pix_shift extern d_pix_min extern d_pix_max extern d_y_aspect_shift extern screenwidth extern transTable ; externs from ASM-only code extern float_point5 extern izistep extern izi extern float_1 extern float_particle_z_clip extern float_minus_1 extern float_0 extern DP_Count extern DP_u extern DP_v extern DP_32768 extern DP_Color extern DP_Pix extern DP_EntryTable SEGMENT .text ALIGN 4 ;;;;;;;;;;;;;;;;;;;;;;;; ; D_DrawParticle1x1b ;;;;;;;;;;;;;;;;;;;;;;;; global D_DrawParticle1x1b D_DrawParticle1x1b: push ebp push edi push ebx mov edi, dword [12+4+esp] fld dword [r_origin] fsubr dword [0+edi] fld dword [0+4+edi] fsub dword [r_origin+4] fld dword [0+8+edi] fsub dword [r_origin+8] fxch st2 fld dword [r_ppn] fmul st0,st1 fld dword [r_ppn+4] fmul st0,st3 fld dword [r_ppn+8] fmul st0,st5 fxch st2 faddp st1,st0 faddp st1,st0 fld st0 fdivr dword [float_1] fxch st1 fcomp dword [float_particle_z_clip] fxch st3 fld dword [r_pup] fmul st0,st2 fld dword [r_pup+4] fnstsw ax test ah,1 jnz near LPop6AndDone fmul st0,st4 fld dword [r_pup+8] fmul st0,st3 fxch st2 faddp st1,st0 faddp st1,st0 fxch st3 fmul dword [r_pright+4] fxch st2 fmul dword [r_pright] fxch st1 fmul dword [r_pright+8] fxch st2 faddp st1,st0 faddp st1,st0 fxch st1 fmul st0,st2 fxch st1 fmul st0,st2 fxch st1 fsubr dword [ycenter] fxch st1 fadd dword [xcenter] fxch st1 fadd dword [float_point5] fxch st1 fadd dword [float_point5] fxch st2 fmul dword [DP_32768] fxch st2 fistp dword [DP_u] fistp dword [DP_v] mov eax, dword [DP_u] mov edx, dword [DP_v] mov ebx, dword [d_vrectbottom_particle] mov ecx, dword [d_vrectright_particle] cmp edx,ebx jg near LPop1AndDone cmp eax,ecx jg near LPop1AndDone mov ebx, dword [d_vrecty] mov ecx, dword [d_vrectx] cmp edx,ebx jl near LPop1AndDone cmp eax,ecx jl near LPop1AndDone fld dword [12+edi] fistp dword [DP_Color] mov ebx, dword [d_viewbuffer] add ebx,eax mov edi, dword [d_scantable+edx*4] imul edx, dword [d_zrowbytes] lea edx, [edx+eax*2] mov eax, dword [d_pzbuffer] fistp dword [izi] add edi,ebx add edx,eax mov eax, dword [izi] mov ecx, dword [d_pix_shift] shr eax,cl mov ebp, dword [izi] mov ebx, dword [d_pix_min] mov ecx, dword [d_pix_max] cmp eax,ebx jnl LTestPixMax mov eax,ebx jmp LTestDone LTestPixMax: cmp eax,ecx jng LTestDone mov eax,ecx LTestDone: mov cx, word [DP_Color] ; get color mov ebx, dword [d_y_aspect_shift] test ebx,ebx jnz LDefault cmp eax,4 ja LDefault ;;;;;;;;;;;;;;;;;;;;;;;; ; globals DP_?x?b ;;;;;;;;;;;;;;;;;;;;;;;; global DP_1x1b DP_1x1b: cmp word [edx],bp jg near LDone ; mov word [edx],bp test ch,ch jnz Trans mov byte [edi],cl jmp LDone Trans: and ecx, 0ffh mov ch, cl mov eax, dword [transTable] mov cl, byte [edi] mov cl, byte [eax+ecx] mov byte [edi], cl jmp LDone LDefault: test ch,ch jnz LTDefault mov ebx,eax mov dword [DP_Pix],eax push cx mov cl, byte [d_y_aspect_shift] shl ebx,cl pop cx LGenRowLoop: mov eax, dword [DP_Pix] LGenColLoop: cmp word [-2+edx+eax*2],bp jg LGSkip mov word [-2+edx+eax*2],bp mov byte [-1+edi+eax*1],cl LGSkip: dec eax jnz LGenColLoop add edx, dword [d_zrowbytes] add edi, dword [screenwidth] dec ebx jnz LGenRowLoop jmp LDone LTDefault: push esi and ecx, 0ffh mov ch, cl mov esi, dword [transTable] mov ebx,eax mov dword [DP_Pix],eax mov cl, byte [d_y_aspect_shift] shl ebx,cl LTGenRowLoop: mov eax, dword [DP_Pix] LTGenColLoop: cmp word [-2+edx+eax*2],bp jg LTGSkip mov word [-2+edx+eax*2],bp mov cl, byte [-1+edi+eax*1] mov cl, byte [esi+ecx] mov byte [-1+edi+eax*1],cl LTGSkip: dec eax jnz LTGenColLoop add edx, dword [d_zrowbytes] add edi, dword [screenwidth] dec ebx jnz LTGenRowLoop pop esi LDone: pop ebx pop edi pop ebp ret LPop6AndDone: fstp st0 fstp st0 fstp st0 fstp st0 fstp st0 LPop1AndDone: fstp st0 jmp LDone engine/h2shared/d_polysa.asm000066400000000000000000000525651444734033100163570ustar00rootroot00000000000000; ; d_polysa.asm ; x86 assembly-language polygon model drawing code ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" %include "d_polysa.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix d_viewbuffer _sym_prefix d_scantable _sym_prefix r_refdef _sym_prefix a_sstepxfrac _sym_prefix r_affinetridesc _sym_prefix acolormap _sym_prefix d_pcolormap _sym_prefix d_sfrac _sym_prefix d_ptex _sym_prefix d_pedgespanpackage _sym_prefix d_tfrac _sym_prefix d_light _sym_prefix d_zi _sym_prefix d_pdest _sym_prefix d_pz _sym_prefix d_aspancount _sym_prefix erroradjustup _sym_prefix erroradjustdown _sym_prefix errorterm _sym_prefix d_xdenom _sym_prefix r_p0 _sym_prefix r_p1 _sym_prefix r_p2 _sym_prefix a_tstepxfrac _sym_prefix a_ststepxwhole _sym_prefix zspantable _sym_prefix skintable _sym_prefix d_countextrastep _sym_prefix ubasestep _sym_prefix a_spans _sym_prefix d_pdestextrastep _sym_prefix d_pzextrastep _sym_prefix d_sfracextrastep _sym_prefix d_ptexextrastep _sym_prefix d_tfracextrastep _sym_prefix d_lightextrastep _sym_prefix d_ziextrastep _sym_prefix d_pdestbasestep _sym_prefix d_pzbasestep _sym_prefix d_sfracbasestep _sym_prefix d_ptexbasestep _sym_prefix d_tfracbasestep _sym_prefix d_lightbasestep _sym_prefix d_zibasestep _sym_prefix r_lstepx _sym_prefix r_lstepy _sym_prefix r_sstepx _sym_prefix r_sstepy _sym_prefix r_tstepx _sym_prefix r_tstepy _sym_prefix r_zistepx _sym_prefix r_zistepy _sym_prefix D_PolysetSetEdgeTable _sym_prefix D_RasterizeAliasPolySmooth ; C-shared globals: _sym_prefix D_PolysetCalcGradients _sym_prefix D_PolysetRecursiveTriangle _sym_prefix D_PolysetAff8Start _sym_prefix D_PolysetDrawSpans8 _sym_prefix D_PolysetAff8End _sym_prefix D_Aff8Patch _sym_prefix D_PolysetDraw _sym_prefix D_PolysetScanLeftEdge _sym_prefix D_PolysetDrawFinalVerts _sym_prefix D_DrawNonSubdiv %endif ; _sym_prefix ; externs from C code extern d_viewbuffer extern d_scantable extern r_refdef extern a_sstepxfrac extern r_affinetridesc extern acolormap extern d_pcolormap extern d_sfrac extern d_ptex extern d_pedgespanpackage extern d_tfrac extern d_light extern d_zi extern d_pdest extern d_pz extern d_aspancount extern erroradjustup extern erroradjustdown extern errorterm extern d_xdenom extern r_p0 extern r_p1 extern r_p2 extern a_tstepxfrac extern a_ststepxwhole extern zspantable extern skintable extern d_countextrastep extern ubasestep extern a_spans extern d_pdestextrastep extern d_pzextrastep extern d_sfracextrastep extern d_ptexextrastep extern d_tfracextrastep extern d_lightextrastep extern d_ziextrastep extern d_pdestbasestep extern d_pzbasestep extern d_sfracbasestep extern d_ptexbasestep extern d_tfracbasestep extern d_lightbasestep extern d_zibasestep extern r_lstepx extern r_lstepy extern r_sstepx extern r_sstepy extern r_tstepx extern r_tstepy extern r_zistepx extern r_zistepy extern D_PolysetSetEdgeTable extern D_RasterizeAliasPolySmooth ; externs from ASM-only code extern float_point5 extern float_1 extern float_minus_1 extern float_0 extern advancetable extern sstep extern tstep extern ceil_cw extern single_cw SEGMENT .data ALIGN 4 p10_minus_p20 dd 0 p01_minus_p21 dd 0 temp0 dd 0 temp1 dd 0 Ltemp dd 0 aff8entryvec_table dd LDraw8, LDraw7, LDraw6, LDraw5 dd LDraw4, LDraw3, LDraw2, LDraw1 lzistepx dd 0 SEGMENT .text ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetCalcGradients ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetCalcGradients D_PolysetCalcGradients: fild dword [r_p0+0] fild dword [r_p2+0] fild dword [r_p0+4] fild dword [r_p2+4] fild dword [r_p1+0] fild dword [r_p1+4] fxch st3 fsub st0,st2 fxch st1 fsub st0,st4 fxch st5 fsubrp st4,st0 fxch st2 fsubrp st1,st0 fxch st1 fld dword [d_xdenom] fxch st4 fstp dword [p10_minus_p20] fstp dword [p01_minus_p21] fxch st2 fild dword [r_p2+16] fild dword [r_p0+16] fild dword [r_p1+16] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmul st0,st5 fxch st2 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmul st0,st5 fxch st2 fsubrp st3,st0 fsubp st1,st0 fld st2 fmul dword [float_minus_1] fxch st2 fmul st0,st3 fxch st1 fmul st0,st2 fldcw word [ceil_cw] fistp dword [r_lstepy] fistp dword [r_lstepx] fldcw word [single_cw] fild dword [r_p2+8] fild dword [r_p0+8] fild dword [r_p1+8] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmul st0,st6 fxch st2 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmul st0,st6 fxch st2 fsubrp st3,st0 fsubp st1,st0 fmul st0,st2 fxch st1 fmul st0,st3 fxch st1 fistp dword [r_sstepy] fistp dword [r_sstepx] fild dword [r_p2+12] fild dword [r_p0+12] fild dword [r_p1+12] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmul st0,st6 fxch st2 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmul st0,st6 fxch st2 fsubrp st3,st0 fsubp st1,st0 fmul st0,st2 fxch st1 fmul st0,st3 fxch st1 fistp dword [r_tstepy] fistp dword [r_tstepx] fild dword [r_p2+20] fild dword [r_p0+20] fild dword [r_p1+20] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmulp st6,st0 fxch st1 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmulp st5,st0 fxch st5 fsubp st1,st0 fxch st3 fsubrp st4,st0 fxch st1 fmulp st2,st0 fmulp st2,st0 fistp dword [r_zistepx] fistp dword [r_zistepy] mov eax, dword [r_sstepx] mov edx, dword [r_tstepx] shl eax,16 shl edx,16 mov dword [a_sstepxfrac],eax mov dword [a_tstepxfrac],edx mov ecx, dword [r_sstepx] mov eax, dword [r_tstepx] sar ecx,16 sar eax,16 imul dword [4+0+esp] add eax,ecx mov dword [a_ststepxwhole],eax ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetRecursiveTriangle ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetRecursiveTriangle D_PolysetRecursiveTriangle: push ebp push esi push edi push ebx mov esi, dword [8+16+esp] mov ebx, dword [4+16+esp] mov edi, dword [12+16+esp] mov eax, dword [0+esi] mov edx, dword [0+ebx] mov ebp, dword [4+esi] sub eax,edx mov ecx, dword [4+ebx] sub ebp,ecx inc eax cmp eax,2 ja LSplit mov eax, dword [0+edi] inc ebp cmp ebp,2 ja LSplit mov edx, dword [0+esi] mov ebp, dword [4+edi] sub eax,edx mov ecx, dword [4+esi] sub ebp,ecx inc eax cmp eax,2 ja LSplit2 mov eax, dword [0+ebx] inc ebp cmp ebp,2 ja LSplit2 mov edx, dword [0+edi] mov ebp, dword [4+ebx] sub eax,edx mov ecx, dword [4+edi] sub ebp,ecx inc eax inc ebp mov edx,ebx cmp eax,2 ja LSplit3 cmp ebp,2 jna near LDone LSplit3: mov ebx,edi mov edi,esi mov esi,edx jmp LSplit LSplit2: mov eax,ebx mov ebx,esi mov esi,edi mov edi,eax LSplit: sub esp,24 mov eax, dword [8+ebx] mov edx, dword [8+esi] mov ecx, dword [12+ebx] add eax,edx mov edx, dword [12+esi] sar eax,1 add ecx,edx mov dword [8+esp],eax mov eax, dword [20+ebx] sar ecx,1 mov edx, dword [20+esi] mov dword [12+esp],ecx add eax,edx mov ecx, dword [0+ebx] mov edx, dword [0+esi] sar eax,1 add edx,ecx mov dword [20+esp],eax mov eax, dword [4+ebx] sar edx,1 mov ebp, dword [4+esi] mov dword [0+esp],edx add ebp,eax sar ebp,1 mov dword [4+esp],ebp cmp dword [4+esi],eax jg LNoDraw mov edx, dword [0+esi] jnz LDraw cmp edx,ecx jl LNoDraw LDraw: mov edx, dword [20+esp] mov ecx, dword [4+esp] sar edx,16 mov ebp, dword [0+esp] mov eax, dword [zspantable+ecx*4] cmp dx, word [eax+ebp*2] jnge LNoDraw mov word [eax+ebp*2],dx mov eax, dword [12+esp] sar eax,16 mov edx, dword [8+esp] sar edx,16 sub ecx,ecx mov eax, dword [skintable+eax*4] mov ebp, dword [4+esp] mov cl, byte [eax+edx] mov edx, dword [d_pcolormap] mov dl, byte [edx+ecx] mov ecx, dword [0+esp] mov eax, dword [d_scantable+ebp*4] add ecx,eax mov eax, dword [d_viewbuffer] mov byte [eax+ecx*1],dl LNoDraw: push esp push ebx push edi call D_PolysetRecursiveTriangle ; call near D_PolysetRecursiveTriangle mov ebx,esp push esi push ebx push edi call D_PolysetRecursiveTriangle ; call near D_PolysetRecursiveTriangle add esp,24 LDone: pop ebx pop edi pop esi pop ebp ret 12 ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetAff8Start ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetAff8Start D_PolysetAff8Start: ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetDrawSpans8 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetDrawSpans8 D_PolysetDrawSpans8: push esi push ebx mov esi, dword [4+8+esp] mov ecx, dword [r_zistepx] push ebp push edi ror ecx,16 mov edx, dword [8+esi] mov dword [lzistepx],ecx LSpanLoop: mov eax, dword [d_aspancount] sub eax,edx mov edx, dword [erroradjustup] mov ebx, dword [errorterm] add ebx,edx js LNoTurnover mov edx, dword [erroradjustdown] mov edi, dword [d_countextrastep] sub ebx,edx mov ebp, dword [d_aspancount] mov dword [errorterm],ebx add ebp,edi mov dword [d_aspancount],ebp jmp LRightEdgeStepped LNoTurnover: mov edi, dword [d_aspancount] mov edx, dword [ubasestep] mov dword [errorterm],ebx add edi,edx mov dword [d_aspancount],edi LRightEdgeStepped: cmp eax,1 jl near LNextSpan jz near LExactlyOneLong mov ecx, dword [a_ststepxwhole] mov edx, dword [r_affinetridesc+8] mov dword [advancetable+4],ecx add ecx,edx mov dword [advancetable],ecx mov ecx, dword [a_tstepxfrac] mov cx, word [r_lstepx] mov edx,eax mov dword [tstep],ecx add edx,7 shr edx,3 mov ebx, dword [16+esi] mov bx,dx mov ecx, dword [4+esi] neg eax mov edi, dword [0+esi] and eax,7 sub edi,eax sub ecx,eax sub ecx,eax mov edx, dword [20+esi] mov dx, word [24+esi] mov ebp, dword [28+esi] ror ebp,16 push esi mov esi, dword [12+esi] jmp dword[aff8entryvec_table+eax*4] LDrawLoop: LDraw8: cmp bp, word [ecx] jl Lp1 xor eax,eax mov ah,dh mov al, byte [esi] mov word [ecx],bp mov al, byte [12345678h+eax] LPatch8: mov byte [edi],al Lp1: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw7: cmp bp, word [2+ecx] jl Lp2 xor eax,eax mov ah,dh mov al, byte [esi] mov word [2+ecx],bp mov al, byte [12345678h+eax] LPatch7: mov byte [1+edi],al Lp2: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw6: cmp bp, word [4+ecx] jl Lp3 xor eax,eax mov ah,dh mov al, byte [esi] mov word [4+ecx],bp mov al, byte [12345678h+eax] LPatch6: mov byte [2+edi],al Lp3: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw5: cmp bp, word [6+ecx] jl Lp4 xor eax,eax mov ah,dh mov al, byte [esi] mov word [6+ecx],bp mov al, byte [12345678h+eax] LPatch5: mov byte [3+edi],al Lp4: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw4: cmp bp, word [8+ecx] jl Lp5 xor eax,eax mov ah,dh mov al, byte [esi] mov word [8+ecx],bp mov al, byte [12345678h+eax] LPatch4: mov byte [4+edi],al Lp5: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw3: cmp bp, word [10+ecx] jl Lp6 xor eax,eax mov ah,dh mov al, byte [esi] mov word [10+ecx],bp mov al, byte [12345678h+eax] LPatch3: mov byte [5+edi],al Lp6: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw2: cmp bp, word [12+ecx] jl Lp7 xor eax,eax mov ah,dh mov al, byte [esi] mov word [12+ecx],bp mov al, byte [12345678h+eax] LPatch2: mov byte [6+edi],al Lp7: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw1: cmp bp, word [14+ecx] jl Lp8 xor eax,eax mov ah,dh mov al, byte [esi] mov word [14+ecx],bp mov al, byte [12345678h+eax] LPatch1: mov byte [7+edi],al Lp8: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] add edi,8 add ecx,16 dec bx jnz near LDrawLoop pop esi LNextSpan: add esi,32 LNextSpanESISet: mov edx, dword [8+esi] cmp edx,offset -999999 jnz near LSpanLoop pop edi pop ebp pop ebx pop esi ret LExactlyOneLong: mov ecx, dword [4+esi] mov ebp, dword [28+esi] ror ebp,16 mov ebx, dword [12+esi] cmp bp, word [ecx] jl LNextSpan xor eax,eax mov edi, dword [0+esi] mov ah, byte [24+1+esi] add esi,32 mov al, byte [ebx] mov word [ecx],bp mov al, byte [12345678h+eax] LPatch9: mov byte [edi],al jmp LNextSpanESISet ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetAff8End ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetAff8End D_PolysetAff8End: ;;;;;;;;;;;;;;;;;;;;;;;; ; D_Aff8Patch ;;;;;;;;;;;;;;;;;;;;;;;; global D_Aff8Patch D_Aff8Patch: mov eax, dword [4+esp] mov dword [LPatch1-4],eax mov dword [LPatch2-4],eax mov dword [LPatch3-4],eax mov dword [LPatch4-4],eax mov dword [LPatch5-4],eax mov dword [LPatch6-4],eax mov dword [LPatch7-4],eax mov dword [LPatch8-4],eax mov dword [LPatch9-4],eax ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetDraw ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetDraw D_PolysetDraw: sub esp,offset SPAN_SIZE mov eax,esp add eax,32 - 1 and eax,offset ~(32 - 1) mov dword [a_spans],eax mov eax, dword [r_affinetridesc+28] test eax,eax jz near D_DrawNonSubdiv ;push ebp ;mov ebp, dword [r_affinetridesc+24] push esi ;shl ebp,4 push ebx mov ebx, dword [r_affinetridesc+16] push edi mov edi, dword [r_affinetridesc+20] Llooptop: xor ecx,ecx xor esi,esi mov cx,word[4+0+ebx] mov si,word[4+2+ebx] xor edx,edx mov dx,word[4+4+ebx] shl ecx,5 add ecx,edi shl esi,5 shl edx,5 add esi,edi add edx,edi fild dword [0+4+ecx] fild dword [0+4+esi] fild dword [0+0+ecx] fild dword [0+0+edx] fxch st2 fsubr st0,st3 fild dword [0+0+esi] fxch st2 fsubr st3,st0 fild dword [0+4+edx] fxch st1 fsubrp st3,st0 fxch st1 fmulp st3,st0 fsubp st3,st0 mov eax, dword [0+16+ecx] and eax,0FF00h fmulp st2,st0 add eax, dword [acolormap] fsubrp st1,st0 mov dword [d_pcolormap],eax fstp dword [Ltemp] mov eax, dword [Ltemp] sub eax,080000001h jc Lskip mov eax, dword [0+ebx] test eax,eax jz Lfacesback push edx push esi push ecx call D_PolysetRecursiveTriangle ; call near D_PolysetRecursiveTriangle ;sub ebp,16 ;jnz Llooptop jmp Ldone2 Lfacesback: mov eax, dword [0+8+ecx] push eax mov eax, dword [0+8+esi] push eax mov eax, dword [0+8+edx] push eax push ecx push edx mov eax, dword [r_affinetridesc+32] test dword [24+ecx],00020h jz Lp11 add dword [0+8+ecx],eax Lp11: test dword [24+esi],00020h jz Lp12 add dword [0+8+esi],eax Lp12: test dword [24+edx],00020h jz Lp13 add dword [0+8+edx],eax Lp13: push edx push esi push ecx call D_PolysetRecursiveTriangle ; call near D_PolysetRecursiveTriangle pop edx pop ecx pop eax mov dword [0+8+edx],eax pop eax mov dword [0+8+esi],eax pop eax mov dword [0+8+ecx],eax Lskip: ;sub ebp,16 ;jnz Llooptop Ldone2: pop edi pop ebx pop esi ;pop ebp add esp,offset SPAN_SIZE ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetScanLeftEdge ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetScanLeftEdge D_PolysetScanLeftEdge: push ebp push esi push edi push ebx mov eax, dword [4+16+esp] mov ecx, dword [d_sfrac] and eax,0FFFFh mov ebx, dword [d_ptex] or ecx,eax mov esi, dword [d_pedgespanpackage] mov edx, dword [d_tfrac] mov edi, dword [d_light] mov ebp, dword [d_zi] LScanLoop: mov dword [12+esi],ebx mov eax, dword [d_pdest] mov dword [0+esi],eax mov eax, dword [d_pz] mov dword [4+esi],eax mov eax, dword [d_aspancount] mov dword [8+esi],eax mov dword [24+esi],edi mov dword [28+esi],ebp mov dword [16+esi],ecx mov dword [20+esi],edx mov al, byte [32+esi] add esi,32 mov eax, dword [erroradjustup] mov dword [d_pedgespanpackage],esi mov esi, dword [errorterm] add esi,eax mov eax, dword [d_pdest] js near LNoLeftEdgeTurnover sub esi, dword [erroradjustdown] add eax, dword [d_pdestextrastep] mov dword [errorterm],esi mov dword [d_pdest],eax mov eax, dword [d_pz] mov esi, dword [d_aspancount] add eax, dword [d_pzextrastep] add ecx, dword [d_sfracextrastep] adc ebx, dword [d_ptexextrastep] add esi, dword [d_countextrastep] mov dword [d_pz],eax mov eax, dword [d_tfracextrastep] mov dword [d_aspancount],esi add edx,eax jnc LSkip1 add ebx, dword [r_affinetridesc+8] LSkip1: add edi, dword [d_lightextrastep] add ebp, dword [d_ziextrastep] mov esi, dword [d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz near LScanLoop pop ebx pop edi pop esi pop ebp ret LNoLeftEdgeTurnover: mov dword [errorterm],esi add eax, dword [d_pdestbasestep] mov dword [d_pdest],eax mov eax, dword [d_pz] mov esi, dword [d_aspancount] add eax, dword [d_pzbasestep] add ecx, dword [d_sfracbasestep] adc ebx, dword [d_ptexbasestep] add esi, dword [ubasestep] mov dword [d_pz],eax mov dword [d_aspancount],esi mov esi, dword [d_tfracbasestep] add edx,esi jnc LSkip2 add ebx, dword [r_affinetridesc+8] LSkip2: add edi, dword [d_lightbasestep] add ebp, dword [d_zibasestep] mov esi, dword [d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz near LScanLoop pop ebx pop edi pop esi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetDrawFinalVerts ;;;;;;;;;;;;;;;;;;;;;;;; L_PDFVert: push esi push edi mov eax, dword [0+0+ebx] mov edx, dword [r_refdef+40] cmp eax,edx jge LNextVert mov esi, dword [0+4+ebx] mov edx, dword [r_refdef+44] cmp esi,edx jge LNextVert mov edi, dword [zspantable+esi*4] mov edx, dword [0+20+ebx] shr edx,16 cmp dx, word [edi+eax*2] jl LNextVert mov word [edi+eax*2],dx mov edi, dword [0+12+ebx] shr edi,16 mov edi, dword [skintable+edi*4] mov edx, dword [0+8+ebx] shr edx,16 mov dl, byte [edi+edx] mov edi, dword [0+16+ebx] and edi,0FF00h and edx,000FFh add edi,edx mov edx, dword [acolormap] mov dl, byte [edx+edi*1] mov edi, dword [d_scantable+esi*4] mov esi, dword [d_viewbuffer] add edi,eax mov byte [esi+edi],dl LNextVert: pop edi pop esi ret global D_PolysetDrawFinalVerts D_PolysetDrawFinalVerts: push ebp push ebx mov ebx,dword[4+8+esp] ;pv1 call L_PDFVert mov ebx,dword[8+8+esp] ;pv2 call L_PDFVert mov ebx,dword[12+8+esp];pv3 call L_PDFVert pop ebx pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_DrawNonSubdiv ;;;;;;;;;;;;;;;;;;;;;;;; global D_DrawNonSubdiv D_DrawNonSubdiv: ;push ebp ;mov ebp, dword [r_affinetridesc+24] ;lnumtriangles push ebx ;shl ebp,4 push esi mov esi, dword [r_affinetridesc+16] ;ptri push edi LNDLoop: mov edi, dword [r_affinetridesc+20] ;pfv xor ecx,ecx; //clear i1 xor edx,edx; //clear i2 mov cx, word[4+0+esi] ;ptri->vertindex[0] xor ebx,ebx; //clear i3 mov dx, word[4+2+esi] ;ptri->vertindex[1] shl ecx,5 mov bx, word[4+4+esi] ;ptri->vertindex[2] shl edx,5 add ecx,edi shl ebx,5 add edx,edi add ebx,edi mov eax, dword [0+4+ecx] mov esi, dword [0+0+ecx] sub eax, dword [0+4+edx] sub esi, dword [0+0+ebx] imul eax,esi mov esi, dword [0+0+ecx] mov edi, dword [0+4+ecx] sub esi, dword [0+0+edx] sub edi, dword [0+4+ebx] imul edi,esi sub eax,edi jns near LNextTri mov dword [d_xdenom],eax fild dword [d_xdenom] mov eax, dword [0+0+ecx] mov esi, dword [0+4+ecx] mov dword [r_p0+0],eax mov dword [r_p0+4],esi mov eax, dword [0+8+ecx] mov esi, dword [0+12+ecx] mov dword [r_p0+8],eax mov dword [r_p0+12],esi mov eax, dword [0+16+ecx] mov esi, dword [0+20+ecx] mov dword [r_p0+16],eax mov dword [r_p0+20],esi fdivr dword [float_1] mov eax, dword [0+0+edx] mov esi, dword [0+4+edx] mov dword [r_p1+0],eax mov dword [r_p1+4],esi mov eax, dword [0+8+edx] mov esi, dword [0+12+edx] mov dword [r_p1+8],eax mov dword [r_p1+12],esi mov eax, dword [0+16+edx] mov esi, dword [0+20+edx] mov dword [r_p1+16],eax mov dword [r_p1+20],esi mov eax, dword [0+0+ebx] mov esi, dword [0+4+ebx] mov dword [r_p2+0],eax mov dword [r_p2+4],esi mov eax, dword [0+8+ebx] mov esi, dword [0+12+ebx] mov dword [r_p2+8],eax mov dword [r_p2+12],esi mov eax, dword [0+16+ebx] mov esi, dword [0+20+ebx] mov dword [r_p2+16],eax mov edi, dword [r_affinetridesc+16] mov dword [r_p2+20],esi mov eax, dword [0+edi] test eax,eax jnz LFacesFront mov eax, dword [24+ecx] mov esi, dword [24+edx] mov edi, dword [24+ebx] test eax,00020h mov eax, dword [r_affinetridesc+32] jz LOnseamDone0 add dword [r_p0+8],eax LOnseamDone0: test esi,00020h jz LOnseamDone1 add dword [r_p1+8],eax LOnseamDone1: test edi,00020h jz LOnseamDone2 add dword [r_p2+8],eax LOnseamDone2: LFacesFront: fstp dword [d_xdenom] call D_PolysetSetEdgeTable ; call near D_PolysetSetEdgeTable call D_RasterizeAliasPolySmooth ; call near D_RasterizeAliasPolySmooth LNextTri: mov esi, dword [r_affinetridesc+16] ;sub ebp,16 ;jnz LNDLoop pop edi pop esi pop ebx ;pop ebp add esp,offset SPAN_SIZE ret engine/h2shared/d_polysa.inc000066400000000000000000000026261444734033100163410ustar00rootroot00000000000000; ; d_polysa.inc -- common header for NASM format d_polys*.asm ; based on asm_draw.h and d_ifacea.h from original Quake source ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 2012 O.Sezer ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; ; !!! if this is changed, it must be changed in quakedef.h too !!! %define CACHE_SIZE 32 ; used to align key data structures ; !!! if this is changed, it must be changed in r_shared.h too !!! %define MAXHEIGHT 1024 ; !!! if this is changed, it must be changed in d_polyse.c too !!! %define DPS_MAXSPANS MAXHEIGHT+1 %define spanpackage_t_size 32 %define SPAN_SIZE (((DPS_MAXSPANS + 1 + ((CACHE_SIZE - 1) / spanpackage_t_size)) + 1) * spanpackage_t_size) ; (MAXHEIGHT+1+1+1)*32 engine/h2shared/d_polysa2.asm000066400000000000000000000577561444734033100164500ustar00rootroot00000000000000; ; d_polysa2.asm ; x86 assembly-language polygon model drawing code ; with translucency handling, #1. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" %include "d_polysa.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix d_viewbuffer _sym_prefix d_scantable _sym_prefix r_refdef _sym_prefix a_sstepxfrac _sym_prefix r_affinetridesc _sym_prefix acolormap _sym_prefix d_pcolormap _sym_prefix d_sfrac _sym_prefix d_ptex _sym_prefix d_pedgespanpackage _sym_prefix d_tfrac _sym_prefix d_light _sym_prefix d_zi _sym_prefix d_pdest _sym_prefix d_pz _sym_prefix d_aspancount _sym_prefix erroradjustup _sym_prefix erroradjustdown _sym_prefix errorterm _sym_prefix d_xdenom _sym_prefix r_p0 _sym_prefix r_p1 _sym_prefix r_p2 _sym_prefix a_tstepxfrac _sym_prefix a_ststepxwhole _sym_prefix zspantable _sym_prefix skintable _sym_prefix d_countextrastep _sym_prefix ubasestep _sym_prefix a_spans _sym_prefix d_pdestextrastep _sym_prefix d_pzextrastep _sym_prefix d_sfracextrastep _sym_prefix d_ptexextrastep _sym_prefix d_tfracextrastep _sym_prefix d_lightextrastep _sym_prefix d_ziextrastep _sym_prefix d_pdestbasestep _sym_prefix d_pzbasestep _sym_prefix d_sfracbasestep _sym_prefix d_ptexbasestep _sym_prefix d_tfracbasestep _sym_prefix d_lightbasestep _sym_prefix d_zibasestep _sym_prefix r_lstepx _sym_prefix r_lstepy _sym_prefix r_sstepx _sym_prefix r_sstepy _sym_prefix r_tstepx _sym_prefix r_tstepy _sym_prefix r_zistepx _sym_prefix r_zistepy _sym_prefix mainTransTable _sym_prefix D_PolysetSetEdgeTable _sym_prefix D_RasterizeAliasPolySmooth ; C-shared globals: _sym_prefix D_PolysetAff8StartT _sym_prefix D_PolysetCalcGradientsT _sym_prefix D_PolysetRecursiveTriangleT _sym_prefix D_PolysetDrawSpans8T _sym_prefix D_Aff8PatchT _sym_prefix D_PolysetDrawT _sym_prefix D_PolysetScanLeftEdgeT _sym_prefix D_PolysetDrawFinalVertsT _sym_prefix D_DrawNonSubdivT _sym_prefix D_PolysetAff8EndT _sym_prefix R_TranPatch1 %endif ; _sym_prefix ; externs from C code extern d_viewbuffer extern d_scantable extern r_refdef extern a_sstepxfrac extern r_affinetridesc extern acolormap extern d_pcolormap extern d_sfrac extern d_ptex extern d_pedgespanpackage extern d_tfrac extern d_light extern d_zi extern d_pdest extern d_pz extern d_aspancount extern erroradjustup extern erroradjustdown extern errorterm extern d_xdenom extern r_p0 extern r_p1 extern r_p2 extern a_tstepxfrac extern a_ststepxwhole extern zspantable extern skintable extern d_countextrastep extern ubasestep extern a_spans extern d_pdestextrastep extern d_pzextrastep extern d_sfracextrastep extern d_ptexextrastep extern d_tfracextrastep extern d_lightextrastep extern d_ziextrastep extern d_pdestbasestep extern d_pzbasestep extern d_sfracbasestep extern d_ptexbasestep extern d_tfracbasestep extern d_lightbasestep extern d_zibasestep extern r_lstepx extern r_lstepy extern r_sstepx extern r_sstepy extern r_tstepx extern r_tstepy extern r_zistepx extern r_zistepy extern mainTransTable extern D_PolysetSetEdgeTable extern D_RasterizeAliasPolySmooth ; externs from ASM-only code extern float_point5 extern float_1 extern float_minus_1 extern float_0 extern advancetable extern sstep extern tstep extern ceil_cw extern single_cw SEGMENT .data ALIGN 4 p10_minus_p20 dd 0 p01_minus_p21 dd 0 temp0 dd 0 temp1 dd 0 Ltemp dd 0 aff8entryvec_table dd LDraw8, LDraw7, LDraw6, LDraw5 dd LDraw4, LDraw3, LDraw2, LDraw1 lzistepx dd 0 SEGMENT .text ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetAff8StartT ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetAff8StartT D_PolysetAff8StartT: ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetCalcGradientsT ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetCalcGradientsT D_PolysetCalcGradientsT: fild dword [r_p0+0] fild dword [r_p2+0] fild dword [r_p0+4] fild dword [r_p2+4] fild dword [r_p1+0] fild dword [r_p1+4] fxch st3 fsub st0,st2 fxch st1 fsub st0,st4 fxch st5 fsubrp st4,st0 fxch st2 fsubrp st1,st0 fxch st1 fld dword [d_xdenom] fxch st4 fstp dword [p10_minus_p20] fstp dword [p01_minus_p21] fxch st2 fild dword [r_p2+16] fild dword [r_p0+16] fild dword [r_p1+16] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmul st0,st5 fxch st2 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmul st0,st5 fxch st2 fsubrp st3,st0 fsubp st1,st0 fld st2 fmul dword [float_minus_1] fxch st2 fmul st0,st3 fxch st1 fmul st0,st2 fldcw word [ceil_cw] fistp dword [r_lstepy] fistp dword [r_lstepx] fldcw word [single_cw] fild dword [r_p2+8] fild dword [r_p0+8] fild dword [r_p1+8] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmul st0,st6 fxch st2 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmul st0,st6 fxch st2 fsubrp st3,st0 fsubp st1,st0 fmul st0,st2 fxch st1 fmul st0,st3 fxch st1 fistp dword [r_sstepy] fistp dword [r_sstepx] fild dword [r_p2+12] fild dword [r_p0+12] fild dword [r_p1+12] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmul st0,st6 fxch st2 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmul st0,st6 fxch st2 fsubrp st3,st0 fsubp st1,st0 fmul st0,st2 fxch st1 fmul st0,st3 fxch st1 fistp dword [r_tstepy] fistp dword [r_tstepx] fild dword [r_p2+20] fild dword [r_p0+20] fild dword [r_p1+20] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmulp st6,st0 fxch st1 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmulp st5,st0 fxch st5 fsubp st1,st0 fxch st3 fsubrp st4,st0 fxch st1 fmulp st2,st0 fmulp st2,st0 fistp dword [r_zistepx] fistp dword [r_zistepy] mov eax, dword [r_sstepx] mov edx, dword [r_tstepx] shl eax,16 shl edx,16 mov dword [a_sstepxfrac],eax mov dword [a_tstepxfrac],edx mov ecx, dword [r_sstepx] mov eax, dword [r_tstepx] sar ecx,16 sar eax,16 imul dword [4+0+esp] add eax,ecx mov dword [a_ststepxwhole],eax ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetRecursiveTriangleT ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetRecursiveTriangleT D_PolysetRecursiveTriangleT: push ebp push esi push edi push ebx mov esi, dword [8+16+esp] mov ebx, dword [4+16+esp] mov edi, dword [12+16+esp] mov eax, dword [0+esi] mov edx, dword [0+ebx] mov ebp, dword [4+esi] sub eax,edx mov ecx, dword [4+ebx] sub ebp,ecx inc eax cmp eax,2 ja LSplit mov eax, dword [0+edi] inc ebp cmp ebp,2 ja LSplit mov edx, dword [0+esi] mov ebp, dword [4+edi] sub eax,edx mov ecx, dword [4+esi] sub ebp,ecx inc eax cmp eax,2 ja LSplit2 mov eax, dword [0+ebx] inc ebp cmp ebp,2 ja LSplit2 mov edx, dword [0+edi] mov ebp, dword [4+ebx] sub eax,edx mov ecx, dword [4+edi] sub ebp,ecx inc eax inc ebp mov edx,ebx cmp eax,2 ja LSplit3 cmp ebp,2 jna near LDone LSplit3: mov ebx,edi mov edi,esi mov esi,edx jmp LSplit LSplit2: mov eax,ebx mov ebx,esi mov esi,edi mov edi,eax LSplit: sub esp,24 mov eax, dword [8+ebx] mov edx, dword [8+esi] mov ecx, dword [12+ebx] add eax,edx mov edx, dword [12+esi] sar eax,1 add ecx,edx mov dword [8+esp],eax mov eax, dword [20+ebx] sar ecx,1 mov edx, dword [20+esi] mov dword [12+esp],ecx add eax,edx mov ecx, dword [0+ebx] mov edx, dword [0+esi] sar eax,1 add edx,ecx mov dword [20+esp],eax mov eax, dword [4+ebx] sar edx,1 mov ebp, dword [4+esi] mov dword [0+esp],edx add ebp,eax sar ebp,1 mov dword [4+esp],ebp cmp dword [4+esi],eax jg LNoDraw mov edx, dword [0+esi] jnz LDraw cmp edx,ecx jl LNoDraw LDraw: mov edx, dword [20+esp] mov ecx, dword [4+esp] sar edx,16 mov ebp, dword [0+esp] mov eax, dword [zspantable+ecx*4] cmp dx, word [eax+ebp*2] jnge LNoDraw ;rj2 mov word [eax+ebp*2],dx mov eax, dword [12+esp] sar eax,16 mov edx, dword [8+esp] sar edx,16 sub ecx,ecx mov eax, dword [skintable+eax*4] mov ebp, dword [4+esp] mov cl, byte [eax+edx] ; texture pixel or cl,cl jz Skip1B ; color 0 = no draw mov edx, dword [d_pcolormap] mov dh, byte [edx+ecx] mov ecx, dword [0+esp] mov eax, dword [d_scantable+ebp*4] add ecx,eax mov eax, dword [d_viewbuffer] ; trans stuff mov dl, byte [eax+ecx] and edx, 0ffffh mov dh, byte [12345678h+edx] TranPatch1: mov byte [eax+ecx],dh ;rjr distance ;mov byte [eax+ecx],0 Skip1B: LNoDraw: push esp push ebx push edi call D_PolysetRecursiveTriangleT ; call near D_PolysetRecursiveTriangleT mov ebx,esp push esi push ebx push edi call D_PolysetRecursiveTriangleT ; call near D_PolysetRecursiveTriangleT add esp,24 LDone: pop ebx pop edi pop esi pop ebp ret 12 ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetDrawSpans8T ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetDrawSpans8T D_PolysetDrawSpans8T: push esi push ebx mov esi, dword [4+8+esp] mov ecx, dword [r_zistepx] push ebp push edi ror ecx,16 mov edx, dword [8+esi] mov dword [lzistepx],ecx LSpanLoop: mov eax, dword [d_aspancount] sub eax,edx mov edx, dword [erroradjustup] mov ebx, dword [errorterm] add ebx,edx js LNoTurnover mov edx, dword [erroradjustdown] mov edi, dword [d_countextrastep] sub ebx,edx mov ebp, dword [d_aspancount] mov dword [errorterm],ebx add ebp,edi mov dword [d_aspancount],ebp jmp LRightEdgeStepped LNoTurnover: mov edi, dword [d_aspancount] mov edx, dword [ubasestep] mov dword [errorterm],ebx add edi,edx mov dword [d_aspancount],edi LRightEdgeStepped: cmp eax,1 jl near LNextSpan jz near LExactlyOneLong mov ecx, dword [a_ststepxwhole] mov edx, dword [r_affinetridesc+8] mov dword [advancetable+4],ecx add ecx,edx mov dword [advancetable],ecx mov ecx, dword [a_tstepxfrac] mov cx, word [r_lstepx] mov edx,eax mov dword [tstep],ecx add edx,7 shr edx,3 mov ebx, dword [16+esi] mov bx,dx mov ecx, dword [4+esi] neg eax mov edi, dword [0+esi] and eax,7 sub edi,eax sub ecx,eax sub ecx,eax mov edx, dword [20+esi] mov dx, word [24+esi] mov ebp, dword [28+esi] ror ebp,16 push esi mov esi, dword [12+esi] jmp dword[aff8entryvec_table+eax*4] LDrawLoop: LDraw8: cmp bp, word [ecx] jl Lp1 xor eax,eax mov ah,dh ; light mov al, byte [esi] ; texture pixel or al,al jz SkipA2 ; color 0 = no draw ;rj2 mov word [ecx],bp mov ah, byte [12345678h+eax] LPatch8: ; trans stuff mov al, byte [edi] mov ah, byte [12345678h+eax] TranPatch2: mov byte [edi],ah ;rj ;mov byte [edi],0 SkipA2: Lp1: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw7: cmp bp, word [2+ecx] jl Lp2 xor eax,eax mov ah,dh mov al, byte [esi] ; texture pixel or al,al jz SkipB2 ; color 0 = no draw ;rj2 mov word [2+ecx],bp mov ah, byte [12345678h+eax] LPatch7: ; trans stuff mov al, byte [edi+1] mov ah, byte [12345678h+eax] TranPatch3: mov byte [1+edi],ah ;rj ;mov byte [1+edi],0 SkipB2: Lp2: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw6: cmp bp, word [4+ecx] jl Lp3 xor eax,eax mov ah,dh mov al, byte [esi] ; texture pixel or al,al jz SkipC2 ; color 0 = no draw ;rj2 mov word [4+ecx],bp mov ah, byte [12345678h+eax] LPatch6: ; trans stuff mov al, byte [edi+2] mov ah, byte [12345678h+eax] TranPatch4: mov byte [2+edi],ah ;rj ;mov byte [2+edi],0 SkipC2: Lp3: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw5: cmp bp, word [6+ecx] jl Lp4 xor eax,eax mov ah,dh mov al, byte [esi] ; texture pixel or al,al jz SkipD2 ; color 0 = no draw ;rj2 mov word [6+ecx],bp mov ah, byte [12345678h+eax] LPatch5: ; trans stuff mov al, byte [edi+3] mov ah, byte [12345678h+eax] TranPatch5: mov byte [3+edi],ah ;rj ;mov byte [3+edi],0 SkipD2: Lp4: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw4: cmp bp, word [8+ecx] jl Lp5 xor eax,eax mov ah,dh mov al, byte [esi] ; texture pixel or al,al jz SkipE2 ; color 0 = no draw ;rj2 mov word [8+ecx],bp mov ah, byte [12345678h+eax] LPatch4: ; trans stuff mov al, byte [edi+4] mov ah, byte [12345678h+eax] TranPatch6: mov byte [4+edi],ah ;rj ;mov byte [4+edi],0 SkipE2: Lp5: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw3: cmp bp, word [10+ecx] jl Lp6 xor eax,eax mov ah,dh mov al, byte [esi] ; texture pixel or al,al jz SkipF2 ; color 0 = no draw ;rj2 mov word [10+ecx],bp mov ah, byte [12345678h+eax] LPatch3: ; trans stuff mov al, byte [edi+5] mov ah, byte [12345678h+eax] TranPatch7: mov byte [5+edi],ah ;rj ;mov byte [5+edi],0 SkipF2: Lp6: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw2: cmp bp, word [12+ecx] jl Lp7 xor eax,eax mov ah,dh mov al, byte [esi] ; texture pixel or al,al jz SkipG2 ; color 0 = no draw ;rj2 mov word [12+ecx],bp mov ah, byte [12345678h+eax] LPatch2: ; trans stuff mov al, byte [edi+6] mov ah, byte [12345678h+eax] TranPatch8: mov byte [6+edi],ah ;rj ;mov byte [6+edi],0 SkipG2: Lp7: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw1: cmp bp, word [14+ecx] jl Lp8 xor eax,eax mov ah,dh mov al, byte [esi] ; texture pixel or al,al jz SkipH2 ; color 0 = no draw ;rj2 mov word [14+ecx],bp mov ah, byte [12345678h+eax] LPatch1: ; trans stuff mov al, byte [edi+7] mov ah, byte [12345678h+eax] TranPatch9: mov byte [7+edi],ah ;rj ;mov byte [7+edi],0 SkipH2: Lp8: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] add edi,8 add ecx,16 dec bx jnz near LDrawLoop pop esi LNextSpan: add esi,32 LNextSpanESISet: mov edx, dword [8+esi] cmp edx,offset -999999 jnz near LSpanLoop pop edi pop ebp pop ebx pop esi ret LExactlyOneLong: mov ecx, dword [4+esi] mov ebp, dword [28+esi] ror ebp,16 mov ebx, dword [12+esi] cmp bp, word [ecx] jl LNextSpan xor eax,eax mov edi, dword [0+esi] mov ah, byte [24+1+esi] add esi,32 mov al, byte [ebx] ; texture pixel or al,al jz SkipI2 ; color 0 = no draw ;rj2 mov word [ecx],bp mov ah, byte [12345678h+eax] LPatch9: ; trans stuff mov al, byte [edi] mov ah, byte [12345678h+eax] TranPatch10: mov byte [edi],ah ;rjr ;mov byte [edi],0 SkipI2: jmp LNextSpanESISet ;;;;;;;;;;;;;;;;;;;;;;;; ; D_Aff8PatchT ;;;;;;;;;;;;;;;;;;;;;;;; global D_Aff8PatchT D_Aff8PatchT: mov eax, dword [4+esp] mov dword [LPatch1-4],eax mov dword [LPatch2-4],eax mov dword [LPatch3-4],eax mov dword [LPatch4-4],eax mov dword [LPatch5-4],eax mov dword [LPatch6-4],eax mov dword [LPatch7-4],eax mov dword [LPatch8-4],eax mov dword [LPatch9-4],eax ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetDrawT ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetDrawT D_PolysetDrawT: sub esp,offset SPAN_SIZE mov eax,esp add eax,32 - 1 and eax,offset ~(32 - 1) mov dword [a_spans],eax mov eax, dword [r_affinetridesc+28] test eax,eax jz near D_DrawNonSubdivT ;push ebp ;mov ebp, dword [r_affinetridesc+24] push esi ;shl ebp,4 push ebx mov ebx, dword [r_affinetridesc+16] push edi mov edi, dword [r_affinetridesc+20] Llooptop: xor ecx,ecx xor esi,esi xor edx,edx mov cx,word[4+0+ebx] mov si,word[4+2+ebx] mov dx,word[4+4+ebx] shl ecx,5 shl esi,5 add ecx,edi shl edx,5 add esi,edi add edx,edi fild dword [0+4+ecx] fild dword [0+4+esi] fild dword [0+0+ecx] fild dword [0+0+edx] fxch st2 fsubr st0,st3 fild dword [0+0+esi] fxch st2 fsubr st3,st0 fild dword [0+4+edx] fxch st1 fsubrp st3,st0 fxch st1 fmulp st3,st0 fsubp st3,st0 mov eax, dword [0+16+ecx] and eax,0FF00h fmulp st2,st0 add eax, dword [acolormap] fsubrp st1,st0 mov dword [d_pcolormap],eax fstp dword [Ltemp] mov eax, dword [Ltemp] sub eax,080000001h jc Lskip mov eax, dword [0+ebx] test eax,eax jz Lfacesback push edx push esi push ecx call D_PolysetRecursiveTriangleT ; call near D_PolysetRecursiveTriangleT ;sub ebp,16 ;jnz Llooptop jmp Ldone2 Lfacesback: mov eax, dword [0+8+ecx] push eax mov eax, dword [0+8+esi] push eax mov eax, dword [0+8+edx] push eax push ecx push edx mov eax, dword [r_affinetridesc+32] test dword [24+ecx],00020h jz Lp11 add dword [0+8+ecx],eax Lp11: test dword [24+esi],00020h jz Lp12 add dword [0+8+esi],eax Lp12: test dword [24+edx],00020h jz Lp13 add dword [0+8+edx],eax Lp13: push edx push esi push ecx call D_PolysetRecursiveTriangleT ; call near D_PolysetRecursiveTriangleT pop edx pop ecx pop eax mov dword [0+8+edx],eax pop eax mov dword [0+8+esi],eax pop eax mov dword [0+8+ecx],eax Lskip: ;sub ebp,16 ;jnz Llooptop Ldone2: pop edi pop ebx pop esi ;pop ebp add esp,offset SPAN_SIZE ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetScanLeftEdgeT ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetScanLeftEdgeT D_PolysetScanLeftEdgeT: push ebp push esi push edi push ebx mov eax, dword [4+16+esp] mov ecx, dword [d_sfrac] and eax,0FFFFh mov ebx, dword [d_ptex] or ecx,eax mov esi, dword [d_pedgespanpackage] mov edx, dword [d_tfrac] mov edi, dword [d_light] mov ebp, dword [d_zi] LScanLoop: mov dword [12+esi],ebx mov eax, dword [d_pdest] mov dword [0+esi],eax mov eax, dword [d_pz] mov dword [4+esi],eax mov eax, dword [d_aspancount] mov dword [8+esi],eax mov dword [24+esi],edi mov dword [28+esi],ebp mov dword [16+esi],ecx mov dword [20+esi],edx mov al, byte [32+esi] add esi,32 mov eax, dword [erroradjustup] mov dword [d_pedgespanpackage],esi mov esi, dword [errorterm] add esi,eax mov eax, dword [d_pdest] js near LNoLeftEdgeTurnover sub esi, dword [erroradjustdown] add eax, dword [d_pdestextrastep] mov dword [errorterm],esi mov dword [d_pdest],eax mov eax, dword [d_pz] mov esi, dword [d_aspancount] add eax, dword [d_pzextrastep] add ecx, dword [d_sfracextrastep] adc ebx, dword [d_ptexextrastep] add esi, dword [d_countextrastep] mov dword [d_pz],eax mov eax, dword [d_tfracextrastep] mov dword [d_aspancount],esi add edx,eax jnc LSkip1 add ebx, dword [r_affinetridesc+8] LSkip1: add edi, dword [d_lightextrastep] add ebp, dword [d_ziextrastep] mov esi, dword [d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz near LScanLoop pop ebx pop edi pop esi pop ebp ret LNoLeftEdgeTurnover: mov dword [errorterm],esi add eax, dword [d_pdestbasestep] mov dword [d_pdest],eax mov eax, dword [d_pz] mov esi, dword [d_aspancount] add eax, dword [d_pzbasestep] add ecx, dword [d_sfracbasestep] adc ebx, dword [d_ptexbasestep] add esi, dword [ubasestep] mov dword [d_pz],eax mov dword [d_aspancount],esi mov esi, dword [d_tfracbasestep] add edx,esi jnc LSkip2 add ebx, dword [r_affinetridesc+8] LSkip2: add edi, dword [d_lightbasestep] add ebp, dword [d_zibasestep] mov esi, dword [d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz near LScanLoop pop ebx pop edi pop esi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetDrawFinalVertsT ;;;;;;;;;;;;;;;;;;;;;;;; L_PDFVertT: push esi push edi mov eax, dword [0+0+ebx] mov edx, dword [r_refdef+40] cmp eax,edx jge near LNextVert mov esi, dword [0+4+ebx] mov edx, dword [r_refdef+44] cmp esi,edx jge LNextVert mov edi, dword [zspantable+esi*4] mov edx, dword [0+20+ebx] shr edx,16 cmp dx, word [edi+eax*2] jl LNextVert ;rj2 mov word [edi+eax*2],dx mov edi, dword [0+12+ebx] shr edi,16 mov edi, dword [skintable+edi*4] mov edx, dword [0+8+ebx] shr edx,16 mov dl, byte [edi+edx] ; texture pixel or dl,dl jz Skip2B ; color 0 = no draw mov edi, dword [0+16+ebx] and edi,0FF00h and edx,000FFh add edi,edx mov edx, dword [acolormap] mov dh, byte [edx+edi*1] mov edi, dword [d_scantable+esi*4] mov esi, dword [d_viewbuffer] add edi,eax ; trans stuff mov dl, byte [esi+edi] and edx, 0ffffh mov dh, byte [12345678h+edx] TranPatch11: mov byte [esi+edi],dh ;rjr distance ;mov byte [esi+edi],0 Skip2B: LNextVert: pop edi pop esi ret global D_PolysetDrawFinalVertsT D_PolysetDrawFinalVertsT: push ebp push ebx mov ebx,dword[4+8+esp] ;pv1 call L_PDFVertT mov ebx,dword[8+8+esp] ;pv2 call L_PDFVertT mov ebx,dword[12+8+esp];pv3 call L_PDFVertT pop ebx pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_DrawNonSubdivT ;;;;;;;;;;;;;;;;;;;;;;;; global D_DrawNonSubdivT D_DrawNonSubdivT: ;push ebp ;mov ebp, dword [r_affinetridesc+24] push ebx ;shl ebp,4 push esi mov esi, dword [r_affinetridesc+16] push edi LNDLoop: mov edi, dword [r_affinetridesc+20] xor ecx,ecx; //clear i1 xor edx,edx; //clear i2 xor ebx,ebx; //clear i3 mov cx, word[4+0+esi] ;ptri->vertindex[0] mov dx, word[4+2+esi] ;ptri->vertindex[1] mov bx, word[4+4+esi] ;ptri->vertindex[2] shl ecx,5 shl edx,5 shl ebx,5 add ecx,edi add edx,edi add ebx,edi mov eax, dword [0+4+ecx] mov esi, dword [0+0+ecx] sub eax, dword [0+4+edx] sub esi, dword [0+0+ebx] imul eax,esi mov esi, dword [0+0+ecx] mov edi, dword [0+4+ecx] sub esi, dword [0+0+edx] sub edi, dword [0+4+ebx] imul edi,esi sub eax,edi jns near LNextTri mov dword [d_xdenom],eax fild dword [d_xdenom] mov eax, dword [0+0+ecx] mov esi, dword [0+4+ecx] mov dword [r_p0+0],eax mov dword [r_p0+4],esi mov eax, dword [0+8+ecx] mov esi, dword [0+12+ecx] mov dword [r_p0+8],eax mov dword [r_p0+12],esi mov eax, dword [0+16+ecx] mov esi, dword [0+20+ecx] mov dword [r_p0+16],eax mov dword [r_p0+20],esi fdivr dword [float_1] mov eax, dword [0+0+edx] mov esi, dword [0+4+edx] mov dword [r_p1+0],eax mov dword [r_p1+4],esi mov eax, dword [0+8+edx] mov esi, dword [0+12+edx] mov dword [r_p1+8],eax mov dword [r_p1+12],esi mov eax, dword [0+16+edx] mov esi, dword [0+20+edx] mov dword [r_p1+16],eax mov dword [r_p1+20],esi mov eax, dword [0+0+ebx] mov esi, dword [0+4+ebx] mov dword [r_p2+0],eax mov dword [r_p2+4],esi mov eax, dword [0+8+ebx] mov esi, dword [0+12+ebx] mov dword [r_p2+8],eax mov dword [r_p2+12],esi mov eax, dword [0+16+ebx] mov esi, dword [0+20+ebx] mov dword [r_p2+16],eax mov edi, dword [r_affinetridesc+16] mov dword [r_p2+20],esi mov eax, dword [0+edi] test eax,eax jnz LFacesFront mov eax, dword [24+ecx] mov esi, dword [24+edx] mov edi, dword [24+ebx] test eax,00020h mov eax, dword [r_affinetridesc+32] jz LOnseamDone0 add dword [r_p0+8],eax LOnseamDone0: test esi,00020h jz LOnseamDone1 add dword [r_p1+8],eax LOnseamDone1: test edi,00020h jz LOnseamDone2 add dword [r_p2+8],eax LOnseamDone2: LFacesFront: fstp dword [d_xdenom] call D_PolysetSetEdgeTable ; call near D_PolysetSetEdgeTable call D_RasterizeAliasPolySmooth ; call near D_RasterizeAliasPolySmooth LNextTri: mov esi, dword [r_affinetridesc+16] ;sub ebp,16 ;jnz LNDLoop pop edi pop esi pop ebx ;pop ebp add esp,offset SPAN_SIZE ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetAff8EndT ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetAff8EndT D_PolysetAff8EndT: SEGMENT .data ALIGN 4 LPatchTable: dd TranPatch1-4 dd TranPatch2-4 dd TranPatch3-4 dd TranPatch4-4 dd TranPatch5-4 dd TranPatch6-4 dd TranPatch7-4 dd TranPatch8-4 dd TranPatch9-4 dd TranPatch10-4 dd TranPatch11-4 SEGMENT .text ALIGN 4 ;;;;;;;;;;;;;;;;;;;;;;;; ; R_TranPatch1 ;;;;;;;;;;;;;;;;;;;;;;;; global R_TranPatch1 R_TranPatch1: push ebx mov eax, dword [mainTransTable] mov ebx,offset LPatchTable mov ecx,11 LPatchLoop: mov edx, dword [ebx] add ebx,4 mov dword [edx],eax dec ecx jnz LPatchLoop pop ebx ret engine/h2shared/d_polysa3.asm000066400000000000000000000604111444734033100164270ustar00rootroot00000000000000; ; d_polysa3.asm ; x86 assembly-language polygon model drawing code ; with translucency handling, #2. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" %include "d_polysa.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix d_viewbuffer _sym_prefix d_scantable _sym_prefix r_refdef _sym_prefix a_sstepxfrac _sym_prefix r_affinetridesc _sym_prefix acolormap _sym_prefix d_pcolormap _sym_prefix d_sfrac _sym_prefix d_ptex _sym_prefix d_pedgespanpackage _sym_prefix d_tfrac _sym_prefix d_light _sym_prefix d_zi _sym_prefix d_pdest _sym_prefix d_pz _sym_prefix d_aspancount _sym_prefix erroradjustup _sym_prefix erroradjustdown _sym_prefix errorterm _sym_prefix d_xdenom _sym_prefix r_p0 _sym_prefix r_p1 _sym_prefix r_p2 _sym_prefix a_tstepxfrac _sym_prefix a_ststepxwhole _sym_prefix zspantable _sym_prefix skintable _sym_prefix d_countextrastep _sym_prefix ubasestep _sym_prefix a_spans _sym_prefix d_pdestextrastep _sym_prefix d_pzextrastep _sym_prefix d_sfracextrastep _sym_prefix d_ptexextrastep _sym_prefix d_tfracextrastep _sym_prefix d_lightextrastep _sym_prefix d_ziextrastep _sym_prefix d_pdestbasestep _sym_prefix d_pzbasestep _sym_prefix d_sfracbasestep _sym_prefix d_ptexbasestep _sym_prefix d_tfracbasestep _sym_prefix d_lightbasestep _sym_prefix d_zibasestep _sym_prefix r_lstepx _sym_prefix r_lstepy _sym_prefix r_sstepx _sym_prefix r_sstepy _sym_prefix r_tstepx _sym_prefix r_tstepy _sym_prefix r_zistepx _sym_prefix r_zistepy _sym_prefix mainTransTable _sym_prefix D_PolysetSetEdgeTable _sym_prefix D_RasterizeAliasPolySmooth ; C-shared globals: _sym_prefix D_PolysetAff8StartT2 _sym_prefix D_PolysetCalcGradientsT2 _sym_prefix D_PolysetRecursiveTriangleT2 _sym_prefix D_PolysetDrawSpans8T2 _sym_prefix D_Aff8PatchT2 _sym_prefix D_PolysetDrawT2 _sym_prefix D_PolysetScanLeftEdgeT2 _sym_prefix D_PolysetDrawFinalVertsT2 _sym_prefix D_DrawNonSubdivT2 _sym_prefix D_PolysetAff8EndT2 _sym_prefix R_TranPatch2 %endif ; _sym_prefix ; externs from C code extern d_viewbuffer extern d_scantable extern r_refdef extern a_sstepxfrac extern r_affinetridesc extern acolormap extern d_pcolormap extern d_sfrac extern d_ptex extern d_pedgespanpackage extern d_tfrac extern d_light extern d_zi extern d_pdest extern d_pz extern d_aspancount extern erroradjustup extern erroradjustdown extern errorterm extern d_xdenom extern r_p0 extern r_p1 extern r_p2 extern a_tstepxfrac extern a_ststepxwhole extern zspantable extern skintable extern d_countextrastep extern ubasestep extern a_spans extern d_pdestextrastep extern d_pzextrastep extern d_sfracextrastep extern d_ptexextrastep extern d_tfracextrastep extern d_lightextrastep extern d_ziextrastep extern d_pdestbasestep extern d_pzbasestep extern d_sfracbasestep extern d_ptexbasestep extern d_tfracbasestep extern d_lightbasestep extern d_zibasestep extern r_lstepx extern r_lstepy extern r_sstepx extern r_sstepy extern r_tstepx extern r_tstepy extern r_zistepx extern r_zistepy extern mainTransTable extern D_PolysetSetEdgeTable extern D_RasterizeAliasPolySmooth ; externs from ASM-only code extern float_point5 extern float_1 extern float_minus_1 extern float_0 extern advancetable extern sstep extern tstep extern ceil_cw extern single_cw SEGMENT .data ALIGN 4 p10_minus_p20 dd 0 p01_minus_p21 dd 0 temp0 dd 0 temp1 dd 0 Ltemp dd 0 aff8entryvec_table dd LDraw8, LDraw7, LDraw6, LDraw5 dd LDraw4, LDraw3, LDraw2, LDraw1 lzistepx dd 0 SEGMENT .text ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetAff8StartT2 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetAff8StartT2 D_PolysetAff8StartT2: ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetCalcGradientsT2 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetCalcGradientsT2 D_PolysetCalcGradientsT2: fild dword [r_p0+0] fild dword [r_p2+0] fild dword [r_p0+4] fild dword [r_p2+4] fild dword [r_p1+0] fild dword [r_p1+4] fxch st3 fsub st0,st2 fxch st1 fsub st0,st4 fxch st5 fsubrp st4,st0 fxch st2 fsubrp st1,st0 fxch st1 fld dword [d_xdenom] fxch st4 fstp dword [p10_minus_p20] fstp dword [p01_minus_p21] fxch st2 fild dword [r_p2+16] fild dword [r_p0+16] fild dword [r_p1+16] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmul st0,st5 fxch st2 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmul st0,st5 fxch st2 fsubrp st3,st0 fsubp st1,st0 fld st2 fmul dword [float_minus_1] fxch st2 fmul st0,st3 fxch st1 fmul st0,st2 fldcw word [ceil_cw] fistp dword [r_lstepy] fistp dword [r_lstepx] fldcw word [single_cw] fild dword [r_p2+8] fild dword [r_p0+8] fild dword [r_p1+8] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmul st0,st6 fxch st2 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmul st0,st6 fxch st2 fsubrp st3,st0 fsubp st1,st0 fmul st0,st2 fxch st1 fmul st0,st3 fxch st1 fistp dword [r_sstepy] fistp dword [r_sstepx] fild dword [r_p2+12] fild dword [r_p0+12] fild dword [r_p1+12] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmul st0,st6 fxch st2 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmul st0,st6 fxch st2 fsubrp st3,st0 fsubp st1,st0 fmul st0,st2 fxch st1 fmul st0,st3 fxch st1 fistp dword [r_tstepy] fistp dword [r_tstepx] fild dword [r_p2+20] fild dword [r_p0+20] fild dword [r_p1+20] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmulp st6,st0 fxch st1 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmulp st5,st0 fxch st5 fsubp st1,st0 fxch st3 fsubrp st4,st0 fxch st1 fmulp st2,st0 fmulp st2,st0 fistp dword [r_zistepx] fistp dword [r_zistepy] mov eax, dword [r_sstepx] mov edx, dword [r_tstepx] shl eax,16 shl edx,16 mov dword [a_sstepxfrac],eax mov dword [a_tstepxfrac],edx mov ecx, dword [r_sstepx] mov eax, dword [r_tstepx] sar ecx,16 sar eax,16 imul dword [4+0+esp] add eax,ecx mov dword [a_ststepxwhole],eax ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetRecursiveTriangleT2 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetRecursiveTriangleT2 D_PolysetRecursiveTriangleT2: push ebp push esi push edi push ebx mov esi, dword [8+16+esp] mov ebx, dword [4+16+esp] mov edi, dword [12+16+esp] mov eax, dword [0+esi] mov edx, dword [0+ebx] mov ebp, dword [4+esi] sub eax,edx mov ecx, dword [4+ebx] sub ebp,ecx inc eax cmp eax,2 ja LSplit mov eax, dword [0+edi] inc ebp cmp ebp,2 ja LSplit mov edx, dword [0+esi] mov ebp, dword [4+edi] sub eax,edx mov ecx, dword [4+esi] sub ebp,ecx inc eax cmp eax,2 ja LSplit2 mov eax, dword [0+ebx] inc ebp cmp ebp,2 ja LSplit2 mov edx, dword [0+edi] mov ebp, dword [4+ebx] sub eax,edx mov ecx, dword [4+edi] sub ebp,ecx inc eax inc ebp mov edx,ebx cmp eax,2 ja LSplit3 cmp ebp,2 jna near LDone LSplit3: mov ebx,edi mov edi,esi mov esi,edx jmp LSplit LSplit2: mov eax,ebx mov ebx,esi mov esi,edi mov edi,eax LSplit: sub esp,24 mov eax, dword [8+ebx] mov edx, dword [8+esi] mov ecx, dword [12+ebx] add eax,edx mov edx, dword [12+esi] sar eax,1 add ecx,edx mov dword [8+esp],eax mov eax, dword [20+ebx] sar ecx,1 mov edx, dword [20+esi] mov dword [12+esp],ecx add eax,edx mov ecx, dword [0+ebx] mov edx, dword [0+esi] sar eax,1 add edx,ecx mov dword [20+esp],eax mov eax, dword [4+ebx] sar edx,1 mov ebp, dword [4+esi] mov dword [0+esp],edx add ebp,eax sar ebp,1 mov dword [4+esp],ebp cmp dword [4+esi],eax jg LNoDraw mov edx, dword [0+esi] jnz LDraw cmp edx,ecx jl LNoDraw LDraw: mov edx, dword [20+esp] mov ecx, dword [4+esp] sar edx,16 mov ebp, dword [0+esp] mov eax, dword [zspantable+ecx*4] cmp dx, word [eax+ebp*2] jnge LNoDraw mov word [eax+ebp*2],dx mov eax, dword [12+esp] sar eax,16 mov edx, dword [8+esp] sar edx,16 sub ecx,ecx mov eax, dword [skintable+eax*4] mov ebp, dword [4+esp] mov cl, byte [eax+edx] ; texture pixel or cl,cl jz Skip1B ; color 0 = no draw mov edx, dword [d_pcolormap] mov dh, byte [edx+ecx] mov eax, dword [0+esp] add eax, dword [d_scantable+ebp*4] add eax, dword [d_viewbuffer] bt cx,0 jnc Skip1 ; trans stuff mov dl, byte [eax] and edx, 0ffffh mov dh, byte [12345678h+edx] TranPatch1: Skip1: mov byte [eax],dh ;rjr distance ;mov byte [eax],0 Skip1B: LNoDraw: push esp push ebx push edi call D_PolysetRecursiveTriangleT2 ; call near D_PolysetRecursiveTriangleT2 mov ebx,esp push esi push ebx push edi call D_PolysetRecursiveTriangleT2 ; call near D_PolysetRecursiveTriangleT2 add esp,24 LDone: pop ebx pop edi pop esi pop ebp ret 12 ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetDrawSpans8T2 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetDrawSpans8T2 D_PolysetDrawSpans8T2: push esi push ebx mov esi, dword [4+8+esp] mov ecx, dword [r_zistepx] push ebp push edi ror ecx,16 mov edx, dword [8+esi] mov dword [lzistepx],ecx LSpanLoop: mov eax, dword [d_aspancount] sub eax,edx mov edx, dword [erroradjustup] mov ebx, dword [errorterm] add ebx,edx js LNoTurnover mov edx, dword [erroradjustdown] mov edi, dword [d_countextrastep] sub ebx,edx mov ebp, dword [d_aspancount] mov dword [errorterm],ebx add ebp,edi mov dword [d_aspancount],ebp jmp LRightEdgeStepped LNoTurnover: mov edi, dword [d_aspancount] mov edx, dword [ubasestep] mov dword [errorterm],ebx add edi,edx mov dword [d_aspancount],edi LRightEdgeStepped: cmp eax,1 jl near LNextSpan jz near LExactlyOneLong mov ecx, dword [a_ststepxwhole] mov edx, dword [r_affinetridesc+8] mov dword [advancetable+4],ecx add ecx,edx mov dword [advancetable],ecx mov ecx, dword [a_tstepxfrac] mov cx, word [r_lstepx] mov edx,eax mov dword [tstep],ecx add edx,7 shr edx,3 mov ebx, dword [16+esi] mov bx,dx mov ecx, dword [4+esi] neg eax mov edi, dword [0+esi] and eax,7 sub edi,eax sub ecx,eax sub ecx,eax mov edx, dword [20+esi] mov dx, word [24+esi] mov ebp, dword [28+esi] ror ebp,16 push esi mov esi, dword [12+esi] jmp dword[aff8entryvec_table+eax*4] LDrawLoop: LDraw8: cmp bp, word [ecx] jl Lp1 xor eax,eax mov ah,dh ; light mov al, byte [esi] ; texture pixel or al,al jz SkipA2 ; color 0 = no draw mov word [ecx],bp mov ah, byte [12345678h+eax] LPatch8: bt ax,0 jnc SkipA ; trans stuff mov al, byte [edi] mov ah, byte [12345678h+eax] TranPatch2: SkipA: mov byte [edi],ah ;rj ;mov byte [edi],0 SkipA2: Lp1: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw7: cmp bp, word [2+ecx] jl Lp2 xor eax,eax mov ah,dh mov al, byte [esi] ; texture pixel or al,al jz SkipB2 ; color 0 = no draw mov word [2+ecx],bp mov ah, byte [12345678h+eax] LPatch7: bt ax,0 jnc SkipB ; trans stuff mov al, byte [edi+1] mov ah, byte [12345678h+eax] TranPatch3: SkipB: mov byte [1+edi],ah ;rj ;mov byte [1+edi],0 SkipB2: Lp2: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw6: cmp bp, word [4+ecx] jl Lp3 xor eax,eax mov ah,dh mov al, byte [esi] ; texture pixel or al,al jz SkipC2 ; color 0 = no draw mov word [4+ecx],bp mov ah, byte [12345678h+eax] LPatch6: bt ax,0 jnc SkipC ; trans stuff mov al, byte [edi+2] mov ah, byte [12345678h+eax] TranPatch4: SkipC: mov byte [2+edi],ah ;rj ;mov byte [2+edi],0 SkipC2: Lp3: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw5: cmp bp, word [6+ecx] jl Lp4 xor eax,eax mov ah,dh mov al, byte [esi] ; texture pixel or al,al jz SkipD2 ; color 0 = no draw mov word [6+ecx],bp mov ah, byte [12345678h+eax] LPatch5: bt ax,0 jnc SkipD ; trans stuff mov al, byte [edi+3] mov ah, byte [12345678h+eax] TranPatch5: SkipD: mov byte [3+edi],ah ;rj ;mov byte [3+edi],0 SkipD2: Lp4: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw4: cmp bp, word [8+ecx] jl Lp5 xor eax,eax mov ah,dh mov al, byte [esi] ; texture pixel or al,al jz SkipE2 ; color 0 = no draw mov word [8+ecx],bp mov ah, byte [12345678h+eax] LPatch4: bt ax,0 jnc SkipE ; trans stuff mov al, byte [edi+4] mov ah, byte [12345678h+eax] TranPatch6: SkipE: mov byte [4+edi],ah ;rj ;mov byte [4+edi],0 SkipE2: Lp5: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw3: cmp bp, word [10+ecx] jl Lp6 xor eax,eax mov ah,dh mov al, byte [esi] ; texture pixel or al,al jz SkipF2 ; color 0 = no draw mov word [10+ecx],bp mov ah, byte [12345678h+eax] LPatch3: bt ax,0 jnc SkipF ; trans stuff mov al, byte [edi+5] mov ah, byte [12345678h+eax] TranPatch7: SkipF: mov byte [5+edi],ah ;rj ;mov byte [5+edi],0 SkipF2: Lp6: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw2: cmp bp, word [12+ecx] jl Lp7 xor eax,eax mov ah,dh mov al, byte [esi] ; texture pixel or al,al jz SkipG2 ; color 0 = no draw mov word [12+ecx],bp mov ah, byte [12345678h+eax] LPatch2: bt ax,0 jnc SkipG ; trans stuff mov al, byte [edi+6] mov ah, byte [12345678h+eax] TranPatch8: SkipG: mov byte [6+edi],ah ;rj ;mov byte [6+edi],0 SkipG2: Lp7: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw1: cmp bp, word [14+ecx] jl Lp8 xor eax,eax mov ah,dh mov al, byte [esi] ; texture pixel or al,al jz SkipH2 ; color 0 = no draw mov word [14+ecx],bp mov ah, byte [12345678h+eax] LPatch1: bt ax,0 jnc SkipH ; trans stuff mov al, byte [edi+7] mov ah, byte [12345678h+eax] TranPatch9: SkipH: mov byte [7+edi],ah ;rj ;mov byte [7+edi],0 SkipH2: Lp8: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] add edi,8 add ecx,16 dec bx jnz near LDrawLoop pop esi LNextSpan: add esi,32 LNextSpanESISet: mov edx, dword [8+esi] cmp edx,offset -999999 jnz near LSpanLoop pop edi pop ebp pop ebx pop esi ret LExactlyOneLong: mov ecx, dword [4+esi] mov ebp, dword [28+esi] ror ebp,16 mov ebx, dword [12+esi] cmp bp, word [ecx] jl LNextSpan xor eax,eax mov edi, dword [0+esi] mov ah, byte [24+1+esi] add esi,32 mov al, byte [ebx] ; texture pixel or al,al jz SkipI2 ; color 0 = no draw mov word [ecx],bp mov ah, byte [12345678h+eax] LPatch9: bt ax,0 jnc SkipI ; trans stuff mov al, byte [edi] mov ah, byte [12345678h+eax] TranPatch10: SkipI: mov byte [edi],ah ;rjr ;mov byte [edi],0 SkipI2: jmp LNextSpanESISet ;;;;;;;;;;;;;;;;;;;;;;;; ; D_Aff8PatchT2 ;;;;;;;;;;;;;;;;;;;;;;;; global D_Aff8PatchT2 D_Aff8PatchT2: mov eax, dword [4+esp] mov dword [LPatch1-4],eax mov dword [LPatch2-4],eax mov dword [LPatch3-4],eax mov dword [LPatch4-4],eax mov dword [LPatch5-4],eax mov dword [LPatch6-4],eax mov dword [LPatch7-4],eax mov dword [LPatch8-4],eax mov dword [LPatch9-4],eax ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetDrawT2 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetDrawT2 D_PolysetDrawT2: sub esp,offset SPAN_SIZE mov eax,esp add eax,32 - 1 and eax,offset ~(32 - 1) mov dword [a_spans],eax mov eax, dword [r_affinetridesc+28] test eax,eax jz near D_DrawNonSubdivT2 ;push ebp ;mov ebp, dword [r_affinetridesc+24] push esi ;shl ebp,4 push ebx mov ebx, dword [r_affinetridesc+16] push edi mov edi, dword [r_affinetridesc+20] Llooptop: xor ecx,ecx xor esi,esi xor edx,edx mov cx,word[4+0+ebx] mov si,word[4+2+ebx] mov dx,word[4+4+ebx] shl ecx,5 shl esi,5 add ecx,edi shl edx,5 add esi,edi add edx,edi fild dword [0+4+ecx] fild dword [0+4+esi] fild dword [0+0+ecx] fild dword [0+0+edx] fxch st2 fsubr st0,st3 fild dword [0+0+esi] fxch st2 fsubr st3,st0 fild dword [0+4+edx] fxch st1 fsubrp st3,st0 fxch st1 fmulp st3,st0 fsubp st3,st0 mov eax, dword [0+16+ecx] and eax,0FF00h fmulp st2,st0 add eax, dword [acolormap] fsubrp st1,st0 mov dword [d_pcolormap],eax fstp dword [Ltemp] mov eax, dword [Ltemp] sub eax,080000001h jc Lskip mov eax, dword [0+ebx] test eax,eax jz Lfacesback push edx push esi push ecx call D_PolysetRecursiveTriangleT2 ; call near D_PolysetRecursiveTriangleT2 ;sub ebp,16 ;jnz Llooptop jmp Ldone2 Lfacesback: mov eax, dword [0+8+ecx] push eax mov eax, dword [0+8+esi] push eax mov eax, dword [0+8+edx] push eax push ecx push edx mov eax, dword [r_affinetridesc+32] test dword [24+ecx],00020h jz Lp11 add dword [0+8+ecx],eax Lp11: test dword [24+esi],00020h jz Lp12 add dword [0+8+esi],eax Lp12: test dword [24+edx],00020h jz Lp13 add dword [0+8+edx],eax Lp13: push edx push esi push ecx call D_PolysetRecursiveTriangleT2 ; call near D_PolysetRecursiveTriangleT2 pop edx pop ecx pop eax mov dword [0+8+edx],eax pop eax mov dword [0+8+esi],eax pop eax mov dword [0+8+ecx],eax Lskip: ;sub ebp,16 ;jnz Llooptop Ldone2: pop edi pop ebx pop esi ;pop ebp add esp,offset SPAN_SIZE ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetScanLeftEdgeT2 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetScanLeftEdgeT2 D_PolysetScanLeftEdgeT2: push ebp push esi push edi push ebx mov eax, dword [4+16+esp] mov ecx, dword [d_sfrac] and eax,0FFFFh mov ebx, dword [d_ptex] or ecx,eax mov esi, dword [d_pedgespanpackage] mov edx, dword [d_tfrac] mov edi, dword [d_light] mov ebp, dword [d_zi] LScanLoop: mov dword [12+esi],ebx mov eax, dword [d_pdest] mov dword [0+esi],eax mov eax, dword [d_pz] mov dword [4+esi],eax mov eax, dword [d_aspancount] mov dword [8+esi],eax mov dword [24+esi],edi mov dword [28+esi],ebp mov dword [16+esi],ecx mov dword [20+esi],edx mov al, byte [32+esi] add esi,32 mov eax, dword [erroradjustup] mov dword [d_pedgespanpackage],esi mov esi, dword [errorterm] add esi,eax mov eax, dword [d_pdest] js near LNoLeftEdgeTurnover sub esi, dword [erroradjustdown] add eax, dword [d_pdestextrastep] mov dword [errorterm],esi mov dword [d_pdest],eax mov eax, dword [d_pz] mov esi, dword [d_aspancount] add eax, dword [d_pzextrastep] add ecx, dword [d_sfracextrastep] adc ebx, dword [d_ptexextrastep] add esi, dword [d_countextrastep] mov dword [d_pz],eax mov eax, dword [d_tfracextrastep] mov dword [d_aspancount],esi add edx,eax jnc LSkip1 add ebx, dword [r_affinetridesc+8] LSkip1: add edi, dword [d_lightextrastep] add ebp, dword [d_ziextrastep] mov esi, dword [d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz near LScanLoop pop ebx pop edi pop esi pop ebp ret LNoLeftEdgeTurnover: mov dword [errorterm],esi add eax, dword [d_pdestbasestep] mov dword [d_pdest],eax mov eax, dword [d_pz] mov esi, dword [d_aspancount] add eax, dword [d_pzbasestep] add ecx, dword [d_sfracbasestep] adc ebx, dword [d_ptexbasestep] add esi, dword [ubasestep] mov dword [d_pz],eax mov dword [d_aspancount],esi mov esi, dword [d_tfracbasestep] add edx,esi jnc LSkip2 add ebx, dword [r_affinetridesc+8] LSkip2: add edi, dword [d_lightbasestep] add ebp, dword [d_zibasestep] mov esi, dword [d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz near LScanLoop pop ebx pop edi pop esi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetDrawFinalVertsT2 ;;;;;;;;;;;;;;;;;;;;;;;; L_PDFVertT2: push esi push edi mov eax, dword [0+0+ebx] mov edx, dword [r_refdef+40] cmp eax,edx jge near LNextVert mov esi, dword [0+4+ebx] mov edx, dword [r_refdef+44] cmp esi,edx jge near LNextVert mov edi, dword [zspantable+esi*4] mov edx, dword [0+20+ebx] shr edx,16 cmp dx, word [edi+eax*2] jl LNextVert mov word [edi+eax*2],dx mov edi, dword [0+12+ebx] shr edi,16 mov edi, dword [skintable+edi*4] mov edx, dword [0+8+ebx] shr edx,16 mov dl, byte [edi+edx] ; texture pixel or dl,dl jz Skip2B ; color 0 = no draw mov edi, dword [0+16+ebx] and edi,0FF00h and edx,000FFh add edi,edx add edi, dword [acolormap] mov dh, byte [edi] mov edi, dword [d_scantable+esi*4] mov esi, dword [d_viewbuffer] add edi,eax bt dx,0 jnc Skip2 ; trans stuff mov dl, byte [esi+edi] and edx, 0ffffh mov dh, byte [12345678h+edx] TranPatch11: Skip2: mov byte [esi+edi],dh ;rjr distance ;mov byte [esi+edi],0 Skip2B: LNextVert: pop edi pop esi ret global D_PolysetDrawFinalVertsT2 D_PolysetDrawFinalVertsT2: push ebp push ebx mov ebx,dword[4+8+esp] ;pv1 call L_PDFVertT2 mov ebx,dword[8+8+esp] ;pv2 call L_PDFVertT2 mov ebx,dword[12+8+esp];pv3 call L_PDFVertT2 pop ebx pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_DrawNonSubdivT2 ;;;;;;;;;;;;;;;;;;;;;;;; global D_DrawNonSubdivT2 D_DrawNonSubdivT2: ;push ebp ;mov ebp, dword [r_affinetridesc+24] push ebx ;shl ebp,4 push esi mov esi, dword [r_affinetridesc+16] push edi LNDLoop: mov edi, dword [r_affinetridesc+20] xor ecx,ecx; //clear i1 xor edx,edx; //clear i2 xor ebx,ebx; //clear i3 mov cx, word[4+0+esi] ;ptri->vertindex[0] mov dx, word[4+2+esi] ;ptri->vertindex[1] mov bx, word[4+4+esi] ;ptri->vertindex[2] shl ecx,5 shl edx,5 shl ebx,5 add ecx,edi add edx,edi add ebx,edi mov eax, dword [0+4+ecx] mov esi, dword [0+0+ecx] sub eax, dword [0+4+edx] sub esi, dword [0+0+ebx] imul eax,esi mov esi, dword [0+0+ecx] mov edi, dword [0+4+ecx] sub esi, dword [0+0+edx] sub edi, dword [0+4+ebx] imul edi,esi sub eax,edi jns near LNextTri mov dword [d_xdenom],eax fild dword [d_xdenom] mov eax, dword [0+0+ecx] mov esi, dword [0+4+ecx] mov dword [r_p0+0],eax mov dword [r_p0+4],esi mov eax, dword [0+8+ecx] mov esi, dword [0+12+ecx] mov dword [r_p0+8],eax mov dword [r_p0+12],esi mov eax, dword [0+16+ecx] mov esi, dword [0+20+ecx] mov dword [r_p0+16],eax mov dword [r_p0+20],esi fdivr dword [float_1] mov eax, dword [0+0+edx] mov esi, dword [0+4+edx] mov dword [r_p1+0],eax mov dword [r_p1+4],esi mov eax, dword [0+8+edx] mov esi, dword [0+12+edx] mov dword [r_p1+8],eax mov dword [r_p1+12],esi mov eax, dword [0+16+edx] mov esi, dword [0+20+edx] mov dword [r_p1+16],eax mov dword [r_p1+20],esi mov eax, dword [0+0+ebx] mov esi, dword [0+4+ebx] mov dword [r_p2+0],eax mov dword [r_p2+4],esi mov eax, dword [0+8+ebx] mov esi, dword [0+12+ebx] mov dword [r_p2+8],eax mov dword [r_p2+12],esi mov eax, dword [0+16+ebx] mov esi, dword [0+20+ebx] mov dword [r_p2+16],eax mov edi, dword [r_affinetridesc+16] mov dword [r_p2+20],esi mov eax, dword [0+edi] test eax,eax jnz LFacesFront mov eax, dword [24+ecx] mov esi, dword [24+edx] mov edi, dword [24+ebx] test eax,00020h mov eax, dword [r_affinetridesc+32] jz LOnseamDone0 add dword [r_p0+8],eax LOnseamDone0: test esi,00020h jz LOnseamDone1 add dword [r_p1+8],eax LOnseamDone1: test edi,00020h jz LOnseamDone2 add dword [r_p2+8],eax LOnseamDone2: LFacesFront: fstp dword [d_xdenom] call D_PolysetSetEdgeTable ; call near D_PolysetSetEdgeTable call D_RasterizeAliasPolySmooth ; call near D_RasterizeAliasPolySmooth LNextTri: mov esi, dword [r_affinetridesc+16] ;sub ebp,16 ;jnz LNDLoop pop edi pop esi pop ebx ;pop ebp add esp,offset SPAN_SIZE ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetAff8EndT2 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetAff8EndT2 D_PolysetAff8EndT2: SEGMENT .data ALIGN 4 LPatchTable: dd TranPatch1-4 dd TranPatch2-4 dd TranPatch3-4 dd TranPatch4-4 dd TranPatch5-4 dd TranPatch6-4 dd TranPatch7-4 dd TranPatch8-4 dd TranPatch9-4 dd TranPatch10-4 dd TranPatch11-4 SEGMENT .text ALIGN 4 ;;;;;;;;;;;;;;;;;;;;;;;; ; R_TranPatch2 ;;;;;;;;;;;;;;;;;;;;;;;; global R_TranPatch2 R_TranPatch2: push ebx mov eax, dword [mainTransTable] mov ebx,offset LPatchTable mov ecx,11 LPatchLoop: mov edx, dword [ebx] add ebx,4 mov dword [edx],eax dec ecx jnz LPatchLoop pop ebx ret engine/h2shared/d_polysa4.asm000066400000000000000000000540161444734033100164340ustar00rootroot00000000000000; ; d_polysa4.asm ; x86 assembly-language polygon model drawing code ; with translucency handling, #3. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" %include "d_polysa.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix d_viewbuffer _sym_prefix d_scantable _sym_prefix r_refdef _sym_prefix a_sstepxfrac _sym_prefix r_affinetridesc _sym_prefix acolormap _sym_prefix d_pcolormap _sym_prefix d_sfrac _sym_prefix d_ptex _sym_prefix d_pedgespanpackage _sym_prefix d_tfrac _sym_prefix d_light _sym_prefix d_zi _sym_prefix d_pdest _sym_prefix d_pz _sym_prefix d_aspancount _sym_prefix erroradjustup _sym_prefix erroradjustdown _sym_prefix errorterm _sym_prefix d_xdenom _sym_prefix r_p0 _sym_prefix r_p1 _sym_prefix r_p2 _sym_prefix a_tstepxfrac _sym_prefix a_ststepxwhole _sym_prefix zspantable _sym_prefix skintable _sym_prefix d_countextrastep _sym_prefix ubasestep _sym_prefix a_spans _sym_prefix d_pdestextrastep _sym_prefix d_pzextrastep _sym_prefix d_sfracextrastep _sym_prefix d_ptexextrastep _sym_prefix d_tfracextrastep _sym_prefix d_lightextrastep _sym_prefix d_ziextrastep _sym_prefix d_pdestbasestep _sym_prefix d_pzbasestep _sym_prefix d_sfracbasestep _sym_prefix d_ptexbasestep _sym_prefix d_tfracbasestep _sym_prefix d_lightbasestep _sym_prefix d_zibasestep _sym_prefix r_lstepx _sym_prefix r_lstepy _sym_prefix r_sstepx _sym_prefix r_sstepy _sym_prefix r_tstepx _sym_prefix r_tstepy _sym_prefix r_zistepx _sym_prefix r_zistepy _sym_prefix D_PolysetSetEdgeTable _sym_prefix D_RasterizeAliasPolySmooth ; C-shared globals: _sym_prefix D_PolysetCalcGradientsT3 _sym_prefix D_PolysetRecursiveTriangleT3 _sym_prefix D_PolysetAff8StartT3 _sym_prefix D_PolysetDrawSpans8T3 _sym_prefix D_PolysetAff8EndT3 _sym_prefix D_Aff8PatchT3 _sym_prefix D_PolysetDrawT3 _sym_prefix D_PolysetScanLeftEdgeT3 _sym_prefix D_PolysetDrawFinalVertsT3 _sym_prefix D_DrawNonSubdivT3 %endif ; _sym_prefix ; externs from C code extern d_viewbuffer extern d_scantable extern r_refdef extern a_sstepxfrac extern r_affinetridesc extern acolormap extern d_pcolormap extern d_sfrac extern d_ptex extern d_pedgespanpackage extern d_tfrac extern d_light extern d_zi extern d_pdest extern d_pz extern d_aspancount extern erroradjustup extern erroradjustdown extern errorterm extern d_xdenom extern r_p0 extern r_p1 extern r_p2 extern a_tstepxfrac extern a_ststepxwhole extern zspantable extern skintable extern d_countextrastep extern ubasestep extern a_spans extern d_pdestextrastep extern d_pzextrastep extern d_sfracextrastep extern d_ptexextrastep extern d_tfracextrastep extern d_lightextrastep extern d_ziextrastep extern d_pdestbasestep extern d_pzbasestep extern d_sfracbasestep extern d_ptexbasestep extern d_tfracbasestep extern d_lightbasestep extern d_zibasestep extern r_lstepx extern r_lstepy extern r_sstepx extern r_sstepy extern r_tstepx extern r_tstepy extern r_zistepx extern r_zistepy extern D_PolysetSetEdgeTable extern D_RasterizeAliasPolySmooth ; externs from ASM-only code extern float_point5 extern float_1 extern float_minus_1 extern float_0 extern advancetable extern sstep extern tstep extern ceil_cw extern single_cw SEGMENT .data ALIGN 4 p10_minus_p20 dd 0 p01_minus_p21 dd 0 temp0 dd 0 temp1 dd 0 Ltemp dd 0 aff8entryvec_table dd LDraw8, LDraw7, LDraw6, LDraw5 dd LDraw4, LDraw3, LDraw2, LDraw1 lzistepx dd 0 SEGMENT .text ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetCalcGradientsT3 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetCalcGradientsT3 D_PolysetCalcGradientsT3: fild dword [r_p0+0] fild dword [r_p2+0] fild dword [r_p0+4] fild dword [r_p2+4] fild dword [r_p1+0] fild dword [r_p1+4] fxch st3 fsub st0,st2 fxch st1 fsub st0,st4 fxch st5 fsubrp st4,st0 fxch st2 fsubrp st1,st0 fxch st1 fld dword [d_xdenom] fxch st4 fstp dword [p10_minus_p20] fstp dword [p01_minus_p21] fxch st2 fild dword [r_p2+16] fild dword [r_p0+16] fild dword [r_p1+16] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmul st0,st5 fxch st2 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmul st0,st5 fxch st2 fsubrp st3,st0 fsubp st1,st0 fld st2 fmul dword [float_minus_1] fxch st2 fmul st0,st3 fxch st1 fmul st0,st2 fldcw word [ceil_cw] fistp dword [r_lstepy] fistp dword [r_lstepx] fldcw word [single_cw] fild dword [r_p2+8] fild dword [r_p0+8] fild dword [r_p1+8] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmul st0,st6 fxch st2 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmul st0,st6 fxch st2 fsubrp st3,st0 fsubp st1,st0 fmul st0,st2 fxch st1 fmul st0,st3 fxch st1 fistp dword [r_sstepy] fistp dword [r_sstepx] fild dword [r_p2+12] fild dword [r_p0+12] fild dword [r_p1+12] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmul st0,st6 fxch st2 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmul st0,st6 fxch st2 fsubrp st3,st0 fsubp st1,st0 fmul st0,st2 fxch st1 fmul st0,st3 fxch st1 fistp dword [r_tstepy] fistp dword [r_tstepx] fild dword [r_p2+20] fild dword [r_p0+20] fild dword [r_p1+20] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmulp st6,st0 fxch st1 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmulp st5,st0 fxch st5 fsubp st1,st0 fxch st3 fsubrp st4,st0 fxch st1 fmulp st2,st0 fmulp st2,st0 fistp dword [r_zistepx] fistp dword [r_zistepy] mov eax, dword [r_sstepx] mov edx, dword [r_tstepx] shl eax,16 shl edx,16 mov dword [a_sstepxfrac],eax mov dword [a_tstepxfrac],edx mov ecx, dword [r_sstepx] mov eax, dword [r_tstepx] sar ecx,16 sar eax,16 imul dword [4+0+esp] add eax,ecx mov dword [a_ststepxwhole],eax ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetRecursiveTriangleT3 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetRecursiveTriangleT3 D_PolysetRecursiveTriangleT3: push ebp push esi push edi push ebx mov esi, dword [8+16+esp] mov ebx, dword [4+16+esp] mov edi, dword [12+16+esp] mov eax, dword [0+esi] mov edx, dword [0+ebx] mov ebp, dword [4+esi] sub eax,edx mov ecx, dword [4+ebx] sub ebp,ecx inc eax cmp eax,2 ja LSplit mov eax, dword [0+edi] inc ebp cmp ebp,2 ja LSplit mov edx, dword [0+esi] mov ebp, dword [4+edi] sub eax,edx mov ecx, dword [4+esi] sub ebp,ecx inc eax cmp eax,2 ja LSplit2 mov eax, dword [0+ebx] inc ebp cmp ebp,2 ja LSplit2 mov edx, dword [0+edi] mov ebp, dword [4+ebx] sub eax,edx mov ecx, dword [4+edi] sub ebp,ecx inc eax inc ebp mov edx,ebx cmp eax,2 ja LSplit3 cmp ebp,2 jna near LDone LSplit3: mov ebx,edi mov edi,esi mov esi,edx jmp LSplit LSplit2: mov eax,ebx mov ebx,esi mov esi,edi mov edi,eax LSplit: sub esp,24 mov eax, dword [8+ebx] mov edx, dword [8+esi] mov ecx, dword [12+ebx] add eax,edx mov edx, dword [12+esi] sar eax,1 add ecx,edx mov dword [8+esp],eax mov eax, dword [20+ebx] sar ecx,1 mov edx, dword [20+esi] mov dword [12+esp],ecx add eax,edx mov ecx, dword [0+ebx] mov edx, dword [0+esi] sar eax,1 add edx,ecx mov dword [20+esp],eax mov eax, dword [4+ebx] sar edx,1 mov ebp, dword [4+esi] mov dword [0+esp],edx add ebp,eax sar ebp,1 mov dword [4+esp],ebp cmp dword [4+esi],eax jg LNoDraw mov edx, dword [0+esi] jnz LDraw cmp edx,ecx jl LNoDraw LDraw: mov edx, dword [20+esp] mov ecx, dword [4+esp] sar edx,16 mov ebp, dword [0+esp] mov eax, dword [zspantable+ecx*4] cmp dx, word [eax+ebp*2] jnge LNoDraw mov word [eax+ebp*2],dx mov eax, dword [12+esp] sar eax,16 mov edx, dword [8+esp] sar edx,16 sub ecx,ecx mov eax, dword [skintable+eax*4] mov ebp, dword [4+esp] mov cl, byte [eax+edx] or cl,cl jz Skip1B ; color 0 = no draw mov edx, dword [d_pcolormap] mov dl, byte [edx+ecx] mov ecx, dword [0+esp] mov eax, dword [d_scantable+ebp*4] add ecx,eax mov eax, dword [d_viewbuffer] mov byte [eax+ecx*1],dl Skip1B: LNoDraw: push esp push ebx push edi call D_PolysetRecursiveTriangleT3 ; call near D_PolysetRecursiveTriangleT3 mov ebx,esp push esi push ebx push edi call D_PolysetRecursiveTriangleT3 ; call near D_PolysetRecursiveTriangleT3 add esp,24 LDone: pop ebx pop edi pop esi pop ebp ret 12 ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetAff8StartT3 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetAff8StartT3 D_PolysetAff8StartT3: ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetDrawSpans8T3 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetDrawSpans8T3 D_PolysetDrawSpans8T3: push esi push ebx mov esi, dword [4+8+esp] mov ecx, dword [r_zistepx] push ebp push edi ror ecx,16 mov edx, dword [8+esi] mov dword [lzistepx],ecx LSpanLoop: mov eax, dword [d_aspancount] sub eax,edx mov edx, dword [erroradjustup] mov ebx, dword [errorterm] add ebx,edx js LNoTurnover mov edx, dword [erroradjustdown] mov edi, dword [d_countextrastep] sub ebx,edx mov ebp, dword [d_aspancount] mov dword [errorterm],ebx add ebp,edi mov dword [d_aspancount],ebp jmp LRightEdgeStepped LNoTurnover: mov edi, dword [d_aspancount] mov edx, dword [ubasestep] mov dword [errorterm],ebx add edi,edx mov dword [d_aspancount],edi LRightEdgeStepped: cmp eax,1 jl near LNextSpan jz near LExactlyOneLong mov ecx, dword [a_ststepxwhole] mov edx, dword [r_affinetridesc+8] mov dword [advancetable+4],ecx add ecx,edx mov dword [advancetable],ecx mov ecx, dword [a_tstepxfrac] mov cx, word [r_lstepx] mov edx,eax mov dword [tstep],ecx add edx,7 shr edx,3 mov ebx, dword [16+esi] mov bx,dx mov ecx, dword [4+esi] neg eax mov edi, dword [0+esi] and eax,7 sub edi,eax sub ecx,eax sub ecx,eax mov edx, dword [20+esi] mov dx, word [24+esi] mov ebp, dword [28+esi] ror ebp,16 push esi mov esi, dword [12+esi] jmp dword[aff8entryvec_table+eax*4] LDrawLoop: LDraw8: cmp bp, word [ecx] jl Lp1 xor eax,eax mov ah,dh mov al, byte [esi] or al,al jz SkipA2 ; color 0 = no draw mov word [ecx],bp mov al, byte [12345678h+eax] LPatch8: mov byte [edi],al SkipA2: Lp1: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw7: cmp bp, word [2+ecx] jl Lp2 xor eax,eax mov ah,dh mov al, byte [esi] or al,al jz SkipB2 ; color 0 = no draw mov word [2+ecx],bp mov al, byte [12345678h+eax] LPatch7: mov byte [1+edi],al SkipB2: Lp2: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw6: cmp bp, word [4+ecx] jl Lp3 xor eax,eax mov ah,dh mov al, byte [esi] or al,al jz SkipC2 ; color 0 = no draw mov word [4+ecx],bp mov al, byte [12345678h+eax] LPatch6: mov byte [2+edi],al SkipC2: Lp3: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw5: cmp bp, word [6+ecx] jl Lp4 xor eax,eax mov ah,dh mov al, byte [esi] or al,al jz SkipD2 ; color 0 = no draw mov word [6+ecx],bp mov al, byte [12345678h+eax] LPatch5: mov byte [3+edi],al SkipD2: Lp4: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw4: cmp bp, word [8+ecx] jl Lp5 xor eax,eax mov ah,dh mov al, byte [esi] or al,al jz SkipE2 ; color 0 = no draw mov word [8+ecx],bp mov al, byte [12345678h+eax] LPatch4: mov byte [4+edi],al SkipE2: Lp5: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw3: cmp bp, word [10+ecx] jl Lp6 xor eax,eax mov ah,dh mov al, byte [esi] or al,al jz SkipF2 ; color 0 = no draw mov word [10+ecx],bp mov al, byte [12345678h+eax] LPatch3: mov byte [5+edi],al SkipF2: Lp6: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw2: cmp bp, word [12+ecx] jl Lp7 xor eax,eax mov ah,dh mov al, byte [esi] or al,al jz SkipG2 ; color 0 = no draw mov word [12+ecx],bp mov al, byte [12345678h+eax] LPatch2: mov byte [6+edi],al SkipG2: Lp7: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw1: cmp bp, word [14+ecx] jl Lp8 xor eax,eax mov ah,dh mov al, byte [esi] or al,al jz SkipH2 ; color 0 = no draw mov word [14+ecx],bp mov al, byte [12345678h+eax] LPatch1: mov byte [7+edi],al SkipH2: Lp8: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] add edi,8 add ecx,16 dec bx jnz near LDrawLoop pop esi LNextSpan: add esi,32 LNextSpanESISet: mov edx, dword [8+esi] cmp edx,offset -999999 jnz near LSpanLoop pop edi pop ebp pop ebx pop esi ret LExactlyOneLong: mov ecx, dword [4+esi] mov ebp, dword [28+esi] ror ebp,16 mov ebx, dword [12+esi] cmp bp, word [ecx] jl LNextSpan xor eax,eax mov edi, dword [0+esi] mov ah, byte [24+1+esi] add esi,32 mov al, byte [ebx] or al,al jz SkipI2 ; color 0 = no draw mov word [ecx],bp mov al, byte [12345678h+eax] LPatch9: mov byte [edi],al SkipI2: jmp LNextSpanESISet ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetAff8EndT3 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetAff8EndT3 D_PolysetAff8EndT3: ;;;;;;;;;;;;;;;;;;;;;;;; ; D_Aff8PatchT3 ;;;;;;;;;;;;;;;;;;;;;;;; global D_Aff8PatchT3 D_Aff8PatchT3: mov eax, dword [4+esp] mov dword [LPatch1-4],eax mov dword [LPatch2-4],eax mov dword [LPatch3-4],eax mov dword [LPatch4-4],eax mov dword [LPatch5-4],eax mov dword [LPatch6-4],eax mov dword [LPatch7-4],eax mov dword [LPatch8-4],eax mov dword [LPatch9-4],eax ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetDrawT3 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetDrawT3 D_PolysetDrawT3: sub esp,offset SPAN_SIZE mov eax,esp add eax,32 - 1 and eax,offset ~(32 - 1) mov dword [a_spans],eax mov eax, dword [r_affinetridesc+28] test eax,eax jz near D_DrawNonSubdivT3 ;push ebp ;mov ebp, dword [r_affinetridesc+24] push esi ;shl ebp,4 push ebx mov ebx, dword [r_affinetridesc+16] push edi mov edi, dword [r_affinetridesc+20] Llooptop: xor ecx,ecx xor esi,esi xor edx,edx mov cx,word[4+0+ebx] mov si,word[4+2+ebx] mov dx,word[4+4+ebx] shl ecx,5 shl esi,5 add ecx,edi shl edx,5 add esi,edi add edx,edi fild dword [0+4+ecx] fild dword [0+4+esi] fild dword [0+0+ecx] fild dword [0+0+edx] fxch st2 fsubr st0,st3 fild dword [0+0+esi] fxch st2 fsubr st3,st0 fild dword [0+4+edx] fxch st1 fsubrp st3,st0 fxch st1 fmulp st3,st0 fsubp st3,st0 mov eax, dword [0+16+ecx] and eax,0FF00h fmulp st2,st0 add eax, dword [acolormap] fsubrp st1,st0 mov dword [d_pcolormap],eax fstp dword [Ltemp] mov eax, dword [Ltemp] sub eax,080000001h jc Lskip mov eax, dword [0+ebx] test eax,eax jz Lfacesback push edx push esi push ecx call D_PolysetRecursiveTriangleT3 ; call near D_PolysetRecursiveTriangleT3 ;sub ebp,16 ;jnz Llooptop jmp Ldone2 Lfacesback: mov eax, dword [0+8+ecx] push eax mov eax, dword [0+8+esi] push eax mov eax, dword [0+8+edx] push eax push ecx push edx mov eax, dword [r_affinetridesc+32] test dword [24+ecx],00020h jz Lp11 add dword [0+8+ecx],eax Lp11: test dword [24+esi],00020h jz Lp12 add dword [0+8+esi],eax Lp12: test dword [24+edx],00020h jz Lp13 add dword [0+8+edx],eax Lp13: push edx push esi push ecx call D_PolysetRecursiveTriangleT3 ; call near D_PolysetRecursiveTriangleT3 pop edx pop ecx pop eax mov dword [0+8+edx],eax pop eax mov dword [0+8+esi],eax pop eax mov dword [0+8+ecx],eax Lskip: ;sub ebp,16 ;jnz Llooptop Ldone2: pop edi pop ebx pop esi ;pop ebp add esp,offset SPAN_SIZE ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetScanLeftEdgeT3 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetScanLeftEdgeT3 D_PolysetScanLeftEdgeT3: push ebp push esi push edi push ebx mov eax, dword [4+16+esp] mov ecx, dword [d_sfrac] and eax,0FFFFh mov ebx, dword [d_ptex] or ecx,eax mov esi, dword [d_pedgespanpackage] mov edx, dword [d_tfrac] mov edi, dword [d_light] mov ebp, dword [d_zi] LScanLoop: mov dword [12+esi],ebx mov eax, dword [d_pdest] mov dword [0+esi],eax mov eax, dword [d_pz] mov dword [4+esi],eax mov eax, dword [d_aspancount] mov dword [8+esi],eax mov dword [24+esi],edi mov dword [28+esi],ebp mov dword [16+esi],ecx mov dword [20+esi],edx mov al, byte [32+esi] add esi,32 mov eax, dword [erroradjustup] mov dword [d_pedgespanpackage],esi mov esi, dword [errorterm] add esi,eax mov eax, dword [d_pdest] js near LNoLeftEdgeTurnover sub esi, dword [erroradjustdown] add eax, dword [d_pdestextrastep] mov dword [errorterm],esi mov dword [d_pdest],eax mov eax, dword [d_pz] mov esi, dword [d_aspancount] add eax, dword [d_pzextrastep] add ecx, dword [d_sfracextrastep] adc ebx, dword [d_ptexextrastep] add esi, dword [d_countextrastep] mov dword [d_pz],eax mov eax, dword [d_tfracextrastep] mov dword [d_aspancount],esi add edx,eax jnc LSkip1 add ebx, dword [r_affinetridesc+8] LSkip1: add edi, dword [d_lightextrastep] add ebp, dword [d_ziextrastep] mov esi, dword [d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz near LScanLoop pop ebx pop edi pop esi pop ebp ret LNoLeftEdgeTurnover: mov dword [errorterm],esi add eax, dword [d_pdestbasestep] mov dword [d_pdest],eax mov eax, dword [d_pz] mov esi, dword [d_aspancount] add eax, dword [d_pzbasestep] add ecx, dword [d_sfracbasestep] adc ebx, dword [d_ptexbasestep] add esi, dword [ubasestep] mov dword [d_pz],eax mov dword [d_aspancount],esi mov esi, dword [d_tfracbasestep] add edx,esi jnc LSkip2 add ebx, dword [r_affinetridesc+8] LSkip2: add edi, dword [d_lightbasestep] add ebp, dword [d_zibasestep] mov esi, dword [d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz near LScanLoop pop ebx pop edi pop esi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetDrawFinalVertsT3 ;;;;;;;;;;;;;;;;;;;;;;;; L_PDFVertT3: push esi push edi mov eax, dword [0+0+ebx] mov edx, dword [r_refdef+40] cmp eax,edx jge LNextVert mov esi, dword [0+4+ebx] mov edx, dword [r_refdef+44] cmp esi,edx jge LNextVert mov edi, dword [zspantable+esi*4] mov edx, dword [0+20+ebx] shr edx,16 cmp dx, word [edi+eax*2] jl LNextVert mov word [edi+eax*2],dx mov edi, dword [0+12+ebx] shr edi,16 mov edi, dword [skintable+edi*4] mov edx, dword [0+8+ebx] shr edx,16 mov dl, byte [edi+edx] or dl,dl jz Skip2B ; color 0 = no draw mov edi, dword [0+16+ebx] and edi,0FF00h and edx,000FFh add edi,edx mov edx, dword [acolormap] mov dl, byte [edx+edi*1] mov edi, dword [d_scantable+esi*4] mov esi, dword [d_viewbuffer] add edi,eax mov byte [esi+edi],dl Skip2B: LNextVert: pop edi pop esi ret global D_PolysetDrawFinalVertsT3 D_PolysetDrawFinalVertsT3: push ebp push ebx mov ebx,dword[4+8+esp] ;pv1 call L_PDFVertT3 mov ebx,dword[8+8+esp] ;pv2 call L_PDFVertT3 mov ebx,dword[12+8+esp];pv3 call L_PDFVertT3 pop ebx pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_DrawNonSubdivT3 ;;;;;;;;;;;;;;;;;;;;;;;; global D_DrawNonSubdivT3 D_DrawNonSubdivT3: ;push ebp ;mov ebp, dword [r_affinetridesc+24] push ebx ;shl ebp,4 push esi mov esi, dword [r_affinetridesc+16] push edi LNDLoop: mov edi, dword [r_affinetridesc+20] xor ecx,ecx; //clear i1 xor edx,edx; //clear i2 xor ebx,ebx; //clear i3 mov cx, word[4+0+esi] ;ptri->vertindex[0] mov dx, word[4+2+esi] ;ptri->vertindex[1] mov bx, word[4+4+esi] ;ptri->vertindex[2] shl ecx,5 shl edx,5 shl ebx,5 add ecx,edi add edx,edi add ebx,edi mov eax, dword [0+4+ecx] mov esi, dword [0+0+ecx] sub eax, dword [0+4+edx] sub esi, dword [0+0+ebx] imul eax,esi mov esi, dword [0+0+ecx] mov edi, dword [0+4+ecx] sub esi, dword [0+0+edx] sub edi, dword [0+4+ebx] imul edi,esi sub eax,edi jns near LNextTri mov dword [d_xdenom],eax fild dword [d_xdenom] mov eax, dword [0+0+ecx] mov esi, dword [0+4+ecx] mov dword [r_p0+0],eax mov dword [r_p0+4],esi mov eax, dword [0+8+ecx] mov esi, dword [0+12+ecx] mov dword [r_p0+8],eax mov dword [r_p0+12],esi mov eax, dword [0+16+ecx] mov esi, dword [0+20+ecx] mov dword [r_p0+16],eax mov dword [r_p0+20],esi fdivr dword [float_1] mov eax, dword [0+0+edx] mov esi, dword [0+4+edx] mov dword [r_p1+0],eax mov dword [r_p1+4],esi mov eax, dword [0+8+edx] mov esi, dword [0+12+edx] mov dword [r_p1+8],eax mov dword [r_p1+12],esi mov eax, dword [0+16+edx] mov esi, dword [0+20+edx] mov dword [r_p1+16],eax mov dword [r_p1+20],esi mov eax, dword [0+0+ebx] mov esi, dword [0+4+ebx] mov dword [r_p2+0],eax mov dword [r_p2+4],esi mov eax, dword [0+8+ebx] mov esi, dword [0+12+ebx] mov dword [r_p2+8],eax mov dword [r_p2+12],esi mov eax, dword [0+16+ebx] mov esi, dword [0+20+ebx] mov dword [r_p2+16],eax mov edi, dword [r_affinetridesc+16] mov dword [r_p2+20],esi mov eax, dword [0+edi] test eax,eax jnz LFacesFront mov eax, dword [24+ecx] mov esi, dword [24+edx] mov edi, dword [24+ebx] test eax,00020h mov eax, dword [r_affinetridesc+32] jz LOnseamDone0 add dword [r_p0+8],eax LOnseamDone0: test esi,00020h jz LOnseamDone1 add dword [r_p1+8],eax LOnseamDone1: test edi,00020h jz LOnseamDone2 add dword [r_p2+8],eax LOnseamDone2: LFacesFront: fstp dword [d_xdenom] call D_PolysetSetEdgeTable ; call near D_PolysetSetEdgeTable call D_RasterizeAliasPolySmooth ; call near D_RasterizeAliasPolySmooth LNextTri: mov esi, dword [r_affinetridesc+16] ;sub ebp,16 ;jnz LNDLoop pop edi pop esi pop ebx ;pop ebp add esp,offset SPAN_SIZE ret engine/h2shared/d_polysa5.asm000066400000000000000000000577701444734033100164470ustar00rootroot00000000000000; ; d_polysa5.asm ; x86 assembly-language polygon model drawing code ; with translucency handling, #5. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" %include "d_polysa.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix d_viewbuffer _sym_prefix d_scantable _sym_prefix r_refdef _sym_prefix a_sstepxfrac _sym_prefix r_affinetridesc _sym_prefix acolormap _sym_prefix d_pcolormap _sym_prefix d_sfrac _sym_prefix d_ptex _sym_prefix d_pedgespanpackage _sym_prefix d_tfrac _sym_prefix d_light _sym_prefix d_zi _sym_prefix d_pdest _sym_prefix d_pz _sym_prefix d_aspancount _sym_prefix erroradjustup _sym_prefix erroradjustdown _sym_prefix errorterm _sym_prefix d_xdenom _sym_prefix r_p0 _sym_prefix r_p1 _sym_prefix r_p2 _sym_prefix a_tstepxfrac _sym_prefix a_ststepxwhole _sym_prefix zspantable _sym_prefix skintable _sym_prefix d_countextrastep _sym_prefix ubasestep _sym_prefix a_spans _sym_prefix d_pdestextrastep _sym_prefix d_pzextrastep _sym_prefix d_sfracextrastep _sym_prefix d_ptexextrastep _sym_prefix d_tfracextrastep _sym_prefix d_lightextrastep _sym_prefix d_ziextrastep _sym_prefix d_pdestbasestep _sym_prefix d_pzbasestep _sym_prefix d_sfracbasestep _sym_prefix d_ptexbasestep _sym_prefix d_tfracbasestep _sym_prefix d_lightbasestep _sym_prefix d_zibasestep _sym_prefix r_lstepx _sym_prefix r_lstepy _sym_prefix r_sstepx _sym_prefix r_sstepy _sym_prefix r_tstepx _sym_prefix r_tstepy _sym_prefix r_zistepx _sym_prefix r_zistepy _sym_prefix transTable _sym_prefix D_PolysetSetEdgeTable _sym_prefix D_RasterizeAliasPolySmooth ; C-shared globals: _sym_prefix D_PolysetAff8StartT5 _sym_prefix D_PolysetCalcGradientsT5 _sym_prefix D_PolysetRecursiveTriangleT5 _sym_prefix D_PolysetDrawSpans8T5 _sym_prefix D_Aff8PatchT5 _sym_prefix D_PolysetDrawT5 _sym_prefix D_PolysetScanLeftEdgeT5 _sym_prefix D_PolysetDrawFinalVertsT5 _sym_prefix D_DrawNonSubdivT5 _sym_prefix D_PolysetAff8EndT5 _sym_prefix R_TranPatch6 %endif ; _sym_prefix ; externs from C code extern d_viewbuffer extern d_scantable extern r_refdef extern a_sstepxfrac extern r_affinetridesc extern acolormap extern d_pcolormap extern d_sfrac extern d_ptex extern d_pedgespanpackage extern d_tfrac extern d_light extern d_zi extern d_pdest extern d_pz extern d_aspancount extern erroradjustup extern erroradjustdown extern errorterm extern d_xdenom extern r_p0 extern r_p1 extern r_p2 extern a_tstepxfrac extern a_ststepxwhole extern zspantable extern skintable extern d_countextrastep extern ubasestep extern a_spans extern d_pdestextrastep extern d_pzextrastep extern d_sfracextrastep extern d_ptexextrastep extern d_tfracextrastep extern d_lightextrastep extern d_ziextrastep extern d_pdestbasestep extern d_pzbasestep extern d_sfracbasestep extern d_ptexbasestep extern d_tfracbasestep extern d_lightbasestep extern d_zibasestep extern r_lstepx extern r_lstepy extern r_sstepx extern r_sstepy extern r_tstepx extern r_tstepy extern r_zistepx extern r_zistepy extern transTable extern D_PolysetSetEdgeTable extern D_RasterizeAliasPolySmooth ; externs from ASM-only code extern float_point5 extern float_1 extern float_minus_1 extern float_0 extern advancetable extern sstep extern tstep extern ceil_cw extern single_cw SEGMENT .data ALIGN 4 p10_minus_p20 dd 0 p01_minus_p21 dd 0 temp0 dd 0 temp1 dd 0 Ltemp dd 0 aff8entryvec_table dd LDraw8, LDraw7, LDraw6, LDraw5 dd LDraw4, LDraw3, LDraw2, LDraw1 lzistepx dd 0 SEGMENT .text ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetAff8StartT5 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetAff8StartT5 D_PolysetAff8StartT5: ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetCalcGradientsT5 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetCalcGradientsT5 D_PolysetCalcGradientsT5: fild dword [r_p0+0] fild dword [r_p2+0] fild dword [r_p0+4] fild dword [r_p2+4] fild dword [r_p1+0] fild dword [r_p1+4] fxch st3 fsub st0,st2 fxch st1 fsub st0,st4 fxch st5 fsubrp st4,st0 fxch st2 fsubrp st1,st0 fxch st1 fld dword [d_xdenom] fxch st4 fstp dword [p10_minus_p20] fstp dword [p01_minus_p21] fxch st2 fild dword [r_p2+16] fild dword [r_p0+16] fild dword [r_p1+16] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmul st0,st5 fxch st2 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmul st0,st5 fxch st2 fsubrp st3,st0 fsubp st1,st0 fld st2 fmul dword [float_minus_1] fxch st2 fmul st0,st3 fxch st1 fmul st0,st2 fldcw word [ceil_cw] fistp dword [r_lstepy] fistp dword [r_lstepx] fldcw word [single_cw] fild dword [r_p2+8] fild dword [r_p0+8] fild dword [r_p1+8] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmul st0,st6 fxch st2 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmul st0,st6 fxch st2 fsubrp st3,st0 fsubp st1,st0 fmul st0,st2 fxch st1 fmul st0,st3 fxch st1 fistp dword [r_sstepy] fistp dword [r_sstepx] fild dword [r_p2+12] fild dword [r_p0+12] fild dword [r_p1+12] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmul st0,st6 fxch st2 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmul st0,st6 fxch st2 fsubrp st3,st0 fsubp st1,st0 fmul st0,st2 fxch st1 fmul st0,st3 fxch st1 fistp dword [r_tstepy] fistp dword [r_tstepx] fild dword [r_p2+20] fild dword [r_p0+20] fild dword [r_p1+20] fxch st2 fld st0 fsubp st2,st0 fsubp st2,st0 fld st0 fmulp st6,st0 fxch st1 fld st0 fmul dword [p01_minus_p21] fxch st2 fmul dword [p10_minus_p20] fxch st1 fmulp st5,st0 fxch st5 fsubp st1,st0 fxch st3 fsubrp st4,st0 fxch st1 fmulp st2,st0 fmulp st2,st0 fistp dword [r_zistepx] fistp dword [r_zistepy] mov eax, dword [r_sstepx] mov edx, dword [r_tstepx] shl eax,16 shl edx,16 mov dword [a_sstepxfrac],eax mov dword [a_tstepxfrac],edx mov ecx, dword [r_sstepx] mov eax, dword [r_tstepx] sar ecx,16 sar eax,16 imul dword [4+0+esp] add eax,ecx mov dword [a_ststepxwhole],eax ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetRecursiveTriangleT5 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetRecursiveTriangleT5 D_PolysetRecursiveTriangleT5: push ebp push esi push edi push ebx mov esi, dword [8+16+esp] mov ebx, dword [4+16+esp] mov edi, dword [12+16+esp] mov eax, dword [0+esi] mov edx, dword [0+ebx] mov ebp, dword [4+esi] sub eax,edx mov ecx, dword [4+ebx] sub ebp,ecx inc eax cmp eax,2 ja LSplit mov eax, dword [0+edi] inc ebp cmp ebp,2 ja LSplit mov edx, dword [0+esi] mov ebp, dword [4+edi] sub eax,edx mov ecx, dword [4+esi] sub ebp,ecx inc eax cmp eax,2 ja LSplit2 mov eax, dword [0+ebx] inc ebp cmp ebp,2 ja LSplit2 mov edx, dword [0+edi] mov ebp, dword [4+ebx] sub eax,edx mov ecx, dword [4+edi] sub ebp,ecx inc eax inc ebp mov edx,ebx cmp eax,2 ja LSplit3 cmp ebp,2 jna near LDone LSplit3: mov ebx,edi mov edi,esi mov esi,edx jmp LSplit LSplit2: mov eax,ebx mov ebx,esi mov esi,edi mov edi,eax LSplit: sub esp,24 mov eax, dword [8+ebx] mov edx, dword [8+esi] mov ecx, dword [12+ebx] add eax,edx mov edx, dword [12+esi] sar eax,1 add ecx,edx mov dword [8+esp],eax mov eax, dword [20+ebx] sar ecx,1 mov edx, dword [20+esi] mov dword [12+esp],ecx add eax,edx mov ecx, dword [0+ebx] mov edx, dword [0+esi] sar eax,1 add edx,ecx mov dword [20+esp],eax mov eax, dword [4+ebx] sar edx,1 mov ebp, dword [4+esi] mov dword [0+esp],edx add ebp,eax sar ebp,1 mov dword [4+esp],ebp cmp dword [4+esi],eax jg LNoDraw mov edx, dword [0+esi] jnz LDraw cmp edx,ecx jl LNoDraw LDraw: mov edx, dword [20+esp] mov ecx, dword [4+esp] sar edx,16 mov ebp, dword [0+esp] mov eax, dword [zspantable+ecx*4] cmp dx, word [eax+ebp*2] jnge LNoDraw ;mov word [eax+ebp*2],dx mov eax, dword [12+esp] sar eax,16 mov edx, dword [8+esp] sar edx,16 sub ecx,ecx mov eax, dword [skintable+eax*4] mov ebp, dword [4+esp] mov dh, byte [eax+edx] ; texture pixel or dh,dh jz Skip1B ; color 0 = no draw ;mov edx, dword [d_pcolormap] ;mov dh, byte [edx+ecx] mov ecx, dword [0+esp] mov eax, dword [d_scantable+ebp*4] add ecx,eax mov eax, dword [d_viewbuffer] ; trans stuff mov dl, byte [eax+ecx] and edx, 0ffffh mov dh, byte [12345678h+edx] TranPatch1: mov byte [eax+ecx],dh ;rjr distance ;mov byte [eax+ecx],0 Skip1B: LNoDraw: push esp push ebx push edi call D_PolysetRecursiveTriangleT5 ; call near D_PolysetRecursiveTriangleT5 mov ebx,esp push esi push ebx push edi call D_PolysetRecursiveTriangleT5 ; call near D_PolysetRecursiveTriangleT5 add esp,24 LDone: pop ebx pop edi pop esi pop ebp ret 12 ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetDrawSpans8T5 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetDrawSpans8T5 D_PolysetDrawSpans8T5: push esi push ebx mov esi, dword [4+8+esp] mov ecx, dword [r_zistepx] push ebp push edi ror ecx,16 mov edx, dword [8+esi] mov dword [lzistepx],ecx LSpanLoop: mov eax, dword [d_aspancount] sub eax,edx mov edx, dword [erroradjustup] mov ebx, dword [errorterm] add ebx,edx js LNoTurnover mov edx, dword [erroradjustdown] mov edi, dword [d_countextrastep] sub ebx,edx mov ebp, dword [d_aspancount] mov dword [errorterm],ebx add ebp,edi mov dword [d_aspancount],ebp jmp LRightEdgeStepped LNoTurnover: mov edi, dword [d_aspancount] mov edx, dword [ubasestep] mov dword [errorterm],ebx add edi,edx mov dword [d_aspancount],edi LRightEdgeStepped: cmp eax,1 jl near LNextSpan jz near LExactlyOneLong mov ecx, dword [a_ststepxwhole] mov edx, dword [r_affinetridesc+8] mov dword [advancetable+4],ecx add ecx,edx mov dword [advancetable],ecx mov ecx, dword [a_tstepxfrac] mov cx, word [r_lstepx] mov edx,eax mov dword [tstep],ecx add edx,7 shr edx,3 mov ebx, dword [16+esi] mov bx,dx mov ecx, dword [4+esi] neg eax mov edi, dword [0+esi] and eax,7 sub edi,eax sub ecx,eax sub ecx,eax mov edx, dword [20+esi] mov dx, word [24+esi] mov ebp, dword [28+esi] ror ebp,16 push esi mov esi, dword [12+esi] jmp dword[aff8entryvec_table+eax*4] LDrawLoop: LDraw8: cmp bp, word [ecx] jl Lp1 xor eax,eax ;mov ah,dh ; light mov ah, byte [esi] ; texture pixel or ah,ah jz SkipA2 ; color 0 = no draw ;mov word [ecx],bp ;mov ah, byte [12345678h+eax] ;LPatch8: ; trans stuff mov al, byte [edi] mov ah, byte [12345678h+eax] TranPatch2: mov byte [edi],ah ;rj ;mov byte [edi],0 SkipA2: Lp1: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw7: cmp bp, word [2+ecx] jl Lp2 xor eax,eax ;mov ah,dh mov ah, byte [esi] ; texture pixel or ah,ah jz SkipB2 ; color 0 = no draw ;mov word [2+ecx],bp ;mov ah, byte [12345678h+eax] ;LPatch7: ; trans stuff mov al, byte [edi+1] mov ah, byte [12345678h+eax] TranPatch3: mov byte [1+edi],ah ;rj ;mov byte [1+edi],0 SkipB2: Lp2: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw6: cmp bp, word [4+ecx] jl Lp3 xor eax,eax ;mov ah,dh mov ah, byte [esi] ; texture pixel or ah,ah jz SkipC2 ; color 0 = no draw ;mov word [4+ecx],bp ;mov ah, byte [12345678h+eax] ;LPatch6: ; trans stuff mov al, byte [edi+2] mov ah, byte [12345678h+eax] TranPatch4: mov byte [2+edi],ah ;rj ;mov byte [2+edi],0 SkipC2: Lp3: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw5: cmp bp, word [6+ecx] jl Lp4 xor eax,eax ;mov ah,dh mov ah, byte [esi] ; texture pixel or ah,ah jz SkipD2 ; color 0 = no draw ;mov word [6+ecx],bp ;mov ah, byte [12345678h+eax] ;LPatch5: ; trans stuff mov al, byte [edi+3] mov ah, byte [12345678h+eax] TranPatch5: mov byte [3+edi],ah ;rj ;mov byte [3+edi],0 SkipD2: Lp4: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw4: cmp bp, word [8+ecx] jl Lp5 xor eax,eax ;mov ah,dh mov ah, byte [esi] ; texture pixel or ah,ah jz SkipE2 ; color 0 = no draw ;mov word [8+ecx],bp ;mov ah, byte [12345678h+eax] ;LPatch4: ; trans stuff mov al, byte [edi+4] mov ah, byte [12345678h+eax] TranPatch6: mov byte [4+edi],ah ;rj ;mov byte [4+edi],0 SkipE2: Lp5: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw3: cmp bp, word [10+ecx] jl Lp6 xor eax,eax ;mov ah,dh mov ah, byte [esi] ; texture pixel or ah,ah jz SkipF2 ; color 0 = no draw ;mov word [10+ecx],bp ;mov ah, byte [12345678h+eax] ;LPatch3: ; trans stuff mov al, byte [edi+5] mov ah, byte [12345678h+eax] TranPatch7: mov byte [5+edi],ah ;rj ;mov byte [5+edi],0 SkipF2: Lp6: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw2: cmp bp, word [12+ecx] jl Lp7 xor eax,eax ;mov ah,dh mov ah, byte [esi] ; texture pixel or ah,ah jz SkipG2 ; color 0 = no draw ;mov word [12+ecx],bp ;mov ah, byte [12345678h+eax] ;LPatch2: ; trans stuff mov al, byte [edi+6] mov ah, byte [12345678h+eax] TranPatch8: mov byte [6+edi],ah ;rj ;mov byte [6+edi],0 SkipG2: Lp7: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] LDraw1: cmp bp, word [14+ecx] jl Lp8 xor eax,eax ;mov ah,dh mov ah, byte [esi] ; texture pixel or ah,ah jz SkipH2 ; color 0 = no draw ;mov word [14+ecx],bp ;mov ah, byte [12345678h+eax] ;LPatch1: ; trans stuff mov al, byte [edi+7] mov ah, byte [12345678h+eax] TranPatch9: mov byte [7+edi],ah ;rj ;mov byte [7+edi],0 SkipH2: Lp8: add edx, dword [tstep] sbb eax,eax add ebp, dword [lzistepx] adc ebp,0 add ebx, dword [a_sstepxfrac] adc esi, dword [advancetable+4+eax*4] add edi,8 add ecx,16 dec bx jnz near LDrawLoop pop esi LNextSpan: add esi,32 LNextSpanESISet: mov edx, dword [8+esi] cmp edx,offset -999999 jnz near LSpanLoop pop edi pop ebp pop ebx pop esi ret LExactlyOneLong: mov ecx, dword [4+esi] mov ebp, dword [28+esi] ror ebp,16 mov ebx, dword [12+esi] cmp bp, word [ecx] jl LNextSpan xor eax,eax mov edi, dword [0+esi] ;mov ah, byte [24+1+esi] add esi,32 mov ah, byte [ebx] ; texture pixel or ah,ah jz SkipI2 ; color 0 = no draw ;mov word [ecx],bp ;mov ah, byte [12345678h+eax] ;LPatch9: ; trans stuff mov al, byte [edi] mov ah, byte [12345678h+eax] TranPatch10: mov byte [edi],ah ;rjr ;mov byte [edi],0 SkipI2: jmp LNextSpanESISet ;;;;;;;;;;;;;;;;;;;;;;;; ; D_Aff8PatchT5 ;;;;;;;;;;;;;;;;;;;;;;;; global D_Aff8PatchT5 D_Aff8PatchT5: ret ;mov eax, dword [4+esp] ;mov dword [LPatch1-4],eax ;mov dword [LPatch2-4],eax ;mov dword [LPatch3-4],eax ;mov dword [LPatch4-4],eax ;mov dword [LPatch5-4],eax ;mov dword [LPatch6-4],eax ;mov dword [LPatch7-4],eax ;mov dword [LPatch8-4],eax ;mov dword [LPatch9-4],eax ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetDrawT5 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetDrawT5 D_PolysetDrawT5: sub esp,offset SPAN_SIZE mov eax,esp add eax,32 - 1 and eax,offset ~(32 - 1) mov dword [a_spans],eax mov eax, dword [r_affinetridesc+28] test eax,eax jz near D_DrawNonSubdivT5 ;push ebp ;mov ebp, dword [r_affinetridesc+24] push esi ;shl ebp,4 push ebx mov ebx, dword [r_affinetridesc+16] push edi mov edi, dword [r_affinetridesc+20] Llooptop: xor ecx,ecx xor esi,esi xor edx,edx mov cx,word[4+0+ebx] mov si,word[4+2+ebx] mov dx,word[4+4+ebx] shl ecx,5 shl esi,5 add ecx,edi shl edx,5 add esi,edi add edx,edi fild dword [0+4+ecx] fild dword [0+4+esi] fild dword [0+0+ecx] fild dword [0+0+edx] fxch st2 fsubr st0,st3 fild dword [0+0+esi] fxch st2 fsubr st3,st0 fild dword [0+4+edx] fxch st1 fsubrp st3,st0 fxch st1 fmulp st3,st0 fsubp st3,st0 mov eax, dword [0+16+ecx] and eax,0FF00h fmulp st2,st0 add eax, dword [acolormap] fsubrp st1,st0 mov dword [d_pcolormap],eax fstp dword [Ltemp] mov eax, dword [Ltemp] sub eax,080000001h jc Lskip mov eax, dword [0+ebx] test eax,eax jz Lfacesback push edx push esi push ecx call D_PolysetRecursiveTriangleT5 ; call near D_PolysetRecursiveTriangleT5 ;sub ebp,16 ;jnz Llooptop jmp Ldone2 Lfacesback: mov eax, dword [0+8+ecx] push eax mov eax, dword [0+8+esi] push eax mov eax, dword [0+8+edx] push eax push ecx push edx mov eax, dword [r_affinetridesc+32] test dword [24+ecx],00020h jz Lp11 add dword [0+8+ecx],eax Lp11: test dword [24+esi],00020h jz Lp12 add dword [0+8+esi],eax Lp12: test dword [24+edx],00020h jz Lp13 add dword [0+8+edx],eax Lp13: push edx push esi push ecx call D_PolysetRecursiveTriangleT5 ; call near D_PolysetRecursiveTriangleT5 pop edx pop ecx pop eax mov dword [0+8+edx],eax pop eax mov dword [0+8+esi],eax pop eax mov dword [0+8+ecx],eax Lskip: ;sub ebp,16 ;jnz Llooptop Ldone2: pop edi pop ebx pop esi ;pop ebp add esp,offset SPAN_SIZE ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetScanLeftEdgeT5 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetScanLeftEdgeT5 D_PolysetScanLeftEdgeT5: push ebp push esi push edi push ebx mov eax, dword [4+16+esp] mov ecx, dword [d_sfrac] and eax,0FFFFh mov ebx, dword [d_ptex] or ecx,eax mov esi, dword [d_pedgespanpackage] mov edx, dword [d_tfrac] mov edi, dword [d_light] mov ebp, dword [d_zi] LScanLoop: mov dword [12+esi],ebx mov eax, dword [d_pdest] mov dword [0+esi],eax mov eax, dword [d_pz] mov dword [4+esi],eax mov eax, dword [d_aspancount] mov dword [8+esi],eax mov dword [24+esi],edi mov dword [28+esi],ebp mov dword [16+esi],ecx mov dword [20+esi],edx mov al, byte [32+esi] add esi,32 mov eax, dword [erroradjustup] mov dword [d_pedgespanpackage],esi mov esi, dword [errorterm] add esi,eax mov eax, dword [d_pdest] js near LNoLeftEdgeTurnover sub esi, dword [erroradjustdown] add eax, dword [d_pdestextrastep] mov dword [errorterm],esi mov dword [d_pdest],eax mov eax, dword [d_pz] mov esi, dword [d_aspancount] add eax, dword [d_pzextrastep] add ecx, dword [d_sfracextrastep] adc ebx, dword [d_ptexextrastep] add esi, dword [d_countextrastep] mov dword [d_pz],eax mov eax, dword [d_tfracextrastep] mov dword [d_aspancount],esi add edx,eax jnc LSkip1 add ebx, dword [r_affinetridesc+8] LSkip1: add edi, dword [d_lightextrastep] add ebp, dword [d_ziextrastep] mov esi, dword [d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz near LScanLoop pop ebx pop edi pop esi pop ebp ret LNoLeftEdgeTurnover: mov dword [errorterm],esi add eax, dword [d_pdestbasestep] mov dword [d_pdest],eax mov eax, dword [d_pz] mov esi, dword [d_aspancount] add eax, dword [d_pzbasestep] add ecx, dword [d_sfracbasestep] adc ebx, dword [d_ptexbasestep] add esi, dword [ubasestep] mov dword [d_pz],eax mov dword [d_aspancount],esi mov esi, dword [d_tfracbasestep] add edx,esi jnc LSkip2 add ebx, dword [r_affinetridesc+8] LSkip2: add edi, dword [d_lightbasestep] add ebp, dword [d_zibasestep] mov esi, dword [d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz near LScanLoop pop ebx pop edi pop esi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetDrawFinalVertsT5 ;;;;;;;;;;;;;;;;;;;;;;;; L_PSDFVertT5: push esi push edi mov eax, dword [0+0+ebx] mov edx, dword [r_refdef+40] cmp eax,edx jge LNextVert mov esi, dword [0+4+ebx] mov edx, dword [r_refdef+44] cmp esi,edx jge LNextVert mov edi, dword [zspantable+esi*4] mov edx, dword [0+20+ebx] shr edx,16 cmp dx, word [edi+eax*2] jl LNextVert ;mov word [edi+eax*2],dx mov edi, dword [0+12+ebx] shr edi,16 mov edi, dword [skintable+edi*4] mov edx, dword [0+8+ebx] shr edx,16 mov dh, byte [edi+edx] ; texture pixel or dh,dh jz Skip2B ; color 0 = no draw mov edi, dword [0+16+ebx] and edi,0FF00h ;and edx,000FFh add edi,edx ;mov edx, dword [acolormap] ;mov dh, byte [edx+edi*1] mov edi, dword [d_scantable+esi*4] mov esi, dword [d_viewbuffer] add edi,eax ; trans stuff mov dl, byte [esi+edi] and edx, 0ffffh mov dh, byte [12345678h+edx] TranPatch11: mov byte [esi+edi],dh ;rjr distance ;mov byte [esi+edi],0 Skip2B: LNextVert: pop edi pop esi ret global D_PolysetDrawFinalVertsT5 D_PolysetDrawFinalVertsT5: push ebp push ebx mov ebx,dword[4+8+esp] ;pv1 call L_PSDFVertT5 mov ebx,dword[8+8+esp] ;pv2 call L_PSDFVertT5 mov ebx,dword[12+8+esp];pv3 call L_PSDFVertT5 pop ebx pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_DrawNonSubdivT5 ;;;;;;;;;;;;;;;;;;;;;;;; global D_DrawNonSubdivT5 D_DrawNonSubdivT5: ;push ebp ;mov ebp, dword [r_affinetridesc+24] push ebx ;shl ebp,4 push esi mov esi, dword [r_affinetridesc+16] push edi LNDLoop: mov edi, dword [r_affinetridesc+20] xor ecx,ecx; //clear i1 xor edx,edx; //clear i2 xor ebx,ebx; //clear i3 mov cx, word[4+0+esi] ;ptri->vertindex[0] mov dx, word[4+2+esi] ;ptri->vertindex[1] mov bx, word[4+4+esi] ;ptri->vertindex[2] shl ecx,5 shl edx,5 shl ebx,5 add ecx,edi add edx,edi add ebx,edi mov eax, dword [0+4+ecx] mov esi, dword [0+0+ecx] sub eax, dword [0+4+edx] sub esi, dword [0+0+ebx] imul eax,esi mov esi, dword [0+0+ecx] mov edi, dword [0+4+ecx] sub esi, dword [0+0+edx] sub edi, dword [0+4+ebx] imul edi,esi sub eax,edi jns near LNextTri mov dword [d_xdenom],eax fild dword [d_xdenom] mov eax, dword [0+0+ecx] mov esi, dword [0+4+ecx] mov dword [r_p0+0],eax mov dword [r_p0+4],esi mov eax, dword [0+8+ecx] mov esi, dword [0+12+ecx] mov dword [r_p0+8],eax mov dword [r_p0+12],esi mov eax, dword [0+16+ecx] mov esi, dword [0+20+ecx] mov dword [r_p0+16],eax mov dword [r_p0+20],esi fdivr dword [float_1] mov eax, dword [0+0+edx] mov esi, dword [0+4+edx] mov dword [r_p1+0],eax mov dword [r_p1+4],esi mov eax, dword [0+8+edx] mov esi, dword [0+12+edx] mov dword [r_p1+8],eax mov dword [r_p1+12],esi mov eax, dword [0+16+edx] mov esi, dword [0+20+edx] mov dword [r_p1+16],eax mov dword [r_p1+20],esi mov eax, dword [0+0+ebx] mov esi, dword [0+4+ebx] mov dword [r_p2+0],eax mov dword [r_p2+4],esi mov eax, dword [0+8+ebx] mov esi, dword [0+12+ebx] mov dword [r_p2+8],eax mov dword [r_p2+12],esi mov eax, dword [0+16+ebx] mov esi, dword [0+20+ebx] mov dword [r_p2+16],eax mov edi, dword [r_affinetridesc+16] mov dword [r_p2+20],esi mov eax, dword [0+edi] test eax,eax jnz LFacesFront mov eax, dword [24+ecx] mov esi, dword [24+edx] mov edi, dword [24+ebx] test eax,00020h mov eax, dword [r_affinetridesc+32] jz LOnseamDone0 add dword [r_p0+8],eax LOnseamDone0: test esi,00020h jz LOnseamDone1 add dword [r_p1+8],eax LOnseamDone1: test edi,00020h jz LOnseamDone2 add dword [r_p2+8],eax LOnseamDone2: LFacesFront: fstp dword [d_xdenom] call D_PolysetSetEdgeTable ; call near D_PolysetSetEdgeTable call D_RasterizeAliasPolySmooth ; call near D_RasterizeAliasPolySmooth LNextTri: mov esi, dword [r_affinetridesc+16] ;sub ebp,16 ;jnz LNDLoop pop edi pop esi pop ebx ;pop ebp add esp,offset SPAN_SIZE ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_PolysetAff8EndT5 ;;;;;;;;;;;;;;;;;;;;;;;; global D_PolysetAff8EndT5 D_PolysetAff8EndT5: SEGMENT .data ALIGN 4 LPatchTable: dd TranPatch1-4 dd TranPatch2-4 dd TranPatch3-4 dd TranPatch4-4 dd TranPatch5-4 dd TranPatch6-4 dd TranPatch7-4 dd TranPatch8-4 dd TranPatch9-4 dd TranPatch10-4 dd TranPatch11-4 SEGMENT .text ALIGN 4 ;;;;;;;;;;;;;;;;;;;;;;;; ; R_TranPatch6 ;;;;;;;;;;;;;;;;;;;;;;;; global R_TranPatch6 R_TranPatch6: push ebx mov eax, dword [transTable] mov ebx,offset LPatchTable mov ecx,11 LPatchLoop: mov edx, dword [ebx] add ebx,4 mov dword [edx],eax dec ecx jnz LPatchLoop pop ebx ret engine/h2shared/d_polyse.c000066400000000000000000001376011444734033100160200ustar00rootroot00000000000000/* d_polyset.c - routines for drawing sets of polygons sharing the same * texture (used for Alias models.) * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * C versions of several asm functions: Juraj Styk * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" #include "d_local.h" ASM_LINKAGE_BEGIN int r_p0[6], r_p1[6], r_p2[6]; byte *d_pcolormap; int d_aflatcolor; int d_xdenom; edgetable_t *pedgetable; edgetable_t edgetables[12] = { { 0, 1, r_p0, r_p2, NULL, 2, r_p0, r_p1, r_p2 }, { 0, 2, r_p1, r_p0, r_p2, 1, r_p1, r_p2, NULL }, { 1, 1, r_p0, r_p2, NULL, 1, r_p1, r_p2, NULL }, { 0, 1, r_p1, r_p0, NULL, 2, r_p1, r_p2, r_p0 }, { 0, 2, r_p0, r_p2, r_p1, 1, r_p0, r_p1, NULL }, { 0, 1, r_p2, r_p1, NULL, 1, r_p2, r_p0, NULL }, { 0, 1, r_p2, r_p1, NULL, 2, r_p2, r_p0, r_p1 }, { 0, 2, r_p2, r_p1, r_p0, 1, r_p2, r_p0, NULL }, { 0, 1, r_p1, r_p0, NULL, 1, r_p1, r_p2, NULL }, { 1, 1, r_p2, r_p1, NULL, 1, r_p0, r_p1, NULL }, { 1, 1, r_p1, r_p0, NULL, 1, r_p2, r_p0, NULL }, { 0, 1, r_p0, r_p2, NULL, 1, r_p0, r_p1, NULL } }; // FIXME: some of these can become statics int a_sstepxfrac, a_tstepxfrac, r_lstepx, a_ststepxwhole; int r_sstepx, r_tstepx, r_lstepy, r_sstepy, r_tstepy; int r_zistepx, r_zistepy; int d_aspancount, d_countextrastep; spanpackage_t *a_spans; spanpackage_t *d_pedgespanpackage; byte *d_pdest, *d_ptex; short *d_pz; int d_sfrac, d_tfrac, d_light, d_zi; int d_ptexextrastep, d_sfracextrastep; int d_tfracextrastep, d_lightextrastep, d_pdestextrastep; int d_lightbasestep, d_pdestbasestep, d_ptexbasestep; int d_sfracbasestep, d_tfracbasestep; int d_ziextrastep, d_zibasestep; int d_pzextrastep, d_pzbasestep; byte *skintable[MAX_SKIN_HEIGHT]; ASM_LINKAGE_END int skinwidth; byte *skinstart; #if !id68k static int ystart; #endif typedef struct { int quotient; int remainder; } adivtab_t; ASM_LINKAGE_BEGIN adivtab_t adivtab[32*32] = { #include "adivtab.h" }; ASM_LINKAGE_END #if !id386 static spanpackage_t spans[DPS_MAXSPANS + 1 + ((CACHE_SIZE - 1) / sizeof(spanpackage_t)) + 1]; /* one extra because of cache line pretouching */ #if !id68k static void D_PolysetDrawSpans8 (spanpackage_t *pspanpackage); static void D_PolysetDrawSpans8T (spanpackage_t *pspanpackage); static void D_PolysetDrawSpans8T2 (spanpackage_t *pspanpackage); static void D_PolysetDrawSpans8T3 (spanpackage_t *pspanpackage); static void D_PolysetDrawSpans8T5 (spanpackage_t *pspanpackage); #endif #endif #if id68k ASM_LINKAGE_BEGIN void (*d_polysetdrawspans) (spanpackage_t *pspanpackage); ASM_LINKAGE_END #endif #if !id386 /* ================ D_PolysetDrawFinalVerts ================ */ #if !id68k static inline void do_PolysetDrawFinalVerts (finalvert_t *pv) { int z; short *zbuf; // valid triangle coordinates for filling can include the bottom and // right clip edges, due to the fill rule; these shouldn't be drawn if (pv->v[0] < r_refdef.vrectright && pv->v[1] < r_refdef.vrectbottom) { z = pv->v[5]>>16; zbuf = zspantable[pv->v[1]] + pv->v[0]; if (z >= *zbuf) { unsigned int pix; *zbuf = z; pix = skintable[pv->v[3]>>16][pv->v[2]>>16]; pix = ((byte *)acolormap)[pix + (pv->v[4] & 0xFF00)]; d_viewbuffer[d_scantable[pv->v[1]] + pv->v[0]] = pix; } } } static inline void do_PolysetDrawFinalVertsT (finalvert_t *pv) { int z; short *zbuf; // valid triangle coordinates for filling can include the bottom and // right clip edges, due to the fill rule; these shouldn't be drawn if (pv->v[0] < r_refdef.vrectright && pv->v[1] < r_refdef.vrectbottom) { z = pv->v[5]>>16; zbuf = zspantable[pv->v[1]] + pv->v[0]; if (z >= *zbuf) { const byte color_map_idx = skintable[pv->v[3]>>16][pv->v[2]>>16]; if (color_map_idx != 0) { unsigned int pix, pix2; *zbuf = z; pix = ((byte *)acolormap)[color_map_idx + (pv->v[4] & 0xFF00)]; pix2 = d_viewbuffer[d_scantable[pv->v[1]] + pv->v[0]]; pix = mainTransTable[(pix<<8) + pix2]; d_viewbuffer[d_scantable[pv->v[1]] + pv->v[0]] = pix; } } } } static inline void do_PolysetDrawFinalVertsT2 (finalvert_t *pv) { int z; short *zbuf; // valid triangle coordinates for filling can include the bottom and // right clip edges, due to the fill rule; these shouldn't be drawn if (pv->v[0] < r_refdef.vrectright && pv->v[1] < r_refdef.vrectbottom) { z = pv->v[5]>>16; zbuf = zspantable[pv->v[1]] + pv->v[0]; if (z >= *zbuf) { const byte color_map_idx = skintable[pv->v[3]>>16][pv->v[2]>>16]; if (color_map_idx != 0) { unsigned int pix, pix2; *zbuf = z; pix = ((byte *)acolormap)[color_map_idx + (pv->v[4] & 0xFF00)]; if (!(color_map_idx & 0x1)) { d_viewbuffer[d_scantable[pv->v[1]] + pv->v[0]] = pix; } else { pix2 = d_viewbuffer[d_scantable[pv->v[1]] + pv->v[0]]; pix = mainTransTable[(pix<<8) + pix2]; d_viewbuffer[d_scantable[pv->v[1]] + pv->v[0]] = pix; } } } } } static inline void do_PolysetDrawFinalVertsT3 (finalvert_t *pv) { int z; short *zbuf; // valid triangle coordinates for filling can include the bottom and // right clip edges, due to the fill rule; these shouldn't be drawn if (pv->v[0] < r_refdef.vrectright && pv->v[1] < r_refdef.vrectbottom) { z = pv->v[5]>>16; zbuf = zspantable[pv->v[1]] + pv->v[0]; if (z >= *zbuf) { const byte color_map_idx = skintable[pv->v[3]>>16][pv->v[2]>>16]; if (color_map_idx != 0) { unsigned int pix; *zbuf = z; pix = ((byte *)acolormap)[color_map_idx + (pv->v[4] & 0xFF00)]; d_viewbuffer[d_scantable[pv->v[1]] + pv->v[0]] = pix; } } } } static inline void do_PolysetDrawFinalVertsT5 (finalvert_t *pv) { int z; short *zbuf; // valid triangle coordinates for filling can include the bottom and // right clip edges, due to the fill rule; these shouldn't be drawn if (pv->v[0] < r_refdef.vrectright && pv->v[1] < r_refdef.vrectbottom) { z = pv->v[5]>>16; zbuf = zspantable[pv->v[1]] + pv->v[0]; if (z >= *zbuf) { const byte color_map_idx = skintable[pv->v[3]>>16][pv->v[2]>>16]; if (color_map_idx != 0) { unsigned int pix, pix2; *zbuf = z; pix2 = d_viewbuffer[d_scantable[pv->v[1]] + pv->v[0]]; pix = transTable[(color_map_idx<<8) + pix2]; d_viewbuffer[d_scantable[pv->v[1]] + pv->v[0]] = pix; } } } } void D_PolysetDrawFinalVerts (finalvert_t *pv1, finalvert_t *pv2, finalvert_t *pv3) { do_PolysetDrawFinalVerts (pv1); do_PolysetDrawFinalVerts (pv2); do_PolysetDrawFinalVerts (pv3); } void D_PolysetDrawFinalVertsT (finalvert_t *pv1, finalvert_t *pv2, finalvert_t *pv3) { do_PolysetDrawFinalVertsT (pv1); do_PolysetDrawFinalVertsT (pv2); do_PolysetDrawFinalVertsT (pv3); } void D_PolysetDrawFinalVertsT2 (finalvert_t *pv1, finalvert_t *pv2, finalvert_t *pv3) { do_PolysetDrawFinalVertsT2 (pv1); do_PolysetDrawFinalVertsT2 (pv2); do_PolysetDrawFinalVertsT2 (pv3); } void D_PolysetDrawFinalVertsT3 (finalvert_t *pv1, finalvert_t *pv2, finalvert_t *pv3) { do_PolysetDrawFinalVertsT3 (pv1); do_PolysetDrawFinalVertsT3 (pv2); do_PolysetDrawFinalVertsT3 (pv3); } void D_PolysetDrawFinalVertsT5 (finalvert_t *pv1, finalvert_t *pv2, finalvert_t *pv3) { do_PolysetDrawFinalVertsT5 (pv1); do_PolysetDrawFinalVertsT5 (pv2); do_PolysetDrawFinalVertsT5 (pv3); } #endif /* ================ D_PolysetRecursiveTriangle ================ */ #if !id68k static void D_PolysetRecursiveTriangle (int *lp1, int *lp2, int *lp3) { int *temp; int d; int new_p[6]; int z; short *zbuf; d = lp2[0] - lp1[0]; if (d < -1 || d > 1) goto split; d = lp2[1] - lp1[1]; if (d < -1 || d > 1) goto split; d = lp3[0] - lp2[0]; if (d < -1 || d > 1) goto split2; d = lp3[1] - lp2[1]; if (d < -1 || d > 1) goto split2; d = lp1[0] - lp3[0]; if (d < -1 || d > 1) goto split3; d = lp1[1] - lp3[1]; if (d < -1 || d > 1) { split3: temp = lp1; lp1 = lp3; lp3 = lp2; lp2 = temp; goto split; } return; // entire tri is filled split2: temp = lp1; lp1 = lp2; lp2 = lp3; lp3 = temp; split: // split this edge new_p[0] = (lp1[0] + lp2[0]) >> 1; new_p[1] = (lp1[1] + lp2[1]) >> 1; new_p[2] = (lp1[2] + lp2[2]) >> 1; new_p[3] = (lp1[3] + lp2[3]) >> 1; new_p[5] = (lp1[5] + lp2[5]) >> 1; // draw the point if splitting a leading edge if (lp2[1] > lp1[1]) goto nodraw; if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) goto nodraw; z = new_p[5]>>16; zbuf = zspantable[new_p[1]] + new_p[0]; if (z >= *zbuf) { unsigned int pix; *zbuf = z; pix = d_pcolormap[skintable[new_p[3]>>16][new_p[2]>>16]]; d_viewbuffer[d_scantable[new_p[1]] + new_p[0]] = pix; } nodraw: // recursively continue D_PolysetRecursiveTriangle (lp3, lp1, new_p); D_PolysetRecursiveTriangle (lp3, new_p, lp2); } static void D_PolysetRecursiveTriangleT (int *lp1, int *lp2, int *lp3) { int *temp; int d; int new_p[6]; int z; short *zbuf; d = lp2[0] - lp1[0]; if (d < -1 || d > 1) goto split; d = lp2[1] - lp1[1]; if (d < -1 || d > 1) goto split; d = lp3[0] - lp2[0]; if (d < -1 || d > 1) goto split2; d = lp3[1] - lp2[1]; if (d < -1 || d > 1) goto split2; d = lp1[0] - lp3[0]; if (d < -1 || d > 1) goto split3; d = lp1[1] - lp3[1]; if (d < -1 || d > 1) { split3: temp = lp1; lp1 = lp3; lp3 = lp2; lp2 = temp; goto split; } return; // entire tri is filled split2: temp = lp1; lp1 = lp2; lp2 = lp3; lp3 = temp; split: // split this edge new_p[0] = (lp1[0] + lp2[0]) >> 1; new_p[1] = (lp1[1] + lp2[1]) >> 1; new_p[2] = (lp1[2] + lp2[2]) >> 1; new_p[3] = (lp1[3] + lp2[3]) >> 1; new_p[5] = (lp1[5] + lp2[5]) >> 1; // draw the point if splitting a leading edge if (lp2[1] > lp1[1]) goto nodraw; if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) goto nodraw; z = new_p[5]>>16; zbuf = zspantable[new_p[1]] + new_p[0]; if (z >= *zbuf) { const byte color_map_idx = skintable[new_p[3]>>16][new_p[2]>>16]; if (color_map_idx != 0) { unsigned int pix, pix2; *zbuf = z; pix = d_pcolormap[color_map_idx]; pix2 = d_viewbuffer[d_scantable[new_p[1]] + new_p[0]]; pix = mainTransTable[(pix<<8) + pix2]; d_viewbuffer[d_scantable[new_p[1]] + new_p[0]] = pix; } } nodraw: // recursively continue D_PolysetRecursiveTriangleT (lp3, lp1, new_p); D_PolysetRecursiveTriangleT (lp3, new_p, lp2); } static void D_PolysetRecursiveTriangleT2 (int *lp1, int *lp2, int *lp3) { int *temp; int d; int new_p[6]; int z; short *zbuf; d = lp2[0] - lp1[0]; if (d < -1 || d > 1) goto split; d = lp2[1] - lp1[1]; if (d < -1 || d > 1) goto split; d = lp3[0] - lp2[0]; if (d < -1 || d > 1) goto split2; d = lp3[1] - lp2[1]; if (d < -1 || d > 1) goto split2; d = lp1[0] - lp3[0]; if (d < -1 || d > 1) goto split3; d = lp1[1] - lp3[1]; if (d < -1 || d > 1) { split3: temp = lp1; lp1 = lp3; lp3 = lp2; lp2 = temp; goto split; } return; // entire tri is filled split2: temp = lp1; lp1 = lp2; lp2 = lp3; lp3 = temp; split: // split this edge new_p[0] = (lp1[0] + lp2[0]) >> 1; new_p[1] = (lp1[1] + lp2[1]) >> 1; new_p[2] = (lp1[2] + lp2[2]) >> 1; new_p[3] = (lp1[3] + lp2[3]) >> 1; new_p[5] = (lp1[5] + lp2[5]) >> 1; // draw the point if splitting a leading edge if (lp2[1] > lp1[1]) goto nodraw; if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) goto nodraw; z = new_p[5]>>16; zbuf = zspantable[new_p[1]] + new_p[0]; if (z >= *zbuf) { const byte color_map_idx = skintable[new_p[3]>>16][new_p[2]>>16]; if (color_map_idx != 0) { unsigned int pix, pix2; *zbuf = z; pix = d_pcolormap[color_map_idx]; if (!(color_map_idx & 0x1)) { d_viewbuffer[d_scantable[new_p[1]] + new_p[0]] = pix; } else { pix2 = d_viewbuffer[d_scantable[new_p[1]] + new_p[0]]; pix = mainTransTable[(pix<<8) + pix2]; d_viewbuffer[d_scantable[new_p[1]] + new_p[0]] = pix; } } } nodraw: // recursively continue D_PolysetRecursiveTriangleT2 (lp3, lp1, new_p); D_PolysetRecursiveTriangleT2 (lp3, new_p, lp2); } static void D_PolysetRecursiveTriangleT3 (int *lp1, int *lp2, int *lp3) { int *temp; int d; int new_p[6]; int z; short *zbuf; d = lp2[0] - lp1[0]; if (d < -1 || d > 1) goto split; d = lp2[1] - lp1[1]; if (d < -1 || d > 1) goto split; d = lp3[0] - lp2[0]; if (d < -1 || d > 1) goto split2; d = lp3[1] - lp2[1]; if (d < -1 || d > 1) goto split2; d = lp1[0] - lp3[0]; if (d < -1 || d > 1) goto split3; d = lp1[1] - lp3[1]; if (d < -1 || d > 1) { split3: temp = lp1; lp1 = lp3; lp3 = lp2; lp2 = temp; goto split; } return; // entire tri is filled split2: temp = lp1; lp1 = lp2; lp2 = lp3; lp3 = temp; split: // split this edge new_p[0] = (lp1[0] + lp2[0]) >> 1; new_p[1] = (lp1[1] + lp2[1]) >> 1; new_p[2] = (lp1[2] + lp2[2]) >> 1; new_p[3] = (lp1[3] + lp2[3]) >> 1; new_p[5] = (lp1[5] + lp2[5]) >> 1; // draw the point if splitting a leading edge if (lp2[1] > lp1[1]) goto nodraw; if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) goto nodraw; z = new_p[5]>>16; zbuf = zspantable[new_p[1]] + new_p[0]; if (z >= *zbuf) { const byte color_map_idx = skintable[new_p[3]>>16][new_p[2]>>16]; if (color_map_idx != 0) { unsigned int pix; *zbuf = z; pix = d_pcolormap[color_map_idx]; d_viewbuffer[d_scantable[new_p[1]] + new_p[0]] = pix; } } nodraw: // recursively continue D_PolysetRecursiveTriangleT3 (lp3, lp1, new_p); D_PolysetRecursiveTriangleT3 (lp3, new_p, lp2); } static void D_PolysetRecursiveTriangleT5 (int *lp1, int *lp2, int *lp3) { int *temp; int d; int new_p[6]; int z; short *zbuf; d = lp2[0] - lp1[0]; if (d < -1 || d > 1) goto split; d = lp2[1] - lp1[1]; if (d < -1 || d > 1) goto split; d = lp3[0] - lp2[0]; if (d < -1 || d > 1) goto split2; d = lp3[1] - lp2[1]; if (d < -1 || d > 1) goto split2; d = lp1[0] - lp3[0]; if (d < -1 || d > 1) goto split3; d = lp1[1] - lp3[1]; if (d < -1 || d > 1) { split3: temp = lp1; lp1 = lp3; lp3 = lp2; lp2 = temp; goto split; } return; // entire tri is filled split2: temp = lp1; lp1 = lp2; lp2 = lp3; lp3 = temp; split: // split this edge new_p[0] = (lp1[0] + lp2[0]) >> 1; new_p[1] = (lp1[1] + lp2[1]) >> 1; new_p[2] = (lp1[2] + lp2[2]) >> 1; new_p[3] = (lp1[3] + lp2[3]) >> 1; new_p[5] = (lp1[5] + lp2[5]) >> 1; // draw the point if splitting a leading edge if (lp2[1] > lp1[1]) goto nodraw; if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) goto nodraw; z = new_p[5]>>16; zbuf = zspantable[new_p[1]] + new_p[0]; if (z >= *zbuf) { const byte color_map_idx = skintable[new_p[3]>>16][new_p[2]>>16]; if (color_map_idx != 0) { unsigned int pix, pix2; *zbuf = z; pix2 = d_viewbuffer[d_scantable[new_p[1]] + new_p[0]]; pix = transTable[(color_map_idx<<8) + pix2]; d_viewbuffer[d_scantable[new_p[1]] + new_p[0]] = pix; } } nodraw: // recursively continue D_PolysetRecursiveTriangleT5 (lp3, lp1, new_p); D_PolysetRecursiveTriangleT5 (lp3, new_p, lp2); } #endif /* !id68k */ /* ================ D_DrawSubdiv ================ */ static void D_DrawSubdiv (void) { mtriangle_t *ptri; finalvert_t *pfv, *index0, *index1, *index2; int i, lnumtriangles; pfv = r_affinetridesc.pfinalverts; ptri = r_affinetridesc.ptriangles; lnumtriangles = r_affinetridesc.numtriangles; for (i = 0; i < lnumtriangles; i++) { index0 = pfv + ptri[i].vertindex[0]; index1 = pfv + ptri[i].vertindex[1]; index2 = pfv + ptri[i].vertindex[2]; if (((index0->v[1]-index1->v[1]) * (index0->v[0]-index2->v[0]) - (index0->v[0]-index1->v[0]) * (index0->v[1]-index2->v[1])) >= 0) { continue; } d_pcolormap = &((byte *)acolormap)[index0->v[4] & 0xFF00]; if (ptri[i].facesfront) { D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v); } else { int s0, s1, s2; s0 = index0->v[2]; s1 = index1->v[2]; s2 = index2->v[2]; if (index0->flags & ALIAS_ONSEAM) index0->v[2] += r_affinetridesc.seamfixupX16; if (index1->flags & ALIAS_ONSEAM) index1->v[2] += r_affinetridesc.seamfixupX16; if (index2->flags & ALIAS_ONSEAM) index2->v[2] += r_affinetridesc.seamfixupX16; D_PolysetRecursiveTriangle(index0->v, index1->v, index2->v); index0->v[2] = s0; index1->v[2] = s1; index2->v[2] = s2; } } } static void D_DrawSubdivT (void) { mtriangle_t *ptri; finalvert_t *pfv, *index0, *index1, *index2; int i, lnumtriangles; pfv = r_affinetridesc.pfinalverts; ptri = r_affinetridesc.ptriangles; lnumtriangles = r_affinetridesc.numtriangles; for (i = 0; i < lnumtriangles; i++) { index0 = pfv + ptri[i].vertindex[0]; index1 = pfv + ptri[i].vertindex[1]; index2 = pfv + ptri[i].vertindex[2]; if (((index0->v[1]-index1->v[1]) * (index0->v[0]-index2->v[0]) - (index0->v[0]-index1->v[0]) * (index0->v[1]-index2->v[1])) >= 0) { continue; } d_pcolormap = &((byte *)acolormap)[index0->v[4] & 0xFF00]; if (ptri[i].facesfront) { D_PolysetRecursiveTriangleT(index0->v, index1->v, index2->v); } else { int s0, s1, s2; s0 = index0->v[2]; s1 = index1->v[2]; s2 = index2->v[2]; if (index0->flags & ALIAS_ONSEAM) index0->v[2] += r_affinetridesc.seamfixupX16; if (index1->flags & ALIAS_ONSEAM) index1->v[2] += r_affinetridesc.seamfixupX16; if (index2->flags & ALIAS_ONSEAM) index2->v[2] += r_affinetridesc.seamfixupX16; D_PolysetRecursiveTriangleT(index0->v, index1->v, index2->v); index0->v[2] = s0; index1->v[2] = s1; index2->v[2] = s2; } } } static void D_DrawSubdivT2 (void) { mtriangle_t *ptri; finalvert_t *pfv, *index0, *index1, *index2; int i, lnumtriangles; pfv = r_affinetridesc.pfinalverts; ptri = r_affinetridesc.ptriangles; lnumtriangles = r_affinetridesc.numtriangles; for (i = 0; i < lnumtriangles; i++) { index0 = pfv + ptri[i].vertindex[0]; index1 = pfv + ptri[i].vertindex[1]; index2 = pfv + ptri[i].vertindex[2]; if (((index0->v[1]-index1->v[1]) * (index0->v[0]-index2->v[0]) - (index0->v[0]-index1->v[0]) * (index0->v[1]-index2->v[1])) >= 0) { continue; } d_pcolormap = &((byte *)acolormap)[index0->v[4] & 0xFF00]; if (ptri[i].facesfront) { D_PolysetRecursiveTriangleT2(index0->v, index1->v, index2->v); } else { int s0, s1, s2; s0 = index0->v[2]; s1 = index1->v[2]; s2 = index2->v[2]; if (index0->flags & ALIAS_ONSEAM) index0->v[2] += r_affinetridesc.seamfixupX16; if (index1->flags & ALIAS_ONSEAM) index1->v[2] += r_affinetridesc.seamfixupX16; if (index2->flags & ALIAS_ONSEAM) index2->v[2] += r_affinetridesc.seamfixupX16; D_PolysetRecursiveTriangleT2(index0->v, index1->v, index2->v); index0->v[2] = s0; index1->v[2] = s1; index2->v[2] = s2; } } } static void D_DrawSubdivT3 (void) { mtriangle_t *ptri; finalvert_t *pfv, *index0, *index1, *index2; int i, lnumtriangles; pfv = r_affinetridesc.pfinalverts; ptri = r_affinetridesc.ptriangles; lnumtriangles = r_affinetridesc.numtriangles; for (i = 0; i < lnumtriangles; i++) { index0 = pfv + ptri[i].vertindex[0]; index1 = pfv + ptri[i].vertindex[1]; index2 = pfv + ptri[i].vertindex[2]; if (((index0->v[1]-index1->v[1]) * (index0->v[0]-index2->v[0]) - (index0->v[0]-index1->v[0]) * (index0->v[1]-index2->v[1])) >= 0) { continue; } d_pcolormap = &((byte *)acolormap)[index0->v[4] & 0xFF00]; if (ptri[i].facesfront) { D_PolysetRecursiveTriangleT3(index0->v, index1->v, index2->v); } else { int s0, s1, s2; s0 = index0->v[2]; s1 = index1->v[2]; s2 = index2->v[2]; if (index0->flags & ALIAS_ONSEAM) index0->v[2] += r_affinetridesc.seamfixupX16; if (index1->flags & ALIAS_ONSEAM) index1->v[2] += r_affinetridesc.seamfixupX16; if (index2->flags & ALIAS_ONSEAM) index2->v[2] += r_affinetridesc.seamfixupX16; D_PolysetRecursiveTriangleT3(index0->v, index1->v, index2->v); index0->v[2] = s0; index1->v[2] = s1; index2->v[2] = s2; } } } static void D_DrawSubdivT5 (void) { mtriangle_t *ptri; finalvert_t *pfv, *index0, *index1, *index2; int i, lnumtriangles; pfv = r_affinetridesc.pfinalverts; ptri = r_affinetridesc.ptriangles; lnumtriangles = r_affinetridesc.numtriangles; for (i = 0; i < lnumtriangles; i++) { index0 = pfv + ptri[i].vertindex[0]; index1 = pfv + ptri[i].vertindex[1]; index2 = pfv + ptri[i].vertindex[2]; if (((index0->v[1]-index1->v[1]) * (index0->v[0]-index2->v[0]) - (index0->v[0]-index1->v[0]) * (index0->v[1]-index2->v[1])) >= 0) { continue; } if (ptri[i].facesfront) { D_PolysetRecursiveTriangleT5(index0->v, index1->v, index2->v); } else { int s0, s1, s2; s0 = index0->v[2]; s1 = index1->v[2]; s2 = index2->v[2]; if (index0->flags & ALIAS_ONSEAM) index0->v[2] += r_affinetridesc.seamfixupX16; if (index1->flags & ALIAS_ONSEAM) index1->v[2] += r_affinetridesc.seamfixupX16; if (index2->flags & ALIAS_ONSEAM) index2->v[2] += r_affinetridesc.seamfixupX16; D_PolysetRecursiveTriangleT5(index0->v, index1->v, index2->v); index0->v[2] = s0; index1->v[2] = s1; index2->v[2] = s2; } } } #if !id68k /* ================ D_DrawNonSubdiv ================ */ static void D_DrawNonSubdiv (void) { mtriangle_t *ptri; finalvert_t *pfv, *index0, *index1, *index2; int i, lnumtriangles; pfv = r_affinetridesc.pfinalverts; ptri = r_affinetridesc.ptriangles; lnumtriangles = r_affinetridesc.numtriangles; for (i = 0; i < lnumtriangles; i++, ptri++) { index0 = pfv + ptri->vertindex[0]; index1 = pfv + ptri->vertindex[1]; index2 = pfv + ptri->vertindex[2]; d_xdenom = (index0->v[1]-index1->v[1]) * (index0->v[0]-index2->v[0]) - (index0->v[0]-index1->v[0])*(index0->v[1]-index2->v[1]); if (d_xdenom >= 0) { continue; } r_p0[0] = index0->v[0]; // u r_p0[1] = index0->v[1]; // v r_p0[2] = index0->v[2]; // s r_p0[3] = index0->v[3]; // t r_p0[4] = index0->v[4]; // light r_p0[5] = index0->v[5]; // iz r_p1[0] = index1->v[0]; r_p1[1] = index1->v[1]; r_p1[2] = index1->v[2]; r_p1[3] = index1->v[3]; r_p1[4] = index1->v[4]; r_p1[5] = index1->v[5]; r_p2[0] = index2->v[0]; r_p2[1] = index2->v[1]; r_p2[2] = index2->v[2]; r_p2[3] = index2->v[3]; r_p2[4] = index2->v[4]; r_p2[5] = index2->v[5]; if (!ptri->facesfront) { if (index0->flags & ALIAS_ONSEAM) r_p0[2] += r_affinetridesc.seamfixupX16; if (index1->flags & ALIAS_ONSEAM) r_p1[2] += r_affinetridesc.seamfixupX16; if (index2->flags & ALIAS_ONSEAM) r_p2[2] += r_affinetridesc.seamfixupX16; } D_PolysetSetEdgeTable (); D_RasterizeAliasPolySmooth (); } } #endif /* !id68k */ /* ================ D_PolysetDraw ================ */ void D_PolysetDraw (void) { a_spans = (spanpackage_t *) (((intptr_t)&spans[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); if (r_affinetridesc.drawtype) { D_DrawSubdiv (); } else { #if id68k d_polysetdrawspans = D_PolysetDrawSpans8; #endif D_DrawNonSubdiv (); } } void D_PolysetDrawT (void) { a_spans = (spanpackage_t *) (((intptr_t)&spans[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); if (r_affinetridesc.drawtype) { D_DrawSubdivT (); } else { #if id68k d_polysetdrawspans = D_PolysetDrawSpans8T; #endif D_DrawNonSubdiv (); } } void D_PolysetDrawT2 (void) { a_spans = (spanpackage_t *) (((intptr_t)&spans[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); if (r_affinetridesc.drawtype) { D_DrawSubdivT2 (); } else { #if id68k d_polysetdrawspans = D_PolysetDrawSpans8T2; #endif D_DrawNonSubdiv (); } } void D_PolysetDrawT3 (void) { a_spans = (spanpackage_t *) (((intptr_t)&spans[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); if (r_affinetridesc.drawtype) { D_DrawSubdivT3 (); } else { #if id68k d_polysetdrawspans = D_PolysetDrawSpans8T3; #endif D_DrawNonSubdiv (); } } void D_PolysetDrawT5 (void) { a_spans = (spanpackage_t *) (((intptr_t)&spans[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); if (r_affinetridesc.drawtype) { D_DrawSubdivT5 (); } else { #if id68k d_polysetdrawspans = D_PolysetDrawSpans8T5; #endif D_DrawNonSubdiv (); } } #endif /* !id386 */ /* ================ D_PolysetUpdateTables ================ */ void D_PolysetUpdateTables (void) { int i; byte *s; if (r_affinetridesc.skinwidth != skinwidth || r_affinetridesc.pskin != skinstart) { skinwidth = r_affinetridesc.skinwidth; skinstart = (byte *) r_affinetridesc.pskin; s = skinstart; for (i = 0; i < MAX_SKIN_HEIGHT; i++, s += skinwidth) skintable[i] = s; } } #if !id386 #define D_PolysetScanLeftEdgeT D_PolysetScanLeftEdge #define D_PolysetScanLeftEdgeT2 D_PolysetScanLeftEdge #define D_PolysetScanLeftEdgeT3 D_PolysetScanLeftEdge #define D_PolysetScanLeftEdgeT5 D_PolysetScanLeftEdge #if !id68k /* =================== D_PolysetScanLeftEdge ==================== */ static void D_PolysetScanLeftEdge (int height) { do { d_pedgespanpackage->pdest = d_pdest; d_pedgespanpackage->pz = d_pz; d_pedgespanpackage->count = d_aspancount; d_pedgespanpackage->ptex = d_ptex; d_pedgespanpackage->sfrac = d_sfrac; d_pedgespanpackage->tfrac = d_tfrac; // FIXME: need to clamp l, s, t, at both ends? d_pedgespanpackage->light = d_light; d_pedgespanpackage->zi = d_zi; d_pedgespanpackage++; errorterm += erroradjustup; if (errorterm >= 0) { d_pdest += d_pdestextrastep; d_pz += d_pzextrastep; d_aspancount += d_countextrastep; d_ptex += d_ptexextrastep; d_sfrac += d_sfracextrastep; d_ptex += d_sfrac >> 16; d_sfrac &= 0xFFFF; d_tfrac += d_tfracextrastep; if (d_tfrac & 0x10000) { d_ptex += r_affinetridesc.skinwidth; d_tfrac &= 0xFFFF; } d_light += d_lightextrastep; d_zi += d_ziextrastep; errorterm -= erroradjustdown; } else { d_pdest += d_pdestbasestep; d_pz += d_pzbasestep; d_aspancount += ubasestep; d_ptex += d_ptexbasestep; d_sfrac += d_sfracbasestep; d_ptex += d_sfrac >> 16; d_sfrac &= 0xFFFF; d_tfrac += d_tfracbasestep; if (d_tfrac & 0x10000) { d_ptex += r_affinetridesc.skinwidth; d_tfrac &= 0xFFFF; } d_light += d_lightbasestep; d_zi += d_zibasestep; } } while (--height); } #endif /* !id68k */ #endif /* !id386 */ #if !id68k /* =================== D_PolysetSetUpForLineScan ==================== */ static void D_PolysetSetUpForLineScan(fixed8_t startvertu, fixed8_t startvertv, fixed8_t endvertu, fixed8_t endvertv) { double dm, dn; int tm, tn; adivtab_t *ptemp; // TODO: implement x86 version errorterm = -1; tm = endvertu - startvertu; tn = endvertv - startvertv; if (((tm <= 16) && (tm >= -15)) && ((tn <= 16) && (tn >= -15))) { ptemp = &adivtab[((tm+15) << 5) + (tn+15)]; ubasestep = ptemp->quotient; erroradjustup = ptemp->remainder; erroradjustdown = tn; } else { dm = (double)tm; dn = (double)tn; FloorDivMod (dm, dn, &ubasestep, &erroradjustup); erroradjustdown = dn; } } #endif /* !id68k */ #if !id386 #define D_PolysetCalcGradientsT D_PolysetCalcGradients #define D_PolysetCalcGradientsT2 D_PolysetCalcGradients #define D_PolysetCalcGradientsT3 D_PolysetCalcGradients #define D_PolysetCalcGradientsT5 D_PolysetCalcGradients #if !id68k /* ================ D_PolysetCalcGradients ================ */ static void D_PolysetCalcGradients (int skin_width) { float xstepdenominv, ystepdenominv, t0, t1; float p01_minus_p21, p11_minus_p21, p00_minus_p20, p10_minus_p20; p00_minus_p20 = r_p0[0] - r_p2[0]; p01_minus_p21 = r_p0[1] - r_p2[1]; p10_minus_p20 = r_p1[0] - r_p2[0]; p11_minus_p21 = r_p1[1] - r_p2[1]; xstepdenominv = 1.0 / (float)d_xdenom; ystepdenominv = -xstepdenominv; // ceil () for light so positive steps are exaggerated, negative steps // diminished, pushing us away from underflow toward overflow. Underflow is // very visible, overflow is very unlikely, because of ambient lighting t0 = r_p0[4] - r_p2[4]; t1 = r_p1[4] - r_p2[4]; r_lstepx = (int)ceil((t1 * p01_minus_p21 - t0 * p11_minus_p21) * xstepdenominv); r_lstepy = (int)ceil((t1 * p00_minus_p20 - t0 * p10_minus_p20) * ystepdenominv); t0 = r_p0[2] - r_p2[2]; t1 = r_p1[2] - r_p2[2]; r_sstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * xstepdenominv); r_sstepy = (int)((t1 * p00_minus_p20 - t0* p10_minus_p20) * ystepdenominv); t0 = r_p0[3] - r_p2[3]; t1 = r_p1[3] - r_p2[3]; r_tstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * xstepdenominv); r_tstepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * ystepdenominv); t0 = r_p0[5] - r_p2[5]; t1 = r_p1[5] - r_p2[5]; r_zistepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * xstepdenominv); r_zistepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * ystepdenominv); // a_sstepxfrac = r_sstepx << 16; // was #if id386 code // a_tstepxfrac = r_tstepx << 16; // was #if id386 code a_sstepxfrac = r_sstepx & 0xFFFF; a_tstepxfrac = r_tstepx & 0xFFFF; a_ststepxwhole = skin_width * (r_tstepx >> 16) + (r_sstepx >> 16); } #endif /* !id68k */ #endif /* !id386 */ #if 0 byte gelmap[256]; void InitGel (byte *palette) { int i; int r; for (i = 0; i < 256; i++) { // r = (palette[i*3]>>4); r = (palette[i*3] + palette[i*3+1] + palette[i*3+2])/(16*3); gelmap[i] = /* 64 */ 0 + r; } } #endif #if !id386 && !id68k /* ================ D_PolysetDrawSpans8 ================ */ static void D_PolysetDrawSpans8 (spanpackage_t *pspanpackage) { int lcount; byte *lpdest; byte *lptex; int lsfrac, ltfrac; int llight; int lzi; short *lpz; do { lcount = d_aspancount - pspanpackage->count; errorterm += erroradjustup; if (errorterm >= 0) { d_aspancount += d_countextrastep; errorterm -= erroradjustdown; } else { d_aspancount += ubasestep; } if (lcount) { lpdest = (byte *) pspanpackage->pdest; lptex = pspanpackage->ptex; lpz = pspanpackage->pz; lsfrac = pspanpackage->sfrac; ltfrac = pspanpackage->tfrac; llight = pspanpackage->light; lzi = pspanpackage->zi; do { if ((lzi >> 16) >= *lpz) { *lpdest = ((byte *)acolormap)[*lptex + (llight & 0xFF00)]; // gel mapping // *lpdest = gelmap[*lpdest]; *lpz = lzi >> 16; } lpdest++; lzi += r_zistepx; lpz++; llight += r_lstepx; lptex += a_ststepxwhole; lsfrac += a_sstepxfrac; lptex += lsfrac >> 16; lsfrac &= 0xFFFF; ltfrac += a_tstepxfrac; if (ltfrac & 0x10000) { lptex += r_affinetridesc.skinwidth; ltfrac &= 0xFFFF; } } while (--lcount); } pspanpackage++; } while (pspanpackage->count != -999999); } static void D_PolysetDrawSpans8T (spanpackage_t *pspanpackage) { int lcount; byte *lpdest; byte *lptex; int lsfrac, ltfrac; int llight; int lzi; short *lpz; byte btemp; do { lcount = d_aspancount - pspanpackage->count; errorterm += erroradjustup; if (errorterm >= 0) { d_aspancount += d_countextrastep; errorterm -= erroradjustdown; } else { d_aspancount += ubasestep; } if (lcount) { lpdest = (byte *) pspanpackage->pdest; lptex = pspanpackage->ptex; lpz = pspanpackage->pz; lsfrac = pspanpackage->sfrac; ltfrac = pspanpackage->tfrac; llight = pspanpackage->light; lzi = pspanpackage->zi; do { const byte color_map_idx = lptex[0]; if (color_map_idx != 0) { if ((lzi >> 16) >= *lpz) { btemp = ((byte *) acolormap)[color_map_idx + (llight & 0xFF00)]; *lpdest = mainTransTable[(btemp<<8) + (*lpdest)]; *lpz = lzi >> 16; } } lpdest++; lzi += r_zistepx; lpz++; llight += r_lstepx; lptex += a_ststepxwhole; lsfrac += a_sstepxfrac; lptex += lsfrac >> 16; lsfrac &= 0xFFFF; ltfrac += a_tstepxfrac; if (ltfrac & 0x10000) { lptex += r_affinetridesc.skinwidth; ltfrac &= 0xFFFF; } } while (--lcount); } pspanpackage++; } while (pspanpackage->count != -999999); } static void D_PolysetDrawSpans8T2 (spanpackage_t *pspanpackage) { int lcount; byte *lpdest; byte *lptex; int lsfrac, ltfrac; int llight; int lzi; short *lpz; byte btemp; do { lcount = d_aspancount - pspanpackage->count; errorterm += erroradjustup; if (errorterm >= 0) { d_aspancount += d_countextrastep; errorterm -= erroradjustdown; } else { d_aspancount += ubasestep; } if (lcount) { lpdest = (byte *) pspanpackage->pdest; lptex = pspanpackage->ptex; lpz = pspanpackage->pz; lsfrac = pspanpackage->sfrac; ltfrac = pspanpackage->tfrac; llight = pspanpackage->light; lzi = pspanpackage->zi; do { const byte color_map_idx = lptex[0]; if (color_map_idx != 0) { if ((lzi >> 16) >= *lpz) { btemp = ((byte *) acolormap)[color_map_idx + (llight & 0xFF00)]; *lpdest = (color_map_idx & 0x1) ? mainTransTable[(btemp<<8) + (*lpdest)] : btemp; *lpz = lzi >> 16; } } lpdest++; lzi += r_zistepx; lpz++; llight += r_lstepx; lptex += a_ststepxwhole; lsfrac += a_sstepxfrac; lptex += lsfrac >> 16; lsfrac &= 0xFFFF; ltfrac += a_tstepxfrac; if (ltfrac & 0x10000) { lptex += r_affinetridesc.skinwidth; ltfrac &= 0xFFFF; } } while (--lcount); } pspanpackage++; } while (pspanpackage->count != -999999); } static void D_PolysetDrawSpans8T3 (spanpackage_t *pspanpackage) { int lcount; byte *lpdest; byte *lptex; int lsfrac, ltfrac; int llight; int lzi; short *lpz; do { lcount = d_aspancount - pspanpackage->count; errorterm += erroradjustup; if (errorterm >= 0) { d_aspancount += d_countextrastep; errorterm -= erroradjustdown; } else { d_aspancount += ubasestep; } if (lcount) { lpdest = (byte *) pspanpackage->pdest; lptex = pspanpackage->ptex; lpz = pspanpackage->pz; lsfrac = pspanpackage->sfrac; ltfrac = pspanpackage->tfrac; llight = pspanpackage->light; lzi = pspanpackage->zi; do { const byte color_map_idx = lptex[0]; if (color_map_idx != 0) { if ((lzi >> 16) >= *lpz) { *lpdest = ((byte *) acolormap)[color_map_idx + (llight & 0xFF00)]; *lpz = lzi >> 16; } } lpdest++; lzi += r_zistepx; lpz++; llight += r_lstepx; lptex += a_ststepxwhole; lsfrac += a_sstepxfrac; lptex += lsfrac >> 16; lsfrac &= 0xFFFF; ltfrac += a_tstepxfrac; if (ltfrac & 0x10000) { lptex += r_affinetridesc.skinwidth; ltfrac &= 0xFFFF; } } while (--lcount); } pspanpackage++; } while (pspanpackage->count != -999999); } static void D_PolysetDrawSpans8T5 (spanpackage_t *pspanpackage) { int lcount; byte *lpdest; byte *lptex; int lsfrac, ltfrac; int lzi; short *lpz; do { lcount = d_aspancount - pspanpackage->count; errorterm += erroradjustup; if (errorterm >= 0) { d_aspancount += d_countextrastep; errorterm -= erroradjustdown; } else { d_aspancount += ubasestep; } if (lcount) { lpdest = (byte *) pspanpackage->pdest; lptex = pspanpackage->ptex; lpz = pspanpackage->pz; lsfrac = pspanpackage->sfrac; ltfrac = pspanpackage->tfrac; lzi = pspanpackage->zi; do { const byte color_map_idx = lptex[0]; if (color_map_idx != 0) { if ((lzi >> 16) >= *lpz) { *lpdest = transTable[(color_map_idx<<8) + (*lpdest)]; *lpz = lzi >> 16; } } lpdest++; lzi += r_zistepx; lpz++; lptex += a_ststepxwhole; lsfrac += a_sstepxfrac; lptex += lsfrac >> 16; lsfrac &= 0xFFFF; ltfrac += a_tstepxfrac; if (ltfrac & 0x10000) { lptex += r_affinetridesc.skinwidth; ltfrac &= 0xFFFF; } } while (--lcount); } pspanpackage++; } while (pspanpackage->count != -999999); } #endif /* !id386 && !id68k */ /* ================ D_PolysetFillSpans8 ================ */ void D_PolysetFillSpans8 (spanpackage_t *pspanpackage) { int color; // FIXME: do z buffering color = d_aflatcolor++; while (1) { int lcount; byte *lpdest; lcount = pspanpackage->count; if (lcount == -1) return; if (lcount) { lpdest = (byte *) pspanpackage->pdest; do { *lpdest++ = color; } while (--lcount); } pspanpackage++; } } #if !id68k /* ================ D_RasterizeAliasPolySmooth ================ */ void D_RasterizeAliasPolySmooth (void) { int initialleftheight, initialrightheight; int *plefttop, *prighttop, *pleftbottom, *prightbottom; int working_lstepx, originalcount; plefttop = pedgetable->pleftedgevert0; prighttop = pedgetable->prightedgevert0; pleftbottom = pedgetable->pleftedgevert1; prightbottom = pedgetable->prightedgevert1; initialleftheight = pleftbottom[1] - plefttop[1]; initialrightheight = prightbottom[1] - prighttop[1]; // // set the s, t, and light gradients, which are consistent across the triangle // because being a triangle, things are affine // if (currententity->model->flags & EF_SPECIAL_TRANS) D_PolysetCalcGradientsT5 (r_affinetridesc.skinwidth); else if (currententity->drawflags & DRF_TRANSLUCENT) D_PolysetCalcGradientsT (r_affinetridesc.skinwidth); else if (currententity->model->flags & EF_TRANSPARENT) D_PolysetCalcGradientsT2 (r_affinetridesc.skinwidth); else if (currententity->model->flags & EF_HOLEY) D_PolysetCalcGradientsT3 (r_affinetridesc.skinwidth); else D_PolysetCalcGradients (r_affinetridesc.skinwidth); // // rasterize the polygon // // // scan out the top (and possibly only) part of the left edge // d_pedgespanpackage = a_spans; ystart = plefttop[1]; d_aspancount = plefttop[0] - prighttop[0]; d_ptex = (byte *)r_affinetridesc.pskin + (plefttop[2] >> 16) + (plefttop[3] >> 16) * r_affinetridesc.skinwidth; #if id386 d_sfrac = (plefttop[2] & 0xFFFF) << 16; d_tfrac = (plefttop[3] & 0xFFFF) << 16; #else d_sfrac = plefttop[2] & 0xFFFF; d_tfrac = plefttop[3] & 0xFFFF; #endif d_light = plefttop[4]; d_zi = plefttop[5]; d_pdest = (byte *)d_viewbuffer + ystart * screenwidth + plefttop[0]; d_pz = d_pzbuffer + ystart * d_zwidth + plefttop[0]; if (initialleftheight == 1) { d_pedgespanpackage->pdest = d_pdest; d_pedgespanpackage->pz = d_pz; d_pedgespanpackage->count = d_aspancount; d_pedgespanpackage->ptex = d_ptex; d_pedgespanpackage->sfrac = d_sfrac; d_pedgespanpackage->tfrac = d_tfrac; // FIXME: need to clamp l, s, t, at both ends? d_pedgespanpackage->light = d_light; d_pedgespanpackage->zi = d_zi; d_pedgespanpackage++; } else { D_PolysetSetUpForLineScan(plefttop[0], plefttop[1], pleftbottom[0], pleftbottom[1]); #if id386 d_pzbasestep = (d_zwidth + ubasestep) << 1; d_pzextrastep = d_pzbasestep + 2; #else d_pzbasestep = d_zwidth + ubasestep; d_pzextrastep = d_pzbasestep + 1; #endif d_pdestbasestep = screenwidth + ubasestep; d_pdestextrastep = d_pdestbasestep + 1; // TODO: can reuse partial expressions here // for negative steps in x along left edge, bias toward overflow rather than // underflow (sort of turning the floor () we did in the gradient calcs into // ceil (), but plus a little bit) if (ubasestep < 0) working_lstepx = r_lstepx - 1; else working_lstepx = r_lstepx; d_countextrastep = ubasestep + 1; d_ptexbasestep = ((r_sstepy + r_sstepx * ubasestep) >> 16) + ((r_tstepy + r_tstepx * ubasestep) >> 16) * r_affinetridesc.skinwidth; #if id386 d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) << 16; d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) << 16; #else d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF; d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF; #endif d_lightbasestep = r_lstepy + working_lstepx * ubasestep; d_zibasestep = r_zistepy + r_zistepx * ubasestep; d_ptexextrastep = ((r_sstepy + r_sstepx * d_countextrastep) >> 16) + ((r_tstepy + r_tstepx * d_countextrastep) >> 16) * r_affinetridesc.skinwidth; #if id386 d_sfracextrastep = (r_sstepy + r_sstepx*d_countextrastep) << 16; d_tfracextrastep = (r_tstepy + r_tstepx*d_countextrastep) << 16; #else d_sfracextrastep = (r_sstepy + r_sstepx*d_countextrastep) & 0xFFFF; d_tfracextrastep = (r_tstepy + r_tstepx*d_countextrastep) & 0xFFFF; #endif d_lightextrastep = d_lightbasestep + working_lstepx; d_ziextrastep = d_zibasestep + r_zistepx; if (currententity->model->flags & EF_SPECIAL_TRANS) D_PolysetScanLeftEdgeT5 (initialleftheight); else if (currententity->drawflags & DRF_TRANSLUCENT) D_PolysetScanLeftEdgeT (initialleftheight); else if (currententity->model->flags & EF_TRANSPARENT) D_PolysetScanLeftEdgeT2 (initialleftheight); else if (currententity->model->flags & EF_HOLEY) D_PolysetScanLeftEdgeT3 (initialleftheight); else D_PolysetScanLeftEdge (initialleftheight); } // // scan out the bottom part of the left edge, if it exists // if (pedgetable->numleftedges == 2) { int height; plefttop = pleftbottom; pleftbottom = pedgetable->pleftedgevert2; height = pleftbottom[1] - plefttop[1]; // TODO: make this a function; modularize this function in general ystart = plefttop[1]; d_aspancount = plefttop[0] - prighttop[0]; d_ptex = (byte *)r_affinetridesc.pskin + (plefttop[2] >> 16) + (plefttop[3] >> 16) * r_affinetridesc.skinwidth; d_sfrac = 0; d_tfrac = 0; d_light = plefttop[4]; d_zi = plefttop[5]; d_pdest = (byte *)d_viewbuffer + ystart * screenwidth + plefttop[0]; d_pz = d_pzbuffer + ystart * d_zwidth + plefttop[0]; if (height == 1) { d_pedgespanpackage->pdest = d_pdest; d_pedgespanpackage->pz = d_pz; d_pedgespanpackage->count = d_aspancount; d_pedgespanpackage->ptex = d_ptex; d_pedgespanpackage->sfrac = d_sfrac; d_pedgespanpackage->tfrac = d_tfrac; // FIXME: need to clamp l, s, t, at both ends? d_pedgespanpackage->light = d_light; d_pedgespanpackage->zi = d_zi; d_pedgespanpackage++; } else { D_PolysetSetUpForLineScan(plefttop[0], plefttop[1], pleftbottom[0], pleftbottom[1]); d_pdestbasestep = screenwidth + ubasestep; d_pdestextrastep = d_pdestbasestep + 1; #if id386 d_pzbasestep = (d_zwidth + ubasestep) << 1; d_pzextrastep = d_pzbasestep + 2; #else d_pzbasestep = d_zwidth + ubasestep; d_pzextrastep = d_pzbasestep + 1; #endif if (ubasestep < 0) working_lstepx = r_lstepx - 1; else working_lstepx = r_lstepx; d_countextrastep = ubasestep + 1; d_ptexbasestep = ((r_sstepy + r_sstepx * ubasestep) >> 16) + ((r_tstepy + r_tstepx * ubasestep) >> 16) * r_affinetridesc.skinwidth; #if id386 d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) << 16; d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) << 16; #else d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF; d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF; #endif d_lightbasestep = r_lstepy + working_lstepx * ubasestep; d_zibasestep = r_zistepy + r_zistepx * ubasestep; d_ptexextrastep = ((r_sstepy + r_sstepx * d_countextrastep) >> 16) + ((r_tstepy + r_tstepx * d_countextrastep) >> 16) * r_affinetridesc.skinwidth; #if id386 d_sfracextrastep = ((r_sstepy+r_sstepx*d_countextrastep) & 0xFFFF)<<16; d_tfracextrastep = ((r_tstepy+r_tstepx*d_countextrastep) & 0xFFFF)<<16; #else d_sfracextrastep = (r_sstepy+r_sstepx*d_countextrastep) & 0xFFFF; d_tfracextrastep = (r_tstepy+r_tstepx*d_countextrastep) & 0xFFFF; #endif d_lightextrastep = d_lightbasestep + working_lstepx; d_ziextrastep = d_zibasestep + r_zistepx; if (currententity->model->flags & EF_SPECIAL_TRANS) D_PolysetScanLeftEdgeT5 (height); else if (currententity->drawflags & DRF_TRANSLUCENT) D_PolysetScanLeftEdgeT (height); else if (currententity->model->flags & EF_TRANSPARENT) D_PolysetScanLeftEdgeT2 (height); else if (currententity->model->flags & EF_HOLEY) D_PolysetScanLeftEdgeT3 (height); else D_PolysetScanLeftEdge (height); } } // scan out the top (and possibly only) part of the right edge, updating the // count field d_pedgespanpackage = a_spans; D_PolysetSetUpForLineScan(prighttop[0], prighttop[1], prightbottom[0], prightbottom[1]); d_aspancount = 0; d_countextrastep = ubasestep + 1; originalcount = a_spans[initialrightheight].count; a_spans[initialrightheight].count = -999999; // mark end of the spanpackages if (currententity->model->flags & EF_SPECIAL_TRANS) D_PolysetDrawSpans8T5 (a_spans); else if (currententity->drawflags & DRF_TRANSLUCENT) D_PolysetDrawSpans8T (a_spans); else if (currententity->model->flags & EF_TRANSPARENT) D_PolysetDrawSpans8T2 (a_spans); else if (currententity->model->flags & EF_HOLEY) D_PolysetDrawSpans8T3 (a_spans); else D_PolysetDrawSpans8 (a_spans); // scan out the bottom part of the right edge, if it exists if (pedgetable->numrightedges == 2) { int height; spanpackage_t *pstart; pstart = a_spans + initialrightheight; pstart->count = originalcount; d_aspancount = prightbottom[0] - prighttop[0]; prighttop = prightbottom; prightbottom = pedgetable->prightedgevert2; height = prightbottom[1] - prighttop[1]; D_PolysetSetUpForLineScan(prighttop[0], prighttop[1], prightbottom[0], prightbottom[1]); d_countextrastep = ubasestep + 1; a_spans[initialrightheight + height].count = -999999; // mark end of the spanpackages if (currententity->model->flags & EF_SPECIAL_TRANS) D_PolysetDrawSpans8T5 (pstart); else if (currententity->drawflags & DRF_TRANSLUCENT) D_PolysetDrawSpans8T (pstart); else if (currententity->model->flags & EF_TRANSPARENT) D_PolysetDrawSpans8T2 (pstart); else if (currententity->model->flags & EF_HOLEY) D_PolysetDrawSpans8T3 (pstart); else D_PolysetDrawSpans8 (pstart); } } #endif /* !id68k */ #if !id68k /* ================ D_PolysetSetEdgeTable ================ */ void D_PolysetSetEdgeTable (void) { int edgetableindex; edgetableindex = 0; // assume the vertices are already in // top to bottom order // // determine which edges are right & left, and the order in which // to rasterize them // if (r_p0[1] >= r_p1[1]) { if (r_p0[1] == r_p1[1]) { if (r_p0[1] < r_p2[1]) pedgetable = &edgetables[2]; else pedgetable = &edgetables[5]; return; } else { edgetableindex = 1; } } if (r_p0[1] == r_p2[1]) { if (edgetableindex) pedgetable = &edgetables[8]; else pedgetable = &edgetables[9]; return; } else if (r_p1[1] == r_p2[1]) { if (edgetableindex) pedgetable = &edgetables[10]; else pedgetable = &edgetables[11]; return; } if (r_p0[1] > r_p2[1]) edgetableindex += 2; if (r_p1[1] > r_p2[1]) edgetableindex += 4; pedgetable = &edgetables[edgetableindex]; } #endif /* !id68k */ #if 0 void D_PolysetRecursiveDrawLine (int *lp1, int *lp2) { int d; int new_p[6]; int ofs; d = lp2[0] - lp1[0]; if (d < -1 || d > 1) goto split; d = lp2[1] - lp1[1]; if (d < -1 || d > 1) goto split; return; // line is completed split: // split this edge new_p[0] = (lp1[0] + lp2[0]) >> 1; new_p[1] = (lp1[1] + lp2[1]) >> 1; new_p[5] = (lp1[5] + lp2[5]) >> 1; new_p[2] = (lp1[2] + lp2[2]) >> 1; new_p[3] = (lp1[3] + lp2[3]) >> 1; new_p[4] = (lp1[4] + lp2[4]) >> 1; // draw the point ofs = d_scantable[new_p[1]] + new_p[0]; if (new_p[5] > d_pzbuffer[ofs]) { unsigned int pix; d_pzbuffer[ofs] = new_p[5]; pix = skintable[new_p[3]>>16][new_p[2]>>16]; // pix = ((byte *)acolormap)[pix + (new_p[4] & 0xFF00)]; d_viewbuffer[ofs] = pix; } // recursively continue D_PolysetRecursiveDrawLine (lp1, new_p); D_PolysetRecursiveDrawLine (new_p, lp2); } void D_PolysetRecursiveTriangle2 (int *lp1, int *lp2, int *lp3) { int d; int new_p[6]; d = lp2[0] - lp1[0]; if (d < -1 || d > 1) goto split; d = lp2[1] - lp1[1]; if (d < -1 || d > 1) goto split; return; split: // split this edge new_p[0] = (lp1[0] + lp2[0]) >> 1; new_p[1] = (lp1[1] + lp2[1]) >> 1; new_p[5] = (lp1[5] + lp2[5]) >> 1; new_p[2] = (lp1[2] + lp2[2]) >> 1; new_p[3] = (lp1[3] + lp2[3]) >> 1; new_p[4] = (lp1[4] + lp2[4]) >> 1; D_PolysetRecursiveDrawLine (new, lp3); // recursively continue D_PolysetRecursiveTriangle (lp1, new, lp3); D_PolysetRecursiveTriangle (new, lp2, lp3); } #endif engine/h2shared/d_polyse68k.s000066400000000000000000003212571444734033100163730ustar00rootroot00000000000000** ** Quake for AMIGA ** d_polyset.c assembler implementations by Frank Wille ** Adapted for Hexen II by Szilard Biro ** ; INCLUDE "quakedef68k.i" R_SEAMFIXUP16 equ 32 R_NUMTRIANGLES equ 24 R_PFINALVERTS equ 20 R_PTRIANGLES equ 16 R_SKINWIDTH equ 8 R_PSKIN equ 0 FV_SIZEOF_EXP equ 5 PSPANP_SIZEOF_EXP equ 5 PSPANP_SIZEOF equ 32 PSPANP_COUNT equ 8 ETAB_PLEV0 equ 8 ETAB_PLEV1 equ 12 ETAB_PLEV2 equ 16 ETAB_NUMRIGHTEDGES equ 20 ETAB_PREV0 equ 24 ETAB_PREV1 equ 28 ETAB_PREV2 equ 32 ETAB_SIZEOF equ 36 ETAB_NUMLEFTEDGES equ 4 PTEMP_QUOTIENT equ 0 PTEMP_REMAINDER equ 4 MT_FACESFRONT equ 0 MT_VERTINDEX equ 4 MT_SIZEOF equ 16 REFDEF_VRECTRIGHT equ 40 REFDEF_VRECTBOTTOM equ 44 XREF _acolormap XREF _d_aspancount XREF _errorterm XREF _erroradjustup XREF _erroradjustdown XREF _d_countextrastep XREF _ubasestep XREF _r_affinetridesc XREF _a_ststepxwhole XREF _a_sstepxfrac XREF _a_tstepxfrac XREF _r_lstepx XREF _r_lstepy XREF _r_sstepx XREF _r_sstepy XREF _r_tstepx XREF _r_tstepy XREF _r_zistepx XREF _r_zistepy XREF _zspantable XREF _skintable XREF _r_p0 XREF _r_p1 XREF _r_p2 XREF _d_xdenom XREF _d_pcolormap XREF _d_scantable XREF _d_viewbuffer XREF _d_pedgespanpackage XREF _d_pdest XREF _d_pz XREF _d_aspancount XREF _d_ptex XREF _d_sfrac XREF _d_tfrac XREF _d_light XREF _d_zi XREF _d_zwidth XREF _d_pzbuffer XREF _a_spans XREF _pedgetable XREF _edgetables XREF _screenwidth XREF _FloorDivMod XREF _mainTransTable XREF _transTable ;XREF _D_RasterizeAliasPolySmooth XREF _adivtab XREF _d_polysetdrawspans XREF _r_refdef XDEF _D_PolysetDrawSpans8 XDEF _D_PolysetDrawSpans8T XDEF _D_PolysetDrawSpans8T2 XDEF _D_PolysetDrawSpans8T3 XDEF _D_PolysetDrawSpans8T5 XDEF _D_PolysetRecursiveTriangle XDEF _D_PolysetRecursiveTriangleT XDEF _D_PolysetRecursiveTriangleT2 XDEF _D_PolysetRecursiveTriangleT3 XDEF _D_PolysetRecursiveTriangleT5 XDEF _D_PolysetCalcGradients XDEF _D_DrawNonSubdiv XDEF _D_RasterizeAliasPolySmooth XDEF _D_PolysetDrawFinalVerts XDEF _D_PolysetDrawFinalVertsT XDEF _D_PolysetDrawFinalVertsT2 XDEF _D_PolysetDrawFinalVertsT3 XDEF _D_PolysetDrawFinalVertsT5 ALIAS_ONSEAM = $20 ;must match the def. in r_shared.h ****************************************************************************** * * void D_PolysetDrawSpans8 (spanpackage_t *pspanpackage) * * standard scan drawing function for alias models * ****************************************************************************** cnop 0,4 _D_PolysetDrawSpans8 ***** stackframe rsreset .intregs rs.l 11 rs.l 1 .pspan rs.l 1 ****** prologue movem.l d2-d7/a2-a6,-(sp) move.l .pspan(sp),a0 move.l _d_aspancount,d0 move.l _errorterm,d1 move.l _erroradjustup,d2 move.l _erroradjustdown,d3 move.l _d_countextrastep,a1 move.l _ubasestep,d5 move.l _acolormap,a3 move.l _r_zistepx,d4 move.l _a_ststepxwhole,a5 move.l _a_sstepxfrac,a4 move.l _a_tstepxfrac,d6 swap d6 move.l _r_lstepx,a6 move.l _r_affinetridesc+R_SKINWIDTH,d7 move d7,d6 .loop * lcount = d_aspancount - pspanpackage->count; * * errorterm += erroradjustup; * if (errorterm >= 0) * { * d_aspancount += d_countextrastep; * errorterm -= erroradjustdown; * } * else * { * d_aspancount += ubasestep; * } move.l d0,d7 sub.l PSPANP_COUNT(a0),d7 ;lcount = d_aspancount-pspa... add.l d2,d1 ;errorterm += erroradjustup blt.b .else ;if (errorterm >= 0) add.l a1,d0 ;d_aspancount += d_countextrastep sub.l d3,d1 ;errorterm -= error adjustdown bra.b .next .else add.l d5,d0 ;d_aspancount += ubasestep .next subq.l #1,d7 blt.b .loopend * lpdest = pspanpackage->pdest; * lptex = pspanpackage->ptex; * lpz = pspanpackage->pz; * lsfrac = pspanpackage->sfrac; * ltfrac = pspanpackage->tfrac; * llight = pspanpackage->light; * lzi = pspanpackage->zi; movem.l d0-d3/d5/a0/a1,-(sp) move.l (a0)+,d2 ;lpdest = pspanpackage->dest move.l (a0)+,a1 ;lpz = pspanpackage->pz addq.l #4,a0 move.l (a0)+,a2 ;lptex = pspanpackage->ptex move.l (a0)+,d3 ;lsfrac = pspanpackage->sfrac move.l (a0)+,d5 ;tsfrac = pspanpackage->tfrac swap d5 move.l (a0)+,d1 ;llight = pspanpackage->light move.l (a0)+,d0 ;lzi = pspanpackage->zi move.l d2,a0 moveq #0,d2 ****** main drawing loop ****** d0 = lzi ****** d1 = llight ****** d3 = lsfrac ****** d4 = r_zistepx ****** d5 = ltfrac ****** d6 = a_tstepxfrac [high] and r_affinetridesc.skinwidth [low] ****** d7 = lcount-1 ****** a0 -> lpdest ****** a1 -> lpz ****** a2 -> lptex ****** a3 -> acolormap ****** a4 = a_sstepxfrac ****** a5 = a_ststepxwhole ****** a6 = r_lstepx * do * { * if ((lzi >> 16) >= *lpz) * { * *lpdest = ((byte *)acolormap)[*lptex + (llight & 0xFF00)]; * *lpz = lzi >> 16; * } * lpdest++; * lzi += r_zistepx; * lpz++; * llight += r_lstepx; * lptex += a_ststepxwhole; * lsfrac += a_sstepxfrac; * lptex += lsfrac >> 16; * lsfrac &= 0xFFFF; * ltfrac += a_tstepxfrac; * if (ltfrac & 0x10000) * { * lptex += r_affinetridesc.skinwidth; * ltfrac &= 0xFFFF; * } * } while (--lcount); .loop2 swap d0 cmp (a1)+,d0 ;if ((lzi >> 16) >= *lpz) blt.b .cont move d1,d2 move.b (a2),d2 ;d3 = *lptex + (llight & $ff00) move.b 0(a3,d2.l),(a0) ;*lpdest = ((byte *)acolormap[d2] move d0,-2(a1) ;*lpz = lzi >> 16 .cont addq.l #1,a0 ;lpdest++ swap d0 add.l d4,d0 ;lzi += r_zistepx add.l a6,d1 ;llight += r_lstepx add.l a5,a2 ;lptex += a_ststepxwhole add.l a4,d3 ;lsfrac += a_sstepxfrac swap d3 add d3,a2 ;lptex += lsfrac >> 16 clr d3 swap d3 ;lsfrac &= $ffff add.l d6,d5 ;ltfrac += a_tstepxfrac bcc.b .cont2 ;if (ltfrac & $10000) add d6,a2 ;lptex += r_affine... .cont2 dbra d7,.loop2 ;while (--lcount) movem.l (sp)+,d0-d3/d5/a0/a1 .loopend * pspanpackage++; * } while (pspanpackage->count != -999999); lea PSPANP_SIZEOF(a0),a0 ;pspanpackage++ cmp.l #-999999,PSPANP_COUNT(a0) ; while (pspanpackage->count...) bne.b .loop movem.l (sp)+,d2-d7/a2-a6 rts ****************************************************************************** * * void D_PolysetDrawSpans8T (spanpackage_t *pspanpackage) * * translucent scan drawing function for alias models * ****************************************************************************** cnop 0,4 _D_PolysetDrawSpans8T ***** stackframe rsreset .intregs rs.l 11 rs.l 1 .pspan rs.l 1 ****** prologue movem.l d2-d7/a2-a6,-(sp) move.l .pspan(sp),a0 move.l _d_aspancount,d0 move.l _errorterm,d1 move.l _erroradjustup,d2 move.l _erroradjustdown,d3 move.l _d_countextrastep,a1 move.l _ubasestep,d5 move.l _acolormap,a3 move.l _r_zistepx,d4 move.l _a_ststepxwhole,a5 move.l _a_sstepxfrac,a4 move.l _a_tstepxfrac,d6 swap d6 move.l _r_lstepx,a6 move.l _r_affinetridesc+R_SKINWIDTH,d7 move d7,d6 .loop * lcount = d_aspancount - pspanpackage->count; * * errorterm += erroradjustup; * if (errorterm >= 0) * { * d_aspancount += d_countextrastep; * errorterm -= erroradjustdown; * } * else * { * d_aspancount += ubasestep; * } move.l d0,d7 sub.l PSPANP_COUNT(a0),d7 ;lcount = d_aspancount-pspa... add.l d2,d1 ;errorterm += erroradjustup blt.b .else ;if (errorterm >= 0) add.l a1,d0 ;d_aspancount += d_countextrastep sub.l d3,d1 ;errorterm -= error adjustdown bra.b .next .else add.l d5,d0 ;d_aspancount += ubasestep .next subq.l #1,d7 blt.b .loopend * lpdest = pspanpackage->pdest; * lptex = pspanpackage->ptex; * lpz = pspanpackage->pz; * lsfrac = pspanpackage->sfrac; * ltfrac = pspanpackage->tfrac; * llight = pspanpackage->light; * lzi = pspanpackage->zi; movem.l d0-d3/d5/a0/a1,-(sp) move.l (a0)+,d2 ;lpdest = pspanpackage->dest move.l (a0)+,a1 ;lpz = pspanpackage->pz addq.l #4,a0 move.l (a0)+,a2 ;lptex = pspanpackage->ptex move.l (a0)+,d3 ;lsfrac = pspanpackage->sfrac move.l (a0)+,d5 ;tsfrac = pspanpackage->tfrac swap d5 move.l (a0)+,d1 ;llight = pspanpackage->light move.l (a0)+,d0 ;lzi = pspanpackage->zi move.l d2,a0 moveq #0,d2 ****** main drawing loop ****** d0 = lzi ****** d1 = llight ****** d3 = lsfrac ****** d4 = r_zistepx ****** d5 = ltfrac ****** d6 = a_tstepxfrac [high] and r_affinetridesc.skinwidth [low] ****** d7 = lcount-1 ****** a0 -> lpdest ****** a1 -> lpz ****** a2 -> lptex ****** a3 -> acolormap ****** a4 = a_sstepxfrac ****** a5 = a_ststepxwhole ****** a6 = r_lstepx * do * { * if (*lptex != 0) * { * if ((lzi >> 16) >= *lpz) * { * btemp = ((byte *) acolormap)[*lptex + (llight & 0xFF00)]; * *lpdest = mainTransTable[(btemp<<8) + (*lpdest)]; * *lpz = lzi >> 16; * } * } * lpdest++; * lzi += r_zistepx; * lpz++; * llight += r_lstepx; * lptex += a_ststepxwhole; * lsfrac += a_sstepxfrac; * lptex += lsfrac >> 16; * lsfrac &= 0xFFFF; * ltfrac += a_tstepxfrac; * if (ltfrac & 0x10000) * { * lptex += r_affinetridesc.skinwidth; * ltfrac &= 0xFFFF; * } * } while (--lcount); .loop2 move.b (a2),d2 ;d2 = *lptex tst.b d2 ;if (*lptex != 0) beq.b .cont3 swap d0 cmp (a1),d0 ;if ((lzi >> 16) >= *lpz) ;cmp (a1)+,d0 ;if ((lzi >> 16) >= *lpz) blt.b .cont move d1,d2 move.b (a2),d2 ;d2 = *lptex + (llight & $ff00) ;move.b 0(a3,d2.l),(a0) ;*lpdest = ((byte *)acolormap[d2] move.b 0(a3,d2.l),d2 ;d2 = ((byte *)acolormap[d2] lsl.w #8,d2 move.b (a0),d2 ;d2 = (btemp<<8) + (*lpdest) move.b ([_mainTransTable],d2.l),(a0) ;*lpdest = mainTransTable[d2] ;move d0,-2(a1) ;*lpz = lzi >> 16 ;move d0,(a1) ;*lpz = lzi >> 16 .cont swap d0 .cont3 addq.l #1,a0 ;lpdest++ add.l d4,d0 ;lzi += r_zistepx addq.l #2,a1 ;lpz++; add.l a6,d1 ;llight += r_lstepx add.l a5,a2 ;lptex += a_ststepxwhole add.l a4,d3 ;lsfrac += a_sstepxfrac swap d3 add d3,a2 ;lptex += lsfrac >> 16 clr d3 swap d3 ;lsfrac &= $ffff add.l d6,d5 ;ltfrac += a_tstepxfrac bcc.b .cont2 ;if (ltfrac & $10000) add d6,a2 ;lptex += r_affine... .cont2 dbra d7,.loop2 ;while (--lcount) movem.l (sp)+,d0-d3/d5/a0/a1 .loopend * pspanpackage++; * } while (pspanpackage->count != -999999); lea PSPANP_SIZEOF(a0),a0 ;pspanpackage++ cmp.l #-999999,PSPANP_COUNT(a0) ; while (pspanpackage->count...) bne.b .loop movem.l (sp)+,d2-d7/a2-a6 rts ****************************************************************************** * * void D_PolysetDrawSpans8T2 (spanpackage_t *pspanpackage) * * transparent scan drawing function for alias models * ****************************************************************************** cnop 0,4 _D_PolysetDrawSpans8T2 ***** stackframe rsreset .intregs rs.l 11 rs.l 1 .pspan rs.l 1 ****** prologue movem.l d2-d7/a2-a6,-(sp) move.l .pspan(sp),a0 move.l _d_aspancount,d0 move.l _errorterm,d1 move.l _erroradjustup,d2 move.l _erroradjustdown,d3 move.l _d_countextrastep,a1 move.l _ubasestep,d5 move.l _acolormap,a3 move.l _r_zistepx,d4 move.l _a_ststepxwhole,a5 move.l _a_sstepxfrac,a4 move.l _a_tstepxfrac,d6 swap d6 move.l _r_lstepx,a6 move.l _r_affinetridesc+R_SKINWIDTH,d7 move d7,d6 .loop * lcount = d_aspancount - pspanpackage->count; * * errorterm += erroradjustup; * if (errorterm >= 0) * { * d_aspancount += d_countextrastep; * errorterm -= erroradjustdown; * } * else * { * d_aspancount += ubasestep; * } move.l d0,d7 sub.l PSPANP_COUNT(a0),d7 ;lcount = d_aspancount-pspa... add.l d2,d1 ;errorterm += erroradjustup blt.b .else ;if (errorterm >= 0) add.l a1,d0 ;d_aspancount += d_countextrastep sub.l d3,d1 ;errorterm -= error adjustdown bra.b .next .else add.l d5,d0 ;d_aspancount += ubasestep .next subq.l #1,d7 blt.b .loopend * lpdest = pspanpackage->pdest; * lptex = pspanpackage->ptex; * lpz = pspanpackage->pz; * lsfrac = pspanpackage->sfrac; * ltfrac = pspanpackage->tfrac; * llight = pspanpackage->light; * lzi = pspanpackage->zi; movem.l d0-d3/d5/a0/a1,-(sp) move.l (a0)+,d2 ;lpdest = pspanpackage->dest move.l (a0)+,a1 ;lpz = pspanpackage->pz addq.l #4,a0 move.l (a0)+,a2 ;lptex = pspanpackage->ptex move.l (a0)+,d3 ;lsfrac = pspanpackage->sfrac move.l (a0)+,d5 ;tsfrac = pspanpackage->tfrac swap d5 move.l (a0)+,d1 ;llight = pspanpackage->light move.l (a0)+,d0 ;lzi = pspanpackage->zi move.l d2,a0 moveq #0,d2 ****** main drawing loop ****** d0 = lzi ****** d1 = llight ****** d3 = lsfrac ****** d4 = r_zistepx ****** d5 = ltfrac ****** d6 = a_tstepxfrac [high] and r_affinetridesc.skinwidth [low] ****** d7 = lcount-1 ****** a0 -> lpdest ****** a1 -> lpz ****** a2 -> lptex ****** a3 -> acolormap ****** a4 = a_sstepxfrac ****** a5 = a_ststepxwhole ****** a6 = r_lstepx * do * { * if (*lptex != 0) * { * if ((lzi >> 16) >= *lpz) * { * if (*lptex % 2 == 0) * { * btemp = ((byte *) acolormap)[*lptex + (llight & 0xFF00)]; * *lpdest = (byte) btemp; * *lpz = lzi >> 16; * } * else * { * btemp = ((byte *) acolormap)[*lptex + (llight & 0xFF00)]; * *lpdest = mainTransTable[(btemp<<8) + (*lpdest)]; * *lpz = lzi >> 16; * } * } * } * lpdest++; * lzi += r_zistepx; * lpz++; * llight += r_lstepx; * lptex += a_ststepxwhole; * lsfrac += a_sstepxfrac; * lptex += lsfrac >> 16; * lsfrac &= 0xFFFF; * ltfrac += a_tstepxfrac; * if (ltfrac & 0x10000) * { * lptex += r_affinetridesc.skinwidth; * ltfrac &= 0xFFFF; * } * } while (--lcount); .loop2 move.b (a2),d2 ;d2 = *lptex tst.b d2 ;if (*lptex != 0) beq.b .cont3 swap d0 cmp (a1),d0 ;if ((lzi >> 16) >= *lpz) blt.b .cont move d1,d2 move.b (a2),d2 ;d2 = *lptex + (llight & $ff00) btst #0,d2 ;if (*lptex % 2 == 0) bne.b .transluc move.b 0(a3,d2.l),(a0) ;*lpdest = ((byte *)acolormap[d2] bra.b .writez .transluc move.b 0(a3,d2.l),d2 ;d2 = ((byte *)acolormap[d2] lsl.w #8,d2 move.b (a0),d2 ;d2 = (btemp<<8) + (*lpdest) move.b ([_mainTransTable],d2.l),(a0) ;*lpdest = mainTransTable[d2] .writez move d0,(a1) ;*lpz = lzi >> 16 .cont swap d0 .cont3 addq.l #1,a0 ;lpdest++ add.l d4,d0 ;lzi += r_zistepx addq.l #2,a1 ;lpz++; add.l a6,d1 ;llight += r_lstepx add.l a5,a2 ;lptex += a_ststepxwhole add.l a4,d3 ;lsfrac += a_sstepxfrac swap d3 add d3,a2 ;lptex += lsfrac >> 16 clr d3 swap d3 ;lsfrac &= $ffff add.l d6,d5 ;ltfrac += a_tstepxfrac bcc.b .cont2 ;if (ltfrac & $10000) add d6,a2 ;lptex += r_affine... .cont2 dbra d7,.loop2 ;while (--lcount) movem.l (sp)+,d0-d3/d5/a0/a1 .loopend * pspanpackage++; * } while (pspanpackage->count != -999999); lea PSPANP_SIZEOF(a0),a0 ;pspanpackage++ cmp.l #-999999,PSPANP_COUNT(a0) ; while (pspanpackage->count...) bne.b .loop movem.l (sp)+,d2-d7/a2-a6 rts ****************************************************************************** * * void D_PolysetDrawSpans8T3 (spanpackage_t *pspanpackage) * * holey scan drawing function for alias models * ****************************************************************************** cnop 0,4 _D_PolysetDrawSpans8T3 ***** stackframe rsreset .intregs rs.l 11 rs.l 1 .pspan rs.l 1 ****** prologue movem.l d2-d7/a2-a6,-(sp) move.l .pspan(sp),a0 move.l _d_aspancount,d0 move.l _errorterm,d1 move.l _erroradjustup,d2 move.l _erroradjustdown,d3 move.l _d_countextrastep,a1 move.l _ubasestep,d5 move.l _acolormap,a3 move.l _r_zistepx,d4 move.l _a_ststepxwhole,a5 move.l _a_sstepxfrac,a4 move.l _a_tstepxfrac,d6 swap d6 move.l _r_lstepx,a6 move.l _r_affinetridesc+R_SKINWIDTH,d7 move d7,d6 .loop * lcount = d_aspancount - pspanpackage->count; * * errorterm += erroradjustup; * if (errorterm >= 0) * { * d_aspancount += d_countextrastep; * errorterm -= erroradjustdown; * } * else * { * d_aspancount += ubasestep; * } move.l d0,d7 sub.l PSPANP_COUNT(a0),d7 ;lcount = d_aspancount-pspa... add.l d2,d1 ;errorterm += erroradjustup blt.b .else ;if (errorterm >= 0) add.l a1,d0 ;d_aspancount += d_countextrastep sub.l d3,d1 ;errorterm -= error adjustdown bra.b .next .else add.l d5,d0 ;d_aspancount += ubasestep .next subq.l #1,d7 blt.b .loopend * lpdest = pspanpackage->pdest; * lptex = pspanpackage->ptex; * lpz = pspanpackage->pz; * lsfrac = pspanpackage->sfrac; * ltfrac = pspanpackage->tfrac; * llight = pspanpackage->light; * lzi = pspanpackage->zi; movem.l d0-d3/d5/a0/a1,-(sp) move.l (a0)+,d2 ;lpdest = pspanpackage->dest move.l (a0)+,a1 ;lpz = pspanpackage->pz addq.l #4,a0 move.l (a0)+,a2 ;lptex = pspanpackage->ptex move.l (a0)+,d3 ;lsfrac = pspanpackage->sfrac move.l (a0)+,d5 ;tsfrac = pspanpackage->tfrac swap d5 move.l (a0)+,d1 ;llight = pspanpackage->light move.l (a0)+,d0 ;lzi = pspanpackage->zi move.l d2,a0 moveq #0,d2 ****** main drawing loop ****** d0 = lzi ****** d1 = llight ****** d3 = lsfrac ****** d4 = r_zistepx ****** d5 = ltfrac ****** d6 = a_tstepxfrac [high] and r_affinetridesc.skinwidth [low] ****** d7 = lcount-1 ****** a0 -> lpdest ****** a1 -> lpz ****** a2 -> lptex ****** a3 -> acolormap ****** a4 = a_sstepxfrac ****** a5 = a_ststepxwhole ****** a6 = r_lstepx * do * { * if (*lptex != 0) * { * if ((lzi >> 16) >= *lpz) * { * *lpdest = ((byte *)acolormap)[*lptex + (llight & 0xFF00)]; * *lpz = lzi >> 16; * } * } * lpdest++; * lzi += r_zistepx; * lpz++; * llight += r_lstepx; * lptex += a_ststepxwhole; * lsfrac += a_sstepxfrac; * lptex += lsfrac >> 16; * lsfrac &= 0xFFFF; * ltfrac += a_tstepxfrac; * if (ltfrac & 0x10000) * { * lptex += r_affinetridesc.skinwidth; * ltfrac &= 0xFFFF; * } * } while (--lcount); .loop2 move.b (a2),d2 ;d2 = *lptex tst.b d2 ;if (*lptex != 0) beq.b .cont3 swap d0 cmp (a1),d0 ;if ((lzi >> 16) >= *lpz) blt.b .cont move d1,d2 move.b (a2),d2 ;d2 = *lptex + (llight & $ff00) move.b 0(a3,d2.l),(a0) ;*lpdest = ((byte *)acolormap[d2] move d0,(a1) ;*lpz = lzi >> 16 .cont swap d0 .cont3 addq.l #1,a0 ;lpdest++ add.l d4,d0 ;lzi += r_zistepx addq.l #2,a1 ;lpz++; add.l a6,d1 ;llight += r_lstepx add.l a5,a2 ;lptex += a_ststepxwhole add.l a4,d3 ;lsfrac += a_sstepxfrac swap d3 add d3,a2 ;lptex += lsfrac >> 16 clr d3 swap d3 ;lsfrac &= $ffff add.l d6,d5 ;ltfrac += a_tstepxfrac bcc.b .cont2 ;if (ltfrac & $10000) add d6,a2 ;lptex += r_affine... .cont2 dbra d7,.loop2 ;while (--lcount) movem.l (sp)+,d0-d3/d5/a0/a1 .loopend * pspanpackage++; * } while (pspanpackage->count != -999999); lea PSPANP_SIZEOF(a0),a0 ;pspanpackage++ cmp.l #-999999,PSPANP_COUNT(a0) ; while (pspanpackage->count...) bne.b .loop movem.l (sp)+,d2-d7/a2-a6 rts ****************************************************************************** * * void D_PolysetDrawSpans8T5 (spanpackage_t *pspanpackage) * * special translucent scan drawing function for alias models * ****************************************************************************** cnop 0,4 _D_PolysetDrawSpans8T5 ***** stackframe rsreset .intregs rs.l 10 rs.l 1 .pspan rs.l 1 ****** prologue movem.l d2-d7/a2-a5,-(sp) move.l .pspan(sp),a0 move.l _d_aspancount,d0 move.l _errorterm,d1 move.l _erroradjustup,d2 move.l _erroradjustdown,d3 move.l _d_countextrastep,a1 move.l _ubasestep,d5 move.l _transTable,a3 move.l _r_zistepx,d4 move.l _a_ststepxwhole,a5 move.l _a_sstepxfrac,a4 move.l _a_tstepxfrac,d6 swap d6 move.l _r_affinetridesc+R_SKINWIDTH,d7 move d7,d6 .loop * lcount = d_aspancount - pspanpackage->count; * * errorterm += erroradjustup; * if (errorterm >= 0) * { * d_aspancount += d_countextrastep; * errorterm -= erroradjustdown; * } * else * { * d_aspancount += ubasestep; * } move.l d0,d7 sub.l PSPANP_COUNT(a0),d7 ;lcount = d_aspancount-pspa... add.l d2,d1 ;errorterm += erroradjustup blt.b .else ;if (errorterm >= 0) add.l a1,d0 ;d_aspancount += d_countextrastep sub.l d3,d1 ;errorterm -= error adjustdown bra.b .next .else add.l d5,d0 ;d_aspancount += ubasestep .next subq.l #1,d7 blt.b .loopend * lpdest = pspanpackage->pdest; * lptex = pspanpackage->ptex; * lpz = pspanpackage->pz; * lsfrac = pspanpackage->sfrac; * ltfrac = pspanpackage->tfrac; * llight = pspanpackage->light; * lzi = pspanpackage->zi; movem.l d0-d3/d5/a0/a1,-(sp) move.l (a0)+,d2 ;lpdest = pspanpackage->dest move.l (a0)+,a1 ;lpz = pspanpackage->pz addq.l #4,a0 move.l (a0)+,a2 ;lptex = pspanpackage->ptex move.l (a0)+,d3 ;lsfrac = pspanpackage->sfrac move.l (a0)+,d5 ;tsfrac = pspanpackage->tfrac swap d5 ;move.l (a0)+,d1 ;llight = pspanpackage->light addq.l #4,a0 move.l (a0)+,d0 ;lzi = pspanpackage->zi move.l d2,a0 moveq #0,d2 ****** main drawing loop ****** d0 = lzi ****** d1 = scratch ****** d3 = lsfrac ****** d4 = r_zistepx ****** d5 = ltfrac ****** d6 = a_tstepxfrac [high] and r_affinetridesc.skinwidth [low] ****** d7 = lcount-1 ****** a0 -> lpdest ****** a1 -> lpz ****** a2 -> lptex ****** a3 -> transTable ****** a4 = a_sstepxfrac ****** a5 = a_ststepxwhole * do * { * if (*lptex != 0) * { * if ((lzi >> 16) >= *lpz) * { * *lpdest = transTable[(*lptex << 8) + (*lpdest)]; * *lpz = lzi >> 16; * } * } * lpdest++; * lzi += r_zistepx; * lpz++; * llight += r_lstepx; * lptex += a_ststepxwhole; * lsfrac += a_sstepxfrac; * lptex += lsfrac >> 16; * lsfrac &= 0xFFFF; * ltfrac += a_tstepxfrac; * if (ltfrac & 0x10000) * { * lptex += r_affinetridesc.skinwidth; * ltfrac &= 0xFFFF; * } * } while (--lcount); .loop2 move.b (a2),d2 ;d2 = *lptex tst.b d2 ;if (*lptex != 0) beq.b .cont3 swap d0 cmp (a1),d0 ;if ((lzi >> 16) >= *lpz) blt.b .cont lsl.w #8,d2 move.b (a0),d2 ;d2 = (btemp<<8) + (*lpdest) move.b 0(a3,d2.l),(a0) ;*lpdest = transTable[d2] ;move d0,(a1) ;*lpz = lzi >> 16 .cont swap d0 .cont3 addq.l #1,a0 ;lpdest++ add.l d4,d0 ;lzi += r_zistepx addq.l #2,a1 ;lpz++; add.l a5,a2 ;lptex += a_ststepxwhole add.l a4,d3 ;lsfrac += a_sstepxfrac swap d3 add d3,a2 ;lptex += lsfrac >> 16 clr d3 swap d3 ;lsfrac &= $ffff add.l d6,d5 ;ltfrac += a_tstepxfrac bcc.b .cont2 ;if (ltfrac & $10000) add d6,a2 ;lptex += r_affine... .cont2 dbra d7,.loop2 ;while (--lcount) movem.l (sp)+,d0-d3/d5/a0/a1 .loopend * pspanpackage++; * } while (pspanpackage->count != -999999); lea PSPANP_SIZEOF(a0),a0 ;pspanpackage++ cmp.l #-999999,PSPANP_COUNT(a0) ; while (pspanpackage->count...) bne.b .loop movem.l (sp)+,d2-d7/a2-a5 rts ****************************************************************************** * * void D_PolysetRecursiveTriangle (int *lp1, int *lp2, int *lp3) * ****************************************************************************** cnop 0,4 _D_PolysetRecursiveTriangle ***** stackframe rsreset .new rs.l 6 .intregs rs.l 11 rs.l 1 .lp1 rs.l 1 .lp2 rs.l 1 .lp3 rs.l 1 movem.l d2-d7/a2-a6,-(sp) sub.l #.intregs,sp move.l .lp1(sp),a0 move.l .lp2(sp),a1 move.l .lp3(sp),a2 lea _zspantable,a5 move.l _d_viewbuffer,a6 bsr DoRecursion add.l #.intregs,sp movem.l (sp)+,d2-d7/a2-a6 rts ****** a0 -> lp1 ****** a1 -> lp2 ****** a2 -> lp3 DoRecursion lea -6*4(sp),sp move.l sp,a4 * d = lp2[0] - lp1[0]; * if (d < -1 || d > 1) * goto split; * d = lp2[1] - lp1[1]; * if (d < -1 || d > 1) * goto split; * * d = lp3[0] - lp2[0]; * if (d < -1 || d > 1) * goto split2; * d = lp3[1] - lp2[1]; * if (d < -1 || d > 1) * goto split2; * * d = lp1[0] - lp3[0]; * if (d < -1 || d > 1) * goto split3; * d = lp1[1] - lp3[1]; * if (d < -1 || d > 1) move.l (a1),d0 move.l (a0),d1 move.l 1*4(a1),d2 move.l 1*4(a0),d3 move.l d0,d4 sub.l d1,d4 ;d = lp2[0] - lp1[0] addq.l #1,d4 cmp.l #2,d4 ;if (d < -1 || d > 1) bhi.b .split ;goto split move.l d2,d5 sub.l d3,d5 ;d = lp2[1] - lp1[1] addq.l #1,d5 cmp.l #2,d5 ;if (d < -1 || d > 1) bhi.b .split ;goto split move.l (a2),d4 move.l d4,d5 sub.l d0,d4 ;d = lp3[0] - lp2[0] addq.l #1,d4 cmp.l #2,d4 ;if (d < -1 || d > 1) bhi.b .split2 ;goto split2 move.l 1*4(a2),d4 move.l d4,d6 sub.l d2,d4 ;d = lp3[1] - lp2[1] addq.l #1,d4 cmp.l #2,d4 ;if (d < -1 || d > 1) bhi.b .split2 ;goto split2 sub.l d1,d5 ;d = lp1[0] - lp3[0] addq.l #1,d5 cmp.l #2,d5 ;if (d < -1 || d > 1) bhi.b .split3 ;goto split3 sub.l d3,d6 ;d = lp1[1] - lp3[1] addq.l #1,d6 cmp.l #2,d6 ;if (d < -1 || d > 1) bls.b .exit *split3: * temp = lp1; * lp1 = lp3; * lp3 = lp2; * lp2 = temp; * * goto split; * *split2: * temp = lp1; * lp1 = lp2; * lp2 = lp3; * lp3 = temp; .split3 exg a1,a2 ;rotate forward exg a1,a0 move.l (a1),d0 move.l (a0),d1 move.l 1*4(a1),d2 move.l 1*4(a0),d3 bra.b .split .split2 exg a1,a0 ;rotate backward exg a1,a2 move.l (a1),d0 move.l (a0),d1 move.l 1*4(a1),d2 move.l 1*4(a0),d3 .split * new[0] = (lp1[0] + lp2[0]) >> 1; * new[1] = (lp1[1] + lp2[1]) >> 1; * new[2] = (lp1[2] + lp2[2]) >> 1; * new[3] = (lp1[3] + lp2[3]) >> 1; * new[5] = (lp1[5] + lp2[5]) >> 1; move.l d0,d4 move.l d0,a3 add.l d1,d4 asr.l #1,d4 ;d4 = new[0] move.l d2,d5 add.l d3,d5 asr.l #1,d5 ;d5 = new[1] move.l 2*4(a0),d6 add.l 2*4(a1),d6 asr.l #1,d6 ;d6 = new[2] move.l 3*4(a0),d7 add.l 3*4(a1),d7 asr.l #1,d7 ;d7 = new[3] move.l 5*4(a0),d0 add.l 5*4(a1),d0 asr.l #1,d0 ;d0 = new[5] * if (lp2[1] > lp1[1]) * goto nodraw; * if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) * goto nodraw; move.l d4,(a4)+ ;store new[] move.l d5,(a4)+ move.l d6,(a4)+ move.l d7,(a4)+ move.l d0,4(a4) lea -16(a4),a4 swap d0 ;z = new[5]>>16 cmp.l d3,d2 ;if (lp2[1] > lp1[1]) bgt.b .nodraw ;goto nodraw cmp.l d2,d3 ;if (lp2[1] == lp1[1) bne.b .draw cmp.l d1,a3 ;&& (lp2[0] < lp1[0]) blt.b .nodraw ;goto nodraw .draw * z = new[5]>>16; * zbuf = zspantable[new[1]] + new[0]; * if (z >= *zbuf) * { * int pix; * * *zbuf = z; * pix = d_pcolormap[skintable[new[3]>>16][new[2]>>16]]; * d_viewbuffer[d_scantable[new[1]] + new[0]] = pix; * } move.l 0(a5,d5.l*4),a3 cmp 0(a3,d4.l*2),d0 ;if (z >= *zbuf) blt.b .nodraw move d0,0(a3,d4.l*2) ;*zbuf = z swap d6 swap d7 lea _skintable,a3 move.l 0(a3,d7.w*4),a3 ;skintable[new[3]] move.b 0(a3,d6.w),d2 ;d2 = skintable[new[3]][new[2]>>16] and #$ff,d2 move.l _d_pcolormap,a3 move.b 0(a3,d2.w),d2 ;pix = d_pcolormap[d2] lea _d_scantable,a3 move.l 0(a3,d5.l*4),d1 ;d_scantable[new[1]] add.l d4,d1 ;+new[0] move.b d2,0(a6,d1.l) ;d_viewbuffer[d1] = pix .nodraw *// recursively continue * D_PolysetRecursiveTriangle (lp3, lp1, new); * D_PolysetRecursiveTriangle (lp3, new, lp2); movem.l a1/a2/a4,-(sp) move.l a0,a1 move.l a2,a0 move.l a4,a2 bsr DoRecursion ;DRT (lp3, lp1, new) movem.l (sp)+,a1/a2/a4 move.l a2,a0 move.l a1,a2 move.l a4,a1 bsr DoRecursion ;DRT (lp3, new, lp2) .exit lea 6*4(sp),sp rts ****************************************************************************** * * void D_PolysetRecursiveTriangleT (int *lp1, int *lp2, int *lp3) * ****************************************************************************** cnop 0,4 _D_PolysetRecursiveTriangleT ***** stackframe rsreset .new rs.l 6 .intregs rs.l 11 rs.l 1 .lp1 rs.l 1 .lp2 rs.l 1 .lp3 rs.l 1 movem.l d2-d7/a2-a6,-(sp) sub.l #.intregs,sp move.l .lp1(sp),a0 move.l .lp2(sp),a1 move.l .lp3(sp),a2 lea _zspantable,a5 move.l _d_viewbuffer,a6 bsr DoRecursionT add.l #.intregs,sp movem.l (sp)+,d2-d7/a2-a6 rts ****** a0 -> lp1 ****** a1 -> lp2 ****** a2 -> lp3 DoRecursionT lea -6*4(sp),sp move.l sp,a4 * d = lp2[0] - lp1[0]; * if (d < -1 || d > 1) * goto split; * d = lp2[1] - lp1[1]; * if (d < -1 || d > 1) * goto split; * * d = lp3[0] - lp2[0]; * if (d < -1 || d > 1) * goto split2; * d = lp3[1] - lp2[1]; * if (d < -1 || d > 1) * goto split2; * * d = lp1[0] - lp3[0]; * if (d < -1 || d > 1) * goto split3; * d = lp1[1] - lp3[1]; * if (d < -1 || d > 1) move.l (a1),d0 move.l (a0),d1 move.l 1*4(a1),d2 move.l 1*4(a0),d3 move.l d0,d4 sub.l d1,d4 ;d = lp2[0] - lp1[0] addq.l #1,d4 cmp.l #2,d4 ;if (d < -1 || d > 1) bhi.b .split ;goto split move.l d2,d5 sub.l d3,d5 ;d = lp2[1] - lp1[1] addq.l #1,d5 cmp.l #2,d5 ;if (d < -1 || d > 1) bhi.b .split ;goto split move.l (a2),d4 move.l d4,d5 sub.l d0,d4 ;d = lp3[0] - lp2[0] addq.l #1,d4 cmp.l #2,d4 ;if (d < -1 || d > 1) bhi.b .split2 ;goto split2 move.l 1*4(a2),d4 move.l d4,d6 sub.l d2,d4 ;d = lp3[1] - lp2[1] addq.l #1,d4 cmp.l #2,d4 ;if (d < -1 || d > 1) bhi.b .split2 ;goto split2 sub.l d1,d5 ;d = lp1[0] - lp3[0] addq.l #1,d5 cmp.l #2,d5 ;if (d < -1 || d > 1) bhi.b .split3 ;goto split3 sub.l d3,d6 ;d = lp1[1] - lp3[1] addq.l #1,d6 cmp.l #2,d6 ;if (d < -1 || d > 1) bls.b .exit *split3: * temp = lp1; * lp1 = lp3; * lp3 = lp2; * lp2 = temp; * * goto split; * *split2: * temp = lp1; * lp1 = lp2; * lp2 = lp3; * lp3 = temp; .split3 exg a1,a2 ;rotate forward exg a1,a0 move.l (a1),d0 move.l (a0),d1 move.l 1*4(a1),d2 move.l 1*4(a0),d3 bra.b .split .split2 exg a1,a0 ;rotate backward exg a1,a2 move.l (a1),d0 move.l (a0),d1 move.l 1*4(a1),d2 move.l 1*4(a0),d3 .split * new[0] = (lp1[0] + lp2[0]) >> 1; * new[1] = (lp1[1] + lp2[1]) >> 1; * new[2] = (lp1[2] + lp2[2]) >> 1; * new[3] = (lp1[3] + lp2[3]) >> 1; * new[5] = (lp1[5] + lp2[5]) >> 1; move.l d0,d4 move.l d0,a3 add.l d1,d4 asr.l #1,d4 ;d4 = new[0] move.l d2,d5 add.l d3,d5 asr.l #1,d5 ;d5 = new[1] move.l 2*4(a0),d6 add.l 2*4(a1),d6 asr.l #1,d6 ;d6 = new[2] move.l 3*4(a0),d7 add.l 3*4(a1),d7 asr.l #1,d7 ;d7 = new[3] move.l 5*4(a0),d0 add.l 5*4(a1),d0 asr.l #1,d0 ;d0 = new[5] * if (lp2[1] > lp1[1]) * goto nodraw; * if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) * goto nodraw; move.l d4,(a4)+ ;store new[] move.l d5,(a4)+ move.l d6,(a4)+ move.l d7,(a4)+ move.l d0,4(a4) lea -16(a4),a4 swap d0 ;z = new[5]>>16 cmp.l d3,d2 ;if (lp2[1] > lp1[1]) bgt.b .nodraw ;goto nodraw cmp.l d2,d3 ;if (lp2[1] == lp1[1) bne.b .draw cmp.l d1,a3 ;&& (lp2[0] < lp1[0]) blt.b .nodraw ;goto nodraw .draw * z = new[5]>>16; * zbuf = zspantable[new[1]] + new[0]; * if (z >= *zbuf) * { * color_map_idx = skintable[new_p[3]>>16][new_p[2]>>16]; * * if (color_map_idx != 0) * { * unsigned int pix, pix2; * * *zbuf = z; * pix = d_pcolormap[color_map_idx]; * pix2 = d_viewbuffer[d_scantable[new_p[1]] + new_p[0]]; * pix = mainTransTable[(pix<<8) + pix2]; * d_viewbuffer[d_scantable[new_p[1]] + new_p[0]] = pix; * } * } move.l 0(a5,d5.l*4),a3 cmp 0(a3,d4.l*2),d0 ;if (z >= *zbuf) blt.b .nodraw ;move d0,0(a3,d4.l*2) ;*zbuf = z swap d6 swap d7 lea _skintable,a3 move.l 0(a3,d7.w*4),a3 ;skintable[new[3]] move.b 0(a3,d6.w),d2 ;d2 = skintable[new[3]][new[2]>>16] tst.b d2 ;if (color_map_idx != 0) beq.b .nodraw and #$ff,d2 move.l _d_pcolormap,a3 move.b 0(a3,d2.w),d2 ;pix = d_pcolormap[d2] lsl.w #8,d2 move.b 0(a6,d1.l),d2 ;d2 = (pix<<8) + pix2 lea _d_scantable,a3 move.l 0(a3,d5.l*4),d1 ;d_scantable[new[1]] add.l d4,d1 ;+new[0] ;move.b d2,0(a6,d1.l) ;d_viewbuffer[d1] = pix move.l _mainTransTable,a3 move.b 0(a3,d2.l),0(a6,d1.l) ;d_viewbuffer[d1] = mainTransTable[d2] .nodraw *// recursively continue * D_PolysetRecursiveTriangleT (lp3, lp1, new); * D_PolysetRecursiveTriangleT (lp3, new, lp2); movem.l a1/a2/a4,-(sp) move.l a0,a1 move.l a2,a0 move.l a4,a2 bsr DoRecursionT ;DRT (lp3, lp1, new) movem.l (sp)+,a1/a2/a4 move.l a2,a0 move.l a1,a2 move.l a4,a1 bsr DoRecursionT ;DRT (lp3, new, lp2) .exit lea 6*4(sp),sp rts ****************************************************************************** * * void D_PolysetRecursiveTriangleT2 (int *lp1, int *lp2, int *lp3) * ****************************************************************************** cnop 0,4 _D_PolysetRecursiveTriangleT2 ***** stackframe rsreset .new rs.l 6 .intregs rs.l 11 rs.l 1 .lp1 rs.l 1 .lp2 rs.l 1 .lp3 rs.l 1 movem.l d2-d7/a2-a6,-(sp) sub.l #.intregs,sp move.l .lp1(sp),a0 move.l .lp2(sp),a1 move.l .lp3(sp),a2 lea _zspantable,a5 move.l _d_viewbuffer,a6 bsr DoRecursionT2 add.l #.intregs,sp movem.l (sp)+,d2-d7/a2-a6 rts ****** a0 -> lp1 ****** a1 -> lp2 ****** a2 -> lp3 DoRecursionT2 lea -6*4(sp),sp move.l sp,a4 * d = lp2[0] - lp1[0]; * if (d < -1 || d > 1) * goto split; * d = lp2[1] - lp1[1]; * if (d < -1 || d > 1) * goto split; * * d = lp3[0] - lp2[0]; * if (d < -1 || d > 1) * goto split2; * d = lp3[1] - lp2[1]; * if (d < -1 || d > 1) * goto split2; * * d = lp1[0] - lp3[0]; * if (d < -1 || d > 1) * goto split3; * d = lp1[1] - lp3[1]; * if (d < -1 || d > 1) move.l (a1),d0 move.l (a0),d1 move.l 1*4(a1),d2 move.l 1*4(a0),d3 move.l d0,d4 sub.l d1,d4 ;d = lp2[0] - lp1[0] addq.l #1,d4 cmp.l #2,d4 ;if (d < -1 || d > 1) bhi.b .split ;goto split move.l d2,d5 sub.l d3,d5 ;d = lp2[1] - lp1[1] addq.l #1,d5 cmp.l #2,d5 ;if (d < -1 || d > 1) bhi.b .split ;goto split move.l (a2),d4 move.l d4,d5 sub.l d0,d4 ;d = lp3[0] - lp2[0] addq.l #1,d4 cmp.l #2,d4 ;if (d < -1 || d > 1) bhi.b .split2 ;goto split2 move.l 1*4(a2),d4 move.l d4,d6 sub.l d2,d4 ;d = lp3[1] - lp2[1] addq.l #1,d4 cmp.l #2,d4 ;if (d < -1 || d > 1) bhi.b .split2 ;goto split2 sub.l d1,d5 ;d = lp1[0] - lp3[0] addq.l #1,d5 cmp.l #2,d5 ;if (d < -1 || d > 1) bhi.b .split3 ;goto split3 sub.l d3,d6 ;d = lp1[1] - lp3[1] addq.l #1,d6 cmp.l #2,d6 ;if (d < -1 || d > 1) bls.b .exit *split3: * temp = lp1; * lp1 = lp3; * lp3 = lp2; * lp2 = temp; * * goto split; * *split2: * temp = lp1; * lp1 = lp2; * lp2 = lp3; * lp3 = temp; .split3 exg a1,a2 ;rotate forward exg a1,a0 move.l (a1),d0 move.l (a0),d1 move.l 1*4(a1),d2 move.l 1*4(a0),d3 bra.b .split .split2 exg a1,a0 ;rotate backward exg a1,a2 move.l (a1),d0 move.l (a0),d1 move.l 1*4(a1),d2 move.l 1*4(a0),d3 .split * new[0] = (lp1[0] + lp2[0]) >> 1; * new[1] = (lp1[1] + lp2[1]) >> 1; * new[2] = (lp1[2] + lp2[2]) >> 1; * new[3] = (lp1[3] + lp2[3]) >> 1; * new[5] = (lp1[5] + lp2[5]) >> 1; move.l d0,d4 move.l d0,a3 add.l d1,d4 asr.l #1,d4 ;d4 = new[0] move.l d2,d5 add.l d3,d5 asr.l #1,d5 ;d5 = new[1] move.l 2*4(a0),d6 add.l 2*4(a1),d6 asr.l #1,d6 ;d6 = new[2] move.l 3*4(a0),d7 add.l 3*4(a1),d7 asr.l #1,d7 ;d7 = new[3] move.l 5*4(a0),d0 add.l 5*4(a1),d0 asr.l #1,d0 ;d0 = new[5] * if (lp2[1] > lp1[1]) * goto nodraw; * if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) * goto nodraw; move.l d4,(a4)+ ;store new[] move.l d5,(a4)+ move.l d6,(a4)+ move.l d7,(a4)+ move.l d0,4(a4) lea -16(a4),a4 swap d0 ;z = new[5]>>16 cmp.l d3,d2 ;if (lp2[1] > lp1[1]) bgt.b .nodraw ;goto nodraw cmp.l d2,d3 ;if (lp2[1] == lp1[1) bne.b .draw cmp.l d1,a3 ;&& (lp2[0] < lp1[0]) blt.b .nodraw ;goto nodraw .draw * z = new[5]>>16; * zbuf = zspantable[new[1]] + new[0]; * if (z >= *zbuf) * { * color_map_idx = skintable[new_p[3]>>16][new_p[2]>>16]; * * if (color_map_idx != 0) * { * unsigned int pix, pix2; * * *zbuf = z; * pix = d_pcolormap[color_map_idx]; * * if (color_map_idx % 2 == 0) * { * d_viewbuffer[d_scantable[new_p[1]] + new_p[0]] = pix; * } * else * { * pix2 = d_viewbuffer[d_scantable[new_p[1]] + new_p[0]]; * pix = mainTransTable[(pix<<8) + pix2]; * d_viewbuffer[d_scantable[new_p[1]] + new_p[0]] = pix; * } * } * } move.l 0(a5,d5.l*4),a3 cmp 0(a3,d4.l*2),d0 ;if (z >= *zbuf) blt.b .nodraw move d0,0(a3,d4.l*2) ;*zbuf = z swap d6 swap d7 lea _skintable,a3 move.l 0(a3,d7.w*4),a3 ;skintable[new[3]] move.b 0(a3,d6.w),d2 ;d2 = skintable[new[3]][new[2]>>16] tst.b d2 ;if (color_map_idx != 0) beq.b .nodraw and #$ff,d2 btst #0,d2 ;if (color_map_idx % 2 == 0) bne.b .transluc move.l _d_pcolormap,a3 move.b 0(a3,d2.w),d2 ;pix = d_pcolormap[d2] lea _d_scantable,a3 move.l 0(a3,d5.l*4),d1 ;d_scantable[new[1]] add.l d4,d1 ;+new[0] move.b d2,0(a6,d1.l) ;d_viewbuffer[d1] = pix bra.b .nodraw .transluc move.l _d_pcolormap,a3 move.b 0(a3,d2.w),d2 ;pix = d_pcolormap[d2] lsl.w #8,d2 move.b 0(a6,d1.l),d2 ;d2 = (pix<<8) + pix2 lea _d_scantable,a3 move.l 0(a3,d5.l*4),d1 ;d_scantable[new[1]] add.l d4,d1 ;+new[0] move.l _mainTransTable,a3 move.b 0(a3,d2.l),0(a6,d1.l) ;d_viewbuffer[d1] = mainTransTable[d2] .nodraw *// recursively continue * D_PolysetRecursiveTriangleT2 (lp3, lp1, new); * D_PolysetRecursiveTriangleT2 (lp3, new, lp2); movem.l a1/a2/a4,-(sp) move.l a0,a1 move.l a2,a0 move.l a4,a2 bsr DoRecursionT2 ;DRT (lp3, lp1, new) movem.l (sp)+,a1/a2/a4 move.l a2,a0 move.l a1,a2 move.l a4,a1 bsr DoRecursionT2 ;DRT (lp3, new, lp2) .exit lea 6*4(sp),sp rts ****************************************************************************** * * void D_PolysetRecursiveTriangleT3 (int *lp1, int *lp2, int *lp3) * ****************************************************************************** cnop 0,4 _D_PolysetRecursiveTriangleT3 ***** stackframe rsreset .new rs.l 6 .intregs rs.l 11 rs.l 1 .lp1 rs.l 1 .lp2 rs.l 1 .lp3 rs.l 1 movem.l d2-d7/a2-a6,-(sp) sub.l #.intregs,sp move.l .lp1(sp),a0 move.l .lp2(sp),a1 move.l .lp3(sp),a2 lea _zspantable,a5 move.l _d_viewbuffer,a6 bsr DoRecursionT3 add.l #.intregs,sp movem.l (sp)+,d2-d7/a2-a6 rts ****** a0 -> lp1 ****** a1 -> lp2 ****** a2 -> lp3 DoRecursionT3 lea -6*4(sp),sp move.l sp,a4 * d = lp2[0] - lp1[0]; * if (d < -1 || d > 1) * goto split; * d = lp2[1] - lp1[1]; * if (d < -1 || d > 1) * goto split; * * d = lp3[0] - lp2[0]; * if (d < -1 || d > 1) * goto split2; * d = lp3[1] - lp2[1]; * if (d < -1 || d > 1) * goto split2; * * d = lp1[0] - lp3[0]; * if (d < -1 || d > 1) * goto split3; * d = lp1[1] - lp3[1]; * if (d < -1 || d > 1) move.l (a1),d0 move.l (a0),d1 move.l 1*4(a1),d2 move.l 1*4(a0),d3 move.l d0,d4 sub.l d1,d4 ;d = lp2[0] - lp1[0] addq.l #1,d4 cmp.l #2,d4 ;if (d < -1 || d > 1) bhi.b .split ;goto split move.l d2,d5 sub.l d3,d5 ;d = lp2[1] - lp1[1] addq.l #1,d5 cmp.l #2,d5 ;if (d < -1 || d > 1) bhi.b .split ;goto split move.l (a2),d4 move.l d4,d5 sub.l d0,d4 ;d = lp3[0] - lp2[0] addq.l #1,d4 cmp.l #2,d4 ;if (d < -1 || d > 1) bhi.b .split2 ;goto split2 move.l 1*4(a2),d4 move.l d4,d6 sub.l d2,d4 ;d = lp3[1] - lp2[1] addq.l #1,d4 cmp.l #2,d4 ;if (d < -1 || d > 1) bhi.b .split2 ;goto split2 sub.l d1,d5 ;d = lp1[0] - lp3[0] addq.l #1,d5 cmp.l #2,d5 ;if (d < -1 || d > 1) bhi.b .split3 ;goto split3 sub.l d3,d6 ;d = lp1[1] - lp3[1] addq.l #1,d6 cmp.l #2,d6 ;if (d < -1 || d > 1) bls.b .exit *split3: * temp = lp1; * lp1 = lp3; * lp3 = lp2; * lp2 = temp; * * goto split; * *split2: * temp = lp1; * lp1 = lp2; * lp2 = lp3; * lp3 = temp; .split3 exg a1,a2 ;rotate forward exg a1,a0 move.l (a1),d0 move.l (a0),d1 move.l 1*4(a1),d2 move.l 1*4(a0),d3 bra.b .split .split2 exg a1,a0 ;rotate backward exg a1,a2 move.l (a1),d0 move.l (a0),d1 move.l 1*4(a1),d2 move.l 1*4(a0),d3 .split * new[0] = (lp1[0] + lp2[0]) >> 1; * new[1] = (lp1[1] + lp2[1]) >> 1; * new[2] = (lp1[2] + lp2[2]) >> 1; * new[3] = (lp1[3] + lp2[3]) >> 1; * new[5] = (lp1[5] + lp2[5]) >> 1; move.l d0,d4 move.l d0,a3 add.l d1,d4 asr.l #1,d4 ;d4 = new[0] move.l d2,d5 add.l d3,d5 asr.l #1,d5 ;d5 = new[1] move.l 2*4(a0),d6 add.l 2*4(a1),d6 asr.l #1,d6 ;d6 = new[2] move.l 3*4(a0),d7 add.l 3*4(a1),d7 asr.l #1,d7 ;d7 = new[3] move.l 5*4(a0),d0 add.l 5*4(a1),d0 asr.l #1,d0 ;d0 = new[5] * if (lp2[1] > lp1[1]) * goto nodraw; * if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) * goto nodraw; move.l d4,(a4)+ ;store new[] move.l d5,(a4)+ move.l d6,(a4)+ move.l d7,(a4)+ move.l d0,4(a4) lea -16(a4),a4 swap d0 ;z = new[5]>>16 cmp.l d3,d2 ;if (lp2[1] > lp1[1]) bgt.b .nodraw ;goto nodraw cmp.l d2,d3 ;if (lp2[1] == lp1[1) bne.b .draw cmp.l d1,a3 ;&& (lp2[0] < lp1[0]) blt.b .nodraw ;goto nodraw .draw * z = new[5]>>16; * zbuf = zspantable[new[1]] + new[0]; * if (z >= *zbuf) * { * int pix; * * *zbuf = z; * if (color_map_idx != 0) * { * unsigned int pix; * * *zbuf = z; * pix = d_pcolormap[color_map_idx]; * d_viewbuffer[d_scantable[new_p[1]] + new_p[0]] = pix; * } * } move.l 0(a5,d5.l*4),a3 cmp 0(a3,d4.l*2),d0 ;if (z >= *zbuf) blt.b .nodraw move d0,0(a3,d4.l*2) ;*zbuf = z swap d6 swap d7 lea _skintable,a3 move.l 0(a3,d7.w*4),a3 ;skintable[new[3]] move.b 0(a3,d6.w),d2 ;d2 = skintable[new[3]][new[2]>>16] tst.b d2 ;if (color_map_idx != 0) beq.b .nodraw and #$ff,d2 move.l _d_pcolormap,a3 move.b 0(a3,d2.w),d2 ;pix = d_pcolormap[d2] lea _d_scantable,a3 move.l 0(a3,d5.l*4),d1 ;d_scantable[new[1]] add.l d4,d1 ;+new[0] move.b d2,0(a6,d1.l) ;d_viewbuffer[d1] = pix .nodraw *// recursively continue * D_PolysetRecursiveTriangleT3 (lp3, lp1, new); * D_PolysetRecursiveTriangleT3 (lp3, new, lp2); movem.l a1/a2/a4,-(sp) move.l a0,a1 move.l a2,a0 move.l a4,a2 bsr DoRecursionT3 ;DRT (lp3, lp1, new) movem.l (sp)+,a1/a2/a4 move.l a2,a0 move.l a1,a2 move.l a4,a1 bsr DoRecursionT3 ;DRT (lp3, new, lp2) .exit lea 6*4(sp),sp rts ****************************************************************************** * * void D_PolysetRecursiveTriangleT5 (int *lp1, int *lp2, int *lp3) * ****************************************************************************** cnop 0,4 _D_PolysetRecursiveTriangleT5 ***** stackframe rsreset .new rs.l 6 .intregs rs.l 11 rs.l 1 .lp1 rs.l 1 .lp2 rs.l 1 .lp3 rs.l 1 movem.l d2-d7/a2-a6,-(sp) sub.l #.intregs,sp move.l .lp1(sp),a0 move.l .lp2(sp),a1 move.l .lp3(sp),a2 lea _zspantable,a5 move.l _d_viewbuffer,a6 bsr DoRecursionT5 add.l #.intregs,sp movem.l (sp)+,d2-d7/a2-a6 rts ****** a0 -> lp1 ****** a1 -> lp2 ****** a2 -> lp3 DoRecursionT5 lea -6*4(sp),sp move.l sp,a4 * d = lp2[0] - lp1[0]; * if (d < -1 || d > 1) * goto split; * d = lp2[1] - lp1[1]; * if (d < -1 || d > 1) * goto split; * * d = lp3[0] - lp2[0]; * if (d < -1 || d > 1) * goto split2; * d = lp3[1] - lp2[1]; * if (d < -1 || d > 1) * goto split2; * * d = lp1[0] - lp3[0]; * if (d < -1 || d > 1) * goto split3; * d = lp1[1] - lp3[1]; * if (d < -1 || d > 1) move.l (a1),d0 move.l (a0),d1 move.l 1*4(a1),d2 move.l 1*4(a0),d3 move.l d0,d4 sub.l d1,d4 ;d = lp2[0] - lp1[0] addq.l #1,d4 cmp.l #2,d4 ;if (d < -1 || d > 1) bhi.b .split ;goto split move.l d2,d5 sub.l d3,d5 ;d = lp2[1] - lp1[1] addq.l #1,d5 cmp.l #2,d5 ;if (d < -1 || d > 1) bhi.b .split ;goto split move.l (a2),d4 move.l d4,d5 sub.l d0,d4 ;d = lp3[0] - lp2[0] addq.l #1,d4 cmp.l #2,d4 ;if (d < -1 || d > 1) bhi.b .split2 ;goto split2 move.l 1*4(a2),d4 move.l d4,d6 sub.l d2,d4 ;d = lp3[1] - lp2[1] addq.l #1,d4 cmp.l #2,d4 ;if (d < -1 || d > 1) bhi.b .split2 ;goto split2 sub.l d1,d5 ;d = lp1[0] - lp3[0] addq.l #1,d5 cmp.l #2,d5 ;if (d < -1 || d > 1) bhi.b .split3 ;goto split3 sub.l d3,d6 ;d = lp1[1] - lp3[1] addq.l #1,d6 cmp.l #2,d6 ;if (d < -1 || d > 1) bls.b .exit *split3: * temp = lp1; * lp1 = lp3; * lp3 = lp2; * lp2 = temp; * * goto split; * *split2: * temp = lp1; * lp1 = lp2; * lp2 = lp3; * lp3 = temp; .split3 exg a1,a2 ;rotate forward exg a1,a0 move.l (a1),d0 move.l (a0),d1 move.l 1*4(a1),d2 move.l 1*4(a0),d3 bra.b .split .split2 exg a1,a0 ;rotate backward exg a1,a2 move.l (a1),d0 move.l (a0),d1 move.l 1*4(a1),d2 move.l 1*4(a0),d3 .split * new[0] = (lp1[0] + lp2[0]) >> 1; * new[1] = (lp1[1] + lp2[1]) >> 1; * new[2] = (lp1[2] + lp2[2]) >> 1; * new[3] = (lp1[3] + lp2[3]) >> 1; * new[5] = (lp1[5] + lp2[5]) >> 1; move.l d0,d4 move.l d0,a3 add.l d1,d4 asr.l #1,d4 ;d4 = new[0] move.l d2,d5 add.l d3,d5 asr.l #1,d5 ;d5 = new[1] move.l 2*4(a0),d6 add.l 2*4(a1),d6 asr.l #1,d6 ;d6 = new[2] move.l 3*4(a0),d7 add.l 3*4(a1),d7 asr.l #1,d7 ;d7 = new[3] move.l 5*4(a0),d0 add.l 5*4(a1),d0 asr.l #1,d0 ;d0 = new[5] * if (lp2[1] > lp1[1]) * goto nodraw; * if ((lp2[1] == lp1[1]) && (lp2[0] < lp1[0])) * goto nodraw; move.l d4,(a4)+ ;store new[] move.l d5,(a4)+ move.l d6,(a4)+ move.l d7,(a4)+ move.l d0,4(a4) lea -16(a4),a4 swap d0 ;z = new[5]>>16 cmp.l d3,d2 ;if (lp2[1] > lp1[1]) bgt.b .nodraw ;goto nodraw cmp.l d2,d3 ;if (lp2[1] == lp1[1) bne.b .draw cmp.l d1,a3 ;&& (lp2[0] < lp1[0]) blt.b .nodraw ;goto nodraw .draw * z = new[5]>>16; * zbuf = zspantable[new[1]] + new[0]; * if (z >= *zbuf) * { * color_map_idx = skintable[new_p[3]>>16][new_p[2]>>16]; * * if (color_map_idx != 0) * { * unsigned int pix, pix2; * * *zbuf = z; * pix = color_map_idx; * pix2 = d_viewbuffer[d_scantable[new_p[1]] + new_p[0]]; * pix = transTable[(pix<<8) + pix2]; * d_viewbuffer[d_scantable[new_p[1]] + new_p[0]] = pix; * } * } move.l 0(a5,d5.l*4),a3 cmp 0(a3,d4.l*2),d0 ;if (z >= *zbuf) blt.b .nodraw ;move d0,0(a3,d4.l*2) ;*zbuf = z swap d6 swap d7 lea _skintable,a3 move.l 0(a3,d7.w*4),a3 ;skintable[new[3]] move.b 0(a3,d6.w),d2 ;d2 = skintable[new[3]][new[2]>>16] tst.b d2 ;if (color_map_idx != 0) beq.b .nodraw and #$ff,d2 ;move.l _d_pcolormap,a3 ;move.b 0(a3,d2.w),d2 ;pix = d_pcolormap[d2] lsl.w #8,d2 move.b 0(a6,d1.l),d2 ;d2 = (pix<<8) + pix2 lea _d_scantable,a3 move.l 0(a3,d5.l*4),d1 ;d_scantable[new[1]] add.l d4,d1 ;+new[0] move.l _transTable,a3 move.b 0(a3,d2.l),0(a6,d1.l) ;d_viewbuffer[d1] = transTable[d2] .nodraw *// recursively continue * D_PolysetRecursiveTriangleT5 (lp3, lp1, new); * D_PolysetRecursiveTriangleT5 (lp3, new, lp2); movem.l a1/a2/a4,-(sp) move.l a0,a1 move.l a2,a0 move.l a4,a2 bsr DoRecursionT5 ;DRT (lp3, lp1, new) movem.l (sp)+,a1/a2/a4 move.l a2,a0 move.l a1,a2 move.l a4,a1 bsr DoRecursionT5 ;DRT (lp3, new, lp2) .exit lea 6*4(sp),sp rts ****************************************************************************** * * void D_PolysetSetUpForLineScan (fixed8_t startvertu, * fixed8_t startvertv, fixed8_t endvertu, fixed8_t endvertv) * * Parameters are transferred in registers d0-d3 ****************************************************************************** cnop 0,4 D_PolysetSetUpForLineScan ***** stackframe rsreset .intregs rs.l 2 rs.l 1 .startvertu rs.l 1 .startvertv rs.l 1 .endvertu rs.l 1 .endvertv rs.l 1 * errorterm = -1; * * tm = endvertu - startvertu; * tn = endvertv - startvertv; move.l #-1,_errorterm sub.l d0,d2 sub.l d1,d3 add.l #15,d2 add.l #15,d3 * if (((tm <= 16) && (tm >= -15)) && * ((tn <= 16) && (tn >= -15))) * { * ptemp = &adivtab[((tm+15) << 5) + (tn+15)]; * ubasestep = ptemp->quotient; * erroradjustup = ptemp->remainder; * erroradjustdown = tn; * } cmp.l #31,d2 bhi.b .else cmp.l #31,d3 bhi.b .else lea _adivtab,a0 lsl.l #5,d2 add.l d3,d2 lea 0(a0,d2.l*8),a0 move.l PTEMP_QUOTIENT(a0),_ubasestep move.l PTEMP_REMAINDER(a0),_erroradjustup sub.l #15,d3 move.l d3,_erroradjustdown bra.b .exit * { * dm = (double)tm; * dn = (double)tn; * * FloorDivMod (dm, dn, &ubasestep, &erroradjustup); * * erroradjustdown = dn; * } .else sub.l #15,d2 sub.l #15,d3 fmove.l d2,fp0 fmove.l d3,fp1 move.l #_erroradjustup,-(sp) move.l #_ubasestep,-(sp) fmove.d fp1,-(sp) fmove.d fp0,-(sp) jsr _FloorDivMod add #24,sp move.l d3,_erroradjustdown .exit rts ***************************************************************************** * * void D_PolysetCalcGradients (int skinwidth) * ****************************************************************************** cnop 0,4 _D_PolysetCalcGradients ***** stackframe rsreset .fpuregs rs.x 6 .intregs rs.l 3 rs.l 1 .skinwidth rs.l 1 ***** prologue movem.l d2/d3/a2,-(sp) fmovem.x fp2-fp7,-(sp) * p00_minus_p20 = r_p0[0] - r_p2[0]; * p01_minus_p21 = r_p0[1] - r_p2[1]; * p10_minus_p20 = r_p1[0] - r_p2[0]; * p11_minus_p21 = r_p1[1] - r_p2[1]; * * xstepdenominv = 1.0 / (float)d_xdenom; * * ystepdenominv = -xstepdenominv; lea _r_p2,a0 lea _r_p1,a1 lea _r_p0,a2 move.l (a2),d0 move.l 1*4(a2),d1 move.l (a0),d2 sub.l d2,d0 fmove.l d0,fp0 ;fp0 = p00_minus_p20 move.l 1*4(a0),d3 sub.l d3,d1 fmove.l d1,fp1 ;fp1 = p01_minus_p21 sub.l (a1),d2 neg.l d2 fmove.l d2,fp2 ;fp2 = p10_minus_p20 sub.l 4(a1),d3 neg.l d3 fmove.l d3,fp3 ;fp3 = p11_minus_p21 fmove.l _d_xdenom,fp4 fmove.s #1,fp5 fdiv fp4,fp5 ;fp5 = 1.0 / (float)d_xdenom fneg fp5,fp6 ;fp6 = ystepdenominv fmul fp5,fp1 fmul fp5,fp3 fmul fp6,fp0 fmul fp6,fp2 * t0 = r_p0[4] - r_p2[4]; * t1 = r_p1[4] - r_p2[4]; * r_lstepx = (int) * ceil((t1 * p01_minus_p21 - t0 * p11_minus_p21) * xstepdenominv); * r_lstepy = (int) * ceil((t1 * p00_minus_p20 - t0 * p10_minus_p20) * ystepdenominv); fmove.l fpcr,d2 fmove.l #$000000b0,fpcr move.l 4*4(a0),d0 move.l d0,d1 sub.l 4*4(a2),d0 neg.l d0 fmove.l d0,fp4 ;fp4 = t0 sub.l 4*4(a1),d1 neg.l d1 fmove.l d1,fp5 ;fp5 = t1 fmove fp4,fp6 fmul fp3,fp6 fmove fp5,fp7 fmul fp1,fp7 fsub fp6,fp7 fmove.l fp7,_r_lstepx fmul fp2,fp4 fmul fp0,fp5 fsub fp4,fp5 fmove.l fp5,_r_lstepy fmove.l d2,fpcr * t0 = r_p0[2] - r_p2[2]; * t1 = r_p1[2] - r_p2[2]; * r_sstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * * xstepdenominv); * r_sstepy = (int)((t1 * p00_minus_p20 - t0* p10_minus_p20) * * ystepdenominv); move.l 2*4(a0),d0 move.l d0,d1 sub.l 2*4(a2),d0 neg.l d0 fmove.l d0,fp4 ;fp4 = t0 sub.l 2*4(a1),d1 neg.l d1 fmove.l d1,fp5 ;fp5 = t1 fmove fp4,fp6 fmul fp3,fp6 fmove fp5,fp7 fmul fp1,fp7 fsub fp6,fp7 fmove.l fp7,d2 move.l d2,_r_sstepx fmul fp2,fp4 fmul fp0,fp5 fsub fp4,fp5 fmove.l fp5,_r_sstepy * t0 = r_p0[3] - r_p2[3]; * t1 = r_p1[3] - r_p2[3]; * r_tstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * * xstepdenominv); * r_tstepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * * ystepdenominv); move.l 3*4(a0),d0 move.l d0,d1 sub.l 3*4(a2),d0 neg.l d0 fmove.l d0,fp4 ;fp4 = t0 sub.l 3*4(a1),d1 neg.l d1 fmove.l d1,fp5 ;fp5 = t1 fmove fp4,fp6 fmul fp3,fp6 fmove fp5,fp7 fmul fp1,fp7 fsub fp6,fp7 fmove.l fp7,d3 move.l d3,_r_tstepx fmul fp2,fp4 fmul fp0,fp5 fsub fp4,fp5 fmove.l fp5,_r_tstepy * t0 = r_p0[5] - r_p2[5]; * t1 = r_p1[5] - r_p2[5]; * r_zistepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * * xstepdenominv); * r_zistepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * * ystepdenominv); move.l 5*4(a0),d0 move.l d0,d1 sub.l 5*4(a2),d0 neg.l d0 fmove.l d0,fp4 ;fp4 = t0 sub.l 5*4(a1),d1 neg.l d1 fmove.l d1,fp5 ;fp5 = t1 fmove fp4,fp6 fmul fp3,fp6 fmove fp5,fp7 fmul fp1,fp7 fsub fp6,fp7 fmove.l fp7,_r_zistepx fmul fp2,fp4 fmul fp0,fp5 fsub fp4,fp5 fmove.l fp5,_r_zistepy * a_sstepxfrac = r_sstepx & 0xFFFF; * a_tstepxfrac = r_tstepx & 0xFFFF; * a_ststepxwhole = skinwidth * (r_tstepx >> 16) + (r_sstepx >> 16); move.l .skinwidth(sp),d0 swap d3 muls d3,d0 clr d3 swap d3 move.l d2,d1 swap d1 ext.l d1 add.l d1,d0 and.l #$ffff,d2 move.l d2,_a_sstepxfrac move.l d3,_a_tstepxfrac move.l d0,_a_ststepxwhole fmovem.x (sp)+,fp2-fp7 movem.l (sp)+,d2/d3/a2 rts ****************************************************************************** * * void D_RasterizeAliasPolySmooth (void) * ****************************************************************************** cnop 0,4 _D_RasterizeAliasPolySmooth ***** stackframe rsreset .pdestx rs.l 1 .pzx rs.l 1 .aspanx rs.l 1 .ptexx rs.l 1 .sfracx rs.l 1 .tfracx rs.l 1 .lightx rs.l 1 .zix rs.l 1 .pdestb rs.l 1 .pzb rs.l 1 .aspanb rs.l 1 .ptexb rs.l 1 .sfracb rs.l 1 .tfracb rs.l 1 .lightb rs.l 1 .zib rs.l 1 .r_affine rs.l 1 .height rs.l 1 .initLH rs.l 1 .initRH rs.l 1 .savearea rs.l 11 .intregs rs.l 11 rs.l 1 movem.l d2-d7/a2-a6,-(sp) sub #.intregs,sp * plefttop = pedgetable->pleftedgevert0; * prighttop = pedgetable->prightedgevert0; * * pleftbottom = pedgetable->pleftedgevert1; * prightbottom = pedgetable->prightedgevert1; * * initialleftheight = pleftbottom[1] - plefttop[1]; * initialrightheight = prightbottom[1] - prighttop[1]; move.l _pedgetable,a0 move.l ETAB_PLEV0(a0),a6 ;a6 = plefttop move.l ETAB_PREV0(a0),a2 ;a2 = prighttop move.l ETAB_PLEV1(a0),a3 ;a3 = pleftbottom move.l ETAB_PREV1(a0),a4 ;a4 = prightbottom lea _r_affinetridesc,a5 move.l 4(a3),d6 move.l 4(a6),d7 ;d7 = plefttop[1] move.l d6,d2 sub.l d7,d2 move.l d2,.initLH(sp) ;initialleftheight move.l 4(a4),d3 move.l 4(a2),d4 move.l d3,d5 sub.l d4,d5 move.l d5,.initRH(sp) ;initialrightheight move.l R_SKINWIDTH(a5),d4 move.l d4,.r_affine(sp) move.l d4,-(sp) bsr _D_PolysetCalcGradients addq #4,sp move.l d6,d3 move.l (a3),d2 move.l d7,d1 move.l (a6),d0 bsr D_PolysetSetUpForLineScan * d_pedgespanpackage = a_spans; * * ystart = plefttop[1]; * d_aspancount = plefttop[0] - prighttop[0]; * * d_ptex = (byte *)r_affinetridesc.pskin + (plefttop[2] >> 16) + * (plefttop[3] >> 16) * r_affinetridesc.skinwidth; * d_sfrac = plefttop[2] & 0xFFFF; * d_tfrac = plefttop[3] & 0xFFFF; * d_pzbasestep = d_zwidth + ubasestep; * d_pzextrastep = d_pzbasestep + 1; * d_light = plefttop[4]; * d_zi = plefttop[5]; * * d_pdestbasestep = screenwidth + ubasestep; * d_pdestextrastep = d_pdestbasestep + 1; * d_pdest = (byte *)d_viewbuffer + * ystart * screenwidth + plefttop[0]; * d_pz = d_pzbuffer + ystart * d_zwidth + plefttop[0]; move.l _a_spans,_d_pedgespanpackage move.l (a6),d2 sub.l (a2),d2 move.l d2,_d_aspancount ;d_aspancount = plefttop[0] - ... move.l 2*4(a6),d0 move.l 3*4(a6),d1 move.l d0,d3 ;d3 = plefttop[2] move.l d1,d6 ;d6 = plefttop[3] swap d0 swap d1 and.l #$ffff,d0 and.l #$ffff,d1 mulu d4,d1 add.l d0,d1 add.l R_PSKIN(a5),d1 move.l d1,_d_ptex ;d_ptex = ... and.l #$ffff,d3 and.l #$ffff,d6 move.l d3,_d_sfrac ;d_sfrac = plefttop[2]&$ffff move.l d6,_d_tfrac ;d_tfrac = plefttop[3]&$ffff move.l _d_zwidth,d0 move.l d0,d3 ;d3 = d_zwidth move.l _ubasestep,d1 move.l d1,.aspanb(sp) move.l d1,d5 ;d5 = ubasestep add.l d1,d0 addq.l #1,d1 move.l d1,_d_countextrastep move.l d1,.aspanx(sp) add.l d0,d0 move.l d0,.pzb(sp) ;d_pzbasestep = d_zwidth + ubasestep addq.l #2,d0 move.l d0,.pzx(sp) ;d_pzextrastep = d_pzbasestep + 1 move.l 4*4(a6),_d_light ;d_light = plefttop[4] move.l 5*4(a6),_d_zi ;d_zi = plefttop[5] move.l _screenwidth,d0 add.l d0,d1 move.l d1,.pdestx(sp) ;d_pdestextrastep = ... subq.l #1,d1 move.l d1,.pdestb(sp) ;d_pdestbasestep = screenwidth + .. mulu d7,d0 ;screenwidth * ystart move.l (a6),d6 add.l d6,d0 ;+ plefttop[0] add.l _d_viewbuffer,d0 move.l d0,_d_pdest ;d_pdest + d_viewbuffer + ... mulu d7,d3 ;d_zwidth * ystart add.l d6,d3 ;+ plefttop[0] add.l d3,d3 add.l _d_pzbuffer,d3 move.l d3,_d_pz ;d_pz = d_pzbuffer + ... * if (ubasestep < 0) * working_lstepx = r_lstepx - 1; * else * working_lstepx = r_lstepx; * * d_countextrastep = ubasestep + 1; * d_ptexbasestep = ((r_sstepy + r_sstepx * ubasestep) >> 16) + * ((r_tstepy + r_tstepx * ubasestep) >> 16) * * r_affinetridesc.skinwidth; * d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF; * d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF; * d_lightbasestep = r_lstepy + working_lstepx * ubasestep; * d_zibasestep = r_zistepy + r_zistepx * ubasestep; * * d_ptexextrastep = ((r_sstepy + r_sstepx * d_countextrastep) >> 16) + * ((r_tstepy + r_tstepx * d_countextrastep) >> 16) * * r_affinetridesc.skinwidth; * d_sfracextrastep = (r_sstepy + r_sstepx*d_countextrastep) & 0xFFFF; * d_tfracextrastep = (r_tstepy + r_tstepx*d_countextrastep) & 0xFFFF; * d_lightextrastep = d_lightbasestep + working_lstepx; * d_ziextrastep = d_zibasestep + r_zistepx; * move.l _r_sstepy,a0 move.l _r_sstepx,d1 move.l _r_tstepy,a1 move.l _r_tstepx,d6 move.l d1,d2 muls.l d5,d2 move.l d6,d7 muls.l d5,d7 add.l a0,d2 add.l a1,d7 move.l d2,d0 move.l d7,d3 and.l #$ffff,d0 move.l d0,.sfracb(sp) and.l #$ffff,d3 swap d3 move.l d3,.tfracb(sp) swap d3 add.l d1,d0 add.l d6,d3 and.l #$ffff,d0 move.l d0,.sfracx(sp) and.l #$ffff,d3 swap d3 move.l d3,.tfracx(sp) swap d3 move.l d2,d0 move.l d7,d3 swap d2 ext.l d2 swap d7 muls d4,d7 add.l d2,d7 move.l d7,.ptexb(sp) add.l d1,d0 add.l d6,d3 swap d0 ext.l d0 swap d3 muls d4,d3 add.l d0,d3 move.l d3,.ptexx(sp) move.l _r_lstepy,d0 move.l _r_lstepx,d1 tst.l d5 ;(if ubasestep < 0) bge.b .ge subq.l #1,d1 ;working_lstepx = r_lstepx - 1 .ge move.l d1,d2 muls.l d5,d1 add.l d0,d1 move.l d1,.lightb(sp) add.l d2,d1 move.l d1,.lightx(sp) move.l _r_zistepy,d0 move.l _r_zistepx,d1 move.l d1,d2 muls.l d5,d1 add.l d0,d1 move.l d1,.zib(sp) add.l d2,d1 move.l d1,.zix(sp) ***** D_PolysetScanLeftEdge (inlined) movem.l d2-d7/a2-a6,.savearea(sp) move.l _d_pedgespanpackage,a0 move.l _d_pdest,a1 move.l _d_pz,a2 move.l _d_aspancount,d6 move.l _d_ptex,a3 move.l _d_sfrac,d4 move.l _d_tfrac,d5 swap d5 move.l _d_light,a5 move.l _d_zi,a4 move.l _errorterm,d2 move.l _erroradjustup,d1 move.l _erroradjustdown,d0 move.l .initLH(sp),d7 subq #1,d7 .loopA * d_pedgespanpackage->pdest = d_pdest; * d_pedgespanpackage->pz = d_pz; * d_pedgespanpackage->count = d_aspancount; * d_pedgespanpackage->ptex = d_ptex; * * d_pedgespanpackage->sfrac = d_sfrac; * d_pedgespanpackage->tfrac = d_tfrac; * * d_pedgespanpackage->light = d_light; * d_pedgespanpackage->zi = d_zi; * * d_pedgespanpackage++; * * errorterm += erroradjustup; * if (errorterm >= 0) move.l a1,(a0)+ move.l a2,(a0)+ move.l d6,(a0)+ move.l a3,(a0)+ move.l d4,(a0)+ swap d5 clr (a0)+ move d5,(a0)+ swap d5 move.l a5,(a0)+ move.l a4,(a0)+ add.l d1,d2 blt.b .elseA * d_pdest += d_pdestextrastep; * d_pz += d_pzextrastep; * d_aspancount += d_countextrastep; * d_ptex += d_ptexextrastep; * d_sfrac += d_sfracextrastep; * d_ptex += d_sfrac >> 16; * * d_sfrac &= 0xFFFF; * d_tfrac += d_tfracextrastep; * if (d_tfrac & 0x10000) * { * d_ptex += r_affinetridesc.skinwidth; * d_tfrac &= 0xFFFF; * } * d_light += d_lightextrastep; * d_zi += d_ziextrastep; * errorterm -= erroradjustdown; lea .pdestx(sp),a6 add.l (a6)+,a1 add.l (a6)+,a2 add.l (a6)+,d6 add.l (a6)+,a3 add.l (a6)+,d4 swap d4 add d4,a3 clr d4 swap d4 add.l (a6)+,d5 bcc.b .contA add.l .r_affine(sp),a3 .contA add.l (a6)+,a5 add.l (a6)+,a4 sub.l d0,d2 bra.b .nextA .elseA * d_pdest += d_pdestbasestep; * d_pz += d_pzbasestep; * d_aspancount += ubasestep; * d_ptex += d_ptexbasestep; * d_sfrac += d_sfracbasestep; * d_ptex += d_sfrac >> 16; * d_sfrac &= 0xFFFF; * d_tfrac += d_tfracbasestep; * if (d_tfrac & 0x10000) * { * d_ptex += r_affinetridesc.skinwidth; * d_tfrac &= 0xFFFF; * } * d_light += d_lightbasestep; * d_zi += d_zibasestep; lea .pdestb(sp),a6 add.l (a6)+,a1 add.l (a6)+,a2 add.l (a6)+,d6 add.l (a6)+,a3 add.l (a6)+,d4 swap d4 add d4,a3 clr d4 swap d4 add.l (a6)+,d5 bcc.b .cont2A add.l .r_affine(sp),a3 .cont2A add.l (a6)+,a5 add.l (a6)+,a4 .nextA dbra d7,.loopA move.l a0,_d_pedgespanpackage movem.l .savearea(sp),d2-d7/a2-a6 ****** End of D_PolysetScanLeftEdge * if (pedgetable->numleftedges == 2) * { * int height; * * plefttop = pleftbottom; * pleftbottom = pedgetable->pleftedgevert2; * * D_PolysetSetUpForLineScan(plefttop[0], plefttop[1], * pleftbottom[0], pleftbottom[1]); * * height = pleftbottom[1] - plefttop[1]; * *// TODO: make this a function; modularize this function in general * * ystart = plefttop[1]; * d_aspancount = plefttop[0] - prighttop[0]; * d_ptex = (byte *)r_affinetridesc.pskin + (plefttop[2] >> 16) + * (plefttop[3] >> 16) * r_affinetridesc.skinwidth; * d_sfrac = 0; * d_tfrac = 0; * d_light = plefttop[4]; * d_zi = plefttop[5]; * * d_pdestbasestep = screenwidth + ubasestep; * d_pdestextrastep = d_pdestbasestep + 1; * d_pdest = (byte *)d_viewbuffer + ystart * screenwidth + plefttop[0]; * d_pzbasestep = d_zwidth + ubasestep; * d_pzextrastep = d_pzbasestep + 1; * d_pz = d_pzbuffer + ystart * d_zwidth + plefttop[0]; * * if (ubasestep < 0) * working_lstepx = r_lstepx - 1; * else * working_lstepx = r_lstepx; * * d_countextrastep = ubasestep + 1; * d_ptexbasestep = ((r_sstepy + r_sstepx * ubasestep) >> 16) + * ((r_tstepy + r_tstepx * ubasestep) >> 16) * * r_affinetridesc.skinwidth; * d_sfracbasestep = (r_sstepy + r_sstepx * ubasestep) & 0xFFFF; * d_tfracbasestep = (r_tstepy + r_tstepx * ubasestep) & 0xFFFF; * d_lightbasestep = r_lstepy + working_lstepx * ubasestep; * d_zibasestep = r_zistepy + r_zistepx * ubasestep; * * d_ptexextrastep = ((r_sstepy + r_sstepx * d_countextrastep) >> 16) + * ((r_tstepy + r_tstepx * d_countextrastep) >> 16) * * r_affinetridesc.skinwidth; * d_sfracextrastep = (r_sstepy+r_sstepx*d_countextrastep) & 0xFFFF; * d_tfracextrastep = (r_tstepy+r_tstepx*d_countextrastep) & 0xFFFF; * d_lightextrastep = d_lightbasestep + working_lstepx; * d_ziextrastep = d_zibasestep + r_zistepx; move.l _pedgetable,a0 cmp.l #2,ETAB_NUMLEFTEDGES(a0) bne.b .cont move.l a3,a6 move.l ETAB_PLEV2(a0),a3 move.l 4(a3),d5 move.l d5,d3 move.l (a3),d2 move.l 4(a6),d7 move.l d7,d1 move.l (a6),d6 move.l d6,d0 bsr D_PolysetSetUpForLineScan sub.l d7,d5 move.l d5,.height(sp) sub.l (a2),d6 move.l d6,_d_aspancount ;d_aspancount = plefttop[0] - ... move.l 2*4(a6),d0 move.l 3*4(a6),d1 move.l d0,d3 ;d3 = plefttop[2] move.l d1,d6 ;d6 = plefttop[3] swap d0 swap d1 and.l #$ffff,d0 and.l #$ffff,d1 mulu d4,d1 add.l d0,d1 add.l R_PSKIN(a5),d1 move.l d1,_d_ptex ;d_ptex = ... clr.l _d_sfrac clr.l _d_tfrac move.l _d_zwidth,d0 move.l d0,d3 ;d3 = d_zwidth move.l _ubasestep,d1 move.l d1,.aspanb(sp) move.l d1,d5 ;d5 = ubasestep add.l d1,d0 addq.l #1,d1 move.l d1,_d_countextrastep move.l d1,.aspanx(sp) add.l d0,d0 move.l d0,.pzb(sp) ;d_pzbasestep = d_zwidth + ubasestep addq.l #2,d0 move.l d0,.pzx(sp) ;d_pzextrastep = d_pzbasestep + 1 move.l 4*4(a6),_d_light ;d_light = plefttop[4] move.l 5*4(a6),_d_zi ;d_zi = plefttop[5] move.l _screenwidth,d0 add.l d0,d1 move.l d1,.pdestx(sp) ;d_pdestextrastep = ... subq.l #1,d1 move.l d1,.pdestb(sp) ;d_pdestbasestep = screenwidth + .. mulu d7,d0 ;screenwidth * ystart move.l (a6),d6 add.l d6,d0 ;+ plefttop[0] add.l _d_viewbuffer,d0 move.l d0,_d_pdest ;d_pdest + d_viewbuffer + ... mulu d7,d3 ;d_zwidth * ystart add.l d6,d3 ;+ plefttop[0] add.l d3,d3 add.l _d_pzbuffer,d3 move.l d3,_d_pz ;d_pz = d_pzbuffer + ... move.l _r_sstepy,a0 move.l _r_sstepx,d1 move.l _r_tstepy,a1 move.l _r_tstepx,d6 move.l d1,d2 muls.l d5,d2 move.l d6,d7 muls.l d5,d7 add.l a0,d2 add.l a1,d7 move.l d2,d0 move.l d7,d3 and.l #$ffff,d0 move.l d0,.sfracb(sp) and.l #$ffff,d3 swap d3 move.l d3,.tfracb(sp) swap d3 add.l d1,d0 add.l d6,d3 and.l #$ffff,d0 move.l d0,.sfracx(sp) and.l #$ffff,d3 swap d3 move.l d3,.tfracx(sp) swap d3 move.l d2,d0 move.l d7,d3 swap d2 ext.l d2 swap d7 muls d4,d7 add.l d2,d7 move.l d7,.ptexb(sp) add.l d1,d0 add.l d6,d3 swap d0 ext.l d0 swap d3 muls d4,d3 add.l d0,d3 move.l d3,.ptexx(sp) move.l _r_lstepy,d0 move.l _r_lstepx,d1 tst.l d5 ;(if ubasestep < 0) bge.b .ge2 subq.l #1,d1 ;working_lstepx = r_lstepx - 1 .ge2 move.l d1,d2 muls.l d5,d1 add.l d0,d1 move.l d1,.lightb(sp) add.l d2,d1 move.l d1,.lightx(sp) move.l _r_zistepy,d0 move.l _r_zistepx,d1 move.l d1,d2 muls.l d5,d1 add.l d0,d1 move.l d1,.zib(sp) add.l d2,d1 move.l d1,.zix(sp) ***** D_PolysetScanLeftEdge (inlined) movem.l d2-d7/a2-a6,.savearea(sp) move.l _d_pedgespanpackage,a0 move.l _d_pdest,a1 move.l _d_pz,a2 move.l _d_aspancount,d6 move.l _d_ptex,a3 move.l _d_sfrac,d4 move.l _d_tfrac,d5 swap d5 move.l _d_light,a5 move.l _d_zi,a4 move.l _errorterm,d2 move.l _erroradjustup,d1 move.l _erroradjustdown,d0 move.l .height(sp),d7 subq #1,d7 .loopB * d_pedgespanpackage->pdest = d_pdest; * d_pedgespanpackage->pz = d_pz; * d_pedgespanpackage->count = d_aspancount; * d_pedgespanpackage->ptex = d_ptex; * * d_pedgespanpackage->sfrac = d_sfrac; * d_pedgespanpackage->tfrac = d_tfrac; * * d_pedgespanpackage->light = d_light; * d_pedgespanpackage->zi = d_zi; * * d_pedgespanpackage++; * * errorterm += erroradjustup; * if (errorterm >= 0) move.l a1,(a0)+ move.l a2,(a0)+ move.l d6,(a0)+ move.l a3,(a0)+ move.l d4,(a0)+ swap d5 clr (a0)+ move d5,(a0)+ swap d5 move.l a5,(a0)+ move.l a4,(a0)+ add.l d1,d2 blt.b .elseB * d_pdest += d_pdestextrastep; * d_pz += d_pzextrastep; * d_aspancount += d_countextrastep; * d_ptex += d_ptexextrastep; * d_sfrac += d_sfracextrastep; * d_ptex += d_sfrac >> 16; * * d_sfrac &= 0xFFFF; * d_tfrac += d_tfracextrastep; * if (d_tfrac & 0x10000) * { * d_ptex += r_affinetridesc.skinwidth; * d_tfrac &= 0xFFFF; * } * d_light += d_lightextrastep; * d_zi += d_ziextrastep; * errorterm -= erroradjustdown; lea .pdestx(sp),a6 add.l (a6)+,a1 add.l (a6)+,a2 add.l (a6)+,d6 add.l (a6)+,a3 add.l (a6)+,d4 swap d4 add d4,a3 clr d4 swap d4 add.l (a6)+,d5 bcc.b .contB add.l .r_affine(sp),a3 .contB add.l (a6)+,a5 add.l (a6)+,a4 sub.l d0,d2 bra.b .nextB .elseB * d_pdest += d_pdestbasestep; * d_pz += d_pzbasestep; * d_aspancount += ubasestep; * d_ptex += d_ptexbasestep; * d_sfrac += d_sfracbasestep; * d_ptex += d_sfrac >> 16; * d_sfrac &= 0xFFFF; * d_tfrac += d_tfracbasestep; * if (d_tfrac & 0x10000) * { * d_ptex += r_affinetridesc.skinwidth; * d_tfrac &= 0xFFFF; * } * d_light += d_lightbasestep; * d_zi += d_zibasestep; lea .pdestb(sp),a6 add.l (a6)+,a1 add.l (a6)+,a2 add.l (a6)+,d6 add.l (a6)+,a3 add.l (a6)+,d4 swap d4 add d4,a3 clr d4 swap d4 add.l (a6)+,d5 bcc.b .cont2B add.l .r_affine(sp),a3 .cont2B add.l (a6)+,a5 add.l (a6)+,a4 .nextB dbra d7,.loopB move.l a0,_d_pedgespanpackage movem.l .savearea(sp),d2-d7/a2-a6 .cont * d_pedgespanpackage = a_spans; * * D_PolysetSetUpForLineScan(prighttop[0], prighttop[1], * prightbottom[0], prightbottom[1]); * d_aspancount = 0; * d_countextrastep = ubasestep + 1; * originalcount = a_spans[initialrightheight].count; * a_spans[initialrightheight].count = -999999; // mark end of the spanpackages * D_PolysetDrawSpans8 (a_spans); move.l _a_spans,_d_pedgespanpackage move.l 4(a4),d3 move.l (a4),d2 move.l 4(a2),d1 move.l (a2),d0 bsr D_PolysetSetUpForLineScan clr.l _d_aspancount move.l _ubasestep,d0 addq.l #1,d0 move.l d0,_d_countextrastep move.l _a_spans,a3 move.l .initRH(sp),d6 move.l d6,d0 asl.l #PSPANP_SIZEOF_EXP,d0 move.l PSPANP_COUNT(a3,d0.l),d7 move.l #-999999,PSPANP_COUNT(a3,d0.l) move.l a3,-(sp) ;bsr _D_PolysetDrawSpans8 move.l _d_polysetdrawspans,a0 jsr (a0) addq #4,sp * if (pedgetable->numrightedges == 2) * { * int height; * spanpackage_t *pstart; * * pstart = a_spans + initialrightheight; * pstart->count = originalcount; * * d_aspancount = prightbottom[0] - prighttop[0]; * * prighttop = prightbottom; * prightbottom = pedgetable->prightedgevert2; * * height = prightbottom[1] - prighttop[1]; * * D_PolysetSetUpForLineScan(prighttop[0], prighttop[1], * prightbottom[0], prightbottom[1]); * * d_countextrastep = ubasestep + 1; * a_spans[initialrightheight + height].count = -999999; * // mark end of the spanpackages * D_PolysetDrawSpans8 (pstart); move.l _pedgetable,a0 cmp.l #2,ETAB_NUMRIGHTEDGES(a0) bne.b .exit move.l a3,a6 move.l d6,d0 asl.l #PSPANP_SIZEOF_EXP,d0 add.l d0,a6 move.l d7,PSPANP_COUNT(a6) move.l (a4),d0 sub.l (a2),d0 move.l d0,_d_aspancount move.l a4,a2 move.l ETAB_PREV2(a0),a4 move.l 4(a4),d5 move.l d5,d3 move.l 4(a2),d0 sub.l d0,d5 move.l (a4),d2 move.l d0,d1 move.l (a2),d0 bsr D_PolysetSetUpForLineScan move.l _ubasestep,d4 addq.l #1,d4 move.l d4,_d_countextrastep add.l d6,d5 asl.l #PSPANP_SIZEOF_EXP,d5 move.l #-999999,PSPANP_COUNT(a3,d5.l) move.l a6,-(sp) ;bsr _D_PolysetDrawSpans8 move.l _d_polysetdrawspans,a0 jsr (a0) addq #4,sp .exit add #.intregs,sp movem.l (sp)+,d2-d7/a2-a6 rts ****************************************************************************** * * void D_DrawNonSubdiv (void) * ****************************************************************************** cnop 0,4 _D_DrawNonSubdiv movem.l d2-d7/a2-a6,-(sp) * pfv = r_affinetridesc.pfinalverts; * ptri = r_affinetridesc.ptriangles; * lnumtriangles = r_affinetridesc.numtriangles; lea _r_affinetridesc,a0 move.l R_PFINALVERTS(a0),a2 ;pfv = f_affinetridesc.pfinalverts move.l R_PTRIANGLES(a0),a3 ;ptri = f_affinetridesc.ptriangles move.l R_NUMTRIANGLES(a0),d3 ;lnumtriangles = r_affine... subq #1,d3 .loop * index0 = pfv + ptri->vertindex[0]; * index1 = pfv + ptri->vertindex[1]; * index2 = pfv + ptri->vertindex[2]; * * d_xdenom = (index0->v[1]-index1->v[1]) * * (index0->v[0]-index2->v[0]) - * (index0->v[0]-index1->v[0])*(index0->v[1]-index2->v[1]); clr.l d0 move.w MT_VERTINDEX+0*2(a3),d0 asl.l #FV_SIZEOF_EXP,d0 lea 0(a2,d0.l),a0 ;a0 = index0 clr.l d0 move.w MT_VERTINDEX+1*2(a3),d0 asl.l #FV_SIZEOF_EXP,d0 lea 0(a2,d0.l),a1 ;a1 = index1 clr.l d0 move.w MT_VERTINDEX+2*2(a3),d0 asl.l #FV_SIZEOF_EXP,d0 lea 0(a2,d0.l),a4 ;a2 = index2 movem.l d3/a3,-(sp) move.l (a0)+,d0 ;d0 = index0->v[0] move.l (a0)+,d1 ;d1 = index0->v[1] move.l (a1)+,a6 ;d2 = index1->v[0] move.l (a1)+,d2 ;a6 = index1->v[1] move.l (a4)+,d4 ;d4 = index2->v[0] move.l (a4)+,d5 ;d5 = index2->v[1] move.l d0,d6 move.l d1,d7 sub.l d4,d6 ;index0->v[0]-index2->v[0] sub.l d2,d7 ;index0->v[1]-index1->v[1] move.l d6,d3 muls.l d7,d3 move.l d0,d6 move.l d1,d7 sub.l a6,d6 ;index0->v[0]-index1->v[0] sub.l d5,d7 ;index0->v[1]-index2->v[1] muls.l d6,d7 * if (d_xdenom >= 0) * { * continue; * } sub.l d7,d3 bge.w .next ;if (d_xdenom >= 0) move.l d3,_d_xdenom ;d_xdenom = ... * r_p0[0] = index0->v[0]; // u * r_p0[1] = index0->v[1]; // v * r_p0[2] = index0->v[2]; // s * r_p0[3] = index0->v[3]; // t * r_p0[4] = index0->v[4]; // light * r_p0[5] = index0->v[5]; // iz * * r_p1[0] = index1->v[0]; * r_p1[1] = index1->v[1]; * r_p1[2] = index1->v[2]; * r_p1[3] = index1->v[3]; * r_p1[4] = index1->v[4]; * r_p1[5] = index1->v[5]; * * r_p2[0] = index2->v[0]; * r_p2[1] = index2->v[1]; * r_p2[2] = index2->v[2]; * r_p2[3] = index2->v[3]; * r_p2[4] = index2->v[4]; * r_p2[5] = index2->v[5]; lea _r_p1,a5 move.l a6,(a5)+ ;r_p1[0] = index1->v[0] move.l d2,(a5)+ ;r_p1[1] = index1->v[1] move.l (a1)+,(a5)+ ;... move.l (a1)+,(a5)+ move.l (a1)+,(a5)+ move.l (a1)+,(a5)+ lea _r_p0,a6 move.l d0,(a6)+ ;r_p0[0] = index0->v[0] move.l d1,(a6)+ ;... move.l (a0)+,(a6)+ move.l (a0)+,(a6)+ move.l (a0)+,(a6)+ move.l (a0)+,(a6)+ move.l MT_FACESFRONT(a3),d6 lea _r_p2,a3 ;r_p2[0] = index2->v[0] move.l d4,(a3)+ ;... move.l d5,(a3)+ move.l (a4)+,(a3)+ move.l (a4)+,(a3)+ move.l (a4)+,(a3)+ move.l (a4)+,(a3)+ * if (!ptri->facesfront) * { * if (index0->flags & ALIAS_ONSEAM) * r_p0[2] += r_affinetridesc.seamfixupX16; * if (index1->flags & ALIAS_ONSEAM) * r_p1[2] += r_affinetridesc.seamfixupX16; * if (index2->flags & ALIAS_ONSEAM) * r_p2[2] += r_affinetridesc.seamfixupX16; * } tst.l d6 ;if (!ptri->facesfront) bne.b .cont move.l _r_affinetridesc+R_SEAMFIXUP16,d3 move.l (a0),d0 ;if (index0->flags & ALIAS_ONSEAM) and.l #ALIAS_ONSEAM,d0 beq.b .1 add.l d3,-16(a6) ;r_p0[2] += r_affinetridesc.se... .1 move.l (a1),d0 and.l #ALIAS_ONSEAM,d0 beq.b .2 add.l d3,-16(a5) .2 move.l (a4),d0 and.l #ALIAS_ONSEAM,d0 beq.b .cont add.l d3,-16(a3) .cont ****** D_PolysetSetEdgeTable (inlined) cmp.l d2,d1 blt.b .lt1 beq.b .eq1 .gt1 moveq #ETAB_SIZEOF,d0 cmp.l d5,d1 beq.b .eq2 cmp.l d5,d2 beq.b .eq3 cmp.l d5,d1 ble.b .skip add.l #2*ETAB_SIZEOF,d0 .skip cmp.l d5,d2 ble.b .skip2 add.l #4*ETAB_SIZEOF,d0 .skip2 add.l #_edgetables,d0 move.l d0,_pedgetable bra.b .done .eq2 move.l #_edgetables+8*ETAB_SIZEOF,_pedgetable bra.b .done .eq3 move.l #_edgetables+10*ETAB_SIZEOF,_pedgetable bra.b .done .eq1 cmp.l d5,d1 bge.b .ge move.l #_edgetables+2*ETAB_SIZEOF,_pedgetable bra.b .done .ge move.l #_edgetables+5*ETAB_SIZEOF,_pedgetable bra.b .done .lt1 moveq #0,d0 cmp.l d5,d1 beq.b .eq4 cmp.l d5,d2 beq.b .eq5 cmp.l d5,d1 ble.b .skip3 add.l #2*ETAB_SIZEOF,d0 .skip3 cmp.l d5,d2 ble.b .skip4 add.l #4*ETAB_SIZEOF,d0 .skip4 add.l #_edgetables,d0 move.l d0,_pedgetable bra.b .done .eq4 move.l #_edgetables+9*ETAB_SIZEOF,_pedgetable bra.b .done .eq5 move.l #_edgetables+11*ETAB_SIZEOF,_pedgetable .done ****** end of D_PolysetSetEdgeTable bsr _D_RasterizeAliasPolySmooth ;jsr _D_RasterizeAliasPolySmooth .next movem.l (sp)+,d3/a3 lea MT_SIZEOF(a3),a3 ;ptri++ dbra d3,.loop movem.l (sp)+,d2-d7/a2-a6 rts ** ** 680x0 optimised Quake render routines by John Selck. ** ****************************************************************************** * * void D_PolysetDrawFinalVerts (finalvert_t *pv1, finalvert_t *pv2, finalvert_t *pv3) * * entity polygon renderer for very small polygons (1 pixel) * ****************************************************************************** cnop 0,4 _D_PolysetDrawFinalVerts ***** stackframe rsreset .intregs rs.l 9 rs.l 1 .pv1 rs.l 1 .pv2 rs.l 1 .pv3 rs.l 1 ****** prologue movem.l d2-d5/a2-a6,-(sp) ; TODO d3 is not used lea _zspantable,a1 lea _skintable,a2 lea _d_scantable,a3 move.l _acolormap,a4 move.l _d_viewbuffer,a5 move.l _r_refdef+REFDEF_VRECTRIGHT,d4 move.l _r_refdef+REFDEF_VRECTBOTTOM,d5 moveq #0,d2 move.l .pv1(sp),a6 bsr.b do_PolysetDrawFinalVerts move.l .pv2(sp),a6 bsr.b do_PolysetDrawFinalVerts move.l .pv3(sp),a6 bsr.b do_PolysetDrawFinalVerts movem.l (sp)+,d2-d5/a2-a6 rts cnop 0,4 do_PolysetDrawFinalVerts move.l (a6),d0 ;d0 = pv->v[0] cmp.l d4,d0 ;if (d0 < r_refdef.vrectright) bge.b .fvskip move.l 4(a6),d1 ;d1 = pv->v[1] cmp.l d5,d1 ;if (d1 < r_refdef.vrectbottom) bge.b .fvskip move.l (a1,d1.w*4),a0 lea 0(a0,d0.w*2),a0 ;a0 = zspantable[pv->v[1]] + pv->v[0] move.w 20(a6),d2 ;d2 = pv->v[5]>>16 cmp.w (a0),d2 ;if (d2 >= *zbuf) blt.b .fvskip move.w d2,(a0) ;*zbuf = d2 move.w 12(a6),d0 ;d0 = pv->v[3]>>16 move.l (a2,d0.w*4),a0 ;a0 = skintable[d0] add.w 8(a6),a0 ;a0 = a0[pv->v[2]>>16] move.w 18(a6),d2 ;d2 = pv->v[4] move.b (a0),d2 ;d2 = *a0 + pv->v[4] & 0xFF00 move.l (a3,d1.w*4),d0 ;d0 = d_scantable[pv->v[1]] add.l (a6),d0 ;d0 = d0 + pv->v[0] move.b 0(a4,d2.l),0(a5,d0.l) ;d_viewbuffer[d0] = acolormap[d2] .fvskip rts ****************************************************************************** * * void D_PolysetDrawFinalVertsT (finalvert_t *pv1, finalvert_t *pv2, finalvert_t *pv3) * * translucent entity polygon renderer for very small polygons (1 pixel) * ****************************************************************************** cnop 0,4 _D_PolysetDrawFinalVertsT ***** stackframe rsreset .intregs rs.l 9 rs.l 1 .pv1 rs.l 1 .pv2 rs.l 1 .pv3 rs.l 1 ****** prologue movem.l d2-d5/a2-a6,-(sp) ; TODO d3 is not used lea _zspantable,a1 lea _skintable,a2 lea _d_scantable,a3 move.l _acolormap,a4 move.l _d_viewbuffer,a5 move.l _r_refdef+REFDEF_VRECTRIGHT,d4 move.l _r_refdef+REFDEF_VRECTBOTTOM,d5 moveq #0,d2 move.l .pv1(sp),a6 bsr.b do_PolysetDrawFinalVertsT move.l .pv2(sp),a6 bsr.b do_PolysetDrawFinalVertsT move.l .pv3(sp),a6 bsr.b do_PolysetDrawFinalVertsT movem.l (sp)+,d2-d5/a2-a6 rts cnop 0,4 do_PolysetDrawFinalVertsT move.l (a6),d0 ;d0 = pv->v[0] cmp.l d4,d0 ;if (d0 < r_refdef.vrectright) bge.b .fvskip move.l 4(a6),d1 ;d1 = pv->v[1] cmp.l d5,d1 ;if (d1 < r_refdef.vrectbottom) bge.b .fvskip move.l (a1,d1.w*4),a0 lea 0(a0,d0.w*2),a0 ;a0 = zspantable[pv->v[1]] + pv->v[0] move.w 20(a6),d2 ;d2 = pv->v[5]>>16 cmp.w (a0),d2 ;if (d2 >= *zbuf) blt.b .fvskip ;move.w d2,(a0) ;*zbuf = d2 move.w 12(a6),d0 ;d0 = pv->v[3]>>16 move.l (a2,d0.w*4),a0 ;a0 = skintable[d0] add.w 8(a6),a0 ;a0 = a0[pv->v[2]>>16] move.w 18(a6),d2 ;d2 = pv->v[4] move.b (a0),d2 ;d2 = *a0 + pv->v[4] & 0xFF00 tst.b d2 ;if (color_map_idx != 0) beq.b .fvskip move.l (a3,d1.w*4),d0 ;d0 = d_scantable[pv->v[1]] add.l (a6),d0 ;d0 = d0 + pv->v[0] ;move.b 0(a4,d2.l),0(a5,d0.l) ;d_viewbuffer[d0] = acolormap[d2] move.b 0(a4,d2.l),d2 ;d2 = acolormap[d2] lsl.w #8,d2 move.b 0(a5,d0.l),d2 ;d2 = (pix<<8) + pix2 move.l _mainTransTable,a0 move.b 0(a0,d2.l),0(a5,d0.l) ;d_viewbuffer[d0] = mainTransTable[d2] .fvskip rts ****************************************************************************** * * void D_PolysetDrawFinalVertsT2 (finalvert_t *pv1, finalvert_t *pv2, finalvert_t *pv3) * * transparent entity polygon renderer for very small polygons (1 pixel) * ****************************************************************************** cnop 0,4 _D_PolysetDrawFinalVertsT2 ***** stackframe rsreset .intregs rs.l 9 rs.l 1 .pv1 rs.l 1 .pv2 rs.l 1 .pv3 rs.l 1 ****** prologue movem.l d2-d5/a2-a6,-(sp) ; TODO d3 is not used lea _zspantable,a1 lea _skintable,a2 lea _d_scantable,a3 move.l _acolormap,a4 move.l _d_viewbuffer,a5 move.l _r_refdef+REFDEF_VRECTRIGHT,d4 move.l _r_refdef+REFDEF_VRECTBOTTOM,d5 moveq #0,d2 move.l .pv1(sp),a6 bsr.b do_PolysetDrawFinalVertsT2 move.l .pv2(sp),a6 bsr.b do_PolysetDrawFinalVertsT2 move.l .pv3(sp),a6 bsr.b do_PolysetDrawFinalVertsT2 movem.l (sp)+,d2-d5/a2-a6 rts cnop 0,4 do_PolysetDrawFinalVertsT2 move.l (a6),d0 ;d0 = pv->v[0] cmp.l d4,d0 ;if (d0 < r_refdef.vrectright) bge.b .fvskip move.l 4(a6),d1 ;d1 = pv->v[1] cmp.l d5,d1 ;if (d1 < r_refdef.vrectbottom) bge.b .fvskip move.l (a1,d1.w*4),a0 lea 0(a0,d0.w*2),a0 ;a0 = zspantable[pv->v[1]] + pv->v[0] move.w 20(a6),d2 ;d2 = pv->v[5]>>16 cmp.w (a0),d2 ;if (d2 >= *zbuf) blt.b .fvskip move.w d2,(a0) ;*zbuf = d2 move.w 12(a6),d0 ;d0 = pv->v[3]>>16 move.l (a2,d0.w*4),a0 ;a0 = skintable[d0] add.w 8(a6),a0 ;a0 = a0[pv->v[2]>>16] move.w 18(a6),d2 ;d2 = pv->v[4] move.b (a0),d2 ;d2 = *a0 + pv->v[4] & 0xFF00 tst.b d2 ;if (color_map_idx != 0) beq.b .fvskip move.l (a3,d1.w*4),d0 ;d0 = d_scantable[pv->v[1]] add.l (a6),d0 ;d0 = d0 + pv->v[0] btst #0,d2 ;if (color_map_idx % 2 == 0) bne.b .transluc move.b 0(a4,d2.l),0(a5,d0.l) ;d_viewbuffer[d0] = acolormap[d2] rts .transluc move.b 0(a4,d2.l),d2 ;d2 = acolormap[d2] lsl.w #8,d2 move.b 0(a5,d0.l),d2 ;d2 = (pix<<8) + pix2 move.l _mainTransTable,a0 move.b 0(a0,d2.l),0(a5,d0.l) ;d_viewbuffer[d0] = mainTransTable[d2] .fvskip rts ****************************************************************************** * * void D_PolysetDrawFinalVertsT3 (finalvert_t *pv1, finalvert_t *pv2, finalvert_t *pv3) * * holey entity polygon renderer for very small polygons (1 pixel) * ****************************************************************************** cnop 0,4 _D_PolysetDrawFinalVertsT3 ***** stackframe rsreset .intregs rs.l 9 rs.l 1 .pv1 rs.l 1 .pv2 rs.l 1 .pv3 rs.l 1 ****** prologue movem.l d2-d5/a2-a6,-(sp) ; TODO d3 is not used lea _zspantable,a1 lea _skintable,a2 lea _d_scantable,a3 move.l _acolormap,a4 move.l _d_viewbuffer,a5 move.l _r_refdef+REFDEF_VRECTRIGHT,d4 move.l _r_refdef+REFDEF_VRECTBOTTOM,d5 moveq #0,d2 move.l .pv1(sp),a6 bsr.b do_PolysetDrawFinalVertsT3 move.l .pv2(sp),a6 bsr.b do_PolysetDrawFinalVertsT3 move.l .pv3(sp),a6 bsr.b do_PolysetDrawFinalVertsT3 movem.l (sp)+,d2-d5/a2-a6 rts cnop 0,4 do_PolysetDrawFinalVertsT3 move.l (a6),d0 ;d0 = pv->v[0] cmp.l d4,d0 ;if (d0 < r_refdef.vrectright) bge.b .fvskip move.l 4(a6),d1 ;d1 = pv->v[1] cmp.l d5,d1 ;if (d1 < r_refdef.vrectbottom) bge.b .fvskip move.l (a1,d1.w*4),a0 lea 0(a0,d0.w*2),a0 ;a0 = zspantable[pv->v[1]] + pv->v[0] move.w 20(a6),d2 ;d2 = pv->v[5]>>16 cmp.w (a0),d2 ;if (d2 >= *zbuf) blt.b .fvskip move.w d2,(a0) ;*zbuf = d2 move.w 12(a6),d0 ;d0 = pv->v[3]>>16 move.l (a2,d0.w*4),a0 ;a0 = skintable[d0] add.w 8(a6),a0 ;a0 = a0[pv->v[2]>>16] move.w 18(a6),d2 ;d2 = pv->v[4] move.b (a0),d2 ;d2 = *a0 + pv->v[4] & 0xFF00 tst.b d2 ;if (color_map_idx != 0) beq.b .fvskip move.l (a3,d1.w*4),d0 ;d0 = d_scantable[pv->v[1]] add.l (a6),d0 ;d0 = d0 + pv->v[0] move.b 0(a4,d2.l),0(a5,d0.l) ;d_viewbuffer[d0] = acolormap[d2] .fvskip rts ****************************************************************************** * * void D_PolysetDrawFinalVertsT5 (finalvert_t *pv1, finalvert_t *pv2, finalvert_t *pv3) * * translucent entity polygon renderer for very small polygons (1 pixel) * ****************************************************************************** cnop 0,4 _D_PolysetDrawFinalVertsT5 ***** stackframe rsreset .intregs rs.l 9 rs.l 1 .pv1 rs.l 1 .pv2 rs.l 1 .pv3 rs.l 1 ****** prologue movem.l d2-d5/a2-a6,-(sp) ; TODO d3 and a4 are not used lea _zspantable,a1 lea _skintable,a2 lea _d_scantable,a3 ;move.l _acolormap,a4 move.l _d_viewbuffer,a5 move.l _r_refdef+REFDEF_VRECTRIGHT,d4 move.l _r_refdef+REFDEF_VRECTBOTTOM,d5 moveq #0,d2 move.l .pv1(sp),a6 bsr.b do_PolysetDrawFinalVertsT5 move.l .pv2(sp),a6 bsr.b do_PolysetDrawFinalVertsT5 move.l .pv3(sp),a6 bsr.b do_PolysetDrawFinalVertsT5 movem.l (sp)+,d2-d5/a2-a6 rts cnop 0,4 do_PolysetDrawFinalVertsT5 move.l (a6),d0 ;d0 = pv->v[0] cmp.l d4,d0 ;if (d0 < r_refdef.vrectright) bge.b .fvskip move.l 4(a6),d1 ;d1 = pv->v[1] cmp.l d5,d1 ;if (d1 < r_refdef.vrectbottom) bge.b .fvskip move.l (a1,d1.w*4),a0 lea 0(a0,d0.w*2),a0 ;a0 = zspantable[pv->v[1]] + pv->v[0] move.w 20(a6),d2 ;d2 = pv->v[5]>>16 cmp.w (a0),d2 ;if (d2 >= *zbuf) blt.b .fvskip ;move.w d2,(a0) ;*zbuf = d2 move.w 12(a6),d0 ;d0 = pv->v[3]>>16 move.l (a2,d0.w*4),a0 ;a0 = skintable[d0] add.w 8(a6),a0 ;a0 = a0[pv->v[2]>>16] ;move.w 18(a6),d2 ;d2 = pv->v[4] move.b (a0),d2 ;d2 = *a0 + pv->v[4] & 0xFF00 tst.b d2 ;if (color_map_idx != 0) beq.b .fvskip move.l (a3,d1.w*4),d0 ;d0 = d_scantable[pv->v[1]] add.l (a6),d0 ;d0 = d0 + pv->v[0] ;move.b 0(a4,d2.l),0(a5,d0.l) ;d_viewbuffer[d0] = acolormap[d2] ;move.b 0(a4,d2.l),d2 ;d2 = acolormap[d2] lsl.w #8,d2 move.b 0(a5,d0.l),d2 ;d2 = (pix<<8) + pix2 move.l _transTable,a0 move.b 0(a0,d2.l),0(a5,d0.l) ;d_viewbuffer[d0] = transTable[d2] .fvskip rts engine/h2shared/d_scan.c000066400000000000000000000506001444734033100154220ustar00rootroot00000000000000/* * d_scan.c - Portable C scan-level rasterization code, all pixel depths. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * C versions of several asm functions: Juraj Styk * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" #include "d_local.h" ASM_LINKAGE_BEGIN unsigned char *r_turb_pbase, *r_turb_pdest; fixed16_t r_turb_s, r_turb_t, r_turb_sstep, r_turb_tstep; int *r_turb_turb; int r_turb_spancount; byte scanList[SCAN_SIZE]; int ZScanCount; ASM_LINKAGE_END /* ============= D_WarpScreen // this performs a slight compression of the screen at the same time as // the sine warp, to keep the edges from wrapping ============= */ void D_WarpScreen (void) { int w, h; int u, v; byte *dest; int *turb; int *col; byte **row; byte *rowptr[MAXHEIGHT+(AMP2*2)]; int column[MAXWIDTH+(AMP2*2)]; float wratio, hratio; int scr_width, scr_height; int rowbytes; w = r_refdef.vrect.width; h = r_refdef.vrect.height; wratio = w / (float)scr_vrect.width; hratio = h / (float)scr_vrect.height; for (v = 0; v < scr_vrect.height + AMP2*2; v++) { rowptr[v] = d_viewbuffer + (r_refdef.vrect.y * screenwidth) + (screenwidth * (int)((float)v * hratio * h / (h + AMP2 * 2))); } for (u = 0; u < scr_vrect.width + AMP2*2; u++) { column[u] = r_refdef.vrect.x + (int)((float)u * wratio * w / (w + AMP2 * 2)); } turb = intsintable + ((int)(cl.time*SPEED) & (CYCLE-1)); dest = vid.buffer + scr_vrect.y * vid.rowbytes + scr_vrect.x; scr_width = scr_vrect.width; scr_height = scr_vrect.height; rowbytes = vid.rowbytes; for (v = 0; v < scr_height; v++, dest += rowbytes) { col = &column[turb[v]]; row = &rowptr[v]; for (u = 0; u < scr_width; u += 4) { dest[u+0] = row[turb[u+0]][col[u+0]]; dest[u+1] = row[turb[u+1]][col[u+1]]; dest[u+2] = row[turb[u+2]][col[u+2]]; dest[u+3] = row[turb[u+3]][col[u+3]]; } } } #if !id386 && !id68k /* ============= D_DrawTurbulent8Span ============= */ static void D_DrawTurbulent8Span (void) { int sturb, tturb; do { sturb = ((r_turb_s + r_turb_turb[(r_turb_t>>16)&(CYCLE-1)])>>16)&63; tturb = ((r_turb_t + r_turb_turb[(r_turb_s>>16)&(CYCLE-1)])>>16)&63; *r_turb_pdest++ = *(r_turb_pbase + (tturb<<6) + sturb); r_turb_s += r_turb_sstep; r_turb_t += r_turb_tstep; } while (--r_turb_spancount > 0); } static void D_DrawTurbulent8TSpan (void) { int sturb, tturb; unsigned char temp; do { sturb = ((r_turb_s + r_turb_turb[(r_turb_t>>16)&(CYCLE-1)])>>16)&63; tturb = ((r_turb_t + r_turb_turb[(r_turb_s>>16)&(CYCLE-1)])>>16)&63; if (scanList[r_turb_spancount-1] == 1) { temp = *(r_turb_pbase + (tturb<<6) + sturb); *r_turb_pdest = mainTransTable[(temp<<8) + (*r_turb_pdest)]; r_turb_pdest++; } else r_turb_pdest++; r_turb_s += r_turb_sstep; r_turb_t += r_turb_tstep; } while (--r_turb_spancount > 0); } static void D_DrawTurbulent8TQuickSpan (void) { int sturb, tturb; unsigned char temp; do { sturb = ((r_turb_s + r_turb_turb[(r_turb_t>>16)&(CYCLE-1)])>>16)&63; tturb = ((r_turb_t + r_turb_turb[(r_turb_s>>16)&(CYCLE-1)])>>16)&63; temp = *(r_turb_pbase + (tturb<<6) + sturb); *r_turb_pdest = mainTransTable[(temp<<8) + (*r_turb_pdest)]; r_turb_pdest++; r_turb_s += r_turb_sstep; r_turb_t += r_turb_tstep; } while (--r_turb_spancount > 0); } #endif /* !id386 && !id68k */ #if !id68k static void D_DrawTurbulent8 (espan_t *pspan) { int count; fixed16_t snext, tnext; float sdivz, tdivz, zi, z, du, dv, spancountminus1; float sdivz16stepu, tdivz16stepu, zi16stepu; r_turb_sstep = 0; // keep compiler happy r_turb_tstep = 0; // ditto r_turb_pbase = (unsigned char *)cacheblock; sdivz16stepu = d_sdivzstepu * 16; tdivz16stepu = d_tdivzstepu * 16; zi16stepu = d_zistepu * 16; do { r_turb_pdest = (unsigned char *)((byte *)d_viewbuffer + (screenwidth * pspan->v) + pspan->u); count = pspan->count; // calculate the initial s/z, t/z, 1/z, s, and t and clamp du = (float)pspan->u; dv = (float)pspan->v; sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point r_turb_s = (int)(sdivz * z) + sadjust; if (r_turb_s > bbextents) r_turb_s = bbextents; else if (r_turb_s < 0) r_turb_s = 0; r_turb_t = (int)(tdivz * z) + tadjust; if (r_turb_t > bbextentt) r_turb_t = bbextentt; else if (r_turb_t < 0) r_turb_t = 0; do { // calculate s and t at the far end of the span if (count >= 16) { r_turb_spancount = 16; } else r_turb_spancount = count; count -= r_turb_spancount; if (count) { // calculate s/z, t/z, zi->fixed s and t at far end of span, // calculate s and t steps across span by shifting sdivz += sdivz16stepu; tdivz += tdivz16stepu; zi += zi16stepu; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < 16) snext = 16; // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < 16) tnext = 16; // guard against round-off error on <0 steps r_turb_sstep = (snext - r_turb_s) >> 4; r_turb_tstep = (tnext - r_turb_t) >> 4; } else { // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so // can't step off polygon), clamp, calculate s and t steps across // span by division, biasing steps low so we don't run off the // texture spancountminus1 = (float)(r_turb_spancount - 1); sdivz += d_sdivzstepu * spancountminus1; tdivz += d_tdivzstepu * spancountminus1; zi += d_zistepu * spancountminus1; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < 16) snext = 16; // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < 16) tnext = 16; // guard against round-off error on <0 steps if (r_turb_spancount > 1) { r_turb_sstep = (snext - r_turb_s) / (r_turb_spancount - 1); r_turb_tstep = (tnext - r_turb_t) / (r_turb_spancount - 1); } } r_turb_s = r_turb_s & ((CYCLE<<16)-1); r_turb_t = r_turb_t & ((CYCLE<<16)-1); D_DrawTurbulent8Span (); r_turb_s = snext; r_turb_t = tnext; } while (count > 0); } while ((pspan = pspan->pnext) != NULL); } static void D_DrawTurbulent8T (espan_t *pspan) { int count; fixed16_t snext, tnext; float sdivz, tdivz, zi, z, du, dv, spancountminus1; float sdivz16stepu, tdivz16stepu, zi16stepu; byte origscanList[SCAN_SIZE]; r_turb_sstep = 0; // keep compiler happy r_turb_tstep = 0; // ditto r_turb_pbase = (unsigned char *)cacheblock; sdivz16stepu = d_sdivzstepu * 16; tdivz16stepu = d_tdivzstepu * 16; zi16stepu = d_zistepu * 16; do { r_turb_pdest = (unsigned char *)((byte *)d_viewbuffer + (screenwidth * pspan->v) + pspan->u); count = pspan->count; D_DrawSingleZSpans(pspan); if (ZScanCount == count) // fully blocked continue; if (ZScanCount) memcpy(origscanList,scanList,count); // calculate the initial s/z, t/z, 1/z, s, and t and clamp du = (float)pspan->u; dv = (float)pspan->v; sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point r_turb_s = (int)(sdivz * z) + sadjust; if (r_turb_s > bbextents) r_turb_s = bbextents; else if (r_turb_s < 0) r_turb_s = 0; r_turb_t = (int)(tdivz * z) + tadjust; if (r_turb_t > bbextentt) r_turb_t = bbextentt; else if (r_turb_t < 0) r_turb_t = 0; do { // calculate s and t at the far end of the span if (count >= 16) { r_turb_spancount = 16; } else r_turb_spancount = count; count -= r_turb_spancount; if (count) { // calculate s/z, t/z, zi->fixed s and t at far end of span, // calculate s and t steps across span by shifting sdivz += sdivz16stepu; tdivz += tdivz16stepu; zi += zi16stepu; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < 16) snext = 16; // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < 16) tnext = 16; // guard against round-off error on <0 steps r_turb_sstep = (snext - r_turb_s) >> 4; r_turb_tstep = (tnext - r_turb_t) >> 4; } else { // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so // can't step off polygon), clamp, calculate s and t steps across // span by division, biasing steps low so we don't run off the // texture spancountminus1 = (float)(r_turb_spancount - 1); sdivz += d_sdivzstepu * spancountminus1; tdivz += d_tdivzstepu * spancountminus1; zi += d_zistepu * spancountminus1; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < 16) snext = 16; // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < 16) tnext = 16; // guard against round-off error on <0 steps if (r_turb_spancount > 1) { r_turb_sstep = (snext - r_turb_s) / (r_turb_spancount - 1); r_turb_tstep = (tnext - r_turb_t) / (r_turb_spancount - 1); } } r_turb_s = r_turb_s & ((CYCLE<<16)-1); r_turb_t = r_turb_t & ((CYCLE<<16)-1); if (ZScanCount) { memcpy(scanList,&origscanList[count],r_turb_spancount); D_DrawTurbulent8TSpan (); } else D_DrawTurbulent8TQuickSpan (); r_turb_s = snext; r_turb_t = tnext; } while (count > 0); } while ((pspan = pspan->pnext) != NULL); } #endif /* !id68k */ /* ============= Turbulent8 ============= */ void Turbulent8 (surf_t *s) { r_turb_turb = sintable + ((int)(cl.time*SPEED)&(CYCLE-1)); if (r_transwater.integer && s->flags & SURF_TRANSLUCENT) D_DrawTurbulent8T(s->spans); else D_DrawTurbulent8(s->spans); } #if !id386 && !id68k /* ============= D_DrawSpans8 ============= */ void D_DrawSpans8 (espan_t *pspan) { int count, spancount; unsigned char *pbase, *pdest; fixed16_t s, t, snext, tnext, sstep, tstep; float sdivz, tdivz, zi, z, du, dv, spancountminus1; float sdivz8stepu, tdivz8stepu, zi8stepu; sstep = 0; // keep compiler happy tstep = 0; // ditto pbase = (unsigned char *)cacheblock; sdivz8stepu = d_sdivzstepu * 8; tdivz8stepu = d_tdivzstepu * 8; zi8stepu = d_zistepu * 8; do { pdest = (unsigned char *)((byte *)d_viewbuffer + (screenwidth * pspan->v) + pspan->u); count = pspan->count; // calculate the initial s/z, t/z, 1/z, s, and t and clamp du = (float)pspan->u; dv = (float)pspan->v; sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point s = (int)(sdivz * z) + sadjust; if (s > bbextents) s = bbextents; else if (s < 0) s = 0; t = (int)(tdivz * z) + tadjust; if (t > bbextentt) t = bbextentt; else if (t < 0) t = 0; do { // calculate s and t at the far end of the span if (count >= 8) spancount = 8; else spancount = count; count -= spancount; if (count) { // calculate s/z, t/z, zi->fixed s and t at far end of span, // calculate s and t steps across span by shifting sdivz += sdivz8stepu; tdivz += tdivz8stepu; zi += zi8stepu; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < 8) snext = 8; // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < 8) tnext = 8; // guard against round-off error on <0 steps sstep = (snext - s) >> 3; tstep = (tnext - t) >> 3; } else { // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so // can't step off polygon), clamp, calculate s and t steps across // span by division, biasing steps low so we don't run off the // texture spancountminus1 = (float)(spancount - 1); sdivz += d_sdivzstepu * spancountminus1; tdivz += d_tdivzstepu * spancountminus1; zi += d_zistepu * spancountminus1; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < 8) snext = 8; // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < 8) tnext = 8; // guard against round-off error on <0 steps if (spancount > 1) { sstep = (snext - s) / (spancount - 1); tstep = (tnext - t) / (spancount - 1); } } do { *pdest++ = *(pbase + (s >> 16) + (t >> 16) * cachewidth); s += sstep; t += tstep; } while (--spancount > 0); s = snext; t = tnext; } while (count > 0); } while ((pspan = pspan->pnext) != NULL); } void D_DrawSpans8T (espan_t *pspan) { int count, spancount; unsigned char *pbase, *pdest; fixed16_t s, t, snext, tnext, sstep, tstep; float sdivz, tdivz, zi, z, du, dv, spancountminus1; float sdivz8stepu, tdivz8stepu, zi8stepu; short *pz; byte btemp; int izi, izistep; sstep = 0; // keep compiler happy tstep = 0; // ditto pbase = (unsigned char *)cacheblock; sdivz8stepu = d_sdivzstepu * 8; tdivz8stepu = d_tdivzstepu * 8; zi8stepu = d_zistepu * 8; izistep = (int)(d_zistepu * 0x8000 * 0x10000); do { pdest = (unsigned char *)((byte *)d_viewbuffer + (screenwidth * pspan->v) + pspan->u); pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; count = pspan->count; // calculate the initial s/z, t/z, 1/z, s, and t and clamp du = (float)pspan->u; dv = (float)pspan->v; sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point izi = (int)(zi * 0x8000 * 0x10000); s = (int)(sdivz * z) + sadjust; if (s > bbextents) s = bbextents; else if (s < 0) s = 0; t = (int)(tdivz * z) + tadjust; if (t > bbextentt) t = bbextentt; else if (t < 0) t = 0; do { // calculate s and t at the far end of the span if (count >= 8) spancount = 8; else spancount = count; count -= spancount; if (count) { // calculate s/z, t/z, zi->fixed s and t at far end of span, // calculate s and t steps across span by shifting sdivz += sdivz8stepu; tdivz += tdivz8stepu; zi += zi8stepu; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < 8) snext = 8; // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < 8) tnext = 8; // guard against round-off error on <0 steps sstep = (snext - s) >> 3; tstep = (tnext - t) >> 3; } else { // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so // can't step off polygon), clamp, calculate s and t steps across // span by division, biasing steps low so we don't run off the // texture spancountminus1 = (float)(spancount - 1); sdivz += d_sdivzstepu * spancountminus1; tdivz += d_tdivzstepu * spancountminus1; zi += d_zistepu * spancountminus1; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < 8) snext = 8; // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < 8) tnext = 8; // guard against round-off error on <0 steps if (spancount > 1) { sstep = (snext - s) / (spancount - 1); tstep = (tnext - t) / (spancount - 1); } } do { btemp = *(pbase + (s >> 16) + (t >> 16) * cachewidth); if (*pz <= (izi >> 16)) { *pdest = mainTransTable[(btemp<<8) + (*pdest)]; } izi += izistep; pdest++; pz++; // *pdest++ = *(pbase + (s >> 16) + (t >> 16) * cachewidth); s += sstep; t += tstep; } while (--spancount > 0); s = snext; t = tnext; } while (count > 0); } while ((pspan = pspan->pnext) != NULL); } /* ============= D_DrawZSpans ============= */ void D_DrawZSpans (espan_t *pspan) { int count, doublecount, izistep; int izi; short *pdest; unsigned int ltemp; double zi; float du, dv; // FIXME: check for clamping/range problems // we count on FP exceptions being turned off to avoid range problems izistep = (int)(d_zistepu * 0x8000 * 0x10000); do { pdest = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; count = pspan->count; // calculate the initial 1/z du = (float)pspan->u; dv = (float)pspan->v; zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; // we count on FP exceptions being turned off to avoid range problems izi = (int)(zi * 0x8000 * 0x10000); if ((intptr_t)pdest & 0x02) { *pdest++ = (short)(izi >> 16); izi += izistep; count--; } if ((doublecount = count >> 1) > 0) { do { ltemp = izi >> 16; izi += izistep; ltemp |= izi & 0xFFFF0000; izi += izistep; *(int *)pdest = ltemp; pdest += 2; } while (--doublecount > 0); } if (count & 1) *pdest = (short)(izi >> 16); } while ((pspan = pspan->pnext) != NULL); } /* ============= D_DrawSingleZSpans ============= */ void D_DrawSingleZSpans (espan_t *pspan) { int count, izistep; int izi; short *pdest; float zi; float du, dv; ZScanCount = 0; // FIXME: check for clamping/range problems // we count on FP exceptions being turned off to avoid range problems izistep = (int)(d_zistepu * 0x8000 * 0x10000); pdest = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; count = pspan->count; // calculate the initial 1/z du = (float)pspan->u; dv = (float)pspan->v; zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; // we count on FP exceptions being turned off to avoid range problems izi = (int)(zi * 0x8000 * 0x10000); if (count > 0) { do { if (*pdest > (short)(izi >> 16)) { ZScanCount++; scanList[count-1] = 0; } else { scanList[count-1] = 1; *pdest = (short)(izi >> 16); } izi += izistep; pdest++; count--; } while (count > 0); } } #endif /* !id386 && !id68k */ engine/h2shared/d_scan68k.s000066400000000000000000003226611444734033100160040ustar00rootroot00000000000000** ** Quake for AMIGA ** d_scan.c assembler implementations by Frank Wille ** Translucent version for Hexen II by Szilard Biro ** ; INCLUDE "quakedef68k.i" SPAN_PNEXT equ 12 XREF _cacheblock XREF _d_sdivzorigin XREF _d_sdivzstepu XREF _d_sdivzstepv XREF _d_tdivzorigin XREF _d_tdivzstepu XREF _d_tdivzstepv XREF _d_ziorigin XREF _d_zistepu XREF _d_zistepv XREF _sadjust XREF _tadjust XREF _bbextents XREF _bbextentt XREF _d_viewbuffer XREF _screenwidth XREF _cachewidth XREF _d_zwidth XREF _d_pzbuffer ;XREF _sintable XREF _r_turb_turb XREF _mainTransTable ;XREF _scanList ;XREF _ZScanCount XDEF _D_DrawTurbulent8 XDEF _D_DrawTurbulent8T XDEF _D_DrawSpans8 XDEF _D_DrawSpans16 XDEF _D_DrawSpans16T XDEF _D_DrawZSpans QDIV = 1 NICE_DIV = 1 CYCLE = 128 ;MUST match the #define in d_iface.h! AMP2 = 3 ;-- SPEED = 20 ;-- SCAN_SIZE = 2048 ;MUST match the #define in d_local.h! ****************************************************************************** * * void D_DrawTurbulent8 (espan_t *pspan) * * standard scan drawing function for animated textures * Note: The function D_DrawTurbulent8Span was inlined into this * function, because it's never used anywhere else. * This function expects r_turb_turb to be set by Turbulent8. * ****************************************************************************** cnop 0,4 _D_DrawTurbulent8 ***** stackframe rsreset .saved4 rs.l 1 .saved5 rs.l 1 .savea1 rs.l 1 .szstpu rs.s 1 .szstpv rs.s 1 .szorg rs.s 1 .tzstpu rs.s 1 .tzstpv rs.s 1 .tzorg rs.s 1 .zistpu rs.s 1 .zistpv rs.s 1 .ziorg rs.s 1 .fpuregs rs.x 6 .intregs rs.l 11 rs.l 1 .pspan rs.l 1 ****** Prologue. Global variables are put into registers or onto the stackframe movem.l d2-d7/a2-a6,-(sp) fmovem.x fp2-fp7,-(sp) move.l _bbextentt,a2 move.l _tadjust,a3 move.l _bbextents,a4 move.l _sadjust,a5 move.l _d_ziorigin,-(sp) move.l _d_zistepv,-(sp) move.l _d_zistepu,-(sp) move.l _d_tdivzorigin,-(sp) move.l _d_tdivzstepv,-(sp) move.l _d_tdivzstepu,-(sp) move.l _d_sdivzorigin,-(sp) move.l _d_sdivzstepv,-(sp) move.l _d_sdivzstepu,-(sp) sub.l #.szstpu,sp ****** First loop. In every iteration one complete span is drawn * r_turb_turb = sintable + ((int)(cl.time*SPEED)&(CYCLE-1)); * * r_turb_pbase = (unsigned char *)cacheblock; * * sdivz16stepu = d_sdivzstepu * 16; * tdivz16stepu = d_tdivzstepu * 16; * zi16stepu = d_zistepu * 16; * * do * { * r_turb_pdest = (unsigned char *)((byte *)d_viewbuffer + * (screenwidth * pspan->v) + pspan->u); * * count = pspan->count; * * // calculate the initial s/z, t/z, 1/z, s, and t and clamp * du = (float)pspan->u; * dv = (float)pspan->v; * * sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; * tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; * zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; * z = (float)0x10000 / zi; // prescale to 16.16 fixed-point * ;fmove.d _cl+CL_TIME,fp0 ;get cl.time ;fmul.s #SPEED,fp0 ;fp0 = cl.time*SPEED ;fmove.l fp0,d0 ;(int)(cl.time*SPEED) ;and.l #CYCLE-1,d0 ;(int)(cl.time*SPEED)&(CYCLE-1) ;lsl.l #2,d0 ;add.l #_sintable,d0 ;r_turb_turb = _sintable + 4*d0 ;move.l d0,a6 move.l _r_turb_turb,a6 fmove.s #16,fp7 fmove.s .szstpu(sp),fp3 fmul fp7,fp3 ;sdivz16stepu = d_sdivzstepu * 16 fmove.s .tzstpu(sp),fp4 fmul fp7,fp4 ;tdivz16stepu = d_tdivzstepu * 16 fmove.s .zistpu(sp),fp5 fmul fp7,fp5 ;zi16stepu = d_zistepu * 16 move.l .pspan(sp),a1 ;get function parameter .loop move.l a1,.savea1(sp) ;save actual ptr to pspan move.l _d_viewbuffer,a0 move.l _screenwidth,d0 move.l (a1)+,d1 fmove.l d1,fp2 ;du = (float)pspan->u move.l (a1)+,d2 fmove.l d2,fp7 ;dv = (float)pspan->v move.l (a1)+,d4 muls d2,d0 ;d0 = screenwidth * pspan->v add.l d1,d0 add.l d0,a0 ;pdest = d_viewbuffer + pspan->u + d0 lea .szstpu(sp),a1 ;a1 -> stackframe fmove.s (a1)+,fp0 fmul fp2,fp0 ;fp0 = du * d_sdivzstepu fmove.s (a1)+,fp1 fmul fp7,fp1 ;fp1 = dv * d_sdivzstepv fadd fp1,fp0 fadd.s (a1)+,fp0 ;sdivz = d_sdivzorigin + fp0 + fp1 fmove.s (a1)+,fp1 fmul fp2,fp1 ;fp1 = du * d_tdivzstepu fmove.s (a1)+,fp6 fmul fp7,fp6 ;fp6 = dv * d_tdivzstepv fadd fp6,fp1 fadd.s (a1)+,fp1 ;tdivz = d_tdivzorigin + fp1 + fp6 fmul.s (a1)+,fp2 ;fp2 = du * d_zistepu fmul.s (a1)+,fp7 ;fp7 = dv * d_zistepv fadd fp7,fp2 fadd.s (a1)+,fp2 ;zi = d_ziorigin + fp2 + fp7 fmove.s #65536,fp6 fdiv fp2,fp6 ;z = (float)0x10000 / zi * s = (int)(sdivz * z) + sadjust; * if (s > bbextents) * s = bbextents; * else if (s < 0) * s = 0; * * t = (int)(tdivz * z) + tadjust; * if (t > bbextentt) * t = bbextentt; * else if (t < 0) * t = 0; fmove fp6,fp7 fmul fp0,fp7 ;fp7 = sdivz * z fmove.l fp7,d6 ;convert to integer add.l a5,d6 ;s = d6 + sadjust cmp.l a4,d6 ;if (s > bbextents) bgt.b .down tst.l d6 ;if (s < 0) bge.b .keep .up moveq #0,d6 ;s = 0 bra.b .keep .down move.l a4,d6 ;s = bbextents .keep fmul fp1,fp6 ;fp6 = tdivz * z fmove.l fp6,d7 ;convert to integer add.l a3,d7 ;t = d7 + tadjust cmp.l a2,d7 ;if (t > bbextentt) bgt.b .down2 tst.l d7 ;if (t < 0) bge.b .keep2 .up2 moveq #0,d7 ;t = 0 bra.b .keep2 .down2 move.l a2,d7 ;t = bbextentt .keep2 move.l d4,d1 ****** Second loop. In every iteration one part of the whole span is drawn ****** d2 gets the value (spancount-1)! [NOT spancount] ****** d1 = count * do * { * // calculate s and t at the far end of the span * if (count >= 16) * spancount = 16; * else * spancount = count; * * count -= spancount; * * if (count) * { .loop2 moveq #16-1,d2 ;spancount = 16 cmp.l #16,d1 ;if (count >= 16) bgt.b .cont move.l d1,d2 ;spancount = count subq.l #1,d2 moveq #0,d1 ;count -= spancount bra.w .finalpart .cont sub.l #16,d1 ;count -= spancount; ****** Evaluation of the values for the inner loop. This version is used for ****** span size = 16 ****** a2 : bbextentt ****** a3 : tadjust ****** a4 : bbextents ****** a5 : sadjust ****** fp0 : sdivz ****** fp1 : tdivz ****** fp2 : zi ****** fp3 : sdivz16stepu ****** fp4 : tdivz16stepu ****** fp5 : zi16stepu * // calculate s/z, t/z, zi->fixed s and t at far end of span, * // calculate s and t steps across span by shifting * sdivz += sdivz16stepu; * tdivz += tdivz16stepu; * zi += zi16stepu; * z = (float)0x10000 / zi; // prescale to 16.16 fixed-point * * snext = (int)(sdivz * z) + sadjust; * if (snext > bbextents) * snext = bbextents; * else if (snext < 16) * snext = 16; // prevent round-off error on <0 steps from * // from causing overstepping & running off the * // edge of the texture * * tnext = (int)(tdivz * z) + tadjust; * if (tnext > bbextentt) * tnext = bbextentt; * else if (tnext < 16) * tnext = 16; // guard against round-off error on <0 steps * * r_turb_sstep = (snext - r_turb_s) >> 4; * r_turb_tstep = (tnext - r_turb_t) >> 4; * } fadd fp3,fp0 ;sdivz += sdivz16stepu fadd fp4,fp1 ;tdivz += tdivz16stepu fadd fp5,fp2 ;zi += zi16stepu fmove.s #65536,fp7 fdiv fp2,fp7 ;z = (float)0x10000 / zi; fmove fp7,fp6 fmul fp0,fp6 ;fp2 = sdivz * z fmove.l fp6,d4 ;convert to integer add.l a5,d4 ;snext = d4 + sadjust cmp.l a4,d4 ;if (snext > bbextents) bgt.b .down3 cmp.l #16,d4 ;if (snext < 16) bge.b .keep3 .up3 moveq #16,d4 ;snext = 16 bra.b .keep3 .down3 move.l a4,d4 ;snext = bbextents .keep3 fmul fp1,fp7 ;fp7 = tdivz * z fmove.l fp7,d5 ;convert to integer add.l a3,d5 ;tnext = d5 + tadjust cmp.l a2,d5 ;if (tnext > bbextentt) bgt.b .down4 cmp.l #16,d5 ;if (tnext < 16) bge.b .keep4 .up4 moveq #16,d5 ;tnext = 16 bra.b .keep4 .down4 move.l a2,d5 ;tnext = bbextentt .keep4 move.l d4,.saved4(sp) ;save snext move.l d5,.saved5(sp) ;save tnext sub.l d6,d4 ;d4 = snext - s sub.l d7,d5 ;d5 = tnext - t asr.l #4,d4 ;r_turb_sstep = d4 >> 4 asr.l #4,d5 ;r_turb_tstep = d5 >> 4 bra.w .mainloop ****** Evaluation of the values for the inner loop. This version is used for ****** span size < 16 ****** The original algorithm has two ugly divisions at the end of this part. ****** These are removed by the following optimization: ****** First, the divisors 1,2 and 4 are handled specially to gain speed. The ****** other divisors are handled using a reciprocal table. ****** a2 : bbextentt ****** a3 : tadjust ****** a4 : bbextents ****** a5 : sadjust ****** fp0 : sdivz ****** fp1 : tdivz ****** fp2 : zi * // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so * // can't step off polygon), clamp, calculate s and t steps across * // span by division, biasing steps low so we don't run off the * // texture * spancountminus1 = (float)(r_turb_spancount - 1); * sdivz += d_sdivzstepu * spancountminus1; * tdivz += d_tdivzstepu * spancountminus1; * zi += d_zistepu * spancountminus1; * z = (float)0x10000 / zi; // prescale to 16.16 fixed-point * snext = (int)(sdivz * z) + sadjust; * if (snext > bbextents) * snext = bbextents; * else if (snext < 16) * snext = 16; // prevent round-off error on <0 steps from * // from causing overstepping & running off the * // edge of the texture * * tnext = (int)(tdivz * z) + tadjust; * if (tnext > bbextentt) * tnext = bbextentt; * else if (tnext < 16) * tnext = 16; // guard against round-off error on <0 steps * * if (r_turb_spancount > 1) * { * r_turb_sstep = (snext - r_turb_s) / (r_turb_spancount - 1); * r_turb_tstep = (tnext - r_turb_t) / (r_turb_spancount - 1); * } * } .finalpart fmove.l d2,fp7 ;spancountminus1 = (float)(r_turb_spancount-1) fmove fp7,fp6 fmul.s .szstpu(sp),fp6 ;fp6 = d_sdivzstepu * spancountminus1 fadd fp6,fp0 ;sdivz += fp6 fmove fp7,fp6 fmul.s .tzstpu(sp),fp6 ;fp6 = d_tdivzstepu * spancountminus1 fadd fp6,fp1 ;tdivz += fp6 fmul.s .zistpu(sp),fp7 ;fp7 = d_zistepu * spancountminus1 fadd fp7,fp2 ;zi += fp7 fmove.s #65536,fp7 fdiv fp2,fp7 ;z = (float)0x10000 / zi; fmove fp7,fp6 fmul fp0,fp6 ;fp6 = sdivz * z fmove.l fp6,d4 ;convert to integer add.l a5,d4 ;snext = d4 + sadjust cmp.l a4,d4 ;if (snext > bbextents) bgt.b .down5 cmp.l #16,d4 ;if (snext < 16) bge.b .keep5 .up5 moveq #16,d4 ;snext = 16 bra.b .keep5 .down5 move.l a4,d4 ;snext = bbextents .keep5 fmul fp1,fp7 ;fp7 = tdivz * z fmove.l fp7,d5 ;convert to integer add.l a3,d5 ;tnext = d5 + tadjust cmp.l a2,d5 ;if (tnext > bbextentt) bgt.b .down6 cmp.l #16,d5 ;if (tnext < 16) bge.b .keep6 .up6 moveq #16,d5 ;tnext = 16 bra.b .keep6 .down6 move.l a2,d5 ;tnext = bbextentt .keep6 move.l d4,.saved4(sp) ;save snext move.l d5,.saved5(sp) ;save tnext sub.l d6,d4 ;d4 = snext - r_turb_s sub.l d7,d5 ;d5 = tnext - r_turb_t IFEQ QDIV tst.l d2 beq.w .mainloop divs.l d2,d4 divs.l d2,d5 ELSEIF cmp #5,d2 ;(r_turb_spancount-1) < 5? blt.b .special ;yes -> special case cmp #8,d2 beq.b .spec_8 .qdiv IFNE NICE_DIV lsl.l #2,d4 lsl.l #2,d5 lea ReciprocTable,a1 move 0(a1,d2.w*2),d0 move.l d4,d3 mulu d0,d3 clr d3 swap d3 swap d4 muls d0,d4 add.l d3,d4 move.l d5,d3 mulu d0,d3 clr d3 swap d3 swap d5 muls d0,d5 add.l d3,d5 bra.b .mainloop ELSEIF asr.l #7,d4 ;d4 >> 7 asr.l #7,d5 ;d5 >> 7 lea ReciprocTable,a1 ;a1 -> reciprocal table move 0(a1,d2.w*2),d0 ;d0 = (1/(r_turb_spancount-1))<<16 muls d0,d4 ;d4 = d4 / (r_turb_spancount-1) asr.l #7,d4 ;sstep = d4 >> 7 muls d0,d5 ;d5 = d5 / (r_turb_spancount-1) asr.l #7,d5 ;tstep = d5 >> 7 bra.b .mainloop ENDC .special cmp #1,d2 ;switch (r_turb_spancount-1) ble.b .mainloop ;0,1 -> no scaling needed cmp #3,d2 ;3 -> standard qdiv beq.b .qdiv blt.b .spec_2 asr.l #2,d4 ;4 -> scale by shifting right asr.l #2,d5 bra.b .mainloop .spec_8 asr.l #3,d4 ;8 -> scale by shifting right asr.l #3,d5 bra.b .mainloop .spec_2 asr.l #1,d4 ;2 -> scale by shifting right asr.l #1,d5 ENDC ****** D_DrawTurbulent8Span (inlined) ****** Main drawing loop. ****** d2 : r_turb_spancount ****** d4 : r_turb_sstep ****** d5 : r_turb_tstep ****** d6 : r_turb_s ****** d7 : r_turb_t ****** a0 : r_turb_pdest ****** a6 : r_turb_turb * do * { * sturb = ((r_turb_s + r_turb_turb[(r_turb_t>>16)&(CYCLE-1)])>>16)&63; * tturb = ((r_turb_t + r_turb_turb[(r_turb_s>>16)&(CYCLE-1)])>>16)&63; * *r_turb_pdest++ = *(r_turb_pbase + (tturb<<6) + sturb); * r_turb_s += r_turb_sstep; * r_turb_t += r_turb_tstep; * } while (--r_turb_spancount > 0); .mainloop move.l d1,-(sp) move.l _cacheblock,a1 ;pbase = (unsigned char *)cacheblock moveq #10,d1 .draw swap d6 ;r_turb_s >> 16 swap d7 ;r_turb_t >> 16 and #CYCLE-1,d6 ;(r_turb_s >> 16) & (CYCLE-1) and #CYCLE-1,d7 ;(r_turb_t >> 16) & (CYCLE-1) move.l 0(a6,d7.w*4),d0 ;r_turb_turb [d7] move.l 0(a6,d6.w*4),d3 ;r_turb_turb [d6] swap d6 swap d7 add.l d6,d0 ;r_turb_s + r_turb_turb [] add.l d7,d3 ;r_turb_t + r_turb_turb [] swap d0 ;d0 >> 16 and.l #$3f,d0 ;sturb = (d0 >> 16) & 63 lsr.l d1,d3 ;(d3 >> (16-6)) and.l #$fc0,d3 ;tturb<<6 = (d3 >> (16-6)) & (63 << 6) add.l d3,d0 ;sturb + tturb << 6 move.b 0(a1,d0.l),(a0)+ ;*r_turb_pdest++ = *(r_turb_pbase + d0) add.l d4,d6 ;r_turb_s += r_turb_sstep add.l d5,d7 ;r_turb_t += r_turb_tstep dbra d2,.draw ;while (--r_turb_spancount > 0) move.l (sp)+,d1 ****** loop terminations move.l .saved5(sp),d7 ;r_turb_t = tnext move.l .saved4(sp),d6 ;r_turb_s = snext tst.l d1 ;while (count > 0) bgt.w .loop2 move.l .savea1(sp),a1 ;while ((pspan = pspan->next) != NULL) move.l SPAN_PNEXT(a1),a1 tst.l a1 bne.w .loop add.l #.fpuregs,sp fmovem.x (sp)+,fp2-fp7 movem.l (sp)+,d2-d7/a2-a6 rts ****************************************************************************** * * void D_DrawTurbulent8T (espan_t *pspan) * * translucent scan drawing function for animated textures * Note: The functions D_DrawSingleZSpans, D_DrawTurbulent8TSpan * and D_DrawTurbulent8TQuickSpan were inlined into this * function, because they are never used anywhere else. * This function expects r_turb_turb to be set by the caller. * ****************************************************************************** cnop 0,4 _D_DrawTurbulent8T ***** stackframe rsreset ;.origscanList rs.b SCAN_SIZE ;.ZScanCount rs.l 1 .izistep rs.l 1 .izi rs.l 1 .pz rs.l 1 .savearea rs.l 5 .saved4 rs.l 1 .saved5 rs.l 1 .savea1 rs.l 1 .szstpu rs.s 1 .szstpv rs.s 1 .szorg rs.s 1 .tzstpu rs.s 1 .tzstpv rs.s 1 .tzorg rs.s 1 .zistpu rs.s 1 .zistpv rs.s 1 .ziorg rs.s 1 .fpuregs rs.x 6 .intregs rs.l 11 rs.l 1 .pspan rs.l 1 ****** Prologue. Global variables are put into registers or onto the stackframe movem.l d2-d7/a2-a6,-(sp) fmovem.x fp2-fp7,-(sp) move.l _bbextentt,a2 move.l _tadjust,a3 move.l _bbextents,a4 move.l _sadjust,a5 move.l _d_ziorigin,-(sp) move.l _d_zistepv,-(sp) move.l _d_zistepu,-(sp) move.l _d_tdivzorigin,-(sp) move.l _d_tdivzstepv,-(sp) move.l _d_tdivzstepu,-(sp) move.l _d_sdivzorigin,-(sp) move.l _d_sdivzstepv,-(sp) move.l _d_sdivzstepu,-(sp) sub.l #.szstpu,sp ****** First loop. In every iteration one complete span is drawn * r_turb_turb = sintable + ((int)(cl.time*SPEED)&(CYCLE-1)); * * r_turb_pbase = (unsigned char *)cacheblock; * * sdivz16stepu = d_sdivzstepu * 16; * tdivz16stepu = d_tdivzstepu * 16; * izistep = (int)(d_zistepu * 0x8000 * 0x10000); * zi16stepu = d_zistepu * 16; * * do * { * pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; * r_turb_pdest = (unsigned char *)((byte *)d_viewbuffer + * (screenwidth * pspan->v) + pspan->u); * * count = pspan->count; * * // calculate the initial s/z, t/z, 1/z, s, and t and clamp * du = (float)pspan->u; * dv = (float)pspan->v; * * sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; * tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; * zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; * izi = (int)(zi * 0x8000 * 0x10000); * z = (float)0x10000 / zi; // prescale to 16.16 fixed-point * ;fmove.d _cl+CL_TIME,fp0 ;get cl.time ;fmul.s #SPEED,fp0 ;fp0 = cl.time*SPEED ;fmove.l fp0,d0 ;(int)(cl.time*SPEED) ;and.l #CYCLE-1,d0 ;(int)(cl.time*SPEED)&(CYCLE-1) ;and.l #CYCLE-1,d0 ;(int)(cl.time*SPEED)&(CYCLE-1) ;lsl.l #2,d0 ;add.l #_sintable,d0 ;r_turb_turb = _sintable + 4*d0 ;move.l d0,a6 move.l _r_turb_turb,a6 fmove.s #16,fp7 fmove.s .szstpu(sp),fp3 fmul fp7,fp3 ;sdivz16stepu = d_sdivzstepu * 16 fmove.s .tzstpu(sp),fp4 fmul fp7,fp4 ;tdivz16stepu = d_tdivzstepu * 16 fmove.s .zistpu(sp),fp5 ; translucent fmove.s #32768*65536,fp0 fmul fp5,fp0 ;izistep = (int)(d_zistepu * 0x8000 * 0x10000) fmove.l fp0,d3 ;d3 = izistep move.l d3,.izistep(sp) ; translucent fmul fp7,fp5 ;zi16stepu = d_zistepu * 16 move.l .pspan(sp),a1 ;get function parameter .loop move.l a1,.savea1(sp) ;save actual ptr to pspan move.l (a1)+,d1 fmove.l d1,fp2 ;du = (float)pspan->u move.l (a1)+,d2 fmove.l d2,fp7 ;dv = (float)pspan->v move.l (a1)+,d4 ;d4 = pspan->count ; translucent move.l _d_zwidth,d7 muls d2,d7 ;d7 = pspan->v * d_zwidth add.l d1,d7 ;d7 = d7 + pspan->u move.l _d_pzbuffer,a0 lea 0(a0,d7.l*2),a1 ;a1 = pz move.l a1,.pz(sp) ; translucent move.l _d_viewbuffer,a0 move.l _screenwidth,d0 muls d2,d0 ;d0 = screenwidth * pspan->v add.l d1,d0 add.l d0,a0 ;pdest = d_viewbuffer + pspan->u + d0 lea .szstpu(sp),a1 ;a1 -> stackframe fmove.s (a1)+,fp0 fmul fp2,fp0 ;fp0 = du * d_sdivzstepu fmove.s (a1)+,fp1 fmul fp7,fp1 ;fp1 = dv * d_sdivzstepv fadd fp1,fp0 fadd.s (a1)+,fp0 ;sdivz = d_sdivzorigin + fp0 + fp1 fmove.s (a1)+,fp1 fmul fp2,fp1 ;fp1 = du * d_tdivzstepu fmove.s (a1)+,fp6 fmul fp7,fp6 ;fp6 = dv * d_tdivzstepv fadd fp6,fp1 fadd.s (a1)+,fp1 ;tdivz = d_tdivzorigin + fp1 + fp6 fmul.s (a1)+,fp2 ;fp2 = du * d_zistepu fmul.s (a1)+,fp7 ;fp7 = dv * d_zistepv fadd fp7,fp2 fadd.s (a1)+,fp2 ;zi = d_ziorigin + fp2 + fp7 ; translucent fmove.s #32768*65536,fp6 fmul fp2,fp6 ;izi = zi * $8000 * $10000 fmove.l fp6,d5 ;d5 = izi move.l d5,.izi(sp) ; translucent fmove.s #65536,fp6 fdiv fp2,fp6 ;z = (float)0x10000 / zi * s = (int)(sdivz * z) + sadjust; * if (s > bbextents) * s = bbextents; * else if (s < 0) * s = 0; * * t = (int)(tdivz * z) + tadjust; * if (t > bbextentt) * t = bbextentt; * else if (t < 0) * t = 0; fmove fp6,fp7 fmul fp0,fp7 ;fp7 = sdivz * z fmove.l fp7,d6 ;convert to integer add.l a5,d6 ;s = d6 + sadjust cmp.l a4,d6 ;if (s > bbextents) bgt.b .down tst.l d6 ;if (s < 0) bge.b .keep .up moveq #0,d6 ;s = 0 bra.b .keep .down move.l a4,d6 ;s = bbextents .keep fmul fp1,fp6 ;fp6 = tdivz * z fmove.l fp6,d7 ;convert to integer add.l a3,d7 ;t = d7 + tadjust cmp.l a2,d7 ;if (t > bbextentt) bgt.b .down2 tst.l d7 ;if (t < 0) bge.b .keep2 .up2 moveq #0,d7 ;t = 0 bra.b .keep2 .down2 move.l a2,d7 ;t = bbextentt .keep2 move.l d4,d1 ****** Second loop. In every iteration one part of the whole span is drawn ****** d2 gets the value (spancount-1)! [NOT spancount] ****** d1 = count * do * { * // calculate s and t at the far end of the span * if (count >= 16) * spancount = 16; * else * spancount = count; * * count -= spancount; * * if (count) * { .loop2 moveq #16-1,d2 ;spancount = 16 cmp.l #16,d1 ;if (count >= 16) bgt.b .cont move.l d1,d2 ;spancount = count subq.l #1,d2 moveq #0,d1 ;count -= spancount bra.w .finalpart .cont sub.l #16,d1 ;count -= spancount; ****** Evaluation of the values for the inner loop. This version is used for ****** span size = 16 ****** a2 : bbextentt ****** a3 : tadjust ****** a4 : bbextents ****** a5 : sadjust ****** fp0 : sdivz ****** fp1 : tdivz ****** fp2 : zi ****** fp3 : sdivz16stepu ****** fp4 : tdivz16stepu ****** fp5 : zi16stepu * // calculate s/z, t/z, zi->fixed s and t at far end of span, * // calculate s and t steps across span by shifting * sdivz += sdivz16stepu; * tdivz += tdivz16stepu; * zi += zi16stepu; * z = (float)0x10000 / zi; // prescale to 16.16 fixed-point * * snext = (int)(sdivz * z) + sadjust; * if (snext > bbextents) * snext = bbextents; * else if (snext < 16) * snext = 16; // prevent round-off error on <0 steps from * // from causing overstepping & running off the * // edge of the texture * * tnext = (int)(tdivz * z) + tadjust; * if (tnext > bbextentt) * tnext = bbextentt; * else if (tnext < 16) * tnext = 16; // guard against round-off error on <0 steps * * r_turb_sstep = (snext - r_turb_s) >> 4; * r_turb_tstep = (tnext - r_turb_t) >> 4; * } fadd fp3,fp0 ;sdivz += sdivz16stepu fadd fp4,fp1 ;tdivz += tdivz16stepu fadd fp5,fp2 ;zi += zi16stepu fmove.s #65536,fp7 fdiv fp2,fp7 ;z = (float)0x10000 / zi; fmove fp7,fp6 fmul fp0,fp6 ;fp2 = sdivz * z fmove.l fp6,d4 ;convert to integer add.l a5,d4 ;snext = d4 + sadjust cmp.l a4,d4 ;if (snext > bbextents) bgt.b .down3 cmp.l #16,d4 ;if (snext < 16) bge.b .keep3 .up3 moveq #16,d4 ;snext = 16 bra.b .keep3 .down3 move.l a4,d4 ;snext = bbextents .keep3 fmul fp1,fp7 ;fp7 = tdivz * z fmove.l fp7,d5 ;convert to integer add.l a3,d5 ;tnext = d5 + tadjust cmp.l a2,d5 ;if (tnext > bbextentt) bgt.b .down4 cmp.l #16,d5 ;if (tnext < 16) bge.b .keep4 .up4 moveq #16,d5 ;tnext = 16 bra.b .keep4 .down4 move.l a2,d5 ;tnext = bbextentt .keep4 move.l d4,.saved4(sp) ;save snext move.l d5,.saved5(sp) ;save tnext sub.l d6,d4 ;d4 = snext - s sub.l d7,d5 ;d5 = tnext - t asr.l #4,d4 ;r_turb_sstep = d4 >> 4 asr.l #4,d5 ;r_turb_tstep = d5 >> 4 bra.w .mainloop ****** Evaluation of the values for the inner loop. This version is used for ****** span size < 16 ****** The original algorithm has two ugly divisions at the end of this part. ****** These are removed by the following optimization: ****** First, the divisors 1,2 and 4 are handled specially to gain speed. The ****** other divisors are handled using a reciprocal table. ****** a2 : bbextentt ****** a3 : tadjust ****** a4 : bbextents ****** a5 : sadjust ****** fp0 : sdivz ****** fp1 : tdivz ****** fp2 : zi * // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so * // can't step off polygon), clamp, calculate s and t steps across * // span by division, biasing steps low so we don't run off the * // texture * spancountminus1 = (float)(r_turb_spancount - 1); * sdivz += d_sdivzstepu * spancountminus1; * tdivz += d_tdivzstepu * spancountminus1; * zi += d_zistepu * spancountminus1; * z = (float)0x10000 / zi; // prescale to 16.16 fixed-point * snext = (int)(sdivz * z) + sadjust; * if (snext > bbextents) * snext = bbextents; * else if (snext < 16) * snext = 16; // prevent round-off error on <0 steps from * // from causing overstepping & running off the * // edge of the texture * * tnext = (int)(tdivz * z) + tadjust; * if (tnext > bbextentt) * tnext = bbextentt; * else if (tnext < 16) * tnext = 16; // guard against round-off error on <0 steps * * if (r_turb_spancount > 1) * { * r_turb_sstep = (snext - r_turb_s) / (r_turb_spancount - 1); * r_turb_tstep = (tnext - r_turb_t) / (r_turb_spancount - 1); * } * } .finalpart fmove.l d2,fp7 ;spancountminus1 = (float)(r_turb_spancount-1) fmove fp7,fp6 fmul.s .szstpu(sp),fp6 ;fp6 = d_sdivzstepu * spancountminus1 fadd fp6,fp0 ;sdivz += fp6 fmove fp7,fp6 fmul.s .tzstpu(sp),fp6 ;fp6 = d_tdivzstepu * spancountminus1 fadd fp6,fp1 ;tdivz += fp6 fmul.s .zistpu(sp),fp7 ;fp7 = d_zistepu * spancountminus1 fadd fp7,fp2 ;zi += fp7 fmove.s #65536,fp7 fdiv fp2,fp7 ;z = (float)0x10000 / zi; fmove fp7,fp6 fmul fp0,fp6 ;fp6 = sdivz * z fmove.l fp6,d4 ;convert to integer add.l a5,d4 ;snext = d4 + sadjust cmp.l a4,d4 ;if (snext > bbextents) bgt.b .down5 cmp.l #16,d4 ;if (snext < 16) bge.b .keep5 .up5 moveq #16,d4 ;snext = 16 bra.b .keep5 .down5 move.l a4,d4 ;snext = bbextents .keep5 fmul fp1,fp7 ;fp7 = tdivz * z fmove.l fp7,d5 ;convert to integer add.l a3,d5 ;tnext = d5 + tadjust cmp.l a2,d5 ;if (tnext > bbextentt) bgt.b .down6 cmp.l #16,d5 ;if (tnext < 16) bge.b .keep6 .up6 moveq #16,d5 ;tnext = 16 bra.b .keep6 .down6 move.l a2,d5 ;tnext = bbextentt .keep6 move.l d4,.saved4(sp) ;save snext move.l d5,.saved5(sp) ;save tnext sub.l d6,d4 ;d4 = snext - r_turb_s sub.l d7,d5 ;d5 = tnext - r_turb_t IFEQ QDIV tst.l d2 beq.w .mainloop divs.l d2,d4 divs.l d2,d5 ELSEIF cmp #5,d2 ;(r_turb_spancount-1) < 5? blt.b .special ;yes -> special case cmp #8,d2 beq.b .spec_8 .qdiv IFNE NICE_DIV lsl.l #2,d4 lsl.l #2,d5 lea ReciprocTable,a1 move 0(a1,d2.w*2),d0 move.l d4,d3 mulu d0,d3 clr d3 swap d3 swap d4 muls d0,d4 add.l d3,d4 move.l d5,d3 mulu d0,d3 clr d3 swap d3 swap d5 muls d0,d5 add.l d3,d5 bra.b .mainloop ELSEIF asr.l #7,d4 ;d4 >> 7 asr.l #7,d5 ;d5 >> 7 lea ReciprocTable,a1 ;a1 -> reciprocal table move 0(a1,d2.w*2),d0 ;d0 = (1/(r_turb_spancount-1))<<16 muls d0,d4 ;d4 = d4 / (r_turb_spancount-1) asr.l #7,d4 ;sstep = d4 >> 7 muls d0,d5 ;d5 = d5 / (r_turb_spancount-1) asr.l #7,d5 ;tstep = d5 >> 7 bra.b .mainloop ENDC .special cmp #1,d2 ;switch (r_turb_spancount-1) ble.b .mainloop ;0,1 -> no scaling needed cmp #3,d2 ;3 -> standard qdiv beq.b .qdiv blt.b .spec_2 asr.l #2,d4 ;4 -> scale by shifting right asr.l #2,d5 bra.b .mainloop .spec_8 asr.l #3,d4 ;8 -> scale by shifting right asr.l #3,d5 bra.b .mainloop .spec_2 asr.l #1,d4 ;2 -> scale by shifting right asr.l #1,d5 ENDC ****** D_DrawTurbulent8TQuickSpan (inlined) ****** Main drawing loop. ****** d2 : r_turb_spancount ****** d4 : r_turb_sstep; ****** d5 : r_turb_tstep; ****** d6 : r_turb_s ****** d7 : r_turb_t ****** a0 : r_turb_pdest ****** a6 : r_turb_turb * do * { * if (*pdest > (short)(izi >> 16)) * { * sturb = ((r_turb_s + r_turb_turb[(r_turb_t>>16)&(CYCLE-1)])>>16)&63; * tturb = ((r_turb_t + r_turb_turb[(r_turb_s>>16)&(CYCLE-1)])>>16)&63; * temp = *(r_turb_pbase + (tturb<<6) + sturb); * *r_turb_pdest = mainTransTable[(temp<<8) + (*r_turb_pdest)]; * *pz = (short)(izi >> 16); * } * r_turb_pdest++; * r_turb_s += r_turb_sstep; * r_turb_t += r_turb_tstep; * } while (--r_turb_spancount > 0); .mainloop ;move.l d1,-(sp) movem.l d1/a2-a5,.savearea(sp) move.l _cacheblock,a1 ;pbase = (unsigned char *)cacheblock moveq #10,d1 move.l _mainTransTable,a2 move.l d4,a3 ;a3 = r_turb_sstep clr.l d4 move.l d5,a4 ;a4 = r_turb_tstep ; translucent move.l .izi(sp),d5 ;d5 = izi move.l .pz(sp),a5 ;a5 = pz ; translucent .draw ; translucent swap d5 cmp.w (a5),d5 ;if (*pz <= (izi >> 16)) blt.b .skipdraw ; translucent swap d6 ;r_turb_s >> 16 swap d7 ;r_turb_t >> 16 and #CYCLE-1,d6 ;(r_turb_s >> 16) & (CYCLE-1) and #CYCLE-1,d7 ;(r_turb_t >> 16) & (CYCLE-1) move.l 0(a6,d7.w*4),d0 ;r_turb_turb [d7] move.l 0(a6,d6.w*4),d3 ;r_turb_turb [d6] swap d6 swap d7 add.l d6,d0 ;r_turb_s + r_turb_turb [] add.l d7,d3 ;r_turb_t + r_turb_turb [] swap d0 ;d0 >> 16 and.l #$3f,d0 ;sturb = (d0 >> 16) & 63 lsr.l d1,d3 ;(d3 >> (16-6)) and.l #$fc0,d3 ;tturb<<6 = (d3 >> (16-6)) & (63 << 6) add.l d3,d0 ;sturb + tturb << 6 ;move.b 0(a1,d0.l),(a0)+ ;*r_turb_pdest++ = *(r_turb_pbase + d0) ; translucent move.b 0(a1,d0.l),d4 ;d4 = *(r_turb_pbase + d0) lsl.w #8,d4 move.b (a0),d4 ;d4 = (temp<<8) + *r_turb_pdest move.b 0(a2,d4.l),(a0) ;move.w d5,(a5) ;*pz = izi >> 16 ; translucent .skipdraw add.l #1,a0 ;r_turb_pdest++ add.l a3,d6 ;r_turb_s += r_turb_sstep add.l a4,d7 ;r_turb_t += r_turb_tstep ; translucent move.l .izistep(sp),d3 ;add.l d3,.izi(sp) ;izi += izistep swap d5 add.l d3,d5 ;izi += izistep addq.l #2,a5 ;pz++ ; translucent dbra d2,.draw ;while (--r_turb_spancount > 0) move.l a5,.pz(sp) ;save pz onto the stack move.l d5,.izi(sp) ;save izi ;move.l (sp)+,d1 movem.l .savearea(sp),d1/a2-a5 ****** loop terminations ;.terminateloop move.l .saved5(sp),d7 ;r_turb_t = tnext move.l .saved4(sp),d6 ;r_turb_s = snext tst.l d1 ;while (count > 0) bgt.w .loop2 .skiploop move.l .savea1(sp),a1 ;while ((pspan = pspan->next) != NULL) move.l SPAN_PNEXT(a1),a1 tst.l a1 bne.w .loop add.l #.fpuregs,sp fmovem.x (sp)+,fp2-fp7 movem.l (sp)+,d2-d7/a2-a6 rts ****************************************************************************** * * void D_DrawSpans8 (espan_t *pspan) * * standard scan drawing function (8 pixel subdivision) * ****************************************************************************** cnop 0,4 _D_DrawSpans8 ***** stackframe rsreset .saved4 rs.l 1 .saved5 rs.l 1 .savea6 rs.l 1 .szstpu rs.s 1 .szstpv rs.s 1 .szorg rs.s 1 .tzstpu rs.s 1 .tzstpv rs.s 1 .tzorg rs.s 1 .zistpu rs.s 1 .zistpv rs.s 1 .ziorg rs.s 1 .fpuregs rs.x 6 .intregs rs.l 11 rs.l 1 .pspan rs.l 1 ****** Prologue. Global variables are put into registers or onto the stackframe ; d_subdiv16 is handled in D_SetupFrame ;fmove.s _d_subdiv16+CVAR_VALUE,fp0 ;fcmp.s #0,fp0 ;fbne _D_DrawSpans16 movem.l d2-d7/a2-a6,-(sp) fmovem.x fp2-fp7,-(sp) move.l _bbextentt,a2 move.l _tadjust,a3 move.l _bbextents,a4 move.l _sadjust,a5 move.l _d_ziorigin,-(sp) move.l _d_zistepv,-(sp) move.l _d_zistepu,-(sp) move.l _d_tdivzorigin,-(sp) move.l _d_tdivzstepv,-(sp) move.l _d_tdivzstepu,-(sp) move.l _d_sdivzorigin,-(sp) move.l _d_sdivzstepv,-(sp) move.l _d_sdivzstepu,-(sp) sub.l #.szstpu,sp ****** First loop. In every iteration one complete span is drawn * pbase = (unsigned char *)cacheblock; * * sdivz8stepu = d_sdivzstepu * 8; * tdivz8stepu = d_tdivzstepu * 8; * zi8stepu = d_zistepu * 8; * * do * { * pdest = (unsigned char *)((byte *)d_viewbuffer + * (screenwidth * pspan->v) + pspan->u); * * count = pspan->count; * * // calculate the initial s/z, t/z, 1/z, s, and t and clamp * du = (float)pspan->u; * dv = (float)pspan->v; * * sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; * tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; * zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; * z = (float)0x10000 / zi; // prescale to 16.16 fixed-point * move.l _cacheblock,a1 ;pbase = (unsigned char *)cacheblock fmove.s #8,fp7 fmove.s .szstpu(sp),fp3 fmul fp7,fp3 ;sdivz8stepu = d_sdivzstepu * 8 fmove.s .tzstpu(sp),fp4 fmul fp7,fp4 ;tdivz8stepu = d_tdivzstepu * 8 fmove.s .zistpu(sp),fp5 fmul fp7,fp5 ;zi8stepu = d_zistepu * 8 move.l .pspan(sp),a6 ;get function parameter .loop move.l a6,.savea6(sp) ;save actual ptr to pspan move.l _d_viewbuffer,a0 move.l _screenwidth,d0 move.l (a6)+,d1 fmove.l d1,fp2 ;du = (float)pspan->u move.l (a6)+,d2 fmove.l d2,fp7 ;dv = (float)pspan->v move.l (a6)+,d4 muls d2,d0 ;d0 = screenwidth * pspan->v add.l d1,d0 add.l d0,a0 ;pdest = d_viewbuffer + pspan->u + d0 lea .szstpu(sp),a6 ;a6 -> stackframe fmove.s (a6)+,fp0 fmul fp2,fp0 ;fp0 = du * d_sdivzstepu fmove.s (a6)+,fp1 fmul fp7,fp1 ;fp1 = dv * d_sdivzstepv fadd fp1,fp0 fadd.s (a6)+,fp0 ;sdivz = d_sdivzorigin + fp0 + fp1 fmove.s (a6)+,fp1 fmul fp2,fp1 ;fp1 = du * d_tdivzstepu fmove.s (a6)+,fp6 fmul fp7,fp6 ;fp6 = dv * d_tdivzstepv fadd fp6,fp1 fadd.s (a6)+,fp1 ;tdivz = d_tdivzorigin + fp1 + fp6 fmul.s (a6)+,fp2 ;fp2 = du * d_zistepu fmul.s (a6)+,fp7 ;fp7 = dv * d_zistepv fadd fp7,fp2 fadd.s (a6)+,fp2 ;zi = d_ziorigin + fp2 + fp7 fmove.s #65536,fp6 fdiv fp2,fp6 ;z = (float)0x10000 / zi * s = (int)(sdivz * z) + sadjust; * if (s > bbextents) * s = bbextents; * else if (s < 0) * s = 0; * * t = (int)(tdivz * z) + tadjust; * if (t > bbextentt) * t = bbextentt; * else if (t < 0) * t = 0; fmove fp6,fp7 fmul fp0,fp7 ;fp7 = sdivz * z fmove.l fp7,d6 ;convert to integer add.l a5,d6 ;s = d6 + sadjust cmp.l a4,d6 ;if (s > bbextents) bgt.b .down tst.l d6 ;if (s < 0) bge.b .keep .up moveq #0,d6 ;s = 0 bra.b .keep .down move.l a4,d6 ;s = bbextents .keep fmul fp1,fp6 ;fp6 = tdivz * z fmove.l fp6,d7 ;convert to integer add.l a3,d7 ;t = d7 + tadjust cmp.l a2,d7 ;if (t > bbextentt) bgt.b .down2 tst.l d7 ;if (t < 0) bge.b .keep2 .up2 moveq #0,d7 ;t = 0 bra.b .keep2 .down2 move.l a2,d7 ;t = bbextentt .keep2 move.l d4,d1 ****** Second loop. In every iteration one part of the whole span is drawn ****** d2 gets the value (spancount-1)! [NOT spancount] ****** d1 = count * do * { * // calculate s and t at the far end of the span * if (count >= 8) * spancount = 8; * else * spancount = count; * * count -= spancount; * * if (count) * { .loop2 moveq #8-1,d2 ;spancount = 8 cmp.l #8,d1 ;if (count >= 8) bgt.b .cont move.l d1,d2 ;spancount = count subq.l #1,d2 moveq #0,d1 ;count -= spancount bra.w .finalpart .cont subq.l #8,d1 ;count -= spancount; ****** Evaluation of the values for the inner loop. This version is used for ****** span size = 8 ****** a2 : bbextentt ****** a3 : tadjust ****** a4 : bbextents ****** a5 : sadjust ****** fp0 : sdivz ****** fp1 : tdivz ****** fp2 : zi ****** fp3 : sdivz8stepu ****** fp4 : tdivz8stepu ****** fp5 : zi8stepu * // calculate s/z, t/z, zi->fixed s and t at far end of span, * // calculate s and t steps across span by shifting * sdivz += sdivz8stepu; * tdivz += tdivz8stepu; * zi += zi8stepu; * z = (float)0x10000 / zi; // prescale to 16.16 fixed-point * snext = (int)(sdivz * z) + sadjust; * if (snext > bbextents) * snext = bbextents; * else if (snext < 8) * snext = 8; // prevent round-off error on <0 steps from * // from causing overstepping & running off the * // edge of the texture * tnext = (int)(tdivz * z) + tadjust; * if (tnext > bbextentt) * tnext = bbextentt; * else if (tnext < 8) * tnext = 8; // guard against round-off error on <0 steps * sstep = (snext - s) >> 3; * tstep = (tnext - t) >> 3; * } fadd fp3,fp0 ;sdivz += sdivz8stepu fadd fp4,fp1 ;tdivz += tdivz8stepu fadd fp5,fp2 ;zi += zi8stepu fmove.s #65536,fp7 fdiv fp2,fp7 ;z = (float)0x10000 / zi; fmove fp7,fp6 fmul fp0,fp6 ;fp2 = sdivz * z fmove.l fp6,d4 ;convert to integer add.l a5,d4 ;snext = d4 + sadjust cmp.l a4,d4 ;if (snext > bbextents) bgt.b .down3 cmp.l #8,d4 ;if (snext < 8) bge.b .keep3 .up3 moveq #8,d4 ;snext = 8 bra.b .keep3 .down3 move.l a4,d4 ;snext = bbextents .keep3 fmul fp1,fp7 ;fp7 = tdivz * z fmove.l fp7,d5 ;convert to integer add.l a3,d5 ;tnext = d5 + tadjust cmp.l a2,d5 ;if (tnext > bbextentt) bgt.b .down4 cmp.l #8,d5 ;if (tnext < 8) bge.b .keep4 .up4 moveq #8,d5 ;tnext = 8 bra.b .keep4 .down4 move.l a2,d5 ;tnext = bbextentt .keep4 move.l d4,.saved4(sp) ;save snext move.l d5,.saved5(sp) ;save tnext sub.l d6,d4 ;d4 = snext - s sub.l d7,d5 ;d5 = tnext - t asr.l #3,d4 ;sstep = d4 >> 3 asr.l #3,d5 ;tstep = d5 >> 3 bra.w .mainloop ****** Evaluation of the values for the inner loop. This version is used for ****** span size < 8 ****** The original algorithm has two ugly divisions at the end of this part. ****** These are removed by the following optimization: ****** First, the divisors 1,2 and 4 are handled specially to gain speed. The ****** other divisors are handled using a reciprocal table. ****** a2 : bbextentt ****** a3 : tadjust ****** a4 : bbextents ****** a5 : sadjust ****** fp0 : sdivz ****** fp1 : tdivz ****** fp2 : zi * // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so * // can't step off polygon), clamp, calculate s and t steps across * // span by division, biasing steps low so we don't run off the * // texture * spancountminus1 = (float)(spancount - 1); * sdivz += d_sdivzstepu * spancountminus1; * tdivz += d_tdivzstepu * spancountminus1; * zi += d_zistepu * spancountminus1; * z = (float)0x10000 / zi; // prescale to 16.16 fixed-point * snext = (int)(sdivz * z) + sadjust; * if (snext > bbextents) * snext = bbextents; * else if (snext < 8) * snext = 8; // prevent round-off error on <0 steps from * // from causing overstepping & running off the * // edge of the texture * * tnext = (int)(tdivz * z) + tadjust; * if (tnext > bbextentt) * tnext = bbextentt; * else if (tnext < 8) * tnext = 8; // guard against round-off error on <0 steps * * if (spancount > 1) * { * sstep = (snext - s) / (spancount - 1); * tstep = (tnext - t) / (spancount - 1); * } * } .finalpart fmove.l d2,fp7 ;spancountminus1 = (float)(spancount-1) fmove fp7,fp6 fmul.s .szstpu(sp),fp6 ;fp6 = d_sdivzstepu * spancountminus1 fadd fp6,fp0 ;sdivz += fp6 fmove fp7,fp6 fmul.s .tzstpu(sp),fp6 ;fp6 = d_tdivzstepu * spancountminus1 fadd fp6,fp1 ;tdivz += fp6 fmul.s .zistpu(sp),fp7 ;fp7 = d_zistepu * spancountminus1 fadd fp7,fp2 ;zi += fp7 fmove.s #65536,fp7 fdiv fp2,fp7 ;z = (float)0x10000 / zi; fmove fp7,fp6 fmul fp0,fp6 ;fp6 = sdivz * z fmove.l fp6,d4 ;convert to integer add.l a5,d4 ;snext = d4 + sadjust cmp.l a4,d4 ;if (snext > bbextents) bgt.b .down5 cmp.l #8,d4 ;if (snext < 8) bge.b .keep5 .up5 moveq #8,d4 ;snext = 8 bra.b .keep5 .down5 move.l a4,d4 ;snext = bbextents .keep5 fmul fp1,fp7 ;fp7 = tdivz * z fmove.l fp7,d5 ;convert to integer add.l a3,d5 ;tnext = d5 + tadjust cmp.l a2,d5 ;if (tnext > bbextentt) bgt.b .down6 cmp.l #8,d5 ;if (tnext < 8) bge.b .keep6 .up6 moveq #8,d5 ;tnext = 8 bra.b .keep6 .down6 move.l a2,d5 ;tnext = bbextentt .keep6 move.l d4,.saved4(sp) ;save snext move.l d5,.saved5(sp) ;save tnext sub.l d6,d4 ;d4 = snext - s sub.l d7,d5 ;d5 = tnext - t IFEQ QDIV tst.l d2 beq.w .mainloop divs.l d2,d4 divs.l d2,d5 ELSEIF cmp #5,d2 ;(spancount-1) < 5? blt.b .special ;yes -> special case .qdiv IFNE NICE_DIV lsl.l #2,d4 lsl.l #2,d5 lea ReciprocTable,a6 move 0(a6,d2.w*2),d0 move.l d4,d3 mulu d0,d3 clr d3 swap d3 swap d4 muls d0,d4 add.l d3,d4 move.l d5,d3 mulu d0,d3 clr d3 swap d3 swap d5 muls d0,d5 add.l d3,d5 bra.b .mainloop ELSEIF asr.l #7,d4 ;d4 >> 7 asr.l #7,d5 ;d5 >> 7 lea ReciprocTable,a6 ;a6 -> reciprocal table move 0(a6,d2.w*2),d0 ;d0 = (1/(spancount-1))<<16 muls d0,d4 ;d4 = d4 / (spancount-1) asr.l #7,d4 ;sstep = d4 >> 7 muls d0,d5 ;d5 = d5 / (spancount-1) asr.l #7,d5 ;tstep = d5 >> 7 bra.b .mainloop ENDC .special cmp #1,d2 ;switch (spancount-1) ble.b .mainloop ;0,1 -> no scaling needed cmp #3,d2 ;3 -> standard qdiv beq.b .qdiv blt.b .spec_2 asr.l #2,d4 ;4 -> scale by shifting right asr.l #2,d5 bra.b .mainloop .spec_2 asr.l #1,d4 ;2 -> scale by shifting right asr.l #1,d5 ENDC ****** Main drawing loop. Here lies the speed. ****** Very optimized (removed multiplication from inner loop) ****** d2 : spancount ****** d4 : sstep ****** d5 : tstep ****** d6 : s ****** d7 : t ****** a0 : pdest ****** a1 : pbase * do * { * *pdest++ = *(pbase + (s >> 16) + (t >> 16) * cachewidth); * s += sstep; * t += tstep; * } while (--spancount > 0); .mainloop move.l d1,-(sp) lea .PixTable,a6 ;a6 -> Functable move.l _cachewidth,d3 ;read cachewidth move.l 0(a6,d2.w*4),a6 ;get pointer to function swap d7 swap d4 move.l d7,d1 swap d5 muls d3,d7 ;d7 = t integer part * cachewidth move d5,d2 clr d1 ;d1 = t fractional part muls d3,d2 ;tstep integer part * cachewidth move d4,d0 ;d0 = sstep integer part clr d5 ;d5 = tstep fractional part clr d4 ;d4 = sstep fractional part swap d6 ;d6 = s swapped jmp (a6) .Pix8 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 ;increment s fractional part addx.w d0,d6 ;increment s integer part add.l d2,d7 ;increment t integer part add.l d5,d1 ;increment t fractional part bcc.b .Pix7 ;check if carry add.l d3,d7 ;add cachewidth to t .Pix7 lea 0(a1,d6.w),a6 ;and so long... move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix6 add.l d3,d7 .Pix6 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix5 add.l d3,d7 .Pix5 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix4 add.l d3,d7 .Pix4 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix3 add.l d3,d7 .Pix3 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix2 add.l d3,d7 .Pix2 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix1 add.l d3,d7 .Pix1 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix0 add.l d3,d7 .Pix0 move.l (sp)+,d1 ****** loop terminations move.l .saved5(sp),d7 ;t = tnext move.l .saved4(sp),d6 ;s = snext tst.l d1 ;while (count > 0) bgt.w .loop2 move.l .savea6(sp),a6 ;while ((pspan = pspan->next) != NULL) move.l SPAN_PNEXT(a6),a6 tst.l a6 bne.w .loop add.l #.fpuregs,sp fmovem.x (sp)+,fp2-fp7 movem.l (sp)+,d2-d7/a2-a6 rts .PixTable dc.l .Pix1 dc.l .Pix2 dc.l .Pix3 dc.l .Pix4 dc.l .Pix5 dc.l .Pix6 dc.l .Pix7 dc.l .Pix8 ****************************************************************************** * * void D_DrawSpans16 (espan_t *pspan) * * standard scan drawing function (16 pixel subdivision) * ****************************************************************************** cnop 0,4 _D_DrawSpans16 ***** stackframe rsreset .saved4 rs.l 1 .saved5 rs.l 1 .savea6 rs.l 1 .szstpu rs.s 1 .szstpv rs.s 1 .szorg rs.s 1 .tzstpu rs.s 1 .tzstpv rs.s 1 .tzorg rs.s 1 .zistpu rs.s 1 .zistpv rs.s 1 .ziorg rs.s 1 .fpuregs rs.x 6 .intregs rs.l 11 rs.l 1 .pspan rs.l 1 ****** Prologue. Global variables are put into registers or onto the stackframe movem.l d2-d7/a2-a6,-(sp) fmovem.x fp2-fp7,-(sp) move.l _bbextentt,a2 move.l _tadjust,a3 move.l _bbextents,a4 move.l _sadjust,a5 move.l _d_ziorigin,-(sp) move.l _d_zistepv,-(sp) move.l _d_zistepu,-(sp) move.l _d_tdivzorigin,-(sp) move.l _d_tdivzstepv,-(sp) move.l _d_tdivzstepu,-(sp) move.l _d_sdivzorigin,-(sp) move.l _d_sdivzstepv,-(sp) move.l _d_sdivzstepu,-(sp) sub.l #.szstpu,sp ****** First loop. In every iteration one complete span is drawn * pbase = (unsigned char *)cacheblock; * * sdivz16stepu = d_sdivzstepu * 16; * tdivz16stepu = d_tdivzstepu * 16; * zi16stepu = d_zistepu * 16; * * do * { * pdest = (unsigned char *)((byte *)d_viewbuffer + * (screenwidth * pspan->v) + pspan->u); * * count = pspan->count; * * // calculate the initial s/z, t/z, 1/z, s, and t and clamp * du = (float)pspan->u; * dv = (float)pspan->v; * * sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; * tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; * zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; * z = (float)0x10000 / zi; // prescale to 16.16 fixed-point * move.l _cacheblock,a1 ;pbase = (unsigned char *)cacheblock fmove.s #16,fp7 fmove.s .szstpu(sp),fp3 fmul fp7,fp3 ;sdivz16stepu = d_sdivzstepu * 16 fmove.s .tzstpu(sp),fp4 fmul fp7,fp4 ;tdivz16stepu = d_tdivzstepu * 16 fmove.s .zistpu(sp),fp5 fmul fp7,fp5 ;zi16stepu = d_zistepu * 16 move.l .pspan(sp),a6 ;get function parameter .loop move.l a6,.savea6(sp) ;save actual ptr to pspan move.l _d_viewbuffer,a0 move.l _screenwidth,d0 move.l (a6)+,d1 fmove.l d1,fp2 ;du = (float)pspan->u move.l (a6)+,d2 fmove.l d2,fp7 ;dv = (float)pspan->v move.l (a6)+,d4 muls d2,d0 ;d0 = screenwidth * pspan->v add.l d1,d0 add.l d0,a0 ;pdest = d_viewbuffer + pspan->u + d0 lea .szstpu(sp),a6 ;a6 -> stackframe fmove.s (a6)+,fp0 fmul fp2,fp0 ;fp0 = du * d_sdivzstepu fmove.s (a6)+,fp1 fmul fp7,fp1 ;fp1 = dv * d_sdivzstepv fadd fp1,fp0 fadd.s (a6)+,fp0 ;sdivz = d_sdivzorigin + fp0 + fp1 fmove.s (a6)+,fp1 fmul fp2,fp1 ;fp1 = du * d_tdivzstepu fmove.s (a6)+,fp6 fmul fp7,fp6 ;fp6 = dv * d_tdivzstepv fadd fp6,fp1 fadd.s (a6)+,fp1 ;tdivz = d_tdivzorigin + fp1 + fp6 fmul.s (a6)+,fp2 ;fp2 = du * d_zistepu fmul.s (a6)+,fp7 ;fp7 = dv * d_zistepv fadd fp7,fp2 fadd.s (a6)+,fp2 ;zi = d_ziorigin + fp2 + fp7 fmove.s #65536,fp6 fdiv fp2,fp6 ;z = (float)0x10000 / zi * s = (int)(sdivz * z) + sadjust; * if (s > bbextents) * s = bbextents; * else if (s < 0) * s = 0; * * t = (int)(tdivz * z) + tadjust; * if (t > bbextentt) * t = bbextentt; * else if (t < 0) * t = 0; fmove fp6,fp7 fmul fp0,fp7 ;fp7 = sdivz * z fmove.l fp7,d6 ;convert to integer add.l a5,d6 ;s = d6 + sadjust cmp.l a4,d6 ;if (s > bbextents) bgt.b .down tst.l d6 ;if (s < 0) bge.b .keep .up moveq #0,d6 ;s = 0 bra.b .keep .down move.l a4,d6 ;s = bbextents .keep fmul fp1,fp6 ;fp6 = tdivz * z fmove.l fp6,d7 ;convert to integer add.l a3,d7 ;t = d7 + tadjust cmp.l a2,d7 ;if (t > bbextentt) bgt.b .down2 tst.l d7 ;if (t < 0) bge.b .keep2 .up2 moveq #0,d7 ;t = 0 bra.b .keep2 .down2 move.l a2,d7 ;t = bbextentt .keep2 move.l d4,d1 ****** Second loop. In every iteration one part of the whole span is drawn ****** d2 gets the value (spancount-1)! [NOT spancount] ****** d1 = count * do * { * // calculate s and t at the far end of the span * if (count >= 16) * spancount = 16; * else * spancount = count; * * count -= spancount; * * if (count) * { .loop2 moveq #16-1,d2 ;spancount = 16 cmp.l #16,d1 ;if (count >= 16) bgt.b .cont move.l d1,d2 ;spancount = count subq.l #1,d2 moveq #0,d1 ;count -= spancount bra.w .finalpart .cont sub.l #16,d1 ;count -= spancount; ****** Evaluation of the values for the inner loop. This version is used for ****** span size = 16 ****** a2 : bbextentt ****** a3 : tadjust ****** a4 : bbextents ****** a5 : sadjust ****** fp0 : sdivz ****** fp1 : tdivz ****** fp2 : zi ****** fp3 : sdivz16stepu ****** fp4 : tdivz16stepu ****** fp5 : zi16stepu * // calculate s/z, t/z, zi->fixed s and t at far end of span, * // calculate s and t steps across span by shifting * sdivz += sdivz16stepu; * tdivz += tdivz16stepu; * zi += zi16stepu; * z = (float)0x10000 / zi; // prescale to 16.16 fixed-point * snext = (int)(sdivz * z) + sadjust; * if (snext > bbextents) * snext = bbextents; * else if (snext < 16) * snext = 16; // prevent round-off error on <0 steps from * // from causing overstepping & running off the * // edge of the texture * tnext = (int)(tdivz * z) + tadjust; * if (tnext > bbextentt) * tnext = bbextentt; * else if (tnext < 16) * tnext = 16; // guard against round-off error on <0 steps * sstep = (snext - s) >> 4; * tstep = (tnext - t) >> 4; * } fadd fp3,fp0 ;sdivz += sdivz16stepu fadd fp4,fp1 ;tdivz += tdivz16stepu fadd fp5,fp2 ;zi += zi16stepu fmove.s #65536,fp7 fdiv fp2,fp7 ;z = (float)0x10000 / zi; fmove fp7,fp6 fmul fp0,fp6 ;fp2 = sdivz * z fmove.l fp6,d4 ;convert to integer add.l a5,d4 ;snext = d4 + sadjust cmp.l a4,d4 ;if (snext > bbextents) bgt.b .down3 cmp.l #16,d4 ;if (snext < 16) bge.b .keep3 .up3 moveq #16,d4 ;snext = 16 bra.b .keep3 .down3 move.l a4,d4 ;snext = bbextents .keep3 fmul fp1,fp7 ;fp7 = tdivz * z fmove.l fp7,d5 ;convert to integer add.l a3,d5 ;tnext = d5 + tadjust cmp.l a2,d5 ;if (tnext > bbextentt) bgt.b .down4 cmp.l #16,d5 ;if (tnext < 16) bge.b .keep4 .up4 moveq #16,d5 ;tnext = 16 bra.b .keep4 .down4 move.l a2,d5 ;tnext = bbextentt .keep4 move.l d4,.saved4(sp) ;save snext move.l d5,.saved5(sp) ;save tnext sub.l d6,d4 ;d4 = snext - s sub.l d7,d5 ;d5 = tnext - t asr.l #4,d4 ;sstep = d4 >> 4 asr.l #4,d5 ;tstep = d5 >> 4 bra.w .mainloop ****** Evaluation of the values for the inner loop. This version is used for ****** span size < 16 ****** The original algorithm has two ugly divisions at the end of this part. ****** These are removed by the following optimization: ****** First, the divisors 1,2 and 4 are handled specially to gain speed. The ****** other divisors are handled using a reciprocal table. ****** a2 : bbextentt ****** a3 : tadjust ****** a4 : bbextents ****** a5 : sadjust ****** fp0 : sdivz ****** fp1 : tdivz ****** fp2 : zi * // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so * // can't step off polygon), clamp, calculate s and t steps across * // span by division, biasing steps low so we don't run off the * // texture * spancountminus1 = (float)(spancount - 1); * sdivz += d_sdivzstepu * spancountminus1; * tdivz += d_tdivzstepu * spancountminus1; * zi += d_zistepu * spancountminus1; * z = (float)0x10000 / zi; // prescale to 16.16 fixed-point * snext = (int)(sdivz * z) + sadjust; * if (snext > bbextents) * snext = bbextents; * else if (snext < 16) * snext = 16; // prevent round-off error on <0 steps from * // from causing overstepping & running off the * // edge of the texture * * tnext = (int)(tdivz * z) + tadjust; * if (tnext > bbextentt) * tnext = bbextentt; * else if (tnext < 16) * tnext = 16; // guard against round-off error on <0 steps * * if (spancount > 1) * { * sstep = (snext - s) / (spancount - 1); * tstep = (tnext - t) / (spancount - 1); * } * } .finalpart fmove.l d2,fp7 ;spancountminus1 = (float)(spancount-1) fmove fp7,fp6 fmul.s .szstpu(sp),fp6 ;fp6 = d_sdivzstepu * spancountminus1 fadd fp6,fp0 ;sdivz += fp6 fmove fp7,fp6 fmul.s .tzstpu(sp),fp6 ;fp6 = d_tdivzstepu * spancountminus1 fadd fp6,fp1 ;tdivz += fp6 fmul.s .zistpu(sp),fp7 ;fp7 = d_zistepu * spancountminus1 fadd fp7,fp2 ;zi += fp7 fmove.s #65536,fp7 fdiv fp2,fp7 ;z = (float)0x10000 / zi; fmove fp7,fp6 fmul fp0,fp6 ;fp6 = sdivz * z fmove.l fp6,d4 ;convert to integer add.l a5,d4 ;snext = d4 + sadjust cmp.l a4,d4 ;if (snext > bbextents) bgt.b .down5 cmp.l #16,d4 ;if (snext < 16) bge.b .keep5 .up5 moveq #16,d4 ;snext = 16 bra.b .keep5 .down5 move.l a4,d4 ;snext = bbextents .keep5 fmul fp1,fp7 ;fp7 = tdivz * z fmove.l fp7,d5 ;convert to integer add.l a3,d5 ;tnext = d5 + tadjust cmp.l a2,d5 ;if (tnext > bbextentt) bgt.b .down6 cmp.l #16,d5 ;if (tnext < 16) bge.b .keep6 .up6 moveq #16,d5 ;tnext = 16 bra.b .keep6 .down6 move.l a2,d5 ;tnext = bbextentt .keep6 move.l d4,.saved4(sp) ;save snext move.l d5,.saved5(sp) ;save tnext sub.l d6,d4 ;d4 = snext - s sub.l d7,d5 ;d5 = tnext - t IFEQ QDIV tst.l d2 beq.w .mainloop divs.l d2,d4 divs.l d2,d5 ELSEIF cmp #5,d2 ;(spancount-1) < 5? blt.b .special ;yes -> special case cmp #8,d2 beq.b .spec_8 .qdiv IFNE NICE_DIV lsl.l #2,d4 lsl.l #2,d5 lea ReciprocTable,a6 move 0(a6,d2.w*2),d0 move.l d4,d3 mulu d0,d3 clr d3 swap d3 swap d4 muls d0,d4 add.l d3,d4 move.l d5,d3 mulu d0,d3 clr d3 swap d3 swap d5 muls d0,d5 add.l d3,d5 bra.b .mainloop ELSEIF asr.l #7,d4 ;d4 >> 7 asr.l #7,d5 ;d5 >> 7 lea ReciprocTable,a6 ;a6 -> reciprocal table move 0(a6,d2.w*2),d0 ;d0 = (1/(spancount-1))<<16 muls d0,d4 ;d4 = d4 / (spancount-1) asr.l #7,d4 ;sstep = d4 >> 7 muls d0,d5 ;d5 = d5 / (spancount-1) asr.l #7,d5 ;tstep = d5 >> 7 bra.b .mainloop ENDC .special cmp #1,d2 ;switch (spancount-1) ble.b .mainloop ;0,1 -> no scaling needed cmp #3,d2 ;3 -> standard qdiv beq.b .qdiv blt.b .spec_2 asr.l #2,d4 ;4 -> scale by shifting right asr.l #2,d5 bra.b .mainloop .spec_8 asr.l #3,d4 ;8 -> scale by shifting right asr.l #3,d5 bra.b .mainloop .spec_2 asr.l #1,d4 ;2 -> scale by shifting right asr.l #1,d5 ENDC ****** Main drawing loop. Here lies the speed. ****** Very optimized (removed multiplication from inner loop) ****** d2 : spancount ****** d4 : sstep ****** d5 : tstep ****** d6 : s ****** d7 : t ****** a0 : pdest ****** a1 : pbase * do * { * *pdest++ = *(pbase + (s >> 16) + (t >> 16) * cachewidth); * s += sstep; * t += tstep; * } while (--spancount > 0); .mainloop move.l d1,-(sp) lea .PixTable,a6 ;a6 -> Functable move.l _cachewidth,d3 ;read cachewidth move.l 0(a6,d2.w*4),a6 ;get pointer to function swap d7 swap d4 move.l d7,d1 swap d5 muls d3,d7 ;d7 = t integer part * cachewidth move d5,d2 clr d1 ;d1 = t fractional part muls d3,d2 ;tstep integer part * cachewidth move d4,d0 ;d0 = sstep integer part clr d5 ;d5 = tstep fractional part clr d4 ;d4 = sstep fractional part swap d6 ;d6 = s swapped jmp (a6) .Pix16 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 ;increment s fractional part addx.w d0,d6 ;increment s integer part add.l d2,d7 ;increment t integer part add.l d5,d1 ;increment t fractional part bcc.b .Pix15 ;check if carry add.l d3,d7 ;add cachewidth to t .Pix15 lea 0(a1,d6.w),a6 ;and so long... move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix14 add.l d3,d7 .Pix14 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix13 add.l d3,d7 .Pix13 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix12 add.l d3,d7 .Pix12 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix11 add.l d3,d7 .Pix11 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix10 add.l d3,d7 .Pix10 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix9 add.l d3,d7 .Pix9 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix8 add.l d3,d7 .Pix8 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix7 add.l d3,d7 .Pix7 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix6 add.l d3,d7 .Pix6 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix5 add.l d3,d7 .Pix5 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix4 add.l d3,d7 .Pix4 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix3 add.l d3,d7 .Pix3 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix2 add.l d3,d7 .Pix2 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix1 add.l d3,d7 .Pix1 lea 0(a1,d6.w),a6 move.b 0(a6,d7.l),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix0 add.l d3,d7 .Pix0 move.l (sp)+,d1 ****** loop terminations move.l .saved5(sp),d7 ;t = tnext move.l .saved4(sp),d6 ;s = snext tst.l d1 ;while (count > 0) bgt.w .loop2 move.l .savea6(sp),a6 ;while ((pspan = pspan->next) != NULL) move.l SPAN_PNEXT(a6),a6 tst.l a6 bne.w .loop add.l #.fpuregs,sp fmovem.x (sp)+,fp2-fp7 movem.l (sp)+,d2-d7/a2-a6 rts .PixTable dc.l .Pix1 dc.l .Pix2 dc.l .Pix3 dc.l .Pix4 dc.l .Pix5 dc.l .Pix6 dc.l .Pix7 dc.l .Pix8 dc.l .Pix9 dc.l .Pix10 dc.l .Pix11 dc.l .Pix12 dc.l .Pix13 dc.l .Pix14 dc.l .Pix15 dc.l .Pix16 ****************************************************************************** * * void D_DrawSpans16T (espan_t *pspan) * * translucent scan drawing function (16 pixel subdivision) * ****************************************************************************** cnop 0,4 _D_DrawSpans16T ***** stackframe rsreset .izistep rs.l 1 .izi rs.l 1 .pz rs.l 1 .savearea rs.l 4 .saved4 rs.l 1 .saved5 rs.l 1 .savea6 rs.l 1 .szstpu rs.s 1 .szstpv rs.s 1 .szorg rs.s 1 .tzstpu rs.s 1 .tzstpv rs.s 1 .tzorg rs.s 1 .zistpu rs.s 1 .zistpv rs.s 1 .ziorg rs.s 1 .fpuregs rs.x 6 .intregs rs.l 11 rs.l 1 .pspan rs.l 1 ****** Prologue. Global variables are put into registers or onto the stackframe movem.l d2-d7/a2-a6,-(sp) fmovem.x fp2-fp7,-(sp) move.l _bbextentt,a2 move.l _tadjust,a3 move.l _bbextents,a4 move.l _sadjust,a5 move.l _d_ziorigin,-(sp) move.l _d_zistepv,-(sp) move.l _d_zistepu,-(sp) move.l _d_tdivzorigin,-(sp) move.l _d_tdivzstepv,-(sp) move.l _d_tdivzstepu,-(sp) move.l _d_sdivzorigin,-(sp) move.l _d_sdivzstepv,-(sp) move.l _d_sdivzstepu,-(sp) sub.l #.szstpu,sp ****** First loop. In every iteration one complete span is drawn * pbase = (unsigned char *)cacheblock; * * sdivz16stepu = d_sdivzstepu * 16; * tdivz16stepu = d_tdivzstepu * 16; * zi16stepu = d_zistepu * 16; * * izistep = (int)(d_zistepu * 0x8000 * 0x10000); * * do * { * pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; * pdest = (unsigned char *)((byte *)d_viewbuffer + * (screenwidth * pspan->v) + pspan->u); * * count = pspan->count; * * // calculate the initial s/z, t/z, 1/z, s, and t and clamp * du = (float)pspan->u; * dv = (float)pspan->v; * * sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; * tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; * zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; * izi = (int)(zi * 0x8000 * 0x10000); * z = (float)0x10000 / zi; // prescale to 16.16 fixed-point * move.l _cacheblock,a1 ;pbase = (unsigned char *)cacheblock fmove.s #16,fp5 fmove.s .szstpu(sp),fp3 fmul fp5,fp3 ;sdivz16stepu = d_sdivzstepu * 16 fmove.s .tzstpu(sp),fp4 fmul fp5,fp4 ;tdivz16stepu = d_tdivzstepu * 16 fmove.s .zistpu(sp),fp7 fmul fp7,fp5 ;zi16stepu = d_zistepu * 16 ; translucent fmove.s #32768*65536,fp0 ;fmove.s .zistpu(sp),fp7 ;fp7 = d_zistepu fmul fp0,fp7 ;multiply by $8000*$10000 fmove.l fp7,d3 ;izistep = d3 move.l d3,.izistep(sp) ; translucent move.l .pspan(sp),a6 ;get function parameter .loop move.l a6,.savea6(sp) ;save actual ptr to pspan move.l _d_viewbuffer,a0 move.l _screenwidth,d0 move.l (a6)+,d1 fmove.l d1,fp2 ;du = (float)pspan->u move.l (a6)+,d2 fmove.l d2,fp7 ;dv = (float)pspan->v move.l (a6)+,d4 ; translucent move.l _d_pzbuffer,a6 move.l _d_zwidth,d3 ;d3 = d_zwidth muls d2,d3 ;d3 = pspan->v * d_zwidth add.l d1,d3 ;d3 = d3 + pspan->u lea 0(a6,d3.l*2),a6 ;pz = d_pzbuffer + d3 move.l a6,.pz(sp) ; translucent muls d2,d0 ;d0 = screenwidth * pspan->v add.l d1,d0 add.l d0,a0 ;pdest = d_viewbuffer + pspan->u + d0 lea .szstpu(sp),a6 ;a6 -> stackframe fmove.s (a6)+,fp0 fmul fp2,fp0 ;fp0 = du * d_sdivzstepu fmove.s (a6)+,fp1 fmul fp7,fp1 ;fp1 = dv * d_sdivzstepv fadd fp1,fp0 fadd.s (a6)+,fp0 ;sdivz = d_sdivzorigin + fp0 + fp1 fmove.s (a6)+,fp1 fmul fp2,fp1 ;fp1 = du * d_tdivzstepu fmove.s (a6)+,fp6 fmul fp7,fp6 ;fp6 = dv * d_tdivzstepv fadd fp6,fp1 fadd.s (a6)+,fp1 ;tdivz = d_tdivzorigin + fp1 + fp6 fmul.s (a6)+,fp2 ;fp2 = du * d_zistepu fmul.s (a6)+,fp7 ;fp7 = dv * d_zistepv fadd fp7,fp2 fadd.s (a6)+,fp2 ;zi = d_ziorigin + fp2 + fp7 ; translucent fmove.s #32768*65536,fp6 ;fmove.l fp2,fp7 ;fp7 = zi fmul fp2,fp6 ;izi = zi * $8000 * $10000 fmove.l fp6,d3 ;convert to integer move.l d3,.izi(sp) ; translucent fmove.s #65536,fp6 fdiv fp2,fp6 ;z = (float)0x10000 / zi * s = (int)(sdivz * z) + sadjust; * if (s > bbextents) * s = bbextents; * else if (s < 0) * s = 0; * * t = (int)(tdivz * z) + tadjust; * if (t > bbextentt) * t = bbextentt; * else if (t < 0) * t = 0; fmove fp6,fp7 fmul fp0,fp7 ;fp7 = sdivz * z fmove.l fp7,d6 ;convert to integer add.l a5,d6 ;s = d6 + sadjust cmp.l a4,d6 ;if (s > bbextents) bgt.b .down tst.l d6 ;if (s < 0) bge.b .keep .up moveq #0,d6 ;s = 0 bra.b .keep .down move.l a4,d6 ;s = bbextents .keep fmul fp1,fp6 ;fp6 = tdivz * z fmove.l fp6,d7 ;convert to integer add.l a3,d7 ;t = d7 + tadjust cmp.l a2,d7 ;if (t > bbextentt) bgt.b .down2 tst.l d7 ;if (t < 0) bge.b .keep2 .up2 moveq #0,d7 ;t = 0 bra.b .keep2 .down2 move.l a2,d7 ;t = bbextentt .keep2 move.l d4,d1 ****** Second loop. In every iteration one part of the whole span is drawn ****** d2 gets the value (spancount-1)! [NOT spancount] ****** d1 = count * do * { * // calculate s and t at the far end of the span * if (count >= 16) * spancount = 16; * else * spancount = count; * * count -= spancount; * * if (count) * { .loop2 moveq #16-1,d2 ;spancount = 16 cmp.l #16,d1 ;if (count >= 16) bgt.b .cont move.l d1,d2 ;spancount = count subq.l #1,d2 moveq #0,d1 ;count -= spancount bra.w .finalpart .cont sub.l #16,d1 ;count -= spancount; ****** Evaluation of the values for the inner loop. This version is used for ****** span size = 16 ****** a2 : bbextentt ****** a3 : tadjust ****** a4 : bbextents ****** a5 : sadjust ****** fp0 : sdivz ****** fp1 : tdivz ****** fp2 : zi ****** fp3 : sdivz16stepu ****** fp4 : tdivz16stepu ****** fp5 : zi16stepu * // calculate s/z, t/z, zi->fixed s and t at far end of span, * // calculate s and t steps across span by shifting * sdivz += sdivz16stepu; * tdivz += tdivz16stepu; * zi += zi16stepu; * z = (float)0x10000 / zi; // prescale to 16.16 fixed-point * snext = (int)(sdivz * z) + sadjust; * if (snext > bbextents) * snext = bbextents; * else if (snext < 16) * snext = 16; // prevent round-off error on <0 steps from * // from causing overstepping & running off the * // edge of the texture * tnext = (int)(tdivz * z) + tadjust; * if (tnext > bbextentt) * tnext = bbextentt; * else if (tnext < 16) * tnext = 16; // guard against round-off error on <0 steps * sstep = (snext - s) >> 4; * tstep = (tnext - t) >> 4; * } fadd fp3,fp0 ;sdivz += sdivz16stepu fadd fp4,fp1 ;tdivz += tdivz16stepu fadd fp5,fp2 ;zi += zi16stepu fmove.s #65536,fp7 fdiv fp2,fp7 ;z = (float)0x10000 / zi; fmove fp7,fp6 fmul fp0,fp6 ;fp2 = sdivz * z fmove.l fp6,d4 ;convert to integer add.l a5,d4 ;snext = d4 + sadjust cmp.l a4,d4 ;if (snext > bbextents) bgt.b .down3 cmp.l #16,d4 ;if (snext < 16) bge.b .keep3 .up3 moveq #16,d4 ;snext = 16 bra.b .keep3 .down3 move.l a4,d4 ;snext = bbextents .keep3 fmul fp1,fp7 ;fp7 = tdivz * z fmove.l fp7,d5 ;convert to integer add.l a3,d5 ;tnext = d5 + tadjust cmp.l a2,d5 ;if (tnext > bbextentt) bgt.b .down4 cmp.l #16,d5 ;if (tnext < 16) bge.b .keep4 .up4 moveq #16,d5 ;tnext = 16 bra.b .keep4 .down4 move.l a2,d5 ;tnext = bbextentt .keep4 move.l d4,.saved4(sp) ;save snext move.l d5,.saved5(sp) ;save tnext sub.l d6,d4 ;d4 = snext - s sub.l d7,d5 ;d5 = tnext - t asr.l #4,d4 ;sstep = d4 >> 4 asr.l #4,d5 ;tstep = d5 >> 4 bra.w .mainloop ****** Evaluation of the values for the inner loop. This version is used for ****** span size < 16 ****** The original algorithm has two ugly divisions at the end of this part. ****** These are removed by the following optimization: ****** First, the divisors 1,2 and 4 are handled specially to gain speed. The ****** other divisors are handled using a reciprocal table. ****** a2 : bbextentt ****** a3 : tadjust ****** a4 : bbextents ****** a5 : sadjust ****** fp0 : sdivz ****** fp1 : tdivz ****** fp2 : zi * // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so * // can't step off polygon), clamp, calculate s and t steps across * // span by division, biasing steps low so we don't run off the * // texture * spancountminus1 = (float)(spancount - 1); * sdivz += d_sdivzstepu * spancountminus1; * tdivz += d_tdivzstepu * spancountminus1; * zi += d_zistepu * spancountminus1; * z = (float)0x10000 / zi; // prescale to 16.16 fixed-point * snext = (int)(sdivz * z) + sadjust; * if (snext > bbextents) * snext = bbextents; * else if (snext < 16) * snext = 16; // prevent round-off error on <0 steps from * // from causing overstepping & running off the * // edge of the texture * * tnext = (int)(tdivz * z) + tadjust; * if (tnext > bbextentt) * tnext = bbextentt; * else if (tnext < 16) * tnext = 16; // guard against round-off error on <0 steps * * if (spancount > 1) * { * sstep = (snext - s) / (spancount - 1); * tstep = (tnext - t) / (spancount - 1); * } * } .finalpart fmove.l d2,fp7 ;spancountminus1 = (float)(spancount-1) fmove fp7,fp6 fmul.s .szstpu(sp),fp6 ;fp6 = d_sdivzstepu * spancountminus1 fadd fp6,fp0 ;sdivz += fp6 fmove fp7,fp6 fmul.s .tzstpu(sp),fp6 ;fp6 = d_tdivzstepu * spancountminus1 fadd fp6,fp1 ;tdivz += fp6 fmul.s .zistpu(sp),fp7 ;fp7 = d_zistepu * spancountminus1 fadd fp7,fp2 ;zi += fp7 fmove.s #65536,fp7 fdiv fp2,fp7 ;z = (float)0x10000 / zi; fmove fp7,fp6 fmul fp0,fp6 ;fp6 = sdivz * z fmove.l fp6,d4 ;convert to integer add.l a5,d4 ;snext = d4 + sadjust cmp.l a4,d4 ;if (snext > bbextents) bgt.b .down5 cmp.l #16,d4 ;if (snext < 16) bge.b .keep5 .up5 moveq #16,d4 ;snext = 16 bra.b .keep5 .down5 move.l a4,d4 ;snext = bbextents .keep5 fmul fp1,fp7 ;fp7 = tdivz * z fmove.l fp7,d5 ;convert to integer add.l a3,d5 ;tnext = d5 + tadjust cmp.l a2,d5 ;if (tnext > bbextentt) bgt.b .down6 cmp.l #16,d5 ;if (tnext < 16) bge.b .keep6 .up6 moveq #16,d5 ;tnext = 16 bra.b .keep6 .down6 move.l a2,d5 ;tnext = bbextentt .keep6 move.l d4,.saved4(sp) ;save snext move.l d5,.saved5(sp) ;save tnext sub.l d6,d4 ;d4 = snext - s sub.l d7,d5 ;d5 = tnext - t IFEQ QDIV tst.l d2 beq.w .mainloop divs.l d2,d4 divs.l d2,d5 ELSEIF cmp #5,d2 ;(spancount-1) < 5? blt.b .special ;yes -> special case cmp #8,d2 beq.b .spec_8 .qdiv IFNE NICE_DIV lsl.l #2,d4 lsl.l #2,d5 lea ReciprocTable,a6 move 0(a6,d2.w*2),d0 move.l d4,d3 mulu d0,d3 clr d3 swap d3 swap d4 muls d0,d4 add.l d3,d4 move.l d5,d3 mulu d0,d3 clr d3 swap d3 swap d5 muls d0,d5 add.l d3,d5 bra.b .mainloop ELSEIF asr.l #7,d4 ;d4 >> 7 asr.l #7,d5 ;d5 >> 7 lea ReciprocTable,a6 ;a6 -> reciprocal table move 0(a6,d2.w*2),d0 ;d0 = (1/(spancount-1))<<16 muls d0,d4 ;d4 = d4 / (spancount-1) asr.l #7,d4 ;sstep = d4 >> 7 muls d0,d5 ;d5 = d5 / (spancount-1) asr.l #7,d5 ;tstep = d5 >> 7 bra.b .mainloop ENDC .special cmp #1,d2 ;switch (spancount-1) ble.b .mainloop ;0,1 -> no scaling needed cmp #3,d2 ;3 -> standard qdiv beq.b .qdiv blt.b .spec_2 asr.l #2,d4 ;4 -> scale by shifting right asr.l #2,d5 bra.b .mainloop .spec_8 asr.l #3,d4 ;8 -> scale by shifting right asr.l #3,d5 bra.b .mainloop .spec_2 asr.l #1,d4 ;2 -> scale by shifting right asr.l #1,d5 ENDC ****** Main drawing loop. Here lies the speed. ****** Very optimized (removed multiplication from inner loop) ****** d2 : spancount ****** d4 : sstep ****** d5 : tstep ****** d6 : s ****** d7 : t ****** a0 : pdest ****** a1 : pbase * do * { * btemp = *(pbase + (s >> 16) + (t >> 16) * cachewidth); * if (*pz <= (izi >> 16)) * { * *pdest = mainTransTable[(btemp<<8) + (*pdest)]; * } * izi += izistep; * pdest++; * pz++; * s += sstep; * t += tstep; * } while (--spancount > 0); .mainloop ;move.l d1,-(sp) movem.l d1/a2-a4,.savearea(sp) lea .PixTable,a6 ;a6 -> Functable move.l _cachewidth,d3 ;read cachewidth move.l 0(a6,d2.w*4),a6 ;get pointer to function swap d7 swap d4 move.l d7,d1 swap d5 muls d3,d7 ;d7 = t integer part * cachewidth move d5,d2 clr d1 ;d1 = t fractional part muls d3,d2 ;d2 = tstep integer part * cachewidth move d4,d0 ;d0 = sstep integer part clr d5 ;d5 = tstep fractional part clr d4 ;d4 = sstep fractional part swap d6 ;d6 = s swapped ; translucent move.l d3,a2 ;a2 = cachewidth move.l _mainTransTable,a3 move.l .pz(sp),a4 ;a4 = pz ; translucent jmp (a6) .Pix16 ;lea 0(a1,d6.w),a6 ;move.b 0(a6,d7.l),(a0)+ ; translucent ;move.l .izi(sp),d3 ;swap d3 ;d3 = izi >> 16 move.w .izi(sp),d3 ;d3 = izi >> 16 cmp.w (a4),d3 ;if (*pz <= (izi >> 16)) blt.b .Pix16_next lea 0(a1,d6.w),a6 clr.l d3 move.b 0(a6,d7.l),d3 ;btemp = *(pbase + (s >> 16) + (t >> 16) * cachewidth) lsl.w #8,d3 move.b (a0),d3 ;(btemp<<8) + (*pdest) move.b 0(a3,d3.l),(a0) .Pix16_next addq.l #1,a0 ;pdest++ move.l .izistep(sp),d3 add.l d3,.izi(sp) ;izi += izistep addq.l #2,a4 ;pz++ ; translucent add.l d4,d6 ;increment s fractional part addx.w d0,d6 ;increment s integer part add.l d2,d7 ;increment t integer part add.l d5,d1 ;increment t fractional part bcc.b .Pix15 ;check if carry add.l a2,d7 ;add cachewidth to t .Pix15 ;lea 0(a1,d6.w),a6 ;and so long... ;move.b 0(a6,d7.l),(a0)+ ; translucent ;move.l .izi(sp),d3 ;swap d3 move.w .izi(sp),d3 cmp.w (a4),d3 blt.b .Pix15_next lea 0(a1,d6.w),a6 clr.l d3 move.b 0(a6,d7.l),d3 lsl.w #8,d3 move.b (a0),d3 move.b 0(a3,d3.l),(a0) .Pix15_next addq.l #1,a0 move.l .izistep(sp),d3 add.l d3,.izi(sp) addq.l #2,a4 ; translucent add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix14 add.l a2,d7 .Pix14 ;lea 0(a1,d6.w),a6 ;move.b 0(a6,d7.l),(a0)+ ; translucent ; move.l .izi(sp),d3 ; swap d3 move.w .izi(sp),d3 cmp.w (a4),d3 blt.b .Pix14_next lea 0(a1,d6.w),a6 clr.l d3 move.b 0(a6,d7.l),d3 lsl.w #8,d3 move.b (a0),d3 move.b 0(a3,d3.l),(a0) .Pix14_next addq.l #1,a0 move.l .izistep(sp),d3 add.l d3,.izi(sp) addq.l #2,a4 ; translucent add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix13 add.l a2,d7 .Pix13 ;lea 0(a1,d6.w),a6 ;move.b 0(a6,d7.l),(a0)+ ; translucent ; move.l .izi(sp),d3 ; swap d3 move.w .izi(sp),d3 cmp.w (a4),d3 blt.b .Pix13_next lea 0(a1,d6.w),a6 clr.l d3 move.b 0(a6,d7.l),d3 lsl.w #8,d3 move.b (a0),d3 move.b 0(a3,d3.l),(a0) .Pix13_next addq.l #1,a0 move.l .izistep(sp),d3 add.l d3,.izi(sp) addq.l #2,a4 ; translucent add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix12 add.l a2,d7 .Pix12 ;lea 0(a1,d6.w),a6 ;move.b 0(a6,d7.l),(a0)+ ; translucent ; move.l .izi(sp),d3 ; swap d3 move.w .izi(sp),d3 cmp.w (a4),d3 blt.b .Pix12_next lea 0(a1,d6.w),a6 clr.l d3 move.b 0(a6,d7.l),d3 lsl.w #8,d3 move.b (a0),d3 move.b 0(a3,d3.l),(a0) .Pix12_next addq.l #1,a0 move.l .izistep(sp),d3 add.l d3,.izi(sp) addq.l #2,a4 ; translucent add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix11 add.l a2,d7 .Pix11 ;lea 0(a1,d6.w),a6 ;move.b 0(a6,d7.l),(a0)+ ; translucent ;move.l .izi(sp),d3 ;swap d3 move.w .izi(sp),d3 cmp.w (a4),d3 blt.b .Pix11_next lea 0(a1,d6.w),a6 clr.l d3 move.b 0(a6,d7.l),d3 lsl.w #8,d3 move.b (a0),d3 move.b 0(a3,d3.l),(a0) .Pix11_next addq.l #1,a0 move.l .izistep(sp),d3 add.l d3,.izi(sp) addq.l #2,a4 ; translucent add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix10 add.l a2,d7 .Pix10 ;lea 0(a1,d6.w),a6 ;move.b 0(a6,d7.l),(a0)+ ; translucent ;move.l .izi(sp),d3 ;swap d3 move.w .izi(sp),d3 cmp.w (a4),d3 blt.b .Pix10_next lea 0(a1,d6.w),a6 clr.l d3 move.b 0(a6,d7.l),d3 lsl.w #8,d3 move.b (a0),d3 move.b 0(a3,d3.l),(a0) .Pix10_next addq.l #1,a0 move.l .izistep(sp),d3 add.l d3,.izi(sp) addq.l #2,a4 ; translucent add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix9 add.l a2,d7 .Pix9 ;lea 0(a1,d6.w),a6 ;move.b 0(a6,d7.l),(a0)+ ; translucent ;move.l .izi(sp),d3 ;swap d3 move.w .izi(sp),d3 cmp.w (a4),d3 blt.b .Pix9_next lea 0(a1,d6.w),a6 clr.l d3 move.b 0(a6,d7.l),d3 lsl.w #8,d3 move.b (a0),d3 move.b 0(a3,d3.l),(a0) .Pix9_next addq.l #1,a0 move.l .izistep(sp),d3 add.l d3,.izi(sp) addq.l #2,a4 ; translucent add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix8 add.l a2,d7 .Pix8 ;lea 0(a1,d6.w),a6 ;move.b 0(a6,d7.l),(a0)+ ; translucent ;move.l .izi(sp),d3 ;swap d3 move.w .izi(sp),d3 cmp.w (a4),d3 blt.b .Pix8_next lea 0(a1,d6.w),a6 clr.l d3 move.b 0(a6,d7.l),d3 lsl.w #8,d3 move.b (a0),d3 move.b 0(a3,d3.l),(a0) .Pix8_next addq.l #1,a0 move.l .izistep(sp),d3 add.l d3,.izi(sp) addq.l #2,a4 ; translucent add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix7 add.l a2,d7 .Pix7 ;lea 0(a1,d6.w),a6 ;move.b 0(a6,d7.l),(a0)+ ; translucent ;move.l .izi(sp),d3 ;swap d3 move.w .izi(sp),d3 cmp.w (a4),d3 blt.b .Pix7_next lea 0(a1,d6.w),a6 clr.l d3 move.b 0(a6,d7.l),d3 lsl.w #8,d3 move.b (a0),d3 move.b 0(a3,d3.l),(a0) .Pix7_next addq.l #1,a0 move.l .izistep(sp),d3 add.l d3,.izi(sp) addq.l #2,a4 ; translucent add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix6 add.l a2,d7 .Pix6 ;lea 0(a1,d6.w),a6 ;move.b 0(a6,d7.l),(a0)+ ; translucent ;move.l .izi(sp),d3 ;swap d3 move.w .izi(sp),d3 cmp.w (a4),d3 blt.b .Pix6_next lea 0(a1,d6.w),a6 clr.l d3 move.b 0(a6,d7.l),d3 lsl.w #8,d3 move.b (a0),d3 move.b 0(a3,d3.l),(a0) .Pix6_next addq.l #1,a0 move.l .izistep(sp),d3 add.l d3,.izi(sp) addq.l #2,a4 ; translucent add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix5 add.l a2,d7 .Pix5 ;lea 0(a1,d6.w),a6 ;move.b 0(a6,d7.l),(a0)+ ; translucent ;move.l .izi(sp),d3 ;swap d3 move.w .izi(sp),d3 cmp.w (a4),d3 blt.b .Pix5_next lea 0(a1,d6.w),a6 clr.l d3 move.b 0(a6,d7.l),d3 lsl.w #8,d3 move.b (a0),d3 move.b 0(a3,d3.l),(a0) .Pix5_next addq.l #1,a0 move.l .izistep(sp),d3 add.l d3,.izi(sp) addq.l #2,a4 ; translucent add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix4 add.l a2,d7 .Pix4 ;lea 0(a1,d6.w),a6 ;move.b 0(a6,d7.l),(a0)+ ; translucent ;move.l .izi(sp),d3 ;swap d3 move.w .izi(sp),d3 cmp.w (a4),d3 blt.b .Pix4_next lea 0(a1,d6.w),a6 clr.l d3 move.b 0(a6,d7.l),d3 lsl.w #8,d3 move.b (a0),d3 move.b 0(a3,d3.l),(a0) .Pix4_next addq.l #1,a0 move.l .izistep(sp),d3 add.l d3,.izi(sp) addq.l #2,a4 ; translucent add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix3 add.l a2,d7 .Pix3 ;lea 0(a1,d6.w),a6 ;move.b 0(a6,d7.l),(a0)+ ; translucent ;move.l .izi(sp),d3 ;swap d3 move.w .izi(sp),d3 cmp.w (a4),d3 blt.b .Pix3_next lea 0(a1,d6.w),a6 clr.l d3 move.b 0(a6,d7.l),d3 lsl.w #8,d3 move.b (a0),d3 move.b 0(a3,d3.l),(a0) .Pix3_next addq.l #1,a0 move.l .izistep(sp),d3 add.l d3,.izi(sp) addq.l #2,a4 ; translucent add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix2 add.l a2,d7 .Pix2 ;lea 0(a1,d6.w),a6 ;move.b 0(a6,d7.l),(a0)+ ; translucent ;move.l .izi(sp),d3 ;swap d3 move.w .izi(sp),d3 cmp.w (a4),d3 blt.b .Pix2_next lea 0(a1,d6.w),a6 clr.l d3 move.b 0(a6,d7.l),d3 lsl.w #8,d3 move.b (a0),d3 move.b 0(a3,d3.l),(a0) .Pix2_next addq.l #1,a0 move.l .izistep(sp),d3 add.l d3,.izi(sp) addq.l #2,a4 ; translucent add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix1 add.l a2,d7 .Pix1 ;lea 0(a1,d6.w),a6 ;move.b 0(a6,d7.l),(a0)+ ; translucent ;move.l .izi(sp),d3 ;swap d3 move.w .izi(sp),d3 cmp.w (a4),d3 blt.b .Pix1_next lea 0(a1,d6.w),a6 clr.l d3 move.b 0(a6,d7.l),d3 lsl.w #8,d3 move.b (a0),d3 move.b 0(a3,d3.l),(a0) .Pix1_next addq.l #1,a0 move.l .izistep(sp),d3 add.l d3,.izi(sp) addq.l #2,a4 ; translucent add.l d4,d6 addx.w d0,d6 add.l d2,d7 add.l d5,d1 bcc.b .Pix0 add.l a2,d7 .Pix0 move.l a4,.pz(sp) ;save pz onto the stack movem.l .savearea(sp),d1/a2-a4 ;move.l (sp)+,d1 ****** loop terminations move.l .saved5(sp),d7 ;t = tnext move.l .saved4(sp),d6 ;s = snext tst.l d1 ;while (count > 0) bgt.w .loop2 move.l .savea6(sp),a6 ;while ((pspan = pspan->next) != NULL) move.l SPAN_PNEXT(a6),a6 tst.l a6 bne.w .loop add.l #.fpuregs,sp fmovem.x (sp)+,fp2-fp7 movem.l (sp)+,d2-d7/a2-a6 rts .PixTable dc.l .Pix1 dc.l .Pix2 dc.l .Pix3 dc.l .Pix4 dc.l .Pix5 dc.l .Pix6 dc.l .Pix7 dc.l .Pix8 dc.l .Pix9 dc.l .Pix10 dc.l .Pix11 dc.l .Pix12 dc.l .Pix13 dc.l .Pix14 dc.l .Pix15 dc.l .Pix16 ****************************************************************************** * * void D_DrawZSpans (espan_t *pspan) * * standard z-scan drawing function * ****************************************************************************** cnop 0,4 _D_DrawZSpans ****** Prologue. Global variables are put into registers or onto the stack ***** stackframe rsreset .fpuregs rs.x 5 .intregs rs.l 7 rs.l 1 .pspan rs.l 1 movem.l d2-d7/a2,-(sp) fmovem.x fp3-fp7,-(sp) move.l .pspan(sp),a2 move.l _d_pzbuffer,a0 move.l _d_zwidth,d7 fmove.s _d_ziorigin,fp5 fmove.s _d_zistepv,fp6 fmove.s _d_zistepu,fp7 fmove.s #32768*65536,fp0 * izistep = (int)(d_zistepu * 0x8000 * 0x10000); fmove fp7,fp1 ;fp1 = d_zistepu fmul fp0,fp1 ;multiply by $8000*$10000 fmove.l fp1,d4 ;izistep = d4 moveq #16,d6 * pdest = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; * * count = pspan->count; * * // calculate the initial 1/z * du = (float)pspan->u; * dv = (float)pspan->v; * * zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; * // we count on FP exceptions being turned off to avoid range problems * izi = (int)(zi * 0x8000 * 0x10000); .loop move.l (a2)+,d0 fmove fp7,fp4 fmul.l d0,fp4 ;fp4 = du * d_zistepu move.l (a2)+,d1 fmove fp6,fp3 fmul.l d1,fp3 ;fp3 = dv * d_zistepv move.l (a2)+,d2 fadd fp3,fp4 muls d7,d1 ;d1 = pspan->v * d_zwidth fadd fp5,fp4 ;fp4 = d_ziorigin + fp3 + fp4 add.l d0,d1 ;d1 = d1 + pspan->u lea 0(a0,d1.l*2),a1 ;pdest = d_pzbuffer + d1 fmul fp0,fp4 ;izi = zi * $8000 * $10000 fmove.l fp4,d3 ;convert to integer * if ((long)pdest & 0x02) * { * *pdest++ = (short)(izi >> 16); * izi += izistep; * count--; * } move.l a1,d0 ;if ((long)pdest & 0x02) and.l #2,d0 beq.b .cont swap d3 move d3,(a1)+ ;*pdest++ = (short)(izi>>16) swap d3 add.l d4,d3 ;izi += izistep; subq #1,d2 ;count-- .cont * if ((doublecount = count >> 1) > 0) * { * do * { * ltemp = izi >> 16; * izi += izistep; * ltemp |= izi & 0xFFFF0000; * izi += izistep; * *(int *)pdest = ltemp; * pdest += 2; * } while (--doublecount > 0); * } move.l d2,d0 ;if ((doublecount=count>>1)>0) asr.l #1,d0 ble.b .cont2 subq #1,d0 .loop2 move.l d3,d5 lsr.l d6,d5 ;temp = izi >> 16 add.l d4,d3 ;izi += izistep move.l d3,d1 and.l #$ffff0000,d1 or.l d1,d5 ;ltemp |= izi & 0xFFFF0000 add.l d4,d3 ;izi += izistep move.l d5,(a1)+ ;*(int *)pdest = ltemp dbra d0,.loop2 ;while (--doublecount > 0) .cont2 * if (count & 1) * *pdest = (short)(izi >> 16); and.l #$1,d2 ;if (count & 1) beq.b .cont3 swap d3 move d3,(a1)+ ;*pdest = (short)(izi >> 16) .cont3 * } while ((pspan = pspan->pnext) != NULL); move.l (a2)+,a2 tst.l a2 bne.w .loop fmovem.x (sp)+,fp3-fp7 movem.l (sp)+,d2-d7/a2 rts ReciprocTable dc.w 0 dc.w 0 dc.w 0 dc.w 16384/3 dc.w 0 dc.w 16384/5 dc.w 16384/6 dc.w 16384/7 dc.w 0 dc.w 16384/9 dc.w 16384/10 dc.w 16384/11 dc.w 16384/12 dc.w 16384/13 dc.w 16384/14 dc.w 16384/15 engine/h2shared/d_scana.asm000066400000000000000000000117221444734033100161230ustar00rootroot00000000000000; ; d_scana.asm ; x86 assembly-language turbulent texture mapping code ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix r_turb_s _sym_prefix r_turb_t _sym_prefix r_turb_pdest _sym_prefix r_turb_spancount _sym_prefix r_turb_turb _sym_prefix r_turb_pbase _sym_prefix r_turb_sstep _sym_prefix r_turb_tstep _sym_prefix mainTransTable _sym_prefix scanList ; C-shared globals: _sym_prefix D_DrawTurbulent8Span _sym_prefix D_DrawTurbulent8TSpan _sym_prefix D_DrawTurbulent8TQuickSpan _sym_prefix D_DrawTurbulent8TSpanEnd _sym_prefix R_TranPatch7 %endif ; _sym_prefix ; externs from C code extern r_turb_s extern r_turb_t extern r_turb_pdest extern r_turb_spancount extern r_turb_turb extern r_turb_pbase extern r_turb_sstep extern r_turb_tstep extern mainTransTable extern scanList ; externs from ASM-only code SEGMENT .data SEGMENT .text ;;;;;;;;;;;;;;;;;;;;;;;; ; D_DrawTurbulent8Span ;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 4 global D_DrawTurbulent8Span D_DrawTurbulent8Span: push ebp push esi push edi push ebx mov esi, dword [r_turb_s] mov ecx, dword [r_turb_t] mov edi, dword [r_turb_pdest] mov ebx, dword [r_turb_spancount] Llp: mov eax,ecx mov edx,esi sar eax,16 mov ebp, dword [r_turb_turb] sar edx,16 and eax,128-1 and edx,128-1 mov eax, dword [ebp+eax*4] mov edx, dword [ebp+edx*4] add eax,esi sar eax,16 add edx,ecx sar edx,16 and eax,64-1 and edx,64-1 shl edx,6 mov ebp, dword [r_turb_pbase] add edx,eax inc edi add esi, dword [r_turb_sstep] add ecx, dword [r_turb_tstep] mov dl, byte [ebp+edx*1] dec ebx mov byte [-1+edi],dl jnz Llp mov dword [r_turb_pdest],edi pop ebx pop edi pop esi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_DrawTurbulent8TSpan ;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 4 global D_DrawTurbulent8TSpan D_DrawTurbulent8TSpan: push ebp push esi push edi push ebx mov esi, dword [r_turb_s] mov ecx, dword [r_turb_t] mov edi, dword [r_turb_pdest] mov ebx, dword [r_turb_spancount] LlpT: mov eax,ecx mov edx,esi sar eax,16 mov ebp, dword [r_turb_turb] sar edx,16 and eax,128-1 and edx,128-1 mov eax, dword [ebp+eax*4] mov edx, dword [ebp+edx*4] add eax,esi sar eax,16 add edx,ecx sar edx,16 and eax,64-1 and edx,64-1 shl edx,6 mov ebp, dword [r_turb_pbase] add edx,eax cmp byte [scanList + ebx - 1], 1 jnz skip1 inc edi mov dh, byte [ebp+edx*1] add esi, dword [r_turb_sstep] mov dl, byte [-1+edi] add ecx, dword [r_turb_tstep] mov dl, byte [12345678h + edx] TranPatch1: dec ebx mov byte [-1+edi],dl ;mov byte [-1+edi],255 jnz LlpT skip2: mov dword [r_turb_pdest],edi pop ebx pop edi pop esi pop ebp ret skip1: inc edi dec ebx jnz LlpT jmp skip2 ;;;;;;;;;;;;;;;;;;;;;;;; ; D_DrawTurbulent8TQuickSpan ;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 4 global D_DrawTurbulent8TQuickSpan D_DrawTurbulent8TQuickSpan: push ebp push esi push edi push ebx mov esi, dword [r_turb_s] mov ecx, dword [r_turb_t] mov edi, dword [r_turb_pdest] mov ebx, dword [r_turb_spancount] LlpTQ: mov eax,ecx mov edx,esi sar eax,16 mov ebp, dword [r_turb_turb] sar edx,16 and eax,128-1 and edx,128-1 mov eax, dword [ebp+eax*4] mov edx, dword [ebp+edx*4] add eax,esi sar eax,16 add edx,ecx sar edx,16 and eax,64-1 and edx,64-1 shl edx,6 mov ebp, dword [r_turb_pbase] add edx,eax ;cmp byte [scanList + ebx - 1], 1 ;jnz skip1 inc edi mov dh, byte [ebp+edx*1] add esi, dword [r_turb_sstep] mov dl, byte [-1+edi] add ecx, dword [r_turb_tstep] mov dl, byte [12345678h + edx] TranPatch2: dec ebx mov byte [-1+edi],dl jnz LlpTQ mov dword [r_turb_pdest],edi pop ebx pop edi pop esi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_DrawTurbulent8TSpanEnd ;;;;;;;;;;;;;;;;;;;;;;;; global D_DrawTurbulent8TSpanEnd D_DrawTurbulent8TSpanEnd: SEGMENT .data ALIGN 4 LPatchTable: dd TranPatch1-4 dd TranPatch2-4 SEGMENT .text ALIGN 4 ;;;;;;;;;;;;;;;;;;;;;;;; ; R_TranPatch7 ;;;;;;;;;;;;;;;;;;;;;;;; global R_TranPatch7 R_TranPatch7: push ebx mov eax, dword [mainTransTable] mov ebx,offset LPatchTable mov ecx,2 LPatchLoop: mov edx, dword [ebx] add ebx,4 mov dword [edx],eax dec ecx jnz LPatchLoop pop ebx ret engine/h2shared/d_sky.c000066400000000000000000000070401444734033100153040ustar00rootroot00000000000000/* * d_sky.c * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" #include "d_local.h" #define SKY_SPAN_SHIFT 5 #define SKY_SPAN_MAX (1 << SKY_SPAN_SHIFT) #if !id68k /* ================= D_Sky_uv_To_st ================= */ static void D_Sky_uv_To_st (int u, int v, fixed16_t *s, fixed16_t *t) { float wu, wv, temp; vec3_t end; /*if (r_refdef.vrect.width >= r_refdef.vrect.height) temp = (float)r_refdef.vrect.width; else temp = (float)r_refdef.vrect.height; wu = 8192.0 * (float)(u - (vid.width>>1)) / temp; wv = 8192.0 * (float)((vid.height>>1) - v)/ temp; end[0] = 4096*vpn[0] + wu*vright[0] + wv*vup[0]; end[1] = 4096*vpn[1] + wu*vright[1] + wv*vup[1]; end[2] = 4096*vpn[2] + wu*vright[2] + wv*vup[2];*/ // ToChriS - begin wu = (u - xcenter) / xscale; wv = (ycenter - v) / yscale; end[0] = vpn[0] + wu*vright[0] + wv*vup[0]; end[1] = vpn[1] + wu*vright[1] + wv*vup[1]; end[2] = vpn[2] + wu*vright[2] + wv*vup[2]; // ToChriS - end end[2] *= 3; VectorNormalizeFast (end); temp = skytime*skyspeed; // TODO: add D_SetupFrame & set this there *s = (int)((temp + 6*(SKYSIZE/2-1)*end[0]) * 0x10000); *t = (int)((temp + 6*(SKYSIZE/2-1)*end[1]) * 0x10000); } /* ================= D_DrawSkyScans8 ================= */ void D_DrawSkyScans8 (espan_t *pspan) { int count, spancount, u, v; unsigned char *pdest; fixed16_t s, t, snext, tnext, sstep, tstep; int spancountminus1; sstep = 0; // keep compiler happy tstep = 0; // ditto snext = 0; // MSVC builds are reported to crash w/o these. tnext = 0; // see: sf.net/p/uhexen2/bugs/62 do { pdest = (unsigned char *)((byte *)d_viewbuffer + (screenwidth * pspan->v) + pspan->u); count = pspan->count; // calculate the initial s & t u = pspan->u; v = pspan->v; D_Sky_uv_To_st (u, v, &s, &t); do { if (count >= SKY_SPAN_MAX) spancount = SKY_SPAN_MAX; else spancount = count; count -= spancount; if (count) { u += spancount; // calculate s and t at far end of span, // calculate s and t steps across span by shifting D_Sky_uv_To_st (u, v, &snext, &tnext); sstep = (snext - s) >> SKY_SPAN_SHIFT; tstep = (tnext - t) >> SKY_SPAN_SHIFT; } else { // calculate s and t at last pixel in span, // calculate s and t steps across span by division spancountminus1 = (float)(spancount - 1); if (spancountminus1 > 0) { u += spancountminus1; D_Sky_uv_To_st (u, v, &snext, &tnext); sstep = (snext - s) / spancountminus1; tstep = (tnext - t) / spancountminus1; } } do { *pdest++ = r_skysource[((t & R_SKY_TMASK) >> 8) + ((s & R_SKY_SMASK) >> 16)]; s += sstep; t += tstep; } while (--spancount > 0); s = snext; t = tnext; } while (count > 0); } while ((pspan = pspan->pnext) != NULL); } #endif /* !id68k */ engine/h2shared/d_sky68k.s000066400000000000000000000331721444734033100156620ustar00rootroot00000000000000** ** Quake for AMIGA ** d_sky.c assembler implementations by Frank Wille ** Adapted for Hexen II by Szilard Biro ** ; INCLUDE "quakedef68k.i" XREF _d_viewbuffer XREF _screenwidth XREF _r_skysource XREF _vright XREF _vpn XREF _vup XREF _skytime XREF _skyspeed XREF _xcenter XREF _xscale XREF _ycenter XREF _yscale XDEF _D_DrawSkyScans8 SKYSHIFT = 7 SKYSIZE = (1 << SKYSHIFT) SKYMASK = (SKYSIZE - 1) SKY_SPAN_SHIFT = 5 SKY_SPAN_MAX = (1 << SKY_SPAN_SHIFT) R_SKY_SMASK = $007f R_SKY_TMASK = $007f ****************************************************************************** * * void D_DrawSkyScans8 (espan_t *pspan) * * standard scan drawing function for the sky * * D_Sky_uv_To_st is inlined. * * IMPORTANT!! SKY_SPAN_SHIFT must *NOT* exceed 5 (The ReciprocTable * has to be extended) * ****************************************************************************** cnop 0,4 _D_DrawSkyScans8 ***** stackframe rsreset .savefp1 rs.x 1 .saved4 rs.l 1 .saved5 rs.l 1 .vr0 rs.s 1 .vr1 rs.s 1 .vr2 rs.s 1 .fpuregs rs.x 6 .intregs rs.l 11 rs.l 1 .pspan rs.l 1 ****** Prologue. Global variables are put into registers or onto the stackframe movem.l d2-d7/a2-a6,-(sp) fmovem.x fp2-fp7,-(sp) sub.l #.fpuregs,sp ****** First loop. In every iteration one complete span is drawn move.l .pspan(sp),a6 ;get function parameter fmove.s #1,fp1 fdiv.s _xscale,fp1 ;fp1 = 1 / xscale move.l _r_skysource,a5 .loop fmove.x fp1,.savefp1(sp) move.l _d_viewbuffer,a0 move.l _screenwidth,d0 move.l (a6)+,d3 ;d3 = pspan->u move.l (a6)+,d2 ;d2 = pspan->v fmove.l d2,fp4 ;fp4 = v muls d2,d0 ;d0 = screenwidth * pspan->v add.l d3,d0 add.l d0,a0 ;pdest = d_viewbuffer + pspan->u + d0 lea _vpn,a2 fmove.s (a2)+,fp5 ;fp5 = vpn[0] fmove.s (a2)+,fp6 ;fp6 = vpn[1] fmove.s (a2)+,fp7 ;fp7 = vpn[2] ;fmove.l _xcenter,d0 fmove.s _xcenter,fp0 fmove.l fp0,d0 move.l d0,a2 ;a2 = (int)xcenter fmove.s _ycenter,fp0 fsub fp4,fp0 ;fp0 = (ycenter-v) fdiv.s _yscale,fp0 ;fp0 = wv = (ycenter-v) / yscale lea _vup,a1 fmove.s (a1)+,fp2 ;fp2 = vup[0] fmul fp0,fp2 ;fp2 = wv*vup[0] fadd fp2,fp5 ;fp5 = vpn[0] + wv*vup[0] fmove.s (a1)+,fp3 ;fp3 = vup[1] fmul fp0,fp3 ;fp3 = wv*vup[1] fadd fp3,fp6 ;fp6 = vpn[1] + wv*vup[1] fmul.s (a1)+,fp0 ;fp0 = wv*vup[2] fadd fp0,fp7 ;fp7 = vpn[2] + wv*vup[2] lea _vright,a1 fmove.s (a1)+,fp2 ;fp2 = vright[0] fmove.s (a1)+,fp3 ;fp3 = vright[1] fmove.s (a1)+,fp4 ;fp4 = vright[2] fmul fp1,fp2 ;fp2 = vright[0] / xscale fmul fp1,fp3 ;fp3 = vright[1] / xscale fmul fp1,fp4 ;fp4 = vright[2] / xscale fmove.s fp2,.vr0(sp) fmove.s fp3,.vr1(sp) fmove.s #3,fp1 fmul fp1,fp7 ;fp7 = (vpn[2] + wv*vup[2]) * 3 fmul fp1,fp4 ;fp4 = (vright[2] / xscale) * 3 fmove.s fp4,.vr2(sp) move.l (a6)+,d1 ;d1 = count = pspan->count fmove.s _skytime,fp0 fmul.s _skyspeed,fp0 fmul.s #65536,fp0 fmove.l fp0,d0 ;d0 = skytime*skyspeed*$10000 move.l d0,a4 ;a4 = skytime*skyspeed*$10000 ****** D_Sky_uv_To_st (inlined) move.l d3,d0 sub.l a2,d0 fmove.l d0,fp0 ;fp0 = (float(u-(int)xcenter)) fmove fp0,fp2 fmul.s .vr0(sp),fp2 ;fp2 = wu*vright[0] fadd fp5,fp2 ;fp2 = end[0] = wu*vright[0] + vpn[0] + wv*vup[0] fmove fp2,fp1 ;fp1 = end[0] fmul fp2,fp2 ;fp2 = end[0]*end[0] fmove fp0,fp3 ;fp3 = u - xcenter fmul.s .vr1(sp),fp3 ;fp3 = wu*vright[1] fadd fp6,fp3 ;fp3 = end[1] = wu*vright[1] + vpn[1] + wv*vup[1] fmove fp3,fp4 ;fp4 = end[1] fmul fp3,fp3 ;fp3 = end[1]*end[1] fadd fp3,fp2 ;fp2 = end[0]*end[0] + end[1]*end[1] fmul.s .vr2(sp),fp0 ;fp0 = wu*vright[2] * 3 fadd fp7,fp0 ;fp0 = end[2] = wu*vright[2] * 3 + (vpn[2] + wv*vup[2]) * 3 fmul fp0,fp0 ;fp2 = end[2] * end[2] fadd fp0,fp2 ;fp2 = end[0]*end[0] + end[1]*end[1] + end[2] * end[2] ;fsqrt fp2 ;fp2 = length(end) ; fast inverse square root fmove.s fp2,d0 ;d0 = -(t.i >> 1) lsr.l #1,d0 neg.l d0 add.l #1597463007,d0 ;d0 = d0 + 0x5f3759df fmul.s #0.5,fp2 ;fp2 = fp2 * 0.5f fmul.s d0,fp2 ;fp2 = fp2 * t.f * t.f fmul.s d0,fp2 fneg.x fp2 ;fp2 = 1.5f - fp2 fadd.s #1.5,fp2 fmul.s d0,fp2 ;fp2 = fp2 * t.f = 1/length(end) ; fast inverse square root fmove.s #(65536*6*(SKYSIZE/2-1)),fp0 ;fdiv fp2,fp0 ;6*(SKYSIZE/2-1) * 0x10000 / length(end) fmul fp2,fp0 ;6*(SKYSIZE/2-1) * 0x10000 * (1/length(end)) fmul fp0,fp1 ;fp1 = 6*(SKYSIZE/2-1)*end[0] fmul fp0,fp4 ;fp4 = 6*(SKYSIZE/2-1)*end[1] fmove.l fp1,d6 add.l a4,d6 ;d6 = s fmove.l fp4,d7 add.l a4,d7 ;d7 = t ****** end of D_Sky_uv_To_st ****** Second loop. In every iteration one part of the whole span is drawn ****** d2 gets the value (spancount-1)! [NOT spancount] ****** d1 = count * do * { * if (count >= SKY_SPAN_MAX) * spancount = SKY_SPAN_MAX; * else * spancount = count; * * count -= spancount; * * if (count) * { .loop2 move.l #SKY_SPAN_MAX-1,d2 ;spancount = SKY_SPAN_MAX cmp.l #SKY_SPAN_MAX,d1 ;if (count >= SKY_SPAN_MAX) bgt.b .cont move.l d1,d2 ;spancount = count subq.l #1,d2 moveq #0,d1 ;count -= spancount bra.w .finalpart .cont sub.l #SKY_SPAN_MAX,d1 ;count -= spancount; ****** Evaluation of the values for the inner loop. This version is used for ****** span size = SKY_SPAN_MAX * // calculate s and t at far end of span, * // calculate s and t steps across span by shifting * u += spancount; * * D_Sky_uv_To_st (u, v, &snext, &tnext); * * sstep = (snext - s) >> SKY_SPAN_SHIFT; * tstep = (tnext - t) >> SKY_SPAN_SHIFT; * } add.l d2,d3 addq.l #1,d3 ****** D_Sky_uv_To_st (inlined) move.l d3,d0 sub.l a2,d0 fmove.l d0,fp0 ;fp0 = (float(u-(int)xcenter)) fmove fp0,fp2 ;fp2 = u - xcenter fmul.s .vr0(sp),fp2 ;fp2 = wu*vright[0] fadd fp5,fp2 ;fp2 = end[0] fmove fp2,fp1 fmul fp2,fp2 fmove fp0,fp3 fmul.s .vr1(sp),fp3 ;fp3 = wu*vright[1] fadd fp6,fp3 ;fp3 = end[1] fmove fp3,fp4 fmul fp3,fp3 fadd fp3,fp2 fmul.s .vr2(sp),fp0 ;fp0 = wu*vright[2] fadd fp7,fp0 ;fp0 = end[2] fmul fp0,fp0 fadd fp0,fp2 ;fsqrt fp2 ;fp2 = length(end) ; fast inverse square root fmove.s fp2,d0 ;d0 = -(t.i >> 1) lsr.l #1,d0 neg.l d0 add.l #1597463007,d0 ;d0 = d0 + 0x5f3759df fmul.s #0.5,fp2 ;fp2 = fp2 * 0.5f fmul.s d0,fp2 ;fp2 = fp2 * t.f * t.f fmul.s d0,fp2 fneg.x fp2 ;fp2 = 1.5f - fp2 fadd.s #1.5,fp2 fmul.s d0,fp2 ;fp2 = fp2 * t.f = 1/length(end) ; fast inverse square root fmove.s #(65536*6*(SKYSIZE/2-1)),fp0 ;fdiv fp2,fp0 fmul fp2,fp0 fmul fp0,fp1 ;fp1 = 6*(SKYSIZE/2-1)*end[0] fmul fp0,fp4 ;fp4 = 6*(SKYSIZE/2-1)*end[1] fmove.l fp1,d4 add.l a4,d4 ;d6 = snext fmove.l fp4,d5 add.l a4,d5 ;d7 = tnext ****** end of D_Sky_uv_To_st move.l d4,.saved4(sp) ;save snext move.l d5,.saved5(sp) ;save tnext sub.l d6,d4 ;d4 = snext - s sub.l d7,d5 ;d5 = tnext - t asr.l #SKY_SPAN_SHIFT,d4 ;sstep = d4 >> SKY_SPAN_SHIFT asr.l #SKY_SPAN_SHIFT,d5 ;tstep = d5 >> SKY_SPAN_SHIFT bra.w .mainloop .finalpart add.l d2,d3 ****** D_Sky_uv_To_st (inlined) move.l d3,d0 sub.l a2,d0 fmove.l d0,fp0 ;fp0 = (float(u-(int)xcenter)) fmove fp0,fp2 ;fp2 = u - xcenter fmul.s .vr0(sp),fp2 ;fp2 = wu*vright[0] fadd fp5,fp2 ;fp2 = end[0] fmove fp2,fp1 fmul fp2,fp2 fmove fp0,fp3 fmul.s .vr1(sp),fp3 ;fp3 = wu*vright[1] fadd fp6,fp3 ;fp3 = end[1] fmove fp3,fp4 fmul fp3,fp3 fadd fp3,fp2 fmul.s .vr2(sp),fp0 ;fp0 = wu*vright[2] fadd fp7,fp0 ;fp0 = end[2] fmul fp0,fp0 fadd fp0,fp2 ;fsqrt fp2 ;fp2 = length(end) ; fast inverse square root fmove.s fp2,d0 ;d0 = -(t.i >> 1) lsr.l #1,d0 neg.l d0 add.l #1597463007,d0 ;d0 = d0 + 0x5f3759df fmul.s #0.5,fp2 ;fp2 = fp2 * 0.5f fmul.s d0,fp2 ;fp2 = fp2 * t.f * t.f fmul.s d0,fp2 fneg.x fp2 ;fp2 = 1.5f - fp2 fadd.s #1.5,fp2 fmul.s d0,fp2 ;fp2 = fp2 * t.f = 1/length(end) ; fast inverse square root fmove.s #(65536*6*(SKYSIZE/2-1)),fp0 ;fdiv fp2,fp0 fmul fp2,fp0 fmul fp0,fp1 ;fp1 = 6*(SKYSIZE/2-1)*end[0] fmul fp0,fp4 ;fp4 = 6*(SKYSIZE/2-1)*end[1] fmove.l fp1,d4 add.l a4,d4 ;d6 = snext fmove.l fp4,d5 add.l a4,d5 ;d7 = tnext ****** end of D_Sky_uv_To_st move.l d4,.saved4(sp) ;save snext move.l d5,.saved5(sp) ;save tnext sub.l d6,d4 ;d4 = snext - s sub.l d7,d5 ;d5 = tnext - t cmp #5,d2 ;(spancount-1) < 5? blt.b .special ;yes -> special case .qdiv asr.l #7,d4 ;d4 >> 7 asr.l #7,d5 ;d5 >> 7 lea ReciprocTable,a3 ;a3 -> reciprocal table move 0(a3,d2.w*2),d0 ;d0 = (1/(spancount-1))<<16 muls d0,d4 ;d4 = d4 / (spancount-1) asr.l #7,d4 ;sstep = d4 >> 7 muls d0,d5 ;d5 = d5 / (spancount-1) asr.l #7,d5 ;tstep = d5 >> 7 bra.b .mainloop .special cmp #1,d2 ;switch (spancount-1) ble.b .mainloop ;0,1 -> no scaling needed cmp #3,d2 ;3 -> standard qdiv beq.b .qdiv blt.b .spec_2 asr.l #2,d4 ;4 -> scale by shifting right asr.l #2,d5 bra.b .mainloop .spec_2 asr.l #1,d4 ;2 -> scale by shifting right asr.l #1,d5 ****** Main drawing loop. ****** d2 : spancount ****** d4 : sstep ****** d5 : tstep ****** d6 : s ****** d7 : t ****** a0 : pdest ****** a5 : r_skysource * do * { * *pdest++ = r_skysource[((t & R_SKY_TMASK) >> 8) + * ((s & R_SKY_SMASK) >> 16)]; * s += sstep; * t += tstep; * } while (--spancount > 0); .mainloop move.l d1,-(sp) swap d4 swap d5 swap d6 swap d7 move d5,d1 ;d2 = tstep integer part move d4,d0 ;d0 = sstep integer part clr d5 ;d5 = tstep fractional part clr d4 ;d4 = sstep fractional part .loop3 and #R_SKY_TMASK,d7 asl #8,d7 lea 0(a5,d7.w),a3 asr #8,d7 and #R_SKY_SMASK,d6 move.b 0(a3,d6.w),(a0)+ add.l d4,d6 addx.w d0,d6 add.l d5,d7 addx.w d1,d7 dbra d2,.loop3 move.l (sp)+,d1 ****** loop terminations move.l .saved5(sp),d7 ;t = tnext move.l .saved4(sp),d6 ;s = snext tst.l d1 ;while (count > 0) bgt.w .loop2 fmove.x .savefp1(sp),fp1 move.l (a6)+,a6 tst.l a6 bne.w .loop add.l #.fpuregs,sp fmovem.x (sp)+,fp2-fp7 movem.l (sp)+,d2-d7/a2-a6 rts ReciprocTable dc.w 0 dc.w 0 dc.w 0 dc.w 16384/3 dc.w 0 dc.w 16384/5 dc.w 16384/6 dc.w 16384/7 dc.w 16384/8 dc.w 16384/9 dc.w 16384/10 dc.w 16384/11 dc.w 16384/12 dc.w 16384/13 dc.w 16384/14 dc.w 16384/15 dc.w 16384/16 dc.w 16384/17 dc.w 16384/18 dc.w 16384/19 dc.w 16384/20 dc.w 16384/21 dc.w 16384/22 dc.w 16384/23 dc.w 16384/24 dc.w 16384/25 dc.w 16384/26 dc.w 16384/27 dc.w 16384/28 dc.w 16384/29 dc.w 16384/30 dc.w 16384/31 engine/h2shared/d_spr8.asm000066400000000000000000000335051444734033100157350ustar00rootroot00000000000000; ; d_spr8.asm ; x86 assembly-language horizontal 8-bpp sprite span-drawing code. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix d_zistepu _sym_prefix d_pzbuffer _sym_prefix d_zistepv _sym_prefix d_zrowbytes _sym_prefix d_ziorigin _sym_prefix d_sdivzstepu _sym_prefix d_tdivzstepu _sym_prefix d_sdivzstepv _sym_prefix d_tdivzstepv _sym_prefix d_sdivzorigin _sym_prefix d_tdivzorigin _sym_prefix sadjust _sym_prefix tadjust _sym_prefix bbextents _sym_prefix bbextentt _sym_prefix cacheblock _sym_prefix d_viewbuffer _sym_prefix cachewidth _sym_prefix d_scantable ; C-shared globals: _sym_prefix D_SpriteDrawSpans %endif ; _sym_prefix ; externs from C code extern d_zistepu extern d_pzbuffer extern d_zistepv extern d_zrowbytes extern d_ziorigin extern d_sdivzstepu extern d_tdivzstepu extern d_sdivzstepv extern d_tdivzstepv extern d_sdivzorigin extern d_tdivzorigin extern sadjust extern tadjust extern bbextents extern bbextentt extern cacheblock extern d_viewbuffer extern cachewidth extern d_scantable ; externs from ASM-only code extern fp_1m extern fp_1m_minus_1 extern fp_8 extern fp_16 extern fp_64k extern fp_64kx64k extern izi extern izistep extern sstep extern tstep extern advancetable extern spr8entryvec_table extern reciprocal_table extern reciprocal_table_16 extern pbase extern s extern t extern sfracf extern tfracf extern snext extern tnext extern spancountminus1 extern zi16stepu extern sdivz16stepu extern tdivz16stepu extern zi8stepu extern sdivz8stepu extern tdivz8stepu extern pz SEGMENT .text LClampHigh0: mov esi, dword [bbextents] jmp LClampReentry0 LClampHighOrLow0: jg LClampHigh0 xor esi,esi jmp LClampReentry0 LClampHigh1: mov edx, dword [bbextentt] jmp LClampReentry1 LClampHighOrLow1: jg LClampHigh1 xor edx,edx jmp LClampReentry1 LClampLow2: mov ebp,2048 jmp LClampReentry2 LClampHigh2: mov ebp, dword [bbextents] jmp LClampReentry2 LClampLow3: mov ecx,2048 jmp LClampReentry3 LClampHigh3: mov ecx, dword [bbextentt] jmp LClampReentry3 LClampLow4: mov eax,2048 jmp LClampReentry4 LClampHigh4: mov eax, dword [bbextents] jmp LClampReentry4 LClampLow5: mov ebx,2048 jmp LClampReentry5 LClampHigh5: mov ebx, dword [bbextentt] jmp LClampReentry5 ;;;;;;;;;;;;;;;;;;;;;;;; ; D_SpriteDrawSpans ;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 4 global D_SpriteDrawSpans D_SpriteDrawSpans: push ebp push edi push esi push ebx fld dword [d_sdivzstepu] fmul dword [fp_8] mov edx, dword [cacheblock] fld dword [d_tdivzstepu] fmul dword [fp_8] mov ebx, dword [4+16+esp] fld dword [d_zistepu] fmul dword [fp_8] mov dword [pbase],edx fld dword [d_zistepu] fmul dword [fp_64kx64k] fxch st3 fstp dword [sdivz8stepu] fstp dword [zi8stepu] fstp dword [tdivz8stepu] fistp dword [izistep] mov eax, dword [izistep] ror eax,16 mov ecx, dword [8+ebx] mov dword [izistep],eax cmp ecx,0 jle near LNextSpan LSpanLoop: fild dword [4+ebx] fild dword [0+ebx] fld st1 fmul dword [d_sdivzstepv] fld st1 fmul dword [d_sdivzstepu] fld st2 fmul dword [d_tdivzstepu] fxch st1 faddp st2,st0 fxch st1 fld st3 fmul dword [d_tdivzstepv] fxch st1 fadd dword [d_sdivzorigin] fxch st4 fmul dword [d_zistepv] fxch st1 faddp st2,st0 fxch st2 fmul dword [d_zistepu] fxch st1 fadd dword [d_tdivzorigin] fxch st2 faddp st1,st0 fld dword [fp_64k] fxch st1 fadd dword [d_ziorigin] fld st0 fmul dword [fp_64kx64k] fxch st1 fdiv st2,st0 fxch st1 fistp dword [izi] mov ebp, dword [izi] ror ebp,16 mov eax, dword [4+ebx] mov dword [izi],ebp mov ebp, dword [0+ebx] imul dword [d_zrowbytes] shl ebp,1 add eax, dword [d_pzbuffer] add eax,ebp mov dword [pz],eax mov ebp, dword [d_viewbuffer] mov eax, dword [4+ebx] push ebx mov edx, dword [tadjust] mov esi, dword [sadjust] mov edi, dword [d_scantable+eax*4] add edi,ebp mov ebp, dword [0+ebx] add edi,ebp cmp ecx,8 ja LSetupNotLast1 dec ecx jz LCleanup1 mov dword [spancountminus1],ecx fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] fild dword [spancountminus1] fld dword [d_tdivzstepu] fld dword [d_zistepu] fmul st0,st2 fxch st1 fmul st0,st2 fxch st2 fmul dword [d_sdivzstepu] fxch st1 faddp st3,st0 fxch st1 faddp st3,st0 faddp st3,st0 fld dword [fp_64k] fdiv st0,st1 jmp LFDIVInFlight1 LCleanup1: fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] jmp LFDIVInFlight1 ALIGN 4 LSetupNotLast1: fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] fadd dword [zi8stepu] fxch st2 fadd dword [sdivz8stepu] fxch st2 fld dword [tdivz8stepu] faddp st2,st0 fld dword [fp_64k] fdiv st0,st1 LFDIVInFlight1: add esi, dword [s] add edx, dword [t] mov ebx, dword [bbextents] mov ebp, dword [bbextentt] cmp esi,ebx ja near LClampHighOrLow0 LClampReentry0: mov dword [s],esi mov ebx, dword [pbase] shl esi,16 cmp edx,ebp mov dword [sfracf],esi ja near LClampHighOrLow1 LClampReentry1: mov dword [t],edx mov esi, dword [s] shl edx,16 mov eax, dword [t] sar esi,16 mov dword [tfracf],edx sar eax,16 add esi,ebx imul eax, dword [cachewidth] add esi,eax cmp ecx,8 jna near LLastSegment LNotLastSegment: fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [snext] fistp dword [tnext] mov eax, dword [snext] mov edx, dword [tnext] sub ecx,8 mov ebp, dword [sadjust] push ecx mov ecx, dword [tadjust] add ebp,eax add ecx,edx mov eax, dword [bbextents] mov edx, dword [bbextentt] cmp ebp,2048 jl near LClampLow2 cmp ebp,eax ja near LClampHigh2 LClampReentry2: cmp ecx,2048 jl near LClampLow3 cmp ecx,edx ja near LClampHigh3 LClampReentry3: mov dword [snext],ebp mov dword [tnext],ecx sub ebp, dword [s] sub ecx, dword [t] mov eax,ecx mov edx,ebp sar edx,19 mov ebx, dword [cachewidth] sar eax,19 jz LIsZero imul eax,ebx LIsZero: add eax,edx mov edx, dword [tfracf] mov dword [advancetable+4],eax add eax,ebx shl ebp,13 mov dword [sstep],ebp mov ebx, dword [sfracf] shl ecx,13 mov dword [advancetable],eax mov dword [tstep],ecx mov ecx, dword [pz] mov ebp, dword [izi] cmp bp, word [ecx] jl Lp1 mov al, byte [esi] cmp al,255 jz Lp1 mov word [ecx],bp mov byte [edi],al Lp1: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [2+ecx] jl Lp2 mov al, byte [esi] cmp al,255 jz Lp2 mov word [2+ecx],bp mov byte [1+edi],al Lp2: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [4+ecx] jl Lp3 mov al, byte [esi] cmp al,255 jz Lp3 mov word [4+ecx],bp mov byte [2+edi],al Lp3: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [6+ecx] jl Lp4 mov al, byte [esi] cmp al,255 jz Lp4 mov word [6+ecx],bp mov byte [3+edi],al Lp4: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [8+ecx] jl Lp5 mov al, byte [esi] cmp al,255 jz Lp5 mov word [8+ecx],bp mov byte [4+edi],al Lp5: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] pop eax cmp eax,8 ja LSetupNotLast2 dec eax jz LFDIVInFlight2 mov dword [spancountminus1],eax fild dword [spancountminus1] fld dword [d_zistepu] fmul st0,st1 fld dword [d_tdivzstepu] fmul st0,st2 fxch st1 faddp st3,st0 fxch st1 fmul dword [d_sdivzstepu] fxch st1 faddp st3,st0 fld dword [fp_64k] fxch st1 faddp st4,st0 fdiv st0,st1 jmp LFDIVInFlight2 ALIGN 4 LSetupNotLast2: fadd dword [zi8stepu] fxch st2 fadd dword [sdivz8stepu] fxch st2 fld dword [tdivz8stepu] faddp st2,st0 fld dword [fp_64k] fdiv st0,st1 LFDIVInFlight2: push eax cmp bp, word [10+ecx] jl Lp6 mov al, byte [esi] cmp al,255 jz Lp6 mov word [10+ecx],bp mov byte [5+edi],al Lp6: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [12+ecx] jl Lp7 mov al, byte [esi] cmp al,255 jz Lp7 mov word [12+ecx],bp mov byte [6+edi],al Lp7: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [14+ecx] jl Lp8 mov al, byte [esi] cmp al,255 jz Lp8 mov word [14+ecx],bp mov byte [7+edi],al Lp8: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] add edi,8 add ecx,16 mov dword [tfracf],edx mov edx, dword [snext] mov dword [sfracf],ebx mov ebx, dword [tnext] mov dword [s],edx mov dword [t],ebx mov dword [pz],ecx mov dword [izi],ebp pop ecx cmp ecx,8 ja near LNotLastSegment LLastSegment: test ecx,ecx jz near LNoSteps fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [snext] fistp dword [tnext] mov ebx, dword [tadjust] mov eax, dword [sadjust] add eax, dword [snext] add ebx, dword [tnext] mov ebp, dword [bbextents] mov edx, dword [bbextentt] cmp eax,2048 jl near LClampLow4 cmp eax,ebp ja near LClampHigh4 LClampReentry4: mov dword [snext],eax cmp ebx,2048 jl near LClampLow5 cmp ebx,edx ja near LClampHigh5 LClampReentry5: cmp ecx,1 je near LOnlyOneStep sub eax, dword [s] sub ebx, dword [t] add eax,eax add ebx,ebx imul dword [reciprocal_table-8+ecx*4] mov ebp,edx mov eax,ebx imul dword [reciprocal_table-8+ecx*4] LSetEntryvec: mov ebx, dword [spr8entryvec_table+ecx*4] mov eax,edx push ebx mov ecx,ebp sar ecx,16 mov ebx, dword [cachewidth] sar edx,16 jz LIsZeroLast imul edx,ebx LIsZeroLast: add edx,ecx mov ecx, dword [tfracf] mov dword [advancetable+4],edx add edx,ebx shl ebp,16 mov ebx, dword [sfracf] shl eax,16 mov dword [advancetable],edx mov dword [tstep],eax mov dword [sstep],ebp mov edx,ecx mov ecx, dword [pz] mov ebp, dword [izi] ret LNoSteps: mov ecx, dword [pz] sub edi,7 sub ecx,14 jmp LEndSpan LOnlyOneStep: sub eax, dword [s] sub ebx, dword [t] mov ebp,eax mov edx,ebx jmp LSetEntryvec ;;;;;;;;;;;;;;;;;;;;;;;; ; globals Spr8Entry?_8 ;;;;;;;;;;;;;;;;;;;;;;;; global Spr8Entry2_8 Spr8Entry2_8: sub edi,6 sub ecx,12 mov al, byte [esi] jmp LLEntry2_8 global Spr8Entry3_8 Spr8Entry3_8: sub edi,5 sub ecx,10 jmp LLEntry3_8 global Spr8Entry4_8 Spr8Entry4_8: sub edi,4 sub ecx,8 jmp LLEntry4_8 global Spr8Entry5_8 Spr8Entry5_8: sub edi,3 sub ecx,6 jmp LLEntry5_8 global Spr8Entry6_8 Spr8Entry6_8: sub edi,2 sub ecx,4 jmp LLEntry6_8 global Spr8Entry7_8 Spr8Entry7_8: dec edi sub ecx,2 jmp LLEntry7_8 global Spr8Entry8_8 Spr8Entry8_8: cmp bp, word [ecx] jl Lp9 mov al, byte [esi] cmp al,255 jz Lp9 mov word [ecx],bp mov byte [edi],al Lp9: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry7_8: cmp bp, word [2+ecx] jl Lp10 mov al, byte [esi] cmp al,255 jz Lp10 mov word [2+ecx],bp mov byte [1+edi],al Lp10: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry6_8: cmp bp, word [4+ecx] jl Lp11 mov al, byte [esi] cmp al,255 jz Lp11 mov word [4+ecx],bp mov byte [2+edi],al Lp11: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry5_8: cmp bp, word [6+ecx] jl Lp12 mov al, byte [esi] cmp al,255 jz Lp12 mov word [6+ecx],bp mov byte [3+edi],al Lp12: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry4_8: cmp bp, word [8+ecx] jl Lp13 mov al, byte [esi] cmp al,255 jz Lp13 mov word [8+ecx],bp mov byte [4+edi],al Lp13: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry3_8: cmp bp, word [10+ecx] jl Lp14 mov al, byte [esi] cmp al,255 jz Lp14 mov word [10+ecx],bp mov byte [5+edi],al Lp14: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry2_8: cmp bp, word [12+ecx] jl Lp15 mov al, byte [esi] cmp al,255 jz Lp15 mov word [12+ecx],bp mov byte [6+edi],al Lp15: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LEndSpan: cmp bp, word [14+ecx] jl Lp16 mov al, byte [esi] cmp al,255 jz Lp16 mov word [14+ecx],bp mov byte [7+edi],al Lp16: fstp st0 fstp st0 fstp st0 pop ebx LNextSpan: add ebx,12 mov ecx, dword [8+ebx] cmp ecx,0 jg near LSpanLoop jz LNextSpan pop ebx pop esi pop edi pop ebp ret engine/h2shared/d_spr8t.asm000066400000000000000000000407071444734033100161230ustar00rootroot00000000000000; ; d_spr8t.asm ; x86 assembly-language horizontal 8-bpp sprite span-drawing code. ; with translucency handling, #1. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix d_zistepu _sym_prefix d_pzbuffer _sym_prefix d_zistepv _sym_prefix d_zrowbytes _sym_prefix d_ziorigin _sym_prefix d_sdivzstepu _sym_prefix d_tdivzstepu _sym_prefix d_sdivzstepv _sym_prefix d_tdivzstepv _sym_prefix d_sdivzorigin _sym_prefix d_tdivzorigin _sym_prefix sadjust _sym_prefix tadjust _sym_prefix bbextents _sym_prefix bbextentt _sym_prefix cacheblock _sym_prefix d_viewbuffer _sym_prefix cachewidth _sym_prefix d_scantable _sym_prefix mainTransTable ; C-shared globals: _sym_prefix D_SpriteSpansStartT _sym_prefix D_SpriteDrawSpansT _sym_prefix D_SpriteSpansEndT _sym_prefix R_TranPatch4 %endif ; _sym_prefix ; externs from C code extern d_zistepu extern d_pzbuffer extern d_zistepv extern d_zrowbytes extern d_ziorigin extern d_sdivzstepu extern d_tdivzstepu extern d_sdivzstepv extern d_tdivzstepv extern d_sdivzorigin extern d_tdivzorigin extern sadjust extern tadjust extern bbextents extern bbextentt extern cacheblock extern d_viewbuffer extern cachewidth extern d_scantable extern mainTransTable ; externs from ASM-only code extern fp_1m extern fp_1m_minus_1 extern fp_8 extern fp_16 extern fp_64k extern fp_64kx64k extern izi extern izistep extern sstep extern tstep extern advancetable extern spr8Tentryvec_table extern reciprocal_table extern reciprocal_table_16 extern pbase extern s extern t extern sfracf extern tfracf extern snext extern tnext extern spancountminus1 extern zi16stepu extern sdivz16stepu extern tdivz16stepu extern zi8stepu extern sdivz8stepu extern tdivz8stepu extern pz SEGMENT .text LClampHigh0: mov esi, dword [bbextents] jmp LClampReentry0 LClampHighOrLow0: jg LClampHigh0 xor esi,esi jmp LClampReentry0 LClampHigh1: mov edx, dword [bbextentt] jmp LClampReentry1 LClampHighOrLow1: jg LClampHigh1 xor edx,edx jmp LClampReentry1 LClampLow2: mov ebp,2048 jmp LClampReentry2 LClampHigh2: mov ebp, dword [bbextents] jmp LClampReentry2 LClampLow3: mov ecx,2048 jmp LClampReentry3 LClampHigh3: mov ecx, dword [bbextentt] jmp LClampReentry3 LClampLow4: mov eax,2048 jmp LClampReentry4 LClampHigh4: mov eax, dword [bbextents] jmp LClampReentry4 LClampLow5: mov ebx,2048 jmp LClampReentry5 LClampHigh5: mov ebx, dword [bbextentt] jmp LClampReentry5 ;;;;;;;;;;;;;;;;;;;;;;;; ; D_SpriteSpansStartT ;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 4 global D_SpriteSpansStartT D_SpriteSpansStartT: ;;;;;;;;;;;;;;;;;;;;;;;; ; D_SpriteDrawSpansT ;;;;;;;;;;;;;;;;;;;;;;;; global D_SpriteDrawSpansT D_SpriteDrawSpansT: push ebp push edi push esi push ebx fld dword [d_sdivzstepu] fmul dword [fp_8] mov edx, dword [cacheblock] fld dword [d_tdivzstepu] fmul dword [fp_8] mov ebx, dword [4+16+esp] fld dword [d_zistepu] fmul dword [fp_8] mov dword [pbase],edx fld dword [d_zistepu] fmul dword [fp_64kx64k] fxch st3 fstp dword [sdivz8stepu] fstp dword [zi8stepu] fstp dword [tdivz8stepu] fistp dword [izistep] mov eax, dword [izistep] ror eax,16 mov ecx, dword [8+ebx] mov dword [izistep],eax cmp ecx,0 jle near LNextSpan LSpanLoop: fild dword [4+ebx] fild dword [0+ebx] fld st1 fmul dword [d_sdivzstepv] fld st1 fmul dword [d_sdivzstepu] fld st2 fmul dword [d_tdivzstepu] fxch st1 faddp st2,st0 fxch st1 fld st3 fmul dword [d_tdivzstepv] fxch st1 fadd dword [d_sdivzorigin] fxch st4 fmul dword [d_zistepv] fxch st1 faddp st2,st0 fxch st2 fmul dword [d_zistepu] fxch st1 fadd dword [d_tdivzorigin] fxch st2 faddp st1,st0 fld dword [fp_64k] fxch st1 fadd dword [d_ziorigin] fld st0 fmul dword [fp_64kx64k] fxch st1 fdiv st2,st0 fxch st1 fistp dword [izi] mov ebp, dword [izi] ror ebp,16 mov eax, dword [4+ebx] mov dword [izi],ebp mov ebp, dword [0+ebx] imul dword [d_zrowbytes] shl ebp,1 add eax, dword [d_pzbuffer] add eax,ebp mov dword [pz],eax mov ebp, dword [d_viewbuffer] mov eax, dword [4+ebx] push ebx mov edx, dword [tadjust] mov esi, dword [sadjust] mov edi, dword [d_scantable+eax*4] add edi,ebp mov ebp, dword [0+ebx] add edi,ebp cmp ecx,8 ja LSetupNotLast1 dec ecx jz LCleanup1 mov dword [spancountminus1],ecx fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] fild dword [spancountminus1] fld dword [d_tdivzstepu] fld dword [d_zistepu] fmul st0,st2 fxch st1 fmul st0,st2 fxch st2 fmul dword [d_sdivzstepu] fxch st1 faddp st3,st0 fxch st1 faddp st3,st0 faddp st3,st0 fld dword [fp_64k] fdiv st0,st1 jmp LFDIVInFlight1 LCleanup1: fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] jmp LFDIVInFlight1 ALIGN 4 LSetupNotLast1: fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] fadd dword [zi8stepu] fxch st2 fadd dword [sdivz8stepu] fxch st2 fld dword [tdivz8stepu] faddp st2,st0 fld dword [fp_64k] fdiv st0,st1 LFDIVInFlight1: add esi, dword [s] add edx, dword [t] mov ebx, dword [bbextents] mov ebp, dword [bbextentt] cmp esi,ebx ja near LClampHighOrLow0 LClampReentry0: mov dword [s],esi mov ebx, dword [pbase] shl esi,16 cmp edx,ebp mov dword [sfracf],esi ja near LClampHighOrLow1 LClampReentry1: mov dword [t],edx mov esi, dword [s] shl edx,16 mov eax, dword [t] sar esi,16 mov dword [tfracf],edx sar eax,16 add esi,ebx imul eax, dword [cachewidth] add esi,eax cmp ecx,8 jna near LLastSegment LNotLastSegment: fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [snext] fistp dword [tnext] mov eax, dword [snext] mov edx, dword [tnext] sub ecx,8 mov ebp, dword [sadjust] push ecx mov ecx, dword [tadjust] add ebp,eax add ecx,edx mov eax, dword [bbextents] mov edx, dword [bbextentt] cmp ebp,2048 jl near LClampLow2 cmp ebp,eax ja near LClampHigh2 LClampReentry2: cmp ecx,2048 jl near LClampLow3 cmp ecx,edx ja near LClampHigh3 LClampReentry3: mov dword [snext],ebp mov dword [tnext],ecx sub ebp, dword [s] sub ecx, dword [t] mov eax,ecx mov edx,ebp sar edx,19 mov ebx, dword [cachewidth] sar eax,19 jz LIsZero imul eax,ebx LIsZero: add eax,edx mov edx, dword [tfracf] mov dword [advancetable+4],eax add eax,ebx shl ebp,13 mov dword [sstep],ebp mov ebx, dword [sfracf] shl ecx,13 mov dword [advancetable],eax mov dword [tstep],ecx mov ecx, dword [pz] mov ebp, dword [izi] cmp bp, word [ecx] jl Lp1 mov ah, byte [esi] cmp ah,255 jz Lp1 ; mov word [ecx],bp ; trans stuff mov al, byte [edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch1: mov byte [edi],ah Lp1: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [2+ecx] jl Lp2 mov ah, byte [esi] cmp ah,255 jz Lp2 ; mov word [2+ecx],bp ; trans stuff mov al, byte [1+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch2: mov byte [1+edi],ah Lp2: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [4+ecx] jl Lp3 mov ah, byte [esi] cmp ah,255 jz Lp3 ; mov word [4+ecx],bp ; trans stuff mov al, byte [2+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch3: mov byte [2+edi],ah Lp3: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [6+ecx] jl Lp4 mov ah, byte [esi] cmp ah,255 jz Lp4 ; mov word [6+ecx],bp ; trans stuff mov al, byte [3+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch4: mov byte [3+edi],ah Lp4: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [8+ecx] jl Lp5 mov ah, byte [esi] cmp ah,255 jz Lp5 ; mov word [8+ecx],bp ; trans stuff mov al, byte [4+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch5: mov byte [4+edi],ah Lp5: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] pop eax cmp eax,8 ja LSetupNotLast2 dec eax jz LFDIVInFlight2 mov dword [spancountminus1],eax fild dword [spancountminus1] fld dword [d_zistepu] fmul st0,st1 fld dword [d_tdivzstepu] fmul st0,st2 fxch st1 faddp st3,st0 fxch st1 fmul dword [d_sdivzstepu] fxch st1 faddp st3,st0 fld dword [fp_64k] fxch st1 faddp st4,st0 fdiv st0,st1 jmp LFDIVInFlight2 ALIGN 4 LSetupNotLast2: fadd dword [zi8stepu] fxch st2 fadd dword [sdivz8stepu] fxch st2 fld dword [tdivz8stepu] faddp st2,st0 fld dword [fp_64k] fdiv st0,st1 LFDIVInFlight2: push eax cmp bp, word [10+ecx] jl Lp6 mov ah, byte [esi] cmp ah,255 jz Lp6 ; mov word [10+ecx],bp ; trans stuff mov al, byte [5+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch6: mov byte [5+edi],ah Lp6: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [12+ecx] jl Lp7 mov ah, byte [esi] cmp ah,255 jz Lp7 ; mov word [12+ecx],bp ; trans stuff mov al, byte [6+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch7: mov byte [6+edi],ah Lp7: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [14+ecx] jl Lp8 mov ah, byte [esi] cmp ah,255 jz Lp8 ; mov word [14+ecx],bp ; trans stuff mov al, byte [7+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch8: mov byte [7+edi],ah Lp8: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] add edi,8 add ecx,16 mov dword [tfracf],edx mov edx, dword [snext] mov dword [sfracf],ebx mov ebx, dword [tnext] mov dword [s],edx mov dword [t],ebx mov dword [pz],ecx mov dword [izi],ebp pop ecx cmp ecx,8 ja near LNotLastSegment LLastSegment: test ecx,ecx jz near LNoSteps fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [snext] fistp dword [tnext] mov ebx, dword [tadjust] mov eax, dword [sadjust] add eax, dword [snext] add ebx, dword [tnext] mov ebp, dword [bbextents] mov edx, dword [bbextentt] cmp eax,2048 jl near LClampLow4 cmp eax,ebp ja near LClampHigh4 LClampReentry4: mov dword [snext],eax cmp ebx,2048 jl near LClampLow5 cmp ebx,edx ja near LClampHigh5 LClampReentry5: cmp ecx,1 je near LOnlyOneStep sub eax, dword [s] sub ebx, dword [t] add eax,eax add ebx,ebx imul dword [reciprocal_table-8+ecx*4] mov ebp,edx mov eax,ebx imul dword [reciprocal_table-8+ecx*4] LSetEntryvec: mov ebx, dword [spr8Tentryvec_table+ecx*4] mov eax,edx push ebx mov ecx,ebp sar ecx,16 mov ebx, dword [cachewidth] sar edx,16 jz LIsZeroLast imul edx,ebx LIsZeroLast: add edx,ecx mov ecx, dword [tfracf] mov dword [advancetable+4],edx add edx,ebx shl ebp,16 mov ebx, dword [sfracf] shl eax,16 mov dword [advancetable],edx mov dword [tstep],eax mov dword [sstep],ebp mov edx,ecx mov ecx, dword [pz] mov ebp, dword [izi] ret LNoSteps: mov ecx, dword [pz] sub edi,7 sub ecx,14 jmp LEndSpan LOnlyOneStep: sub eax, dword [s] sub ebx, dword [t] mov ebp,eax mov edx,ebx jmp LSetEntryvec ;;;;;;;;;;;;;;;;;;;;;;;; ; globals Spr8Entry?_8T ;;;;;;;;;;;;;;;;;;;;;;;; global Spr8Entry2_8T Spr8Entry2_8T: sub edi,6 sub ecx,12 mov al, byte [esi] jmp LLEntry2_8 global Spr8Entry3_8T Spr8Entry3_8T: sub edi,5 sub ecx,10 jmp LLEntry3_8 global Spr8Entry4_8T Spr8Entry4_8T: sub edi,4 sub ecx,8 jmp LLEntry4_8 global Spr8Entry5_8T Spr8Entry5_8T: sub edi,3 sub ecx,6 jmp LLEntry5_8 global Spr8Entry6_8T Spr8Entry6_8T: sub edi,2 sub ecx,4 jmp LLEntry6_8 global Spr8Entry7_8T Spr8Entry7_8T: dec edi sub ecx,2 jmp LLEntry7_8 global Spr8Entry8_8T Spr8Entry8_8T: cmp bp, word [ecx] jl Lp9 mov ah, byte [esi] cmp ah,255 jz Lp9 ; mov word [ecx],bp ; trans stuff mov al, byte [edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch9: mov byte [edi],ah Lp9: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry7_8: cmp bp, word [2+ecx] jl Lp10 mov ah, byte [esi] cmp ah,255 jz Lp10 ; mov word [2+ecx],bp ; trans stuff mov al, byte [1+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch10: mov byte [1+edi],ah Lp10: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry6_8: cmp bp, word [4+ecx] jl Lp11 mov ah, byte [esi] cmp ah,255 jz Lp11 ; mov word [4+ecx],bp ; trans stuff mov al, byte [2+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch11: mov byte [2+edi],ah Lp11: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry5_8: cmp bp, word [6+ecx] jl Lp12 mov ah, byte [esi] cmp ah,255 jz Lp12 ; mov word [6+ecx],bp ; trans stuff mov al, byte [3+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch12: mov byte [3+edi],ah Lp12: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry4_8: cmp bp, word [8+ecx] jl Lp13 mov ah, byte [esi] cmp ah,255 jz Lp13 ; mov word [8+ecx],bp ; trans stuff mov al, byte [4+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch13: mov byte [4+edi],ah Lp13: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry3_8: cmp bp, word [10+ecx] jl Lp14 mov ah, byte [esi] cmp ah,255 jz Lp14 ; mov word [10+ecx],bp ; trans stuff mov al, byte [5+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch14: mov byte [5+edi],ah Lp14: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry2_8: cmp bp, word [12+ecx] jl Lp15 mov ah, byte [esi] cmp ah,255 jz Lp15 ; mov word [12+ecx],bp ; trans stuff mov al, byte [6+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch15: mov byte [6+edi],ah Lp15: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LEndSpan: cmp bp, word [14+ecx] jl Lp16 mov ah, byte [esi] cmp ah,255 jz Lp16 ; mov word [14+ecx],bp ; trans stuff mov al, byte [7+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch16: mov byte [7+edi],ah Lp16: fstp st0 fstp st0 fstp st0 pop ebx LNextSpan: add ebx,12 mov ecx, dword [8+ebx] cmp ecx,0 jg near LSpanLoop jz LNextSpan pop ebx pop esi pop edi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_SpriteSpansEndT ;;;;;;;;;;;;;;;;;;;;;;;; global D_SpriteSpansEndT D_SpriteSpansEndT: SEGMENT .data ALIGN 4 LPatchTable: dd TranPatch1-4 dd TranPatch2-4 dd TranPatch3-4 dd TranPatch4-4 dd TranPatch5-4 dd TranPatch6-4 dd TranPatch7-4 dd TranPatch8-4 dd TranPatch9-4 dd TranPatch10-4 dd TranPatch11-4 dd TranPatch12-4 dd TranPatch13-4 dd TranPatch14-4 dd TranPatch15-4 dd TranPatch16-4 SEGMENT .text ;;;;;;;;;;;;;;;;;;;;;;;; ; R_TranPatch4 ;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 4 global R_TranPatch4 R_TranPatch4: push ebx mov eax, dword [mainTransTable] mov ebx,offset LPatchTable mov ecx,16 LPatchLoop: mov edx, dword [ebx] add ebx,4 mov dword [edx],eax dec ecx jnz LPatchLoop pop ebx ret engine/h2shared/d_spr8t2.asm000066400000000000000000000417031444734033100162020ustar00rootroot00000000000000; ; d_spr8t2.asm ; x86 assembly-language horizontal 8-bpp sprite span-drawing code. ; with translucency handling, #2. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix d_zistepu _sym_prefix d_pzbuffer _sym_prefix d_zistepv _sym_prefix d_zrowbytes _sym_prefix d_ziorigin _sym_prefix d_sdivzstepu _sym_prefix d_tdivzstepu _sym_prefix d_sdivzstepv _sym_prefix d_tdivzstepv _sym_prefix d_sdivzorigin _sym_prefix d_tdivzorigin _sym_prefix sadjust _sym_prefix tadjust _sym_prefix bbextents _sym_prefix bbextentt _sym_prefix cacheblock _sym_prefix d_viewbuffer _sym_prefix cachewidth _sym_prefix d_scantable _sym_prefix mainTransTable ; C-shared globals: _sym_prefix D_SpriteSpansStartT2 _sym_prefix D_SpriteDrawSpansT2 _sym_prefix D_SpriteSpansEndT2 _sym_prefix R_TranPatch5 %endif ; _sym_prefix ; externs from C code extern d_zistepu extern d_pzbuffer extern d_zistepv extern d_zrowbytes extern d_ziorigin extern d_sdivzstepu extern d_tdivzstepu extern d_sdivzstepv extern d_tdivzstepv extern d_sdivzorigin extern d_tdivzorigin extern sadjust extern tadjust extern bbextents extern bbextentt extern cacheblock extern d_viewbuffer extern cachewidth extern d_scantable extern mainTransTable ; externs from ASM-only code extern fp_1m extern fp_1m_minus_1 extern fp_8 extern fp_16 extern fp_64k extern fp_64kx64k extern izi extern izistep extern sstep extern tstep extern advancetable extern spr8T2entryvec_table extern reciprocal_table extern reciprocal_table_16 extern pbase extern s extern t extern sfracf extern tfracf extern snext extern tnext extern spancountminus1 extern zi16stepu extern sdivz16stepu extern tdivz16stepu extern zi8stepu extern sdivz8stepu extern tdivz8stepu extern pz SEGMENT .text LClampHigh0: mov esi, dword [bbextents] jmp LClampReentry0 LClampHighOrLow0: jg LClampHigh0 xor esi,esi jmp LClampReentry0 LClampHigh1: mov edx, dword [bbextentt] jmp LClampReentry1 LClampHighOrLow1: jg LClampHigh1 xor edx,edx jmp LClampReentry1 LClampLow2: mov ebp,2048 jmp LClampReentry2 LClampHigh2: mov ebp, dword [bbextents] jmp LClampReentry2 LClampLow3: mov ecx,2048 jmp LClampReentry3 LClampHigh3: mov ecx, dword [bbextentt] jmp LClampReentry3 LClampLow4: mov eax,2048 jmp LClampReentry4 LClampHigh4: mov eax, dword [bbextents] jmp LClampReentry4 LClampLow5: mov ebx,2048 jmp LClampReentry5 LClampHigh5: mov ebx, dword [bbextentt] jmp LClampReentry5 ;;;;;;;;;;;;;;;;;;;;;;;; ; D_SpriteSpansStartT2 ;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 4 global D_SpriteSpansStartT2 D_SpriteSpansStartT2: ;;;;;;;;;;;;;;;;;;;;;;;; ; D_SpriteDrawSpansT2 ;;;;;;;;;;;;;;;;;;;;;;;; global D_SpriteDrawSpansT2 D_SpriteDrawSpansT2: push ebp push edi push esi push ebx fld dword [d_sdivzstepu] fmul dword [fp_8] mov edx, dword [cacheblock] fld dword [d_tdivzstepu] fmul dword [fp_8] mov ebx, dword [4+16+esp] fld dword [d_zistepu] fmul dword [fp_8] mov dword [pbase],edx fld dword [d_zistepu] fmul dword [fp_64kx64k] fxch st3 fstp dword [sdivz8stepu] fstp dword [zi8stepu] fstp dword [tdivz8stepu] fistp dword [izistep] mov eax, dword [izistep] ror eax,16 mov ecx, dword [8+ebx] mov dword [izistep],eax cmp ecx,0 jle near LNextSpan LSpanLoop: fild dword [4+ebx] fild dword [0+ebx] fld st1 fmul dword [d_sdivzstepv] fld st1 fmul dword [d_sdivzstepu] fld st2 fmul dword [d_tdivzstepu] fxch st1 faddp st2,st0 fxch st1 fld st3 fmul dword [d_tdivzstepv] fxch st1 fadd dword [d_sdivzorigin] fxch st4 fmul dword [d_zistepv] fxch st1 faddp st2,st0 fxch st2 fmul dword [d_zistepu] fxch st1 fadd dword [d_tdivzorigin] fxch st2 faddp st1,st0 fld dword [fp_64k] fxch st1 fadd dword [d_ziorigin] fld st0 fmul dword [fp_64kx64k] fxch st1 fdiv st2,st0 fxch st1 fistp dword [izi] mov ebp, dword [izi] ror ebp,16 mov eax, dword [4+ebx] mov dword [izi],ebp mov ebp, dword [0+ebx] imul dword [d_zrowbytes] shl ebp,1 add eax, dword [d_pzbuffer] add eax,ebp mov dword [pz],eax mov ebp, dword [d_viewbuffer] mov eax, dword [4+ebx] push ebx mov edx, dword [tadjust] mov esi, dword [sadjust] mov edi, dword [d_scantable+eax*4] add edi,ebp mov ebp, dword [0+ebx] add edi,ebp cmp ecx,8 ja LSetupNotLast1 dec ecx jz LCleanup1 mov dword [spancountminus1],ecx fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] fild dword [spancountminus1] fld dword [d_tdivzstepu] fld dword [d_zistepu] fmul st0,st2 fxch st1 fmul st0,st2 fxch st2 fmul dword [d_sdivzstepu] fxch st1 faddp st3,st0 fxch st1 faddp st3,st0 faddp st3,st0 fld dword [fp_64k] fdiv st0,st1 jmp LFDIVInFlight1 LCleanup1: fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] jmp LFDIVInFlight1 ALIGN 4 LSetupNotLast1: fxch st1 fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [s] fistp dword [t] fadd dword [zi8stepu] fxch st2 fadd dword [sdivz8stepu] fxch st2 fld dword [tdivz8stepu] faddp st2,st0 fld dword [fp_64k] fdiv st0,st1 LFDIVInFlight1: add esi, dword [s] add edx, dword [t] mov ebx, dword [bbextents] mov ebp, dword [bbextentt] cmp esi,ebx ja near LClampHighOrLow0 LClampReentry0: mov dword [s],esi mov ebx, dword [pbase] shl esi,16 cmp edx,ebp mov dword [sfracf],esi ja near LClampHighOrLow1 LClampReentry1: mov dword [t],edx mov esi, dword [s] shl edx,16 mov eax, dword [t] sar esi,16 mov dword [tfracf],edx sar eax,16 add esi,ebx imul eax, dword [cachewidth] add esi,eax cmp ecx,8 jna near LLastSegment LNotLastSegment: fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [snext] fistp dword [tnext] mov eax, dword [snext] mov edx, dword [tnext] sub ecx,8 mov ebp, dword [sadjust] push ecx mov ecx, dword [tadjust] add ebp,eax add ecx,edx mov eax, dword [bbextents] mov edx, dword [bbextentt] cmp ebp,2048 jl near LClampLow2 cmp ebp,eax ja near LClampHigh2 LClampReentry2: cmp ecx,2048 jl near LClampLow3 cmp ecx,edx ja near LClampHigh3 LClampReentry3: mov dword [snext],ebp mov dword [tnext],ecx sub ebp, dword [s] sub ecx, dword [t] mov eax,ecx mov edx,ebp sar edx,19 mov ebx, dword [cachewidth] sar eax,19 jz LIsZero imul eax,ebx LIsZero: add eax,edx mov edx, dword [tfracf] mov dword [advancetable+4],eax add eax,ebx shl ebp,13 mov dword [sstep],ebp mov ebx, dword [sfracf] shl ecx,13 mov dword [advancetable],eax mov dword [tstep],ecx mov ecx, dword [pz] mov ebp, dword [izi] cmp bp, word [ecx] jl Lp1 mov ah, byte [esi] cmp ah,255 jz Lp1 bt ax,8 jnc Skip1 ; mov word [ecx],bp ; trans stuff mov al, byte [edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch1: Skip1: mov byte [edi],ah Lp1: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [2+ecx] jl Lp2 mov ah, byte [esi] cmp ah,255 jz Lp2 bt ax,8 jnc Skip2 ; mov word [2+ecx],bp ; trans stuff mov al, byte [1+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch2: Skip2: mov byte [1+edi],ah Lp2: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [4+ecx] jl Lp3 mov ah, byte [esi] cmp ah,255 jz Lp3 bt ax,8 jnc Skip3 ; mov word [4+ecx],bp ; trans stuff mov al, byte [2+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch3: Skip3: mov byte [2+edi],ah Lp3: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [6+ecx] jl Lp4 mov ah, byte [esi] cmp ah,255 jz Lp4 bt ax,8 jnc Skip4 ; mov word [6+ecx],bp ; trans stuff mov al, byte [3+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch4: Skip4: mov byte [3+edi],ah Lp4: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [8+ecx] jl Lp5 mov ah, byte [esi] cmp ah,255 jz Lp5 bt ax,8 jnc Skip5 ; mov word [8+ecx],bp ; trans stuff mov al, byte [4+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch5: Skip5: mov byte [4+edi],ah Lp5: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] pop eax cmp eax,8 ja LSetupNotLast2 dec eax jz LFDIVInFlight2 mov dword [spancountminus1],eax fild dword [spancountminus1] fld dword [d_zistepu] fmul st0,st1 fld dword [d_tdivzstepu] fmul st0,st2 fxch st1 faddp st3,st0 fxch st1 fmul dword [d_sdivzstepu] fxch st1 faddp st3,st0 fld dword [fp_64k] fxch st1 faddp st4,st0 fdiv st0,st1 jmp LFDIVInFlight2 ALIGN 4 LSetupNotLast2: fadd dword [zi8stepu] fxch st2 fadd dword [sdivz8stepu] fxch st2 fld dword [tdivz8stepu] faddp st2,st0 fld dword [fp_64k] fdiv st0,st1 LFDIVInFlight2: push eax cmp bp, word [10+ecx] jl Lp6 mov ah, byte [esi] cmp ah,255 jz Lp6 bt ax,8 jnc Skip6 ; mov word [10+ecx],bp ; trans stuff mov al, byte [5+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch6: Skip6: mov byte [5+edi],ah Lp6: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [12+ecx] jl Lp7 mov ah, byte [esi] cmp ah,255 jz Lp7 bt ax,8 jnc Skip7 ; mov word [12+ecx],bp ; trans stuff mov al, byte [6+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch7: Skip7: mov byte [6+edi],ah Lp7: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] cmp bp, word [14+ecx] jl Lp8 mov ah, byte [esi] cmp ah,255 jz Lp8 bt ax,8 jnc Skip8 ; mov word [14+ecx],bp ; trans stuff mov al, byte [7+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch8: Skip8: mov byte [7+edi],ah Lp8: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] add edi,8 add ecx,16 mov dword [tfracf],edx mov edx, dword [snext] mov dword [sfracf],ebx mov ebx, dword [tnext] mov dword [s],edx mov dword [t],ebx mov dword [pz],ecx mov dword [izi],ebp pop ecx cmp ecx,8 ja near LNotLastSegment LLastSegment: test ecx,ecx jz near LNoSteps fld st0 fmul st0,st4 fxch st1 fmul st0,st3 fxch st1 fistp dword [snext] fistp dword [tnext] mov ebx, dword [tadjust] mov eax, dword [sadjust] add eax, dword [snext] add ebx, dword [tnext] mov ebp, dword [bbextents] mov edx, dword [bbextentt] cmp eax,2048 jl near LClampLow4 cmp eax,ebp ja near LClampHigh4 LClampReentry4: mov dword [snext],eax cmp ebx,2048 jl near LClampLow5 cmp ebx,edx ja near LClampHigh5 LClampReentry5: cmp ecx,1 je near LOnlyOneStep sub eax, dword [s] sub ebx, dword [t] add eax,eax add ebx,ebx imul dword [reciprocal_table-8+ecx*4] mov ebp,edx mov eax,ebx imul dword [reciprocal_table-8+ecx*4] LSetEntryvec: mov ebx, dword [spr8T2entryvec_table+ecx*4] mov eax,edx push ebx mov ecx,ebp sar ecx,16 mov ebx, dword [cachewidth] sar edx,16 jz LIsZeroLast imul edx,ebx LIsZeroLast: add edx,ecx mov ecx, dword [tfracf] mov dword [advancetable+4],edx add edx,ebx shl ebp,16 mov ebx, dword [sfracf] shl eax,16 mov dword [advancetable],edx mov dword [tstep],eax mov dword [sstep],ebp mov edx,ecx mov ecx, dword [pz] mov ebp, dword [izi] ret LNoSteps: mov ecx, dword [pz] sub edi,7 sub ecx,14 jmp LEndSpan LOnlyOneStep: sub eax, dword [s] sub ebx, dword [t] mov ebp,eax mov edx,ebx jmp LSetEntryvec ;;;;;;;;;;;;;;;;;;;;;;;; ; globals Spr8Entry?_8T2 ;;;;;;;;;;;;;;;;;;;;;;;; global Spr8Entry2_8T2 Spr8Entry2_8T2: sub edi,6 sub ecx,12 mov al, byte [esi] jmp LLEntry2_8 global Spr8Entry3_8T2 Spr8Entry3_8T2: sub edi,5 sub ecx,10 jmp LLEntry3_8 global Spr8Entry4_8T2 Spr8Entry4_8T2: sub edi,4 sub ecx,8 jmp LLEntry4_8 global Spr8Entry5_8T2 Spr8Entry5_8T2: sub edi,3 sub ecx,6 jmp LLEntry5_8 global Spr8Entry6_8T2 Spr8Entry6_8T2: sub edi,2 sub ecx,4 jmp LLEntry6_8 global Spr8Entry7_8T2 Spr8Entry7_8T2: dec edi sub ecx,2 jmp LLEntry7_8 global Spr8Entry8_8T2 Spr8Entry8_8T2: cmp bp, word [ecx] jl Lp9 mov ah, byte [esi] cmp ah,255 jz Lp9 bt ax,8 jnc Skip9 ; mov word [ecx],bp ; trans stuff mov al, byte [edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch9: Skip9: mov byte [edi],ah Lp9: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry7_8: cmp bp, word [2+ecx] jl Lp10 mov ah, byte [esi] cmp ah,255 jz Lp10 bt ax,8 jnc Skip10 ; mov word [2+ecx],bp ; trans stuff mov al, byte [1+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch10: Skip10: mov byte [1+edi],ah Lp10: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry6_8: cmp bp, word [4+ecx] jl Lp11 mov ah, byte [esi] cmp ah,255 jz Lp11 bt ax,8 jnc Skip11 ; mov word [4+ecx],bp ; trans stuff mov al, byte [2+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch11: Skip11: mov byte [2+edi],ah Lp11: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry5_8: cmp bp, word [6+ecx] jl Lp12 mov ah, byte [esi] cmp ah,255 jz Lp12 bt ax,8 jnc Skip12 ; mov word [6+ecx],bp ; trans stuff mov al, byte [3+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch12: Skip12: mov byte [3+edi],ah Lp12: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry4_8: cmp bp, word [8+ecx] jl Lp13 mov ah, byte [esi] cmp ah,255 jz Lp13 bt ax,8 jnc Skip13 ; mov word [8+ecx],bp ; trans stuff mov al, byte [4+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch13: Skip13: mov byte [4+edi],ah Lp13: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry3_8: cmp bp, word [10+ecx] jl Lp14 mov ah, byte [esi] cmp ah,255 jz Lp14 bt ax,8 jnc Skip14 ; mov word [10+ecx],bp ; trans stuff mov al, byte [5+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch14: Skip14: mov byte [5+edi],ah Lp14: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LLEntry2_8: cmp bp, word [12+ecx] jl Lp15 mov ah, byte [esi] cmp ah,255 jz Lp15 bt ax,8 jnc Skip15 ; mov word [12+ecx],bp ; trans stuff mov al, byte [6+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch15: Skip15: mov byte [6+edi],ah Lp15: add ebp, dword [izistep] adc ebp,0 add edx, dword [tstep] sbb eax,eax add ebx, dword [sstep] adc esi, dword [advancetable+4+eax*4] LEndSpan: cmp bp, word [14+ecx] jl Lp16 mov ah, byte [esi] cmp ah,255 jz Lp16 bt ax,8 jnc Skip16 ; mov word [14+ecx],bp ; trans stuff mov al, byte [7+edi] and eax, 0ffffh mov ah, byte [12345678h + eax] TranPatch16: Skip16: mov byte [7+edi],ah Lp16: fstp st0 fstp st0 fstp st0 pop ebx LNextSpan: add ebx,12 mov ecx, dword [8+ebx] cmp ecx,0 jg near LSpanLoop jz LNextSpan pop ebx pop esi pop edi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; D_SpriteSpansEndT2 ;;;;;;;;;;;;;;;;;;;;;;;; global D_SpriteSpansEndT2 D_SpriteSpansEndT2: SEGMENT .data ALIGN 4 LPatchTable: dd TranPatch1-4 dd TranPatch2-4 dd TranPatch3-4 dd TranPatch4-4 dd TranPatch5-4 dd TranPatch6-4 dd TranPatch7-4 dd TranPatch8-4 dd TranPatch9-4 dd TranPatch10-4 dd TranPatch11-4 dd TranPatch12-4 dd TranPatch13-4 dd TranPatch14-4 dd TranPatch15-4 dd TranPatch16-4 SEGMENT .text ;;;;;;;;;;;;;;;;;;;;;;;; ; R_TranPatch5 ;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 4 global R_TranPatch5 R_TranPatch5: push ebx mov eax, dword [mainTransTable] mov ebx,offset LPatchTable mov ecx,16 LPatchLoop: mov edx, dword [ebx] add ebx,4 mov dword [edx],eax dec ecx jnz LPatchLoop pop ebx ret engine/h2shared/d_sprite.c000066400000000000000000000444501444734033100160120ustar00rootroot00000000000000/* * d_sprite.c -- * software top-level rasterization driver module for drawing sprites * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "d_local.h" static int sprite_height; static int minindex, maxindex; static sspan_t *sprite_spans; #if !id386 && !id68k /* ===================== D_SpriteDrawSpans ===================== */ static void D_SpriteDrawSpans (sspan_t *pspan) { int count, spancount, izistep; int izi; byte *pbase, *pdest; fixed16_t s, t, snext, tnext, sstep, tstep; float sdivz, tdivz, zi, z, du, dv, spancountminus1; float sdivz8stepu, tdivz8stepu, zi8stepu; byte btemp; short *pz; sstep = 0; // keep compiler happy tstep = 0; // ditto pbase = cacheblock; sdivz8stepu = d_sdivzstepu * 8; tdivz8stepu = d_tdivzstepu * 8; zi8stepu = d_zistepu * 8; // we count on FP exceptions being turned off to avoid range problems izistep = (int)(d_zistepu * 0x8000 * 0x10000); do { pdest = (byte *)d_viewbuffer + (screenwidth * pspan->v) + pspan->u; pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; count = pspan->count; if (count <= 0) goto NextSpan; // calculate the initial s/z, t/z, 1/z, s, and t and clamp du = (float)pspan->u; dv = (float)pspan->v; sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point // we count on FP exceptions being turned off to avoid range problems izi = (int)(zi * 0x8000 * 0x10000); s = (int)(sdivz * z) + sadjust; if (s > bbextents) s = bbextents; else if (s < 0) s = 0; t = (int)(tdivz * z) + tadjust; if (t > bbextentt) t = bbextentt; else if (t < 0) t = 0; do { // calculate s and t at the far end of the span if (count >= 8) spancount = 8; else spancount = count; count -= spancount; if (count) { // calculate s/z, t/z, zi->fixed s and t at far end of span, // calculate s and t steps across span by shifting sdivz += sdivz8stepu; tdivz += tdivz8stepu; zi += zi8stepu; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < 8) snext = 8; // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < 8) tnext = 8; // guard against round-off error on <0 steps sstep = (snext - s) >> 3; tstep = (tnext - t) >> 3; } else { // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so // can't step off polygon), clamp, calculate s and t steps across // span by division, biasing steps low so we don't run off the // texture spancountminus1 = (float)(spancount - 1); sdivz += d_sdivzstepu * spancountminus1; tdivz += d_tdivzstepu * spancountminus1; zi += d_zistepu * spancountminus1; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < 8) snext = 8; // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < 8) tnext = 8; // guard against round-off error on <0 steps if (spancount > 1) { sstep = (snext - s) / (spancount - 1); tstep = (tnext - t) / (spancount - 1); } } do { btemp = *(pbase + (s >> 16) + (t >> 16) * cachewidth); if (btemp != 255) { if (*pz <= (izi >> 16)) { *pz = izi >> 16; *pdest = btemp; } } izi += izistep; pdest++; pz++; s += sstep; t += tstep; } while (--spancount > 0); s = snext; t = tnext; } while (count > 0); NextSpan: pspan++; } while (pspan->count != DS_SPAN_LIST_END); } static void D_SpriteDrawSpansT (sspan_t *pspan) { int count, spancount, izistep; int izi; byte *pbase, *pdest; fixed16_t s, t, snext, tnext, sstep, tstep; float sdivz, tdivz, zi, z, du, dv, spancountminus1; float sdivz8stepu, tdivz8stepu, zi8stepu; byte btemp; short *pz; sstep = 0; // keep compiler happy tstep = 0; // ditto pbase = cacheblock; sdivz8stepu = d_sdivzstepu * 8; tdivz8stepu = d_tdivzstepu * 8; zi8stepu = d_zistepu * 8; // we count on FP exceptions being turned off to avoid range problems izistep = (int)(d_zistepu * 0x8000 * 0x10000); do { pdest = (byte *)d_viewbuffer + (screenwidth * pspan->v) + pspan->u; pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; count = pspan->count; if (count <= 0) goto NextSpan; // calculate the initial s/z, t/z, 1/z, s, and t and clamp du = (float)pspan->u; dv = (float)pspan->v; sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point // we count on FP exceptions being turned off to avoid range problems izi = (int)(zi * 0x8000 * 0x10000); s = (int)(sdivz * z) + sadjust; if (s > bbextents) s = bbextents; else if (s < 0) s = 0; t = (int)(tdivz * z) + tadjust; if (t > bbextentt) t = bbextentt; else if (t < 0) t = 0; do { // calculate s and t at the far end of the span if (count >= 8) spancount = 8; else spancount = count; count -= spancount; if (count) { // calculate s/z, t/z, zi->fixed s and t at far end of span, // calculate s and t steps across span by shifting sdivz += sdivz8stepu; tdivz += tdivz8stepu; zi += zi8stepu; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < 8) snext = 8; // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < 8) tnext = 8; // guard against round-off error on <0 steps sstep = (snext - s) >> 3; tstep = (tnext - t) >> 3; } else { // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so // can't step off polygon), clamp, calculate s and t steps across // span by division, biasing steps low so we don't run off the // texture spancountminus1 = (float)(spancount - 1); sdivz += d_sdivzstepu * spancountminus1; tdivz += d_tdivzstepu * spancountminus1; zi += d_zistepu * spancountminus1; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < 8) snext = 8; // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < 8) tnext = 8; // guard against round-off error on <0 steps if (spancount > 1) { sstep = (snext - s) / (spancount - 1); tstep = (tnext - t) / (spancount - 1); } } do { btemp = *(pbase + (s >> 16) + (t >> 16) * cachewidth); if (btemp != 255) { if (*pz <= (izi >> 16)) { //*pz = izi >> 16; *pdest = mainTransTable[(btemp<<8) + (*pdest)]; } } izi += izistep; pdest++; pz++; s += sstep; t += tstep; } while (--spancount > 0); s = snext; t = tnext; } while (count > 0); NextSpan: pspan++; } while (pspan->count != DS_SPAN_LIST_END); } static void D_SpriteDrawSpansT2 (sspan_t *pspan) { int count, spancount, izistep; int izi; byte *pbase, *pdest; fixed16_t s, t, snext, tnext, sstep, tstep; float sdivz, tdivz, zi, z, du, dv, spancountminus1; float sdivz8stepu, tdivz8stepu, zi8stepu; byte btemp; short *pz; sstep = 0; // keep compiler happy tstep = 0; // ditto pbase = cacheblock; sdivz8stepu = d_sdivzstepu * 8; tdivz8stepu = d_tdivzstepu * 8; zi8stepu = d_zistepu * 8; // we count on FP exceptions being turned off to avoid range problems izistep = (int)(d_zistepu * 0x8000 * 0x10000); do { pdest = (byte *)d_viewbuffer + (screenwidth * pspan->v) + pspan->u; pz = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; count = pspan->count; if (count <= 0) goto NextSpan; // calculate the initial s/z, t/z, 1/z, s, and t and clamp du = (float)pspan->u; dv = (float)pspan->v; sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point // we count on FP exceptions being turned off to avoid range problems izi = (int)(zi * 0x8000 * 0x10000); s = (int)(sdivz * z) + sadjust; if (s > bbextents) s = bbextents; else if (s < 0) s = 0; t = (int)(tdivz * z) + tadjust; if (t > bbextentt) t = bbextentt; else if (t < 0) t = 0; do { // calculate s and t at the far end of the span if (count >= 8) spancount = 8; else spancount = count; count -= spancount; if (count) { // calculate s/z, t/z, zi->fixed s and t at far end of span, // calculate s and t steps across span by shifting sdivz += sdivz8stepu; tdivz += tdivz8stepu; zi += zi8stepu; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < 8) snext = 8; // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < 8) tnext = 8; // guard against round-off error on <0 steps sstep = (snext - s) >> 3; tstep = (tnext - t) >> 3; } else { // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so // can't step off polygon), clamp, calculate s and t steps across // span by division, biasing steps low so we don't run off the // texture spancountminus1 = (float)(spancount - 1); sdivz += d_sdivzstepu * spancountminus1; tdivz += d_tdivzstepu * spancountminus1; zi += d_zistepu * spancountminus1; z = (float)0x10000 / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < 8) snext = 8; // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < 8) tnext = 8; // guard against round-off error on <0 steps if (spancount > 1) { sstep = (snext - s) / (spancount - 1); tstep = (tnext - t) / (spancount - 1); } } do { btemp = *(pbase + (s >> 16) + (t >> 16) * cachewidth); if (btemp != 255) { if (*pz <= (izi >> 16)) { //*pz = izi >> 16; *pdest = (btemp & 0x1) ? mainTransTable[(btemp<<8) + (*pdest)] : btemp; } } izi += izistep; pdest++; pz++; s += sstep; t += tstep; } while (--spancount > 0); s = snext; t = tnext; } while (count > 0); NextSpan: pspan++; } while (pspan->count != DS_SPAN_LIST_END); } #endif /* ===================== D_SpriteScanLeftEdge ===================== */ static void D_SpriteScanLeftEdge (void) { int i, v, itop, ibottom, lmaxindex; emitpoint_t *pvert, *pnext; sspan_t *pspan; float du, dv, vtop, vbottom, slope; fixed16_t u, u_step; pspan = sprite_spans; i = minindex; if (i == 0) i = r_spritedesc.nump; lmaxindex = maxindex; if (lmaxindex == 0) lmaxindex = r_spritedesc.nump; vtop = ceil (r_spritedesc.pverts[i].v); do { pvert = &r_spritedesc.pverts[i]; pnext = pvert - 1; vbottom = ceil (pnext->v); if (vtop < vbottom) { du = pnext->u - pvert->u; dv = pnext->v - pvert->v; slope = du / dv; u_step = (int)(slope * 0x10000); // adjust u to ceil the integer portion u = (int)((pvert->u + (slope * (vtop - pvert->v))) * 0x10000) + (0x10000 - 1); itop = (int)vtop; ibottom = (int)vbottom; for (v=itop ; vu = u >> 16; pspan->v = v; u += u_step; pspan++; } } vtop = vbottom; i--; if (i == 0) i = r_spritedesc.nump; } while (i != lmaxindex); } /* ===================== D_SpriteScanRightEdge ===================== */ static void D_SpriteScanRightEdge (void) { int i, v, itop, ibottom; emitpoint_t *pvert, *pnext; sspan_t *pspan; float du, dv, vtop, vbottom, slope, uvert, unext, vvert, vnext; fixed16_t u, u_step; pspan = sprite_spans; i = minindex; vvert = r_spritedesc.pverts[i].v; if (vvert < r_refdef.fvrecty_adj) vvert = r_refdef.fvrecty_adj; if (vvert > r_refdef.fvrectbottom_adj) vvert = r_refdef.fvrectbottom_adj; vtop = ceil (vvert); do { pvert = &r_spritedesc.pverts[i]; pnext = pvert + 1; vnext = pnext->v; if (vnext < r_refdef.fvrecty_adj) vnext = r_refdef.fvrecty_adj; if (vnext > r_refdef.fvrectbottom_adj) vnext = r_refdef.fvrectbottom_adj; vbottom = ceil (vnext); if (vtop < vbottom) { uvert = pvert->u; if (uvert < r_refdef.fvrectx_adj) uvert = r_refdef.fvrectx_adj; if (uvert > r_refdef.fvrectright_adj) uvert = r_refdef.fvrectright_adj; unext = pnext->u; if (unext < r_refdef.fvrectx_adj) unext = r_refdef.fvrectx_adj; if (unext > r_refdef.fvrectright_adj) unext = r_refdef.fvrectright_adj; du = unext - uvert; dv = vnext - vvert; slope = du / dv; u_step = (int)(slope * 0x10000); // adjust u to ceil the integer portion u = (int)((uvert + (slope * (vtop - vvert))) * 0x10000) + (0x10000 - 1); itop = (int)vtop; ibottom = (int)vbottom; for (v = itop; v < ibottom; v++) { pspan->count = (u >> 16) - pspan->u; u += u_step; pspan++; } } vtop = vbottom; vvert = vnext; i++; if (i == r_spritedesc.nump) i = 0; } while (i != maxindex); pspan->count = DS_SPAN_LIST_END; // mark the end of the span list } /* ===================== D_SpriteCalculateGradients ===================== */ static void D_SpriteCalculateGradients (void) { vec3_t p_normal, p_saxis, p_taxis, p_temp1; float distinv; TransformVector (r_spritedesc.vpn, p_normal); TransformVector (r_spritedesc.vright, p_saxis); TransformVector (r_spritedesc.vup, p_taxis); VectorInverse (p_taxis); distinv = 1.0 / (-DotProduct (modelorg, r_spritedesc.vpn)); d_sdivzstepu = p_saxis[0] * xscaleinv; d_tdivzstepu = p_taxis[0] * xscaleinv; d_sdivzstepv = -p_saxis[1] * yscaleinv; d_tdivzstepv = -p_taxis[1] * yscaleinv; d_zistepu = p_normal[0] * xscaleinv * distinv; d_zistepv = -p_normal[1] * yscaleinv * distinv; d_sdivzorigin = p_saxis[2] - xcenter * d_sdivzstepu - ycenter * d_sdivzstepv; d_tdivzorigin = p_taxis[2] - xcenter * d_tdivzstepu - ycenter * d_tdivzstepv; d_ziorigin = p_normal[2] * distinv - xcenter * d_zistepu - ycenter * d_zistepv; TransformVector (modelorg, p_temp1); sadjust = ((fixed16_t)(DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - (-(cachewidth >> 1) << 16); tadjust = ((fixed16_t)(DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) - (-(sprite_height >> 1) << 16); // -1 (-epsilon) so we never wander off the edge of the texture bbextents = (cachewidth << 16) - 1; bbextentt = (sprite_height << 16) - 1; } /* ===================== D_DrawSprite ===================== */ void D_DrawSprite (void) { int i, nump; float ymin, ymax; emitpoint_t *pverts; sspan_t spans[MAXHEIGHT+1]; sprite_spans = spans; // find the top and bottom vertices, and make sure there's at least one scan to // draw ymin = 999999.9; /* FIXME: change these two to FLT_MAX/-FLT_MAX */ ymax = -999999.9; pverts = r_spritedesc.pverts; for (i = 0; i < r_spritedesc.nump; i++) { if (pverts->v < ymin) { ymin = pverts->v; minindex = i; } if (pverts->v > ymax) { ymax = pverts->v; maxindex = i; } pverts++; } ymin = ceil (ymin); ymax = ceil (ymax); if (ymin >= ymax) return; // doesn't cross any scans at all if (ymin < 0) { /* happens when R_SetupAndDrawSprite() sets pout->v to a value <= -1 * with sprites rendered very close to the camera origin such as the * teleporter puff. this makes the screen pointer pz to point to an * invalid address in D_SpriteDrawTransSpans(), leading to an access * violation. -- see uHexen2 bug #3562290: * http://sourceforge.net/tracker/?func=detail&atid=701006&aid=3562290&group_id=124987 */ Con_DPrintf ("%s: ymin: %f\n", __thisfunc__, ymin); return; } cachewidth = r_spritedesc.pspriteframe->width; sprite_height = r_spritedesc.pspriteframe->height; cacheblock = (byte *)&r_spritedesc.pspriteframe->pixels[0]; // copy the first vertex to the last vertex, so we don't have to deal with // wrapping nump = r_spritedesc.nump; pverts = r_spritedesc.pverts; pverts[nump] = pverts[0]; D_SpriteCalculateGradients (); D_SpriteScanLeftEdge (); D_SpriteScanRightEdge (); if (currententity->drawflags & DRF_TRANSLUCENT) D_SpriteDrawSpansT (sprite_spans); else if (currententity->model->flags & EF_TRANSPARENT) D_SpriteDrawSpansT2 (sprite_spans); else D_SpriteDrawSpans (sprite_spans); } engine/h2shared/d_sprite68k.s000066400000000000000000000254771444734033100163730ustar00rootroot00000000000000; 680x0 optimised Quake render routines by John Selck. ; Adapted for Hexen II by Szilard Biro ; d_sprite.c: XDEF _D_SpriteDrawSpans XDEF _D_SpriteDrawSpansT XDEF _D_SpriteDrawSpansT2 ; external defs: XREF _cacheblock XREF _d_sdivzorigin XREF _d_sdivzstepu XREF _d_sdivzstepv XREF _d_tdivzorigin XREF _d_tdivzstepu XREF _d_tdivzstepv XREF _d_ziorigin XREF _sadjust XREF _tadjust XREF _bbextents XREF _bbextentt XREF _screenwidth XREF _mainTransTable SSPAN_U equ 0 SSPAN_V equ 4 SSPAN_COUNT equ 8 DS_SPAN_LIST_END equ -128 SECTION CODE,CODE ***************************************************************************** * * void D_SpriteDrawSpans (sspan_t *pspan) * ****************************************************************************** cnop 0,4 _D_SpriteDrawSpans ***** stackframe rsreset .fpuregs rs.x 4 .intregs rs.l 11 rs.l 1 .pspan rs.l 1 ***** prologue movem.l d2-d7/a2-a6,-(sp) fmovem.x fp2-fp5,-(sp) fmove.s _d_ziorigin,fp2 fmove.s #65536,fp3 fdiv.x fp2,fp3 ;fp3 = (float)0x10000 / d_ziorigin move.l .pspan(sp),a6 move.l _cacheblock,a0 ;a0 = pbase fmul.s #32768,fp2 ;fp2 = d_ziorigin * (float)0x8000 move.l _bbextents,a4 move.l _sadjust,a5 fmove.l fp2,d6 ;d6 = (int)(d_ziorigin * 0x8000); .ls1 move.l SSPAN_COUNT(a6),d7 ble.w .skip move.l SSPAN_U(a6),d0 ;d0 = pspan->u fmove.l d0,fp4 ;fp4 = du = (float)psan->u fmove.s _d_sdivzstepu,fp0 fmul.x fp4,fp0 ;fp0 = du*d_sdivzstepu move.l SSPAN_V(a6),d1 ;d1 = pspan->v fmove.l d1,fp5 ;fp5 = dv = (float)pspan->v fmove.s _d_sdivzstepv,fp1 fmul.x fp5,fp1 ;fp1 = dv*d_sdivzstepv move.l _d_viewbuffer,a1 move.l d1,d2 ;d2 = pspan->v add.l d0,a1 ;a1 = d_viewbuffer + pspan->u fadd.x fp1,fp0 ;fp0 = dv*d_sdivzstepv + du*d_sdivzstepu fadd.s _d_sdivzorigin,fp0 ;fp0 = sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu fmove.s _d_tdivzstepu,fp1 fmul.x fp4,fp1 ;fp1 = du*d_tdivzstepu mulu.l _screenwidth,d2 ;d2 = screenwidth * pspan->v add.l d2,a1 ;a1 = d_viewbuffer + pspan->u + (screenwidth * pspan->v) fmove.s _d_tdivzstepv,fp2 fmul.x fp5,fp2 ;fp2 = dv*d_tdivzstepv move.l _d_pzbuffer,a2 move.l d1,d2 ;d2 = pspan->v mulu.l _d_zwidth,d2 ;d2 = d_zwidth * pspan->v fadd.x fp2,fp1 ;fp1 = dv*d_tdivzstepv + du*d_tdivzstepu fadd.s _d_tdivzorigin,fp1 ;fp1 = tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu fmove.x fp0,fp4 fmul.x fp3,fp4 add.l d0,d2 moveq #-1,d1 add.l d2,d2 move.l d7,d0 add.l d2,a2 ; pz subq.l #1,d0 fmove.l fp4,d2 fmove.l d0,fp4 ; spancountminus1 fmul.x fp3,fp1 add.l a5,d2 ; s cmp.l a4,d2 ble.b .s0 move.l a4,d2 bra.b .s1 .s0 tst.l d2 bpl.b .s1 moveq #0,d2 .s1 fmove.l fp1,d3 fmove.s _d_sdivzstepu,fp5 fmul.x fp4,fp5 add.l _tadjust,d3 ; t move.l _bbextentt,d0 cmp.l d0,d3 ble.b .s2 move.l d0,d3 bra.b .s3 .s2 tst.l d3 bpl.b .s3 moveq #0,d3 .s3 fadd.x fp5,fp0 fmul.x fp3,fp0 swap d3 muls.w _cachewidth+2,d3 move.l a0,a3 move.l a4,d0 add.l d3,a3 fmove.l fp0,d4 add.l a5,d4 cmp.l d0,d4 bgt.b .s4 moveq #8,d0 cmp.l d0,d4 bge.b .s5 .s4 move.l d0,d4 .s5 move.l d7,d0 subq.l #1,d0 ble.b .sd sub.l d2,d4 divs.l d0,d4 .sd .ls0 move.l d2,d0 swap d0 move.b (a3,d0.w),d3 ;btemp = *(a3 + (s >> 16) + (t >> 16) * cachewidth) cmp.b d1,d3 ;if (btemp != 255) beq.b .ss cmp.w (a2),d6 ;if (*pz <= (izi >> 16)) ble.b .ss move.w d6,(a2) ;*pz = izi >> 16 move.b d3,(a1) ;*pdest = btemp .ss add.l d4,d2 addq.l #2,a2 addq.l #1,a1 subq.l #1,d7 bgt.b .ls0 .skip add.w #12,a6 moveq #DS_SPAN_LIST_END,d0 cmp.l 8(a6),d0 bne.w .ls1 fmovem.x (sp)+,fp2-fp5 movem.l (sp)+,d2-d7/a2-a6 rts ***************************************************************************** * * void D_SpriteDrawSpansT (sspan_t *pspan) * ****************************************************************************** cnop 0,4 _D_SpriteDrawSpansT ***** stackframe rsreset .savea6 rs.l 1 .fpuregs rs.x 4 .intregs rs.l 11 rs.l 1 .pspan rs.l 1 ***** prologue movem.l d2-d7/a2-a6,-(sp) fmovem.x fp2-fp5,-(sp) sub #.fpuregs,sp fmove.s _d_ziorigin,fp2 fmove.s #65536,fp3 fdiv.x fp2,fp3 ;fp3 = (float)0x10000 / d_ziorigin move.l .pspan(sp),a6 move.l _cacheblock,a0 ;a0 = pbase fmul.s #32768,fp2 ;fp2 = d_ziorigin * (float)0x8000 move.l _bbextents,a4 move.l _sadjust,a5 fmove.l fp2,d6 ;d6 = (int)(d_ziorigin * 0x8000); .ls1 move.l SSPAN_COUNT(a6),d7 ble.w .skip move.l SSPAN_U(a6),d0 ;d0 = pspan->u fmove.l d0,fp4 ;fp4 = du = (float)psan->u fmove.s _d_sdivzstepu,fp0 fmul.x fp4,fp0 ;fp0 = du*d_sdivzstepu move.l SSPAN_V(a6),d1 ;d1 = pspan->v fmove.l d1,fp5 ;fp5 = dv = (float)pspan->v fmove.s _d_sdivzstepv,fp1 fmul.x fp5,fp1 ;fp1 = dv*d_sdivzstepv move.l _d_viewbuffer,a1 move.l d1,d2 ;d2 = pspan->v add.l d0,a1 ;a1 = d_viewbuffer + pspan->u fadd.x fp1,fp0 ;fp0 = dv*d_sdivzstepv + du*d_sdivzstepu fadd.s _d_sdivzorigin,fp0 ;fp0 = sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu fmove.s _d_tdivzstepu,fp1 fmul.x fp4,fp1 ;fp1 = du*d_tdivzstepu mulu.l _screenwidth,d2 ;d2 = screenwidth * pspan->v add.l d2,a1 ;a1 = d_viewbuffer + pspan->u + (screenwidth * pspan->v) fmove.s _d_tdivzstepv,fp2 fmul.x fp5,fp2 ;fp2 = dv*d_tdivzstepv move.l _d_pzbuffer,a2 move.l d1,d2 ;d2 = pspan->v mulu.l _d_zwidth,d2 ;d2 = d_zwidth * pspan->v fadd.x fp2,fp1 ;fp1 = dv*d_tdivzstepv + du*d_tdivzstepu fadd.s _d_tdivzorigin,fp1 ;fp1 = tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu fmove.x fp0,fp4 fmul.x fp3,fp4 add.l d0,d2 moveq #-1,d1 add.l d2,d2 move.l d7,d0 add.l d2,a2 ; pz subq.l #1,d0 fmove.l fp4,d2 fmove.l d0,fp4 ; spancountminus1 fmul.x fp3,fp1 add.l a5,d2 ; s cmp.l a4,d2 ble.b .s0 move.l a4,d2 bra.b .s1 .s0 tst.l d2 bpl.b .s1 moveq #0,d2 .s1 fmove.l fp1,d3 fmove.s _d_sdivzstepu,fp5 fmul.x fp4,fp5 add.l _tadjust,d3 ; t move.l _bbextentt,d0 cmp.l d0,d3 ble.b .s2 move.l d0,d3 bra.b .s3 .s2 tst.l d3 bpl.b .s3 moveq #0,d3 .s3 fadd.x fp5,fp0 fmul.x fp3,fp0 swap d3 muls.w _cachewidth+2,d3 move.l a0,a3 move.l a4,d0 add.l d3,a3 fmove.l fp0,d4 add.l a5,d4 cmp.l d0,d4 bgt.b .s4 moveq #8,d0 cmp.l d0,d4 bge.b .s5 .s4 move.l d0,d4 .s5 move.l d7,d0 subq.l #1,d0 ble.b .sd sub.l d2,d4 divs.l d0,d4 .sd move.l a6,.savea6(sp) ;save actual ptr to pspan move.l _mainTransTable,a6 .ls0 move.l d2,d0 swap d0 move.b (a3,d0.w),d3 ;btemp = *(a3 + (s >> 16) + (t >> 16) * cachewidth) cmp.b d1,d3 ;if (btemp != 255) beq.b .ss cmp.w (a2),d6 ;if (*pz <= (izi >> 16)) ble.b .ss lsl.w #8,d3 move.b (a1),d3 ;d3 = (btemp<<8) + (*pdest) ;move.w d6,(a2) ;*pz = izi >> 16 ;move.b d3,(a1) ;*pdest = btemp move.b (a6,d3.l),(a1) ;*pdest = mainTransTable[(btemp<<8) + (*pdest)] .ss add.l d4,d2 addq.l #2,a2 addq.l #1,a1 subq.l #1,d7 bgt.b .ls0 move.l .savea6(sp),a6 ;restore pspan .skip add.w #12,a6 moveq #DS_SPAN_LIST_END,d0 cmp.l 8(a6),d0 bne.w .ls1 add.l #.fpuregs,sp fmovem.x (sp)+,fp2-fp5 movem.l (sp)+,d2-d7/a2-a6 rts ***************************************************************************** * * void D_SpriteDrawSpansT2 (sspan_t *pspan) * ****************************************************************************** cnop 0,4 _D_SpriteDrawSpansT2 ***** stackframe rsreset .savea6 rs.l 1 .fpuregs rs.x 4 .intregs rs.l 11 rs.l 1 .pspan rs.l 1 ***** prologue movem.l d2-d7/a2-a6,-(sp) fmovem.x fp2-fp5,-(sp) sub #.fpuregs,sp fmove.s _d_ziorigin,fp2 fmove.s #65536,fp3 fdiv.x fp2,fp3 ;fp3 = (float)0x10000 / d_ziorigin move.l .pspan(sp),a6 move.l _cacheblock,a0 ;a0 = pbase fmul.s #32768,fp2 ;fp2 = d_ziorigin * (float)0x8000 move.l _bbextents,a4 move.l _sadjust,a5 fmove.l fp2,d6 ;d6 = (int)(d_ziorigin * 0x8000); .ls1 move.l SSPAN_COUNT(a6),d7 ble.w .skip move.l SSPAN_U(a6),d0 ;d0 = pspan->u fmove.l d0,fp4 ;fp4 = du = (float)psan->u fmove.s _d_sdivzstepu,fp0 fmul.x fp4,fp0 ;fp0 = du*d_sdivzstepu move.l SSPAN_V(a6),d1 ;d1 = pspan->v fmove.l d1,fp5 ;fp5 = dv = (float)pspan->v fmove.s _d_sdivzstepv,fp1 fmul.x fp5,fp1 ;fp1 = dv*d_sdivzstepv move.l _d_viewbuffer,a1 move.l d1,d2 ;d2 = pspan->v add.l d0,a1 ;a1 = d_viewbuffer + pspan->u fadd.x fp1,fp0 ;fp0 = dv*d_sdivzstepv + du*d_sdivzstepu fadd.s _d_sdivzorigin,fp0 ;fp0 = sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu fmove.s _d_tdivzstepu,fp1 fmul.x fp4,fp1 ;fp1 = du*d_tdivzstepu mulu.l _screenwidth,d2 ;d2 = screenwidth * pspan->v add.l d2,a1 ;a1 = d_viewbuffer + pspan->u + (screenwidth * pspan->v) fmove.s _d_tdivzstepv,fp2 fmul.x fp5,fp2 ;fp2 = dv*d_tdivzstepv move.l _d_pzbuffer,a2 move.l d1,d2 ;d2 = pspan->v mulu.l _d_zwidth,d2 ;d2 = d_zwidth * pspan->v fadd.x fp2,fp1 ;fp1 = dv*d_tdivzstepv + du*d_tdivzstepu fadd.s _d_tdivzorigin,fp1 ;fp1 = tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu fmove.x fp0,fp4 fmul.x fp3,fp4 add.l d0,d2 moveq #-1,d1 add.l d2,d2 move.l d7,d0 add.l d2,a2 ; pz subq.l #1,d0 fmove.l fp4,d2 fmove.l d0,fp4 ; spancountminus1 fmul.x fp3,fp1 add.l a5,d2 ; s cmp.l a4,d2 ble.b .s0 move.l a4,d2 bra.b .s1 .s0 tst.l d2 bpl.b .s1 moveq #0,d2 .s1 fmove.l fp1,d3 fmove.s _d_sdivzstepu,fp5 fmul.x fp4,fp5 add.l _tadjust,d3 ; t move.l _bbextentt,d0 cmp.l d0,d3 ble.b .s2 move.l d0,d3 bra.b .s3 .s2 tst.l d3 bpl.b .s3 moveq #0,d3 .s3 fadd.x fp5,fp0 fmul.x fp3,fp0 swap d3 muls.w _cachewidth+2,d3 move.l a0,a3 move.l a4,d0 add.l d3,a3 fmove.l fp0,d4 add.l a5,d4 cmp.l d0,d4 bgt.b .s4 moveq #8,d0 cmp.l d0,d4 bge.b .s5 .s4 move.l d0,d4 .s5 move.l d7,d0 subq.l #1,d0 ble.b .sd sub.l d2,d4 divs.l d0,d4 .sd move.l a6,.savea6(sp) ;save actual ptr to pspan move.l _mainTransTable,a6 .ls0 move.l d2,d0 swap d0 move.b (a3,d0.w),d3 ;btemp = *(a3 + (s >> 16) + (t >> 16) * cachewidth) cmp.b d1,d3 ;if (btemp != 255) beq.b .ss cmp.w (a2),d6 ;if (*pz <= (izi >> 16)) ble.b .ss btst #0,d3 ;if (btemp % 2 == 0) bne.b .transluc move.w d6,(a2) ;*pz = izi >> 16 move.b d3,(a1) ;*pdest = btemp bra.b .ss .transluc lsl.w #8,d3 move.b (a1),d3 ;d3 = (btemp<<8) + (*pdest) move.b (a6,d3.l),(a1) ;*pdest = mainTransTable[(btemp<<8) + (*pdest)] .ss add.l d4,d2 addq.l #2,a2 addq.l #1,a1 subq.l #1,d7 bgt.b .ls0 move.l .savea6(sp),a6 ;restore pspan .skip add.w #12,a6 moveq #DS_SPAN_LIST_END,d0 cmp.l 8(a6),d0 bne.w .ls1 add.l #.fpuregs,sp fmovem.x (sp)+,fp2-fp5 movem.l (sp)+,d2-d7/a2-a6 rts END engine/h2shared/d_surf.c000066400000000000000000000163261444734033100154640ustar00rootroot00000000000000/* * d_surf.c - rasterization driver surface heap manager * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "d_local.h" #include "r_local.h" static float surfscale; qboolean r_cache_thrash = false; // set if surface cache is thrashing static int sc_size; surfcache_t *sc_rover, *sc_base; #define GUARDSIZE 4 int D_SurfaceCacheForRes (int width, int height) { int i, size, pix; i = COM_CheckParm ("-surfcachesize"); if (i && i < com_argc-1) { size = atoi(com_argv[i+1]) * 1024; return size; } size = SURFCACHE_SIZE_AT_320X200; pix = width*height; if (pix > 64000) size += (pix-64000)*3; return size; } static void D_CheckCacheGuard (void) { byte *s; int i; s = (byte *)sc_base + sc_size; for (i = 0; i < GUARDSIZE; i++) { if (s[i] != (byte)i) Sys_Error ("%s: failed", __thisfunc__); } } static void D_ClearCacheGuard (void) { byte *s; int i; s = (byte *)sc_base + sc_size; for (i = 0; i < GUARDSIZE; i++) s[i] = (byte)i; } /* ================ D_InitCaches ================ */ void D_InitCaches (void *buffer, int size) { if (!msg_suppress_1) Con_Printf ("%ik surface cache\n", size/1024); sc_size = size - GUARDSIZE; sc_base = (surfcache_t *)buffer; sc_rover = sc_base; sc_base->next = NULL; sc_base->owner = NULL; sc_base->size = sc_size; D_ClearCacheGuard (); } /* ================== D_FlushCaches ================== */ void D_FlushCaches (void) { surfcache_t *c; if (!sc_base) return; for (c = sc_base ; c ; c = c->next) { if (c->owner) *c->owner = NULL; } sc_rover = sc_base; sc_base->next = NULL; sc_base->owner = NULL; sc_base->size = sc_size; } /* ================= D_SCAlloc ================= */ static surfcache_t *D_SCAlloc (int width, int size) { surfcache_t *new_sc; qboolean wrapped_this_time; if ((width < 0) || (width > 256)) Sys_Error ("%s: bad cache width %d", __thisfunc__, width); if ((size <= 0) || (size > 0x10000)) Sys_Error ("%s: bad cache size %d", __thisfunc__, size); /* This adds the offset of data[0] in the surfcache_t struct. */ size += (intptr_t) &(((surfcache_t *)0)->data[0]); #define SIZE_ALIGN (sizeof (surfcache_t *) - 1) size = (size + SIZE_ALIGN) & ~SIZE_ALIGN; #undef SIZE_ALIGN size = (size + 3) & ~3; if (size > sc_size) Sys_Error ("%s: %i > cache size", __thisfunc__, size); // if there is not size bytes after the rover, reset to the start wrapped_this_time = false; if ( !sc_rover || (byte *)sc_rover - (byte *)sc_base > sc_size - size) { if (sc_rover) { wrapped_this_time = true; } sc_rover = sc_base; } // colect and free surfcache_t blocks until the rover block is large enough new_sc = sc_rover; if (sc_rover->owner) *sc_rover->owner = NULL; while (new_sc->size < size) { // free another sc_rover = sc_rover->next; if (!sc_rover) Sys_Error ("%s: hit the end of memory", __thisfunc__); if (sc_rover->owner) *sc_rover->owner = NULL; new_sc->size += sc_rover->size; new_sc->next = sc_rover->next; } // create a fragment out of any leftovers if (new_sc->size - size > 256) { sc_rover = (surfcache_t *)( (byte *)new_sc + size); sc_rover->size = new_sc->size - size; sc_rover->next = new_sc->next; sc_rover->width = 0; sc_rover->owner = NULL; new_sc->next = sc_rover; new_sc->size = size; } else sc_rover = new_sc->next; new_sc->width = width; // DEBUG if (width > 0) new_sc->height = (size - sizeof(*new_sc) + sizeof(new_sc->data)) / width; new_sc->owner = NULL; // should be set properly after return if (d_roverwrapped) { if (wrapped_this_time || (sc_rover >= d_initial_rover)) r_cache_thrash = true; } else if (wrapped_this_time) { d_roverwrapped = true; } D_CheckCacheGuard (); // DEBUG return new_sc; } /* ================= D_SCDump ================= */ #if 0 /* unused */ static void D_SCDump (void) { surfcache_t *test; for (test = sc_base ; test ; test = test->next) { if (test == sc_rover) Sys_Printf ("ROVER:\n"); printf ("%p : %i bytes %i width\n",test, test->size, test->width); } } #endif //============================================================================= /* ================ D_CacheSurface ================ */ surfcache_t *D_CacheSurface (msurface_t *surface, int miplevel) { surfcache_t *cache; qboolean DoSurface; // // if the surface is animating or flashing, flush the cache // r_drawsurf.texture = R_TextureAnimation (surface->texinfo->texture); r_drawsurf.lightadj[0] = d_lightstylevalue[surface->styles[0]]; r_drawsurf.lightadj[1] = d_lightstylevalue[surface->styles[1]]; r_drawsurf.lightadj[2] = d_lightstylevalue[surface->styles[2]]; r_drawsurf.lightadj[3] = d_lightstylevalue[surface->styles[3]]; // // see if the cache holds apropriate data // cache = surface->cachespots[miplevel]; DoSurface = false; if (cache) { if ((cache->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT && (currententity->drawflags & MLS_ABSLIGHT) != MLS_ABSLIGHT) { DoSurface = true; } else if (cache->abslight != currententity->abslight) { DoSurface = true; } } if (cache && !cache->dlight && surface->dlightframe != r_framecount && cache->texture == r_drawsurf.texture && cache->lightadj[0] == r_drawsurf.lightadj[0] && cache->lightadj[1] == r_drawsurf.lightadj[1] && cache->lightadj[2] == r_drawsurf.lightadj[2] && cache->lightadj[3] == r_drawsurf.lightadj[3] && !DoSurface) return cache; // // determine shape of surface // surfscale = 1.0 / (1<extents[0] >> miplevel; r_drawsurf.rowbytes = r_drawsurf.surfwidth; r_drawsurf.surfheight = surface->extents[1] >> miplevel; // // allocate memory if needed // if (!cache) // if a texture just animated, don't reallocate it { cache = D_SCAlloc (r_drawsurf.surfwidth, r_drawsurf.surfwidth * r_drawsurf.surfheight); surface->cachespots[miplevel] = cache; cache->owner = &surface->cachespots[miplevel]; cache->mipscale = surfscale; } cache->drawflags = currententity->drawflags; cache->abslight = currententity->abslight; if (surface->dlightframe == r_framecount) cache->dlight = 1; else cache->dlight = 0; r_drawsurf.surfdat = (pixel_t *)cache->data; cache->texture = r_drawsurf.texture; cache->lightadj[0] = r_drawsurf.lightadj[0]; cache->lightadj[1] = r_drawsurf.lightadj[1]; cache->lightadj[2] = r_drawsurf.lightadj[2]; cache->lightadj[3] = r_drawsurf.lightadj[3]; // // draw and light the surface texture // r_drawsurf.surf = surface; c_surf++; R_DrawSurface (); return surface->cachespots[miplevel]; } engine/h2shared/d_vars.c000066400000000000000000000030231444734033100154460ustar00rootroot00000000000000/* d_vars.c - rasterization driver global variables * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if !id386 // all global and static refresh variables are collected in a contiguous block // to avoid cache conflicts. //------------------------------------------------------- // global refresh variables //------------------------------------------------------- // FIXME: make into one big structure, like cl or sv // FIXME: do separately for refresh engine and driver float d_sdivzstepu, d_tdivzstepu, d_zistepu; float d_sdivzstepv, d_tdivzstepv, d_zistepv; float d_sdivzorigin, d_tdivzorigin, d_ziorigin; fixed16_t sadjust, tadjust, bbextents, bbextentt; pixel_t *cacheblock; int cachewidth; pixel_t *d_viewbuffer; short *d_pzbuffer; int d_zrowbytes; int d_zwidth; #endif /* !id386 */ engine/h2shared/d_varsa.asm000066400000000000000000000132031444734033100161460ustar00rootroot00000000000000; ; d_varsa.asm ; rasterization driver global variables ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: ; C-shared globals: _sym_prefix d_sdivzstepu _sym_prefix d_tdivzstepu _sym_prefix d_zistepu _sym_prefix d_sdivzstepv _sym_prefix d_tdivzstepv _sym_prefix d_zistepv _sym_prefix d_sdivzorigin _sym_prefix d_tdivzorigin _sym_prefix d_ziorigin _sym_prefix sadjust _sym_prefix tadjust _sym_prefix bbextents _sym_prefix bbextentt _sym_prefix cacheblock _sym_prefix d_viewbuffer _sym_prefix cachewidth _sym_prefix d_pzbuffer _sym_prefix d_zrowbytes _sym_prefix d_zwidth %endif ; _sym_prefix SEGMENT .data ALIGN 4 global d_sdivzstepu global d_tdivzstepu global d_zistepu global d_sdivzstepv global d_tdivzstepv global d_zistepv global d_sdivzorigin global d_tdivzorigin global d_ziorigin d_sdivzstepu dd 0 d_tdivzstepu dd 0 d_zistepu dd 0 d_sdivzstepv dd 0 d_tdivzstepv dd 0 d_zistepv dd 0 d_sdivzorigin dd 0 d_tdivzorigin dd 0 d_ziorigin dd 0 global sadjust global tadjust global bbextents global bbextentt sadjust dd 0 tadjust dd 0 bbextents dd 0 bbextentt dd 0 global cacheblock global d_viewbuffer global cachewidth global d_pzbuffer global d_zrowbytes global d_zwidth cacheblock dd 0 cachewidth dd 0 d_viewbuffer dd 0 d_pzbuffer dd 0 d_zrowbytes dd 0 d_zwidth dd 0 ; ; ASM-only variables ; global izi izi dd 0 global pbase, s, t, sfracf, tfracf, snext, tnext global spancountminus1, zi16stepu, sdivz16stepu, tdivz16stepu global zi8stepu, sdivz8stepu, tdivz8stepu, pz s dd 0 t dd 0 snext dd 0 tnext dd 0 sfracf dd 0 tfracf dd 0 pbase dd 0 zi8stepu dd 0 sdivz8stepu dd 0 tdivz8stepu dd 0 zi16stepu dd 0 sdivz16stepu dd 0 tdivz16stepu dd 0 spancountminus1 dd 0 pz dd 0 global izistep izistep dd 0 global reciprocal_table_16, entryvec_table_16, entryvec_table_16T reciprocal_table_16 dd 040000000h, 02aaaaaaah, 020000000h dd 019999999h, 015555555h, 012492492h dd 010000000h, 0e38e38eh, 0ccccccch, 0ba2e8bah dd 0aaaaaaah, 09d89d89h, 09249249h, 08888888h extern Entry2_16 extern Entry3_16 extern Entry4_16 extern Entry5_16 extern Entry6_16 extern Entry7_16 extern Entry8_16 extern Entry9_16 extern Entry10_16 extern Entry11_16 extern Entry12_16 extern Entry13_16 extern Entry14_16 extern Entry15_16 extern Entry16_16 entryvec_table_16 dd 0, Entry2_16, Entry3_16, Entry4_16 dd Entry5_16, Entry6_16, Entry7_16, Entry8_16 dd Entry9_16, Entry10_16, Entry11_16, Entry12_16 dd Entry13_16, Entry14_16, Entry15_16, Entry16_16 extern Entry2_16T extern Entry3_16T extern Entry4_16T extern Entry5_16T extern Entry6_16T extern Entry7_16T extern Entry8_16T extern Entry9_16T extern Entry10_16T extern Entry11_16T extern Entry12_16T extern Entry13_16T extern Entry14_16T extern Entry15_16T extern Entry16_16T entryvec_table_16T dd 0, Entry2_16T, Entry3_16T, Entry4_16T dd Entry5_16T, Entry6_16T, Entry7_16T, Entry8_16T dd Entry9_16T, Entry10_16T, Entry11_16T, Entry12_16T dd Entry13_16T, Entry14_16T, Entry15_16T, Entry16_16T global DP_Count, DP_u, DP_v, DP_32768, DP_Color, DP_Pix, DP_EntryTable, DP_EntryTransTable DP_Count dd 0 DP_u dd 0 DP_v dd 0 DP_32768 dd 32768.0 DP_Color dd 0 DP_Pix dd 0 extern DP_1x1 extern DP_2x2 extern DP_3x3 extern DP_4x4 DP_EntryTable dd DP_1x1, DP_2x2, DP_3x3, DP_4x4 extern DP_T1x1 extern DP_T2x2 extern DP_T3x3 extern DP_T4x4 DP_EntryTransTable dd DP_T1x1, DP_T2x2, DP_T3x3, DP_T4x4 global advancetable, sstep, tstep, pspantemp, counttemp, jumptemp advancetable dd 0, 0 sstep dd 0 tstep dd 0 pspantemp dd 0 counttemp dd 0 jumptemp dd 0 global reciprocal_table, entryvec_table reciprocal_table dd 040000000h, 02aaaaaaah, 020000000h dd 019999999h, 015555555h, 012492492h extern Entry2_8 extern Entry3_8 extern Entry4_8 extern Entry5_8 extern Entry6_8 extern Entry7_8 extern Entry8_8 entryvec_table dd 0, Entry2_8, Entry3_8, Entry4_8 dd Entry5_8, Entry6_8, Entry7_8, Entry8_8 extern Spr8Entry2_8 extern Spr8Entry3_8 extern Spr8Entry4_8 extern Spr8Entry5_8 extern Spr8Entry6_8 extern Spr8Entry7_8 extern Spr8Entry8_8 global spr8entryvec_table spr8entryvec_table dd 0, Spr8Entry2_8, Spr8Entry3_8, Spr8Entry4_8 dd Spr8Entry5_8, Spr8Entry6_8, Spr8Entry7_8, Spr8Entry8_8 extern Spr8Entry2_8T extern Spr8Entry3_8T extern Spr8Entry4_8T extern Spr8Entry5_8T extern Spr8Entry6_8T extern Spr8Entry7_8T extern Spr8Entry8_8T global spr8Tentryvec_table spr8Tentryvec_table dd 0, Spr8Entry2_8T, Spr8Entry3_8T, Spr8Entry4_8T dd Spr8Entry5_8T, Spr8Entry6_8T, Spr8Entry7_8T, Spr8Entry8_8T extern Spr8Entry2_8T2 extern Spr8Entry3_8T2 extern Spr8Entry4_8T2 extern Spr8Entry5_8T2 extern Spr8Entry6_8T2 extern Spr8Entry7_8T2 extern Spr8Entry8_8T2 global spr8T2entryvec_table spr8T2entryvec_table dd 0, Spr8Entry2_8T2, Spr8Entry3_8T2, Spr8Entry4_8T2 dd Spr8Entry5_8T2, Spr8Entry6_8T2, Spr8Entry7_8T2, Spr8Entry8_8T2 engine/h2shared/d_zpoint.c000066400000000000000000000023631444734033100160240ustar00rootroot00000000000000/* * d_zpoint.c - software driver module for drawing z-buffered points * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "d_local.h" /* ===================== D_DrawZPoint ===================== */ void D_DrawZPoint (void) { byte *pdest; short *pz; int izi; pz = d_pzbuffer + (d_zwidth * r_zpointdesc.v) + r_zpointdesc.u; pdest = d_viewbuffer + d_scantable[r_zpointdesc.v] + r_zpointdesc.u; izi = (int)(r_zpointdesc.zi * 0x8000); if (*pz <= izi) { *pz = izi; *pdest = r_zpointdesc.color; } } engine/h2shared/debuglog.c000066400000000000000000000110601444734033100157600ustar00rootroot00000000000000/* * debuglog.c -- logging console output to a file. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2008-2010 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "debuglog.h" #ifdef PLATFORM_AMIGA #include #include #else #include #include #endif #ifdef PLATFORM_WINDOWS #include /* write() */ #include "io_msvc.h" #endif #if defined(PLATFORM_OS2)||defined(PLATFORM_DOS) #include /* write() */ #endif #if defined(PLATFORM_UNIX) || \ defined(__DJGPP__) || \ defined(PLATFORM_RISCOS) #include /* write() */ #endif #include "filenames.h" unsigned int con_debuglog = LOG_NONE; #ifdef PLATFORM_AMIGA static BPTR log_fd = 0; #else static int log_fd = -1; #endif static char logfilename[MAX_OSPATH]; /* current logfile name */ static char logbuff[MAX_PRINTMSG]; /* our log text buffer */ static const char separator_line[] = "=======================================\n"; #ifdef PLATFORM_AMIGA void LOG_Print (const char *logdata) { if (!logdata || !*logdata) return; if (!log_fd) return; Write (log_fd, (CONST APTR) logdata, strlen(logdata)); } #else void LOG_Print (const char *logdata) { if (!logdata || !*logdata) return; if (log_fd == -1) return; write (log_fd, logdata, strlen(logdata)); } #endif void LOG_Printf (const char *fmt, ...) { va_list argptr; va_start (argptr, fmt); q_vsnprintf (logbuff, sizeof(logbuff), fmt, argptr); va_end (argptr); LOG_Print (logbuff); } static void LOG_PrintVersion (void) { /* repeating the PrintVersion() messages from main() here */ LOG_Printf("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); #if defined(SERVERONLY) && !defined(H2W) LOG_Printf("Hexen II dedicated server %4.2f (%s)\n", ENGINE_VERSION, PLATFORM_STRING); #else LOG_Printf("running on %s engine %4.2f (%s)\n", ENGINE_NAME, ENGINE_VERSION, PLATFORM_STRING); #endif } void LOG_Init (quakeparms_t *parms) { int i, j; char session[24]; if (COM_CheckParm("-condebug") || COM_CheckParm("-debuglog")) { con_debuglog |= LOG_NORMAL; } if (COM_CheckParm("-devlog")) /* always log Con_DPrintf and Sys_DPrintf */ { con_debuglog |= LOG_DEVEL; } if (con_debuglog == LOG_NONE) return; j = q_strlcpy (logfilename, parms->userdir, sizeof(logfilename)); if (j && !IS_DIR_SEPARATOR(logfilename[j - 1])) q_strlcat(logfilename, DIR_SEPARATOR_STR, sizeof(logfilename)); q_strlcat(logfilename, DEBUGLOG_FILENAME, sizeof(logfilename)); Sys_DateTimeString (session); #ifdef PLATFORM_AMIGA if (!(log_fd = Open ((const STRPTR)logfilename, MODE_NEWFILE))) { con_debuglog = LOG_NONE; Sys_PrintTerm ("Error: Unable to create log file\n"); return; } #else /* Sys_unlink (logfilename); */ log_fd = open (logfilename, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (log_fd == -1) { con_debuglog = LOG_NONE; Sys_PrintTerm ("Error: Unable to create log file\n"); return; } #endif LOG_Printf("LOG started on: %s - LOG LEVEL: %s\n", session, (con_debuglog & LOG_DEVEL) ? "full" : "normal"); /* build the commandline args as a string */ q_strlcpy (logbuff, "Command line: ", sizeof(logbuff)); for (i = 0, j = 0; i < parms->argc; i++) { if (parms->argv[i][0] && parms->argv[i][0] != ' ') { q_strlcat (logbuff, parms->argv[i], sizeof(logbuff)); q_strlcat (logbuff, " ", sizeof(logbuff)); j++; } } if (j) { logbuff[sizeof(logbuff)-2] = 0; q_strlcat (logbuff, "\n", sizeof(logbuff)); LOG_Print (logbuff); } else { q_strlcat (logbuff, "(none)\n", sizeof(logbuff)); LOG_Print (logbuff); } /* print the version information to the log */ LOG_PrintVersion (); LOG_Print (separator_line); } #ifdef PLATFORM_AMIGA void LOG_Close (void) { if (!log_fd) return; Close (log_fd); log_fd = 0; } #else void LOG_Close (void) { if (log_fd == -1) return; close (log_fd); log_fd = -1; } #endif engine/h2shared/debuglog.h000066400000000000000000000031631444734033100157720ustar00rootroot00000000000000/* debuglog.h -- logging console output to a file. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2008-2010 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __DEBUGLOG_H #define __DEBUGLOG_H #define DEBUG_PREFIX "DEBUG: " /* log filenames: */ #if defined(H2W) #define DEBUGLOG_FILENAME "debug_hw.log" #elif defined(SERVERONLY) #define DEBUGLOG_FILENAME "debugded.log" #else #define DEBUGLOG_FILENAME "debug_h2.log" #endif /* log level: */ #define LOG_NONE 0 /* no logging */ #define LOG_NORMAL 1 /* normal logging: what you see on the game console and terminal */ #define LOG_DEVEL 2 /* log the _DPrintf content even if the developer cvar isn't set */ extern unsigned int con_debuglog; void LOG_Print (const char *logdata); void LOG_Printf(const char *fmt, ...) FUNC_PRINTF(1,2); struct quakeparms_s; void LOG_Init (struct quakeparms_s *parms); void LOG_Close (void); #endif /* __DEBUGLOG_H */ engine/h2shared/dos_dmesa.c000066400000000000000000000122431444734033100161320ustar00rootroot00000000000000/* gl_dmesa.c -- DOS OpenGL refresh using DMesa api. * for use with Mesa library version 5.x or 6.x possibly built * against 3dfx glide. * Copyright (C) 2015 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "gl_dos.h" #include "sys_dxe.h" #if !defined(REFGL_MESA) int DMESA_LoadAPI (void *handle) { return -1; } #else #include #if defined(GL_DLSYM) typedef DMesaVisual (*DMesaCreateVisual_f) (GLint, GLint, GLint, GLint, GLboolean, GLboolean, GLint, GLint, GLint, GLint); typedef void (*DMesaDestroyVisual_f) (DMesaVisual); typedef DMesaContext (*DMesaCreateContext_f) (DMesaVisual, DMesaContext); typedef void (*DMesaDestroyContext_f) (DMesaContext); typedef DMesaBuffer (*DMesaCreateBuffer_f) (DMesaVisual, GLint, GLint, GLint, GLint); typedef void (*DMesaDestroyBuffer_f) (DMesaBuffer b); typedef void (*DMesaSwapBuffers_f) (DMesaBuffer); typedef GLboolean (*DMesaMakeCurrent_f) (DMesaContext, DMesaBuffer); typedef DMesaProc (*DMesaGetProcAddress_f) (const char *); static DMesaCreateVisual_f DMesaCreateVisual_fp; static DMesaDestroyVisual_f DMesaDestroyVisual_fp; static DMesaCreateContext_f DMesaCreateContext_fp; static DMesaDestroyContext_f DMesaDestroyContext_fp; static DMesaCreateBuffer_f DMesaCreateBuffer_fp; static DMesaDestroyBuffer_f DMesaDestroyBuffer_fp; static DMesaSwapBuffers_f DMesaSwapBuffers_fp; static DMesaMakeCurrent_f DMesaMakeCurrent_fp; static DMesaGetProcAddress_f DMesaGetProcAddress_fp; #else #define DMesaCreateVisual_fp DMesaCreateVisual #define DMesaDestroyVisual_fp DMesaDestroyVisual #define DMesaCreateContext_fp DMesaCreateContext #define DMesaDestroyContext_fp DMesaDestroyContext #define DMesaCreateBuffer_fp DMesaCreateBuffer #define DMesaDestroyBuffer_fp DMesaDestroyBuffer #define DMesaSwapBuffers_fp DMesaSwapBuffers #define DMesaMakeCurrent_fp DMesaMakeCurrent #define DMesaGetProcAddress_fp DMesaGetProcAddress #endif static DMesaVisual dv; static DMesaContext dc; static DMesaBuffer db; static int DMESA_InitCtx (int *width, int *height, int *bpp) { /* request either 16 or 32 bpp visual */ if (*bpp <= 16) /* 565 */ dv = DMesaCreateVisual_fp(*width, *height, 16, 0, true, true, 0, 16, 0, 0); else dv = DMesaCreateVisual_fp(*width, *height, 32, 0, true, true, 8, 24, 8, 0); if (!dv) return -1; dc = DMesaCreateContext_fp(dv, NULL); if (!dc) return -1; db = DMesaCreateBuffer_fp(dv, 0, 0, *width, *height); if (!db) return -1; DMesaMakeCurrent_fp(dc, db); return 0; } static void DMESA_Shutdown (void) { if (db) { DMesaDestroyBuffer_fp(db); db = NULL; } if (dc) { DMesaDestroyContext_fp(dc); dc = NULL; } if (dv) { DMesaDestroyVisual_fp(dv); dv = NULL; } } static void DMESA_EndFrame (void) { glFlush_fp(); DMesaSwapBuffers_fp(db); } #ifdef GL_DLSYM static void *DMESA_GetProcAddress (const char *sym) { if (DMesaGetProcAddress_fp) return (void *) DMesaGetProcAddress_fp (sym); return NULL; } #else /* assume the function is present */ static void *DMESA_GetProcAddress (const char *sym) { return (void *) DMesaGetProcAddress (sym); } #endif static const char *DMESA_APIName (void) { return "DMesa"; } int DMESA_LoadAPI (void *handle) { #ifdef GL_DLSYM DOSGL_InitCtx = NULL; DOSGL_Shutdown = NULL; DOSGL_EndFrame = NULL; DOSGL_GetProcAddress = NULL; DOSGL_APIName = NULL; DMesaCreateVisual_fp = (DMesaCreateVisual_f) Sys_dlsym(handle,"_DMesaCreateVisual"); DMesaDestroyVisual_fp = (DMesaDestroyVisual_f) Sys_dlsym(handle,"_DMesaDestroyVisual"); DMesaCreateContext_fp = (DMesaCreateContext_f) Sys_dlsym(handle,"_DMesaCreateContext"); DMesaDestroyContext_fp = (DMesaDestroyContext_f) Sys_dlsym(handle,"_DMesaDestroyContext"); DMesaCreateBuffer_fp = (DMesaCreateBuffer_f) Sys_dlsym(handle,"_DMesaCreateBuffer"); DMesaDestroyBuffer_fp = (DMesaDestroyBuffer_f) Sys_dlsym(handle,"_DMesaDestroyBuffer"); DMesaSwapBuffers_fp = (DMesaSwapBuffers_f) Sys_dlsym(handle,"_DMesaSwapBuffers"); DMesaMakeCurrent_fp = (DMesaMakeCurrent_f) Sys_dlsym(handle,"_DMesaMakeCurrent"); DMesaGetProcAddress_fp = (DMesaGetProcAddress_f) Sys_dlsym(handle,"_DMesaGetProcAddress"); if (!DMesaCreateVisual_fp || !DMesaDestroyVisual_fp || !DMesaCreateContext_fp || !DMesaDestroyContext_fp || !DMesaCreateBuffer_fp || !DMesaDestroyBuffer_fp || !DMesaSwapBuffers_fp || !DMesaMakeCurrent_fp) { return -1; } #endif DOSGL_InitCtx = DMESA_InitCtx; DOSGL_Shutdown = DMESA_Shutdown; DOSGL_EndFrame = DMESA_EndFrame; DOSGL_GetProcAddress = DMESA_GetProcAddress; DOSGL_APIName = DMESA_APIName; return 0; } #endif engine/h2shared/dos_fxmesa.c000066400000000000000000000141771444734033100163340ustar00rootroot00000000000000/* gl_fxmesa.c -- DOS OpenGL refresh using fxMesa api: * for use with Mesa library built against 3dfx glide. * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2015 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* fxMesa api changes across Mesa versions: * * Mesa-3.1 .. 3.4: seems no change. * Mesa-3.4 -> 3.5: fxMesaSetNearFar() removed. * Mesa-3.5 .. 5.0: seems no change. * Mesa-5.0 -> 5.1: * - new FXMESA_COLORDEPTH and FXMESA_SHARE_CONTEXT attributes added. * - new fxGetScreenGeometry() added. * - fxQueryHardware() semantics changed: it returned success (bool), * instead of hardware type (int). * Mesa-5.1 -> 6.x: * - fxQueryHardware() became a private function. * * NOTE: * Direct use of fxMesa api from Mesa >= 5.1 does NOT seem to work. */ #include "quakedef.h" #include "gl_dos.h" #include "sys_dxe.h" #if !defined(REFGL_FXMESA) int FXMESA_LoadAPI (void *handle) { return -1; } #else #include #ifndef FXMESA_COLORDEPTH #define FXMESA_COLORDEPTH 20 #endif #if defined(GL_DLSYM) typedef fxMesaContext (*fxMesaCreateContext_f) (GLuint, GrScreenResolution_t, GrScreenRefresh_t, const GLint attribList[]); typedef fxMesaContext (*fxMesaCreateBestContext_f) (GLuint, GLint, GLint, const GLint attribList[]); typedef void (*fxMesaMakeCurrent_f) (fxMesaContext); typedef void (*fxMesaDestroyContext_f) (fxMesaContext); typedef void (*fxMesaSwapBuffers_f) (void); typedef void (*FXMESAPROC) (); typedef FXMESAPROC (*fxMesaGetProcAddress_f) (const char *); static fxMesaCreateContext_f fxMesaCreateContext_fp; static fxMesaCreateBestContext_f fxMesaCreateBestContext_fp; static fxMesaMakeCurrent_f fxMesaMakeCurrent_fp; static fxMesaDestroyContext_f fxMesaDestroyContext_fp; static fxMesaSwapBuffers_f fxMesaSwapBuffers_fp; static fxMesaGetProcAddress_f fxMesaGetProcAddress_fp; #else #define fxMesaCreateContext_fp fxMesaCreateContext #define fxMesaCreateBestContext_fp fxMesaCreateBestContext #define fxMesaMakeCurrent_fp fxMesaMakeCurrent #define fxMesaDestroyContext_fp fxMesaDestroyContext #define fxMesaSwapBuffers_fp fxMesaSwapBuffers /*#define fxMesaGetProcAddress_fp fxMesaGetProcAddress*/ #endif static fxMesaContext fc = NULL; typedef struct { int width, height; GrScreenResolution_t fxmode; } fxmode_t; static fxmode_t fx_modes[] = { { 320,200, GR_RESOLUTION_320x200 }, { 320,240, GR_RESOLUTION_320x240 }, { 400,256, GR_RESOLUTION_400x256 }, { 400,300, GR_RESOLUTION_400x300 }, { 512,384, GR_RESOLUTION_512x384 }, { 640,200, GR_RESOLUTION_640x200 }, { 640,350, GR_RESOLUTION_640x350 }, { 640,400, GR_RESOLUTION_640x400 }, { 640,480, GR_RESOLUTION_640x480 }, { 800,600, GR_RESOLUTION_800x600 }, { 960,720, GR_RESOLUTION_960x720 }, { 856,480, GR_RESOLUTION_856x480 }, { 512,256, GR_RESOLUTION_512x256 }, { 1024,768, GR_RESOLUTION_1024x768 }, { 1280,1024,GR_RESOLUTION_1280x1024 }, { 1600,1200,GR_RESOLUTION_1600x1200 }, }; #define MAX_FXMODES (int)(sizeof(fx_modes) / sizeof(fx_modes[0])) static GrScreenResolution_t findres (int *width, int *height) { int i; for (i = 0; i < MAX_FXMODES; i++) { if (*width <= fx_modes[i].width && *height <= fx_modes[i].height) { *width = fx_modes[i].width; *height = fx_modes[i].height; return fx_modes[i].fxmode; } } *width = 640; *height = 480; return GR_RESOLUTION_640x480; } static int FXMESA_InitCtx (int *width, int *height, int *bpp) { GLint attribs[32]; attribs[0] = FXMESA_DOUBLEBUFFER; attribs[1] = FXMESA_ALPHA_SIZE; attribs[2] = 1; attribs[3] = FXMESA_DEPTH_SIZE; attribs[4] = 1; attribs[5] = FXMESA_NONE; if (*bpp != 16) { Con_SafePrintf("ignoring %d bpp request, using 16 bpp.\n", *bpp); *bpp = 16; } // fc = fxMesaCreateBestContext_fp(0, *width, *height, attribs); fc = fxMesaCreateContext_fp(0, findres(width, height), GR_REFRESH_60Hz, attribs); if (!fc) return -1; fxMesaMakeCurrent_fp(fc); return 0; } static void FXMESA_Shutdown (void) { if (fc) { fxMesaDestroyContext_fp(fc); fc = NULL; } } static void FXMESA_EndFrame (void) { glFlush_fp(); fxMesaSwapBuffers_fp(); } #ifdef GL_DLSYM static void *FXMESA_GetProcAddress (const char *sym) { if (fxMesaGetProcAddress_fp) return (void *) fxMesaGetProcAddress_fp (sym); return NULL; } #else /* assume the function is NOT present */ static void *FXMESA_GetProcAddress (const char *sym) { return NULL; } #endif static const char *FXMESA_APIName (void) { return "fxMesa"; } int FXMESA_LoadAPI (void *handle) { #ifdef GL_DLSYM DOSGL_InitCtx = NULL; DOSGL_Shutdown = NULL; DOSGL_EndFrame = NULL; DOSGL_GetProcAddress = NULL; DOSGL_APIName = NULL; fxMesaCreateContext_fp = (fxMesaCreateContext_f) Sys_dlsym(handle,"_fxMesaCreateContext"); fxMesaCreateBestContext_fp = (fxMesaCreateBestContext_f) Sys_dlsym(handle,"_fxMesaCreateBestContext"); fxMesaMakeCurrent_fp = (fxMesaMakeCurrent_f) Sys_dlsym(handle,"_fxMesaMakeCurrent"); fxMesaDestroyContext_fp = (fxMesaDestroyContext_f) Sys_dlsym(handle,"_fxMesaDestroyContext"); fxMesaSwapBuffers_fp = (fxMesaSwapBuffers_f) Sys_dlsym(handle,"_fxMesaSwapBuffers"); fxMesaGetProcAddress_fp = (fxMesaGetProcAddress_f) Sys_dlsym(handle,"_fxMesaGetProcAddress"); if (!fxMesaCreateContext_fp || !fxMesaCreateBestContext_fp || !fxMesaMakeCurrent_fp || !fxMesaDestroyContext_fp || !fxMesaSwapBuffers_fp) { return -1; } #endif DOSGL_InitCtx = FXMESA_InitCtx; DOSGL_Shutdown = FXMESA_Shutdown; DOSGL_EndFrame = FXMESA_EndFrame; DOSGL_GetProcAddress = FXMESA_GetProcAddress; DOSGL_APIName = FXMESA_APIName; return 0; } #endif engine/h2shared/dos_sage.c000066400000000000000000000102401444734033100157530ustar00rootroot00000000000000/* gl_sage.c -- DOS OpenGL refresh using sage api : * for use with SAGE library of Daniel Borca. * http://www.geocities.ws/dborca/opengl/sage.html * https://github.com/sezero/sage * Copyright (C) 2015 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "gl_dos.h" #include "sys_dxe.h" #if !defined(REFGL_SAGE) int SAGE_LoadAPI (void *handle) { return -1; } #else #include #if defined(GL_DLSYM) typedef int (*sage_init_f) (void); typedef sageContext* (*sage_open_f) (int, int, int, int, int, int, int); typedef int (*sage_bind_f) (sageContext *, void *, int, int); typedef void (*sage_shut_f) (sageContext *); typedef void (*sage_fini_f) (void); typedef void (*sage_swap_f) (int); typedef SageProc (*sage_GetProcAddress_f) (const char *); static sage_init_f sage_init_fp; static sage_open_f sage_open_fp; static sage_bind_f sage_bind_fp; static sage_shut_f sage_shut_fp; static sage_fini_f sage_fini_fp; static sage_swap_f sage_swap_fp; static sage_GetProcAddress_f sage_GetProcAddress_fp; #else #define sage_init_fp sage_init #define sage_open_fp sage_open #define sage_bind_fp sage_bind #define sage_shut_fp sage_shut #define sage_fini_fp sage_fini #define sage_swap_fp sage_swap #define sage_GetProcAddress_fp sage_GetProcAddress #endif static sageContext *ctx; static void SAGE_Shutdown (void); static int SAGE_InitCtx (int *width, int *height, int *bpp) { int hwbpp; hwbpp = sage_init_fp (); if (hwbpp <= 0) return -1; if (hwbpp < *bpp) { Con_SafePrintf("%d bpp not supported. Falling back to 16 bpp.\n", *bpp); *bpp = 16; } /* request either 16 or 32 bpp visual */ if (*bpp <= 16) ctx = sage_open_fp(true, 5, 6, 5, 0, 16, 0); else ctx = sage_open_fp(true, 8, 8, 8, 8, 24, 8); if (!ctx) { sage_fini_fp (); return -1; } if (sage_bind_fp(ctx, (void *)0xB16B00B5, *width, *height) != 0) { sage_bind_fp (NULL, NULL, 0, 0); sage_shut_fp (ctx); sage_fini_fp (); ctx = NULL; return -1; } return 0; } static void SAGE_Shutdown (void) { if (ctx) { sage_bind_fp (NULL, NULL, 0, 0); sage_shut_fp (ctx); sage_fini_fp (); ctx = NULL; } } static void SAGE_EndFrame (void) { sage_swap_fp (1); } #ifdef GL_DLSYM static void *SAGE_GetProcAddress (const char *sym) { if (sage_GetProcAddress_fp) return (void *) sage_GetProcAddress_fp (sym); return NULL; } #else /* assume the function is present */ static void *SAGE_GetProcAddress (const char *sym) { return (void *) sage_GetProcAddress (sym); } #endif static const char *SAGE_APIName (void) { return "sage"; } int SAGE_LoadAPI (void *handle) { #ifdef GL_DLSYM DOSGL_InitCtx = NULL; DOSGL_Shutdown = NULL; DOSGL_EndFrame = NULL; DOSGL_GetProcAddress = NULL; DOSGL_APIName = NULL; sage_init_fp = (sage_init_f) Sys_dlsym(handle,"_sage_init"); sage_open_fp = (sage_open_f) Sys_dlsym(handle,"_sage_open"); sage_bind_fp = (sage_bind_f) Sys_dlsym(handle,"_sage_bind"); sage_shut_fp = (sage_shut_f) Sys_dlsym(handle,"_sage_shut"); sage_fini_fp = (sage_fini_f) Sys_dlsym(handle,"_sage_fini"); sage_swap_fp = (sage_swap_f) Sys_dlsym(handle,"_sage_swap"); sage_GetProcAddress_fp = (sage_GetProcAddress_f) Sys_dlsym(handle,"_sage_GetProcAddress"); if (!sage_init_fp || !sage_open_fp || !sage_bind_fp || !sage_shut_fp || !sage_fini_fp || !sage_swap_fp) { return -1; } #endif DOSGL_InitCtx = SAGE_InitCtx; DOSGL_Shutdown = SAGE_Shutdown; DOSGL_EndFrame = SAGE_EndFrame; DOSGL_GetProcAddress = SAGE_GetProcAddress; DOSGL_APIName = SAGE_APIName; return 0; } #endif engine/h2shared/dos_v2.c000066400000000000000000000120771444734033100153750ustar00rootroot00000000000000/* * dos_v2.c -- DOS / DJGPP system interface. * from quake1 source with minor adaptations for uhexen2. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #ifndef DJGPP_NO_INLINES #define DJGPP_NO_INLINES 1 #endif #include "dosisms.h" #include "q_stdinc.h" #include "h2config.h" #include "compiler.h" #include "sys.h" /* global variables: */ __dpmi_regs regs; static unsigned int conventional_memory = (unsigned int)-1; static void map_in_conventional_memory (void) { // if (! (_crt0_startup_flags & _CRT0_FLAG_NEARPTR)) if (conventional_memory == (unsigned int)-1) { if (__djgpp_nearptr_enable()) { conventional_memory = __djgpp_conventional_base; } } } unsigned int ptr2real (void *ptr) { map_in_conventional_memory(); return (int)ptr - conventional_memory; } void *real2ptr (unsigned int real) { map_in_conventional_memory(); return (void *) (real + conventional_memory); } void *far2ptr (unsigned int farptr) { return real2ptr(((farptr & ~0xffff) >>12) + (farptr&0xffff)); } unsigned int ptr2far (void *ptr) { return ((ptr2real(ptr)&~0xf) << 12) + (ptr2real(ptr) & 0xf); } int dos_inportb (int port) { return inportb(port); } int dos_inportw (int port) { return inportw(port); } void dos_outportb (int port, int val) { outportb(port, val); } void dos_outportw (int port, int val) { outportw(port, val); } void dos_irqenable (void) { enable(); } void dos_irqdisable (void) { disable(); } // // Returns 0 on success // int dos_int86 (int vec) { int rc; regs.x.ss = regs.x.sp = 0; rc = _go32_dpmi_simulate_int(vec, ®s); return rc || (regs.x.flags & 1); } int dos_int386 (int vec, __dpmi_regs *inregs, __dpmi_regs *outregs) { int rc; memcpy(outregs, inregs, sizeof(__dpmi_regs)); outregs->x.ss = outregs->x.sp = 0; rc = _go32_dpmi_simulate_int(vec, outregs); return rc || (outregs->x.flags & 1); } // // Because of a quirk in dj's alloc-dos-memory wrapper, you need to keep // the seginfo structure around for when you free the mem. // #define MAX_SEGINFO 10 static _go32_dpmi_seginfo seginfo[MAX_SEGINFO]; void *dos_getmemory (int size) { int rc; _go32_dpmi_seginfo info; static int firsttime = 1; int i; if (firsttime) { firsttime = 0; memset(seginfo, 0, sizeof(seginfo)); } info.size = (size + 15) / 16; rc = _go32_dpmi_allocate_dos_memory(&info); if (rc) return NULL; for (i = 0; i < MAX_SEGINFO; i++) { if (!seginfo[i].rm_segment) { seginfo[i] = info; return real2ptr((int) info.rm_segment << 4); } } Sys_Error("%s: Reached MAX_SEGINFO", __thisfunc__); } void dos_freememory (void *ptr) { int i; int segment; segment = ptr2real(ptr) >> 4; for (i = 0; i < MAX_SEGINFO; i++) { if (seginfo[i].rm_segment == segment) { _go32_dpmi_free_dos_memory(&seginfo[i]); seginfo[i].rm_segment = 0; return; } } Sys_Error("%s: Unknown seginfo", __thisfunc__); } static struct handlerhistory_s { int intr; _go32_dpmi_seginfo pm_oldvec; } handlerhistory[4]; static int handlercount = 0; void dos_registerintr (int intr, void (*handler)(void)) { _go32_dpmi_seginfo info; struct handlerhistory_s *oldstuff; oldstuff = &handlerhistory[handlercount]; // remember old handler _go32_dpmi_get_protected_mode_interrupt_vector(intr, &oldstuff->pm_oldvec); oldstuff->intr = intr; info.pm_offset = (int) handler; _go32_dpmi_allocate_iret_wrapper(&info); // set new protected mode handler _go32_dpmi_set_protected_mode_interrupt_vector(intr, &info); handlercount++; } void dos_restoreintr (int intr) { int i; struct handlerhistory_s *oldstuff; // find and reinstall previous interrupt for (i = 0; i < handlercount; i++) { oldstuff = &handlerhistory[i]; if (oldstuff->intr == intr) { _go32_dpmi_set_protected_mode_interrupt_vector(intr, &oldstuff->pm_oldvec); oldstuff->intr = -1; break; } } } int dos_lockmem (void *addr, int size) { __dpmi_meminfo info; info.address = (long) addr + __djgpp_base_address; info.size = size; if (__dpmi_lock_linear_region(&info)) return __dpmi_error; else return 0; } int dos_unlockmem (void *addr, int size) { __dpmi_meminfo info; info.address = (long) addr + __djgpp_base_address; info.size = size; if (__dpmi_unlock_linear_region(&info)) return __dpmi_error; else return 0; } engine/h2shared/dosasm.asm000066400000000000000000000036171444734033100160250ustar00rootroot00000000000000; dosasm.asm -- ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; ; externs from C code ; ; externs from ASM-only code ; SEGMENT .data fpenv: dd 0,0,0,0,0,0,0,0 SEGMENT .text global StartMSRInterval StartMSRInterval: mov ecx,011h ; read the CESR db 00Fh db 032h ; RDMSR and eax,0FE3FFE3Fh ; stop both counters db 00Fh db 030h ; WRMSR mov eax, [esp+4] ; point counter 0 to desired event, with counters and eax,03Fh ; still stopped mov ecx,011h db 00Fh db 030h ; WRMSR mov ecx,012h ; set counter 0 to the value 0 sub eax,eax sub edx,edx db 00Fh db 030h ; WRMSR mov eax, [esp+4] ; restart counter 0 with selected event and eax,03Fh sub edx,edx or eax,0C0h mov ecx,011h ; control and event select db 00Fh db 030h ; WRMSR ret global EndMSRInterval EndMSRInterval: mov ecx,012h ; counter 0 db 00Fh db 032h ; RDMSR ret ; lower 32 bits of count in %eax %if 0 SEGMENT .data Lxxx: dd 0 SEGMENT .text global setstackcheck setstackcheck: mov eax,esp sub eax,038000h mov dword [eax],05A5A5A5Ah mov [Lxxx],eax ret global dostackcheck dostackcheck: mov edx, [Lxxx] mov eax,0 cmp dword [edx],05A5A5A5Ah jz qqq inc eax qqq: ret %endif engine/h2shared/dosisms.h000066400000000000000000000064341444734033100156670ustar00rootroot00000000000000/* * dosisms.h -- DOS / DJGPP system header. * From quake1 source with minor adaptations for uhexen2. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _DOSISMS_H_ #define _DOSISMS_H_ #include #if defined(DEBUG) && \ !defined(DJGPP_NO_INLINES) #define DJGPP_NO_INLINES 1 #endif #if 0 /* same as __dpmi_regs */ typedef union { struct { unsigned long edi; unsigned long esi; unsigned long ebp; unsigned long res; unsigned long ebx; unsigned long edx; unsigned long ecx; unsigned long eax; } d; struct { unsigned short di, di_hi; unsigned short si, si_hi; unsigned short bp, bp_hi; unsigned short res, res_hi; unsigned short bx, bx_hi; unsigned short dx, dx_hi; unsigned short cx, cx_hi; unsigned short ax, ax_hi; unsigned short flags; unsigned short es; unsigned short ds; unsigned short fs; unsigned short gs; unsigned short ip; unsigned short cs; unsigned short sp; unsigned short ss; } x; struct { unsigned char edi[4]; unsigned char esi[4]; unsigned char ebp[4]; unsigned char res[4]; unsigned char bl, bh, ebx_b2, ebx_b3; unsigned char dl, dh, edx_b2, edx_b3; unsigned char cl, ch, ecx_b2, ecx_b3; unsigned char al, ah, eax_b2, eax_b3; } h; } regs_t; #endif /* #if 0 */ int dos_lockmem (void *addr, int size); int dos_unlockmem (void *addr, int size); void *dos_getmemory (int size); void dos_freememory (void *ptr); #define dos_getheapsize _go32_dpmi_remaining_physical_memory unsigned int ptr2real (void *ptr); void *real2ptr (unsigned int real); void *far2ptr (unsigned int farptr); unsigned int ptr2far (void *ptr); #if !defined(DJGPP_NO_INLINES) /* shortcuts for when we aren't debugging */ #include #define dos_inportb inportb #define dos_inportw inportw #define dos_outportb outportb #define dos_outportw outportw #else /* DJGPP_NO_INLINES */ int dos_inportb (int port); int dos_inportw (int port); void dos_outportb (int port, int val); void dos_outportw (int port, int val); #endif /* DJGPP_NO_INLINES */ void dos_irqenable (void); void dos_irqdisable (void); void dos_registerintr (int intr, void (*handler)(void)); void dos_restoreintr (int intr); int dos_int86 (int vec); int dos_int386 (int vec, __dpmi_regs *inregs, __dpmi_regs *outregs); /* global variables: */ extern __dpmi_regs regs; /* memory setup: */ #define LEAVE_FOR_CACHE 0x80000 /* 512K - FIXME: tune */ #define MALLOC_NEEDMEM 0x20000 /* 128K - FIXME: tune */ #if defined(USE_WATT32) #define LOCKED_FOR_MALLOC (MALLOC_NEEDMEM + WATT32_NEEDMEM) #else #define LOCKED_FOR_MALLOC (MALLOC_NEEDMEM) #endif #endif /* _DOSISMS_H_ */ engine/h2shared/draw.c000066400000000000000000001231511444734033100151320ustar00rootroot00000000000000/* * draw.c * This is the only file outside the refresh that touches the vid buffer. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "hashindex.h" #include "r_shared.h" typedef struct { vrect_t rect; int width; int height; byte *ptexbytes; int rowbytes; } rectdesc_t; static rectdesc_t r_rectdesc; static byte *draw_smallchars; // Small characters for status bar static byte *draw_chars; // 8*8 graphic characters static qpic_t *draw_backtile; #if defined(DRAW_LOADINGSKULL) static qpic_t *draw_disc[MAX_DISC] = { NULL // make the first one null for sure }; #endif qboolean draw_reinit = false; //============================================================================= /* Support Routines */ typedef struct cachepic_s { char name[MAX_QPATH]; cache_user_t cache; } cachepic_t; #define MAX_CACHED_PICS 256 static cachepic_t menu_cachepics[MAX_CACHED_PICS]; static int menu_numcachepics; static hashindex_t hash_cachepics; static void Draw_PicCheckError (void *ptr, const char *name) { if (!ptr) Sys_Error ("Failed to load %s", name); } qpic_t *Draw_PicFromWad (const char *name) { return (qpic_t *) W_GetLumpName(name); } /* ================ Draw_CachePic ================ */ qpic_t *Draw_CachePic (const char *path) { cachepic_t *pic; int i, key; qpic_t *dat; key = Hash_GenerateKeyString (&hash_cachepics, path, true); for (i = Hash_First(&hash_cachepics, key); i != -1; i = Hash_Next(&hash_cachepics, i)) { pic = &menu_cachepics[i]; if (!strcmp (path, pic->name)) break; } if (i == -1) { if (menu_numcachepics == MAX_CACHED_PICS) Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); Hash_Add (&hash_cachepics, key, menu_numcachepics); pic = &menu_cachepics[menu_numcachepics]; menu_numcachepics++; q_strlcpy (pic->name, path, MAX_QPATH); } dat = (qpic_t *) Cache_Check (&pic->cache); if (dat) return dat; // // load the pic from disk // FS_LoadCacheFile (path, &pic->cache, NULL); dat = (qpic_t *)pic->cache.data; Draw_PicCheckError (dat, path); SwapPic (dat); return dat; } #if !defined(DRAW_PROGRESSBARS) /* ================ Draw_CacheLoadingPic like Draw_CachePic() but only for loading.lmp with its progress bars eliminated. ================ */ static const char ls_path[] = "gfx/menu/loading.lmp"; qpic_t *Draw_CacheLoadingPic (void) { cachepic_t *pic; int i, key; qpic_t *dat; key = Hash_GenerateKeyString (&hash_cachepics, ls_path, true); for (i = Hash_First(&hash_cachepics, key); i != -1; i = Hash_Next(&hash_cachepics, i)) { pic = &menu_cachepics[i]; if (!strcmp (ls_path, pic->name)) break; } if (i == -1) { if (menu_numcachepics == MAX_CACHED_PICS) Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); Hash_Add (&hash_cachepics, key, menu_numcachepics); pic = &menu_cachepics[menu_numcachepics]; menu_numcachepics++; q_strlcpy (pic->name, ls_path, MAX_QPATH); } dat = (qpic_t *) Cache_Check (&pic->cache); if (dat) return dat; // // load the pic from disk // FS_LoadCacheFile (ls_path, &pic->cache, NULL); dat = (qpic_t *)pic->cache.data; Draw_PicCheckError (dat, ls_path); SwapPic (dat); if (fs_filesize != 17592 || dat->width != 157 || dat->height != 112) return dat; /* kill the progress slot pixels between rows [85:103] */ memmove(dat->data + 157*85, dat->data + 157*104, 157*(112 - 104)); dat->height -= (104 - 85); return dat; } #endif /* !DRAW_PROGRESSBARS */ #if FULLSCREEN_INTERMISSIONS /* ================ Draw_CachePicResize New function by Pa3PyX; will load a pic resizing it (needed for intermissions) ================ */ qpic_t *Draw_CachePicResize (const char *path, int targetWidth, int targetHeight) { cachepic_t *pic; int i, j, key; int sourceWidth, sourceHeight; qpic_t *dat, *temp; key = Hash_GenerateKeyString (&hash_cachepics, path, true); for (i = Hash_First(&hash_cachepics, key); i != -1; i = Hash_Next(&hash_cachepics, i)) { pic = &menu_cachepics[i]; if (!strcmp (path, pic->name)) break; } if (i == -1) { if (menu_numcachepics == MAX_CACHED_PICS) Sys_Error("menu_numcachepics == MAX_CACHED_PICS"); Hash_Add (&hash_cachepics, key, menu_numcachepics); pic = &menu_cachepics[menu_numcachepics]; menu_numcachepics++; q_strlcpy(pic->name, path, MAX_QPATH); } dat = (qpic_t *) Cache_Check(&pic->cache); if (dat) { if (targetWidth == dat->width && targetHeight == dat->height) return dat; else Cache_Free (&pic->cache); } // Allocate original data temporarily temp = (qpic_t *)FS_LoadTempFile(path, NULL); Draw_PicCheckError (temp, path); SwapPic(temp); /* I wish Carmack would thought of something more intuitive than out-of-bounds array for storing image data */ Cache_Alloc(&pic->cache, targetWidth * targetHeight * sizeof(byte) + sizeof(qpic_t), path); dat = (qpic_t *)pic->cache.data; if (!dat) Sys_Error("%s: failed to load %s (cache flushed prematurely)", __thisfunc__, path); dat->width = targetWidth; dat->height = targetHeight; sourceWidth = temp->width; sourceHeight = temp->height; for (j = 0; j < targetHeight; j++) { for (i = 0; i < targetWidth; i++) { dat->data[i + targetWidth * j] = temp->data[(i * sourceWidth / targetWidth) + sourceWidth * (j * sourceHeight / targetHeight)]; } } return dat; } #endif /* FULLSCREEN_INTERMISSIONS */ /* =============== Draw_Init =============== */ void Draw_Init (void) { #if defined(DRAW_LOADINGSKULL) int i; char temp[MAX_QPATH]; if (!draw_reinit) { for (i = 0; i < MAX_DISC; i++) draw_disc[i] = NULL; } // Do this backwards so we don't try and draw the // skull as we are loading for (i = MAX_DISC - 1; i >= 0; i--) { if (draw_disc[i]) Z_Free (draw_disc[i]); q_snprintf(temp, sizeof(temp), "gfx/menu/skull%d.lmp", i); draw_disc[i] = (qpic_t *)FS_LoadZoneFile (temp, Z_SECZONE, NULL); // Draw_PicCheckError (draw_disc[i], temp); if (draw_disc[i]) SwapPic (draw_disc[i]); } #endif /* DRAW_LOADINGSKULL */ if (draw_chars) Z_Free (draw_chars); draw_chars = FS_LoadZoneFile ("gfx/menu/conchars.lmp", Z_SECZONE, NULL); Draw_PicCheckError (draw_chars, "gfx/menu/conchars.lmp"); if (fs_filesize != 256*128) { Sys_Error ("gfx/menu/conchars.lmp: bad size."); } draw_smallchars = (byte *) W_GetLumpName("tinyfont"); if (draw_backtile) Z_Free (draw_backtile); draw_backtile = (qpic_t *)FS_LoadZoneFile ("gfx/menu/backtile.lmp", Z_SECZONE, NULL); Draw_PicCheckError (draw_backtile, "gfx/menu/backtile.lmp"); SwapPic (draw_backtile); r_rectdesc.width = draw_backtile->width; r_rectdesc.height = draw_backtile->height; r_rectdesc.ptexbytes = draw_backtile->data; r_rectdesc.rowbytes = draw_backtile->width; if (!draw_reinit) Hash_Allocate (&hash_cachepics, MAX_CACHED_PICS); } /* =============== Draw_ReInit Delete and reload textures that read during engine's init phase which may be changed by mods and purge all others, i.e. map/model textures. Should NEVER be called when a map is active: Only intended to be called upon a game directory change. =============== */ void Draw_ReInit (void) { int temp; temp = scr_disabled_for_loading; scr_disabled_for_loading = true; draw_reinit = true; W_LoadWadFile ("gfx.wad"); Draw_Init(); SCR_Init(); Sbar_Init(); draw_reinit = false; scr_disabled_for_loading = temp; } /* ================ Draw_Character Draws one 8*8 graphics character with 0 being transparent. It can be clipped to the top of the screen to allow the console to be smoothly scrolled off. ================ */ void Draw_Character (int x, int y, unsigned int num) { byte *source; int drawline; int row, col; num &= 511; if (y <= -8) return; // totally off screen if (y > vid.height - 8 || x < 0 || x > vid.width - 8) return; row = num >> 5; col = num & 31; source = draw_chars + (row<<11) + (col<<3); if (y < 0) { // clipped drawline = 8 + y; source -= 256*y; y = 0; } else drawline = 8; if (r_pixbytes == 1) { byte *dest = vid.conbuffer + y*vid.conrowbytes + x; switch (trans_level) { case 0: while (drawline--) { if (source[0]) dest[0] = source[0]; if (source[1]) dest[1] = source[1]; if (source[2]) dest[2] = source[2]; if (source[3]) dest[3] = source[3]; if (source[4]) dest[4] = source[4]; if (source[5]) dest[5] = source[5]; if (source[6]) dest[6] = source[6]; if (source[7]) dest[7] = source[7]; source += 256; dest += vid.conrowbytes; } break; case 1: while (drawline--) { if (source[0]) dest[0] = mainTransTable[(((unsigned int)dest[0])<<8) + source[0]]; if (source[1]) dest[1] = mainTransTable[(((unsigned int)dest[1])<<8) + source[1]]; if (source[2]) dest[2] = mainTransTable[(((unsigned int)dest[2])<<8) + source[2]]; if (source[3]) dest[3] = mainTransTable[(((unsigned int)dest[3])<<8) + source[3]]; if (source[4]) dest[4] = mainTransTable[(((unsigned int)dest[4])<<8) + source[4]]; if (source[5]) dest[5] = mainTransTable[(((unsigned int)dest[5])<<8) + source[5]]; if (source[6]) dest[6] = mainTransTable[(((unsigned int)dest[6])<<8) + source[6]]; if (source[7]) dest[7] = mainTransTable[(((unsigned int)dest[7])<<8) + source[7]]; source += 256; dest += vid.conrowbytes; } break; case 2: while (drawline--) { if (source[0]) dest[0] = mainTransTable[(((unsigned int)source[0])<<8) + dest[0]]; if (source[1]) dest[1] = mainTransTable[(((unsigned int)source[1])<<8) + dest[1]]; if (source[2]) dest[2] = mainTransTable[(((unsigned int)source[2])<<8) + dest[2]]; if (source[3]) dest[3] = mainTransTable[(((unsigned int)source[3])<<8) + dest[3]]; if (source[4]) dest[4] = mainTransTable[(((unsigned int)source[4])<<8) + dest[4]]; if (source[5]) dest[5] = mainTransTable[(((unsigned int)source[5])<<8) + dest[5]]; if (source[6]) dest[6] = mainTransTable[(((unsigned int)source[6])<<8) + dest[6]]; if (source[7]) dest[7] = mainTransTable[(((unsigned int)source[7])<<8) + dest[7]]; source += 256; dest += vid.conrowbytes; } break; } } else /* r_pixbytes == 2 */ { // FIXME: pre-expand to native format? unsigned short *dest = (unsigned short *) ((byte *)vid.conbuffer + y*vid.conrowbytes + (x<<1)); // FIXME: transparency bits are missing while (drawline--) { if (source[0]) dest[0] = d_8to16table[source[0]]; if (source[1]) dest[1] = d_8to16table[source[1]]; if (source[2]) dest[2] = d_8to16table[source[2]]; if (source[3]) dest[3] = d_8to16table[source[3]]; if (source[4]) dest[4] = d_8to16table[source[4]]; if (source[5]) dest[5] = d_8to16table[source[5]]; if (source[6]) dest[6] = d_8to16table[source[6]]; if (source[7]) dest[7] = d_8to16table[source[7]]; source += 256; dest += vid.conrowbytes / 2; } } } /* ================ Draw_String ================ */ void Draw_String (int x, int y, const char *str) { while (*str) { Draw_Character (x, y, *str); str++; x += 8; } } void Draw_RedString (int x, int y, const char *str) { while (*str) { Draw_Character (x, y, ((unsigned char)(*str))+256); str++; x += 8; } } static void Draw_Pixel (int x, int y, const byte color) { if (r_pixbytes == 1) { byte *dest = vid.conbuffer + y*vid.conrowbytes + x; *dest = color; } else /* r_pixbytes == 2 */ { // FIXME: pre-expand to native format? unsigned short *dest = (unsigned short *) ((byte *)vid.conbuffer + y*vid.conrowbytes + (x<<1)); *dest = d_8to16table[color]; } } /* ================ Draw_Crosshair ================ */ void Draw_Crosshair (void) { int x, y; byte c = (byte)crosshaircolor.integer; x = scr_vrect.x + scr_vrect.width/2 + cl_crossx.value; y = scr_vrect.y + scr_vrect.height/2 + cl_crossy.value; if (crosshair.integer == 2) { Draw_Pixel(x - 1, y, c); Draw_Pixel(x - 3, y, c); Draw_Pixel(x + 1, y, c); Draw_Pixel(x + 3, y, c); Draw_Pixel(x, y - 1, c); Draw_Pixel(x, y - 3, c); Draw_Pixel(x, y + 1, c); Draw_Pixel(x, y + 3, c); } else if (crosshair.integer) { Draw_Character (x - 4, y - 4, '+'); } } //========================================================================== // // Draw_SmallCharacter // // Draws a small character that is clipped at the bottom edge of the // screen. // //========================================================================== void Draw_SmallCharacter (int x, int y, int num) { byte *source; int height, row, col; if (num < 32) { num = 0; } else if (num >= 'a' && num <= 'z') { num -= 64; } else if (num > '_') { num = 0; } else { num -= 32; } if (y >= vid.height) { // Totally off screen return; } #ifdef PARANOID if ((y < 0) || (x < 0) || (x+8 > vid.width)) { Sys_Error("Bad Draw_SmallCharacter: (%d, %d)", x, y); } #endif if (y + 5 > vid.height) { height = vid.height - y; } else { height = 5; } row = num >> 4; col = num & 15; source = draw_smallchars + (row<<10) + (col<<3); if (r_pixbytes == 1) { byte *dest = vid.buffer + y*vid.rowbytes + x; switch (trans_level) { case 0: while (height--) { if (source[0]) dest[0] = source[0]; if (source[1]) dest[1] = source[1]; if (source[2]) dest[2] = source[2]; if (source[3]) dest[3] = source[3]; if (source[4]) dest[4] = source[4]; if (source[5]) dest[5] = source[5]; if (source[6]) dest[6] = source[6]; if (source[7]) dest[7] = source[7]; source += 128; dest += vid.conrowbytes; } break; case 1: while (height--) { if (source[0]) dest[0] = mainTransTable[(((unsigned int)dest[0])<<8) + source[0]]; if (source[1]) dest[1] = mainTransTable[(((unsigned int)dest[1])<<8) + source[1]]; if (source[2]) dest[2] = mainTransTable[(((unsigned int)dest[2])<<8) + source[2]]; if (source[3]) dest[3] = mainTransTable[(((unsigned int)dest[3])<<8) + source[3]]; if (source[4]) dest[4] = mainTransTable[(((unsigned int)dest[4])<<8) + source[4]]; if (source[5]) dest[5] = mainTransTable[(((unsigned int)dest[5])<<8) + source[5]]; if (source[6]) dest[6] = mainTransTable[(((unsigned int)dest[6])<<8) + source[6]]; if (source[7]) dest[7] = mainTransTable[(((unsigned int)dest[7])<<8) + source[7]]; source += 128; dest += vid.conrowbytes; } break; case 2: while (height--) { if (source[0]) dest[0] = mainTransTable[(((unsigned int)source[0])<<8) + dest[0]]; if (source[1]) dest[1] = mainTransTable[(((unsigned int)source[1])<<8) + dest[1]]; if (source[2]) dest[2] = mainTransTable[(((unsigned int)source[2])<<8) + dest[2]]; if (source[3]) dest[3] = mainTransTable[(((unsigned int)source[3])<<8) + dest[3]]; if (source[4]) dest[4] = mainTransTable[(((unsigned int)source[4])<<8) + dest[4]]; if (source[5]) dest[5] = mainTransTable[(((unsigned int)source[5])<<8) + dest[5]]; if (source[6]) dest[6] = mainTransTable[(((unsigned int)source[6])<<8) + dest[6]]; if (source[7]) dest[7] = mainTransTable[(((unsigned int)source[7])<<8) + dest[7]]; source += 128; dest += vid.conrowbytes; } break; } } else /* r_pixbytes == 2 */ { // FIXME: pre-expand to native format? unsigned short *dest = (unsigned short *) ((byte *)vid.buffer + y*vid.rowbytes + (x<<1)); // FIXME: transparency bits are missing while (height--) { if (source[0]) dest[0] = d_8to16table[source[0]]; if (source[1]) dest[1] = d_8to16table[source[1]]; if (source[2]) dest[2] = d_8to16table[source[2]]; if (source[3]) dest[3] = d_8to16table[source[3]]; if (source[4]) dest[4] = d_8to16table[source[4]]; if (source[5]) dest[5] = d_8to16table[source[5]]; if (source[6]) dest[6] = d_8to16table[source[6]]; if (source[7]) dest[7] = d_8to16table[source[7]]; source += 128; dest += vid.conrowbytes / 2; } } } //========================================================================== // // Draw_SmallString // //========================================================================== void Draw_SmallString (int x, int y, const char *str) { while (*str) { Draw_SmallCharacter(x, y, *str); str++; x += 6; } } //========================================================================== // // Draw_BigCharacter // // Callback for M_DrawBigCharacter() of menu.c // //========================================================================== void Draw_BigCharacter (int x, int y, int num) { qpic_t *p; int ypos, xpos; byte *dest; byte *source; p = Draw_CachePic ("gfx/menu/bigfont.lmp"); source = p->data + ((num % 8) * 20) + (num / 8 * p->width * 20); // FIXME: only for r_pixbytes == 1 for (ypos = 0; ypos < 19; ypos++) { dest = vid.buffer + (y + ypos) * vid.rowbytes + x; for (xpos = 0; xpos < 19; xpos++, dest++, source++) { if (*source) { *dest = *source; } } source += (p->width - 19); } } /* ============= Draw_Pic ============= */ void Draw_Pic (int x, int y, qpic_t *pic) { byte *source; int v, u; if ((x < 0) || (x + pic->width > vid.width) || (y < 0) || (y + pic->height > vid.height)) { Sys_Error ("%s: bad coordinates", __thisfunc__); } source = pic->data; if (r_pixbytes == 1) { byte *dest = vid.buffer + y * vid.rowbytes + x; for (v = 0; v < pic->height; v++) { memcpy (dest, source, pic->width); dest += vid.rowbytes; source += pic->width; } } else /* r_pixbytes == 2 */ { // FIXME: pretranslate at load time? unsigned short *dest = (unsigned short *)vid.buffer + y * (vid.rowbytes / 2) + x; // FIXME: transparency bits are missing for (v = 0; v < pic->height; v++) { for (u = 0; u < pic->width; u++) { dest[u] = d_8to16table[source[u]]; } dest += vid.rowbytes / 2; source += pic->width; } } } //========================================================================== // // Draw_PicCropped // // Draws a qpic_t that is clipped at the bottom/top edges of the screen. // //========================================================================== void Draw_PicCropped (int x, int y, qpic_t *pic) { byte *source; int v, u, height; if ((x < 0) || (x+pic->width > (int)vid.width)) { Sys_Error("%s: bad coordinates", __thisfunc__); } if (y >= vid.height || y+pic->height < 0) { // Totally off screen return; } if (y+pic->height > vid.height) { height = vid.height-y; } else if (y < 0) { height = pic->height+y; } else { height = pic->height; } source = pic->data; if (y < 0) { source += (pic->width * (-y)); y = 0; } if (r_pixbytes == 1) { byte *dest = vid.buffer + y*vid.rowbytes + x; switch (trans_level) { case 0: for (v = 0; v < height; v++) { memcpy(dest, source, pic->width); dest += vid.rowbytes; source += pic->width; } break; case 1: for (v = 0; v < height; v++) { for (u = 0; u < pic->width; u++, source++) { dest[u] = mainTransTable[(((unsigned int)dest[u])<<8) + (*source)]; } dest += vid.rowbytes; } break; case 2: for (v = 0; v < height; v++) { for (u = 0; u < pic->width; u++, source++) { dest[u] = mainTransTable[(((unsigned int)(*source))<<8) + dest[u]]; } dest += vid.rowbytes; } break; } } else /* r_pixbytes == 2 */ { // FIXME: pretranslate at load time? unsigned short *dest = (unsigned short *)vid.buffer + y * (vid.rowbytes / 2) + x; // FIXME: transparency bits are missing for (v = 0; v < height; v++) { for (u = 0; u < pic->width; u++) { dest[u] = d_8to16table[source[u]]; } dest += vid.rowbytes / 2; source += pic->width; } } } /* ============= Draw_TransPic ============= */ void Draw_TransPic (int x, int y, qpic_t *pic) { byte *source, tbyte; int v, u; if (x < 0 || (x + pic->width) > vid.width || y < 0 || (y + pic->height) > vid.height) { Sys_Error("%s: bad coordinates", __thisfunc__); } source = pic->data; if (r_pixbytes == 1) { byte *dest = vid.buffer + y * vid.rowbytes + x; if (pic->width & 7) { // general for (v = 0; v < pic->height; v++) { for (u = 0; u < pic->width; u++) { if ( (tbyte=source[u]) != TRANSPARENT_COLOR) dest[u] = tbyte; } dest += vid.rowbytes; source += pic->width; } } else { // unwound for (v = 0; v < pic->height; v++) { for (u = 0; u < pic->width; u += 8) { if ( (tbyte=source[u]) != TRANSPARENT_COLOR) dest[u] = tbyte; if ( (tbyte=source[u+1]) != TRANSPARENT_COLOR) dest[u+1] = tbyte; if ( (tbyte=source[u+2]) != TRANSPARENT_COLOR) dest[u+2] = tbyte; if ( (tbyte=source[u+3]) != TRANSPARENT_COLOR) dest[u+3] = tbyte; if ( (tbyte=source[u+4]) != TRANSPARENT_COLOR) dest[u+4] = tbyte; if ( (tbyte=source[u+5]) != TRANSPARENT_COLOR) dest[u+5] = tbyte; if ( (tbyte=source[u+6]) != TRANSPARENT_COLOR) dest[u+6] = tbyte; if ( (tbyte=source[u+7]) != TRANSPARENT_COLOR) dest[u+7] = tbyte; } dest += vid.rowbytes; source += pic->width; } } } else /* r_pixbytes == 2 */ { // FIXME: pretranslate at load time? unsigned short *dest = (unsigned short *)vid.buffer + y * (vid.rowbytes / 2) + x; // FIXME: transparency bits are missing for (v = 0; v < pic->height; v++) { for (u = 0; u < pic->width; u++) { tbyte = source[u]; if (tbyte != TRANSPARENT_COLOR) { dest[u] = d_8to16table[tbyte]; } } dest += vid.rowbytes / 2; source += pic->width; } } } //========================================================================== // // Draw_SubPicCropped // // Draws a qpic_t that is clipped at the bottom/top edges of the screen. // //========================================================================== void Draw_SubPicCropped (int x, int y, int h, qpic_t *pic) { byte *source; int v, u, height; if ((x < 0) || (x+pic->width > vid.width)) { Sys_Error("%s: bad coordinates", __thisfunc__); } if (y >= vid.height || y+h < 0) { // Totally off screen return; } if (y+pic->height > vid.height) { height = vid.height-y; } else if (y < 0) { height = pic->height+y; } else { height = pic->height; } if (height > h) { height = h; } source = pic->data; if (y < 0) { source += (pic->width * (-y)); y = 0; } if (r_pixbytes == 1) { byte *dest = vid.buffer + y * vid.rowbytes + x; switch (trans_level) { case 0: for (v = 0; v < height; v++) { memcpy(dest, source, pic->width); dest += vid.rowbytes; source += pic->width; } break; case 1: for (v = 0; v < height; v++) { for (u = 0; u < pic->width; u++, source++) { dest[u] = mainTransTable[(((unsigned int)dest[u])<<8) + (*source)]; } dest += vid.rowbytes; } break; case 2: for (v = 0; v < height; v++) { for (u = 0; u < pic->width; u++, source++) { dest[u] = mainTransTable[(((unsigned int)(*source))<<8) + dest[u]]; } dest += vid.rowbytes; } break; } } else /* r_pixbytes == 2 */ { // FIXME: pretranslate at load time? unsigned short *dest = (unsigned short *)vid.buffer + y * (vid.rowbytes / 2) + x; // FIXME: transparency bits are missing for (v = 0; v < height; v++) { for (u = 0; u < pic->width; u++) { dest[u] = d_8to16table[source[u]]; } dest += vid.rowbytes / 2; source += pic->width; } } } //========================================================================== // // Draw_TransPicCropped // // Draws a holey qpic_t that is clipped at the bottom edge of the screen. // //========================================================================== void Draw_TransPicCropped (int x, int y, qpic_t *pic) { byte *source, tbyte; int v, u, height; if ((x < 0) || (x+pic->width > vid.width)) { Sys_Error("%s: bad coordinates", __thisfunc__); } if (y >= vid.height || y+pic->height < 0) { // Totally off screen return; } if (y+pic->height > vid.height) { height = vid.height-y; } else if (y < 0) { height = pic->height+y; } else { height = pic->height; } source = pic->data; if (y < 0) { source += (pic->width * (-y)); y = 0; } if (r_pixbytes == 1) { byte *dest = vid.buffer + y * vid.rowbytes + x; if (pic->width & 7) { // General switch (trans_level) { case 0: for (v = 0; v < height; v++) { for (u = 0; u < pic->width; u++) { if ((tbyte = source[u]) != TRANSPARENT_COLOR) { dest[u] = tbyte; } } dest += vid.rowbytes; source += pic->width; } break; case 1: for (v = 0; v < height; v++) { for (u = 0; u < pic->width; u++) { if ((tbyte = source[u]) != TRANSPARENT_COLOR) { dest[u] = mainTransTable[(((unsigned int)dest[u])<<8) + tbyte]; } } dest += vid.rowbytes; source += pic->width; } break; case 2: for (v = 0; v < height; v++) { for (u = 0; u < pic->width; u++) { if ((tbyte = source[u]) != TRANSPARENT_COLOR) { dest[u] = mainTransTable[(((unsigned int)tbyte)<<8) + dest[u]]; } } dest += vid.rowbytes; source += pic->width; } break; } } else { // Unwound switch (trans_level) { case 0: for (v = 0; v < height; v++) { for (u = 0; u < pic->width; u += 8) { if ((tbyte = source[u]) != TRANSPARENT_COLOR) dest[u] = tbyte; if ((tbyte = source[u+1]) != TRANSPARENT_COLOR) dest[u+1] = tbyte; if ((tbyte = source[u+2]) != TRANSPARENT_COLOR) dest[u+2] = tbyte; if ((tbyte = source[u+3]) != TRANSPARENT_COLOR) dest[u+3] = tbyte; if ((tbyte = source[u+4]) != TRANSPARENT_COLOR) dest[u+4] = tbyte; if ((tbyte = source[u+5]) != TRANSPARENT_COLOR) dest[u+5] = tbyte; if ((tbyte = source[u+6]) != TRANSPARENT_COLOR) dest[u+6] = tbyte; if ((tbyte = source[u+7]) != TRANSPARENT_COLOR) dest[u+7] = tbyte; } dest += vid.rowbytes; source += pic->width; } break; case 1: for (v = 0; v < height; v++) { for (u = 0; u < pic->width; u += 8) { if ((tbyte = source[u]) != TRANSPARENT_COLOR) dest[u] = mainTransTable[(((unsigned int)dest[u])<<8) + tbyte]; if ((tbyte = source[u+1]) != TRANSPARENT_COLOR) dest[u+1] = mainTransTable[(((unsigned int)dest[u+1])<<8) + tbyte]; if ((tbyte = source[u+2]) != TRANSPARENT_COLOR) dest[u+2] = mainTransTable[(((unsigned int)dest[u+2])<<8) + tbyte]; if ((tbyte = source[u+3]) != TRANSPARENT_COLOR) dest[u+3] = mainTransTable[(((unsigned int)dest[u+3])<<8) + tbyte]; if ((tbyte = source[u+4]) != TRANSPARENT_COLOR) dest[u+4] = mainTransTable[(((unsigned int)dest[u+4])<<8) + tbyte]; if ((tbyte = source[u+5]) != TRANSPARENT_COLOR) dest[u+5] = mainTransTable[(((unsigned int)dest[u+5])<<8) + tbyte]; if ((tbyte = source[u+6]) != TRANSPARENT_COLOR) dest[u+6] = mainTransTable[(((unsigned int)dest[u+6])<<8) + tbyte]; if ((tbyte = source[u+7]) != TRANSPARENT_COLOR) dest[u+7] = mainTransTable[(((unsigned int)dest[u+7])<<8) + tbyte]; } dest += vid.rowbytes; source += pic->width; } break; case 2: for (v = 0; v < height; v++) { for (u = 0; u < pic->width; u += 8) { if ((tbyte = source[u]) != TRANSPARENT_COLOR) dest[u] = mainTransTable[(((unsigned int)tbyte)<<8) + dest[u]]; if ((tbyte = source[u+1]) != TRANSPARENT_COLOR) dest[u+1] = mainTransTable[(((unsigned int)tbyte)<<8) + dest[u+1]]; if ((tbyte = source[u+2]) != TRANSPARENT_COLOR) dest[u+2] = mainTransTable[(((unsigned int)tbyte)<<8) + dest[u+2]]; if ((tbyte = source[u+3]) != TRANSPARENT_COLOR) dest[u+3] = mainTransTable[(((unsigned int)tbyte)<<8) + dest[u+3]]; if ((tbyte = source[u+4]) != TRANSPARENT_COLOR) dest[u+4] = mainTransTable[(((unsigned int)tbyte)<<8) + dest[u+4]]; if ((tbyte = source[u+5]) != TRANSPARENT_COLOR) dest[u+5] = mainTransTable[(((unsigned int)tbyte)<<8) + dest[u+5]]; if ((tbyte = source[u+6]) != TRANSPARENT_COLOR) dest[u+6] = mainTransTable[(((unsigned int)tbyte)<<8) + dest[u+6]]; if ((tbyte = source[u+7]) != TRANSPARENT_COLOR) dest[u+7] = mainTransTable[(((unsigned int)tbyte)<<8) + dest[u+7]]; } dest += vid.rowbytes; source += pic->width; } break; } } } else /* r_pixbytes == 2 */ { // FIXME: pretranslate at load time? unsigned short *dest = (unsigned short *)vid.buffer + y * (vid.rowbytes / 2) + x; // FIXME: transparency bits are missing for (v = 0; v < height; v++) { for (u = 0; u < pic->width; u++) { tbyte = source[u]; if (tbyte != TRANSPARENT_COLOR) { dest[u] = d_8to16table[tbyte]; } } dest += vid.rowbytes / 2; source += pic->width; } } } /* ============= Draw_SubPic ============= */ void Draw_SubPic (int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height) { byte *source; int v, u; if ((x < 0) || (x + width > vid.width) || (y < 0) || (y + height > vid.height)) { Sys_Error ("%s: bad coordinates", __thisfunc__); } source = pic->data + srcy * pic->width + srcx; if (r_pixbytes == 1) { byte *dest = vid.buffer + y * vid.rowbytes + x; for (v = 0; v < height; v++) { memcpy (dest, source, width); dest += vid.rowbytes; source += pic->width; } } else /* r_pixbytes == 2 */ { // FIXME: pretranslate at load time? unsigned short *dest = (unsigned short *)vid.buffer + y * (vid.rowbytes / 2) + x; for (v = 0; v < height; v++) { for (u = srcx; u < (srcx+width); u++) { dest[u] = d_8to16table[source[u]]; } dest += vid.rowbytes / 2; source += pic->width; } } } /* ============= Draw_TransPicTranslate ============= */ void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation, int p_class) { byte *source, tbyte; int v, u; if (x < 0 || (x + pic->width) > vid.width || y < 0 || (y + pic->height) > vid.height) { Sys_Error ("%s: bad coordinates", __thisfunc__); } source = pic->data; if (r_pixbytes == 1) { byte *dest = vid.buffer + y * vid.rowbytes + x; if (pic->width & 7) { // general for (v = 0; v < pic->height; v++) { for (u = 0; u < pic->width; u++) { if ( (tbyte=source[u]) != TRANSPARENT_COLOR) dest[u] = translation[tbyte]; } dest += vid.rowbytes; source += pic->width; } } else { // unwound for (v = 0; v < pic->height; v++) { for (u = 0; u < pic->width; u += 8) { if ( (tbyte=source[u]) != TRANSPARENT_COLOR) dest[u] = translation[tbyte]; if ( (tbyte=source[u+1]) != TRANSPARENT_COLOR) dest[u+1] = translation[tbyte]; if ( (tbyte=source[u+2]) != TRANSPARENT_COLOR) dest[u+2] = translation[tbyte]; if ( (tbyte=source[u+3]) != TRANSPARENT_COLOR) dest[u+3] = translation[tbyte]; if ( (tbyte=source[u+4]) != TRANSPARENT_COLOR) dest[u+4] = translation[tbyte]; if ( (tbyte=source[u+5]) != TRANSPARENT_COLOR) dest[u+5] = translation[tbyte]; if ( (tbyte=source[u+6]) != TRANSPARENT_COLOR) dest[u+6] = translation[tbyte]; if ( (tbyte=source[u+7]) != TRANSPARENT_COLOR) dest[u+7] = translation[tbyte]; } dest += vid.rowbytes; source += pic->width; } } } else /* r_pixbytes == 2 */ { // FIXME: pretranslate at load time? unsigned short *dest = (unsigned short *)vid.buffer + y * (vid.rowbytes / 2) + x; // FIXME: transparency bits are missing for (v = 0; v < pic->height; v++) { for (u = 0; u < pic->width; u++) { tbyte = source[u]; if (tbyte != TRANSPARENT_COLOR) { dest[u] = d_8to16table[tbyte]; } } dest += vid.rowbytes / 2; source += pic->width; } } } /* ================ Draw_ConsoleBackground ================ */ static void Draw_ConsoleVersionInfo (int lines) { static const char ver[] = ENGINE_WATERMARK; const char *ptr = ver; int x = vid.conwidth - (strlen(ver) * 8 + 11); int y = lines - 14; for (; *ptr; ++ptr) Draw_Character (x + (int)(ptr - ver) * 8, y, *ptr | 0x100); } void Draw_ConsoleBackground (int lines) { int x, y, v; byte *src; int f, fstep; qpic_t *conback; conback = Draw_CachePic ("gfx/menu/conback.lmp"); /* Since the status bar or deahmatch overlay will never be drawn * at the same as the console background we can freely play with * trans_level here. */ if (!con_forcedup) { trans_level = scr_contrans.integer; if (trans_level < 0 || trans_level > 2) trans_level = 0; } if (r_pixbytes == 1) { byte *dest = vid.conbuffer; fstep = conback->width * 0x10000 / vid.conwidth; for (y = 0; y < lines; y++, dest += vid.conrowbytes) { v = (vid.conheight - lines + y)* conback->height / vid.conheight; src = conback->data + v * conback->width; if (vid.conwidth == conback->width && trans_level == 0) { memcpy (dest, src, vid.conwidth); continue; } f = 0; switch (trans_level) { case 0: for (x = 0; x < (int)vid.conwidth; x += 4) { dest[x] = src[f>>16]; f += fstep; dest[x+1] = src[f>>16]; f += fstep; dest[x+2] = src[f>>16]; f += fstep; dest[x+3] = src[f>>16]; f += fstep; } break; case 1: for (x = 0; x < (int)vid.conwidth; x += 4) { dest[x] = mainTransTable[(((unsigned int)dest[x])<<8) + src[f>>16]]; f += fstep; dest[x+1] = mainTransTable[(((unsigned int)dest[x+1])<<8) + src[f>>16]]; f += fstep; dest[x+2] = mainTransTable[(((unsigned int)dest[x+2])<<8) + src[f>>16]]; f += fstep; dest[x+3] = mainTransTable[(((unsigned int)dest[x+3])<<8) + src[f>>16]]; f += fstep; } break; case 2: for (x = 0; x < (int)vid.conwidth; x += 4) { dest[x] = mainTransTable[(((unsigned int)src[f>>16])<<8) + dest[x]]; f += fstep; dest[x+1] = mainTransTable[(((unsigned int)src[f>>16])<<8) + dest[x+1]]; f += fstep; dest[x+2] = mainTransTable[(((unsigned int)src[f>>16])<<8) + dest[x+2]]; f += fstep; dest[x+3] = mainTransTable[(((unsigned int)src[f>>16])<<8) + dest[x+3]]; f += fstep; } break; } } } else /* r_pixbytes == 2 */ { unsigned short *dest = (unsigned short *)vid.conbuffer; fstep = conback->width * 0x10000 / vid.conwidth; for (y = 0; y < lines; y++, dest += (vid.conrowbytes / 2)) { // FIXME: pre-expand to native format? // FIXME: does the endian switching go away in production? v = (vid.conheight - lines + y) * conback->height / vid.conheight; src = conback->data + v * conback->width; f = 0; // FIXME: transparency bits are missing for (x = 0; x < (int)vid.conwidth; x += 4) { dest[x] = d_8to16table[src[f>>16]]; f += fstep; dest[x+1] = d_8to16table[src[f>>16]]; f += fstep; dest[x+2] = d_8to16table[src[f>>16]]; f += fstep; dest[x+3] = d_8to16table[src[f>>16]]; f += fstep; } } } trans_level = 0; #if defined(H2W) if (cls.download) return; #endif Draw_ConsoleVersionInfo (lines); } /* ============== R_DrawRect8 ============== */ void R_DrawRect8 (vrect_t *prect, int rowbytes, byte *psrc, int transparent) { byte t; int i, j, srcdelta, destdelta; byte *pdest; pdest = vid.buffer + (prect->y * vid.rowbytes) + prect->x; srcdelta = rowbytes - prect->width; destdelta = vid.rowbytes - prect->width; if (transparent) { for (i = 0; i < prect->height; i++) { for (j = 0; j < prect->width; j++) { t = *psrc; if (t != TRANSPARENT_COLOR) { *pdest = t; } psrc++; pdest++; } psrc += srcdelta; pdest += destdelta; } } else { for (i = 0; i < prect->height; i++) { memcpy (pdest, psrc, prect->width); psrc += rowbytes; pdest += vid.rowbytes; } } } /* ============== R_DrawRect16 ============== */ void R_DrawRect16 (vrect_t *prect, int rowbytes, byte *psrc, int transparent) { byte t; int i, j, srcdelta, destdelta; unsigned short *pdest; // FIXME: would it be better to pre-expand native-format versions? pdest = (unsigned short *)vid.buffer + (prect->y * (vid.rowbytes / 2)) + prect->x; srcdelta = rowbytes - prect->width; destdelta = (vid.rowbytes / 2) - prect->width; if (transparent) { for (i = 0; i < prect->height; i++) { for (j = 0; j < prect->width; j++) { t = *psrc; if (t != TRANSPARENT_COLOR) { *pdest = d_8to16table[t]; } psrc++; pdest++; } psrc += srcdelta; pdest += destdelta; } } else { for (i = 0; i < prect->height; i++) { for (j = 0; j < prect->width; j++) { *pdest = d_8to16table[*psrc]; psrc++; pdest++; } psrc += srcdelta; pdest += destdelta; } } } /* ============= Draw_TileClear This repeats a 64*64 tile graphic to fill the screen around a sized down refresh window. ============= */ void Draw_TileClear (int x, int y, int w, int h) { int width, height, tileoffsetx, tileoffsety; byte *psrc; vrect_t vr; r_rectdesc.rect.x = x; r_rectdesc.rect.y = y; r_rectdesc.rect.width = w; r_rectdesc.rect.height = h; vr.y = r_rectdesc.rect.y; height = r_rectdesc.rect.height; tileoffsety = vr.y % r_rectdesc.height; while (height > 0) { vr.x = r_rectdesc.rect.x; width = r_rectdesc.rect.width; if (tileoffsety != 0) vr.height = r_rectdesc.height - tileoffsety; else vr.height = r_rectdesc.height; if (vr.height > height) vr.height = height; tileoffsetx = vr.x % r_rectdesc.width; while (width > 0) { if (tileoffsetx != 0) vr.width = r_rectdesc.width - tileoffsetx; else vr.width = r_rectdesc.width; if (vr.width > width) vr.width = width; psrc = r_rectdesc.ptexbytes + (tileoffsety * r_rectdesc.rowbytes) + tileoffsetx; if (r_pixbytes == 1) { R_DrawRect8 (&vr, r_rectdesc.rowbytes, psrc, 0); } else { R_DrawRect16 (&vr, r_rectdesc.rowbytes, psrc, 0); } vr.x += vr.width; width -= vr.width; tileoffsetx = 0; // only the left tile can be left-clipped } vr.y += vr.height; height -= vr.height; tileoffsety = 0; // only the top tile can be top-clipped } } /* ============= Draw_Fill Fills a box of pixels with a single color ============= */ void Draw_Fill (int x, int y, int w, int h, int c) { int u, v; if (x < 0 || x + w > vid.width || y < 0 || y + h > vid.height) { Con_Printf("Bad Draw_Fill(%d, %d, %d, %d, %c)\n", x, y, w, h, c); return; } if (r_pixbytes == 1) { byte *dest = vid.buffer + y*vid.rowbytes + x; switch (trans_level) { case 0: for (v = 0; v < h; v++, dest += vid.rowbytes) { for (u = 0; u < w; u++) { dest[u] = c; } } break; case 1: for (v = 0; v < h; v++, dest += vid.rowbytes) { for (u = 0; u < w; u++) { dest[u] = mainTransTable[(((unsigned int)dest[u])<<8) + c]; } } break; case 2: for (v = 0; v < h; v++, dest += vid.rowbytes) { for (u = 0; u < w; u++) { dest[u] = mainTransTable[(c<<8) + dest[u]]; } } break; } } else /* r_pixbytes == 2 */ { unsigned short *dest = (unsigned short *)vid.buffer + y * (vid.rowbytes / 2) + x; unsigned int uc = d_8to16table[c]; // FIXME: transparency bits are missing for (v = 0; v < h; v++, dest += (vid.rowbytes / 2)) { for (u = 0; u < w; u++) dest[u] = uc; } } } //============================================================================= /* ================ Draw_FadeScreen ================ */ void Draw_FadeScreen (void) { int x, y; byte *pbuf; int temp[2048], *pos; VID_UnlockBuffer (); S_ExtraUpdate (); VID_LockBuffer (); for (x = 0; x < 2048; x++) temp[x] = (164 + rand() % 6) * 256; for (y = 0; y < vid.height; y++) { pbuf = (byte *)(vid.buffer + vid.rowbytes*y); pos = &temp[rand() % 256]; if ((y & 127) == 127) { VID_UnlockBuffer (); S_ExtraUpdate (); VID_LockBuffer (); } for (x = 0; x < vid.width; x++, pbuf++) { // if ((x & 3) != t) // pbuf[x] = 0; *pbuf = mainTransTable[(*pos)+(*pbuf)]; pos++; // pbuf[x] = mainTransTable[((170+(rand() % 6))*256)+pbuf[x]]; // pbuf[x] = mainTransTable[159 + (256*pbuf[x])]; } } VID_UnlockBuffer (); S_ExtraUpdate (); VID_LockBuffer (); } //============================================================================= #if defined(DRAW_LOADINGSKULL) /* ================ Draw_BeginDisc Draws the little blue disc in the corner of the screen. Call before beginning any disc IO. ================ */ void Draw_BeginDisc (void) { static int disc_idx = 0; #ifndef H2W if (loading_stage) return; #endif if (!draw_disc[disc_idx]) return; if (++disc_idx >= MAX_DISC) disc_idx = 0; D_BeginDirectRect (vid.width - 28, 0, draw_disc[disc_idx]->data, 28, 24); scr_topupdate = 0; // this was disabled by rjr in the hw source for what reason? } /* ================ Draw_EndDisc Erases the disc icon. Call after completing any disc IO ================ */ void Draw_EndDisc (void) { /* this is causing problems, at least with mgl-win32. the sdl driver seems to work fine either way. the problem should be some kind of a bad interaction with D_ShowLoadingSize(), due to clashes in direct access usage I think: didn't look at it carefully, yet. */ #if 0 if (!draw_disc[0]) return; D_EndDirectRect (vid.width - 28, 0, 28, 24); scr_topupdate = 0; // this was disabled by rjr in the hw source for what reason? #endif } #endif /* DRAW_LOADINGSKULL */ engine/h2shared/draw.h000066400000000000000000000062451444734033100151430ustar00rootroot00000000000000/* * draw.h * these are the only functions outside the refresh * allowed to touch the vid buffer * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_DRAW_H #define __HX2_DRAW_H #define MAX_DISC 18 extern qboolean draw_reinit; void Draw_Init (void); void Draw_ReInit (void); qpic_t *Draw_PicFromWad (const char *name); qpic_t *Draw_PicFromFile (const char *name); qpic_t *Draw_CachePic (const char *path); #if !defined(DRAW_PROGRESSBARS) qpic_t *Draw_CacheLoadingPic (void); /* without the progress bars. */ #else #define Draw_CacheLoadingPic () Draw_CachePic ("gfx/menu/loading.lmp") #endif /* DRAW_PROGRESSBARS */ void Draw_Pic (int x, int y, qpic_t *pic); void Draw_PicCropped (int x, int y, qpic_t *pic); void Draw_SubPic (int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height); void Draw_SubPicCropped (int x, int y, int h, qpic_t *pic); void Draw_TransPic (int x, int y, qpic_t *pic); void Draw_TransPicCropped (int x, int y, qpic_t *pic); void Draw_ConsoleBackground (int lines); void Draw_Crosshair (void); #if defined(GLQUAKE) void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha); #endif /* GLQUAKE */ void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation, int p_class); /* Only used for the player color selection menu */ #if FULLSCREEN_INTERMISSIONS # if defined(GLQUAKE) qpic_t *Draw_CachePicNoTrans (const char *path); void Draw_IntermissionPic (qpic_t *pic); # else /* !GLQUAKE */ qpic_t *Draw_CachePicResize (const char *path, int targetWidth, int targetHeight); # endif /* GLQUAKE */ #endif /* FULLSCREEN_INTERMISSIONS */ #if defined(GLQUAKE) #undef DRAW_LOADINGSKULL #endif #if !defined(DRAW_LOADINGSKULL) #define Draw_BeginDisc() #define Draw_EndDisc() #else void Draw_BeginDisc (void); void Draw_EndDisc (void); #endif void Draw_TileClear (int x, int y, int w, int h); void Draw_Fill (int x, int y, int w, int h, int c); void Draw_FadeScreen (void); void Draw_Character (int x, int y, unsigned int num); void Draw_String (int x, int y, const char *str); void Draw_SmallCharacter (int x, int y, int num); void Draw_SmallString (int x, int y, const char *str); void Draw_RedString (int x, int y, const char *str); void Draw_BigCharacter (int x, int y, int num); /* game/engine name to draw on the console */ #define GAME_MOD_NAME ENGINE_NAME #define ENGINE_WATERMARK GAME_MOD_NAME " " STRINGIFY(ENGINE_VERSION) " (" PLATFORM_STRING ")" #endif /* __HX2_DRAW_H */ engine/h2shared/dxe.c000066400000000000000000000130051444734033100147510ustar00rootroot00000000000000/* Dynamic module loading/unloading with DJGPP DXE3 * Copyright (C) 2015-2016 Q2DOS developers. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if 1 /* for mesa w/o 3dfx glide */ #include #include #endif #include "quakedef.h" #include "sys_dxe.h" DXE_EXPORT_TABLE (syms) /* dlfcn */ DXE_EXPORT (dlclose) DXE_EXPORT (dlopen) DXE_EXPORT (dlsym) /* assert */ DXE_EXPORT (__dj_assert) /* errno */ DXE_EXPORT (errno) /* setjmp */ DXE_EXPORT (longjmp) DXE_EXPORT (setjmp) /* signal */ DXE_EXPORT (signal) /* stdlib */ DXE_EXPORT (abort) DXE_EXPORT (exit) DXE_EXPORT (rand) DXE_EXPORT (srand) DXE_EXPORT (bsearch) DXE_EXPORT (atol) DXE_EXPORT (strtod) DXE_EXPORT (strtol) DXE_EXPORT (strtoul) DXE_EXPORT (qsort) DXE_EXPORT (getenv) DXE_EXPORT (putenv) DXE_EXPORT (malloc) DXE_EXPORT (calloc) DXE_EXPORT (realloc) DXE_EXPORT (free) /* string */ DXE_EXPORT (memcmp) DXE_EXPORT (memcpy) DXE_EXPORT (memset) DXE_EXPORT (memmove) DXE_EXPORT (strcat) DXE_EXPORT (strncat) DXE_EXPORT (memchr) DXE_EXPORT (strchr) DXE_EXPORT (strcmp) DXE_EXPORT (strcpy) DXE_EXPORT (strdup) DXE_EXPORT (strlen) DXE_EXPORT (strncmp) DXE_EXPORT (strncpy) DXE_EXPORT (strrchr) DXE_EXPORT (strstr) DXE_EXPORT (strcspn) DXE_EXPORT (strtok) #if 0 DXE_EXPORT (strtok_r) #endif DXE_EXPORT (strcasecmp) DXE_EXPORT (strncasecmp) DXE_EXPORT (stricmp) DXE_EXPORT (strnicmp) DXE_EXPORT (strlwr) DXE_EXPORT (strupr) DXE_EXPORT (stpcpy) /* stdio */ DXE_EXPORT (__dj_stderr) DXE_EXPORT (__dj_stdout) DXE_EXPORT (fopen) DXE_EXPORT (freopen) DXE_EXPORT (fclose) DXE_EXPORT (fflush) DXE_EXPORT (fread) DXE_EXPORT (fwrite) DXE_EXPORT (fseek) DXE_EXPORT (ftell) DXE_EXPORT (feof) DXE_EXPORT (getc) DXE_EXPORT (ungetc) DXE_EXPORT (fgetc) DXE_EXPORT (fgets) DXE_EXPORT (fputc) DXE_EXPORT (fputs) DXE_EXPORT (putc) DXE_EXPORT (putchar) DXE_EXPORT (puts) DXE_EXPORT (fprintf) DXE_EXPORT (printf) DXE_EXPORT (sprintf) DXE_EXPORT (vsprintf) DXE_EXPORT (vsnprintf) DXE_EXPORT (vfprintf) DXE_EXPORT (fscanf) DXE_EXPORT (sscanf) #if 0 /* dir */ DXE_EXPORT (findfirst) DXE_EXPORT (findnext) /* sys/stat */ DXE_EXPORT (mkdir) #endif /* unistd */ DXE_EXPORT (usleep) DXE_EXPORT (write) /* time */ DXE_EXPORT (clock) DXE_EXPORT (uclock) #if 0 DXE_EXPORT (time) DXE_EXPORT (gettimeofday) DXE_EXPORT (localtime) DXE_EXPORT (asctime) DXE_EXPORT (strftime) #endif /* ctype */ DXE_EXPORT (__dj_ctype_tolower) DXE_EXPORT (__dj_ctype_toupper) DXE_EXPORT (__dj_ctype_flags) DXE_EXPORT (tolower) DXE_EXPORT (toupper) /* math */ DXE_EXPORT (__dj_huge_val) DXE_EXPORT (acos) DXE_EXPORT (asin) DXE_EXPORT (atan) DXE_EXPORT (atan2) DXE_EXPORT (atof) DXE_EXPORT (atoi) DXE_EXPORT (ceil) DXE_EXPORT (sin) DXE_EXPORT (cos) DXE_EXPORT (tan) DXE_EXPORT (floor) DXE_EXPORT (sqrt) DXE_EXPORT (log) DXE_EXPORT (pow) DXE_EXPORT (exp) DXE_EXPORT (frexp) DXE_EXPORT (ldexp) /* crt0 */ DXE_EXPORT (_crt0_startup_flags) /* nearptr */ DXE_EXPORT (__djgpp_base_address) DXE_EXPORT (__djgpp_nearptr_enable) DXE_EXPORT (__djgpp_nearptr_disable) /* movedata */ DXE_EXPORT (dosmemput) DXE_EXPORT (movedata) /* dos */ DXE_EXPORT (enable) DXE_EXPORT (disable) DXE_EXPORT (int86) /* dpmi */ DXE_EXPORT (__dpmi_int) DXE_EXPORT (__dpmi_physical_address_mapping) DXE_EXPORT (__dpmi_free_physical_address_mapping) #if 1 /* for mesa w/o 3dfx glide */ DXE_EXPORT (__dpmi_allocate_ldt_descriptors) DXE_EXPORT (__dpmi_free_ldt_descriptor) DXE_EXPORT (__dpmi_get_segment_base_address) DXE_EXPORT (__dpmi_set_segment_base_address) DXE_EXPORT (__dpmi_set_segment_limit) /* stubinfo.h, exceptn.h */ DXE_EXPORT (_stubinfo) DXE_EXPORT (__djgpp_dos_sel) #endif DXE_EXPORT_END static int num_unres; static int dxe_fail () { return 0; } static void *dxe_res (const char *sym) { FILE *f = fopen ("dxe.log", "a"); fprintf (f, "%s: unresolved symbol.\n", sym); fflush (f); fclose (f); ++num_unres; return (void *)dxe_fail; } void Sys_InitDXE3 (void) { remove ("dxe.log"); /* Register the symbols exported into dynamic modules */ dlregsym (syms); } void *Sys_dlopen (const char *filename, qboolean globalmode) { void *lib; _dlsymresolver = dxe_res; num_unres = 0; lib = dlopen (filename, (globalmode)? RTLD_GLOBAL : 0); _dlsymresolver = NULL; if (num_unres) Sys_Error ("Unresolved symbol(s) in %s. See DXE.LOG for details.", filename); return lib; } void *Sys_dlsym (void *handle, const char *symbol) { return dlsym (handle, symbol); } int Sys_dlclose (void *handle) { return dlclose (handle); } engine/h2shared/fx_gamma.c000066400000000000000000000155711444734033100157620ustar00rootroot00000000000000/* fx_gamma.c * * Small library providing gamma control functions for 3Dfx Voodoo1/2 * cards by abusing the exposed glide symbols when using fxMesa. * * Author: O. Sezer License: GPL * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the accompanying COPYING file for more details. * * Compiling as a shared library: * gcc -O2 -fPIC -Wall -W fx_gamma.c -o lib3dfxgamma.so -shared * * How to use: * If you are linking to the opengl library at compile time (-lGL), * not much is necessary. If you dlopen() the opengl library, then * RTLD_GLOBAL flag is necessary so that the library's symbols would be * available to you: SDL_GL_LoadLibrary() is just fine in this regard. * In either case, if the gllib is an fxMesa library, then you will have * the necessary glide symbols exposed on you. Decide whether you have * a Voodoo1/2 and then use the functions here. * * Issues: * glSetDeviceGammaRamp3DFX works nicely with Voodoo2, but it crashes * Voodoo1. The ramp functions are added for completeness sake anyway. * do3dfxGammaCtrl works just fine for both Voodoo1 and Voodoo2. * Besides, the GammaRamp3DFX functions are only available for Glide3: * Glide2 users cannot benefit them, but the gamma control option is * available for both Glide2 and Glide3. Therefore employing the gamma * control option seems more beneficial. * * Revision history: * v0.0.1, 2005-06-04: Initial version, do3dfxGammaCtrl works fine, * glGetDeviceGammaRamp3DFX & co need more care * v0.0.2, 2005-06-13: tried following the exact win32 versions for * glGetDeviceGammaRamp3DFX/glSetDeviceGammaRamp3DFX * v0.0.3, 2005-12-05: Updated documentation about the RTLD_GLOBAL flag. * v0.0.4, 2006-03-16: Fixed incorrect prototype for the grGet function * (it takes a signed int param*, not unsigned). * Also renamed FX_Get to FX_GetInteger to be more * explicit. * v0.0.5, 2013-07-24: Several cleanups/tidy-ups. * v0.1.0, 2015-09-18: Use dlsym(RTLD_DEFAULT,symname) instead of using * the handle returned by dlopen(NULL,mode). * Handle dlsym() implementations needing a leading * underscore to function names, e.g. DJGPP. * v0.1.1, 2015-10-02 Detect V1 / Rush and reject to work: they freeze. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #if 0 #include #else #define FX_CALL /*__stdcall*/ #define GR_HARDWARE 0xa1 #define GR_GAMMA_TABLE_ENTRIES 0x05 typedef signed int FxI32; typedef unsigned int FxU32; typedef float FxFloat; #endif #include "fx_gamma.h" #ifdef __DJGPP__ #define DLSYM_NEEDS_UNDERSCORE #endif #ifdef DLSYM_NEEDS_UNDERSCORE #define DLSYM_NAME(symname) "_"#symname #else #define DLSYM_NAME(symname) #symname #endif /**********************************************************************/ /** PRIVATE STUFF **/ static const char * (FX_CALL *grGetString_fp)(FxU32); /* 3dfx glide2 func for gamma correction */ static void (FX_CALL *grGammaCorrectionValue_fp)(FxFloat) = NULL; /* 3dfx glide3 func for gamma correction */ static void (FX_CALL *guGammaCorrectionRGB_fp)(FxFloat, FxFloat, FxFloat) = NULL; /* 3dfx glide3 funcs to make a replacement wglSetDeviceGammaRamp3DFX */ static FxU32 (FX_CALL *grGet_fp)(FxU32, FxU32, FxI32*) = NULL; static void (FX_CALL *grLoadGammaTable_fp)(FxU32, FxU32*, FxU32*, FxU32*) = NULL; /**********************************************************************/ static int check_v1 (void) { const char *hw; if (!grGetString_fp) { if ((grGetString_fp = (const char * (*) FX_CALL (FxU32)) dlsym(RTLD_DEFAULT, DLSYM_NAME(grGetString))) == NULL) return -1; } hw = grGetString_fp(GR_HARDWARE); if (!strcmp(hw,"Voodoo Graphics") || !strcmp(hw,"Voodoo Rush")) return -1; /* V1 and Rush freeze with this hack. */ return 0; /* others seem to work */ } /* * Init_3dfxGammaCtrl * Sends 0 for failure, 2 for glide2 or 3 for glide3 api. */ int Init_3dfxGammaCtrl (void) { if (grGammaCorrectionValue_fp != NULL) return 2; /* already have glide2x proc address */ if (guGammaCorrectionRGB_fp != NULL) return 3; /* already have glide3x proc address */ if (check_v1() < 0) return 0; if ((grGammaCorrectionValue_fp = (void (*) (FxFloat)) dlsym(RTLD_DEFAULT, DLSYM_NAME(grGammaCorrectionValue))) != NULL) return 2;/* glide2x */ else if ((guGammaCorrectionRGB_fp = (void (*) (FxFloat, FxFloat, FxFloat)) dlsym(RTLD_DEFAULT, DLSYM_NAME(guGammaCorrectionRGB))) != NULL) return 3;/* glide3x */ else return 0; } void Shutdown_3dfxGamma (void) { grGetString_fp = NULL; grGammaCorrectionValue_fp = NULL; guGammaCorrectionRGB_fp = NULL; grGet_fp = NULL; grLoadGammaTable_fp = NULL; } /* * do3dfxGammaCtrl */ int do3dfxGammaCtrl (float value) { if (grGammaCorrectionValue_fp) /* glide2x */ { grGammaCorrectionValue_fp (value); return 1; } if (guGammaCorrectionRGB_fp) /* glide3x */ { guGammaCorrectionRGB_fp (value, value, value); return 1; } return 0; } /**********************************************************************/ static int Check_3DfxGammaRamp (void) { if (grLoadGammaTable_fp != NULL && grGet_fp != NULL) return 1; if (check_v1() < 0) return 0; grGet_fp = (FxU32 (*) (FxU32, FxU32, FxI32 *)) dlsym(RTLD_DEFAULT, DLSYM_NAME(grGet)); grLoadGammaTable_fp = (void (*) (FxU32, FxU32 *, FxU32 *, FxU32 *)) dlsym(RTLD_DEFAULT, DLSYM_NAME(grLoadGammaTable)); if (grLoadGammaTable_fp != NULL && grGet_fp != NULL) return 1; return 0; } /* * glSetDeviceGammaRamp3DFX is adapted from Mesa-6.x * * glSetDeviceGammaRamp3DFX crashes Voodoo1, at least * currently, so it is not recommended yet. */ int glSetDeviceGammaRamp3DFX (void *arrays) { FxI32 tableSize = 0; FxI32 i, inc, idx; unsigned short *red, *green, *blue; FxU32 gammaTableR[256], gammaTableG[256], gammaTableB[256]; if (grLoadGammaTable_fp == NULL || grGet_fp == NULL) return 0; grGet_fp (GR_GAMMA_TABLE_ENTRIES, 4, &tableSize); if (tableSize <= 0) return 0; inc = 256 / tableSize; red = (unsigned short *)arrays; green = (unsigned short *)arrays + 256; blue = (unsigned short *)arrays + 512; for (i = 0, idx = 0; i < tableSize; i++, idx += inc) { gammaTableR[i] = red[idx] >> 8; gammaTableG[i] = green[idx] >> 8; gammaTableB[i] = blue[idx] >> 8; } grLoadGammaTable_fp(tableSize, gammaTableR, gammaTableG, gammaTableB); return 1; } /* * glGetDeviceGammaRamp3DFX * Sends a 1.0 gamma table. Also to be used for querying the lib. */ int glGetDeviceGammaRamp3DFX (void *arrays) { int i; unsigned short gammaTable[3][256]; if (grLoadGammaTable_fp == NULL || grGet_fp == NULL) { if (Check_3DfxGammaRamp() == 0) return 0; } for (i = 0; i < 256; i++) { gammaTable[0][i] = i << 8; gammaTable[1][i] = i << 8; gammaTable[2][i] = i << 8; } memcpy (arrays, gammaTable, 3 * 256 * sizeof(unsigned short)); return 1; } engine/h2shared/fx_gamma.h000066400000000000000000000022271444734033100157610ustar00rootroot00000000000000/* fx_gamma.h * * Small library providing gamma control functions for 3Dfx Voodoo1/2 * cards by abusing the exposed glide symbols when using fxMesa. * Author: O. Sezer License: GPL * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the accompanying COPYING file for more details. */ #ifndef __FXGAMMA_H #define __FXGAMMA_H #ifdef __cplusplus extern "C" { #endif extern void Shutdown_3dfxGamma (void); extern int Init_3dfxGammaCtrl (void); /* returns 0 for failure, non-zero for success (2 glide2 or 3 for glide3 api.) */ extern int do3dfxGammaCtrl (float/* gamma value*/); /* gamma correction proc (1.0 ... 0.333). returns 0 for failure, 1 for success. */ extern int glSetDeviceGammaRamp3DFX (void *arrays); /* returns 0 for failure, 1 for success. */ extern int glGetDeviceGammaRamp3DFX (void *arrays); /* sends an identity (1.0) gamma table. use for querying the lib. returns 0 for failure, 1 for success. */ #ifdef __cplusplus } #endif #endif /* __FXGAMMA_H */ engine/h2shared/gl_dos.h000066400000000000000000000023371444734033100154530ustar00rootroot00000000000000/* glimp_dos.h: header file for DOS-specific OpenGL video stuff * Copyright (C) 2015 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef GLIMP_DOS_H #define GLIMP_DOS_H extern int (*DOSGL_InitCtx ) (int *width, int *height, int *bpp); extern void (*DOSGL_Shutdown) (void); extern void (*DOSGL_EndFrame) (void); extern void * (*DOSGL_GetProcAddress) (const char *); extern const char * (*DOSGL_APIName) (void); int DMESA_LoadAPI (void *handle); int SAGE_LoadAPI (void *handle); int FXMESA_LoadAPI (void *handle); #endif engine/h2shared/gl_draw.c000066400000000000000000001327111444734033100156160ustar00rootroot00000000000000/* gl_draw.c * this is the only file outside the refresh that touches the vid buffer * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "hashindex.h" #if ENDIAN_RUNTIME_DETECT /* initialized by VID_Init() */ unsigned int MASK_r; unsigned int MASK_g; unsigned int MASK_b; unsigned int MASK_a; unsigned int MASK_rgb; unsigned int SHIFT_r; unsigned int SHIFT_g; unsigned int SHIFT_b; unsigned int SHIFT_a; #endif qboolean draw_reinit = false; static cvar_t gl_picmip = {"gl_picmip", "0", CVAR_NONE}; static cvar_t gl_constretch = {"gl_constretch", "0", CVAR_ARCHIVE}; static cvar_t gl_texturemode = {"gl_texturemode", "", CVAR_ARCHIVE}; static cvar_t gl_texture_anisotropy = {"gl_texture_anisotropy", "1", CVAR_ARCHIVE}; static GLuint menuplyr_textures[MAX_PLAYER_CLASS]; // player textures in multiplayer config screens static GLuint draw_backtile; static GLuint conback; static GLuint char_texture; static GLuint cs_texture; // crosshair texture static GLuint char_smalltexture; static GLuint char_menufonttexture; // Crosshair texture is a 32x32 alpha map with 8 levels of alpha. // The format is similar to an X11 pixmap, but not the same. // 7 is 100% solid, 0 and any other characters are transparent. static const char *cs_data = { /* This is actually the QuakeWorld crosshair which Raven didn't bother changing. It is possible to make class-based crosshairs. */ "................................" "................................" "..............7777.............." "..............7777.............." "..............7777.............." "..............7777.............." "................................" "................................" "................................" "................................" "..............7777.............." "..............7777.............." "..............7777.............." "..............7777.............." "..7777....7777....7777....7777.." "..7777....7777....7777....7777.." "..7777....7777....7777....7777.." "..7777....7777....7777....7777.." "..............7777.............." "..............7777.............." "..............7777.............." "..............7777.............." "................................" "................................" "................................" "................................" "..............7777.............." "..............7777.............." "..............7777.............." "..............7777.............." "................................" "................................" }; int gl_filter_idx = 4; /* Bilinear */ gltexture_t gltextures[MAX_GLTEXTURES]; int numgltextures; hashindex_t hash_gltextures; static GLuint GL_LoadPixmap (const char *name, const char *data); static void GL_Upload32 (unsigned int *data, gltexture_t *glt); static void GL_Upload8 (byte *data, gltexture_t *glt); //============================================================================= /* Support Routines */ cachepic_t menu_cachepics[MAX_CACHED_PICS]; int menu_numcachepics; hashindex_t hash_cachepics; /* * Geometry for the player/skin selection screen image. */ #define PLAYER_PIC_WIDTH 68 #define PLAYER_PIC_HEIGHT 114 #define PLAYER_DEST_WIDTH 128 #define PLAYER_DEST_HEIGHT 128 static byte menuplyr_pixels[MAX_PLAYER_CLASS][PLAYER_PIC_WIDTH*PLAYER_PIC_HEIGHT]; static void Draw_PicCheckError (void *ptr, const char *name) { if (!ptr) Sys_Error ("Failed to load %s", name); } qpic_t *Draw_PicFromFile (const char *name) { qpic_t *p; glpic_t gl; p = (qpic_t *)FS_LoadHunkFile (name, NULL); if (!p) return NULL; SwapPic (p); gl.texnum = GL_LoadPicTexture (p); gl.sl = 0; gl.sh = 1; gl.tl = 0; gl.th = 1; memcpy (p->data, &gl, sizeof(glpic_t)); return p; } qpic_t *Draw_PicFromWad (const char *name) { qpic_t *p; glpic_t gl; p = (qpic_t *) W_GetLumpName (name); gl.texnum = GL_LoadPicTexture (p); gl.sl = 0; gl.sh = 1; gl.tl = 0; gl.th = 1; memcpy (p->data, &gl, sizeof(glpic_t)); return p; } /* ================ Draw_CachePic ================ */ qpic_t *Draw_CachePic (const char *path) { cachepic_t *pic; int i, key; qpic_t *dat; glpic_t gl; key = Hash_GenerateKeyString (&hash_cachepics, path, true); for (i = Hash_First(&hash_cachepics, key); i != -1; i = Hash_Next(&hash_cachepics, i)) { pic = &menu_cachepics[i]; if (!strcmp (path, pic->name)) return &pic->pic; } if (menu_numcachepics == MAX_CACHED_PICS) Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); Hash_Add (&hash_cachepics, key, menu_numcachepics); pic = &menu_cachepics[menu_numcachepics]; menu_numcachepics++; q_strlcpy (pic->name, path, MAX_QPATH); // // load the pic from disk // dat = (qpic_t *)FS_LoadTempFile (path, NULL); Draw_PicCheckError (dat, path); SwapPic (dat); // HACK HACK HACK --- we need to keep the bytes for // the translatable player picture just for the menu // configuration dialog /* garymct */ if (!strcmp (path, "gfx/menu/netp1.lmp")) memcpy (menuplyr_pixels[0], dat->data, dat->width*dat->height); else if (!strcmp (path, "gfx/menu/netp2.lmp")) memcpy (menuplyr_pixels[1], dat->data, dat->width*dat->height); else if (!strcmp (path, "gfx/menu/netp3.lmp")) memcpy (menuplyr_pixels[2], dat->data, dat->width*dat->height); else if (!strcmp (path, "gfx/menu/netp4.lmp")) memcpy (menuplyr_pixels[3], dat->data, dat->width*dat->height); else if (!strcmp (path, "gfx/menu/netp5.lmp")) memcpy (menuplyr_pixels[4], dat->data, dat->width*dat->height); #if defined (H2W) else if (!strcmp (path, "gfx/menu/netp6.lmp")) memcpy (menuplyr_pixels[5], dat->data, dat->width*dat->height); #endif pic->pic.width = dat->width; pic->pic.height = dat->height; gl.texnum = GL_LoadPicTexture (dat); gl.sl = 0; gl.sh = 1; gl.tl = 0; gl.th = 1; memcpy (pic->pic.data, &gl, sizeof(glpic_t)); return &pic->pic; } #if !defined(DRAW_PROGRESSBARS) /* ================ Draw_CacheLoadingPic like Draw_CachePic() but only for loading.lmp with its progress bars eliminated. ================ */ static const char ls_path[] = "gfx/menu/loading.lmp"; qpic_t *Draw_CacheLoadingPic (void) { cachepic_t *pic; int i, key; qpic_t *dat; glpic_t gl; key = Hash_GenerateKeyString (&hash_cachepics, ls_path, true); for (i = Hash_First(&hash_cachepics, key); i != -1; i = Hash_Next(&hash_cachepics, i)) { pic = &menu_cachepics[i]; if (!strcmp (ls_path, pic->name)) return &pic->pic; } if (menu_numcachepics == MAX_CACHED_PICS) Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); dat = (qpic_t *)FS_LoadTempFile (ls_path, NULL); Draw_PicCheckError (dat, ls_path); SwapPic (dat); if (fs_filesize != 17592 || dat->width != 157 || dat->height != 112) return Draw_CachePic(ls_path); Hash_Add (&hash_cachepics, key, menu_numcachepics); pic = &menu_cachepics[menu_numcachepics]; q_strlcpy (pic->name, ls_path, MAX_QPATH); menu_numcachepics++; /* kill the progress slot pixels between rows [85:103] */ memmove(dat->data + 157*85, dat->data + 157*104, 157*(112 - 104)); dat->height -= (104 - 85); pic->pic.width = dat->width; pic->pic.height = dat->height; gl.texnum = GL_LoadPicTexture (dat); gl.sl = 0; gl.sh = 1; gl.tl = 0; gl.th = 1; memcpy (pic->pic.data, &gl, sizeof(glpic_t)); return &pic->pic; } #endif /* !DRAW_PROGRESSBARS */ glmode_t gl_texmodes[NUM_GL_FILTERS] = { { "GL_NEAREST", GL_NEAREST, GL_NEAREST }, /* point sampled */ { "GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST }, /* nearest, 1 mipmap */ { "GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST }, /* nearest, 2 mipmaps */ { "GL_LINEAR", GL_LINEAR, GL_LINEAR }, /* Bilinear, no mipmaps */ { "GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR }, /* Bilinear, 1 mipmap */ { "GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR } /* Trilinear: 2 mipmaps */ }; /* =============== Draw_TextureMode_f =============== */ static void Draw_TouchAllFilterModes (void) { gltexture_t *glt; int i; for (i = 0, glt = gltextures; i < numgltextures; i++, glt++) { if (glt->flags & (TEX_NEAREST|TEX_LINEAR)) /* TEX_MIPMAP mustn't be set in this case */ continue; GL_Bind (glt->texnum); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_texmodes[gl_filter_idx].maximize); if (glt->flags & TEX_MIPMAP) { glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_texmodes[gl_filter_idx].minimize); if (gl_max_anisotropy >= 2) glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_texture_anisotropy.value); } else glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_texmodes[gl_filter_idx].maximize); } } static void Draw_TextureMode_f (cvar_t *var) { int i; for (i = 0; i < NUM_GL_FILTERS; i++) { if (!strcmp (gl_texmodes[i].name, var->string)) { if (gl_filter_idx != i) { gl_filter_idx = i; // change all the existing mipmap texture objects Draw_TouchAllFilterModes (); } return; } } for (i = 0; i < NUM_GL_FILTERS; i++) { if (!q_strcasecmp (gl_texmodes[i].name, var->string)) { Cvar_SetQuick (var, gl_texmodes[i].name); return; } } Con_Printf ("bad filter name\n"); Cvar_SetQuick (var, gl_texmodes[gl_filter_idx].name); } static void Draw_TouchMipmapFilterModes (void) { gltexture_t *glt; int i; for (i = 0, glt = gltextures; i < numgltextures; i++, glt++) { if (glt->flags & TEX_MIPMAP) { GL_Bind (glt->texnum); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_texmodes[gl_filter_idx].maximize); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_texmodes[gl_filter_idx].minimize); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_texture_anisotropy.value); } } } static void Draw_Anisotropy_f (cvar_t *var) { if (var->value < 1) { Cvar_SetQuick (var, "1"); } else if (var->value > gl_max_anisotropy) { Cvar_SetValueQuick (var, gl_max_anisotropy); } else { if (gl_max_anisotropy >= 2) Draw_TouchMipmapFilterModes (); } } /* =============== Draw_ClearAllModels Callback for Draw_ReInit() and Draw_ReinitTextures(): Clear all models along with their textures. =============== */ static void Draw_ClearAllModels (void) { int temp = gl_purge_maptex.integer; flush_textures = true; Cvar_Set ("gl_purge_maptex", "1"); Mod_ClearAll (); Cvar_SetValue ("gl_purge_maptex", temp); } #if 0 /* =============== Draw_ReinitTextures Delete and reload all textures =============== */ static void Draw_ReinitTextures (void) { int temp; temp = scr_disabled_for_loading; scr_disabled_for_loading = true; draw_reinit = true; D_ClearOpenGLTextures(0); memset (lightmap_textures, 0, sizeof(lightmap_textures)); // make sure all of alias models are cleared if (cls.state != ca_active) Draw_ClearAllModels (); // Reload pre-map pics, fonts, console, etc W_LoadWadFile ("gfx.wad"); Draw_Init(); SCR_Init(); Sbar_Init(); // Reload the particle texture R_InitParticleTexture(); R_InitExtraTextures (); #ifdef H2W R_InitNetgraphTexture(); #endif // Reload the map's textures if (cls.state == ca_active) { Mod_ReloadTextures(); GL_BuildLightmaps(); } draw_reinit = false; scr_disabled_for_loading = temp; } #endif /* =============== Draw_ReInit Delete and reload textures that read during engine's init phase which may be changed by mods and purge all others, i.e. map/model textures. Should NEVER be called when a map is active: Only intended to be called upon a game directory change. =============== */ void Draw_ReInit (void) { int temp; temp = scr_disabled_for_loading; scr_disabled_for_loading = true; draw_reinit = true; D_ClearOpenGLTextures(0); memset (lightmap_textures, 0, sizeof(lightmap_textures)); // make sure all of alias models are cleared Draw_ClearAllModels (); // Reload pre-map pics, fonts, console, etc W_LoadWadFile ("gfx.wad"); Draw_Init(); SCR_Init(); Sbar_Init(); // Reload the particle texture R_InitParticleTexture(); R_InitExtraTextures (); #ifdef H2W R_InitNetgraphTexture(); #endif draw_reinit = false; scr_disabled_for_loading = temp; } /* =============== Draw_Init =============== */ void Draw_Init (void) { qpic_t *p; byte *chars; int i; if (!draw_reinit) { Cvar_RegisterVariable (&gl_picmip); Cvar_RegisterVariable (&gl_constretch); gl_texturemode.string = gl_texmodes[gl_filter_idx].name; Cvar_RegisterVariable (&gl_texturemode); Cvar_RegisterVariable (&gl_texture_anisotropy); Cvar_SetCallback (&gl_texturemode, Draw_TextureMode_f); Cvar_SetCallback (&gl_texture_anisotropy, Draw_Anisotropy_f); Hash_Allocate (&hash_cachepics, MAX_CACHED_PICS); Hash_Allocate (&hash_gltextures, MAX_GLTEXTURES); } // load the charset: 8*8 graphic characters chars = FS_LoadTempFile ("gfx/menu/conchars.lmp", NULL); Draw_PicCheckError (chars, "gfx/menu/conchars.lmp"); if (fs_filesize != 256*128) { Sys_Error ("gfx/menu/conchars.lmp: bad size."); } for (i = 0; i < 256*128; i++) { if (chars[i] == 0) chars[i] = 255; // proper transparent color } char_texture = GL_LoadTexture ("charset", chars, 256, 128, TEX_ALPHA|TEX_NEAREST); // load the small characters for status bar chars = (byte *) W_GetLumpName("tinyfont"); for (i = 0; i < 128*32; i++) { if (chars[i] == 0) chars[i] = 255; // proper transparent color } char_smalltexture = GL_LoadTexture ("smallcharset", chars, 128, 32, TEX_ALPHA|TEX_NEAREST); // load the big menu font // Note: old version of demo has bigfont.lmp, not bigfont2.lmp p = (qpic_t *)FS_LoadTempFile("gfx/menu/bigfont2.lmp", NULL); if (!p) p = (qpic_t *)FS_LoadTempFile("gfx/menu/bigfont.lmp", NULL); Draw_PicCheckError (p, "gfx/menu/bigfont2.lmp"); SwapPic (p); for (i = 0; i < p->width * p->height; i++) // MUST be 160 * 80 { if (p->data[i] == 0) p->data[i] = 255; // proper transparent color } char_menufonttexture = GL_LoadTexture ("menufont", p->data, p->width, p->height, TEX_ALPHA|TEX_LINEAR); // load the console background p = (qpic_t *)FS_LoadTempFile ("gfx/menu/conback.lmp", NULL); Draw_PicCheckError (p, "gfx/menu/conback.lmp"); SwapPic (p); conback = GL_LoadTexture ("conback", p->data, p->width, p->height, TEX_LINEAR); // load the backtile p = (qpic_t *)FS_LoadTempFile ("gfx/menu/backtile.lmp", NULL); Draw_PicCheckError (p, "gfx/menu/backtile.lmp"); SwapPic (p); draw_backtile = GL_LoadPicTexture (p); // load the crosshair texture cs_texture = GL_LoadPixmap ("crosshair", cs_data); // initialize the player texnums for multiplayer config screens glGenTextures_fp(MAX_PLAYER_CLASS, menuplyr_textures); } /* ================ Draw_Character Draws one 8*8 graphics character with 0 being transparent. It can be clipped to the top of the screen to allow the console to be smoothly scrolled off. ================ */ void Draw_Character (int x, int y, unsigned int num) { int row, col; float frow, fcol, xsize,ysize; num &= 511; if (num == 32) return; // space if (y <= -8) return; // totally off screen row = num >> 5; col = num & 31; xsize = 0.03125; ysize = 0.0625; fcol = col*xsize; frow = row*ysize; GL_Bind (char_texture); glBegin_fp (GL_QUADS); glTexCoord2f_fp (fcol, frow); glVertex2f_fp (x, y); glTexCoord2f_fp (fcol + xsize, frow); glVertex2f_fp (x+8, y); glTexCoord2f_fp (fcol + xsize, frow + ysize); glVertex2f_fp (x+8, y+8); glTexCoord2f_fp (fcol, frow + ysize); glVertex2f_fp (x, y+8); glEnd_fp (); } /* ================ Draw_String ================ */ void Draw_String (int x, int y, const char *str) { while (*str) { Draw_Character (x, y, *str); str++; x += 8; } } void Draw_RedString (int x, int y, const char *str) { while (*str) { Draw_Character (x, y, ((unsigned char)(*str))+256); str++; x += 8; } } /* ================ Draw_Crosshair ================ */ void Draw_Crosshair (void) { int x, y; unsigned char *pColor; x = scr_vrect.x + scr_vrect.width/2 + cl_crossx.value; y = scr_vrect.y + scr_vrect.height/2 + cl_crossy.value; if (crosshair.integer == 2) { pColor = (unsigned char *) &d_8to24table[(byte) crosshaircolor.integer]; glTexEnvf_fp (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4ubv_fp (pColor); GL_Bind (cs_texture); // Our crosshair is now 32x32, but we're drawing 16x16 here // to have a smaller pic. If, in the pixmap, the pixels are // not drawn in doubles, the final image on the screen may // have some of the pixels missing. Sigh... glBegin_fp (GL_QUADS); glTexCoord2f_fp (0, 0); glVertex2f_fp (x - 7, y - 7); glTexCoord2f_fp (1, 0); glVertex2f_fp (x + 9, y - 7); glTexCoord2f_fp (1, 1); glVertex2f_fp (x + 9, y + 9); glTexCoord2f_fp (0, 1); glVertex2f_fp (x - 7, y + 9); glEnd_fp (); glTexEnvf_fp (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } else if (crosshair.integer) { Draw_Character (x - 4, y - 4, '+'); } } //========================================================================== // // Draw_SmallCharacter // // Draws a small character that is clipped at the bottom edge of the // screen. // //========================================================================== void Draw_SmallCharacter (int x, int y, int num) { int row, col; float frow, fcol, xsize,ysize; if (num < 32) { num = 0; } else if (num >= 'a' && num <= 'z') { num -= 64; } else if (num > '_') { num = 0; } else { num -= 32; } if (num == 0) return; if (y <= -8 || y >= vid.height) return; // totally off screen row = num >> 4; col = num & 15; xsize = 0.0625; ysize = 0.25; fcol = col*xsize; frow = row*ysize; GL_Bind (char_smalltexture); glBegin_fp (GL_QUADS); glTexCoord2f_fp (fcol, frow); glVertex2f_fp (x, y); glTexCoord2f_fp (fcol + xsize, frow); glVertex2f_fp (x+8, y); glTexCoord2f_fp (fcol + xsize, frow + ysize); glVertex2f_fp (x+8, y+8); glTexCoord2f_fp (fcol, frow + ysize); glVertex2f_fp (x, y+8); glEnd_fp (); } //========================================================================== // // Draw_SmallString // //========================================================================== void Draw_SmallString (int x, int y, const char *str) { while (*str) { Draw_SmallCharacter (x, y, *str); str++; x += 6; } } //========================================================================== // // Draw_BigCharacter // // Callback for M_DrawBigCharacter() of menu.c // //========================================================================== void Draw_BigCharacter (int x, int y, int num) { int row, col; float frow, fcol, xsize, ysize; row = num / 8; col = num % 8; xsize = 0.125; ysize = 0.25; fcol = col*xsize; frow = row*ysize; GL_Bind (char_menufonttexture); glBegin_fp (GL_QUADS); glTexCoord2f_fp (fcol, frow); glVertex2f_fp (x, y); glTexCoord2f_fp (fcol + xsize, frow); glVertex2f_fp (x+20, y); glTexCoord2f_fp (fcol + xsize, frow + ysize); glVertex2f_fp (x+20, y+20); glTexCoord2f_fp (fcol, frow + ysize); glVertex2f_fp (x, y+20); glEnd_fp (); } /* ============= Draw_Pic ============= */ void Draw_Pic (int x, int y, qpic_t *pic) { glpic_t *gl; gl = (glpic_t *)pic->data; glColor4f_fp (1,1,1,1); GL_Bind (gl->texnum); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glBegin_fp (GL_QUADS); glTexCoord2f_fp (gl->sl, gl->tl); glVertex2f_fp (x, y); glTexCoord2f_fp (gl->sh, gl->tl); glVertex2f_fp (x+pic->width, y); glTexCoord2f_fp (gl->sh, gl->th); glVertex2f_fp (x+pic->width, y+pic->height); glTexCoord2f_fp (gl->sl, gl->th); glVertex2f_fp (x, y+pic->height); glEnd_fp (); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } /* ============= Draw_AlphaPic ============= */ void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha) { glpic_t *gl; gl = (glpic_t *)pic->data; glDisable_fp(GL_ALPHA_TEST); glEnable_fp (GL_BLEND); glCullFace_fp(GL_FRONT); glColor4f_fp (1,1,1,alpha); GL_Bind (gl->texnum); glBegin_fp (GL_QUADS); glTexCoord2f_fp (gl->sl, gl->tl); glVertex2f_fp (x, y); glTexCoord2f_fp (gl->sh, gl->tl); glVertex2f_fp (x+pic->width, y); glTexCoord2f_fp (gl->sh, gl->th); glVertex2f_fp (x+pic->width, y+pic->height); glTexCoord2f_fp (gl->sl, gl->th); glVertex2f_fp (x, y+pic->height); glEnd_fp (); glColor4f_fp (1,1,1,1); glEnable_fp(GL_ALPHA_TEST); glDisable_fp (GL_BLEND); } #if FULLSCREEN_INTERMISSIONS /* ================ Draw_CachePicNoTrans Pa3PyX: Function added to cache pics ignoring transparent colors (e.g. in intermission screens) ================ */ qpic_t *Draw_CachePicNoTrans (const char *path) { cachepic_t *pic; int i, key; qpic_t *dat; glpic_t gl; key = Hash_GenerateKeyString (&hash_cachepics, path, true); for (i = Hash_First(&hash_cachepics, key); i != -1; i = Hash_Next(&hash_cachepics, i)) { pic = &menu_cachepics[i]; if (!strcmp (path, pic->name)) return &pic->pic; } if (menu_numcachepics == MAX_CACHED_PICS) Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); Hash_Add (&hash_cachepics, key, menu_numcachepics); pic = &menu_cachepics[menu_numcachepics]; menu_numcachepics++; q_strlcpy (pic->name, path, MAX_QPATH); // // load the pic from disk // dat = (qpic_t *)FS_LoadTempFile (path, NULL); Draw_PicCheckError (dat, path); SwapPic (dat); pic->pic.width = dat->width; pic->pic.height = dat->height; // Get rid of transparencies for (i = 0; i < dat->width * dat->height; i++) { if (dat->data[i] == 255) dat->data[i] = 31; // pal(31) == pal(255) == FCFCFC (white) } gl.texnum = GL_LoadPicTexture (dat); gl.sl = 0; gl.sh = 1; gl.tl = 0; gl.th = 1; memcpy (pic->pic.data, &gl, sizeof(glpic_t)); return &pic->pic; } /* ============= Draw_IntermissionPic Pa3PyX: this new function introduced to draw the intermission screen only ============= */ void Draw_IntermissionPic (qpic_t *pic) { glpic_t *gl; gl = (glpic_t *)pic->data; glColor4f_fp (1,1,1,1); GL_Bind (gl->texnum); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glBegin_fp(GL_QUADS); glTexCoord2f_fp(0.0f, 0.0f); glVertex2f_fp(0.0f, 0.0f); glTexCoord2f_fp(1.0f, 0.0f); glVertex2f_fp(vid.width, 0.0f); glTexCoord2f_fp(1.0f, 1.0f); glVertex2f_fp(vid.width, vid.height); glTexCoord2f_fp(0.0f, 1.0f); glVertex2f_fp(0.0f, vid.height); glEnd_fp(); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } #endif /* FULLSCREEN_INTERMISSIONS */ void Draw_SubPic (int x, int y, qpic_t *pic, int srcx, int srcy, int width, int height) { glpic_t *gl; float newsl, newtl, newsh, newth; float oldglwidth, oldglheight; gl = (glpic_t *)pic->data; oldglwidth = gl->sh - gl->sl; oldglheight = gl->th - gl->tl; newsl = gl->sl + (srcx*oldglwidth)/pic->width; newsh = newsl + (width*oldglwidth)/pic->width; newtl = gl->tl + (srcy*oldglheight)/pic->height; newth = newtl + (height*oldglheight)/pic->height; glColor4f_fp (1,1,1,1); GL_Bind (gl->texnum); glBegin_fp (GL_QUADS); glTexCoord2f_fp (newsl, newtl); glVertex2f_fp (x, y); glTexCoord2f_fp (newsh, newtl); glVertex2f_fp (x+width, y); glTexCoord2f_fp (newsh, newth); glVertex2f_fp (x+width, y+height); glTexCoord2f_fp (newsl, newth); glVertex2f_fp (x, y+height); glEnd_fp (); } void Draw_PicCropped (int x, int y, qpic_t *pic) { int height; glpic_t *gl; float th, tl; if ((x < 0) || (x+pic->width > vid.width)) { Sys_Error("%s: bad coordinates", __thisfunc__); } if (y >= vid.height || y+pic->height < 0) return; // totally off screen gl = (glpic_t *)pic->data; // rjr tl/th need to be computed based upon pic->tl and pic->th // cuz the piece may come from the scrap if (y+pic->height > vid.height) { height = vid.height-y; tl = 0; th = (height-0.01)/pic->height; } else if (y < 0) { height = pic->height+y; y = -y; tl = (y-0.01)/pic->height; th = (pic->height-0.01)/pic->height; y = 0; } else { height = pic->height; tl = gl->tl; th = gl->th;//(height-0.01)/pic->height; } glColor4f_fp (1,1,1,1); GL_Bind (gl->texnum); glBegin_fp (GL_QUADS); glTexCoord2f_fp (gl->sl, tl); glVertex2f_fp (x, y); glTexCoord2f_fp (gl->sh, tl); glVertex2f_fp (x+pic->width, y); glTexCoord2f_fp (gl->sh, th); glVertex2f_fp (x+pic->width, y+height); glTexCoord2f_fp (gl->sl, th); glVertex2f_fp (x, y+height); glEnd_fp (); } void Draw_SubPicCropped (int x, int y, int h, qpic_t *pic) { int height; glpic_t *gl; float th,tl; if ((x < 0) || (x+pic->width > vid.width)) { Sys_Error("%s: bad coordinates", __thisfunc__); } if (y >= vid.height || y+h < 0) return; // totally off screen gl = (glpic_t *)pic->data; // rjr tl/th need to be computed based upon pic->tl and pic->th // cuz the piece may come from the scrap if (y+pic->height > vid.height) { height = vid.height-y; tl = 0; th = (height-0.01)/pic->height; } else if (y < 0) { height = pic->height+y; y = -y; tl = (y-0.01)/pic->height; th = (pic->height-0.01)/pic->height; y = 0; } else { height = pic->height; tl = gl->tl; th = gl->th;//(height-0.01)/pic->height; } if (height > h) { height = h; } glColor4f_fp (1,1,1,1); GL_Bind (gl->texnum); glBegin_fp (GL_QUADS); glTexCoord2f_fp (gl->sl, tl); glVertex2f_fp (x, y); glTexCoord2f_fp (gl->sh, tl); glVertex2f_fp (x+pic->width, y); glTexCoord2f_fp (gl->sh, th); glVertex2f_fp (x+pic->width, y+height); glTexCoord2f_fp (gl->sl, th); glVertex2f_fp (x, y+height); glEnd_fp (); } /* ============= Draw_TransPic ============= */ void Draw_TransPic (int x, int y, qpic_t *pic) { if (x < 0 || (x + pic->width) > vid.width || y < 0 || (y + pic->height) > vid.height) { Sys_Error ("%s: bad coordinates", __thisfunc__); } Draw_Pic (x, y, pic); } void Draw_TransPicCropped(int x, int y, qpic_t *pic) { Draw_PicCropped (x, y, pic); } /* ============= Draw_TransPicTranslate Only used for the player color selection menu ============= */ void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation, int p_class) { int i, j, v, u; unsigned int trans[PLAYER_DEST_WIDTH * PLAYER_DEST_HEIGHT], *dest; byte *src; int p; GL_Bind(menuplyr_textures[p_class-1]); dest = trans; for (v = 0; v < 64; v++, dest += 64) { src = &menuplyr_pixels[p_class-1][((v*pic->height)>>6) * pic->width]; for (u = 0; u < 64; u++) { p = src[(u*pic->width)>>6]; dest[u] = (p == 255) ? 255 : d_8to24table[translation[p]]; } } for (i = 0; i < PLAYER_PIC_WIDTH; i++) { for (j = 0; j < PLAYER_PIC_HEIGHT; j++) { trans[j * PLAYER_DEST_WIDTH + i] = d_8to24table[translation[menuplyr_pixels[p_class-1][j * PLAYER_PIC_WIDTH + i]]]; } } glTexImage2D_fp (GL_TEXTURE_2D, 0, gl_alpha_format, PLAYER_DEST_WIDTH, PLAYER_DEST_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glColor3f_fp (1,1,1); glBegin_fp (GL_QUADS); glTexCoord2f_fp (0, 0); glVertex2f_fp (x, y); glTexCoord2f_fp (( float )PLAYER_PIC_WIDTH / PLAYER_DEST_WIDTH, 0); glVertex2f_fp (x+pic->width, y); glTexCoord2f_fp (( float )PLAYER_PIC_WIDTH / PLAYER_DEST_WIDTH, ( float )PLAYER_PIC_HEIGHT / PLAYER_DEST_HEIGHT); glVertex2f_fp (x+pic->width, y+pic->height); glTexCoord2f_fp (0, ( float )PLAYER_PIC_HEIGHT / PLAYER_DEST_HEIGHT); glVertex2f_fp (x, y+pic->height); glEnd_fp (); } /* ================ Draw_ConsoleBackground ================ */ static void Draw_ConsolePic (int lines, float ofs, GLuint num, float alpha) { glDisable_fp(GL_ALPHA_TEST); glEnable_fp (GL_BLEND); glCullFace_fp(GL_FRONT); glColor4f_fp (1,1,1,alpha); GL_Bind (num); glBegin_fp (GL_QUADS); glTexCoord2f_fp (0, 0 + ofs); glVertex2f_fp (0, 0); glTexCoord2f_fp (1, 0 + ofs); glVertex2f_fp (vid.conwidth, 0); glTexCoord2f_fp (1, 1); glVertex2f_fp (vid.conwidth, lines); glTexCoord2f_fp (0, 1); glVertex2f_fp (0, lines); glEnd_fp (); glColor4f_fp (1,1,1,1); glEnable_fp(GL_ALPHA_TEST); glDisable_fp (GL_BLEND); } static void Draw_ConsoleVersionInfo (int lines) { static const char ver[] = ENGINE_WATERMARK; const char *ptr = ver; int x = vid.conwidth - (strlen(ver) * 8 + 11); int y = lines - 14; for (; *ptr; ++ptr) Draw_Character (x + (int)(ptr - ver) * 8, y, *ptr | 0x100); } void Draw_ConsoleBackground (int lines) { int y; float ofs, alpha; y = (vid.height * 3) >> 2; ofs = (gl_constretch.integer) ? 0.0f : (vid.conheight - lines) / (float) vid.conheight; alpha = (lines > y) ? 1.0f : 1.1f * lines / y; Draw_ConsolePic (lines, ofs, conback, alpha); #if defined(H2W) if (cls.download) return; #endif /* H2W */ Draw_ConsoleVersionInfo (lines); } /* ============= Draw_TileClear This repeats a 64*64 tile graphic to fill the screen around a sized down refresh window. ============= */ void Draw_TileClear (int x, int y, int w, int h) { glColor3f_fp (1,1,1); GL_Bind (draw_backtile); glBegin_fp (GL_QUADS); glTexCoord2f_fp (x/64.0, y/64.0); glVertex2f_fp (x, y); glTexCoord2f_fp ( (x+w)/64.0, y/64.0); glVertex2f_fp (x+w, y); glTexCoord2f_fp ( (x+w)/64.0, (y+h)/64.0); glVertex2f_fp (x+w, y+h); glTexCoord2f_fp (x/64.0, (y+h)/64.0); glVertex2f_fp (x, y+h); glEnd_fp (); } /* ============= Draw_Fill Fills a box of pixels with a single color ============= */ void Draw_Fill (int x, int y, int w, int h, int c) { glDisable_fp (GL_TEXTURE_2D); glColor3f_fp (host_basepal[c*3]/255.0, host_basepal[c*3+1]/255.0, host_basepal[c*3+2]/255.0); glBegin_fp (GL_QUADS); glVertex2f_fp (x,y); glVertex2f_fp (x+w, y); glVertex2f_fp (x+w, y+h); glVertex2f_fp (x, y+h); glEnd_fp (); glColor3f_fp (1,1,1); glEnable_fp (GL_TEXTURE_2D); } //============================================================================= /* ================ Draw_FadeScreen ================ */ void Draw_FadeScreen (void) { int bx, by, ex, ey; int c; glAlphaFunc_fp(GL_ALWAYS, 0); glEnable_fp (GL_BLEND); glDisable_fp (GL_TEXTURE_2D); glColor4f_fp (248.0/255.0, 220.0/255.0, 120.0/255.0, 0.1); glBegin_fp (GL_QUADS); glVertex2f_fp (0,0); glVertex2f_fp (vid.width, 0); glVertex2f_fp (vid.width, vid.height); glVertex2f_fp (0, vid.height); glEnd_fp (); glColor4f_fp (248.0/255.0, 220.0/255.0, 120.0/255.0, 0.018); for (c = 0 ; c < 40 ; c++) { bx = (rand() % vid.width) - 20; by = (rand() % vid.height) - 20; ex = bx + (rand() % 40) + 20; ey = by + (rand() % 40) + 20; if (bx < 0) bx = 0; if (by < 0) by = 0; if (ex > vid.width) ex = vid.width; if (ey > vid.height) ey = vid.height; glBegin_fp (GL_QUADS); glVertex2f_fp (bx, by); glVertex2f_fp (ex, by); glVertex2f_fp (ex, ey); glVertex2f_fp (bx, ey); glEnd_fp (); } glColor4f_fp (1,1,1,1); glEnable_fp (GL_TEXTURE_2D); glDisable_fp (GL_BLEND); glAlphaFunc_fp(GL_GREATER, 0.632); Sbar_Changed(); } //============================================================================= /* ================ GL_Set2D Setup as if the screen was 320*200 ================ */ void GL_Set2D (void) { glViewport_fp (glx, gly, glwidth, glheight); glMatrixMode_fp(GL_PROJECTION); glLoadIdentity_fp (); glOrtho_fp (0, vid.width, vid.height, 0, -99999, 99999); glMatrixMode_fp(GL_MODELVIEW); glLoadIdentity_fp (); glDisable_fp (GL_DEPTH_TEST); glDisable_fp (GL_CULL_FACE); glDisable_fp (GL_BLEND); glEnable_fp (GL_ALPHA_TEST); // glDisable_fp (GL_ALPHA_TEST); glColor4f_fp (1,1,1,1); } //==================================================================== /* ================ GL_FindTexture ================ */ #if 0 /* seems to have no users */ int GL_FindTexture (const char *identifier) { int i; gltexture_t *glt; for (i = 0, glt = gltextures; i < numgltextures; i++, glt++) { if (!strcmp (identifier, glt->identifier)) return gltextures[i].texnum; } return -1; } #endif /* ================ GL_ResampleTexture ================ */ static void GL_ResampleTexture (unsigned int *in, int inwidth, int inheight, unsigned int *out, int outwidth, int outheight) { int i, j, mark; unsigned int *inrow, *inrow2; unsigned int frac, fracstep; unsigned int *p1, *p2; byte *pix1, *pix2, *pix3, *pix4; fracstep = inwidth * 0x10000 / outwidth; mark = Hunk_LowMark(); p1 = (unsigned int *) Hunk_Alloc (outwidth * sizeof(unsigned int)); p2 = (unsigned int *) Hunk_Alloc (outwidth * sizeof(unsigned int)); frac = fracstep >> 2; for (i = 0; i < outwidth; i++) { p1[i] = 4*(frac>>16); frac += fracstep; } frac = 3 * (fracstep >> 2); for (i = 0; i < outwidth; i++) { p2[i] = 4 * (frac >> 16); frac += fracstep; } for (i = 0; i < outheight; i++, out += outwidth) { inrow = in + inwidth*(int)((i+0.25)*inheight/outheight); inrow2 = in + inwidth*(int)((i+0.75)*inheight/outheight); frac = fracstep >> 1; for (j = 0 ; j < outwidth; j++) { pix1 = (byte *)inrow + p1[j]; pix2 = (byte *)inrow + p2[j]; pix3 = (byte *)inrow2 + p1[j]; pix4 = (byte *)inrow2 + p2[j]; ((byte *)(out+j))[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0])>>2; ((byte *)(out+j))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2; ((byte *)(out+j))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2; ((byte *)(out+j))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2; } } Hunk_FreeToLowMark(mark); } /* ================ GL_MipMap Quarters the size of the texture May operate in-place. This version is from Darkplaces. ================ */ static void GL_MipMap (const byte *in, byte *out, int *width, int *height, int destwidth, int destheight) { const byte *inrow; int x, y, nextrow; // if given odd width/height this discards the last row/column // of pixels, rather than doing a proper box-filter scale down inrow = in; nextrow = *width * 4; if (*width > destwidth) { *width >>= 1; if (*height > destheight) { // reduce both *height >>= 1; for (y = 0; y < *height; y++, inrow += nextrow * 2) { for (in = inrow, x = 0; x < *width; x++) { out[0] = (byte) ((in[0] + in[4] + in[nextrow ] + in[nextrow+4]) >> 2); out[1] = (byte) ((in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2); out[2] = (byte) ((in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2); out[3] = (byte) ((in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2); out += 4; in += 8; } } } else { // reduce width for (y = 0; y < *height; y++, inrow += nextrow) { for (in = inrow, x = 0; x < *width; x++) { out[0] = (byte) ((in[0] + in[4]) >> 1); out[1] = (byte) ((in[1] + in[5]) >> 1); out[2] = (byte) ((in[2] + in[6]) >> 1); out[3] = (byte) ((in[3] + in[7]) >> 1); out += 4; in += 8; } } } } else { if (*height > destheight) { // reduce height *height >>= 1; for (y = 0; y < *height; y++, inrow += nextrow * 2) { for (in = inrow, x = 0; x < *width; x++) { out[0] = (byte) ((in[0] + in[nextrow ]) >> 1); out[1] = (byte) ((in[1] + in[nextrow+1]) >> 1); out[2] = (byte) ((in[2] + in[nextrow+2]) >> 1); out[3] = (byte) ((in[3] + in[nextrow+3]) >> 1); out += 4; in += 4; } } } } } /* ================ fxPalTexImage2D Acts the same as glTexImage2D, except that it maps color into the current palette and uses paletteized textures. If you are on a 3Dfx card and your texture has no alpha, then download it as a palettized texture to save memory. fxpal_buf is a pointer to hunk allocated temporary buffer. The callers of fxPalTexImage2D() are responsible for the allocation and freeing of fxpal_buf. According to Pa3PyX, fxpal_buf must remain static until all mipmap reductions are uploaded, otherwise garbage results with 3dfx. ================ */ static unsigned char *fxpal_buf; static void fxPalTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) { GLsizei i; // we don't want textures with alpha if (internalformat != 3) Sys_Error ("%s: internalformat != 3", __thisfunc__); for (i = 0; i < width * height; i++) { int r, g, b, idx; r = ((unsigned char *)pixels)[i * 4]; g = ((unsigned char *)pixels)[i * 4 +1]; b = ((unsigned char *)pixels)[i * 4 +2]; r >>= 8 - INVERSE_PAL_R_BITS; g >>= 8 - INVERSE_PAL_G_BITS; b >>= 8 - INVERSE_PAL_B_BITS; idx = (r << (INVERSE_PAL_G_BITS + INVERSE_PAL_B_BITS)) | (g << INVERSE_PAL_B_BITS) | b; fxpal_buf[i] = inverse_pal[idx]; } glTexImage2D_fp(target, level, GL_COLOR_INDEX8_EXT, width, height, border, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, fxpal_buf); } /* =============== GL_Upload32 =============== */ static void GL_Upload32 (unsigned int *data, gltexture_t *glt) { int samples; unsigned int *scaled; int mark = 0; int scaled_width, scaled_height; if (gl_tex_NPOT && !is8bit) { scaled_width = glt->width >> gl_picmip.integer; scaled_height = glt->height >> gl_picmip.integer; } else { // Snap the height and width to a power of 2. for (scaled_width = 1; scaled_width < glt->width; scaled_width <<= 1) ; for (scaled_height = 1; scaled_height < glt->height; scaled_height <<= 1) ; scaled_width >>= gl_picmip.integer; scaled_height >>= gl_picmip.integer; } if (scaled_width < 1) scaled_width = 1; if (scaled_height < 1) scaled_height = 1; if (scaled_width > gl_max_size) scaled_width = gl_max_size; if (scaled_height > gl_max_size) scaled_height = gl_max_size; // 3dfx has some aspect ratio constraints. // can't go beyond 8 to 1 or below 1 to 8. if (is_3dfx) { if (scaled_width * 8 < scaled_height) { scaled_width = scaled_height >> 3; } else if (scaled_height * 8 < scaled_width) { scaled_height = scaled_width >> 3; } } samples = (glt->flags & TEX_ALPHA) ? gl_alpha_format : gl_solid_format; if (scaled_width == glt->width && scaled_height == glt->height) { scaled = data; } else { mark = Hunk_LowMark(); scaled = (unsigned int *) Hunk_AllocName(scaled_width * scaled_height * sizeof(unsigned int), "texbuf_upload32"); GL_ResampleTexture (data, glt->width, glt->height, scaled, scaled_width, scaled_height); } if (is8bit && !(glt->flags & TEX_ALPHA)) { if (!mark) mark = Hunk_LowMark(); fxpal_buf = (unsigned char *) Hunk_AllocName(scaled_width * scaled_height, "texbuf_upload8pal"); fxPalTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } else { glTexImage2D_fp (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } if (glt->flags & TEX_MIPMAP) { int miplevel; miplevel = 0; while (scaled_width > 1 || scaled_height > 1) { GL_MipMap ((byte *)scaled, (byte *)scaled, &scaled_width, &scaled_height, 1, 1); miplevel++; if (is8bit && !(glt->flags & TEX_ALPHA)) fxPalTexImage2D (GL_TEXTURE_2D, miplevel, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); else glTexImage2D_fp (GL_TEXTURE_2D, miplevel, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } } if (glt->flags & TEX_NEAREST) { glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } else if (glt->flags & TEX_LINEAR) { glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } else if (glt->flags & TEX_MIPMAP) { glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_texmodes[gl_filter_idx].minimize); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_texmodes[gl_filter_idx].maximize); if (gl_max_anisotropy >= 2) glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_texture_anisotropy.value); } else { glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_texmodes[gl_filter_idx].maximize); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_texmodes[gl_filter_idx].maximize); } if (mark) Hunk_FreeToLowMark(mark); } /* =============== GL_Upload8 modes: 0 - standard 1 - color 0 transparent, odd - translucent, even - full value 2 - color 0 transparent 3 - special (particle translucency table) =============== */ static void GL_Upload8 (byte *data, gltexture_t *glt) { unsigned int *trans; int mark; int i, p, s; s = glt->width * glt->height; mark = Hunk_LowMark(); trans = (unsigned int *) Hunk_AllocName(s * sizeof(unsigned int), "texbuf_upload8"); if (glt->flags & (TEX_ALPHA|TEX_TRANSPARENT|TEX_HOLEY|TEX_SPECIAL_TRANS)) { // if there are no transparent pixels, make it a 3 component // texture even if it was flagged as TEX_ALPHA. qboolean noalpha = !(glt->flags & (TEX_TRANSPARENT|TEX_HOLEY|TEX_SPECIAL_TRANS)); for (i = 0; i < s; i++) { p = data[i]; trans[i] = d_8to24table[p]; if (p == 255) { if (noalpha) noalpha = false; /* transparent, so scan around for another color * to avoid alpha fringes */ /* this is a replacement from Quake II for Raven's * "neighboring colors" code */ if (i > glt->width && data[i-glt->width] != 255) p = data[i-glt->width]; else if (i < s-glt->width && data[i+glt->width] != 255) p = data[i+glt->width]; else if (i > 0 && data[i-1] != 255) p = data[i-1]; else if (i < s-1 && data[i+1] != 255) p = data[i+1]; else p = 0; /* copy rgb components */ ((byte *)&trans[i])[0] = ((byte *)&d_8to24table[p])[0]; ((byte *)&trans[i])[1] = ((byte *)&d_8to24table[p])[1]; ((byte *)&trans[i])[2] = ((byte *)&d_8to24table[p])[2]; } if (glt->flags & TEX_TRANSPARENT) { p = data[i]; if (p == 0) { trans[i] &= MASK_rgb; } else if (p & 1) { p = (int)(255 * r_wateralpha.value) & 0xff; trans[i] &= MASK_rgb; trans[i] |= p << SHIFT_a; } else { trans[i] |= MASK_a; } } else if (glt->flags & TEX_HOLEY) { p = data[i]; if (p == 0) trans[i] &= MASK_rgb; } else if (glt->flags & TEX_SPECIAL_TRANS) { p = data[i]; trans[i] = d_8to24table[ColorIndex[p>>4]] & MASK_rgb; trans[i] |= (( int )ColorPercent[p&15] & 0xff) << SHIFT_a; } } if (noalpha) glt->flags &= ~TEX_ALPHA; if (glt->flags & (TEX_TRANSPARENT|TEX_HOLEY|TEX_SPECIAL_TRANS)) glt->flags |= TEX_ALPHA; } else { if (s&3) Sys_Error ("%s: s&3", __thisfunc__); for (i = 0; i < s; i += 4) { trans[i] = d_8to24table[data[i]]; trans[i+1] = d_8to24table[data[i+1]]; trans[i+2] = d_8to24table[data[i+2]]; trans[i+3] = d_8to24table[data[i+3]]; } } GL_Upload32 (trans, glt); Hunk_FreeToLowMark(mark); } /* ================ GL_LoadTexture ================ */ GLuint GL_LoadTexture (const char *identifier, byte *data, int width, int height, int flags) { int i, size, key; unsigned short crc; gltexture_t *glt; #if !defined (H2W) if (cls.state == ca_dedicated) return GL_UNUSED_TEXTURE; #endif size = width * height; if (flags & TEX_RGBA) size *= 4; crc = CRC_Block (data, size); key = Hash_GenerateKeyString (&hash_gltextures, identifier, true); if (identifier[0]) { /* texture already present? */ for (i = Hash_First(&hash_gltextures, key); i != -1; i = Hash_Next(&hash_gltextures, i)) { glt = &gltextures[i]; if (!strcmp (identifier, glt->identifier)) { if (crc != glt->crc || (glt->flags & TEX_MIPMAP) != (flags & TEX_MIPMAP) || width != glt->width || height != glt->height) { /* not the same, delete and rebind to new image */ Con_DPrintf ("Texture cache mismatch: %lu, %s, reloading\n", (unsigned long)glt->texnum, identifier); glDeleteTextures_fp (1, &glt->texnum); goto gl_rebind; } else return glt->texnum; /* the same is present. */ } } } if (numgltextures >= MAX_GLTEXTURES) Sys_Error ("%s: cache full, max is %i textures.", __thisfunc__, MAX_GLTEXTURES); Hash_Add (&hash_gltextures, key, numgltextures); glt = &gltextures[numgltextures]; numgltextures++; q_strlcpy (glt->identifier, identifier, MAX_QPATH); gl_rebind: glGenTextures_fp(1, &glt->texnum); glt->width = width; glt->height = height; glt->flags = flags; glt->crc = crc; GL_Bind (glt->texnum); if (flags & TEX_RGBA) GL_Upload32 ((unsigned int *)data, glt); else GL_Upload8 (data, glt); return glt->texnum; } /* =============== GL_LoadPixmap from LordHavoc's Twilight (DarkPlaces) project Loads a string into a named 32x32 greyscale OpenGL texture, suitable for crosshairs or pointers. The data string is in a format similar to an X11 pixmap. '0'-'7' are brightness levels, any other character is considered transparent. Remember, NO error checking is performed on the input string. */ static GLuint GL_LoadPixmap (const char *name, const char *data) { int i; unsigned char pixels[32*32][4]; for (i = 0; i < 32*32; i++) { if (data[i] >= '0' && data[i] < '8') { pixels[i][0] = 255; pixels[i][1] = 255; pixels[i][2] = 255; pixels[i][3] = (data[i] - '0') * 32; } else { pixels[i][0] = 255; pixels[i][1] = 255; pixels[i][2] = 255; pixels[i][3] = 0; } } return GL_LoadTexture (name, (unsigned char *) pixels, 32, 32, TEX_ALPHA | TEX_RGBA | TEX_LINEAR); } /* ================ GL_LoadPicTexture ================ */ GLuint GL_LoadPicTexture (qpic_t *pic) { return GL_LoadTexture ("", pic->data, pic->width, pic->height, TEX_ALPHA|TEX_NEAREST); } engine/h2shared/gl_func.h000066400000000000000000000166241444734033100156250ustar00rootroot00000000000000/* * gl_func.h -- opengl function pointers * make sure NOT to protect this file against multiple inclusions! * * Copyright (C) 2001 contributors of the Anvil of Thyrion project * Copyright (C) 2005-2016 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* whether to dlsym gl function calls: * the define GL_DLSYM is decided in the Makefile */ #ifndef __GL_FUNC_EXTERN #define __GL_FUNC_EXTERN extern #endif /* core gl functions */ #if defined(GL_DLSYM) #ifndef GL_FUNCTION #define GL_FUNCTION(ret, func, params) \ typedef ret (APIENTRY *func##_f) params; \ __GL_FUNC_EXTERN func##_f func##_fp; #endif GL_FUNCTION(void, glBindTexture, (GLenum,GLuint)) GL_FUNCTION(void, glDeleteTextures, (GLsizei,const GLuint *)) GL_FUNCTION(void, glGenTextures, (GLsizei,GLuint *)) GL_FUNCTION(void, glTexParameterf, (GLenum,GLenum,GLfloat)) GL_FUNCTION(void, glTexEnvf, (GLenum,GLenum,GLfloat)) GL_FUNCTION(void, glScalef, (GLfloat,GLfloat,GLfloat)) GL_FUNCTION(void, glTexImage2D, (GLenum,GLint,GLint,GLsizei,GLsizei,GLint,GLenum,GLenum,const GLvoid*)) GL_FUNCTION(void, glTexSubImage2D, (GLenum,GLint,GLint,GLint,GLsizei,GLsizei,GLenum,GLenum,const GLvoid *)) GL_FUNCTION(void, glBegin, (GLenum)) GL_FUNCTION(void, glEnd, (void)) GL_FUNCTION(void, glEnable, (GLenum)) GL_FUNCTION(void, glDisable, (GLenum)) GL_FUNCTION(GLboolean, glIsEnabled, (GLenum)) GL_FUNCTION(void, glFinish, (void)) GL_FUNCTION(void, glFlush, (void)) GL_FUNCTION(void, glClear, (GLbitfield)) GL_FUNCTION(void, glVertex2f, (GLfloat,GLfloat)) GL_FUNCTION(void, glVertex3f, (GLfloat,GLfloat,GLfloat)) GL_FUNCTION(void, glVertex3fv, (const GLfloat *)) GL_FUNCTION(void, glTexCoord2f, (GLfloat,GLfloat)) GL_FUNCTION(void, glTexCoord2fv, (const GLfloat *)) GL_FUNCTION(void, glColor4f, (GLfloat,GLfloat,GLfloat,GLfloat)) GL_FUNCTION(void, glColor4fv, (const GLfloat *)) GL_FUNCTION(void, glColor4ub, (GLubyte,GLubyte,GLubyte,GLubyte)) GL_FUNCTION(void, glColor4ubv, (const GLubyte *)) GL_FUNCTION(void, glColor3ubv, (const GLubyte *)) GL_FUNCTION(void, glColor3f, (GLfloat,GLfloat,GLfloat)) GL_FUNCTION(void, glClearColor, (GLclampf,GLclampf,GLclampf,GLclampf)) GL_FUNCTION(void, glAlphaFunc, (GLenum,GLclampf)) GL_FUNCTION(void, glBlendFunc, (GLenum,GLenum)) GL_FUNCTION(void, glShadeModel, (GLenum)) GL_FUNCTION(void, glPolygonMode, (GLenum,GLenum)) GL_FUNCTION(void, glDepthMask, (GLboolean)) GL_FUNCTION(void, glDepthRange, (GLclampd,GLclampd)) GL_FUNCTION(void, glDepthFunc, (GLenum)) #if defined(DRAW_PROGRESSBARS) /* D_ShowLoadingSize() */ GL_FUNCTION(void, glDrawBuffer, (GLenum)) #endif GL_FUNCTION(void, glReadPixels, (GLint,GLint,GLsizei,GLsizei,GLenum,GLenum, GLvoid *)) GL_FUNCTION(void, glPixelStorei, (GLenum,GLint)) GL_FUNCTION(void, glHint, (GLenum,GLenum)) GL_FUNCTION(void, glCullFace, (GLenum)) GL_FUNCTION(void, glRotatef, (GLfloat,GLfloat,GLfloat,GLfloat)) GL_FUNCTION(void, glTranslatef, (GLfloat,GLfloat,GLfloat)) GL_FUNCTION(void, glOrtho, (GLdouble,GLdouble,GLdouble,GLdouble,GLdouble,GLdouble)) GL_FUNCTION(void, glFrustum, (GLdouble,GLdouble,GLdouble,GLdouble,GLdouble,GLdouble)) GL_FUNCTION(void, glViewport, (GLint,GLint,GLsizei,GLsizei)) GL_FUNCTION(void, glPushMatrix, (void)) GL_FUNCTION(void, glPopMatrix, (void)) GL_FUNCTION(void, glLoadIdentity, (void)) GL_FUNCTION(void, glMatrixMode, (GLenum)) GL_FUNCTION(void, glLoadMatrixf, (const GLfloat *)) /* GL_FUNCTION(void, glPolygonOffset, (GLfloat,GLfloat)) */ GL_FUNCTION(const GLubyte*, glGetString, (GLenum)) GL_FUNCTION(void, glGetFloatv, (GLenum,GLfloat *)) GL_FUNCTION(void, glGetIntegerv, (GLenum,GLint *)) GL_FUNCTION(void, glStencilFunc, (GLenum,GLint,GLuint)) GL_FUNCTION(void, glStencilOp, (GLenum,GLenum,GLenum)) GL_FUNCTION(void, glClearStencil, (GLint)) #else #ifndef GL_FUNC_H #define GL_FUNC_H #define glBindTexture_fp glBindTexture #define glDeleteTextures_fp glDeleteTextures #define glGenTextures_fp glGenTextures #define glTexParameterf_fp glTexParameterf #define glTexEnvf_fp glTexEnvf #define glScalef_fp glScalef #define glTexImage2D_fp glTexImage2D #define glTexSubImage2D_fp glTexSubImage2D #define glBegin_fp glBegin #define glEnd_fp glEnd #define glEnable_fp glEnable #define glDisable_fp glDisable #define glIsEnabled_fp glIsEnabled #define glFinish_fp glFinish #define glFlush_fp glFlush #define glClear_fp glClear #define glVertex2f_fp glVertex2f #define glVertex3f_fp glVertex3f #define glVertex3fv_fp glVertex3fv #define glTexCoord2f_fp glTexCoord2f #define glTexCoord2fv_fp glTexCoord2fv #define glColor4f_fp glColor4f #define glColor4fv_fp glColor4fv #define glColor4ub_fp glColor4ub #define glColor4ubv_fp glColor4ubv #define glColor3ubv_fp glColor3ubv #define glColor3f_fp glColor3f #define glClearColor_fp glClearColor #define glAlphaFunc_fp glAlphaFunc #define glBlendFunc_fp glBlendFunc #define glShadeModel_fp glShadeModel #define glPolygonMode_fp glPolygonMode #define glDepthMask_fp glDepthMask #define glDepthRange_fp glDepthRange #define glDepthFunc_fp glDepthFunc #define glDrawBuffer_fp glDrawBuffer #define glReadPixels_fp glReadPixels #define glPixelStorei_fp glPixelStorei #define glHint_fp glHint #define glCullFace_fp glCullFace #define glRotatef_fp glRotatef #define glTranslatef_fp glTranslatef #define glOrtho_fp glOrtho #define glFrustum_fp glFrustum #define glViewport_fp glViewport #define glPushMatrix_fp glPushMatrix #define glPopMatrix_fp glPopMatrix #define glLoadIdentity_fp glLoadIdentity #define glMatrixMode_fp glMatrixMode #define glLoadMatrixf_fp glLoadMatrixf #define glPolygonOffset_fp glPolygonOffset #define glGetString_fp glGetString #define glGetFloatv_fp glGetFloatv #define glGetIntegerv_fp glGetIntegerv #define glStencilFunc_fp glStencilFunc #define glStencilOp_fp glStencilOp #define glClearStencil_fp glClearStencil #endif /* GL_FUNC_H */ #endif /* !defined(GL_DLSYM) */ #undef GL_FUNCTION /* global gl functions link to at runtime */ #ifndef GL_FUNCTION_OPT #define GL_FUNCTION_OPT(ret, func, params) \ typedef ret (APIENTRY *func##_f) params; \ __GL_FUNC_EXTERN func##_f func##_fp; #endif /* GL_ARB_multitexture */ GL_FUNCTION_OPT(void, glActiveTextureARB, (GLenum)) GL_FUNCTION_OPT(void, glMultiTexCoord2fARB, (GLenum,GLfloat,GLfloat)) #undef GL_FUNCTION_OPT /* typedefs for functions linked to locally at runtime */ #ifndef GL_FUNC_TYPEDEFS #define GL_FUNC_TYPEDEFS /* this one doesn't seem to in amiga opengl libs :( */ typedef void (APIENTRY *glGetTexParameterfv_f) (GLenum,GLenum,GLfloat *); /* GL_EXT_shared_texture_palette */ typedef void (APIENTRY *glColorTableEXT_f) (GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid *); /* 3DFX_set_global_palette (NOTE: the PowerVR equivalent is POWERVR_set_global_palette / glSetGlobalPalettePOWERVR) */ typedef void (APIENTRY *gl3DfxSetPaletteEXT_f) (GLuint *); #endif /* GL_FUNC_TYPEDEFS */ engine/h2shared/gl_mesh.c000066400000000000000000000172151444734033100156160ustar00rootroot00000000000000/* * gl_mesh.c -- triangle model functions * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" /* ================================================================= ALIAS MODEL DISPLAY LIST GENERATION ================================================================= */ static int used[8192]; // the command list holds counts and s/t values that are valid for // every frame static int commands[8192]; static int numcommands; // all frames will have their vertexes rearranged and expanded // so they are in the order expected by the command list static int vertexorder[8192]; static int numorder; static int stripverts[128]; static int striptris[128]; static int stripstverts[128]; static int stripcount; /* ================ StripLength ================ */ static int StripLength (int starttri, int startv) { int m1, m2; int st1, st2; int j; mtriangle_t *last, *check; int k; used[starttri] = 2; last = &triangles[starttri]; stripverts[0] = last->vertindex[(startv)%3]; stripstverts[0] = last->stindex[(startv)%3]; stripverts[1] = last->vertindex[(startv+1)%3]; stripstverts[1] = last->stindex[(startv+1)%3]; stripverts[2] = last->vertindex[(startv+2)%3]; stripstverts[2] = last->stindex[(startv+2)%3]; striptris[0] = starttri; stripcount = 1; m1 = last->vertindex[(startv+2)%3]; st1 = last->stindex[(startv+2)%3]; m2 = last->vertindex[(startv+1)%3]; st2 = last->stindex[(startv+1)%3]; // look for a matching triangle nexttri: for (j = starttri+1, check = &triangles[starttri+1]; j < pheader->numtris; j++, check++) { if (check->facesfront != last->facesfront) continue; for (k = 0; k < 3; k++) { if (check->vertindex[k] != m1) continue; if (check->stindex[k] != st1) continue; if (check->vertindex[ (k+1)%3 ] != m2) continue; if (check->stindex[ (k+1)%3 ] != st2) continue; // this is the next part of the fan // if we can't use this triangle, this tristrip is done if (used[j]) goto done; // the new edge if (stripcount & 1) { m2 = check->vertindex[ (k+2)%3 ]; st2 = check->stindex[ (k+2)%3 ]; } else { m1 = check->vertindex[ (k+2)%3 ]; st1 = check->stindex[ (k+2)%3 ]; } stripverts[stripcount+2] = check->vertindex[ (k+2)%3 ]; stripstverts[stripcount+2] = check->stindex[ (k+2)%3 ]; striptris[stripcount] = j; stripcount++; used[j] = 2; goto nexttri; } } done: // clear the temp used flags for (j = starttri+1; j < pheader->numtris; j++) { if (used[j] == 2) used[j] = 0; } return stripcount; } /* =========== FanLength =========== */ static int FanLength (int starttri, int startv) { int m1, m2; int st1, st2; int j; mtriangle_t *last, *check; int k; used[starttri] = 2; last = &triangles[starttri]; stripverts[0] = last->vertindex[(startv)%3]; stripstverts[0] = last->stindex[(startv)%3]; stripverts[1] = last->vertindex[(startv+1)%3]; stripstverts[1] = last->stindex[(startv+1)%3]; stripverts[2] = last->vertindex[(startv+2)%3]; stripstverts[2] = last->stindex[(startv+2)%3]; striptris[0] = starttri; stripcount = 1; m1 = last->vertindex[(startv+0)%3]; st1 = last->stindex[(startv+2)%3]; m2 = last->vertindex[(startv+2)%3]; st2 = last->stindex[(startv+1)%3]; // look for a matching triangle nexttri: for (j = starttri+1, check = &triangles[starttri+1]; j < pheader->numtris; j++, check++) { if (check->facesfront != last->facesfront) continue; for (k = 0; k < 3; k++) { if (check->vertindex[k] != m1) continue; if (check->stindex[k] != st1) continue; if (check->vertindex[ (k+1)%3 ] != m2) continue; if (check->stindex[ (k+1)%3 ] != st2) continue; // this is the next part of the fan // if we can't use this triangle, this tristrip is done if (used[j]) goto done; // the new edge m2 = check->vertindex[ (k+2)%3 ]; st2 = check->stindex[ (k+2)%3 ]; stripverts[stripcount+2] = m2; stripstverts[stripcount+2] = st2; striptris[stripcount] = j; stripcount++; used[j] = 2; goto nexttri; } } done: // clear the temp used flags for (j = starttri+1; j < pheader->numtris; j++) { if (used[j] == 2) used[j] = 0; } return stripcount; } /* ================ BuildTris Generate a list of trifans or strips for the model, which holds for all frames ================ */ static void BuildTris (void) { int i, j, k; int startv; float s, t; int len, bestlen, besttype; int bestverts[1024]; int besttris[1024]; int beststverts[1024]; int type; // // build tristrips // numorder = 0; numcommands = 0; memset (used, 0, sizeof(used)); for (i = 0; i < pheader->numtris; i++) { // pick an unused triangle and start the trifan if (used[i]) continue; bestlen = 0; besttype = 0; for (type = 0 ; type < 2 ; type++) { for (startv = 0; startv < 3; startv++) { if (type == 1) len = StripLength (i, startv); else len = FanLength (i, startv); if (len > bestlen) { besttype = type; bestlen = len; for (j = 0; j < bestlen+2; j++) { beststverts[j] = stripstverts[j]; bestverts[j] = stripverts[j]; } for (j = 0; j < bestlen; j++) besttris[j] = striptris[j]; } } } // mark the tris on the best strip as used for (j = 0; j < bestlen; j++) used[besttris[j]] = 1; if (besttype == 1) commands[numcommands++] = (bestlen+2); else commands[numcommands++] = -(bestlen+2); for (j = 0; j < bestlen+2; j++) { int tmp; // emit a vertex into the reorder buffer k = bestverts[j]; vertexorder[numorder++] = k; k = beststverts[j]; // emit s/t coords into the commands stream s = stverts[k].s; t = stverts[k].t; if (!triangles[besttris[0]].facesfront && stverts[k].onseam) s += pheader->skinwidth / 2; // on back side s = (s + 0.5) / pheader->skinwidth; t = (t + 0.5) / pheader->skinheight; // *(float *)&commands[numcommands++] = s; // *(float *)&commands[numcommands++] = t; // NOTE: 4 == sizeof(int) // == sizeof(float) memcpy (&tmp, &s, 4); commands[numcommands++] = tmp; memcpy (&tmp, &t, 4); commands[numcommands++] = tmp; } } commands[numcommands++] = 0; // end of list marker DEBUG_Printf ("%3i tri %3i vert %3i cmd\n", pheader->numtris, numorder, numcommands); } /* ================ GL_MakeAliasModelDisplayLists ================ */ void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *hdr) { int i, j; int *cmds; trivertx_t *verts; DEBUG_Printf ("meshing %s...\n", m->name); BuildTris (); // trifans or lists hdr->poseverts = numorder; cmds = (int *) Hunk_AllocName (numcommands * 4, "cmds"); hdr->commands = (byte *)cmds - (byte *)hdr; memcpy (cmds, commands, numcommands * 4); verts = (trivertx_t *) Hunk_AllocName (hdr->numposes * hdr->poseverts * sizeof(trivertx_t), "verts"); hdr->posedata = (byte *)verts - (byte *)hdr; for (i = 0; i < hdr->numposes; i++) { for (j = 0; j < numorder; j++) *verts++ = poseverts[i][vertexorder[j]]; } } engine/h2shared/gl_model.c000066400000000000000000002304251444734033100157620ustar00rootroot00000000000000/* * model.c -- model loading and caching * models are the only shared resource between a client and server * running on the same machine. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "hashindex.h" #include "hwal.h" static qmodel_t* loadmodel; static char loadname[MAX_QPATH]; /* for hunk tags */ static qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash); static void Mod_LoadSpriteModel (qmodel_t *mod, void *buffer); static void Mod_LoadBrushModel (qmodel_t *mod, void *buffer); static void Mod_LoadAliasModel (qmodel_t *mod, void *buffer); static void Mod_LoadAliasModelNew (qmodel_t *mod, void *buffer); static void Mod_Print (void); static cvar_t external_ents = {"external_ents", "1", CVAR_ARCHIVE}; static byte mod_novis[MAX_MAP_LEAFS/8]; // 650 should be enough with model handle recycling, but.. (Pa3PyX) #define MAX_MOD_KNOWN 2048 static qmodel_t mod_known[MAX_MOD_KNOWN]; static int mod_numknown; static hashindex_t hash_mod; static vec3_t aliasmins, aliasmaxs; #ifndef H2W int entity_file_size; #endif static qboolean spr_reload_only = false; /* =============== Mod_Init =============== */ void Mod_Init (void) { Cvar_RegisterVariable (&external_ents); Cmd_AddCommand ("mcache", Mod_Print); memset (mod_novis, 0xff, sizeof(mod_novis)); Hash_Allocate (&hash_mod, MAX_MOD_KNOWN); } /* =============== Mod_Extradata Caches the data if needed =============== */ void *Mod_Extradata (qmodel_t *mod) { void *r; r = Cache_Check (&mod->cache); if (r) return r; Mod_LoadModel (mod, true); if (!mod->cache.data) Sys_Error ("%s: caching failed", __thisfunc__); return mod->cache.data; } /* =============== Mod_PointInLeaf =============== */ mleaf_t *Mod_PointInLeaf (vec3_t p, qmodel_t *model) { mnode_t *node; float d; mplane_t *plane; if (!model) Sys_Error ("%s: NULL model", __thisfunc__); if (!model->nodes) Sys_Error ("%s: model w/o nodes : %s", __thisfunc__, model->name); node = model->nodes; while (1) { if (node->contents < 0) return (mleaf_t *)node; plane = node->plane; d = DotProduct (p,plane->normal) - plane->dist; if (d > 0) node = node->children[0]; else node = node->children[1]; } return NULL; // never reached } /* =================== Mod_DecompressVis =================== */ static byte *Mod_DecompressVis (byte *in, qmodel_t *model) { static byte decompressed[MAX_MAP_LEAFS/8]; int c; byte *out; int row; row = (model->numleafs+7)>>3; out = decompressed; if (!in) { // no vis info, so make all visible while (row) { *out++ = 0xff; row--; } return decompressed; } do { if (*in) { *out++ = *in++; continue; } c = in[1]; in += 2; while (c) { *out++ = 0; c--; } } while (out - decompressed < row); return decompressed; } byte *Mod_LeafPVS (mleaf_t *leaf, qmodel_t *model) { if (leaf == model->leafs) return mod_novis; return Mod_DecompressVis (leaf->compressed_vis, model); } /* =================== Mod_ClearAll =================== */ void Mod_ClearAll (void) { int i, key; qmodel_t *mod; for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { // clear alias models only if textures were flushed (Pa3PyX) if (mod->type == mod_alias) { if (flush_textures && gl_purge_maptex.integer) { if (Cache_Check(&(mod->cache))) Cache_Free(&(mod->cache)); mod->needload = NL_NEEDS_LOADED; } } else { // Clear all other models completely if (mod->name[0]) { key = Hash_GenerateKeyString (&hash_mod, mod->name, true); Hash_Remove(&hash_mod, key, i); } memset(mod, 0, sizeof(qmodel_t)); mod->needload = NL_UNREFERENCED; } } } /* ================== Mod_FindName ================== */ qmodel_t *Mod_FindName (const char *name) { int i, key; qmodel_t *mod; if (!name[0]) Sys_Error ("%s: NULL name", __thisfunc__); // // search the currently loaded models // // allow recycling of model handles (Pa3PyX) key = Hash_GenerateKeyString (&hash_mod, name, true); for (i = Hash_First(&hash_mod, key); i != -1; i = Hash_Next(&hash_mod, i)) { if (!strcmp(mod_known[i].name, name)) { mod = &(mod_known[i]); return mod; } } if (i == -1) { if (mod_numknown == MAX_MOD_KNOWN) { for (i = 0; i < mod_numknown; i++) { if (mod_known[i].needload == NL_UNREFERENCED) break; } if (i < mod_numknown) { Hash_Add (&hash_mod, key, i); mod = &(mod_known[i]); } else // No free model handle Sys_Error ("mod_numknown == MAX_MOD_KNOWN"); } else { Hash_Add (&hash_mod, key, mod_numknown); mod = &(mod_known[mod_numknown++]); } } q_strlcpy (mod->name, name, MAX_QPATH); mod->needload = NL_NEEDS_LOADED; return mod; } /* ================== Mod_TouchModel ================== */ void Mod_TouchModel (const char *name) { qmodel_t *mod; mod = Mod_FindName (name); if (mod->needload == NL_PRESENT) { if (mod->type == mod_alias) Cache_Check (&mod->cache); } } /* ================== Mod_LoadModel Loads a model into the cache ================== */ static qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash) { byte *buf; byte stackbuf[1024]; // avoid dirtying the cache heap int mod_type; // allow recycling of models (Pa3PyX) if (mod->type == mod_alias) { if (Cache_Check(&mod->cache)) { mod->needload = NL_PRESENT; return mod; } } else if (mod->needload == NL_PRESENT) { return mod; } // // load the file // buf = FS_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf), & mod->path_id); if (!buf) { if (crash) Sys_Error ("%s: %s not found", __thisfunc__, mod->name); return NULL; } // // allocate a new model // COM_FileBase (mod->name, loadname, sizeof(loadname)); loadmodel = mod; // // fill it in // // call the apropriate loader mod->needload = NL_PRESENT; mod_type = (buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24)); switch (mod_type) { case RAPOLYHEADER: Mod_LoadAliasModelNew (mod, buf); break; case IDPOLYHEADER: Mod_LoadAliasModel (mod, buf); break; case IDSPRITEHEADER: Mod_LoadSpriteModel (mod, buf); break; default: Mod_LoadBrushModel (mod, buf); break; } return mod; } /* ================== Mod_ForName Loads in a model for the given name ================== */ qmodel_t *Mod_ForName (const char *name, qboolean crash) { qmodel_t *mod; mod = Mod_FindName (name); return Mod_LoadModel (mod, crash); } /* =============================================================================== BRUSHMODEL LOADING =============================================================================== */ static byte *mod_base; /* ================= Mod_LoadTextures ================= */ static void Mod_LoadTextures (lump_t *l) { int i, j, pixels, num, maxanim, altmax; miptex_t *mt; texture_t *tx, *tx2; texture_t *anims[10]; texture_t *altanims[10]; dmiptexlump_t *m; #ifdef WAL_TEXTURES // external WAL texture loading char texname[MAX_QPATH]; int mark; miptex_wal_t *mt_wal; #endif if (!l->filelen) { loadmodel->textures = NULL; return; } m = (dmiptexlump_t *)(mod_base + l->fileofs); m->nummiptex = LittleLong (m->nummiptex); loadmodel->numtextures = m->nummiptex; loadmodel->textures = (texture_t **) Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures), "texture"); for (i = 0; i < m->nummiptex; i++) { m->dataofs[i] = LittleLong(m->dataofs[i]); if (m->dataofs[i] == -1) continue; mt = (miptex_t *)((byte *)m + m->dataofs[i]); mt->width = LittleLong (mt->width); mt->height = LittleLong (mt->height); for (j = 0; j < MIPLEVELS; j++) mt->offsets[j] = LittleLong (mt->offsets[j]); #ifdef WAL_TEXTURES if (!r_texture_external.integer) goto bsp_tex_internal; // try an external wal texture file first q_snprintf (texname, sizeof(texname), "textures/%s.wal", mt->name); if (texname[sizeof(WAL_EXT_DIRNAME)] == '*') texname[sizeof(WAL_EXT_DIRNAME)] = WAL_REPLACE_ASTERIX; mark = Hunk_LowMark (); mt_wal = (miptex_wal_t *)FS_LoadHunkFile(texname, NULL); if (mt_wal != NULL) { mt_wal->ident = LittleLong (mt_wal->ident); mt_wal->version = LittleLong (mt_wal->version); if (mt_wal->ident == IDWALHEADER && mt_wal->version == WALVERSION) { mt_wal->width = LittleLong (mt_wal->width); mt_wal->height = LittleLong (mt_wal->height); for (j = 0; j < MIPLEVELS; j++) mt_wal->offsets[j] = LittleLong (mt_wal->offsets[j]); if ( (mt_wal->width & 15) || (mt_wal->height & 15) ) { Hunk_FreeToLowMark (mark); Sys_Printf ("Texture %s is not 16 aligned", texname); goto bsp_tex_internal; } pixels = mt_wal->width*mt_wal->height/64*85; tx = (texture_t *) Hunk_AllocName (sizeof(texture_t) +pixels, "texture"); loadmodel->textures[i] = tx; memcpy (tx->name, mt_wal->name, sizeof(tx->name)); tx->width = mt_wal->width; tx->height = mt_wal->height; for (j = 0; j < MIPLEVELS; j++) tx->offsets[j] = mt_wal->offsets[j] + sizeof(texture_t) - sizeof(miptex_wal_t); // the pixels immediately follow the structures memcpy (tx+1, mt_wal+1, pixels); } else { if (mt_wal->ident != IDWALHEADER) Sys_Printf ("%s: %s is not a valid WAL file\n", __thisfunc__, texname); if (mt_wal->version != WALVERSION) Sys_Printf ("%s: WAL file %s has unsupported version (%d)\n", __thisfunc__, texname, mt_wal->version); Hunk_FreeToLowMark (mark); goto bsp_tex_internal; } } else { // load internal bsp pixel data bsp_tex_internal: #endif /* WAL_TEXTURES */ if ( (mt->width & 15) || (mt->height & 15) ) Sys_Error ("Texture %s is not 16 aligned", mt->name); pixels = mt->width*mt->height/64*85; tx = (texture_t *) Hunk_AllocName (sizeof(texture_t) +pixels, "texture" ); loadmodel->textures[i] = tx; memcpy (tx->name, mt->name, sizeof(tx->name)); tx->width = mt->width; tx->height = mt->height; for (j = 0; j < MIPLEVELS; j++) tx->offsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); // the pixels immediately follow the structures memcpy ( tx+1, mt+1, pixels); #ifdef WAL_TEXTURES } #endif #if !defined (H2W) if (cls.state == ca_dedicated) continue; #endif if (!strncmp(mt->name,"sky",3)) R_InitSky (tx); else tx->gl_texturenum = GL_LoadTexture (mt->name, (byte *)(tx+1), tx->width, tx->height, TEX_MIPMAP); } // // sequence the animations // for (i = 0; i < m->nummiptex; i++) { tx = loadmodel->textures[i]; if (!tx || tx->name[0] != '+') continue; if (tx->anim_next) continue; // already sequenced // find the number of frames in the animation memset (anims, 0, sizeof(anims)); memset (altanims, 0, sizeof(altanims)); maxanim = tx->name[1]; altmax = 0; if (maxanim >= 'a' && maxanim <= 'z') maxanim -= 'a' - 'A'; if (maxanim >= '0' && maxanim <= '9') { maxanim -= '0'; altmax = 0; anims[maxanim] = tx; maxanim++; } else if (maxanim >= 'A' && maxanim <= 'J') { altmax = maxanim - 'A'; maxanim = 0; altanims[altmax] = tx; altmax++; } else Sys_Error ("Bad animating texture %s", tx->name); for (j = i+1; j < m->nummiptex; j++) { tx2 = loadmodel->textures[j]; if (!tx2 || tx2->name[0] != '+') continue; if (strcmp (tx2->name+2, tx->name+2)) continue; num = tx2->name[1]; if (num >= 'a' && num <= 'z') num -= 'a' - 'A'; if (num >= '0' && num <= '9') { num -= '0'; anims[num] = tx2; if (num+1 > maxanim) maxanim = num + 1; } else if (num >= 'A' && num <= 'J') { num = num - 'A'; altanims[num] = tx2; if (num+1 > altmax) altmax = num+1; } else Sys_Error ("Bad animating texture %s", tx->name); } #define ANIM_CYCLE 2 // link them all together for (j = 0; j < maxanim; j++) { tx2 = anims[j]; if (!tx2) Sys_Error ("Missing frame %i of %s",j, tx->name); tx2->anim_total = maxanim * ANIM_CYCLE; tx2->anim_min = j * ANIM_CYCLE; tx2->anim_max = (j+1) * ANIM_CYCLE; tx2->anim_next = anims[ (j+1)%maxanim ]; if (altmax) tx2->alternate_anims = altanims[0]; } for (j = 0; j < altmax; j++) { tx2 = altanims[j]; if (!tx2) Sys_Error ("Missing frame %i of %s",j, tx->name); tx2->anim_total = altmax * ANIM_CYCLE; tx2->anim_min = j * ANIM_CYCLE; tx2->anim_max = (j+1) * ANIM_CYCLE; tx2->anim_next = altanims[ (j+1)%altmax ]; if (maxanim) tx2->alternate_anims = anims[0]; } } } /* ================= Mod_ReloadTextures Pa3PyX: Analogous to Mod_LoadTextures() below, except that we already have all models and textures allocated, only need to rebind and re- upload them to OpenGL pipeline. Called when video mode is changed upon which all OpenGL textures are gone. ================= */ void Mod_ReloadTextures (void) { int j; qmodel_t *mod; texture_t *tx; // Reload world (brush models are submodels of world), // don't touch if not yet loaded mod = cl.worldmodel; if (mod && !mod->needload) { for (j = 0; j < mod->numtextures; j++) { tx = mod->textures[j]; if (tx) { if (!strncmp(tx->name, "sky", 3)) R_InitSky(tx); else tx->gl_texturenum = GL_LoadTexture (tx->name, (byte *)(tx+1), tx->width, tx->height, TEX_MIPMAP); } } } // Reload alias models and sprites for (j = 0; j < mod_numknown; j++) { if ((mod_known[j].type == mod_alias) && (mod_known[j].needload != NL_UNREFERENCED)) { if (Cache_Check(&(mod_known[j].cache))) Cache_Free(&(mod_known[j].cache)); mod_known[j].needload = NL_NEEDS_LOADED; Mod_LoadModel(mod_known + j, false); } else if ((mod_known[j].type == mod_sprite) && (mod_known[j].needload != NL_UNREFERENCED)) { mod_known[j].needload = NL_NEEDS_LOADED; spr_reload_only = true; Mod_LoadModel(mod_known + j, false); spr_reload_only = false; } } // Reload player skins if (cls.state == ca_active) { #ifdef H2W player_info_t *s; for (j = 0; j < MAX_CLIENTS; j++) { s = &cl.players[j]; if (!s->name[0] || s->spectator) continue; R_TranslatePlayerSkin(j); } #else for (j = 0; j < cl.maxclients && j < cl.num_entities + 1; j++) { R_TranslatePlayerSkin(j); } #endif } } /* ================= Mod_LoadLighting ================= */ static void Mod_LoadLighting (lump_t *l) { GL_SetupLightmapFmt(); // setup the lightmap format to reflect any // changes via the cvar gl_lightmapfmt // bound the gl_coloredlight value if (gl_coloredlight.integer < 0) Cvar_Set ("gl_coloredlight", "0"); gl_coloredstatic = gl_coloredlight.integer; if (gl_lightmap_format == GL_RGBA) { int i; byte *in, *out, *data; byte d; loadmodel->lightdata = NULL; if (gl_coloredlight.integer) { // LordHavoc: check for a .lit file int mark; char litfilename[MAX_QPATH]; unsigned int path_id; q_strlcpy(litfilename, loadmodel->name, sizeof(litfilename)); COM_StripExtension(litfilename, litfilename, sizeof(litfilename)); q_strlcat(litfilename, ".lit", sizeof(litfilename)); Con_DPrintf("trying to load %s\n", litfilename); mark = Hunk_LowMark(); data = (byte*) FS_LoadHunkFile (litfilename, &path_id); if (data == NULL) goto _load_internal; // use lit file only from the same gamedir as the map // itself or from a searchpath with higher priority. if (path_id < loadmodel->path_id) { Hunk_FreeToLowMark(mark); Con_DPrintf("ignored %s from a gamedir with lower priority\n", litfilename); goto _load_internal; } if (data[0] != 'Q' || data[1] != 'L' || data[2] != 'I' || data[3] != 'T') { Hunk_FreeToLowMark(mark); Con_Printf("Corrupt .lit file (old version?), ignoring\n"); goto _load_internal; } i = LittleLong(((int *)data)[1]); if (i != 1) { Hunk_FreeToLowMark(mark); Con_Printf("Unknown .lit file version (%d)\n", i); goto _load_internal; } Con_DPrintf("%s loaded\n", litfilename); Con_DPrintf("Loaded colored light (32-bit)\n"); if (gl_coloredlight.integer == 1) { loadmodel->lightdata = data + 8; return; } else if (!l->filelen) { loadmodel->lightdata = data + 8; Con_Printf("No white light data. Using colored only\n"); return; } else // experimental blend code for gl_coloredlight.integer == 2 { int min_light = 8; int k = 0; int j, r, g, b; float l2lc = 0; float lc = 0; float li = 0; // allocate memory and load light data from .bsp mark = Hunk_LowMark(); loadmodel->lightdata = (byte *) Hunk_AllocName (l->filelen, "light"); memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); for (i = 0, j = 0, k = 0; i < l->filelen * 3; i += 3, j += 3) { // set some minimal light level r = q_max(data[8+i ], min_light); g = q_max(data[8+i+1], min_light); b = q_max(data[8+i+2], min_light); // compute brightness of colored ligths present in .lit file lc = (r + g + b) / 3.0f; li = (float) loadmodel->lightdata[k]; if (li == 0) li = min_light; if (lc == 0) lc = min_light; // compute light amplification level l2lc = li / lc; if (l2lc < 1.5f) l2lc = 1.0f; // update colors data[8+j] = (byte) q_min (q_max(ceil(r*l2lc), min_light), 255); data[8+j+1] = (byte) q_min (q_max(ceil(g*l2lc), min_light), 255); data[8+j+2] = (byte) q_min (q_max(ceil(b*l2lc), min_light), 255); k++; } Hunk_FreeToLowMark(mark); loadmodel->lightdata = data + 8; Con_DPrintf("Blended colored and white light.\n"); return; } } _load_internal: // no .lit found, expand the white lighting data to color if (!l->filelen) return; loadmodel->lightdata = (byte *) Hunk_AllocName (l->filelen*3, "light"); in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write out = loadmodel->lightdata; memcpy (in, mod_base + l->fileofs, l->filelen); for (i = 0; i < l->filelen; i++) { d = *in++; *out++ = d; *out++ = d; *out++ = d; } Con_DPrintf("Loaded white light (32-bit)\n"); } else { if (!l->filelen) { loadmodel->lightdata = NULL; return; } loadmodel->lightdata = (byte *) Hunk_AllocName ( l->filelen, "light"); memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); Con_DPrintf("Loaded white light (8-bit)\n"); } } /* ================= Mod_LoadVisibility ================= */ static void Mod_LoadVisibility (lump_t *l) { if (!l->filelen) { loadmodel->visdata = NULL; return; } loadmodel->visdata = (byte *) Hunk_AllocName ( l->filelen, "vis"); memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); } /* ================= Mod_LoadEntities ================= */ static void Mod_LoadEntities (lump_t *l) { char entfilename[MAX_QPATH]; char *ents; int mark; unsigned int path_id; if (! external_ents.integer) goto _load_embedded; q_strlcpy(entfilename, loadmodel->name, sizeof(entfilename)); COM_StripExtension(entfilename, entfilename, sizeof(entfilename)); q_strlcat(entfilename, ".ent", sizeof(entfilename)); Con_DPrintf("trying to load %s\n", entfilename); mark = Hunk_LowMark(); ents = (char *) FS_LoadHunkFile (entfilename, &path_id); if (ents) { // use ent file only from the same gamedir as the map // itself or from a searchpath with higher priority. if (path_id < loadmodel->path_id) { Hunk_FreeToLowMark(mark); Con_DPrintf("ignored %s from a gamedir with lower priority\n", entfilename); } else { #ifndef H2W entity_file_size = fs_filesize; #endif loadmodel->entities = ents; Con_DPrintf("Loaded external entity file %s\n", entfilename); return; } } _load_embedded: if (!l->filelen) { loadmodel->entities = NULL; return; } loadmodel->entities = (char *) Hunk_AllocName ( l->filelen, "entities"); memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); #ifndef H2W entity_file_size = l->filelen; #endif } /* ================= Mod_LoadVertexes ================= */ static void Mod_LoadVertexes (lump_t *l) { dvertex_t *in; mvertex_t *out; int i, count; in = (dvertex_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mvertex_t *) Hunk_AllocName (count * sizeof(*out), "vertexes"); loadmodel->vertexes = out; loadmodel->numvertexes = count; for (i = 0; i < count; i++, in++, out++) { out->position[0] = LittleFloat (in->point[0]); out->position[1] = LittleFloat (in->point[1]); out->position[2] = LittleFloat (in->point[2]); } } /* ================= Mod_LoadSubmodels ================= */ static void Mod_LoadSubmodels (lump_t *l) { dmodel_t *in; dmodel_t *out; int i, j, count; in = (dmodel_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (dmodel_t *) Hunk_AllocName (count * sizeof(*out), "submodels"); loadmodel->submodels = out; loadmodel->numsubmodels = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { // spread the mins / maxs by a pixel out->mins[j] = LittleFloat (in->mins[j]) - 1; out->maxs[j] = LittleFloat (in->maxs[j]) + 1; out->origin[j] = LittleFloat (in->origin[j]); } for (j = 0; j < MAX_MAP_HULLS; j++) out->headnode[j] = LittleLong (in->headnode[j]); out->visleafs = LittleLong (in->visleafs); out->firstface = LittleLong (in->firstface); out->numfaces = LittleLong (in->numfaces); } } /* ================= Mod_LoadEdges ================= */ #ifndef ENABLE_BSP2 #define Mod_LoadEdges(_l, _v) Mod_LoadEdges_V29((_l)) #else static void Mod_LoadEdges_V29 (lump_t *l); static void Mod_LoadEdges_BSP2(lump_t *l); static void Mod_LoadEdges (lump_t *l, qboolean bsp2) { if (bsp2) Mod_LoadEdges_BSP2(l); else Mod_LoadEdges_V29 (l); } static void Mod_LoadEdges_BSP2(lump_t *l) { dedge2_t *in; medge_t *out; int i, count; in = (dedge2_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (medge_t *) Hunk_AllocName ((count + 1) * sizeof(*out), "edges"); loadmodel->edges = out; loadmodel->numedges = count; for (i = 0; i < count; i++, in++, out++) { out->v[0] = (unsigned int)LittleLong(in->v[0]); out->v[1] = (unsigned int)LittleLong(in->v[1]); } } #endif static void Mod_LoadEdges_V29 (lump_t *l) { dedge_t *in; medge_t *out; int i, count; in = (dedge_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (medge_t *) Hunk_AllocName ((count + 1) * sizeof(*out), "edges"); loadmodel->edges = out; loadmodel->numedges = count; for (i = 0; i < count; i++, in++, out++) { out->v[0] = (unsigned short)LittleShort(in->v[0]); out->v[1] = (unsigned short)LittleShort(in->v[1]); } } /* ================= Mod_LoadTexinfo ================= */ static void Mod_LoadTexinfo (lump_t *l) { texinfo_t *in; mtexinfo_t *out; int i, j, count; int miptex; in = (texinfo_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mtexinfo_t *) Hunk_AllocName (count * sizeof(*out), "texture"); loadmodel->texinfo = out; loadmodel->numtexinfo = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 4; j++) { out->vecs[0][j] = LittleFloat (in->vecs[0][j]); out->vecs[1][j] = LittleFloat (in->vecs[1][j]); } miptex = LittleLong (in->miptex); out->flags = LittleLong (in->flags); if (!loadmodel->textures) { out->texture = r_notexture_mip; // checkerboard texture out->flags = 0; } else { if (miptex >= loadmodel->numtextures) Sys_Error ("miptex >= loadmodel->numtextures"); out->texture = loadmodel->textures[miptex]; if (!out->texture) { out->texture = r_notexture_mip; // texture not found out->flags = 0; } } } } /* ================ CalcSurfaceExtents Fills in s->texturemins[] and s->extents[] ================ */ static void CalcSurfaceExtents (msurface_t *s) { float mins[2], maxs[2], val; int i, j, e; mvertex_t *v; mtexinfo_t *tex; int bmins[2], bmaxs[2]; mins[0] = mins[1] = 9999999; /* FIXME: change these two to FLT_MAX/-FLT_MAX */ maxs[0] = maxs[1] = -9999999; tex = s->texinfo; for (i = 0; i < s->numedges; i++) { e = loadmodel->surfedges[s->firstedge+i]; if (e >= 0) v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; else v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; for (j = 0; j < 2; j++) { /* added double casts so that 64 bit/sse2 builds' precision * matches that of x87 floating point. took from QuakeSpasm, * patch by Eric Wasylishen. */ val = ((double)v->position[0] * (double)tex->vecs[j][0]) + ((double)v->position[1] * (double)tex->vecs[j][1]) + ((double)v->position[2] * (double)tex->vecs[j][2]) + (double)tex->vecs[j][3]; if (val < mins[j]) mins[j] = val; if (val > maxs[j]) maxs[j] = val; } } for (i = 0; i < 2; i++) { bmins[i] = (int) floor(mins[i]/16); bmaxs[i] = (int) ceil(maxs[i]/16); s->texturemins[i] = bmins[i] * 16; s->extents[i] = (bmaxs[i] - bmins[i]) * 16; if ( !(tex->flags & TEX_SPECIAL) && (s->extents[i]>>4)+1 > MAX_SURFACE_LIGHTMAP /* 256 */ ) Sys_Error ("Bad surface extents"); } } /* ================= Mod_LoadFaces ================= */ static void Mod_SetDrawingFlags(msurface_t *out); #ifndef ENABLE_BSP2 #define Mod_LoadFaces(_l, _v) Mod_LoadFaces_V29((_l)) #else static void Mod_LoadFaces_BSP2(lump_t *l); static void Mod_LoadFaces_V29 (lump_t *l); static void Mod_LoadFaces (lump_t *l, qboolean bsp2) { if (bsp2) Mod_LoadFaces_BSP2(l); else Mod_LoadFaces_V29 (l); } static void Mod_LoadFaces_BSP2(lump_t *l) { dface2_t *in; msurface_t *out; int i, count, surfnum; int planenum, side; in = (dface2_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (msurface_t *) Hunk_AllocName (count * sizeof(*out), "faces"); loadmodel->surfaces = out; loadmodel->numsurfaces = count; for (surfnum = 0; surfnum < count; surfnum++, in++, out++) { out->firstedge = LittleLong(in->firstedge); out->numedges = LittleLong(in->numedges); out->flags = 0; planenum = LittleLong(in->planenum); side = LittleLong(in->side); if (side) out->flags |= SURF_PLANEBACK; out->plane = loadmodel->planes + planenum; out->texinfo = loadmodel->texinfo + LittleLong (in->texinfo); CalcSurfaceExtents (out); // lighting info for (i = 0; i < MAXLIGHTMAPS; i++) out->styles[i] = in->styles[i]; i = LittleLong(in->lightofs); if (i == -1) { out->samples = NULL; } else { //out->samples = loadmodel->lightdata + i; if (gl_lightmap_format == GL_RGBA) out->samples = loadmodel->lightdata + (i * 3); else out->samples = loadmodel->lightdata + i; } // set the drawing flags flag Mod_SetDrawingFlags(out); } } #endif static void Mod_LoadFaces_V29 (lump_t *l) { dface_t *in; msurface_t *out; int i, count, surfnum; int planenum, side; in = (dface_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (msurface_t *) Hunk_AllocName (count * sizeof(*out), "faces"); loadmodel->surfaces = out; loadmodel->numsurfaces = count; for (surfnum = 0; surfnum < count; surfnum++, in++, out++) { out->firstedge = LittleLong(in->firstedge); out->numedges = LittleShort(in->numedges); out->flags = 0; planenum = LittleShort(in->planenum); side = LittleShort(in->side); if (side) out->flags |= SURF_PLANEBACK; out->plane = loadmodel->planes + planenum; out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo); CalcSurfaceExtents (out); // lighting info for (i = 0; i < MAXLIGHTMAPS; i++) out->styles[i] = in->styles[i]; i = LittleLong(in->lightofs); if (i == -1) { out->samples = NULL; } else { //out->samples = loadmodel->lightdata + i; if (gl_lightmap_format == GL_RGBA) out->samples = loadmodel->lightdata + (i * 3); else out->samples = loadmodel->lightdata + i; } // set the drawing flags flag Mod_SetDrawingFlags(out); } } static void Mod_SetDrawingFlags(msurface_t *out) { int i; if (!strncmp(out->texinfo->texture->name,"sky",3)) // sky { out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); #ifndef QUAKE2 GL_SubdivideSurface (loadmodel, out); // cut up polygon for warps #endif return; } if (out->texinfo->texture->name[0] == '*') // turbulent { out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); for (i = 0; i < 2; i++) { out->extents[i] = 16384; out->texturemins[i] = -8192; } GL_SubdivideSurface (loadmodel, out); // cut up polygon for warps if (!q_strncasecmp(out->texinfo->texture->name,"*rtex078",8) || !q_strncasecmp(out->texinfo->texture->name,"*lowlight",9) ) out->flags |= SURF_TRANSLUCENT; return; } } /* ================= Mod_SetParent ================= */ static void Mod_SetParent (mnode_t *node, mnode_t *parent) { node->parent = parent; if (node->contents < 0) return; Mod_SetParent (node->children[0], node); Mod_SetParent (node->children[1], node); } /* ================= Mod_LoadNodes ================= */ #ifndef ENABLE_BSP2 #define Mod_LoadNodes(_l, _v) Mod_LoadNodes_V29((_l)) #else static void Mod_LoadNodes_BSP2(lump_t *l); static void Mod_LoadNodes_V29 (lump_t *l); static void Mod_LoadNodes (lump_t *l, qboolean bsp2) { if (bsp2) Mod_LoadNodes_BSP2(l); else Mod_LoadNodes_V29 (l); } static void Mod_LoadNodes_BSP2(lump_t *l) { int i, j, count, p; dnode2_t *in; mnode_t *out; in = (dnode2_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mnode_t *) Hunk_AllocName (count * sizeof(*out), "nodes"); loadmodel->nodes = out; loadmodel->numnodes = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { out->minmaxs[j] = LittleFloat (in->mins[j]); out->minmaxs[3+j] = LittleFloat (in->maxs[j]); } p = LittleLong(in->planenum); out->plane = loadmodel->planes + p; out->firstsurface = LittleLong (in->firstface); out->numsurfaces = LittleLong (in->numfaces); for (j = 0; j < 2; j++) { p = LittleLong (in->children[j]); if (p >= 0) out->children[j] = loadmodel->nodes + p; else out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); } } Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs } #endif static void Mod_LoadNodes_V29 (lump_t *l) { int i, j, count, p; dnode_t *in; mnode_t *out; in = (dnode_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mnode_t *) Hunk_AllocName (count * sizeof(*out), "nodes"); loadmodel->nodes = out; loadmodel->numnodes = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { out->minmaxs[j] = LittleShort (in->mins[j]); out->minmaxs[3+j] = LittleShort (in->maxs[j]); } p = LittleLong(in->planenum); out->plane = loadmodel->planes + p; out->firstsurface = LittleShort (in->firstface); out->numsurfaces = LittleShort (in->numfaces); for (j = 0; j < 2; j++) { p = LittleShort (in->children[j]); if (p >= 0) out->children[j] = loadmodel->nodes + p; else out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); } } Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs } /* ================= Mod_LoadLeafs ================= */ #ifdef H2W static qboolean Mod_isnotmap(void) { char s[80]; q_snprintf (s, sizeof(s), "maps/%s.bsp", Info_ValueForKey(cl.serverinfo,"map")); return (!strcmp(s, loadmodel->name)) ? false : true; } #endif #ifndef ENABLE_BSP2 #define Mod_LoadLeafs(_l, _v) Mod_LoadLeafs_V29((_l)) #else static void Mod_LoadLeafs_BSP2(lump_t *l); static void Mod_LoadLeafs_V29 (lump_t *l); static void Mod_LoadLeafs (lump_t *l, qboolean bsp2) { if (bsp2) Mod_LoadLeafs_BSP2(l); else Mod_LoadLeafs_V29 (l); } static void Mod_LoadLeafs_BSP2(lump_t *l) { dleaf2_t *in; mleaf_t *out; int i, j, count, p; #ifdef H2W qboolean isnotmap = Mod_isnotmap(); #endif in = (dleaf2_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mleaf_t *) Hunk_AllocName (count * sizeof(*out), "leafs"); loadmodel->leafs = out; loadmodel->numleafs = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { out->minmaxs[j] = LittleFloat (in->mins[j]); out->minmaxs[3+j] = LittleFloat (in->maxs[j]); } p = LittleLong(in->contents); out->contents = p; out->firstmarksurface = loadmodel->marksurfaces + LittleLong(in->firstmarksurface); out->nummarksurfaces = LittleLong(in->nummarksurfaces); p = LittleLong(in->visofs); if (p == -1) out->compressed_vis = NULL; else out->compressed_vis = loadmodel->visdata + p; out->efrags = NULL; for (j = 0; j < 4; j++) out->ambient_sound_level[j] = in->ambient_level[j]; // gl underwater warp if (out->contents != CONTENTS_EMPTY) { for (j = 0; j < out->nummarksurfaces; j++) out->firstmarksurface[j]->flags |= SURF_UNDERWATER; } #ifdef H2W if (isnotmap) { for (j = 0; j < out->nummarksurfaces; j++) out->firstmarksurface[j]->flags |= SURF_DONTWARP; } #endif } } #endif static void Mod_LoadLeafs_V29 (lump_t *l) { dleaf_t *in; mleaf_t *out; int i, j, count, p; #ifdef H2W qboolean isnotmap = Mod_isnotmap(); #endif in = (dleaf_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mleaf_t *) Hunk_AllocName (count * sizeof(*out), "leafs"); loadmodel->leafs = out; loadmodel->numleafs = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { out->minmaxs[j] = LittleShort (in->mins[j]); out->minmaxs[3+j] = LittleShort (in->maxs[j]); } p = LittleLong(in->contents); out->contents = p; out->firstmarksurface = loadmodel->marksurfaces + LittleShort(in->firstmarksurface); out->nummarksurfaces = LittleShort(in->nummarksurfaces); p = LittleLong(in->visofs); if (p == -1) out->compressed_vis = NULL; else out->compressed_vis = loadmodel->visdata + p; out->efrags = NULL; for (j = 0; j < 4; j++) out->ambient_sound_level[j] = in->ambient_level[j]; // gl underwater warp if (out->contents != CONTENTS_EMPTY) { for (j = 0; j < out->nummarksurfaces; j++) out->firstmarksurface[j]->flags |= SURF_UNDERWATER; } #ifdef H2W if (isnotmap) { for (j = 0; j < out->nummarksurfaces; j++) out->firstmarksurface[j]->flags |= SURF_DONTWARP; } #endif } } /* ================= Mod_LoadClipnodes ================= */ static void Mod_MakeHulls (int count); #ifndef ENABLE_BSP2 #define Mod_LoadClipnodes(_l, _v) Mod_LoadClipnodes_V29((_l)) #else static void Mod_LoadClipnodes_BSP2(lump_t *l); static void Mod_LoadClipnodes_V29 (lump_t *l); static void Mod_LoadClipnodes (lump_t *l, qboolean bsp2) { if (bsp2) Mod_LoadClipnodes_BSP2(l); else Mod_LoadClipnodes_V29 (l); } static void Mod_LoadClipnodes_BSP2(lump_t *l) { dclipnode2_t *in; mclipnode_t *out; int i, count; in = (dclipnode2_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mclipnode_t *) Hunk_AllocName (count * sizeof(*out), "clipnodes"); loadmodel->clipnodes = out; loadmodel->numclipnodes = count; for (i = 0; i < count; i++, out++, in++) { out->planenum = LittleLong(in->planenum); out->children[0] = LittleLong(in->children[0]); out->children[1] = LittleLong(in->children[1]); } Mod_MakeHulls(count); } #endif static void Mod_LoadClipnodes_V29 (lump_t *l) { dclipnode_t *in; mclipnode_t *out; int i, count; in = (dclipnode_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mclipnode_t *) Hunk_AllocName (count * sizeof(*out), "clipnodes"); loadmodel->clipnodes = out; loadmodel->numclipnodes = count; for (i = 0; i < count; i++, out++, in++) { out->planenum = LittleLong(in->planenum); out->children[0] = LittleShort(in->children[0]); out->children[1] = LittleShort(in->children[1]); } Mod_MakeHulls(count); } static void Mod_MakeHulls (int count) { hull_t *hull; //player hull = &loadmodel->hulls[1]; hull->clipnodes = loadmodel->clipnodes; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; hull->clip_mins[0] = -16; hull->clip_mins[1] = -16; hull->clip_mins[2] = -24; hull->clip_maxs[0] = 16; hull->clip_maxs[1] = 16; hull->clip_maxs[2] = 32; //scorpion hull = &loadmodel->hulls[2]; hull->clipnodes = loadmodel->clipnodes; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; hull->clip_mins[0] = -24; hull->clip_mins[1] = -24; hull->clip_mins[2] = -20; hull->clip_maxs[0] = 24; hull->clip_maxs[1] = 24; hull->clip_maxs[2] = 20; //crouch hull = &loadmodel->hulls[3]; hull->clipnodes = loadmodel->clipnodes; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; hull->clip_mins[0] = -16; hull->clip_mins[1] = -16; hull->clip_mins[2] = -12; hull->clip_maxs[0] = 16; hull->clip_maxs[1] = 16; hull->clip_maxs[2] = 16; //hydra -changing in MP to '-8 -8 -8', '8 8 8' for pentacles hull = &loadmodel->hulls[4]; hull->clipnodes = loadmodel->clipnodes; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; #ifdef H2W hull->clip_mins[0] = -40; hull->clip_mins[1] = -40; hull->clip_mins[2] = -42; hull->clip_maxs[0] = 40; hull->clip_maxs[1] = 40; hull->clip_maxs[2] = 42; #else hull->clip_mins[0] = -8; hull->clip_mins[1] = -8; hull->clip_mins[2] = -8; hull->clip_maxs[0] = 8; hull->clip_maxs[1] = 8; hull->clip_maxs[2] = 8; #endif //golem - maybe change to '-28 -28 -40', '28 28 40' for Yakman hull = &loadmodel->hulls[5]; hull->clipnodes = loadmodel->clipnodes; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; #if 0 //use yak sizes hull->clip_mins[0] = -28; hull->clip_mins[1] = -28; hull->clip_mins[2] = -40; hull->clip_maxs[0] = 28; hull->clip_maxs[1] = 28; hull->clip_maxs[2] = 40; #else hull->clip_mins[0] = -48; hull->clip_mins[1] = -48; hull->clip_mins[2] = -50; hull->clip_maxs[0] = 48; hull->clip_maxs[1] = 48; hull->clip_maxs[2] = 50; #endif } /* ================= Mod_MakeHull0 Duplicate the drawing hull structure as a clipping hull ================= */ static void Mod_MakeHull0 (void) { mnode_t *in, *child; mclipnode_t *out; int i, j, count; hull_t *hull; hull = &loadmodel->hulls[0]; in = loadmodel->nodes; count = loadmodel->numnodes; out = (mclipnode_t *) Hunk_AllocName (count * sizeof(*out), "hull0"); hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; for (i = 0; i < count; i++, out++, in++) { out->planenum = in->plane - loadmodel->planes; for (j = 0; j < 2; j++) { child = in->children[j]; if (child->contents < 0) out->children[j] = child->contents; else out->children[j] = child - loadmodel->nodes; } } } /* ================= Mod_LoadMarksurfaces ================= */ #ifndef ENABLE_BSP2 #define Mod_LoadMarksurfaces(_l, _v) Mod_LoadMarksurfaces_V29((_l)) #else static void Mod_LoadMarksurfaces_BSP2(lump_t *l); static void Mod_LoadMarksurfaces_V29 (lump_t *l); static void Mod_LoadMarksurfaces (lump_t *l, qboolean bsp2) { if (bsp2) Mod_LoadMarksurfaces_BSP2(l); else Mod_LoadMarksurfaces_V29 (l); } static void Mod_LoadMarksurfaces_BSP2(lump_t *l) { int i, j, count; int *in; msurface_t **out; in = (int *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (msurface_t **) Hunk_AllocName (count * sizeof(*out), "marksurfaces"); loadmodel->marksurfaces = out; loadmodel->nummarksurfaces = count; for (i = 0; i < count; i++) { j = LittleLong(in[i]); if (j >= loadmodel->numsurfaces) Sys_Error ("%s: bad surface number", __thisfunc__); out[i] = loadmodel->surfaces + j; } } #endif static void Mod_LoadMarksurfaces_V29 (lump_t *l) { int i, j, count; unsigned short *in; msurface_t **out; in = (unsigned short *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (msurface_t **) Hunk_AllocName (count * sizeof(*out), "marksurfaces"); loadmodel->marksurfaces = out; loadmodel->nummarksurfaces = count; for (i = 0; i < count; i++) { j = (unsigned short)LittleShort(in[i]); if (j >= loadmodel->numsurfaces) Sys_Error ("%s: bad surface number", __thisfunc__); out[i] = loadmodel->surfaces + j; } } /* ================= Mod_LoadSurfedges ================= */ static void Mod_LoadSurfedges (lump_t *l) { int i, count; int *in, *out; in = (int *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (int *) Hunk_AllocName (count * sizeof(*out), "surfedges"); loadmodel->surfedges = out; loadmodel->numsurfedges = count; for (i = 0; i < count; i++) out[i] = LittleLong (in[i]); } /* ================= Mod_LoadPlanes ================= */ static void Mod_LoadPlanes (lump_t *l) { int i, j; mplane_t *out; dplane_t *in; int count; int bits; in = (dplane_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mplane_t *) Hunk_AllocName (count * 2 * sizeof(*out), "planes"); loadmodel->planes = out; loadmodel->numplanes = count; for (i = 0; i < count; i++, in++, out++) { bits = 0; for (j = 0; j < 3; j++) { out->normal[j] = LittleFloat (in->normal[j]); if (out->normal[j] < 0) bits |= 1<dist = LittleFloat (in->dist); out->type = LittleLong (in->type); out->signbits = bits; } } /* ================= RadiusFromBounds ================= */ static float RadiusFromBounds (const vec3_t mins, const vec3_t maxs) { int i; vec3_t corner; vec_t a, b; for (i = 0; i < 3; i++) { b = fabs(maxs[i]); a = fabs(mins[i]); corner[i] = (a > b)? a : b; } return VectorLength (corner); } /* ================= Mod_LoadBrushModel ================= */ static void Mod_LoadBrushModel (qmodel_t *mod, void *buffer) { int i, j; dheader_t *header; dmodel_t *bm; qboolean bsp2 = false; loadmodel->type = mod_brush; header = (dheader_t *)buffer; i = LittleLong (header->version); #ifndef ENABLE_BSP2 (void) bsp2; #else if (i == BSP2VERSION) bsp2 = true; else #endif if (i != BSPVERSION) Sys_Error ("%s: %s has unsupported version %i", __thisfunc__, mod->name, i); // swap all the lumps mod_base = (byte *)header; for (i = 0; i < (int) sizeof(dheader_t) / 4; i++) ((int *)header)[i] = LittleLong ( ((int *)header)[i]); // load into heap Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); Mod_LoadEdges (&header->lumps[LUMP_EDGES], bsp2); Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]); Mod_LoadPlanes (&header->lumps[LUMP_PLANES]); Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]); Mod_LoadFaces (&header->lumps[LUMP_FACES], bsp2); Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES], bsp2); Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); Mod_LoadLeafs (&header->lumps[LUMP_LEAFS], bsp2); Mod_LoadNodes (&header->lumps[LUMP_NODES], bsp2); Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES], bsp2); Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); Mod_MakeHull0 (); mod->numframes = 2; // regular and alternate animation // // set up the submodels (FIXME: this is confusing) // for (i = 0; i < mod->numsubmodels; i++) { bm = &mod->submodels[i]; mod->hulls[0].firstclipnode = bm->headnode[0]; for (j = 1; j < MAX_MAP_HULLS; j++) { mod->hulls[j].firstclipnode = bm->headnode[j]; mod->hulls[j].lastclipnode = mod->numclipnodes-1; } mod->firstmodelsurface = bm->firstface; mod->nummodelsurfaces = bm->numfaces; VectorCopy (bm->maxs, mod->maxs); VectorCopy (bm->mins, mod->mins); mod->radius = RadiusFromBounds (mod->mins, mod->maxs); mod->numleafs = bm->visleafs; if (i < mod->numsubmodels-1) { // duplicate the basic information char name[10]; q_snprintf (name, sizeof(name), "*%i", i+1); loadmodel = Mod_FindName (name); *loadmodel = *mod; strcpy (loadmodel->name, name); mod = loadmodel; } } } /* ============================================================================== ALIAS MODELS ============================================================================== */ aliashdr_t *pheader; stvert_t stverts[MAXALIASVERTS]; mtriangle_t triangles[MAXALIASTRIS]; // a pose is a single set of vertexes. a frame may be // an animating sequence of poses trivertx_t *poseverts[MAXALIASFRAMES]; static int posenum; byte player_8bit_texels[MAX_PLAYER_CLASS][620*245]; static float aliastransform[3][4]; static void R_AliasTransformVector (vec3_t in, vec3_t out) { out[0] = DotProduct(in, aliastransform[0]) + aliastransform[0][3]; out[1] = DotProduct(in, aliastransform[1]) + aliastransform[1][3]; out[2] = DotProduct(in, aliastransform[2]) + aliastransform[2][3]; } /* ================= Mod_LoadAliasFrame ================= */ static void *Mod_LoadAliasFrame (void *pin, maliasframedesc_t *frame) { trivertx_t *pinframe; int i, j; daliasframe_t *pdaliasframe; vec3_t in, out; pdaliasframe = (daliasframe_t *)pin; strcpy (frame->name, pdaliasframe->name); frame->firstpose = posenum; frame->numposes = 1; for (i = 0; i < 3; i++) { // these are byte values, so we don't have to worry about // endianness frame->bboxmin.v[i] = pdaliasframe->bboxmin.v[i]; frame->bboxmax.v[i] = pdaliasframe->bboxmax.v[i]; } pinframe = (trivertx_t *)(pdaliasframe + 1); aliastransform[0][0] = pheader->scale[0]; aliastransform[1][1] = pheader->scale[1]; aliastransform[2][2] = pheader->scale[2]; aliastransform[0][3] = pheader->scale_origin[0]; aliastransform[1][3] = pheader->scale_origin[1]; aliastransform[2][3] = pheader->scale_origin[2]; for (j = 0; j < pheader->numverts; j++) { in[0] = pinframe[j].v[0]; in[1] = pinframe[j].v[1]; in[2] = pinframe[j].v[2]; R_AliasTransformVector(in, out); for (i = 0; i < 3; i++) { if (aliasmins[i] > out[i]) aliasmins[i] = out[i]; if (aliasmaxs[i] < out[i]) aliasmaxs[i] = out[i]; } } poseverts[posenum] = pinframe; posenum++; pinframe += pheader->numverts; return (void *)pinframe; } /* ================= Mod_LoadAliasGroup ================= */ static void *Mod_LoadAliasGroup (void *pin, maliasframedesc_t *frame) { daliasgroup_t *pingroup; int i, j, k, numframes; daliasinterval_t *pin_intervals; void *ptemp; vec3_t in, out; pingroup = (daliasgroup_t *)pin; numframes = LittleLong (pingroup->numframes); frame->firstpose = posenum; frame->numposes = numframes; for (i = 0; i < 3; i++) { // these are byte values, so we don't have to worry about endianness frame->bboxmin.v[i] = pingroup->bboxmin.v[i]; frame->bboxmax.v[i] = pingroup->bboxmax.v[i]; } pin_intervals = (daliasinterval_t *)(pingroup + 1); frame->interval = LittleFloat (pin_intervals->interval); pin_intervals += numframes; ptemp = (void *)pin_intervals; aliastransform[0][0] = pheader->scale[0]; aliastransform[1][1] = pheader->scale[1]; aliastransform[2][2] = pheader->scale[2]; aliastransform[0][3] = pheader->scale_origin[0]; aliastransform[1][3] = pheader->scale_origin[1]; aliastransform[2][3] = pheader->scale_origin[2]; for (i = 0; i < numframes; i++) { poseverts[posenum] = (trivertx_t *)((daliasframe_t *)ptemp + 1); for (j = 0; j < pheader->numverts; j++) { in[0] = poseverts[posenum][j].v[0]; in[1] = poseverts[posenum][j].v[1]; in[2] = poseverts[posenum][j].v[2]; R_AliasTransformVector(in, out); for (k = 0; k < 3; k++) { if (aliasmins[k] > out[k]) aliasmins[k] = out[k]; if (aliasmaxs[k] < out[k]) aliasmaxs[k] = out[k]; } } posenum++; ptemp = (trivertx_t *)((daliasframe_t *)ptemp + 1) + pheader->numverts; } return ptemp; } //========================================================= /* ================= Mod_FloodFillSkin Fill background pixels so mipmapping doesn't have haloes - Ed ================= */ typedef struct { short x, y; } floodfill_t; // must be a power of 2 #define FLOODFILL_FIFO_SIZE 0x1000 #define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1) #define FLOODFILL_STEP( off, dx, dy ) \ do { \ if (pos[(off)] == fillcolor) \ { \ pos[(off)] = 255; \ fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \ inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \ } \ else if (pos[(off)] != 255) \ fdc = pos[(off)]; \ } while (0) static void Mod_FloodFillSkin (byte *skin, int skinwidth, int skinheight) { byte fillcolor = *skin; // assume this is the pixel to fill floodfill_t fifo[FLOODFILL_FIFO_SIZE]; int inpt = 0, outpt = 0; int filledcolor = -1; int i; if (filledcolor == -1) { filledcolor = 0; // attempt to find opaque black for (i = 0; i < 256; ++i) { if (d_8to24table[i] == (255 << 0)) // alpha 1.0 { filledcolor = i; break; } } } // can't fill to filled color or to transparent color (used as visited marker) if ((fillcolor == filledcolor) || (fillcolor == 255)) { //printf( "not filling skin from %d to %d\n", fillcolor, filledcolor ); return; } fifo[inpt].x = 0, fifo[inpt].y = 0; inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; while (outpt != inpt) { int x = fifo[outpt].x, y = fifo[outpt].y; int fdc = filledcolor; byte *pos = &skin[x + skinwidth * y]; outpt = (outpt + 1) & FLOODFILL_FIFO_MASK; if (x > 0) { FLOODFILL_STEP( -1, -1, 0 ); } if (x < skinwidth - 1) { FLOODFILL_STEP( 1, 1, 0 ); } if (y > 0) { FLOODFILL_STEP( -skinwidth, 0, -1 ); } if (y < skinheight - 1) { FLOODFILL_STEP( skinwidth, 0, 1 ); } skin[x + skinwidth * y] = fdc; } } /* =============== Mod_LoadAllSkins =============== */ static void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype, int mdl_flags) { int i, j, k; char name[MAX_QPATH]; int s; byte *skin; int tex_mode; int groupskins; daliasskingroup_t *pinskingroup; daliasskininterval_t *pinskinintervals; skin = (byte *)(pskintype + 1); if (numskins < 1 || numskins > MAX_SKINS) Sys_Error ("%s: Invalid # of skins: %d", __thisfunc__, numskins); s = pheader->skinwidth * pheader->skinheight; tex_mode = TEX_DEFAULT | TEX_MIPMAP; if (mdl_flags & EF_TRANSPARENT) tex_mode |= TEX_TRANSPARENT; else if (mdl_flags & EF_HOLEY) tex_mode |= TEX_HOLEY; else if (mdl_flags & EF_SPECIAL_TRANS) tex_mode |= TEX_SPECIAL_TRANS; for (i = 0; i < numskins; i++) { k = LittleLong (pskintype->type); /* aliasskintype_t */ if (k == ALIAS_SKIN_SINGLE) { Mod_FloodFillSkin (skin, pheader->skinwidth, pheader->skinheight); // save 8 bit texels for the player model to remap if (!strcmp(loadmodel->name,"models/paladin.mdl")) { if (s > (int) sizeof(player_8bit_texels[0])) goto skin_too_large; memcpy (player_8bit_texels[0], (byte *)(pskintype + 1), s); } else if (!strcmp(loadmodel->name,"models/crusader.mdl")) { if (s > (int) sizeof(player_8bit_texels[1])) goto skin_too_large; memcpy (player_8bit_texels[1], (byte *)(pskintype + 1), s); } else if (!strcmp(loadmodel->name,"models/necro.mdl")) { if (s > (int) sizeof(player_8bit_texels[2])) goto skin_too_large; memcpy (player_8bit_texels[2], (byte *)(pskintype + 1), s); } else if (!strcmp(loadmodel->name,"models/assassin.mdl")) { if (s > (int) sizeof(player_8bit_texels[3])) goto skin_too_large; memcpy (player_8bit_texels[3], (byte *)(pskintype + 1), s); } else if (!strcmp(loadmodel->name,"models/succubus.mdl")) { if (s > (int) sizeof(player_8bit_texels[4])) goto skin_too_large; memcpy (player_8bit_texels[4], (byte *)(pskintype + 1), s); } #if defined(H2W) else if (!strcmp(loadmodel->name,"models/hank.mdl")) { if (s > (int) sizeof(player_8bit_texels[5])) goto skin_too_large; memcpy (player_8bit_texels[5], (byte *)(pskintype + 1), s); } #endif q_snprintf (name, sizeof(name), "%s_%i", loadmodel->name, i); pheader->gl_texturenum[i][0] = pheader->gl_texturenum[i][1] = pheader->gl_texturenum[i][2] = pheader->gl_texturenum[i][3] = GL_LoadTexture (name, (byte *)(pskintype + 1), pheader->skinwidth, pheader->skinheight, tex_mode); pskintype = (daliasskintype_t *)((byte *)(pskintype+1) + s); } else /*if (k == ALIAS_SKIN_GROUP)*/ { /* animating skin group. yuck. */ pskintype++; pinskingroup = (daliasskingroup_t *)pskintype; groupskins = LittleLong (pinskingroup->numskins); pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1); pskintype = (daliasskintype_t *)(pinskinintervals + groupskins); for (j = 0; j < groupskins; j++) { Mod_FloodFillSkin (skin, pheader->skinwidth, pheader->skinheight); q_snprintf (name, sizeof(name), "%s_%i_%i", loadmodel->name, i, j); pheader->gl_texturenum[i][j&3] = GL_LoadTexture (name, (byte *)(pskintype), pheader->skinwidth, pheader->skinheight, tex_mode); pskintype = (daliasskintype_t *)((byte *)(pskintype) + s); } for (k = j; j < 4; j++) pheader->gl_texturenum[i][j&3] = pheader->gl_texturenum[i][j - k]; } } return (void *)pskintype; skin_too_large: Sys_Error ("Player skin too large"); return NULL; } //========================================================================= static void Mod_SetAliasModelExtraFlags (qmodel_t *mod) { mod->ex_flags = 0; // Torch glows if (!q_strncasecmp (mod->name, "models/rflmtrch",15) || !q_strncasecmp (mod->name, "models/cflmtrch",15) || !q_strncasecmp (mod->name, "models/castrch",15) || !q_strncasecmp (mod->name, "models/rometrch",15) || !q_strncasecmp (mod->name, "models/egtorch",14) || !q_strncasecmp (mod->name, "models/flame",12)) { mod->ex_flags |= XF_TORCH_GLOW; // set yellow color mod->glow_color[0] = 0.8f; mod->glow_color[1] = 0.4f; mod->glow_color[2] = 0.1f; mod->glow_color[3] = 1.0f; } else if (!q_strncasecmp (mod->name, "models/eflmtrch",15)) { mod->ex_flags |= (XF_TORCH_GLOW | XF_TORCH_GLOW_EGYPT); mod->glow_color[0] = 0.8f; mod->glow_color[1] = 0.4f; mod->glow_color[2] = 0.1f; mod->glow_color[3] = 1.0f; } // Mana glows else if (!q_strncasecmp (mod->name, "models/i_bmana",14)) { mod->ex_flags |= XF_GLOW; mod->glow_color[0] = 0.25f; mod->glow_color[1] = 0.25f; mod->glow_color[2] = 1.0f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/i_gmana",14)) { mod->ex_flags |= XF_GLOW; mod->glow_color[0] = 0.25f; mod->glow_color[1] = 1.0f; mod->glow_color[2] = 0.25f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/i_btmana",15)) { mod->ex_flags |= XF_GLOW; mod->glow_color[0] = 1.0f; mod->glow_color[1] = 0.25f; mod->glow_color[2] = 0.25f; mod->glow_color[3] = 0.5f; } // Missile glows else if (!q_strncasecmp (mod->name, "models/drgnball",15)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 1.0f; mod->glow_color[1] = 0.75f; mod->glow_color[2] = 0.25f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/eidoball",15)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 1.0f; mod->glow_color[1] = 0.55f; mod->glow_color[2] = 0.25f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/lavaball",15)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 1.0f; mod->glow_color[1] = 0.75f; mod->glow_color[2] = 0.25f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/glowball",15)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 1.0f; mod->glow_color[1] = 0.75f; mod->glow_color[2] = 0.25f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/fireball",15)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 1.0f; mod->glow_color[1] = 0.25f; mod->glow_color[2] = 0.25f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/famshot",14)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 0.2f; mod->glow_color[1] = 0.8f; mod->glow_color[2] = 0.2f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/pestshot",15)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 0.2f; mod->glow_color[1] = 0.2f; mod->glow_color[2] = 0.2f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/mumshot",14)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 1.0f; mod->glow_color[1] = 0.75f; mod->glow_color[2] = 0.25f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/scrbstp1",15)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 1.0f; mod->glow_color[1] = 0.75f; mod->glow_color[2] = 0.05f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/scrbpbody",16)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 1.0f; mod->glow_color[1] = 0.75f; mod->glow_color[2] = 0.05f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/iceshot2",15)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 0.25f; mod->glow_color[1] = 0.25f; mod->glow_color[2] = 1.0f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/iceshot",14)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 0.25f; mod->glow_color[1] = 0.25f; mod->glow_color[2] = 1.0f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/flaming",14)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 1.0f; mod->glow_color[1] = 0.75f; mod->glow_color[2] = 0.55f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/sucwp1p",14)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 8.0f; mod->glow_color[1] = 0.2f; mod->glow_color[2] = 0.2f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/sucwp2p",14)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 0.2f; mod->glow_color[1] = 0.8f; mod->glow_color[2] = 0.2f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/goop",11)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 1.0f; mod->glow_color[1] = 0.8f; mod->glow_color[2] = 0.2f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/purfir1",14)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 1.0f; mod->glow_color[1] = 0.75f; mod->glow_color[2] = 0.25f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/golemmis",15)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 1.0f; mod->glow_color[1] = 0.25f; mod->glow_color[2] = 0.25f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/shard",12) && strlen(mod->name) == 12) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 1.0f; mod->glow_color[1] = 0.25f; mod->glow_color[2] = 0.25f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/shardice",15)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 0.25f; mod->glow_color[1] = 0.25f; mod->glow_color[2] = 1.0f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/snakearr",15)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 1.0f; mod->glow_color[1] = 0.25f; mod->glow_color[2] = 0.25f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/spit",11)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 0.25f; mod->glow_color[1] = 1.0f; mod->glow_color[2] = 0.25f; mod->glow_color[3] = 0.5f; } else if (!q_strncasecmp (mod->name, "models/spike",12)) { mod->ex_flags |= XF_MISSILE_GLOW; mod->glow_color[0] = 1.0f; mod->glow_color[1] = 1.0f; mod->glow_color[2] = 1.0f; mod->glow_color[3] = 0.5f; } } /* ================= Mod_LoadAliasModelNew reads extra field for num ST verts, and extra index list of them ================= */ static void Mod_LoadAliasModelNew (qmodel_t *mod, void *buffer) { int i, j; newmdl_t *pinmodel; stvert_t *pinstverts; dnewtriangle_t *pintriangles; int version, numframes; int size; daliasframetype_t *pframetype; daliasskintype_t *pskintype; int start, end, total; Mod_SetAliasModelExtraFlags (mod); start = Hunk_LowMark (); pinmodel = (newmdl_t *)buffer; version = LittleLong (pinmodel->version); if (version != ALIAS_NEWVERSION) Sys_Error ("%s has wrong version number (%i should be %i)", mod->name, version, ALIAS_NEWVERSION); // Con_Printf("Loading NEW model %s\n",mod->name); // // allocate space for a working header, plus all the data except the frames, // skin and group info // size = sizeof (aliashdr_t) + (LittleLong (pinmodel->numframes) - 1) * sizeof (pheader->frames[0]); pheader = (aliashdr_t *) Hunk_AllocName (size, loadname); mod->flags = LittleLong (pinmodel->flags); // // endian-adjust and copy the data, starting with the alias model header // pheader->boundingradius = LittleFloat (pinmodel->boundingradius); pheader->numskins = LittleLong (pinmodel->numskins); pheader->skinwidth = LittleLong (pinmodel->skinwidth); pheader->skinheight = LittleLong (pinmodel->skinheight); if (pheader->skinheight > MAX_SKIN_HEIGHT) Sys_Error ("model %s has a skin taller than %d", mod->name, MAX_SKIN_HEIGHT); pheader->numverts = LittleLong (pinmodel->numverts); pheader->version = LittleLong (pinmodel->num_st_verts); //hide num_st in version if (pheader->numverts <= 0) Sys_Error ("model %s has no vertices", mod->name); if (pheader->numverts > MAXALIASVERTS) Sys_Error ("model %s has too many vertices", mod->name); pheader->numtris = LittleLong (pinmodel->numtris); if (pheader->numtris <= 0) Sys_Error ("model %s has no triangles", mod->name); pheader->numframes = LittleLong (pinmodel->numframes); numframes = pheader->numframes; if (numframes < 1) Sys_Error ("%s: Invalid # of frames: %d", __thisfunc__, numframes); pheader->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO; mod->synctype = (synctype_t) LittleLong (pinmodel->synctype); mod->numframes = pheader->numframes; for (i = 0; i < 3; i++) { pheader->scale[i] = LittleFloat (pinmodel->scale[i]); pheader->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]); pheader->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]); } // // load the skins // pskintype = (daliasskintype_t *)&pinmodel[1]; pskintype = (daliasskintype_t *) Mod_LoadAllSkins (pheader->numskins, pskintype, mod->flags); // // load base s and t vertices // pinstverts = (stvert_t *)pskintype; for (i = 0; i < pheader->version; i++) //version holds num_st_verts { stverts[i].onseam = LittleLong (pinstverts[i].onseam); stverts[i].s = LittleLong (pinstverts[i].s); stverts[i].t = LittleLong (pinstverts[i].t); } // // load triangle lists // pintriangles = (dnewtriangle_t *)&pinstverts[pheader->version]; for (i = 0; i < pheader->numtris; i++) { triangles[i].facesfront = LittleLong (pintriangles[i].facesfront); for (j = 0; j < 3; j++) { triangles[i].vertindex[j] = LittleShort (pintriangles[i].vertindex[j]); triangles[i].stindex[j] = LittleShort (pintriangles[i].stindex[j]); } } // // load the frames // posenum = 0; pframetype = (daliasframetype_t *)&pintriangles[pheader->numtris]; aliasmins[0] = aliasmins[1] = aliasmins[2] = 32768; aliasmaxs[0] = aliasmaxs[1] = aliasmaxs[2] = -32768; for (i = 0; i < numframes; i++) { aliasframetype_t frametype; frametype = (aliasframetype_t) LittleLong (pframetype->type); if (frametype == ALIAS_SINGLE) { pframetype = (daliasframetype_t *) Mod_LoadAliasFrame (pframetype + 1, &pheader->frames[i]); } else { pframetype = (daliasframetype_t *) Mod_LoadAliasGroup (pframetype + 1, &pheader->frames[i]); } } //Con_Printf("Model is %s\n",mod->name); //Con_Printf(" Mins is %5.2f, %5.2f, %5.2f\n",aliasmins[0],aliasmins[1],aliasmins[2]); //Con_Printf(" Maxs is %5.2f, %5.2f, %5.2f\n",aliasmaxs[0],aliasmaxs[1],aliasmaxs[2]); pheader->numposes = posenum; mod->type = mod_alias; // FIXME: do this right // mod->mins[0] = mod->mins[1] = mod->mins[2] = -16; // mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16; mod->mins[0] = aliasmins[0] - 10; mod->mins[1] = aliasmins[1] - 10; mod->mins[2] = aliasmins[2] - 10; mod->maxs[0] = aliasmaxs[0] + 10; mod->maxs[1] = aliasmaxs[1] + 10; mod->maxs[2] = aliasmaxs[2] + 10; // // build the draw lists // GL_MakeAliasModelDisplayLists (mod, pheader); // // move the complete, relocatable alias model to the cache // end = Hunk_LowMark (); total = end - start; if (!mod->cache.data) Cache_Alloc (&mod->cache, total, loadname); if (!mod->cache.data) return; memcpy (mod->cache.data, pheader, total); Hunk_FreeToLowMark (start); } /* ================= Mod_LoadAliasModel ================= */ static void Mod_LoadAliasModel (qmodel_t *mod, void *buffer) { int i, j; mdl_t *pinmodel; stvert_t *pinstverts; dtriangle_t *pintriangles; int version, numframes; int size; daliasframetype_t *pframetype; daliasskintype_t *pskintype; int start, end, total; Mod_SetAliasModelExtraFlags (mod); start = Hunk_LowMark (); pinmodel = (mdl_t *)buffer; version = LittleLong (pinmodel->version); if (version != ALIAS_VERSION) Sys_Error ("%s has wrong version number (%i should be %i)", mod->name, version, ALIAS_VERSION); // // allocate space for a working header, plus all the data except the frames, // skin and group info // size = sizeof (aliashdr_t) + (LittleLong (pinmodel->numframes) - 1) * sizeof (pheader->frames[0]); pheader = (aliashdr_t *) Hunk_AllocName (size, loadname); mod->flags = LittleLong (pinmodel->flags); // // endian-adjust and copy the data, starting with the alias model header // pheader->boundingradius = LittleFloat (pinmodel->boundingradius); pheader->numskins = LittleLong (pinmodel->numskins); pheader->skinwidth = LittleLong (pinmodel->skinwidth); pheader->skinheight = LittleLong (pinmodel->skinheight); if (pheader->skinheight > MAX_SKIN_HEIGHT) Sys_Error ("model %s has a skin taller than %d", mod->name, MAX_SKIN_HEIGHT); pheader->numverts = LittleLong (pinmodel->numverts); pheader->version = pheader->numverts; //hide num_st in version if (pheader->numverts <= 0) Sys_Error ("model %s has no vertices", mod->name); if (pheader->numverts > MAXALIASVERTS) Sys_Error ("model %s has too many vertices", mod->name); pheader->numtris = LittleLong (pinmodel->numtris); if (pheader->numtris <= 0) Sys_Error ("model %s has no triangles", mod->name); pheader->numframes = LittleLong (pinmodel->numframes); numframes = pheader->numframes; if (numframes < 1) Sys_Error ("%s: Invalid # of frames: %d", __thisfunc__, numframes); pheader->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO; mod->synctype = (synctype_t) LittleLong (pinmodel->synctype); mod->numframes = pheader->numframes; for (i = 0; i < 3; i++) { pheader->scale[i] = LittleFloat (pinmodel->scale[i]); pheader->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]); pheader->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]); } // // load the skins // pskintype = (daliasskintype_t *)&pinmodel[1]; pskintype = (daliasskintype_t *) Mod_LoadAllSkins (pheader->numskins, pskintype, mod->flags); // // load base s and t vertices // pinstverts = (stvert_t *)pskintype; for (i = 0; i < pheader->version; i++) //version holds num_st_verts { stverts[i].onseam = LittleLong (pinstverts[i].onseam); stverts[i].s = LittleLong (pinstverts[i].s); stverts[i].t = LittleLong (pinstverts[i].t); } // // load triangle lists // pintriangles = (dtriangle_t *)&pinstverts[pheader->numverts]; for (i = 0; i < pheader->numtris; i++) { triangles[i].facesfront = LittleLong (pintriangles[i].facesfront); for (j = 0; j < 3; j++) { triangles[i].vertindex[j] = (unsigned short)LittleLong (pintriangles[i].vertindex[j]); triangles[i].stindex[j] = triangles[i].vertindex[j]; } } // // load the frames // posenum = 0; pframetype = (daliasframetype_t *)&pintriangles[pheader->numtris]; aliasmins[0] = aliasmins[1] = aliasmins[2] = 32768; aliasmaxs[0] = aliasmaxs[1] = aliasmaxs[2] = -32768; for (i = 0; i < numframes; i++) { aliasframetype_t frametype; frametype = (aliasframetype_t) LittleLong (pframetype->type); if (frametype == ALIAS_SINGLE) { pframetype = (daliasframetype_t *) Mod_LoadAliasFrame (pframetype + 1, &pheader->frames[i]); } else { pframetype = (daliasframetype_t *) Mod_LoadAliasGroup (pframetype + 1, &pheader->frames[i]); } } //Con_Printf("Model is %s\n",mod->name); //Con_Printf(" Mins is %5.2f, %5.2f, %5.2f\n",aliasmins[0],aliasmins[1],aliasmins[2]); //Con_Printf(" Maxs is %5.2f, %5.2f, %5.2f\n",aliasmaxs[0],aliasmaxs[1],aliasmaxs[2]); pheader->numposes = posenum; mod->type = mod_alias; // FIXME: do this right // mod->mins[0] = mod->mins[1] = mod->mins[2] = -16; // mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16; mod->mins[0] = aliasmins[0] - 10; mod->mins[1] = aliasmins[1] - 10; mod->mins[2] = aliasmins[2] - 10; mod->maxs[0] = aliasmaxs[0] + 10; mod->maxs[1] = aliasmaxs[1] + 10; mod->maxs[2] = aliasmaxs[2] + 10; // // build the draw lists // GL_MakeAliasModelDisplayLists (mod, pheader); // // move the complete, relocatable alias model to the cache // end = Hunk_LowMark (); total = end - start; if (!mod->cache.data) Cache_Alloc (&mod->cache, total, loadname); if (!mod->cache.data) return; memcpy (mod->cache.data, pheader, total); Hunk_FreeToLowMark (start); } //============================================================================= /* ================= Mod_LoadSpriteFrame ================= */ static void *Mod_LoadSpriteFrame (void *pin, mspriteframe_t **ppframe, int framenum) { dspriteframe_t *pinframe; mspriteframe_t *pspriteframe; int width, height, size, origin[2]; char name[MAX_QPATH]; pinframe = (dspriteframe_t *)pin; width = LittleLong (pinframe->width); height = LittleLong (pinframe->height); size = width * height; if (spr_reload_only && (*ppframe)) { pspriteframe = *ppframe; } else { pspriteframe = (mspriteframe_t *) Hunk_AllocName(sizeof(mspriteframe_t), loadname); *ppframe = pspriteframe; } pspriteframe->width = width; pspriteframe->height = height; origin[0] = LittleLong (pinframe->origin[0]); origin[1] = LittleLong (pinframe->origin[1]); pspriteframe->up = origin[1]; pspriteframe->down = origin[1] - height; pspriteframe->left = origin[0]; pspriteframe->right = width + origin[0]; q_snprintf (name, sizeof(name), "%s_%i", loadmodel->name, framenum); pspriteframe->gl_texturenum = GL_LoadTexture (name, (byte *)(pinframe + 1), width, height, TEX_MIPMAP | TEX_ALPHA); return (void *)((byte *)pinframe + sizeof (dspriteframe_t) + size); } /* ================= Mod_LoadSpriteGroup ================= */ static void *Mod_LoadSpriteGroup (void *pin, mspriteframe_t **ppframe, int framenum) { dspritegroup_t *pingroup; mspritegroup_t *pspritegroup; int i, numframes; dspriteinterval_t *pin_intervals; float *poutintervals; void *ptemp; pingroup = (dspritegroup_t *)pin; numframes = LittleLong (pingroup->numframes); if (spr_reload_only && (*ppframe)) { pspritegroup = (mspritegroup_t *)(*ppframe); } else { pspritegroup = (mspritegroup_t *) Hunk_AllocName(sizeof(mspritegroup_t) + (numframes - 1) * sizeof(pspritegroup->frames[0]), loadname); *ppframe = (mspriteframe_t *)pspritegroup; } pspritegroup->numframes = numframes; pin_intervals = (dspriteinterval_t *)(pingroup + 1); if (spr_reload_only && pspritegroup->intervals) { poutintervals = pspritegroup->intervals; } else { poutintervals = (float *) Hunk_AllocName (numframes * sizeof (float), loadname); pspritegroup->intervals = poutintervals; } for (i = 0; i < numframes; i++) { *poutintervals = LittleFloat (pin_intervals->interval); if (*poutintervals <= 0.0) Sys_Error ("%s: interval <= 0", __thisfunc__); poutintervals++; pin_intervals++; } ptemp = (void *)pin_intervals; for (i = 0; i < numframes; i++) { ptemp = Mod_LoadSpriteFrame (ptemp, &pspritegroup->frames[i], framenum * 100 + i); } return ptemp; } /* ================= Mod_LoadSpriteModel ================= */ static void Mod_LoadSpriteModel (qmodel_t *mod, void *buffer) { int i; int version; dsprite_t *pin; msprite_t *psprite; int numframes; int size; dspriteframetype_t *pframetype; pin = (dsprite_t *)buffer; version = LittleLong (pin->version); if (version != SPRITE_VERSION) Sys_Error ("%s has wrong version number " "(%i should be %i)", mod->name, version, SPRITE_VERSION); numframes = LittleLong (pin->numframes); size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames); // Pa3PyX: if the pointer is set and needload == NL_NEEDS_LOADED, // we are just reloading textures, and are already allocated if (spr_reload_only && mod->cache.data) { psprite = (msprite_t *) mod->cache.data; } else { psprite = (msprite_t *) Hunk_AllocName (size, loadname); mod->cache.data = psprite; } psprite->type = LittleLong (pin->type); psprite->maxwidth = LittleLong (pin->width); psprite->maxheight = LittleLong (pin->height); psprite->beamlength = LittleFloat (pin->beamlength); mod->synctype = (synctype_t) LittleLong (pin->synctype); psprite->numframes = numframes; mod->mins[0] = mod->mins[1] = -psprite->maxwidth/2; mod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2; mod->mins[2] = -psprite->maxheight/2; mod->maxs[2] = psprite->maxheight/2; // // load the frames // if (numframes < 1) Sys_Error ("%s: Invalid # of frames: %d", __thisfunc__, numframes); mod->numframes = numframes; pframetype = (dspriteframetype_t *)(pin + 1); for (i = 0; i < numframes; i++) { spriteframetype_t frametype; frametype = (spriteframetype_t) LittleLong (pframetype->type); psprite->frames[i].type = frametype; if (frametype == SPR_SINGLE) { pframetype = (dspriteframetype_t *) Mod_LoadSpriteFrame (pframetype + 1, &psprite->frames[i].frameptr, i); } else { pframetype = (dspriteframetype_t *) Mod_LoadSpriteGroup (pframetype + 1, &psprite->frames[i].frameptr, i); } } mod->type = mod_sprite; } //============================================================================= /* ================ Mod_Print ================ */ #if defined(__GNUC__) && !(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) #define MOD_Printf(FH, fmt, args...) \ do { \ if ((FH)) fprintf((FH), fmt, ##args); \ else Con_Printf(fmt, ##args); \ } while (0) #else #define MOD_Printf(FH, ...) \ do { \ if ((FH)) fprintf((FH), __VA_ARGS__); \ else Con_Printf(__VA_ARGS__); \ } while (0) #endif static void Mod_Print (void) { int i, counter; FILE *FH = NULL; qmodel_t *mod; i = Cmd_Argc(); for (counter = 1; counter < i; counter++) { if (q_strcasecmp(Cmd_Argv(counter),"save") == 0) { FH = fopen(FS_MakePath(FS_USERDIR,NULL,"mcache.txt"), "w"); break; } } MOD_Printf (FH, "Cached models:\n"); for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { MOD_Printf (FH, "%4i (%8p): %s", i, mod->cache.data, mod->name); if (mod->needload & NL_UNREFERENCED) MOD_Printf (FH, " (!R)"); if (mod->needload & NL_NEEDS_LOADED) MOD_Printf (FH, " (!P)"); MOD_Printf (FH, "\n"); } if (FH) { fclose(FH); Con_Printf ("Wrote to mcache.txt\n"); } } engine/h2shared/gl_model.h000066400000000000000000000317211444734033100157650ustar00rootroot00000000000000/* * model.h -- header for model loading and caching * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef GL_MODEL_H #define GL_MODEL_H #include "genmodel.h" #include "spritegn.h" /* d*_t structures are on-disk representations m*_t structures are in-memory */ /* ============================================================================== BRUSH MODELS ============================================================================== */ // // in memory representation // // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct { vec3_t position; } mvertex_t; #define SIDE_FRONT 0 #define SIDE_BACK 1 #define SIDE_ON 2 // plane_t structure // !!! if this is changed, it must be changed in asm_i386.h too !!! typedef struct mplane_s { vec3_t normal; float dist; byte type; // for texture axis selection and fast side tests byte signbits; // signx + signy<<1 + signz<<1 byte pad[2]; } mplane_t; typedef struct texture_s { char name[16]; unsigned int width, height; GLuint gl_texturenum; struct msurface_s *texturechain; // for gl_texsort drawing int anim_total; // total tenths in sequence ( 0 = no) int anim_min, anim_max; // time for this frame min <=time< max struct texture_s *anim_next; // in the animation sequence struct texture_s *alternate_anims; // bmodels in frmae 1 use these unsigned int offsets[MIPLEVELS]; // four mip maps stored } texture_t; #define SURF_PLANEBACK 2 #define SURF_DRAWSKY 4 #define SURF_DRAWSPRITE 8 #define SURF_DRAWTURB 0x10 #define SURF_DRAWTILED 0x20 #define SURF_DRAWBACKGROUND 0x40 #define SURF_TRANSLUCENT 0x80 /* r_edge.asm checks this */ #define SURF_DRAWBLACK 0x100 #define SURF_UNDERWATER 0x200 #define SURF_DONTWARP 0x400 // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct { unsigned int v[2]; unsigned int cachededgeoffset; } medge_t; typedef struct { float vecs[2][4]; texture_t *texture; int flags; } mtexinfo_t; #define VERTEXSIZE 7 typedef struct glpoly_s { struct glpoly_s *next; struct glpoly_s *chain; int numverts; int flags; // for SURF_UNDERWATER float verts[4][VERTEXSIZE]; // variable sized (xyz s1t1 s2t2) } glpoly_t; typedef struct msurface_s { int visframe; // should be drawn when node is crossed mplane_t *plane; int flags; int firstedge; // look up in model->surfedges[], negative numbers int numedges; // are backwards edges int texturemins[2]; int extents[2]; int light_s, light_t; // gl lightmap coordinates glpoly_t *polys; // multiple if warped struct msurface_s *texturechain; mtexinfo_t *texinfo; // lighting info int dlightframe; int dlightbits; unsigned int lightmaptexturenum; byte styles[MAXLIGHTMAPS]; int cached_light[MAXLIGHTMAPS]; // values currently used in lightmap qboolean cached_dlight; // true if dynamic light in cache byte *samples; // [numstyles*surfsize] } msurface_t; typedef struct mnode_s { // common with leaf int contents; // 0, to differentiate from leafs int visframe; // node needs to be traversed if current float minmaxs[6]; // for bounding box culling struct mnode_s *parent; // node specific mplane_t *plane; struct mnode_s *children[2]; unsigned int firstsurface; unsigned int numsurfaces; } mnode_t; typedef struct mleaf_s { // common with node int contents; // wil be a negative contents number int visframe; // node needs to be traversed if current float minmaxs[6]; // for bounding box culling struct mnode_s *parent; // leaf specific byte *compressed_vis; struct efrag_s *efrags; msurface_t **firstmarksurface; int nummarksurfaces; int key; // BSP sequence number for leaf's contents byte ambient_sound_level[NUM_AMBIENTS]; } mleaf_t; #ifdef ENABLE_BSP2 typedef dclipnode2_t mclipnode_t; #else typedef dclipnode_t mclipnode_t; #endif // !!! if this is changed, it must be changed in asm_i386.h too !!! typedef struct { mclipnode_t *clipnodes; mplane_t *planes; int firstclipnode; int lastclipnode; vec3_t clip_mins; vec3_t clip_maxs; } hull_t; #define HULL_IMPLICIT 0 // Choose the hull based on bounding box- like in Quake #define HULL_POINT 1 // 0 0 0, 0 0 0 #define HULL_PLAYER 2 // '-16 -16 0', '16 16 56' #define HULL_SCORPION 3 // '-24 -24 -20', '24 24 20' #define HULL_CROUCH 4 // '-16 -16 0', '16 16 28' #define HULL_HYDRA 5 // '-28 -28 -24', '28 28 24' #define HULL_GOLEM 6 // ???,??? /* ============================================================================== SPRITE MODELS ============================================================================== */ // FIXME: shorten these? typedef struct mspriteframe_s { short width; short height; float up, down, left, right; GLuint gl_texturenum; } mspriteframe_t; typedef struct { short numframes; float *intervals; mspriteframe_t *frames[1]; } mspritegroup_t; typedef struct { spriteframetype_t type; mspriteframe_t *frameptr; } mspriteframedesc_t; typedef struct { short type; short maxwidth; short maxheight; short numframes; float beamlength; // remove? //void *cachespot; // remove? mspriteframedesc_t frames[1]; } msprite_t; /* ============================================================================== ALIAS MODELS Alias models are position independent, so the cache manager can move them. ============================================================================== */ typedef struct { int firstpose; int numposes; float interval; trivertx_t bboxmin; trivertx_t bboxmax; int frame; char name[16]; } maliasframedesc_t; typedef struct { trivertx_t bboxmin; trivertx_t bboxmax; int frame; } maliasgroupframedesc_t; typedef struct { int numframes; int intervals; maliasgroupframedesc_t frames[1]; } maliasgroup_t; //this is only the GL version typedef struct mtriangle_s { int facesfront; unsigned short vertindex[3]; unsigned short stindex[3]; } mtriangle_t; #define MAX_SKINS 32 typedef struct { int ident; int version; vec3_t scale; vec3_t scale_origin; float boundingradius; vec3_t eyeposition; int numskins; int skinwidth; int skinheight; int numverts; int numtris; int numframes; synctype_t synctype; int flags; float size; int numposes; int poseverts; int posedata; // numposes*poseverts trivert_t int commands; // gl command list with embedded s/t GLuint gl_texturenum[MAX_SKINS][4]; maliasframedesc_t frames[1]; // variable sized } aliashdr_t; #define MAXALIASVERTS 2000 #define MAXALIASFRAMES 256 #define MAXALIASTRIS 2048 extern aliashdr_t *pheader; extern stvert_t stverts[MAXALIASVERTS]; extern mtriangle_t triangles[MAXALIASTRIS]; extern trivertx_t *poseverts[MAXALIASFRAMES]; //=================================================================== // // entity effects // #ifndef H2W /* see below for hexenworld */ #define EF_BRIGHTFIELD 0x00000001 #endif #define EF_MUZZLEFLASH 0x00000002 #define EF_BRIGHTLIGHT 0x00000004 #define EF_DIMLIGHT 0x00000008 #define EF_DARKLIGHT 0x00000010 #define EF_DARKFIELD 0x00000020 #define EF_LIGHT 0x00000040 #define EF_NODRAW 0x00000080 #ifdef H2W /* The only difference between Raven's hw-0.15 binary release and the * later HexenC source release is the EF_BRIGHTFIELD and EF_ONFIRE values: * the original binary releases had them as 1 and 1024 respectively, but * the later hcode src releases have them flipped: EF_BRIGHTFIELD = 1024 * and EF_ONFIRE = 1, which is a BIG BOO BOO. (On the other hand, Siege * binary and source releases have EF_BRIGHTFIELD and EF_ONFIRE values as * 1 and 1024, which makes the mess even messier.. Sigh..) * The hexenworld engine src release also have EF_BRIGHTFIELD as 1024 and * EF_ONFIRE as 1, therefore uHexen2 sticks to those values. */ #define EF_ONFIRE 0x00000001 #define EF_BRIGHTFIELD 0x00000400 #define EF_POWERFLAMEBURN 0x00000800 #define EF_UPDATESOUND 0x00002000 #define EF_POISON_GAS 0x00200000 #define EF_ACIDBLOB 0x00400000 //#define EF_PURIFY2_EFFECT 0x00200000 //#define EF_AXE_EFFECT 0x00400000 //#define EF_SWORD_EFFECT 0x00800000 //#define EF_TORNADO_EFFECT 0x01000000 #define EF_ICESTORM_EFFECT 0x02000000 //#define EF_ICEBALL_EFFECT 0x04000000 //#define EF_METEOR_EFFECT 0x08000000 #define EF_HAMMER_EFFECTS 0x10000000 #define EF_BEETLE_EFFECTS 0x20000000 #endif /* H2W */ //=================================================================== // // Whole model // typedef enum {mod_brush, mod_sprite, mod_alias} modtype_t; /* EF_ changes must also be made in both model.h and gl_model.h and you MUST check with the constants in gamecode, as well. */ #define EF_ROCKET (1 << 0 ) /* leave a trail */ #define EF_GRENADE (1 << 1 ) /* leave a trail */ #define EF_GIB (1 << 2 ) /* leave a trail */ #define EF_ROTATE (1 << 3 ) /* rotate (bonus items) */ #define EF_TRACER (1 << 4 ) /* green split trail */ #define EF_ZOMGIB (1 << 5 ) /* small blood trail */ #define EF_TRACER2 (1 << 6 ) /* orange split trail + rotate */ #define EF_TRACER3 (1 << 7 ) /* purple trail */ #define EF_FIREBALL (1 << 8 ) /* Yellow transparent trail in all directions */ #define EF_ICE (1 << 9 ) /* Blue-white transparent trail, with gravity */ #define EF_MIP_MAP (1 << 10) /* This model has mip-maps */ #define EF_SPIT (1 << 11) /* Black transparent trail with negative light */ #define EF_TRANSPARENT (1 << 12) /* Transparent sprite */ #define EF_SPELL (1 << 13) /* Vertical spray of particles */ #define EF_HOLEY (1 << 14) /* Solid model with color 0 */ #define EF_SPECIAL_TRANS (1 << 15) /* Translucency through the particle table */ #define EF_FACE_VIEW (1 << 16) /* Poly Model always faces you */ #define EF_VORP_MISSILE (1 << 17) /* leave a trail at top and bottom of model */ #define EF_SET_STAFF (1 << 18) /* slowly move up and left/right */ #define EF_MAGICMISSILE (1 << 19) /* a trickle of blue/white particles with gravity */ #define EF_BONESHARD (1 << 20) /* a trickle of brown particles with gravity */ #define EF_SCARAB (1 << 21) /* white transparent particles with little gravity */ #define EF_ACIDBALL (1 << 22) /* Green drippy acid shit */ #define EF_BLOODSHOT (1 << 23) /* Blood rain shot trail */ #define EF_MIP_MAP_FAR (1 << 24) /* Set per frame, this model will use the far mip map */ // XF_ Extra model effects set by engine: qmodel_t->ex_flags // effects are model name dependent #define XF_TORCH_GLOW (1 << 0 ) /* glowing torches */ #define XF_TORCH_GLOW_EGYPT (1 << 30) /* glowing torches, egypt */ #define XF_GLOW (1 << 1 ) /* other glows */ #define XF_MISSILE_GLOW (1 << 2 ) /* missile glows */ typedef struct qmodel_s { char name[MAX_QPATH]; unsigned int path_id; // path id of the game directory // that this model came from int needload; // bmodels and sprites don't cache normally modtype_t type; int numframes; synctype_t synctype; int flags; int ex_flags; // // volume occupied by the model graphics // vec3_t mins, maxs; float radius; // // brush model // int firstmodelsurface, nummodelsurfaces; int numsubmodels; dmodel_t *submodels; int numplanes; mplane_t *planes; int numleafs; // number of visible leafs, not counting 0 mleaf_t *leafs; int numvertexes; mvertex_t *vertexes; int numedges; medge_t *edges; int numnodes; mnode_t *nodes; int numtexinfo; mtexinfo_t *texinfo; int numsurfaces; msurface_t *surfaces; int numsurfedges; int *surfedges; int numclipnodes; mclipnode_t *clipnodes; int nummarksurfaces; msurface_t **marksurfaces; hull_t hulls[MAX_MAP_HULLS]; int numtextures; texture_t **textures; byte *visdata; byte *lightdata; char *entities; // // additional model data // cache_user_t cache; // only access through Mod_Extradata float glow_color[4]; } qmodel_t; // values for qmodel_t->needload #define NL_PRESENT 0 #define NL_NEEDS_LOADED 1 #define NL_UNREFERENCED 2 //============================================================================ void Mod_Init (void); void Mod_ClearAll (void); qmodel_t *Mod_ForName (const char *name, qboolean crash); qmodel_t *Mod_FindName (const char *name); void *Mod_Extradata (qmodel_t *mod); // handles caching void Mod_TouchModel (const char *name); void Mod_ReloadTextures (void); mleaf_t *Mod_PointInLeaf (vec3_t p, qmodel_t *model); byte *Mod_LeafPVS (mleaf_t *leaf, qmodel_t *model); #endif /* GL_MODEL_H */ engine/h2shared/gl_refrag.c000066400000000000000000000103001444734033100161140ustar00rootroot00000000000000/* * r_efrag.c -- entity fragments * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" static mnode_t *r_pefragtopnode; /* =============================================================================== ENTITY FRAGMENT FUNCTIONS =============================================================================== */ static efrag_t **lastlink; static entity_t *r_addent; static vec3_t r_emins, r_emaxs; /* ================ R_RemoveEfrags Call when removing an object from the world or moving it to another position ================ */ void R_RemoveEfrags (entity_t *ent) { efrag_t *ef, *old, *walk, **prev; ef = ent->efrag; while (ef) { prev = &ef->leaf->efrags; while (1) { walk = *prev; if (!walk) break; if (walk == ef) { // remove this fragment *prev = ef->leafnext; break; } else prev = &walk->leafnext; } old = ef; ef = ef->entnext; // put it on the free list old->entnext = cl.free_efrags; cl.free_efrags = old; } ent->efrag = NULL; } /* =================== R_SplitEntityOnNode =================== */ static void R_SplitEntityOnNode (mnode_t *node) { efrag_t *ef; mplane_t *splitplane; mleaf_t *leaf; int sides; if (node->contents == CONTENTS_SOLID) { return; } // add an efrag if the node is a leaf if (node->contents < 0) { if (!r_pefragtopnode) r_pefragtopnode = node; leaf = (mleaf_t *)node; // grab an efrag off the free list ef = cl.free_efrags; if (!ef) { Con_Printf ("Too many efrags!\n"); return; // no free fragments... } cl.free_efrags = cl.free_efrags->entnext; ef->entity = r_addent; // add the entity link *lastlink = ef; lastlink = &ef->entnext; ef->entnext = NULL; // set the leaf links ef->leaf = leaf; ef->leafnext = leaf->efrags; leaf->efrags = ef; return; } // NODE_MIXED splitplane = node->plane; sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane); if (sides == 3) { // split on this plane // if this is the first splitter of this bmodel, remember it if (!r_pefragtopnode) r_pefragtopnode = node; } // recurse down the contacted sides if (sides & 1) R_SplitEntityOnNode (node->children[0]); if (sides & 2) R_SplitEntityOnNode (node->children[1]); } /* =========== R_AddEfrags =========== */ void R_AddEfrags (entity_t *ent) { qmodel_t *entmodel; int i; if (!ent->model) return; r_addent = ent; lastlink = &ent->efrag; r_pefragtopnode = NULL; entmodel = ent->model; for (i = 0; i < 3; i++) { r_emins[i] = ent->origin[i] + entmodel->mins[i]; r_emaxs[i] = ent->origin[i] + entmodel->maxs[i]; } R_SplitEntityOnNode (cl.worldmodel->nodes); ent->topnode = r_pefragtopnode; } /* ================ R_StoreEfrags // FIXME: a lot of this goes away with edge-based ================ */ void R_StoreEfrags (efrag_t **ppefrag) { entity_t *pent; qmodel_t *clmodel; efrag_t *pefrag; while ((pefrag = *ppefrag) != NULL) { pent = pefrag->entity; clmodel = pent->model; switch (clmodel->type) { case mod_alias: case mod_brush: case mod_sprite: pent = pefrag->entity; if ((pent->visframe != r_framecount) && (cl_numvisedicts < MAX_VISEDICTS)) { #if defined (H2W) cl_visedicts[cl_numvisedicts++] = *pent; #else cl_visedicts[cl_numvisedicts++] = pent; #endif // mark that we've recorded this entity for this frame pent->visframe = r_framecount; } ppefrag = &pefrag->leafnext; break; default: Sys_Error ("%s: Bad entity type %d\n", __thisfunc__, clmodel->type); } } } engine/h2shared/gl_rlight.c000066400000000000000000000342261444734033100161540ustar00rootroot00000000000000/* * r_light.c * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" static int r_dlightframecount; /* ================== R_AnimateLight ================== */ void R_AnimateLight (void) { int i, c, v; int defaultLocus; int locusHz[3]; defaultLocus = locusHz[0] = (int)(cl.time*10); locusHz[1] = (int)(cl.time*20); locusHz[2] = (int)(cl.time*30); for (i = 0; i < MAX_LIGHTSTYLES; i++) { if (!cl_lightstyle[i].length) { // No style def d_lightstylevalue[i] = 256; continue; } c = cl_lightstyle[i].map[0]; if (c == '1' || c == '2' || c == '3') { // Explicit anim rate if (cl_lightstyle[i].length == 1) { // Bad style def d_lightstylevalue[i] = 256; continue; } v = locusHz[c-'1'] % (cl_lightstyle[i].length-1); d_lightstylevalue[i] = (cl_lightstyle[i].map[v+1]-'a')*22; continue; } // Default anim rate (10 Hz) v = defaultLocus % cl_lightstyle[i].length; d_lightstylevalue[i] = (cl_lightstyle[i].map[v]-'a')*22; } } /* ============================================================================= DYNAMIC LIGHTS BLEND RENDERING ============================================================================= */ static void AddLightBlend (float r, float g, float b, float a2) { float a; v_blend[3] = a = v_blend[3] + a2*(1-v_blend[3]); a2 = a2/a; v_blend[0] = v_blend[1]*(1-a2) + r*a2; v_blend[1] = v_blend[1]*(1-a2) + g*a2; v_blend[2] = v_blend[2]*(1-a2) + b*a2; } #define DLIGHT_BUBBLE_WEDGES 16 static float bubble_sintable[DLIGHT_BUBBLE_WEDGES+1]; static float bubble_costable[DLIGHT_BUBBLE_WEDGES+1]; void R_InitBubble (void) { float a; int i; float *bub_sin, *bub_cos; bub_sin = bubble_sintable; bub_cos = bubble_costable; for (i = DLIGHT_BUBBLE_WEDGES; i >= 0; i--) { a = i / ((float)DLIGHT_BUBBLE_WEDGES) * M_PI * 2; *bub_sin++ = sin(a); *bub_cos++ = cos(a); } } static void R_RenderDlight (dlight_t *light) { int i, j; vec3_t v; float rad; float *bub_sin, *bub_cos; bub_sin = bubble_sintable; bub_cos = bubble_costable; rad = light->radius * 0.35; VectorSubtract (light->origin, r_origin, v); if (VectorLengthFast (v) < rad) { // view is inside the dlight AddLightBlend (1, 0.5, 0, light->radius * 0.0003); return; } glBegin_fp (GL_TRIANGLE_FAN); if (light->color[0] || light->color[1] || light->color[2]) { glColor4fv_fp (light->color); } else { #ifndef H2W glColor3f_fp (0.2,0.1,0.0); #else glColor3f_fp (0.2,0.1,0.05); // changed dimlight effect #endif } for (i = 0; i < 3; i++) v[i] = light->origin[i] - vpn[i]*rad; glVertex3fv_fp (v); glColor3f_fp (0,0,0); for (i = DLIGHT_BUBBLE_WEDGES; i >= 0; i--) { for (j = 0; j < 3; j++) v[j] = light->origin[j] + (vright[j] * (*bub_cos) + vup[j] * (*bub_sin)) * rad; bub_sin++; bub_cos++; glVertex3fv_fp (v); } glEnd_fp (); } /* ============= R_RenderDlights ============= */ void R_RenderDlights (void) { int i; dlight_t *l; if (!gl_flashblend.integer) return; r_dlightframecount = r_framecount + 1; // because the count hasn't // advanced yet for this frame glDepthMask_fp (0); glDisable_fp (GL_TEXTURE_2D); glShadeModel_fp (GL_SMOOTH); glEnable_fp (GL_BLEND); glBlendFunc_fp (GL_ONE, GL_ONE); l = cl_dlights; for (i = 0; i < MAX_DLIGHTS; i++, l++) { if (l->die < cl.time || !l->radius) continue; R_RenderDlight (l); } glColor3f_fp (1,1,1); glDisable_fp (GL_BLEND); glEnable_fp (GL_TEXTURE_2D); glBlendFunc_fp (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask_fp (1); } /* ============================================================================= DYNAMIC LIGHTS ============================================================================= */ /* ============= R_MarkLights ============= */ #if 0 /* the original version from ID */ void R_MarkLights (dlight_t *light, int bit, mnode_t *node) { mplane_t *splitplane; float dist; msurface_t *surf; int i; if (node->contents < 0) return; splitplane = node->plane; dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; if (dist > light->radius) { R_MarkLights (light, bit, node->children[0]); return; } if (dist < -light->radius) { R_MarkLights (light, bit, node->children[1]); return; } // mark the polygons surf = cl.worldmodel->surfaces + node->firstsurface; for (i = 0; i < node->numsurfaces; i++, surf++) { if (surf->dlightframe != r_dlightframecount) { surf->dlightbits = 0; surf->dlightframe = r_dlightframecount; } surf->dlightbits |= bit; } R_MarkLights (light, bit, node->children[0]); R_MarkLights (light, bit, node->children[1]); } #else /* the major speedup version by Lord Havoc */ void R_MarkLights (dlight_t *light, int bit, mnode_t *node) { mplane_t *splitplane; float l, dist, maxdist; msurface_t *surf; int i, j, s, t; vec3_t impact; loc0: if (node->contents < 0) return; splitplane = node->plane; if (splitplane->type < 3) dist = light->origin[splitplane->type] - splitplane->dist; else dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; if (dist > light->radius) { node = node->children[0]; goto loc0; } if (dist < -light->radius) { node = node->children[1]; goto loc0; } maxdist = light->radius * light->radius; // mark the polygons surf = cl.worldmodel->surfaces + node->firstsurface; for (i = 0; i < node->numsurfaces; i++, surf++) { // eliminates marking of surfaces too far away from light, // thus preventing unnecessary renders and uploads for (j = 0; j < 3; j++) impact[j] = light->origin[j] - surf->plane->normal[j]*dist; // clamp center of light to corner and check brightness l = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0]; s = l + 0.5; if (s < 0) s = 0; else if (s > surf->extents[0]) s = surf->extents[0]; s = l - s; l = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1]; t = l + 0.5; if (t < 0) t = 0; else if (t > surf->extents[1]) t = surf->extents[1]; t = l - t; // compare to minimum light if ((s*s + t*t + dist*dist) < maxdist) { if (surf->dlightframe != r_dlightframecount) { // not dynamic until now surf->dlightbits = bit; surf->dlightframe = r_dlightframecount; } else // already dynamic surf->dlightbits |= bit; } } if (node->children[0]->contents >= 0) R_MarkLights (light, bit, node->children[0]); if (node->children[1]->contents >= 0) R_MarkLights (light, bit, node->children[1]); } #endif /* end of 2 R_MarkLights versions */ /* ============= R_PushDlights ============= */ void R_PushDlights (void) { int i; dlight_t *l; if (gl_flashblend.integer) return; r_dlightframecount = r_framecount + 1; // because the count hasn't // advanced yet for this frame l = cl_dlights; for (i = 0; i < MAX_DLIGHTS; i++, l++) { if (l->die < cl.time || !l->radius) continue; R_MarkLights ( l, 1<nodes ); } } /* ============================================================================= LIGHT SAMPLING ============================================================================= */ vec3_t lightspot; static int RecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end) { int r; float front, back, frac; int side; mplane_t *plane; vec3_t mid; msurface_t *surf; int s, t, ds, dt; int i; mtexinfo_t *tex; byte *lightmap; unsigned int scale; int maps; if (node->contents < 0) return -1; // didn't hit anything // calculate mid point // FIXME: optimize for axial plane = node->plane; front = DotProduct (start, plane->normal) - plane->dist; back = DotProduct (end, plane->normal) - plane->dist; side = front < 0; if ( (back < 0) == side) return RecursiveLightPoint (node->children[side], start, end); frac = front / (front-back); mid[0] = start[0] + (end[0] - start[0])*frac; mid[1] = start[1] + (end[1] - start[1])*frac; mid[2] = start[2] + (end[2] - start[2])*frac; // go down front side r = RecursiveLightPoint (node->children[side], start, mid); if (r >= 0) return r; // hit something if ( (back < 0) == side ) return -1; // didn't hit anything // check for impact on this node VectorCopy (mid, lightspot); surf = cl.worldmodel->surfaces + node->firstsurface; for (i = 0; i < node->numsurfaces; i++, surf++) { if (surf->flags & SURF_DRAWTILED) continue; // no lightmaps tex = surf->texinfo; /* added double casts so that 64 bit/sse2 builds' precision * matches that of x87 floating point. took from QuakeSpasm, * patch by Eric Wasylishen. */ s = DotProductDBL(mid, tex->vecs[0]) + (double)tex->vecs[0][3]; t = DotProductDBL(mid, tex->vecs[1]) + (double)tex->vecs[1][3]; if (s < surf->texturemins[0] || t < surf->texturemins[1]) continue; ds = s - surf->texturemins[0]; dt = t - surf->texturemins[1]; if (ds > surf->extents[0] || dt > surf->extents[1]) continue; if (!surf->samples) return 0; ds >>= 4; dt >>= 4; lightmap = surf->samples; r = 0; if (lightmap) { lightmap += dt * ((surf->extents[0] >> 4) + 1) + ds; for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) { scale = d_lightstylevalue[surf->styles[maps]]; r += *lightmap * scale; lightmap += ((surf->extents[0] >> 4) + 1) * ((surf->extents[1] >> 4) + 1); } r >>= 8; } return r; } // go down back side return RecursiveLightPoint (node->children[!side], mid, end); } int R_LightPoint (vec3_t p) { vec3_t end; int r; if (!cl.worldmodel->lightdata) return 255; end[0] = p[0]; end[1] = p[1]; end[2] = p[2] - 2048; r = RecursiveLightPoint (cl.worldmodel->nodes, p, end); if (r == -1) r = 0; return r; } static int RecursiveLightPointColor (vec3_t color, mnode_t *node, vec3_t start, vec3_t end) { float front, back, frac; vec3_t mid; loc0: if (node->contents < 0) return false; // didn't hit anything // calculate mid point if (node->plane->type < 3) { front = start[node->plane->type] - node->plane->dist; back = end[node->plane->type] - node->plane->dist; } else { front = DotProduct(start, node->plane->normal) - node->plane->dist; back = DotProduct(end, node->plane->normal) - node->plane->dist; } // LordHavoc: optimized recursion if ((back < 0) == (front < 0)) { node = node->children[front < 0]; goto loc0; } frac = front / (front-back); mid[0] = start[0] + (end[0] - start[0])*frac; mid[1] = start[1] + (end[1] - start[1])*frac; mid[2] = start[2] + (end[2] - start[2])*frac; // go down front side if (RecursiveLightPointColor (color, node->children[front < 0], start, mid)) return true; // hit something else { int i, ds, dt; msurface_t *surf; // check for impact on this node VectorCopy (mid, lightspot); surf = cl.worldmodel->surfaces + node->firstsurface; for (i = 0; i < node->numsurfaces; i++, surf++) { if (surf->flags & SURF_DRAWTILED) continue; // no lightmaps ds = (int) ((float) DotProduct(mid, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]); dt = (int) ((float) DotProduct(mid, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]); if (ds < surf->texturemins[0] || dt < surf->texturemins[1]) continue; ds -= surf->texturemins[0]; dt -= surf->texturemins[1]; if (ds > surf->extents[0] || dt > surf->extents[1]) continue; if (surf->samples) { // LordHavoc: enhanced to interpolate lighting byte *lightmap; float scale; int maps, line3, dsfrac = ds & 15, dtfrac = dt & 15, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0; line3 = ((surf->extents[0]>>4) + 1) * 3; lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4) + 1) + (ds>>4)) * 3; for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) { scale = (float) d_lightstylevalue[surf->styles[maps]] * 1.0 / 256.0; r00 += (float) lightmap[0] * scale; g00 += (float) lightmap[1] * scale; b00 += (float) lightmap[2] * scale; r01 += (float) lightmap[3] * scale; g01 += (float) lightmap[4] * scale; b01 += (float) lightmap[5] * scale; r10 += (float) lightmap[line3+0] * scale; g10 += (float) lightmap[line3+1] * scale; b10 += (float) lightmap[line3+2] * scale; r11 += (float) lightmap[line3+3] * scale; g11 += (float) lightmap[line3+4] * scale; b11 += (float) lightmap[line3+5] * scale; lightmap += ((surf->extents[0]>>4) + 1) * ((surf->extents[1]>>4) + 1) * 3; } color[0] += (float) ((int) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00))); color[1] += (float) ((int) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00))); color[2] += (float) ((int) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00))); } return true; // success } // go down back side return RecursiveLightPointColor (color, node->children[front >= 0], mid, end); } } vec3_t lightcolor; float R_LightPointColor (vec3_t p) { vec3_t end; if (!cl.worldmodel->lightdata) { lightcolor[0] = lightcolor[1] = lightcolor[2] = 255.0; return 255.0; } end[0] = p[0]; end[1] = p[1]; end[2] = p[2] - 2048; lightcolor[0] = lightcolor[1] = lightcolor[2] = 0; RecursiveLightPointColor (lightcolor, cl.worldmodel->nodes, p, end); return (lightcolor[0] + lightcolor[1] + lightcolor[2]) / 3.0; } engine/h2shared/gl_screen.c000066400000000000000000000712661444734033100161470ustar00rootroot00000000000000/* * screen.c -- master for refresh, status bar, console, chat, notify, etc * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*============================================================================= background clear rendering turtle/net/ram icons sbar centerprint / slow centerprint notify lines intermission / finale overlay loading plaque console menu required background clears required update regions syncronous draw mode or async One off screen buffer, with updates either copied or xblited Need to double buffer? async draw will require the refresh area to be cleared, because it will be xblited, but sync draw can just ignore it. sync draw CenterPrint () SlowPrint () Screen_Update (); Con_Printf (); net turn off messages option the refresh is always rendered, unless the console is full screen console is: notify lines half full =============================================================================*/ #include "quakedef.h" #ifdef PLATFORM_WINDOWS #include "winquake.h" #endif static qboolean scr_initialized; // ready to draw vrect_t scr_vrect; int glx, gly, glwidth, glheight; /* these are only functional in the software renderer */ int scr_copytop; // only the refresh window will be updated int scr_copyeverything; // unless these variables are flagged int scr_topupdate; int scr_fullupdate; static int clearconsole; int clearnotify; float scr_con_current; float scr_conlines; // lines of console to display int trans_level = 0; cvar_t scr_viewsize = {"viewsize", "110", CVAR_ARCHIVE}; cvar_t scr_fov = {"fov", "90", CVAR_NONE}; // 10 - 170 cvar_t scr_fov_adapt = {"fov_adapt", "1", CVAR_ARCHIVE}; // "Hor+" scaling cvar_t scr_contrans = {"contrans", "0", CVAR_ARCHIVE}; static cvar_t scr_conspeed = {"scr_conspeed", "300", CVAR_NONE}; static cvar_t scr_centertime = {"scr_centertime", "4", CVAR_NONE}; static cvar_t scr_showram = {"showram", "1", CVAR_NONE}; static cvar_t scr_showturtle = {"showturtle", "0", CVAR_NONE}; static cvar_t scr_showpause = {"showpause", "1", CVAR_NONE}; static cvar_t scr_showfps = {"showfps", "0", CVAR_NONE}; //static cvar_t gl_triplebuffer = {"gl_triplebuffer", "0", CVAR_ARCHIVE}; #if !defined(H2W) static qboolean scr_drawloading; static float scr_disabled_time; int total_loading_size, current_loading_size, loading_stage; #endif /* H2W */ qboolean scr_disabled_for_loading; qboolean scr_skipupdate; qboolean block_drawing; static qpic_t *scr_ram; static qpic_t *scr_net; static qpic_t *scr_turtle; static void SCR_ScreenShot_f (void); static const char *plaquemessage = ""; // pointer to current plaque message static void Plaque_Draw (const char *message, qboolean AlwaysDraw); #if !defined(H2W) /* procedures for the mission pack intro messages and objectives */ static void Info_Plaque_Draw (const char *message); static void Bottom_Plaque_Draw (const char *message); #endif /* H2W */ /* =============================================================================== CENTER PRINTING =============================================================================== */ static char scr_centerstring[1024]; float scr_centertime_off; static int scr_center_lines; static int scr_erase_lines; #define MAXLINES 27 static int lines; static int StartC[MAXLINES], EndC[MAXLINES]; #if !defined(H2W) /* mission pack objectives: */ #define MAX_INFO 1024 static char infomessage[MAX_INFO]; static void UpdateInfoMessage (void) { unsigned int i, check; const char *newmessage; q_strlcpy(infomessage, "Objectives:", sizeof(infomessage)); if (!info_string_count) return; for (i = 0; i < 32; i++) { check = (1 << i); if (cl.info_mask & check) { newmessage = CL_GetInfoString(i); q_strlcat(infomessage, "@@", sizeof(infomessage)); q_strlcat(infomessage, newmessage, sizeof(infomessage)); } } for (i = 0; i < 32; i++) { check = (1 << i); if (cl.info_mask2 & check) { newmessage = CL_GetInfoString(i + 32); q_strlcat(infomessage, "@@", sizeof(infomessage)); q_strlcat(infomessage, newmessage, sizeof(infomessage)); } } } #endif /* H2W */ static void FindTextBreaks (const char *message, int Width) { int pos, start, lastspace, oldlast; lines = pos = start = 0; lastspace = -1; while (1) { if (pos-start >= Width || message[pos] == '@' || message[pos] == 0) { oldlast = lastspace; if (message[pos] == '@' || lastspace == -1 || message[pos] == 0) lastspace = pos; StartC[lines] = start; EndC[lines] = lastspace; lines++; if (lines == MAXLINES) return; if (message[pos] == '@') start = pos + 1; else if (oldlast == -1) start = lastspace; else start = lastspace + 1; lastspace = -1; } if (message[pos] == 32) lastspace = pos; else if (message[pos] == 0) break; pos++; } } /* ============== SCR_CenterPrint Called for important messages that should stay in the center of the screen for a few moments ============== */ void SCR_CenterPrint (const char *str) { strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1); scr_centertime_off = scr_centertime.value; FindTextBreaks(scr_centerstring, 38); scr_center_lines = lines; } static void SCR_DrawCenterString (void) { int i, cnt; int bx, by; char temp[80]; FindTextBreaks(scr_centerstring, 38); by = (25-lines) * 8 / 2 + ((vid.height - 200)>>1); for (i = 0; i < lines; i++, by += 8) { cnt = EndC[i] - StartC[i]; strncpy (temp, &scr_centerstring[StartC[i]], cnt); temp[cnt] = 0; bx = (40-strlen(temp)) * 8 / 2; M_Print (bx, by, temp); } } static void SCR_CheckDrawCenterString (void) { scr_copytop = 1; if (scr_center_lines > scr_erase_lines) scr_erase_lines = scr_center_lines; scr_centertime_off -= host_frametime; if (scr_centertime_off <= 0 && !cl.intermission) return; if (Key_GetDest() != key_game) return; #if !defined(H2W) if (intro_playing) { Bottom_Plaque_Draw(scr_centerstring); return; } #endif /* H2W */ SCR_DrawCenterString (); } //============================================================================= /* ==================== AdaptFovx Adapt a 4:3 horizontal FOV to the current screen size using the "Hor+" scaling: 2.0 * atan(width / height * 3.0 / 4.0 * tan(fov43 / 2.0)) ==================== */ static float AdaptFovx (float fov_x, float width, float height) { float a, x; if (fov_x < 1 || fov_x > 179) Sys_Error ("Bad fov: %f", fov_x); if (!scr_fov_adapt.integer) return fov_x; if ((x = height / width) == 0.75) return fov_x; a = atan(0.75 / x * tan(fov_x / 360 * M_PI)); a = a * 360 / M_PI; return a; } /* ==================== CalcFovy ==================== */ static float CalcFovy (float fov_x, float width, float height) { float a, x; if (fov_x < 1 || fov_x > 179) Sys_Error ("Bad fov: %f", fov_x); x = width / tan(fov_x / 360 * M_PI); a = atan(height / x); a = a * 360 / M_PI; return a; } /* ================= SCR_CalcRefdef Must be called whenever vid changes Internal use only ================= */ static void SCR_CalcRefdef (void) { float size; int h; scr_fullupdate = 0; // force a background redraw // bound viewsize if (scr_viewsize.integer < 30) Cvar_SetQuick (&scr_viewsize, "30"); else if (scr_viewsize.integer > 130) Cvar_SetQuick (&scr_viewsize, "130"); // bound field of view if (scr_fov.integer < 10) Cvar_SetQuick (&scr_fov, "10"); else if (scr_fov.integer > 170) Cvar_SetQuick (&scr_fov, "170"); vid.recalc_refdef = 0; // force the status bar to redraw SB_ViewSizeChanged (); Sbar_Changed(); if (scr_viewsize.integer >= 110) sb_lines = 0; // no status bar else sb_lines = 36; // FIXME: why not 46, i.e. BAR_TOP_HEIGHT? size = scr_viewsize.integer > 100 ? 100.0 : scr_viewsize.integer; if (cl.intermission) { size = 100.0; // intermission is always full screen sb_lines = 0; } size /= 100.0; h = vid.height - sb_lines; r_refdef.vrect.width = vid.width * size; if (r_refdef.vrect.width < 96) { size = 96.0 / vid.width; r_refdef.vrect.width = 96; // min for icons } r_refdef.vrect.height = vid.height * size; if (r_refdef.vrect.height > vid.height - sb_lines) r_refdef.vrect.height = vid.height - sb_lines; r_refdef.vrect.x = (vid.width - r_refdef.vrect.width)/2; r_refdef.vrect.y = (h - r_refdef.vrect.height)/2; r_refdef.fov_x = AdaptFovx (scr_fov.value, r_refdef.vrect.width, r_refdef.vrect.height); r_refdef.fov_y = CalcFovy (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height); scr_vrect = r_refdef.vrect; } //============================================================================= /* ================= SCR_SizeUp_f Keybinding command ================= */ static void SCR_SizeUp_f (void) { Cvar_SetValueQuick (&scr_viewsize, scr_viewsize.integer + 10); } /* ================= SCR_SizeDown_f Keybinding command ================= */ static void SCR_SizeDown_f (void) { Cvar_SetValueQuick (&scr_viewsize, scr_viewsize.integer - 10); } static void SCR_Callback_refdef (cvar_t *var) { vid.recalc_refdef = 1; } //============================================================================= /* ================== SCR_Init ================== */ void SCR_Init (void) { scr_ram = Draw_PicFromWad ("ram"); scr_net = Draw_PicFromWad ("net"); scr_turtle = Draw_PicFromWad ("turtle"); if (draw_reinit) return; Cvar_SetCallback (&scr_fov, SCR_Callback_refdef); Cvar_SetCallback (&scr_fov_adapt, SCR_Callback_refdef); Cvar_SetCallback (&scr_viewsize, SCR_Callback_refdef); Cvar_RegisterVariable (&scr_fov); Cvar_RegisterVariable (&scr_fov_adapt); Cvar_RegisterVariable (&scr_viewsize); Cvar_RegisterVariable (&scr_contrans); Cvar_RegisterVariable (&scr_conspeed); Cvar_RegisterVariable (&scr_showram); Cvar_RegisterVariable (&scr_showturtle); Cvar_RegisterVariable (&scr_showpause); Cvar_RegisterVariable (&scr_showfps); Cvar_RegisterVariable (&scr_centertime); // Cvar_RegisterVariable (&gl_triplebuffer); Cmd_AddCommand ("screenshot",SCR_ScreenShot_f); Cmd_AddCommand ("sizeup",SCR_SizeUp_f); Cmd_AddCommand ("sizedown",SCR_SizeDown_f); scr_initialized = true; con_forcedup = true; // we're just initialized and not connected yet } //============================================================================= /* ============== SCR_DrawRam ============== */ static void SCR_DrawRam (void) { if (!scr_showram.integer) return; if (!r_cache_thrash) return; Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram); } /* ============== SCR_DrawTurtle ============== */ static void SCR_DrawTurtle (void) { static int count; if (!scr_showturtle.integer) return; if (host_frametime < 0.1) { count = 0; return; } count++; if (count < 3) return; Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle); } /* ============== SCR_DrawNet ============== */ static void SCR_DrawNet (void) { #if !defined(H2W) if (realtime - cl.last_received_message < 0.3) return; #else if (cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged < UPDATE_BACKUP-1) return; #endif if (cls.demoplayback) return; Draw_Pic (scr_vrect.x+64, scr_vrect.y, scr_net); } static void SCR_DrawFPS (void) { static double oldtime = 0; static double lastfps = 0; static int oldframecount = 0; double elapsed_time; int frames; char st[16]; int x, y; if (!scr_showfps.integer) return; elapsed_time = realtime - oldtime; frames = r_framecount - oldframecount; if (elapsed_time < 0 || frames < 0) { oldtime = realtime; oldframecount = r_framecount; return; } // update value every 3/4 second if (elapsed_time > 0.75) { lastfps = frames / elapsed_time; oldtime = realtime; oldframecount = r_framecount; } sprintf(st, "%4.0f FPS", lastfps); x = vid.width - strlen(st) * 8 - 8; y = vid.height - sb_lines - 8; // Draw_TileClear(x, y, strlen(st) * 8, 8); Draw_String(x, y, st); } /* ============== DrawPause ============== */ static void SCR_DrawPause (void) { static qboolean newdraw = false; static float LogoPercent, LogoTargetPercent; qpic_t *pic; int finaly; float delta; if (!scr_showpause.integer) // turn off for screenshots return; if (!cl.paused) { newdraw = false; return; } if (!newdraw) { newdraw = true; LogoTargetPercent = 1; LogoPercent = 0; } pic = Draw_CachePic ("gfx/menu/paused.lmp"); // Draw_Pic ( (vid.width - pic->width)/2, (vid.height - 48 - pic->height)/2, pic); if (LogoPercent < LogoTargetPercent) { delta = ((LogoTargetPercent - LogoPercent) / .5) * host_frametime; if (delta < 0.004) delta = 0.004; LogoPercent += delta; if (LogoPercent > LogoTargetPercent) LogoPercent = LogoTargetPercent; } finaly = ((float)pic->height * LogoPercent) - pic->height; Draw_TransPicCropped ( (vid.width - pic->width)/2, finaly, pic); } #if !defined(H2W) /* ============== SCR_DrawLoading ============== */ #if !defined(DRAW_PROGRESSBARS) void SCR_DrawLoading (void) { int offset; qpic_t *pic; if (!scr_drawloading && loading_stage == 0) return; pic = Draw_CacheLoadingPic (); offset = (vid.width - pic->width) / 2; Draw_TransPic (offset, 0, pic); } #else void SCR_DrawLoading (void) { int size, count, offset; qpic_t *pic; if (!scr_drawloading && loading_stage == 0) return; pic = Draw_CachePic ("gfx/menu/loading.lmp"); offset = (vid.width - pic->width) / 2; Draw_TransPic (offset, 0, pic); if (loading_stage == 0) return; size = (total_loading_size) ? (current_loading_size * 106 / total_loading_size) : 0; offset += 42; count = (loading_stage == 1) ? size : 106; if (count) { Draw_Fill (offset, 87+0, count, 1, 136); Draw_Fill (offset, 87+1, count, 4, 138); Draw_Fill (offset, 87+5, count, 1, 136); } count = (loading_stage == 2) ? size : 0; if (count) { Draw_Fill (offset, 97+0, count, 1, 168); Draw_Fill (offset, 97+1, count, 4, 170); Draw_Fill (offset, 97+5, count, 1, 168); } } #endif /* !DRAW_PROGRESSBARS */ /* =============== SCR_BeginLoadingPlaque ================ */ void SCR_BeginLoadingPlaque (void) { S_StopAllSounds (true); if (cls.state != ca_connected) return; if (cls.signon != SIGNONS) return; // redraw with no console and the loading plaque Con_ClearNotify (); scr_centertime_off = 0; scr_con_current = 0; scr_drawloading = true; scr_fullupdate = 0; Sbar_Changed(); SCR_UpdateScreen (); scr_drawloading = false; scr_disabled_for_loading = true; scr_disabled_time = realtime; scr_fullupdate = 0; } /* =============== SCR_EndLoadingPlaque ================ */ void SCR_EndLoadingPlaque (void) { scr_disabled_for_loading = false; scr_fullupdate = 0; Con_ClearNotify (); } #endif /* H2W */ //============================================================================= /* ================== SCR_SetUpToDrawConsole ================== */ static void SCR_SetUpToDrawConsole (void) { Con_CheckResize (); #if !defined(H2W) if (scr_drawloading) return; // never a console with loading plaque con_forcedup = !cl.worldmodel || cls.signon != SIGNONS; #else con_forcedup = cls.state != ca_active; #endif /* H2W */ // decide on the height of the console if (con_forcedup) { scr_conlines = vid.height; // full screen scr_con_current = scr_conlines; } else if (Key_GetDest() == key_console) scr_conlines = vid.height / 2; // half screen else scr_conlines = 0; // none visible if (scr_conlines < scr_con_current) { scr_con_current -= scr_conspeed.value * host_frametime; if (scr_conlines > scr_con_current) scr_con_current = scr_conlines; } else if (scr_conlines > scr_con_current) { scr_con_current += scr_conspeed.value * host_frametime; if (scr_conlines < scr_con_current) scr_con_current = scr_conlines; } if (clearconsole++ < vid.numpages) { Sbar_Changed(); } else if (clearnotify++ < vid.numpages) { } else con_notifylines = 0; } /* ================== SCR_DrawConsole ================== */ static void SCR_DrawConsole (void) { if (scr_con_current) { scr_copyeverything = 1; Con_DrawConsole (scr_con_current); clearconsole = 0; } else { keydest_t dest = Key_GetDest(); if (dest == key_game || dest == key_message) Con_DrawNotify (); // only draw notify in game } } /* ============================================================================== SCREEN SHOTS ============================================================================== */ typedef struct _TargaHeader { unsigned char id_length, colormap_type, image_type; unsigned short colormap_index, colormap_length; unsigned char colormap_size; unsigned short x_origin, y_origin, width, height; unsigned char pixel_size, attributes; } TargaHeader; /* ================== SCR_ScreenShot_f ================== */ #if !defined(H2W) static const char scr_shotbase[] = "shots/hexen00.tga"; #define SHOTNUM_POS 11 #else static const char scr_shotbase[] = "shots/hw00.tga"; #define SHOTNUM_POS 8 #endif static void SCR_ScreenShot_f (void) { char pcxname[80]; char checkname[MAX_OSPATH]; int i, size, temp; qboolean freebuf = false; byte *buffer; FS_MakePath_BUF (FS_USERDIR, NULL, checkname, sizeof(checkname), "shots"); Sys_mkdir (checkname, false); // find a slot to save it to q_strlcpy (pcxname, scr_shotbase, sizeof(pcxname)); for (i = 0; i <= 99; i++) { pcxname[SHOTNUM_POS+0] = i/10 + '0'; pcxname[SHOTNUM_POS+1] = i%10 + '0'; FS_MakePath_BUF (FS_USERDIR, NULL, checkname, sizeof(checkname), pcxname); if (Sys_FileType(checkname) == FS_ENT_NONE) break; // file doesn't exist } if (i == 100) { Con_Printf ("%s: Couldn't create a TGA file\n", __thisfunc__); return; } size = glwidth * glheight * 3 + 18; buffer = (byte *) Hunk_TempAlloc(size); if (!buffer) { buffer = (byte *) malloc(size); if (!buffer) { Con_Printf("%s: not enough memory\n", __thisfunc__); return; } freebuf = true; } memset (buffer, 0, 18); buffer[2] = 2; // uncompressed type buffer[12] = glwidth & 255; buffer[13] = glwidth >> 8; buffer[14] = glheight & 255; buffer[15] = glheight >> 8; buffer[16] = 24; // pixel size glPixelStorei_fp (GL_PACK_ALIGNMENT, 1);/* for widths that aren't a multiple of 4 */ glReadPixels_fp (glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE, buffer+18); // swap rgb to bgr for (i = 18; i < size; i += 3) { temp = buffer[i]; buffer[i] = buffer[i+2]; buffer[i+2] = temp; } i = FS_WriteFile (pcxname, buffer, size); if (freebuf) free(buffer); if (i == 0) Con_Printf ("Wrote %s\n", pcxname); } //============================================================================= static const char *scr_notifystring; static qboolean scr_drawdialog; static void SCR_DrawNotifyString (void) { Plaque_Draw(scr_notifystring, true); } /* ================== SCR_ModalMessage Displays a text string in the center of the screen and waits for a Y or N keypress. ================== */ int SCR_ModalMessage (const char *text) { #if !defined(H2W) if (cls.state == ca_dedicated) return true; #endif /* H2W */ scr_notifystring = text; // draw a fresh screen scr_fullupdate = 0; scr_drawdialog = true; SCR_UpdateScreen (); scr_drawdialog = false; S_ClearBuffer (); // so dma doesn't loop current sound do { key_count = -1; // wait for a key down and up Sys_SendKeyEvents (); } while (key_lastpress != 'y' && key_lastpress != 'n' && key_lastpress != K_ESCAPE); scr_fullupdate = 0; SCR_UpdateScreen (); return key_lastpress == 'y'; } //============================================================================= /* =============== SCR_BringDownConsole Brings the console down and fades the palettes back to normal ================ */ #if 0 /* all uses are commented out */ void SCR_BringDownConsole (void) { int i; scr_centertime_off = 0; for (i = 0; i < 20 && scr_conlines != scr_con_current; i++) SCR_UpdateScreen (); cl.cshifts[0].percent = 0; // no area contents palette on next frame VID_SetPalette (host_basepal); } #endif //============================================================================= void SCR_SetPlaqueMessage (const char *msg) { plaquemessage = msg; } static void Plaque_Draw (const char *message, qboolean AlwaysDraw) { int i, cnt; int bx, by; char temp[80]; if (scr_con_current == vid.height && !AlwaysDraw) return; // console is full screen if (!*message) return; FindTextBreaks(message, PLAQUE_WIDTH); by = (25-lines) * 8 / 2 + ((vid.height - 200)>>1); M_DrawTextBox (32, by - 16, PLAQUE_WIDTH + 4, lines + 2); for (i = 0; i < lines; i++, by += 8) { cnt = EndC[i] - StartC[i]; strncpy (temp, &message[StartC[i]], cnt); temp[cnt] = 0; bx = (40-strlen(temp)) * 8 / 2; M_Print (bx, by, temp); } } #if !defined(H2W) static void Info_Plaque_Draw (const char *message) { int i, cnt; int bx, by; char temp[80]; if (scr_con_current == vid.height) return; // console is full screen if (!info_string_count || !*message) return; FindTextBreaks(message, PLAQUE_WIDTH+4); if (lines == MAXLINES) { Con_DPrintf("%s: line overflow error\n", __thisfunc__); lines = MAXLINES-1; } by = (25-lines) * 8 / 2 + ((vid.height - 200)>>1); M_DrawTextBox (15, by - 16, PLAQUE_WIDTH + 4 + 4, lines + 2); for (i = 0; i < lines; i++, by += 8) { cnt = EndC[i] - StartC[i]; strncpy (temp, &message[StartC[i]], cnt); temp[cnt] = 0; bx = (40-strlen(temp)) * 8 / 2; M_Print (bx, by, temp); } } static void Bottom_Plaque_Draw (const char *message) { int i, cnt; int bx, by; char temp[80]; if (!*message) return; FindTextBreaks(message, PLAQUE_WIDTH); by = (((vid.height) / 8) - lines - 2) * 8; M_DrawTextBox (32, by - 16, PLAQUE_WIDTH + 4, lines + 2); for (i = 0; i < lines; i++, by += 8) { cnt = EndC[i] - StartC[i]; strncpy (temp, &message[StartC[i]], cnt); temp[cnt] = 0; bx = (40-strlen(temp)) * 8 / 2; M_Print (bx, by, temp); } } //============================================================================= static void I_Print (int cx, int cy, const char *str, int flags) { int num, x, y; const char *s; x = cx + ((vid.width - 320)>>1); y = cy; if (!(flags & (INTERMISSION_PRINT_TOP|INTERMISSION_PRINT_TOPMOST))) y += ((vid.height - 200)>>1); s = str; while (*s) { num = (unsigned char)(*s); if (!(flags & INTERMISSION_PRINT_WHITE)) num += 256; Draw_Character (x, y, num); s++; x += 8; } } #if FULLSCREEN_INTERMISSIONS # define Load_IntermissionPic_FN(X,Y,Z) Draw_CachePicNoTrans((X)) # define Draw_IntermissionPic_FN(X,Y,Z) Draw_IntermissionPic((Z)) #else # define Load_IntermissionPic_FN(X,Y,Z) Draw_CachePic((X)) # define Draw_IntermissionPic_FN(X,Y,Z) Draw_Pic((X),(Y),(Z)) #endif /* =============== SB_IntermissionOverlay =============== */ static void SB_IntermissionOverlay (void) { qpic_t *pic; int elapsed, size, bx, by, i; char temp[80]; const char *message; scr_copyeverything = 1; scr_fullupdate = 0; #if !defined(H2W) if (cl.gametype == GAME_DEATHMATCH) #else if (!cl_siege) #endif { Sbar_DeathmatchOverlay (); return; } if (cl.intermission_pic == NULL) Host_Error ("%s: NULL intermission picture", __thisfunc__); else { pic = Load_IntermissionPic_FN (cl.intermission_pic, vid.width, vid.height); Draw_IntermissionPic_FN (((vid.width - 320)>>1), ((vid.height - 200)>>1), pic); } if (cl.message_index >= 0 && cl.message_index < host_string_count) message = Host_GetString (cl.message_index); else if (cl.intermission_flags & INTERMISSION_NO_MESSAGE) message = ""; else { message = ""; /* silence compilers */ Host_Error ("%s: Intermission string #%d not available (host_string_count: %d)", __thisfunc__, cl.message_index, host_string_count); } if (cl.intermission_flags & INTERMISSION_NOT_CONNECTED) elapsed = (realtime - cl.completed_time) * 20; else elapsed = (cl.time - cl.completed_time) * 20; if (cl.intermission_flags & INTERMISSION_PRINT_DELAY) { elapsed -= 50; /* delay about 2.5 seconds */ if (elapsed < 0) elapsed = 0; } FindTextBreaks(message, 38); if (cl.intermission_flags & INTERMISSION_PRINT_TOPMOST) by = 16; else by = (25-lines) * 8 / 2; for (i = 0; i < lines; i++, by += 8) { size = EndC[i] - StartC[i]; strncpy (temp, &message[StartC[i]], size); if (size > elapsed) size = elapsed; temp[size] = 0; bx = (40-strlen(temp)) * 8 / 2; I_Print (bx, by, temp, cl.intermission_flags); elapsed -= size; if (elapsed <= 0) break; } if (i == lines && cl.lasting_time && elapsed >= 20*cl.lasting_time) CL_SetupIntermission (cl.intermission_next); } #endif /* H2W */ //============================================================================= /* =============== SCR_TileClear ================ */ static void SCR_TileClear (void) { if (vid.conwidth > 320) { if (r_refdef.vrect.x > 0) { // left Draw_TileClear (0, 0, r_refdef.vrect.x, vid.height); // right Draw_TileClear (r_refdef.vrect.x + r_refdef.vrect.width, 0, vid.width - r_refdef.vrect.x + r_refdef.vrect.width, vid.height); } // if (r_refdef.vrect.y > 0) // if (r_refdef.vrect.height < vid.height - 44) { // top Draw_TileClear (r_refdef.vrect.x, 0, r_refdef.vrect.x + r_refdef.vrect.width, r_refdef.vrect.y); // bottom Draw_TileClear (r_refdef.vrect.x, r_refdef.vrect.y + r_refdef.vrect.height, r_refdef.vrect.width, vid.height - (r_refdef.vrect.height + r_refdef.vrect.y)); } } else { if (r_refdef.vrect.x > 0) { // left Draw_TileClear (0, 0, r_refdef.vrect.x, vid.height - sb_lines); // right Draw_TileClear (r_refdef.vrect.x + r_refdef.vrect.width, 0, vid.width - r_refdef.vrect.x + r_refdef.vrect.width, vid.height - sb_lines); } if (r_refdef.vrect.y > 0) { // top Draw_TileClear (r_refdef.vrect.x, 0, r_refdef.vrect.x + r_refdef.vrect.width, r_refdef.vrect.y); // bottom Draw_TileClear (r_refdef.vrect.x, r_refdef.vrect.y + r_refdef.vrect.height, r_refdef.vrect.width, vid.height - sb_lines - (r_refdef.vrect.height + r_refdef.vrect.y)); } } } //============================================================================= /* ================== SCR_UpdateScreen This is called every frame, and can also be called explicitly to flush text to the screen. WARNING: be very careful calling this from elsewhere, because the refresh needs almost the entire 256k of stack space! ================== */ void SCR_UpdateScreen (void) { if (block_drawing) return; // vid.numpages = (gl_triplebuffer.integer)? 3 : 2; scr_copytop = 0; scr_copyeverything = 0; #if defined(H2W) if (scr_disabled_for_loading) return; #else if (scr_disabled_for_loading) { if (realtime - scr_disabled_time > 25) { /* this can happen with clients connected to servers * older than uHexen2-1.5.6 who don't issue an error * upon changelevel failures. Or, it could happen if * loading is taking a really long time. */ scr_disabled_for_loading = false; total_loading_size = 0; loading_stage = 0; Con_Printf ("load timeout.\n"); } else { return; } } if (cls.state == ca_dedicated) return; // stdout only #endif /* H2W */ if (!scr_initialized || !con_initialized) return; // not initialized yet GL_BeginRendering (&glx, &gly, &glwidth, &glheight); // // check for vid changes // if (vid.recalc_refdef) { // something changed, so reorder the screen SCR_CalcRefdef (); } // // do 3D refresh drawing, and then update the screen // SCR_SetUpToDrawConsole (); #if FULLSCREEN_INTERMISSIONS // no need to draw view in fullscreen intermission screens if (!cl.intermission) #endif V_RenderView (); GL_Set2D (); SCR_TileClear (); // draw any areas not covered by the refresh #if defined(H2W) if (r_netgraph.integer) R_NetGraph (); #endif /* H2W */ if (scr_drawdialog) { Sbar_Draw (); Draw_FadeScreen (); SCR_DrawNotifyString (); scr_copyeverything = true; } else if (cl.intermission) { #if !defined(H2W) SB_IntermissionOverlay(); if (!(cl.intermission_flags & INTERMISSION_NO_MENUS)) { SCR_DrawConsole(); M_Draw(); } if (scr_drawloading) SCR_DrawLoading(); #endif /* H2W */ } #if !defined(H2W) else if (scr_drawloading) { Draw_FadeScreen (); SCR_DrawLoading (); } #endif /* H2W */ else { if (crosshair.integer && !cls.demoplayback) Draw_Crosshair(); SCR_DrawRam(); SCR_DrawNet(); SCR_DrawTurtle(); SCR_DrawPause(); SCR_CheckDrawCenterString(); Sbar_Draw(); SCR_DrawFPS(); Plaque_Draw(plaquemessage, false); SCR_DrawConsole(); M_Draw(); #if !defined(H2W) if (info_up) { UpdateInfoMessage(); Info_Plaque_Draw(infomessage); } #endif /* H2W */ } V_UpdatePalette (); GL_EndRendering (); } engine/h2shared/gl_vidamiga.c000066400000000000000000001505321444734033100164430ustar00rootroot00000000000000/* * gl_vidamiga.c -- GL vid component for AmigaOS & variants. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2004-2005 Steven Atkinson * Copyright (C) 2005-2016 O.Sezer * Copyright (C) 2012-2016 Szilard Biro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef GL_DLSYM #error GL_DLSYM not supported. #endif #define __GL_FUNC_EXTERN #include #include #include #include #include #if defined(__AROS__) && defined(AROS_USE_GLA) #include #elif defined(__AROS__) #include #elif defined __MORPHOS__ #include #elif defined __amigaos4__ #error AmigaOS4 support not yet available #elif defined(__AMIGA__) && defined(REFGL_MINIGL) #include #elif defined(__AMIGA__) && defined(REFGL_AMESA) #include #else #error Unknown / Unsupported AmigaOS variant. #endif #if defined(__AROS__) && defined(AROS_USE_GLA) typedef GLAProc AMIGAGL_Proc; #elif defined(__AROS__) typedef AROSMesaProc AMIGAGL_Proc; #else typedef void (*AMIGAGL_Proc)(); #endif #include "quakedef.h" #include "cfgfile.h" #include "bgmusic.h" #include "cdaudio.h" #include "filenames.h" #include /* IPTR */ #define WARP_WIDTH 320 #define WARP_HEIGHT 200 #define MAXWIDTH 10000 #define MAXHEIGHT 10000 #define MIN_WIDTH 320 //#define MIN_HEIGHT 200 #define MIN_HEIGHT 240 #define MAX_DESC 33 typedef struct { modestate_t type; int width; int height; int modenum; int fullscreen; int bpp; /* int halfscreen;*/ char modedesc[MAX_DESC]; } vmode_t; typedef struct { int width; int height; } stdmode_t; #define RES_640X480 3 static const stdmode_t std_modes[] = { // NOTE: keep this list in order {320, 240}, // 0 {400, 300}, // 1 {512, 384}, // 2 {640, 480}, // 3 == RES_640X480, this is our default, below // this is the lowresmodes region. // either do not change its order, // or change the above define, too {800, 600}, // 4, RES_640X480 + 1 {1024, 768}, // 5, RES_640X480 + 2 {1280, 1024}, // 6 {1600, 1200} // 7 }; #define MAX_MODE_LIST 128 #define MAX_STDMODES (sizeof(std_modes) / sizeof(std_modes[0])) #define NUM_LOWRESMODES (RES_640X480) static vmode_t fmodelist[MAX_MODE_LIST+1]; // list of enumerated fullscreen modes static vmode_t wmodelist[MAX_STDMODES +1]; // list of standart 4:3 windowed modes static vmode_t *modelist; // modelist in use, points to one of the above lists static int num_fmodes; static int num_wmodes; static int *nummodes; static int bpp = 16; #if defined(H2W) # define WM_TITLEBAR_TEXT "HexenWorld" /*# define WM_ICON_TEXT "HexenWorld"*/ #else # define WM_TITLEBAR_TEXT "Hexen II" /*# define WM_ICON_TEXT "HEXEN2"*/ #endif typedef struct { int red, green, blue, alpha, depth, stencil; } attributes_t; static attributes_t vid_attribs; static void VID_KillContext (void); struct Window *window = NULL; /* used by in_amiga.c */ static struct Screen *screen = NULL; #if defined(__AROS__) && defined(AROS_USE_GLA) static GLAContext context = NULL; #elif defined(__AROS__) static AROSMesaContext context = NULL; #elif defined __MORPHOS__ GLContext *__tglContext = NULL; static qboolean contextinit = false; #elif defined(__AMIGA__) && defined(REFGL_MINIGL) #ifdef __CLIB2__ struct GfxBase *GfxBase = NULL; #endif static int lockmode = MGL_LOCK_MANUAL; static cvar_t gl_lockmode = {"gl_lockmode", "manual", CVAR_ARCHIVE}; #elif defined(__AMIGA__) && defined(REFGL_AMESA) #ifdef __CLIB2__ struct GfxBase *GfxBase = NULL; #endif struct Library *CyberGfxBase = NULL; static AmigaMesaContext context = NULL; #endif static qboolean vid_menu_fs; static qboolean fs_toggle_works = false; // vars for vid state viddef_t vid; // global video state modestate_t modestate = MS_UNINIT; static int vid_default = -1; // modenum of 640x480 as a safe default static int vid_modenum = -1; // current video mode, set after mode setting succeeds static int vid_maxwidth = 640, vid_maxheight = 480; static qboolean vid_conscale = false; static int WRHeight, WRWidth; static qboolean vid_initialized = false; qboolean in_mode_set = false; // cvar vid_mode must be set before calling // VID_SetMode, VID_ChangeVideoMode or VID_Restart_f static cvar_t vid_mode = {"vid_mode", "0", CVAR_NONE}; static cvar_t vid_config_consize = {"vid_config_consize", "640", CVAR_ARCHIVE}; static cvar_t vid_config_glx = {"vid_config_glx", "640", CVAR_ARCHIVE}; static cvar_t vid_config_gly = {"vid_config_gly", "480", CVAR_ARCHIVE}; #ifdef PLATFORM_AMIGAOS3 static cvar_t vid_config_fscr= {"vid_config_fscr", "1", CVAR_ARCHIVE}; #else static cvar_t vid_config_fscr= {"vid_config_fscr", "0", CVAR_ARCHIVE}; #endif // cvars for compatibility with the software version static cvar_t vid_config_swx = {"vid_config_swx", "320", CVAR_ARCHIVE}; static cvar_t vid_config_swy = {"vid_config_swy", "240", CVAR_ARCHIVE}; static cvar_t vid_config_mon = {"vid_config_mon", "0", CVAR_ARCHIVE}; byte globalcolormap[VID_GRADES*256]; float RTint[256], GTint[256], BTint[256]; unsigned short d_8to16table[256]; unsigned int d_8to24table[256]; unsigned int d_8to24TranslucentTable[256]; unsigned char *inverse_pal; // gl stuff static void GL_Init (void); static const char *gl_vendor; static const char *gl_renderer; static const char *gl_version; static const char *gl_extensions; qboolean is_3dfx = false; GLint gl_max_size = 256; static qboolean have_NPOT = false; qboolean gl_tex_NPOT = false; static cvar_t gl_texture_NPOT = {"gl_texture_NPOT", "0", CVAR_ARCHIVE}; GLfloat gl_max_anisotropy; float gldepthmin, gldepthmax; // palettized textures static qboolean have8bit = false; qboolean is8bit = false; static cvar_t vid_config_gl8bit = {"vid_config_gl8bit", "0", CVAR_ARCHIVE}; static qboolean gammaworks = false; // whether hw-gamma works // multitexturing qboolean gl_mtexable = false; static GLint num_tmus = 1; static qboolean have_mtex = false; static cvar_t gl_multitexture = {"gl_multitexture", "0", CVAR_ARCHIVE}; // stencil buffer qboolean have_stencil = false; // this is useless: things aren't like those in quake //static qboolean fullsbardraw = false; // menu drawing static void VID_MenuDraw (void); static void VID_MenuKey (int key); // input stuff static void ClearAllStates (void); static int enable_mouse; cvar_t _enable_mouse = {"_enable_mouse", "1", CVAR_ARCHIVE}; //==================================== static qboolean GL_ParseExtensionList (const char *list, const char *name) { const char *start; const char *where, *terminator; if (!list || !name || !*name) return false; if (strchr(name, ' ') != NULL) return false; // extension names must not have spaces start = list; while (1) { where = strstr (start, name); if (!where) break; terminator = where + strlen (name); if (where == start || where[-1] == ' ') if (*terminator == ' ' || *terminator == '\0') return true; start = terminator; } return false; } //==================================== void VID_LockBuffer (void) { // nothing to do } void VID_UnlockBuffer (void) { // nothing to do } void VID_HandlePause (qboolean paused) { if (_enable_mouse.integer/* && (modestate == MS_WINDOWED)*/) { // for consistency, don't show pointer - S.A if (paused) { IN_DeactivateMouse (); // IN_ShowMouse (); } else { IN_ActivateMouse (); // IN_HideMouse (); } } } //==================================== static void VID_ConWidth (int modenum) { int w, h; if (!vid_conscale) { Cvar_SetValueQuick (&vid_config_consize, modelist[modenum].width); return; } w = vid_config_consize.integer; w &= ~7; /* make it a multiple of eight */ if (w < MIN_WIDTH) w = MIN_WIDTH; else if (w > modelist[modenum].width) w = modelist[modenum].width; h = w * modelist[modenum].height / modelist[modenum].width; if (h < 200 /* MIN_HEIGHT */ || h > modelist[modenum].height || w > modelist[modenum].width) { vid_conscale = false; Cvar_SetValueQuick (&vid_config_consize, modelist[modenum].width); return; } vid.width = vid.conwidth = w; vid.height = vid.conheight = h; if (w != modelist[modenum].width) vid_conscale = true; else vid_conscale = false; } void VID_ChangeConsize (int dir) { int w, h; switch (dir) { case -1: /* smaller text */ w = ((float)vid.conwidth/(float)vid.width + 0.05f) * vid.width; /* use 0.10f increment ?? */ w &= ~7; /* make it a multiple of eight */ if (w > modelist[vid_modenum].width) w = modelist[vid_modenum].width; break; case 1: /* bigger text */ w = ((float)vid.conwidth/(float)vid.width - 0.05f) * vid.width; w &= ~7; /* make it a multiple of eight */ if (w < MIN_WIDTH) w = MIN_WIDTH; break; default: /* bad key */ return; } h = w * modelist[vid_modenum].height / modelist[vid_modenum].width; if (h < 200) return; vid.width = vid.conwidth = w; vid.height = vid.conheight = h; Cvar_SetValueQuick (&vid_config_consize, vid.conwidth); vid.recalc_refdef = 1; if (vid.conwidth != modelist[vid_modenum].width) vid_conscale = true; else vid_conscale = false; } float VID_ReportConsize(void) { return (float)modelist[vid_modenum].width/vid.conwidth; } static qboolean VID_SetMode (int modenum) { ULONG flags; in_mode_set = true; VID_KillContext(); flags = WFLG_ACTIVATE | WFLG_RMBTRAP; Con_SafePrintf ("Requested mode %d: %dx%dx%d\n", modenum, modelist[modenum].width, modelist[modenum].height, bpp); #if defined(REFGL_MINIGL) /* Hyperion's MiniGL 1.2 handles window creation within itself. */ if (vid_config_fscr.integer) mglChooseWindowMode(GL_FALSE); else mglChooseWindowMode(GL_TRUE ); #else /* ! REFGL_MINIGL */ if (vid_config_fscr.integer) { ULONG ModeID; ModeID = BestCModeIDTags( CYBRBIDTG_Depth, bpp, CYBRBIDTG_NominalWidth, modelist[modenum].width, CYBRBIDTG_NominalHeight, modelist[modenum].height, TAG_DONE); #ifndef __MORPHOS__ screen = OpenScreenTags(0, ModeID != INVALID_ID ? SA_DisplayID : TAG_IGNORE, ModeID, SA_Width, modelist[modenum].width, SA_Height, modelist[modenum].height, SA_Depth, bpp, SA_Quiet, TRUE, TAG_DONE); #else screen = OpenScreenTags(0, ModeID != INVALID_ID ? SA_DisplayID : TAG_IGNORE, ModeID, SA_Width, modelist[modenum].width, SA_Height, modelist[modenum].height, SA_Depth, bpp, SA_Quiet, TRUE, SA_GammaControl, TRUE, SA_3DSupport, TRUE, TAG_DONE); #endif } if (screen) { flags |= WFLG_BACKDROP | WFLG_BORDERLESS; } else { Cvar_SetValueQuick (&vid_config_fscr, 0); flags |= WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET; } window = OpenWindowTags(0, WA_InnerWidth, modelist[modenum].width, WA_InnerHeight, modelist[modenum].height, WA_Title, (IPTR)WM_TITLEBAR_TEXT, WA_Flags, flags, screen ? WA_CustomScreen : TAG_IGNORE, (IPTR)screen, WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_ACTIVEWINDOW | IDCMP_INACTIVEWINDOW, TAG_DONE); if (!window) goto fail; #endif /* ! REFGL_MINIGL */ WRWidth = vid.width = vid.conwidth = modelist[modenum].width; WRHeight = vid.height = vid.conheight = modelist[modenum].height; #if defined(__AROS__) && defined(AROS_USE_GLA) context = glACreateContextTags( GLA_Window, window, GLA_Left, screen ? 0 : window->BorderLeft, GLA_Top, screen ? 0 : window->BorderTop, GLA_Width, vid.width, GLA_Height, vid.height, screen ? GLA_Screen : TAG_IGNORE, screen, GLA_DoubleBuf, GL_TRUE, GLA_RGBMode, GL_TRUE, GLA_NoAccum, GL_TRUE, TAG_DONE); if (!context) goto fail; glAMakeCurrent(context); #elif defined(__AROS__) context = AROSMesaCreateContextTags( AMA_Window, window, AMA_Left, screen ? 0 : window->BorderLeft, AMA_Top, screen ? 0 : window->BorderTop, AMA_Width, vid.width, AMA_Height, vid.height, screen ? AMA_Screen : TAG_IGNORE, screen, AMA_DoubleBuf, GL_TRUE, AMA_RGBMode, GL_TRUE, AMA_NoAccum, GL_TRUE, TAG_DONE); if (!context) goto fail; AROSMesaMakeCurrent(context); #elif defined __MORPHOS__ __tglContext = GLInit(); if (!__tglContext) goto fail; if (screen && !(TinyGLBase->lib_Version == 0 && TinyGLBase->lib_Revision < 4)) { if (!(contextinit = glAInitializeContextScreen(screen))) goto fail; } else { if (!(contextinit = glAInitializeContextWindowed(window))) goto fail; } #elif defined(__AMIGA__) && defined(REFGL_MINIGL) if (!mglCreateContext(0,0,vid.width,vid.height)) goto fail; mglLockMode(lockmode); window = MGLGetWindowHandle(mini_CurrentContext); ModifyIDCMP(window, IDCMP_CLOSEWINDOW | IDCMP_ACTIVEWINDOW | IDCMP_INACTIVEWINDOW); #elif defined(__AMIGA__) && defined(REFGL_AMESA) context = AmigaMesaCreateContextTags( AMA_Window, window, AMA_Left, screen ? 0 : window->BorderLeft, AMA_Bottom, screen ? 0 : window->BorderBottom, AMA_Width, vid.width, AMA_Height, vid.height, screen ? AMA_Screen : TAG_IGNORE, screen, AMA_DoubleBuf, GL_TRUE, AMA_RGBMode, GL_TRUE, AMA_NoAccum, GL_TRUE, TAG_DONE); if (!context) goto fail; AmigaMesaMakeCurrent(context, context->buffer); #endif vid.height = vid.conheight = modelist[modenum].height; vid.rowbytes = vid.conrowbytes = vid.width = vid.conwidth = modelist[modenum].width; // set vid_modenum properly and adjust other vars vid_modenum = modenum; modestate = (screen) ? MS_FULLDIB : MS_WINDOWED; Cvar_SetValueQuick (&vid_config_glx, modelist[vid_modenum].width); Cvar_SetValueQuick (&vid_config_gly, modelist[vid_modenum].height); // setup the effective console width VID_ConWidth(modenum); Con_SafePrintf ("Video Mode Set : %dx%dx%d\n", modelist[modenum].width, modelist[modenum].height, bpp); //IN_HideMouse (); in_mode_set = false; return true; fail: VID_KillContext(); in_mode_set = false; return false; } //==================================== #if defined(__AROS__) && defined(AROS_USE_GLA) static AMIGAGL_Proc AMIGAGL_GetProcAddress (const char *s) { return glAGetProcAddress((const GLubyte *) s); } #elif defined(__AROS__) static AMIGAGL_Proc AMIGAGL_GetProcAddress (const char *s) { return AROSMesaGetProcAddress((const GLubyte *) s); } #elif defined(__MORPHOS__) static void MY_glMultiTexCoord2fARB (GLenum unit, GLfloat s, GLfloat t) { GLMultiTexCoord2fARB(__tglContext, unit, s, t); } static void MY_glActiveTextureARB (GLenum unit) { GLActiveTextureARB(__tglContext, unit); } static AMIGAGL_Proc AMIGAGL_GetProcAddress (const char *s) { if (strcmp(s, "glMultiTexCoord2fARB") == 0) return (AMIGAGL_Proc)MY_glMultiTexCoord2fARB; if (strcmp(s, "glActiveTextureARB") == 0) return (AMIGAGL_Proc)MY_glActiveTextureARB; return NULL; } #elif defined(__AMIGA__) && defined(REFGL_MINIGL) static void MY_glMultiTexCoord2fARB (GLenum unit, GLfloat s, GLfloat t) { GLMultiTexCoord2fARB(mini_CurrentContext, unit, s, t); } static void MY_glActiveTextureARB (GLenum unit) { GLActiveTextureARB(mini_CurrentContext, unit); } static AMIGAGL_Proc AMIGAGL_GetProcAddress (const char *s) { if (strcmp(s, "glMultiTexCoord2fARB") == 0) return (AMIGAGL_Proc)MY_glMultiTexCoord2fARB; if (strcmp(s, "glActiveTextureARB") == 0) return (AMIGAGL_Proc)MY_glActiveTextureARB; return NULL; } #elif defined(__AMIGA__) && defined(REFGL_AMESA) /* NOTE: StormMesa 3.0 has EXT_multitexture, not ARB_multitexture.. */ /* 250 == ( GL_TEXTURE0_ARB - GL_TEXTURE0_EXT ) */ static void MY_glMultiTexCoord2fARB (GLenum unit, GLfloat s, GLfloat t) { glMultiTexCoord2fEXT (unit - 250, s, t); } static void MY_glActiveTextureARB (GLenum unit) { glSelectTextureEXT (unit - 250); } static void MY_glColorTableEXT (GLenum target, GLenum internalfmt, GLsizei width, GLenum format, GLenum type, const GLvoid *table) { glColorTableEXT (target, internalfmt, width, format, type, table); } static void MY_glGetTexParameterfv (GLenum target, GLenum pname, GLfloat *params) { glGetTexParameterfv (target, pname, params); } static AMIGAGL_Proc AMIGAGL_GetProcAddress (const char *s) { if (strcmp(s, "glMultiTexCoord2fARB") == 0) return (AMIGAGL_Proc)MY_glMultiTexCoord2fARB; if (strcmp(s, "glActiveTextureARB") == 0) return (AMIGAGL_Proc)MY_glActiveTextureARB; if (strcmp(s, "glColorTableEXT") == 0) return (AMIGAGL_Proc)MY_glColorTableEXT; if (strcmp(s, "glGetTexParameterfv") == 0) return (AMIGAGL_Proc)MY_glGetTexParameterfv; return NULL; } #endif #if 0 /* No.. */ static void CheckSetGlobalPalette (void) { gl3DfxSetPaletteEXT_f gl3DfxSetPaletteEXT_fp; if (GL_ParseExtensionList(gl_extensions, "3DFX_set_global_palette")) { gl3DfxSetPaletteEXT_fp = (gl3DfxSetPaletteEXT_f) AMIGAGL_GetProcAddress("gl3DfxSetPaletteEXT"); if (!gl3DfxSetPaletteEXT_fp) gl3DfxSetPaletteEXT_fp = (gl3DfxSetPaletteEXT_f) AMIGAGL_GetProcAddress("3DFX_set_global_palette"); if (!gl3DfxSetPaletteEXT_fp) return; Con_SafePrintf("Found 3DFX_set_global_palette\n"); } else if (GL_ParseExtensionList(gl_extensions, "POWERVR_set_global_palette")) { gl3DfxSetPaletteEXT_fp = (gl3DfxSetPaletteEXT_f) AMIGAGL_GetProcAddress("glSetGlobalPalettePOWERVR"); if (!gl3DfxSetPaletteEXT_fp) gl3DfxSetPaletteEXT_fp = (gl3DfxSetPaletteEXT_f) AMIGAGL_GetProcAddress("POWERVR_set_global_palette"); if (!gl3DfxSetPaletteEXT_fp) return; Con_SafePrintf("Found POWERVR_set_global_palette\n"); } else { return; } have8bit = true; if (!vid_config_gl8bit.integer) return; else { int i; GLubyte table[256][4]; char *oldpal; is8bit = true; oldpal = (char *) d_8to24table; for (i = 0; i < 256; i++) { table[i][2] = *oldpal++; table[i][1] = *oldpal++; table[i][0] = *oldpal++; table[i][3] = 255; oldpal++; } glEnable_fp (GL_SHARED_TEXTURE_PALETTE_EXT); gl3DfxSetPaletteEXT_fp ((GLuint *)table); } } #endif /* #if 0 */ static void CheckSharedTexturePalette (void) { glColorTableEXT_f glColorTableEXT_fp; if (!GL_ParseExtensionList(gl_extensions, "GL_EXT_shared_texture_palette")) return; glColorTableEXT_fp = (glColorTableEXT_f) AMIGAGL_GetProcAddress("glColorTableEXT"); if (glColorTableEXT_fp == NULL) return; have8bit = true; Con_SafePrintf("Found GL_EXT_shared_texture_palette\n"); if (!vid_config_gl8bit.integer) return; else { int i; char thePalette[256*3]; char *oldPalette, *newPalette; is8bit = true; oldPalette = (char *) d_8to24table; newPalette = thePalette; for (i = 0; i < 256; i++) { *newPalette++ = *oldPalette++; *newPalette++ = *oldPalette++; *newPalette++ = *oldPalette++; oldPalette++; } glEnable_fp (GL_SHARED_TEXTURE_PALETTE_EXT); glColorTableEXT_fp (GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, (void *) thePalette); } } static void VID_Init8bitPalette (void) { have8bit = false; is8bit = false; /* Check for 8bit Extensions and initialize them */ CheckSharedTexturePalette(); #if 0 /* No.. */ if (!have8bit) CheckSetGlobalPalette(); #endif if (is8bit) Con_SafePrintf("8-bit palettized textures enabled\n"); } static void VID_InitGamma (void) { gammaworks = false; #ifdef __MORPHOS__ if (screen && (IntuitionBase->LibNode.lib_Version > 50 || (IntuitionBase->LibNode.lib_Version == 50 && IntuitionBase->LibNode.lib_Revision >= 74))) gammaworks = true; #endif if (!gammaworks) Con_SafePrintf("gamma adjustment not available\n"); } void VID_ShiftPalette (const unsigned char *palette) { #ifdef __MORPHOS__ if (screen) { SetAttrs(screen, SA_GammaRed, gammatable, SA_GammaGreen, gammatable, SA_GammaBlue, gammatable, TAG_DONE); } #endif } static void CheckMultiTextureExtensions (void) { gl_mtexable = have_mtex = false; if (COM_CheckParm("-nomtex")) { Con_SafePrintf("Multitexture extensions disabled\n"); } #if defined (REFGL_AMESA) /* StormMesa 3.0 has EXT_multitexture .. */ else if (GL_ParseExtensionList(gl_extensions, "GL_EXT_multitexture")) #else else if (GL_ParseExtensionList(gl_extensions, "GL_ARB_multitexture")) #endif { Con_SafePrintf("ARB Multitexture extensions found\n"); #if defined (REFGL_AMESA) glGetIntegerv_fp(GL_MAX_TEXTURES_EXT, &num_tmus); #else glGetIntegerv_fp(GL_MAX_TEXTURE_UNITS_ARB, &num_tmus); #endif if (num_tmus < 2) { Con_SafePrintf("ignoring multitexture (%i TMUs)\n", (int) num_tmus); return; } glMultiTexCoord2fARB_fp = (glMultiTexCoord2fARB_f) AMIGAGL_GetProcAddress("glMultiTexCoord2fARB"); glActiveTextureARB_fp = (glActiveTextureARB_f) AMIGAGL_GetProcAddress("glActiveTextureARB"); if (glMultiTexCoord2fARB_fp == NULL || glActiveTextureARB_fp == NULL) { Con_SafePrintf ("Couldn't link to multitexture functions\n"); return; } have_mtex = true; if (!gl_multitexture.integer) { Con_SafePrintf("ignoring multitexture (cvar disabled)\n"); return; } Con_SafePrintf("Found %i TMUs support\n", (int) num_tmus); gl_mtexable = true; glDisable_fp(GL_TEXTURE_2D); glActiveTextureARB_fp(GL_TEXTURE0_ARB); } else { Con_SafePrintf("GL_ARB_multitexture not found\n"); } } static void CheckAnisotropyExtensions (void) { gl_max_anisotropy = 1; Con_SafePrintf("Anisotropic filtering "); if (GL_ParseExtensionList(gl_extensions, "GL_EXT_texture_filter_anisotropic")) { GLfloat test1 = 0, test2 = 0; GLuint tex; glGetTexParameterfv_f glGetTexParameterfv_fp; glGetTexParameterfv_fp = (glGetTexParameterfv_f) AMIGAGL_GetProcAddress("glGetTexParameterfv"); if (glGetTexParameterfv_fp == NULL) { Con_SafePrintf("... can't check driver-lock status\n... "); goto _skiptest; } // test to make sure we really have control over it // 1.0 and 2.0 should always be legal values. glGenTextures_fp(1, &tex); glBindTexture_fp(GL_TEXTURE_2D, tex); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); glGetTexParameterfv_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &test1); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 2.0f); glGetTexParameterfv_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &test2); glDeleteTextures_fp(1, &tex); if (test1 != 1 || test2 != 2) { Con_SafePrintf("driver-locked @ %.1f\n", test1); } else { _skiptest: glGetFloatv_fp(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_max_anisotropy); if (gl_max_anisotropy < 2) Con_SafePrintf("broken\n"); else Con_SafePrintf("found, max %.1f\n", gl_max_anisotropy); } } else { Con_SafePrintf("not found\n"); } } static void CheckNonPowerOfTwoTextures (void) { /* On Mac OS X, old Radeons lie about NPOT textures capability, they * fallback to software with mipmap NPOT textures. see, e.g.: * http://lists.apple.com/archives/mac-opengl/2006/Dec/msg00000.html * http://lists.apple.com/archives/mac-opengl/2009/Oct/msg00040.html * http://www.idevgames.com/forums/printthread.php?tid=3814&page=2 * MH says NVIDIA once did the same with their GeForce FX on Windows: * http://forums.inside3d.com/viewtopic.php?f=10&t=4832 * Therefore, advertisement of this extension is an unreliable way of * detecting the actual capability. */ gl_tex_NPOT = have_NPOT = false; if (GL_ParseExtensionList(gl_extensions, "GL_ARB_texture_non_power_of_two")) { have_NPOT = true; Con_SafePrintf("Found ARB_texture_non_power_of_two\n"); if (!gl_texture_NPOT.integer) { Con_SafePrintf("ignoring texture_NPOT (cvar disabled)\n"); } else { gl_tex_NPOT = true; } } else { Con_SafePrintf("GL_ARB_texture_non_power_of_two not found\n"); } } static void CheckStencilBuffer (void) { have_stencil = !!vid_attribs.stencil; } static void GL_ResetFunctions (void) { #define GL_FUNCTION_OPT(ret, func, params) \ func##_fp = NULL; #include "gl_func.h" have_stencil = false; gl_mtexable = false; have_mtex = false; have8bit = false; is8bit = false; have_NPOT = false; gl_tex_NPOT = false; } /* =============== GL_Init =============== */ static void GL_Init (void) { // collect the visual attributes memset (&vid_attribs, 0, sizeof(attributes_t)); glGetIntegerv_fp(GL_RED_BITS, &vid_attribs.red); glGetIntegerv_fp(GL_GREEN_BITS, &vid_attribs.green); glGetIntegerv_fp(GL_BLUE_BITS, &vid_attribs.blue); glGetIntegerv_fp(GL_ALPHA_BITS, &vid_attribs.alpha); glGetIntegerv_fp(GL_DEPTH_BITS, &vid_attribs.depth); glGetIntegerv_fp(GL_STENCIL_BITS, &vid_attribs.stencil); Con_SafePrintf ("R:%d G:%d B:%d A:%d, Z:%d, S:%d\n", vid_attribs.red, vid_attribs.green, vid_attribs.blue, vid_attribs.alpha, vid_attribs.depth, vid_attribs.stencil); gl_vendor = (const char *)glGetString_fp (GL_VENDOR); Con_SafePrintf ("GL_VENDOR: %s\n", gl_vendor); gl_renderer = (const char *)glGetString_fp (GL_RENDERER); Con_SafePrintf ("GL_RENDERER: %s\n", gl_renderer); gl_version = (const char *)glGetString_fp (GL_VERSION); Con_SafePrintf ("GL_VERSION: %s\n", gl_version); gl_extensions = (const char *)glGetString_fp (GL_EXTENSIONS); Con_SafeDPrintf ("GL_EXTENSIONS: %s\n", gl_extensions); glGetIntegerv_fp(GL_MAX_TEXTURE_SIZE, &gl_max_size); if (gl_max_size < 256) // Refuse to work when less than 256 Sys_Error ("hardware capable of min. 256k opengl texture size needed"); Con_SafePrintf("OpenGL max.texture size: %i\n", (int) gl_max_size); CheckMultiTextureExtensions(); CheckAnisotropyExtensions(); CheckNonPowerOfTwoTextures(); CheckStencilBuffer(); // glClearColor_fp(1,0,0,0); glCullFace_fp(GL_FRONT); glEnable_fp(GL_TEXTURE_2D); glEnable_fp(GL_ALPHA_TEST); glAlphaFunc_fp(GL_GREATER, 0.632); // 1 - e^-1 : replaced 0.666 to avoid clipping of smaller fonts/graphics #if 0 /* causes side effects at least in 16 bpp. */ /* Get rid of Z-fighting for textures by offsetting the * drawing of entity models compared to normal polygons. * (See: R_DrawBrushModel.) * (Only works if gl_ztrick is turned off) */ #if defined(__AMIGA__) && defined(REFGL_MINIGL) #error port to use mglSetZOffset() instead of glPolygonOffset() #else glPolygonOffset_fp(0.05f, 25.0f); #endif #endif /* #if 0 */ glPolygonMode_fp (GL_FRONT_AND_BACK, GL_FILL); glShadeModel_fp (GL_FLAT); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glBlendFunc_fp (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); #if defined(__AMIGA__) && defined(REFGL_MINIGL) glHint_fp (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); #endif } /* ================= GL_BeginRendering ================= */ void GL_BeginRendering (int *x, int *y, int *width, int *height) { *x = *y = 0; *width = WRWidth; *height = WRHeight; // glViewport_fp (*x, *y, *width, *height); #if defined(__AMIGA__) && defined(REFGL_MINIGL) mglLockDisplay(); #endif } void GL_EndRendering (void) { if (!scr_skipupdate) #if defined(__AROS__) && defined(AROS_USE_GLA) glASwapBuffers(context); #elif defined(__AROS__) AROSMesaSwapBuffers(context); #elif defined __MORPHOS__ glASwapBuffers(); //GLASwapBuffers(__tglContext); #elif defined(__AMIGA__) && defined(REFGL_MINIGL) mglSwitchDisplay(); //performs unlock #elif defined(__AMIGA__) && defined(REFGL_AMESA) AmigaMesaSwapBuffers(context); #endif // handle the mouse state when windowed if that's changed if (_enable_mouse.integer != enable_mouse /*&& modestate == MS_WINDOWED*/) { if (_enable_mouse.integer) IN_ActivateMouse (); else IN_DeactivateMouse (); enable_mouse = _enable_mouse.integer; } // if (fullsbardraw) // Sbar_Changed(); } const int ColorIndex[16] = { 0, 31, 47, 63, 79, 95, 111, 127, 143, 159, 175, 191, 199, 207, 223, 231 }; const unsigned int ColorPercent[16] = { 25, 51, 76, 102, 114, 127, 140, 153, 165, 178, 191, 204, 216, 229, 237, 247 }; #define INVERSE_PALNAME "gfx/invpal.lmp" static int ConvertTrueColorToPal (const unsigned char *true_color, const unsigned char *palette) { int i; long min_dist; int min_index; long r, g, b; min_dist = 256 * 256 + 256 * 256 + 256 * 256; min_index = -1; r = (long) true_color[0]; g = (long) true_color[1]; b = (long) true_color[2]; for (i = 0; i < 256; i++) { long palr, palg, palb, dist; long dr, dg, db; palr = palette[3*i]; palg = palette[3*i+1]; palb = palette[3*i+2]; dr = palr - r; dg = palg - g; db = palb - b; dist = dr * dr + dg * dg + db * db; if (dist < min_dist) { min_dist = dist; min_index = i; } } return min_index; } static void VID_CreateInversePalette (const unsigned char *palette) { long r, g, b; long idx = 0; unsigned char true_color[3]; Con_SafePrintf ("Creating inverse palette\n"); for (r = 0; r < (1 << INVERSE_PAL_R_BITS); r++) { for (g = 0; g < (1 << INVERSE_PAL_G_BITS); g++) { for (b = 0; b < (1 << INVERSE_PAL_B_BITS); b++) { true_color[0] = (unsigned char)(r << (8 - INVERSE_PAL_R_BITS)); true_color[1] = (unsigned char)(g << (8 - INVERSE_PAL_G_BITS)); true_color[2] = (unsigned char)(b << (8 - INVERSE_PAL_B_BITS)); inverse_pal[idx] = ConvertTrueColorToPal(true_color, palette); idx++; } } } FS_CreatePath(FS_MakePath(FS_USERDIR, NULL, INVERSE_PALNAME)); FS_WriteFile (INVERSE_PALNAME, inverse_pal, INVERSE_PAL_SIZE); } static void VID_InitPalette (const unsigned char *palette) { const unsigned char *pal; unsigned short r, g, b; unsigned short i, p, c; unsigned int v, *table; int mark; #if ENDIAN_RUNTIME_DETECT switch (host_byteorder) { case BIG_ENDIAN: /* R G B A */ MASK_r = 0xff000000; MASK_g = 0x00ff0000; MASK_b = 0x0000ff00; MASK_a = 0x000000ff; SHIFT_r = 24; SHIFT_g = 16; SHIFT_b = 8; SHIFT_a = 0; break; case LITTLE_ENDIAN: /* A B G R */ MASK_r = 0x000000ff; MASK_g = 0x0000ff00; MASK_b = 0x00ff0000; MASK_a = 0xff000000; SHIFT_r = 0; SHIFT_g = 8; SHIFT_b = 16; SHIFT_a = 24; break; default: break; } MASK_rgb = (MASK_r|MASK_g|MASK_b); #endif /* ENDIAN_RUNTIME_DETECT */ // // 8 8 8 encoding // pal = palette; table = d_8to24table; for (i = 0; i < 256; i++) { r = pal[0]; g = pal[1]; b = pal[2]; pal += 3; v = (255 << SHIFT_a) + (r << SHIFT_r) + (g << SHIFT_g) + (b << SHIFT_b); *table++ = v; } d_8to24table[255] &= MASK_rgb; // 255 is transparent pal = palette; table = d_8to24TranslucentTable; for (i = 0; i < 16; i++) { c = ColorIndex[i] * 3; r = pal[c]; g = pal[c + 1]; b = pal[c + 2]; for (p = 0; p < 16; p++) { v = (ColorPercent[15 - p] << SHIFT_a) + (r << SHIFT_r) + (g << SHIFT_g) + (b << SHIFT_b); *table++ = v; RTint[i*16 + p] = ((float)r) / ((float)ColorPercent[15-p]); GTint[i*16 + p] = ((float)g) / ((float)ColorPercent[15-p]); BTint[i*16 + p] = ((float)b) / ((float)ColorPercent[15-p]); } } // Initialize the palettized textures data mark = Hunk_LowMark (); inverse_pal = (unsigned char *) FS_LoadHunkFile (INVERSE_PALNAME, NULL); if (inverse_pal != NULL && fs_filesize != INVERSE_PAL_SIZE) { Hunk_FreeToLowMark (mark); inverse_pal = NULL; } if (inverse_pal == NULL) { inverse_pal = (unsigned char *) Hunk_AllocName (INVERSE_PAL_SIZE + 1, INVERSE_PALNAME); VID_CreateInversePalette (palette); } } void VID_SetPalette (const unsigned char *palette) { // nothing to do } /* =================================================================== MAIN WINDOW =================================================================== */ /* ================ ClearAllStates ================ */ static void ClearAllStates (void) { Key_ClearStates (); IN_ClearStates (); } /* ================= VID_ChangeVideoMode intended only as a callback for VID_Restart_f ================= */ static void VID_ChangeVideoMode (int newmode) { int temp; temp = scr_disabled_for_loading; scr_disabled_for_loading = true; // restore gamma, reset gamma function pointers /*VID_ShutdownGamma();*/ CDAudio_Pause (); BGM_Pause (); S_ClearBuffer (); // Unload all textures and reset texture counts D_ClearOpenGLTextures(0); memset (lightmap_textures, 0, sizeof(lightmap_textures)); // reset all opengl function pointers GL_ResetFunctions(); // Avoid re-registering commands and re-allocating memory draw_reinit = true; // temporarily disable input devices IN_DeactivateMouse(); //IN_ShowMouse (); if (!VID_SetMode (newmode)) Sys_Error ("Couldn't set video mode"); // Reload graphics wad file (Draw_PicFromWad writes glpic_t data (sizes, // texnums) right on top of the original pic data, so the pic data will // be dirty after gl textures are loaded the first time; we need to load // a clean version) W_LoadWadFile ("gfx.wad"); // Initialize extensions and default OpenGL parameters GL_Init(); VID_InitGamma(); VID_Init8bitPalette(); // Reload pre-map pics, fonts, console, etc Draw_Init(); SCR_Init(); // R_Init() stuff: R_InitParticleTexture(); R_InitExtraTextures (); #if defined(H2W) R_InitNetgraphTexture(); #endif /* H2W */ Sbar_Init(); vid.recalc_refdef = 1; IN_ReInit (); ClearAllStates (); CDAudio_Resume (); BGM_Resume (); // Reload model textures and player skins Mod_ReloadTextures(); // rebuild the lightmaps GL_BuildLightmaps(); // finished reloading all images draw_reinit = false; scr_disabled_for_loading = temp; // apply our gamma VID_ShiftPalette(NULL); } static void VID_Restart_f (void) { if (vid_mode.integer < 0 || vid_mode.integer >= *nummodes) { Con_Printf ("Bad video mode %d\n", vid_mode.integer); Cvar_SetValueQuick (&vid_mode, vid_modenum); return; } Con_Printf ("Re-initializing video:\n"); VID_ChangeVideoMode (vid_mode.integer); } static int sort_modes (const void *arg1, const void *arg2) { const vmode_t *a1, *a2; a1 = (const vmode_t *) arg1; a2 = (const vmode_t *) arg2; if (a1->width == a2->width) return a1->height - a2->height; // lowres-to-highres // return a2->height - a1->height; // highres-to-lowres else return a1->width - a2->width; // lowres-to-highres // return a2->width - a1->width; // highres-to-lowres } static void VID_PrepareModes (void) { int i; ULONG id; APTR handle; struct DimensionInfo diminfo; num_fmodes = 0; num_wmodes = 0; // Add the standart 4:3 modes to the windowed modes list // In an unlikely case that we receive no fullscreen modes, // this will be our modes list (kind of...) for (i = 0; i < (int)MAX_STDMODES; i++) { wmodelist[num_wmodes].width = std_modes[i].width; wmodelist[num_wmodes].height = std_modes[i].height; /*wmodelist[num_wmodes].halfscreen = 0;*/ wmodelist[num_wmodes].fullscreen = 0; wmodelist[num_wmodes].bpp = 16; q_snprintf (wmodelist[num_wmodes].modedesc, MAX_DESC, "%d x %d", std_modes[i].width, std_modes[i].height); num_wmodes++; } // fullscreen modes id = INVALID_ID; while((id = NextDisplayInfo(id)) != INVALID_ID) { handle = FindDisplayInfo(id); if (handle) { if (!GetDisplayInfoData(handle, (UBYTE *) &diminfo, sizeof(diminfo), DTAG_DIMS, 0)) continue; #ifdef __AROS__ if (diminfo.MaxDepth != 24) #else if (diminfo.MaxDepth != 16) #endif continue; fmodelist[num_fmodes].width = diminfo.Nominal.MaxX + 1; fmodelist[num_fmodes].height = diminfo.Nominal.MaxY + 1; fmodelist[num_fmodes].fullscreen = 1; fmodelist[num_fmodes].bpp = 16; // diminfo.MaxDepth q_snprintf (fmodelist[num_fmodes].modedesc, MAX_DESC, "%d x %d", (fmodelist[num_fmodes].width), (fmodelist[num_fmodes].height)); //Con_SafePrintf ("fmodelist[%d].modedesc = %s maxdepth %d\n", num_fmodes, fmodelist[num_fmodes].modedesc, diminfo.MaxDepth); if (++num_fmodes == MAX_MODE_LIST) break; } } if (num_fmodes > 1) qsort(fmodelist, num_fmodes, sizeof(vmode_t), sort_modes); // disaster scenario #1: no fullscreen modes. bind to the // windowed modes list. limit it to 640x480 max. because // we don't know the desktop dimensions if (num_fmodes == 0) { Con_SafePrintf ("No fullscreen video modes available\n"); num_wmodes = RES_640X480 + 1; modelist = wmodelist; nummodes = &num_wmodes; vid_default = RES_640X480; Cvar_SetValueQuick (&vid_config_glx, modelist[vid_default].width); Cvar_SetValueQuick (&vid_config_gly, modelist[vid_default].height); return; } // At his point, we have a list of valid fullscreen modes: // Let's bind to it and use it for windowed modes, as well. // The only downside is that if SDL doesn't report any low // resolutions to us, we shall not have any for windowed // rendering where they would be perfectly legitimate... // Since our fullscreen/windowed toggling is instant and // doesn't require a vid_restart, switching lists won't be // feasible, either. The -width/-height commandline args // remain as the user's trusty old friends here. nummodes = &num_fmodes; modelist = fmodelist; vid_maxwidth = fmodelist[num_fmodes-1].width; vid_maxheight = fmodelist[num_fmodes-1].height; // find the 640x480 default resolution. this shouldn't fail // at all (for any adapter suporting the VGA/XGA legacy). for (i = 0; i < num_fmodes; i++) { if (fmodelist[i].width == 640 && fmodelist[i].height == 480) { vid_default = i; break; } } if (vid_default < 0) { // No 640x480? Unexpected, at least today.. // Easiest thing is to set the default mode // as the highest reported one. Con_SafePrintf ("WARNING: 640x480 not found in fullscreen modes\n" "Using the largest reported dimension as default\n"); vid_default = num_fmodes-1; } // limit the windowed (standart) modes list to desktop dimensions for (i = 0; i < num_wmodes; i++) { if (wmodelist[i].width > vid_maxwidth || wmodelist[i].height > vid_maxheight) break; } if (i < num_wmodes) num_wmodes = i; Cvar_SetValueQuick (&vid_config_glx, modelist[vid_default].width); Cvar_SetValueQuick (&vid_config_gly, modelist[vid_default].height); } static void VID_ListModes_f (void) { int i; Con_Printf ("Maximum allowed mode: %d x %d\n", vid_maxwidth, vid_maxheight); Con_Printf ("Windowed modes enabled:\n"); for (i = 0; i < num_wmodes; i++) Con_Printf ("%2d: %d x %d\n", i, wmodelist[i].width, wmodelist[i].height); Con_Printf ("Fullscreen modes enumerated:"); if (num_fmodes) { Con_Printf ("\n"); for (i = 0; i < num_fmodes; i++) Con_Printf ("%2d: %d x %d\n", i, fmodelist[i].width, fmodelist[i].height); } else { Con_Printf (" None\n"); } } static void VID_NumModes_f (void) { Con_Printf ("%d video modes in current list\n", *nummodes); } #if defined(__AMIGA__) && defined(REFGL_MINIGL) static void gl_lockmode_f (cvar_t *var) { if (!q_strcasecmp(var->string, "smart")) lockmode = MGL_LOCK_SMART; else if (!q_strcasecmp(var->string, "auto")) lockmode = MGL_LOCK_AUTOMATIC; else if (!q_strcasecmp(var->string, "manual")) lockmode = MGL_LOCK_MANUAL; else { Con_Printf("Bad lock mode %s, setting to manual\n", var->string); lockmode = MGL_LOCK_MANUAL; Cvar_SetQuick(var, "manual"); return; } if (mini_CurrentContext) { mglLockMode(lockmode); } } #endif /* =================== VID_Init =================== */ void VID_Init (const unsigned char *palette) { int i, temp, width, height; const char *read_vars[] = { "vid_config_fscr", "vid_config_gl8bit", "vid_config_glx", "vid_config_gly", "vid_config_consize", #if defined(__AMIGA__) && defined(REFGL_MINIGL) "gl_lockmode", #endif "gl_texture_NPOT", "gl_multitexture", "gl_lightmapfmt" }; #define num_readvars ( sizeof(read_vars)/sizeof(read_vars[0]) ) Cvar_RegisterVariable (&vid_config_gl8bit); Cvar_RegisterVariable (&vid_config_fscr); Cvar_RegisterVariable (&vid_config_swy); Cvar_RegisterVariable (&vid_config_swx); Cvar_RegisterVariable (&vid_config_mon); Cvar_RegisterVariable (&vid_config_gly); Cvar_RegisterVariable (&vid_config_glx); Cvar_RegisterVariable (&vid_config_consize); Cvar_RegisterVariable (&vid_mode); Cvar_RegisterVariable (&_enable_mouse); Cvar_RegisterVariable (&gl_texture_NPOT); Cvar_RegisterVariable (&gl_lightmapfmt); Cvar_RegisterVariable (&gl_multitexture); Cmd_AddCommand ("vid_listmodes", VID_ListModes_f); Cmd_AddCommand ("vid_nummodes", VID_NumModes_f); Cmd_AddCommand ("vid_restart", VID_Restart_f); VID_InitPalette (palette); vid.numpages = 2; #if defined(REFGL_MINIGL) #ifdef __CLIB2__ GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0); if (!GfxBase) Sys_Error ("Cannot open graphics.library!"); #endif mini_CurrentContext = NULL;/* should I do this? */ Cvar_RegisterVariable (&gl_lockmode); Cvar_SetCallback (&gl_lockmode, gl_lockmode_f); if (MGLInit() == GL_FALSE) { Sys_Error ("Couldn't initialize MiniGL"); } mglChoosePixelDepth (16);/* set pixel depth to 16 */ mglChooseVertexBufferSize (3000); /* default 30 not enough */ if (COM_CheckParm("-guardband")) mglChooseGuardBand (GL_TRUE);/* helps with Voodoo3 */ else mglChooseGuardBand (GL_FALSE); #endif #if defined(REFGL_AMESA) #ifdef __CLIB2__ GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0); if (!GfxBase) Sys_Error ("Cannot open graphics.library!"); #endif CyberGfxBase = OpenLibrary("cybergraphics.library", 0); if (!CyberGfxBase) Sys_Error ("Cannot open cybergraphics.library!"); #endif // prepare the modelists, find the actual modenum for vid_default VID_PrepareModes(); // set vid_mode to our safe default first Cvar_SetValueQuick (&vid_mode, vid_default); // perform an early read of config.cfg CFG_ReadCvars (read_vars, num_readvars); // windowed mode is default // see if the user wants fullscreen if (COM_CheckParm("-fullscreen") || COM_CheckParm("-f")) { Cvar_SetQuick (&vid_config_fscr, "1"); } else if (COM_CheckParm("-window") || COM_CheckParm("-w")) { Cvar_SetQuick (&vid_config_fscr, "0"); } if (vid_config_fscr.integer && !num_fmodes) // FIXME: see below, as well Sys_Error ("No fullscreen modes available at this color depth"); width = vid_config_glx.integer; height = vid_config_gly.integer; if (vid_config_consize.integer != width) vid_conscale = true; // user is always right ... i = COM_CheckParm("-width"); if (i && i < com_argc-1) { // FIXME: this part doesn't know about a disaster case // like we aren't reported any fullscreen modes. width = atoi(com_argv[i+1]); i = COM_CheckParm("-height"); if (i && i < com_argc-1) height = atoi(com_argv[i+1]); else // proceed with 4/3 ratio height = 3 * width / 4; } i = COM_CheckParm("-bpp"); if (i && i < com_argc-1) { bpp = atoi(com_argv[i+1]); } // user requested a mode either from the config or from the // command line // scan existing modes to see if this is already available // if not, add this as the last "valid" video mode and set // vid_mode to it only if it doesn't go beyond vid_maxwidth i = 0; while (i < *nummodes) { if (modelist[i].width == width && modelist[i].height == height) break; i++; } if (i < *nummodes) { Cvar_SetValueQuick (&vid_mode, i); } else if ( (width <= vid_maxwidth && width >= MIN_WIDTH && height <= vid_maxheight && height >= MIN_HEIGHT) || COM_CheckParm("-force") ) { modelist[*nummodes].width = width; modelist[*nummodes].height = height; /*modelist[*nummodes].halfscreen = 0;*/ modelist[*nummodes].fullscreen = 1; modelist[*nummodes].bpp = 16; q_snprintf (modelist[*nummodes].modedesc, MAX_DESC, "%d x %d (user mode)", width, height); Cvar_SetValueQuick (&vid_mode, *nummodes); (*nummodes)++; } else { Con_SafePrintf ("ignoring invalid -width and/or -height arguments\n"); } if (!vid_conscale) Cvar_SetValueQuick (&vid_config_consize, width); // This will display a bigger hud and readable fonts at high // resolutions. The fonts will be somewhat distorted, though i = COM_CheckParm("-conwidth"); if (i != 0 && i < com_argc-1) i = atoi(com_argv[i + 1]); else i = vid_config_consize.integer; if (i < MIN_WIDTH) i = MIN_WIDTH; else if (i > width) i = width; Cvar_SetValueQuick(&vid_config_consize, i); if (vid_config_consize.integer != width) vid_conscale = true; if (COM_CheckParm("-paltex")) Cvar_SetQuick (&vid_config_gl8bit, "1"); vid.maxwarpwidth = WARP_WIDTH; vid.maxwarpheight = WARP_HEIGHT; vid.colormap = host_colormap; vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); temp = scr_disabled_for_loading; scr_disabled_for_loading = true; //set the mode if (!VID_SetMode (vid_mode.integer)) Sys_Error ("Couldn't set video mode"); ClearAllStates (); GL_SetupLightmapFmt(); GL_Init (); VID_InitGamma(); VID_Init8bitPalette(); // lock the early-read cvars until Host_Init is finished for (i = 0; i < (int)num_readvars; i++) Cvar_LockVar (read_vars[i]); vid_initialized = true; scr_disabled_for_loading = temp; vid.recalc_refdef = 1; Con_SafePrintf ("Video initialized.\n"); vid_menudrawfn = VID_MenuDraw; vid_menukeyfn = VID_MenuKey; // if (COM_CheckParm("-fullsbar")) // fullsbardraw = true; } void VID_Shutdown (void) { VID_KillContext(); #ifdef REFGL_MINIGL MGLTerm(); #ifdef __CLIB2__ if (GfxBase) { CloseLibrary((struct Library *)GfxBase); GfxBase = NULL; } #endif #endif #ifdef REFGL_AMESA if (CyberGfxBase) { CloseLibrary(CyberGfxBase); CyberGfxBase = NULL; } #ifdef __CLIB2__ if (GfxBase) { CloseLibrary((struct Library *)GfxBase); GfxBase = NULL; } #endif #endif } static void VID_KillContext (void) { #if defined(__AROS__) && defined(AROS_USE_GLA) if (context) { glADestroyContext(context); context = NULL; } #elif defined(__AROS__) if (context) { AROSMesaDestroyContext(context); context = NULL; } #elif defined __MORPHOS__ if (contextinit) { if (screen && !(TinyGLBase->lib_Version == 0 && TinyGLBase->lib_Revision < 4)) glADestroyContextScreen(); else glADestroyContextWindowed(); contextinit = false; } if (__tglContext) { GLClose(__tglContext); __tglContext = NULL; } #elif defined(__AMIGA__) && defined(REFGL_MINIGL) if (mini_CurrentContext) { mglDeleteContext(); window = NULL; screen = NULL; mini_CurrentContext = NULL; } #elif defined(__AMIGA__) && defined(REFGL_AMESA) if (context) { AmigaMesaDestroyContext(context); context = NULL; } #endif #ifndef REFGL_MINIGL if (window) { CloseWindow(window); window = NULL; } if (screen) { CloseScreen(screen); screen = NULL; } #endif } /* ================ VID_ToggleFullscreen Handles switching between fullscreen/windowed modes and brings the mouse to a proper state afterwards ================ */ extern qboolean menu_disabled_mouse; void VID_ToggleFullscreen (void) { // implement this } #ifndef H2W /* unused in hexenworld */ void D_ShowLoadingSize (void) { #if defined(DRAW_PROGRESSBARS) int cur_perc; static int prev_perc; if (!vid_initialized) return; cur_perc = loading_stage * 100; if (total_loading_size) cur_perc += current_loading_size * 100 / total_loading_size; if (cur_perc == prev_perc) return; prev_perc = cur_perc; glDrawBuffer_fp (GL_FRONT); SCR_DrawLoading(); glFlush_fp(); glDrawBuffer_fp (GL_BACK); #endif /* DRAW_PROGRESSBARS */ } #endif //======================================================== // Video menu stuff //======================================================== static int vid_menunum; static int vid_cursor; static qboolean want_fstoggle, need_apply; static qboolean vid_menu_firsttime = true; enum { VID_FULLSCREEN, // make sure the fullscreen entry (0) VID_RESOLUTION, // is lower than resolution entry (1) VID_MULTISAMPLE, VID_MULTITEXTURE, VID_NPOT, VID_PALTEX, VID_BLANKLINE, // spacer line VID_RESET, VID_APPLY, VID_ITEMS }; static void M_DrawYesNo (int x, int y, int on, int white) { if (on) { if (white) M_PrintWhite (x, y, "yes"); else M_Print (x, y, "yes"); } else { if (white) M_PrintWhite (x, y, "no"); else M_Print (x, y, "no"); } } /* ================ VID_MenuDraw ================ */ static void VID_MenuDraw (void) { ScrollTitle("gfx/menu/title7.lmp"); if (vid_menu_firsttime) { // settings for entering the menu first time vid_menunum = vid_modenum; vid_menu_fs = (modestate != MS_WINDOWED); vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; vid_menu_firsttime = false; } want_fstoggle = ( ((modestate == MS_WINDOWED) && vid_menu_fs) || ((modestate != MS_WINDOWED) && !vid_menu_fs) ); need_apply = (vid_menunum != vid_modenum) || want_fstoggle || (have_mtex && (gl_mtexable != !!gl_multitexture.integer)) || (have_NPOT && (gl_tex_NPOT != !!gl_texture_NPOT.integer)) || (have8bit && (is8bit != !!vid_config_gl8bit.integer)); M_Print (76, 92 + 8*VID_FULLSCREEN, "Fullscreen: "); M_DrawYesNo (76+12*8, 92 + 8*VID_FULLSCREEN, vid_menu_fs, !want_fstoggle); M_Print (76, 92 + 8*VID_RESOLUTION, "Resolution: "); if (vid_menunum == vid_modenum) M_PrintWhite (76+12*8, 92 + 8*VID_RESOLUTION, modelist[vid_menunum].modedesc); else M_Print (76+12*8, 92 + 8*VID_RESOLUTION, modelist[vid_menunum].modedesc); M_Print (76, 92 + 8*VID_MULTISAMPLE, "Antialiasing :"); M_PrintWhite (76+16*8, 92 + 8*VID_MULTISAMPLE, "Not found"); M_Print (76, 92 + 8*VID_MULTITEXTURE, "Multitexturing:"); if (have_mtex) M_DrawYesNo (76+16*8, 92 + 8*VID_MULTITEXTURE, gl_multitexture.integer, (gl_mtexable == !!gl_multitexture.integer)); else M_PrintWhite (76+16*8, 92 + 8*VID_MULTITEXTURE, "Not found"); M_Print (76, 92 + 8*VID_NPOT, "NPOT textures :"); if (have_NPOT) M_DrawYesNo (76+16*8, 92 + 8*VID_NPOT, gl_texture_NPOT.integer, (gl_tex_NPOT == !!gl_texture_NPOT.integer)); else M_PrintWhite (76+16*8, 92 + 8*VID_NPOT, "Not found"); M_Print (76, 92 + 8*VID_PALTEX, "8 bit textures:"); if (have8bit) M_DrawYesNo (76+16*8, 92 + 8*VID_PALTEX, vid_config_gl8bit.integer, (is8bit == !!vid_config_gl8bit.integer)); else M_PrintWhite (76+16*8, 92 + 8*VID_PALTEX, "Not found"); if (need_apply) { M_Print (76, 92 + 8*VID_RESET, "RESET CHANGES"); M_Print (76, 92 + 8*VID_APPLY, "APPLY CHANGES"); } M_DrawCharacter (64, 92 + vid_cursor*8, 12+((int)(realtime*4)&1)); } /* ================ VID_MenuKey ================ */ static void VID_MenuKey (int key) { switch (key) { case K_ESCAPE: vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; M_Menu_Options_f (); break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); vid_cursor--; if (vid_cursor < 0) { vid_cursor = (need_apply) ? VID_ITEMS-1 : VID_BLANKLINE-1; } else if (vid_cursor == VID_BLANKLINE) { vid_cursor--; } break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); vid_cursor++; if (vid_cursor >= VID_ITEMS) { vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; break; } if (vid_cursor >= VID_BLANKLINE) { if (need_apply) { if (vid_cursor == VID_BLANKLINE) vid_cursor++; } else { vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; } } break; case K_ENTER: switch (vid_cursor) { case VID_RESET: vid_menu_fs = (modestate != MS_WINDOWED); vid_menunum = vid_modenum; Cvar_SetValueQuick (&vid_config_gl8bit, is8bit); vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; break; case VID_APPLY: if (need_apply) { Cvar_SetValueQuick(&vid_mode, vid_menunum); Cvar_SetValueQuick(&vid_config_fscr, vid_menu_fs); VID_Restart_f(); } vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; break; } return; case K_LEFTARROW: switch (vid_cursor) { case VID_FULLSCREEN: vid_menu_fs = !vid_menu_fs; if (fs_toggle_works) VID_ToggleFullscreen(); break; case VID_RESOLUTION: S_LocalSound ("raven/menu1.wav"); vid_menunum--; if (vid_menunum < 0) vid_menunum = 0; break; case VID_MULTISAMPLE: break; case VID_MULTITEXTURE: if (have_mtex) Cvar_SetQuick (&gl_multitexture, gl_multitexture.integer ? "0" : "1"); break; case VID_NPOT: if (have_NPOT) Cvar_SetQuick (&gl_texture_NPOT, gl_texture_NPOT.integer ? "0" : "1"); break; case VID_PALTEX: if (have8bit) Cvar_SetQuick (&vid_config_gl8bit, vid_config_gl8bit.integer ? "0" : "1"); break; } return; case K_RIGHTARROW: switch (vid_cursor) { case VID_FULLSCREEN: vid_menu_fs = !vid_menu_fs; if (fs_toggle_works) VID_ToggleFullscreen(); break; case VID_RESOLUTION: S_LocalSound ("raven/menu1.wav"); vid_menunum++; if (vid_menunum >= *nummodes) vid_menunum = *nummodes - 1; break; case VID_MULTISAMPLE: break; case VID_MULTITEXTURE: if (have_mtex) Cvar_SetQuick (&gl_multitexture, gl_multitexture.integer ? "0" : "1"); break; case VID_NPOT: if (have_NPOT) Cvar_SetQuick (&gl_texture_NPOT, gl_texture_NPOT.integer ? "0" : "1"); break; case VID_PALTEX: if (have8bit) Cvar_SetQuick (&vid_config_gl8bit, vid_config_gl8bit.integer ? "0" : "1"); break; } return; default: break; } } engine/h2shared/gl_viddos.c000066400000000000000000001137301444734033100161510ustar00rootroot00000000000000/* * gl_viddos.c -- DOS OpenGL (based on gl_vidsdl.c and gl_vidnt.c) * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2015-2016 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __GL_FUNC_EXTERN #include "quakedef.h" #include "cfgfile.h" #include "bgmusic.h" #include "cdaudio.h" #include #include "filenames.h" #include "gl_dos.h" #include "sys_dxe.h" /* DOSGL interface */ int (*DOSGL_InitCtx ) (int *width, int *height, int *bpp); void (*DOSGL_Shutdown) (void); void (*DOSGL_EndFrame) (void); void * (*DOSGL_GetProcAddress) (const char *); const char * (*DOSGL_APIName) (void); #define WARP_WIDTH 320 #define WARP_HEIGHT 200 #define MAXWIDTH 10000 #define MAXHEIGHT 10000 #define MIN_WIDTH 320 //#define MIN_HEIGHT 200 #define MIN_HEIGHT 240 #define MAX_DESC 33 typedef struct { int width; int height; int modenum; int bpp; int halfscreen; char modedesc[MAX_DESC]; } vmode_t; typedef struct { int width; int height; } stdmode_t; #define RES_640X480 3 static const stdmode_t std_modes[] = { // NOTE: keep this list in order {320, 240}, // 0 {400, 300}, // 1 {512, 384}, // 2 {640, 480}, // 3 == RES_640X480, this is our default {800, 600}, {856, 480}, // GR_RESOLUTION_856x480 {960, 720}, // GR_RESOLUTION_960x720 {1024,768}, {1280,1024}, {1600,1200} }; #define MAX_MODE_LIST 20 #define MAX_STDMODES (sizeof(std_modes) / sizeof(std_modes[0])) static vmode_t modelist[MAX_MODE_LIST+1]; static int nummodes; static int bpp = 16; typedef struct { int red, green, blue, alpha, depth, stencil; } attributes_t; static attributes_t vid_attribs; // vars for vid state viddef_t vid; // global video state modestate_t modestate = MS_UNINIT; static int vid_default = -1; // modenum of 640x480 as a safe default static int vid_modenum = -1; // current video mode, set after mode setting succeeds static int vid_maxwidth = 640, vid_maxheight = 480; static qboolean vid_conscale = false; static int WRHeight, WRWidth; static qboolean vid_initialized = false; qboolean in_mode_set = false; // cvar vid_mode must be set before calling // VID_SetMode, VID_ChangeVideoMode or VID_Restart_f static cvar_t vid_mode = {"vid_mode", "0", CVAR_NONE}; static cvar_t vid_config_consize = {"vid_config_consize", "640", CVAR_ARCHIVE}; static cvar_t vid_config_glx = {"vid_config_glx", "640", CVAR_ARCHIVE}; static cvar_t vid_config_gly = {"vid_config_gly", "480", CVAR_ARCHIVE}; // cvars for compatibility with the software version static cvar_t vid_nopageflip = {"vid_nopageflip", "0", CVAR_ARCHIVE}; static cvar_t _vid_wait_override = {"_vid_wait_override", "0", CVAR_ARCHIVE}; static cvar_t _vid_default_mode = {"_vid_default_mode", "0", CVAR_ARCHIVE}; byte globalcolormap[VID_GRADES*256]; float RTint[256], GTint[256], BTint[256]; unsigned short d_8to16table[256]; unsigned int d_8to24table[256]; unsigned int d_8to24TranslucentTable[256]; unsigned char *inverse_pal; // gl stuff static void GL_Init (void); #ifdef GL_DLSYM static const char *gl_library; #endif static void *gl_handle; static const char *gl_vendor; static const char *gl_renderer; static const char *gl_version; static const char *gl_extensions; qboolean is_3dfx = false; GLint gl_max_size = 256; static qboolean have_NPOT = false; qboolean gl_tex_NPOT = false; static cvar_t gl_texture_NPOT = {"gl_texture_NPOT", "0", CVAR_ARCHIVE}; GLfloat gl_max_anisotropy; float gldepthmin, gldepthmax; // palettized textures static qboolean have8bit = false; qboolean is8bit = false; static cvar_t vid_config_gl8bit = {"vid_config_gl8bit", "0", CVAR_ARCHIVE}; /* Gamma stuff */ #define USE_GAMMA_RAMPS 0 /* 3dfx gamma hacks: see fx_gamma.c */ #define USE_3DFX_RAMPS 0 #if defined(USE_3DFXGAMMA) #include "fx_gamma.h" #endif #if (USE_GAMMA_RAMPS) || (defined(USE_3DFXGAMMA) && (USE_3DFX_RAMPS)) static unsigned short orig_ramps[3][256]; #endif static qboolean fx_gamma = false; // 3dfx-specific gamma control static qboolean gammaworks = false; // whether hw-gamma works // multitexturing qboolean gl_mtexable = false; static GLint num_tmus = 1; static qboolean have_mtex = false; static cvar_t gl_multitexture = {"gl_multitexture", "0", CVAR_ARCHIVE}; // stencil buffer qboolean have_stencil = false; // this is useless: things aren't like those in quake //static qboolean fullsbardraw = false; // menu drawing static void VID_MenuDraw (void); static void VID_MenuKey (int key); // input stuff static void ClearAllStates (void); cvar_t _enable_mouse = {"_enable_mouse", "1", CVAR_ARCHIVE}; //==================================== static qboolean GL_ParseExtensionList (const char *list, const char *name) { const char *start; const char *where, *terminator; if (!list || !name || !*name) return false; if (strchr(name, ' ') != NULL) return false; // extension names must not have spaces start = list; while (1) { where = strstr (start, name); if (!where) break; terminator = where + strlen (name); if (where == start || where[-1] == ' ') if (*terminator == ' ' || *terminator == '\0') return true; start = terminator; } return false; } //==================================== void VID_LockBuffer (void) { // nothing to do } void VID_UnlockBuffer (void) { // nothing to do } void VID_HandlePause (qboolean paused) { if (paused) IN_DeactivateMouse (); else IN_ActivateMouse (); } //==================================== static void VID_ConWidth (int modenum) { int w, h; if (!vid_conscale) { Cvar_SetValueQuick (&vid_config_consize, modelist[modenum].width); return; } w = vid_config_consize.integer; w &= ~7; /* make it a multiple of eight */ if (w < MIN_WIDTH) w = MIN_WIDTH; else if (w > modelist[modenum].width) w = modelist[modenum].width; h = w * modelist[modenum].height / modelist[modenum].width; if (h < 200 /* MIN_HEIGHT */ || h > modelist[modenum].height || w > modelist[modenum].width) { vid_conscale = false; Cvar_SetValueQuick (&vid_config_consize, modelist[modenum].width); return; } vid.width = vid.conwidth = w; vid.height = vid.conheight = h; if (w != modelist[modenum].width) vid_conscale = true; else vid_conscale = false; } void VID_ChangeConsize (int dir) { int w, h; switch (dir) { case -1: /* smaller text */ w = ((float)vid.conwidth/(float)vid.width + 0.05f) * vid.width; /* use 0.10f increment ?? */ w &= ~7; /* make it a multiple of eight */ if (w > modelist[vid_modenum].width) w = modelist[vid_modenum].width; break; case 1: /* bigger text */ w = ((float)vid.conwidth/(float)vid.width - 0.05f) * vid.width; w &= ~7; /* make it a multiple of eight */ if (w < MIN_WIDTH) w = MIN_WIDTH; break; default: /* bad key */ return; } h = w * modelist[vid_modenum].height / modelist[vid_modenum].width; if (h < 200) return; vid.width = vid.conwidth = w; vid.height = vid.conheight = h; Cvar_SetValueQuick (&vid_config_consize, vid.conwidth); vid.recalc_refdef = 1; if (vid.conwidth != modelist[vid_modenum].width) vid_conscale = true; else vid_conscale = false; } float VID_ReportConsize(void) { return (float)modelist[vid_modenum].width/vid.conwidth; } static qboolean VID_SetMode (int modenum) { int width, height; in_mode_set = true; width = modelist[modenum].width; height = modelist[modenum].height; Con_SafePrintf ("Requested mode %d: %dx%dx%d\n", modenum, width, height, bpp); if (DOSGL_InitCtx(&width, &height, &bpp) < 0) Sys_Error("DOSGL: Failed creating context."); // set vid_modenum properly and adjust other vars vid_modenum = modenum; modestate = MS_FULLDIB; Cvar_SetValueQuick (&vid_config_glx, modelist[vid_modenum].width); Cvar_SetValueQuick (&vid_config_gly, modelist[vid_modenum].height); WRWidth = vid.width = vid.conwidth = modelist[modenum].width; WRHeight = vid.height = vid.conheight = modelist[modenum].height; // setup the effective console width VID_ConWidth(modenum); Con_SafePrintf ("Video Mode Set : %dx%dx%d\n", width, height, bpp); in_mode_set = false; return true; } //==================================== #if 0 /* No.. */ static void CheckSetGlobalPalette (void) { gl3DfxSetPaletteEXT_f gl3DfxSetPaletteEXT_fp; if (GL_ParseExtensionList(gl_extensions, "3DFX_set_global_palette")) { gl3DfxSetPaletteEXT_fp = (gl3DfxSetPaletteEXT_f) DOSGL_GetProcAddress("gl3DfxSetPaletteEXT"); if (!gl3DfxSetPaletteEXT_fp) gl3DfxSetPaletteEXT_fp = (gl3DfxSetPaletteEXT_f) DOSGL_GetProcAddress("3DFX_set_global_palette"); if (!gl3DfxSetPaletteEXT_fp) return; Con_SafePrintf("Found 3DFX_set_global_palette\n"); } else if (GL_ParseExtensionList(gl_extensions, "POWERVR_set_global_palette")) { gl3DfxSetPaletteEXT_fp = (gl3DfxSetPaletteEXT_f) DOSGL_GetProcAddress("glSetGlobalPalettePOWERVR"); if (!gl3DfxSetPaletteEXT_fp) gl3DfxSetPaletteEXT_fp = (gl3DfxSetPaletteEXT_f) DOSGL_GetProcAddress("POWERVR_set_global_palette"); if (!gl3DfxSetPaletteEXT_fp) return; Con_SafePrintf("Found POWERVR_set_global_palette\n"); } else { return; } have8bit = true; if (!vid_config_gl8bit.integer) return; else { int i; GLubyte table[256][4]; char *oldpal; is8bit = true; oldpal = (char *) d_8to24table; for (i = 0; i < 256; i++) { table[i][2] = *oldpal++; table[i][1] = *oldpal++; table[i][0] = *oldpal++; table[i][3] = 255; oldpal++; } glEnable_fp (GL_SHARED_TEXTURE_PALETTE_EXT); gl3DfxSetPaletteEXT_fp ((GLuint *)table); } } #endif /* #if 0 */ static void CheckSharedTexturePalette (void) { glColorTableEXT_f glColorTableEXT_fp; if (!GL_ParseExtensionList(gl_extensions, "GL_EXT_shared_texture_palette")) return; glColorTableEXT_fp = (glColorTableEXT_f) DOSGL_GetProcAddress("glColorTableEXT"); if (glColorTableEXT_fp == NULL) return; have8bit = true; Con_SafePrintf("Found GL_EXT_shared_texture_palette\n"); if (!vid_config_gl8bit.integer) return; else { int i; char thePalette[256*3]; char *oldPalette, *newPalette; is8bit = true; oldPalette = (char *) d_8to24table; newPalette = thePalette; for (i = 0; i < 256; i++) { *newPalette++ = *oldPalette++; *newPalette++ = *oldPalette++; *newPalette++ = *oldPalette++; oldPalette++; } glEnable_fp (GL_SHARED_TEXTURE_PALETTE_EXT); glColorTableEXT_fp (GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, (void *) thePalette); } } static void VID_Init8bitPalette (void) { have8bit = false; is8bit = false; /* Check for 8bit Extensions and initialize them */ CheckSharedTexturePalette(); #if 0 /* No.. */ if (!have8bit) CheckSetGlobalPalette(); #endif if (is8bit) Con_SafePrintf("8-bit palettized textures enabled\n"); } #if !defined(USE_3DFXGAMMA) static inline int FUNC_UNUSED Init_3dfxGammaCtrl (void) { return 0; } static inline void FUNC_UNUSED Shutdown_3dfxGamma (void) {/*nothing*/} static inline int FUNC_UNUSED do3dfxGammaCtrl (float _f) { return 0; } static inline int FUNC_UNUSED glGetDeviceGammaRamp3DFX (void *_p) { return 0; } static inline int FUNC_UNUSED glSetDeviceGammaRamp3DFX (void *_p) { return 0; } static inline qboolean FUNC_UNUSED VID_Check3dfxGamma (void) { return false; } #else static qboolean VID_Check3dfxGamma (void) { int ret; if (COM_CheckParm("-no3dfxgamma")) return false; #if USE_3DFX_RAMPS /* not recommended for Voodoo1, currently crashes */ ret = glGetDeviceGammaRamp3DFX(orig_ramps); if (ret != 0) { Con_SafePrintf ("Using 3dfx glide3 specific gamma ramps\n"); return true; } #else ret = Init_3dfxGammaCtrl(); if (ret > 0) { Con_SafePrintf ("Using 3dfx glide%d gamma controls\n", ret); return true; } #endif return false; } #endif /* USE_3DFXGAMMA */ static void VID_InitGamma (void) { gammaworks = fx_gamma = false; /* we don't have WGL_3DFX_gamma_control or an equivalent in dos. */ /* Here is an evil hack abusing the exposed Glide symbols: */ if (is_3dfx) fx_gamma = VID_Check3dfxGamma(); if (!gammaworks && !fx_gamma) Con_SafePrintf("gamma adjustment not available\n"); } static void VID_ShutdownGamma (void) { #if USE_3DFX_RAMPS if (fx_gamma) glSetDeviceGammaRamp3DFX(orig_ramps); #else /* if (fx_gamma) do3dfxGammaCtrl(1);*/ #endif Shutdown_3dfxGamma(); } static void VID_SetGamma (void) { #if (!USE_GAMMA_RAMPS) || (!USE_3DFX_RAMPS) float value = (v_gamma.value > (1.0 / GAMMA_MAX))? (1.0 / v_gamma.value) : GAMMA_MAX; #endif #if USE_3DFX_RAMPS if (fx_gamma) glSetDeviceGammaRamp3DFX(ramps); #else if (fx_gamma) do3dfxGammaCtrl(value); #endif } void VID_ShiftPalette (const unsigned char *palette) { VID_SetGamma(); } static void CheckMultiTextureExtensions (void) { gl_mtexable = have_mtex = false; if (COM_CheckParm("-nomtex")) { Con_SafePrintf("Multitexture extensions disabled\n"); } else if (GL_ParseExtensionList(gl_extensions, "GL_ARB_multitexture")) { Con_SafePrintf("ARB Multitexture extensions found\n"); glGetIntegerv_fp(GL_MAX_TEXTURE_UNITS_ARB, &num_tmus); if (num_tmus < 2) { Con_SafePrintf("ignoring multitexture (%i TMUs)\n", (int) num_tmus); return; } glMultiTexCoord2fARB_fp = (glMultiTexCoord2fARB_f) DOSGL_GetProcAddress("glMultiTexCoord2fARB"); glActiveTextureARB_fp = (glActiveTextureARB_f) DOSGL_GetProcAddress("glActiveTextureARB"); if (glMultiTexCoord2fARB_fp == NULL || glActiveTextureARB_fp == NULL) { Con_SafePrintf ("Couldn't link to multitexture functions\n"); return; } have_mtex = true; if (!gl_multitexture.integer) { Con_SafePrintf("ignoring multitexture (cvar disabled)\n"); return; } Con_SafePrintf("Found %i TMUs support\n", (int) num_tmus); gl_mtexable = true; glDisable_fp(GL_TEXTURE_2D); glActiveTextureARB_fp(GL_TEXTURE0_ARB); } else { Con_SafePrintf("GL_ARB_multitexture not found\n"); } } static void CheckAnisotropyExtensions (void) { gl_max_anisotropy = 1; Con_SafePrintf("Anisotropic filtering "); if (GL_ParseExtensionList(gl_extensions, "GL_EXT_texture_filter_anisotropic")) { GLfloat test1 = 0, test2 = 0; GLuint tex; glGetTexParameterfv_f glGetTexParameterfv_fp; glGetTexParameterfv_fp = (glGetTexParameterfv_f) DOSGL_GetProcAddress("glGetTexParameterfv"); if (glGetTexParameterfv_fp == NULL) { Con_SafePrintf("... can't check driver-lock status\n... "); goto _skiptest; } // test to make sure we really have control over it // 1.0 and 2.0 should always be legal values. glGenTextures_fp(1, &tex); glBindTexture_fp(GL_TEXTURE_2D, tex); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); glGetTexParameterfv_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &test1); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 2.0f); glGetTexParameterfv_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &test2); glDeleteTextures_fp(1, &tex); if (test1 != 1 || test2 != 2) { Con_SafePrintf("driver-locked @ %.1f\n", test1); } else { _skiptest: glGetFloatv_fp(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_max_anisotropy); if (gl_max_anisotropy < 2) Con_SafePrintf("broken\n"); else Con_SafePrintf("found, max %.1f\n", gl_max_anisotropy); } } else { Con_SafePrintf("not found\n"); } } static void CheckNonPowerOfTwoTextures (void) { gl_tex_NPOT = have_NPOT = false; if (GL_ParseExtensionList(gl_extensions, "GL_ARB_texture_non_power_of_two")) { have_NPOT = true; Con_SafePrintf("Found ARB_texture_non_power_of_two\n"); if (!gl_texture_NPOT.integer) { Con_SafePrintf("ignoring texture_NPOT (cvar disabled)\n"); } else { gl_tex_NPOT = true; } } else { Con_SafePrintf("GL_ARB_texture_non_power_of_two not found\n"); } } static void CheckStencilBuffer (void) { have_stencil = !!vid_attribs.stencil; } static void DOSGL_Init (void) { int rc = -1; if (rc < 0) rc = DMESA_LoadAPI (gl_handle); if (rc < 0) rc = SAGE_LoadAPI (gl_handle); if (rc < 0) rc = FXMESA_LoadAPI (gl_handle); if (rc < 0) { Sys_Error("Unable to find a supported API for DOSGL."); } Con_SafePrintf("DOSGL: driver using %s API.\n", DOSGL_APIName()); } #ifdef GL_DLSYM static qboolean GL_OpenLibrary (const char *name) { Con_SafePrintf("Loading OpenGL library %s\n", name); // open the library if (!(gl_handle = Sys_dlopen(name, false))) { Con_SafePrintf("Unable to dlopen %s\n", name); return false; } return true; } static void GL_CloseLibrary (void) { // clear the DOSGL function pointers DOSGL_InitCtx = NULL; DOSGL_Shutdown = NULL; DOSGL_EndFrame = NULL; DOSGL_GetProcAddress = NULL; DOSGL_APIName = NULL; // free the library if (gl_handle != NULL) { Sys_dlclose(gl_handle); gl_handle = NULL; } } #endif /* GL_DLSYM */ #ifdef GL_DLSYM static void GL_Init_Functions (void) { #define GL_FUNCTION(ret, func, params) \ do { \ func##_fp = (func##_f) Sys_dlsym(gl_handle, "_"#func); \ if (func##_fp == NULL) \ Sys_Error("%s not found in GL library", "_"#func); \ } while (0); #define GL_FUNCTION_OPT(ret, func, params) #include "gl_func.h" } #endif /* GL_DLSYM */ static void GL_ResetFunctions (void) { #ifdef GL_DLSYM #define GL_FUNCTION(ret, func, params) \ func##_fp = NULL; #endif #define GL_FUNCTION_OPT(ret, func, params) \ func##_fp = NULL; #include "gl_func.h" have_stencil = false; gl_mtexable = false; have_mtex = false; have8bit = false; is8bit = false; have_NPOT = false; gl_tex_NPOT = false; } /* =============== GL_Init =============== */ static void GL_Init (void) { #ifdef GL_DLSYM // initialize gl function pointers GL_Init_Functions(); #endif // collect the visual attributes memset (&vid_attribs, 0, sizeof(attributes_t)); glGetIntegerv_fp(GL_RED_BITS, &vid_attribs.red); glGetIntegerv_fp(GL_GREEN_BITS, &vid_attribs.green); glGetIntegerv_fp(GL_BLUE_BITS, &vid_attribs.blue); glGetIntegerv_fp(GL_ALPHA_BITS, &vid_attribs.alpha); glGetIntegerv_fp(GL_DEPTH_BITS, &vid_attribs.depth); glGetIntegerv_fp(GL_STENCIL_BITS, &vid_attribs.stencil); Con_SafePrintf ("R:%d G:%d B:%d A:%d, Z:%d, S:%d\n", vid_attribs.red, vid_attribs.green, vid_attribs.blue, vid_attribs.alpha, vid_attribs.depth, vid_attribs.stencil); gl_vendor = (const char *)glGetString_fp (GL_VENDOR); Con_SafePrintf ("GL_VENDOR: %s\n", gl_vendor); gl_renderer = (const char *)glGetString_fp (GL_RENDERER); Con_SafePrintf ("GL_RENDERER: %s\n", gl_renderer); gl_version = (const char *)glGetString_fp (GL_VERSION); Con_SafePrintf ("GL_VERSION: %s\n", gl_version); gl_extensions = (const char *)glGetString_fp (GL_EXTENSIONS); Con_SafeDPrintf ("GL_EXTENSIONS: %s\n", gl_extensions); glGetIntegerv_fp(GL_MAX_TEXTURE_SIZE, &gl_max_size); if (gl_max_size < 256) // Refuse to work when less than 256 Sys_Error ("hardware capable of min. 256k opengl texture size needed"); Con_SafePrintf("OpenGL max.texture size: %i\n", (int) gl_max_size); is_3dfx = false; if (!q_strncasecmp(gl_renderer, "3dfx", 4) || !q_strncasecmp(gl_renderer, "SAGE Glide", 10) || !q_strncasecmp(gl_renderer, "Glide ", 6) || /* possible with Mesa 3.x/4.x/5.0.x */ !q_strncasecmp(gl_renderer, "Mesa Glide", 10)) { Con_SafePrintf("3dfx Voodoo found\n"); is_3dfx = true; } CheckMultiTextureExtensions(); CheckAnisotropyExtensions(); CheckNonPowerOfTwoTextures(); CheckStencilBuffer(); // glClearColor_fp(1,0,0,0); glCullFace_fp(GL_FRONT); glEnable_fp(GL_TEXTURE_2D); glEnable_fp(GL_ALPHA_TEST); glAlphaFunc_fp(GL_GREATER, 0.632); // 1 - e^-1 : replaced 0.666 to avoid clipping of smaller fonts/graphics #if 0 /* causes side effects at least in 16 bpp. */ /* Get rid of Z-fighting for textures by offsetting the * drawing of entity models compared to normal polygons. * (See: R_DrawBrushModel.) * (Only works if gl_ztrick is turned off) */ glPolygonOffset_fp(0.05f, 25.0f); #endif /* #if 0 */ glPolygonMode_fp (GL_FRONT_AND_BACK, GL_FILL); glShadeModel_fp (GL_FLAT); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glBlendFunc_fp (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } /* ================= GL_BeginRendering ================= */ void GL_BeginRendering (int *x, int *y, int *width, int *height) { *x = *y = 0; *width = WRWidth; *height = WRHeight; } void GL_EndRendering (void) { if (!scr_skipupdate) { DOSGL_EndFrame(); } } const int ColorIndex[16] = { 0, 31, 47, 63, 79, 95, 111, 127, 143, 159, 175, 191, 199, 207, 223, 231 }; const unsigned int ColorPercent[16] = { 25, 51, 76, 102, 114, 127, 140, 153, 165, 178, 191, 204, 216, 229, 237, 247 }; #define INVERSE_PALNAME "gfx/invpal.lmp" static int ConvertTrueColorToPal (const unsigned char *true_color, const unsigned char *palette) { int i; long min_dist; int min_index; long r, g, b; min_dist = 256 * 256 + 256 * 256 + 256 * 256; min_index = -1; r = (long) true_color[0]; g = (long) true_color[1]; b = (long) true_color[2]; for (i = 0; i < 256; i++) { long palr, palg, palb, dist; long dr, dg, db; palr = palette[3*i]; palg = palette[3*i+1]; palb = palette[3*i+2]; dr = palr - r; dg = palg - g; db = palb - b; dist = dr * dr + dg * dg + db * db; if (dist < min_dist) { min_dist = dist; min_index = i; } } return min_index; } static void VID_CreateInversePalette (const unsigned char *palette) { long r, g, b; long idx = 0; unsigned char true_color[3]; Con_SafePrintf ("Creating inverse palette\n"); for (r = 0; r < (1 << INVERSE_PAL_R_BITS); r++) { for (g = 0; g < (1 << INVERSE_PAL_G_BITS); g++) { for (b = 0; b < (1 << INVERSE_PAL_B_BITS); b++) { true_color[0] = (unsigned char)(r << (8 - INVERSE_PAL_R_BITS)); true_color[1] = (unsigned char)(g << (8 - INVERSE_PAL_G_BITS)); true_color[2] = (unsigned char)(b << (8 - INVERSE_PAL_B_BITS)); inverse_pal[idx] = ConvertTrueColorToPal(true_color, palette); idx++; } } } FS_CreatePath(FS_MakePath(FS_USERDIR, NULL, INVERSE_PALNAME)); FS_WriteFile (INVERSE_PALNAME, inverse_pal, INVERSE_PAL_SIZE); } static void VID_InitPalette (const unsigned char *palette) { const unsigned char *pal; unsigned short r, g, b; unsigned short i, p, c; unsigned int v, *table; int mark; #if ENDIAN_RUNTIME_DETECT switch (host_byteorder) { case BIG_ENDIAN: /* R G B A */ MASK_r = 0xff000000; MASK_g = 0x00ff0000; MASK_b = 0x0000ff00; MASK_a = 0x000000ff; SHIFT_r = 24; SHIFT_g = 16; SHIFT_b = 8; SHIFT_a = 0; break; case LITTLE_ENDIAN: /* A B G R */ MASK_r = 0x000000ff; MASK_g = 0x0000ff00; MASK_b = 0x00ff0000; MASK_a = 0xff000000; SHIFT_r = 0; SHIFT_g = 8; SHIFT_b = 16; SHIFT_a = 24; break; default: break; } MASK_rgb = (MASK_r|MASK_g|MASK_b); #endif /* ENDIAN_RUNTIME_DETECT */ // // 8 8 8 encoding // pal = palette; table = d_8to24table; for (i = 0; i < 256; i++) { r = pal[0]; g = pal[1]; b = pal[2]; pal += 3; v = (255 << SHIFT_a) + (r << SHIFT_r) + (g << SHIFT_g) + (b << SHIFT_b); *table++ = v; } d_8to24table[255] &= MASK_rgb; // 255 is transparent pal = palette; table = d_8to24TranslucentTable; for (i = 0; i < 16; i++) { c = ColorIndex[i] * 3; r = pal[c]; g = pal[c + 1]; b = pal[c + 2]; for (p = 0; p < 16; p++) { v = (ColorPercent[15 - p] << SHIFT_a) + (r << SHIFT_r) + (g << SHIFT_g) + (b << SHIFT_b); *table++ = v; RTint[i*16 + p] = ((float)r) / ((float)ColorPercent[15-p]); GTint[i*16 + p] = ((float)g) / ((float)ColorPercent[15-p]); BTint[i*16 + p] = ((float)b) / ((float)ColorPercent[15-p]); } } // Initialize the palettized textures data mark = Hunk_LowMark (); inverse_pal = (unsigned char *) FS_LoadHunkFile (INVERSE_PALNAME, NULL); if (inverse_pal != NULL && fs_filesize != INVERSE_PAL_SIZE) { Hunk_FreeToLowMark (mark); inverse_pal = NULL; } if (inverse_pal == NULL) { inverse_pal = (unsigned char *) Hunk_AllocName (INVERSE_PAL_SIZE + 1, INVERSE_PALNAME); VID_CreateInversePalette (palette); } } void VID_SetPalette (const unsigned char *palette) { // nothing to do } /* =================================================================== MAIN WINDOW =================================================================== */ /* ================ ClearAllStates ================ */ static void ClearAllStates (void) { Key_ClearStates (); IN_ClearStates (); } /* ================= VID_ChangeVideoMode intended only as a callback for VID_Restart_f ================= */ static void VID_ChangeVideoMode (int newmode) { int temp; temp = scr_disabled_for_loading; scr_disabled_for_loading = true; // restore gamma, reset gamma function pointers VID_ShutdownGamma(); CDAudio_Pause (); BGM_Pause (); S_ClearBuffer (); // Unload all textures and reset texture counts D_ClearOpenGLTextures(0); memset (lightmap_textures, 0, sizeof(lightmap_textures)); // reset all opengl function pointers GL_ResetFunctions(); // Avoid re-registering commands and re-allocating memory draw_reinit = true; // temporarily disable input devices IN_DeactivateMouse(); // Kill device and rendering contexts DOSGL_Shutdown(); VID_SetMode (newmode); // Reload graphics wad file (Draw_PicFromWad writes glpic_t data (sizes, // texnums) right on top of the original pic data, so the pic data will // be dirty after gl textures are loaded the first time; we need to load // a clean version) W_LoadWadFile ("gfx.wad"); // Initialize extensions and default OpenGL parameters GL_Init(); VID_InitGamma(); VID_Init8bitPalette(); // Reload pre-map pics, fonts, console, etc Draw_Init(); SCR_Init(); // R_Init() stuff: R_InitParticleTexture(); R_InitExtraTextures (); #if defined(H2W) R_InitNetgraphTexture(); #endif /* H2W */ Sbar_Init(); vid.recalc_refdef = 1; ClearAllStates (); CDAudio_Resume (); BGM_Resume (); // Reload model textures and player skins Mod_ReloadTextures(); // rebuild the lightmaps GL_BuildLightmaps(); // finished reloading all images draw_reinit = false; scr_disabled_for_loading = temp; // apply our gamma VID_ShiftPalette(NULL); } static void VID_Restart_f (void) { if (vid_mode.integer < 0 || vid_mode.integer >= nummodes) { Con_Printf ("Bad video mode %d\n", vid_mode.integer); Cvar_SetValueQuick (&vid_mode, vid_modenum); return; } Con_Printf ("Re-initializing video:\n"); VID_ChangeVideoMode (vid_mode.integer); } static void VID_PrepareModes (void) { int i; nummodes = 0; // Add the standart 4:3 modes to the windowed modes list // In an unlikely case that we receive no fullscreen modes, // this will be our modes list (kind of...) for (i = 0; i < (int)MAX_STDMODES; i++) { modelist[nummodes].width = std_modes[i].width; modelist[nummodes].height = std_modes[i].height; modelist[nummodes].halfscreen = 0; modelist[nummodes].bpp = 16; q_snprintf (modelist[nummodes].modedesc, MAX_DESC, "%d x %d", std_modes[i].width, std_modes[i].height); nummodes++; } vid_maxwidth = modelist[nummodes-1].width; vid_maxheight = modelist[nummodes-1].height; vid_default = RES_640X480; Cvar_SetValueQuick (&vid_config_glx, modelist[vid_default].width); Cvar_SetValueQuick (&vid_config_gly, modelist[vid_default].height); } static void VID_ListModes_f (void) { int i; Con_Printf ("Maximum allowed mode: %d x %d\n", vid_maxwidth, vid_maxheight); Con_Printf ("Supported modes:\n"); for (i = 0; i < nummodes; i++) Con_Printf ("%2d: %d x %d\n", i, modelist[i].width, modelist[i].height); } static void VID_NumModes_f (void) { Con_Printf ("%d video modes in current list\n", nummodes); } /* =================== VID_Init =================== */ void VID_Init (const unsigned char *palette) { static char fxmesa_env_multitex[32] = "FX_DONT_FAKE_MULTITEX=1"; static char fxglide_env_nosplash[32] = "FX_GLIDE_NO_SPLASH=1"; int i, temp, width, height; const char *read_vars[] = { "vid_config_gl8bit", "vid_config_glx", "vid_config_gly", "vid_config_consize", "gl_texture_NPOT", "gl_multitexture", "gl_lightmapfmt" }; #define num_readvars ( sizeof(read_vars)/sizeof(read_vars[0]) ) Cvar_RegisterVariable (&vid_config_gl8bit); Cvar_RegisterVariable (&vid_config_gly); Cvar_RegisterVariable (&vid_config_glx); Cvar_RegisterVariable (&vid_config_consize); Cvar_RegisterVariable (&vid_mode); Cvar_RegisterVariable (&_enable_mouse); Cvar_RegisterVariable (&gl_texture_NPOT); Cvar_RegisterVariable (&gl_lightmapfmt); Cvar_RegisterVariable (&gl_multitexture); Cvar_RegisterVariable (&vid_nopageflip); Cvar_RegisterVariable (&_vid_wait_override); Cvar_RegisterVariable (&_vid_default_mode); Cmd_AddCommand ("vid_listmodes", VID_ListModes_f); Cmd_AddCommand ("vid_nummodes", VID_NumModes_f); Cmd_AddCommand ("vid_restart", VID_Restart_f); VID_InitPalette (palette); vid.numpages = 2; // don't let fxMesa cheat multitexturing putenv (fxmesa_env_multitex); // disable the 3dfx splash screen. putenv (fxglide_env_nosplash); #ifdef GL_DLSYM i = COM_CheckParm("-gllibrary"); if (i == 0) i = COM_CheckParm ("-g"); if (i && i < com_argc - 1) gl_library = com_argv[i+1]; else gl_library = "gl.dxe"; // load the opengl library if (!GL_OpenLibrary(gl_library)) Sys_Error ("Unable to load GL library %s", gl_library); #endif DOSGL_Init(); i = COM_CheckParm("-bpp"); if (i && i < com_argc-1) { bpp = atoi(com_argv[i+1]); } // prepare the modelists, find the actual modenum for vid_default VID_PrepareModes(); // set vid_mode to our safe default first Cvar_SetValueQuick (&vid_mode, vid_default); // perform an early read of config.cfg CFG_ReadCvars (read_vars, num_readvars); width = vid_config_glx.integer; height = vid_config_gly.integer; if (vid_config_consize.integer != width) vid_conscale = true; // user is always right ... i = COM_CheckParm("-width"); if (i && i < com_argc-1) { // FIXME: this part doesn't know about a disaster case // like we aren't reported any fullscreen modes. width = atoi(com_argv[i+1]); i = COM_CheckParm("-height"); if (i && i < com_argc-1) height = atoi(com_argv[i+1]); else // proceed with 4/3 ratio height = 3 * width / 4; } // user requested a mode either from the config or from the // command line // scan existing modes to see if this is already available // if not, add this as the last "valid" video mode and set // vid_mode to it only if it doesn't go beyond vid_maxwidth i = 0; while (i < nummodes) { if (modelist[i].width == width && modelist[i].height == height) break; i++; } if (i < nummodes) { Cvar_SetValueQuick (&vid_mode, i); } else if ( (width <= vid_maxwidth && width >= MIN_WIDTH && height <= vid_maxheight && height >= MIN_HEIGHT) || COM_CheckParm("-force") ) { modelist[nummodes].width = width; modelist[nummodes].height = height; modelist[nummodes].halfscreen = 0; modelist[nummodes].bpp = 16; q_snprintf (modelist[nummodes].modedesc, MAX_DESC, "%d x %d (user mode)", width, height); Cvar_SetValueQuick (&vid_mode, nummodes); nummodes++; } else { Con_SafePrintf ("ignoring invalid -width and/or -height arguments\n"); } if (!vid_conscale) Cvar_SetValueQuick (&vid_config_consize, width); // This will display a bigger hud and readable fonts at high // resolutions. The fonts will be somewhat distorted, though i = COM_CheckParm("-conwidth"); if (i != 0 && i < com_argc-1) i = atoi(com_argv[i + 1]); else i = vid_config_consize.integer; if (i < MIN_WIDTH) i = MIN_WIDTH; else if (i > width) i = width; Cvar_SetValueQuick(&vid_config_consize, i); if (vid_config_consize.integer != width) vid_conscale = true; if (COM_CheckParm("-paltex")) Cvar_SetQuick (&vid_config_gl8bit, "1"); vid.maxwarpwidth = WARP_WIDTH; vid.maxwarpheight = WARP_HEIGHT; vid.colormap = host_colormap; vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); temp = scr_disabled_for_loading; scr_disabled_for_loading = true; // disable printing to terminal Cvar_SetROM ("sys_nostdout", "1"); //set the mode VID_SetMode (vid_mode.integer); ClearAllStates (); GL_SetupLightmapFmt(); GL_Init (); VID_InitGamma(); VID_Init8bitPalette(); // lock the early-read cvars until Host_Init is finished for (i = 0; i < (int)num_readvars; i++) Cvar_LockVar (read_vars[i]); vid_initialized = true; scr_disabled_for_loading = temp; vid.recalc_refdef = 1; Con_SafePrintf ("Video initialized.\n"); vid_menudrawfn = VID_MenuDraw; vid_menukeyfn = VID_MenuKey; } void VID_Shutdown (void) { VID_ShutdownGamma(); if (DOSGL_Shutdown) DOSGL_Shutdown (); #ifdef GL_DLSYM GL_CloseLibrary(); #endif } /* ================ VID_ToggleFullscreen Handles switching between fullscreen/windowed modes and brings the mouse to a proper state afterwards ================ */ void VID_ToggleFullscreen (void) { // always fullscreen in DOS } #ifndef H2W /* unused in hexenworld */ void D_ShowLoadingSize (void) { #if defined(DRAW_PROGRESSBARS) int cur_perc; static int prev_perc; if (!vid_initialized) return; cur_perc = loading_stage * 100; if (total_loading_size) cur_perc += current_loading_size * 100 / total_loading_size; if (cur_perc == prev_perc) return; prev_perc = cur_perc; glDrawBuffer_fp (GL_FRONT); SCR_DrawLoading(); glFlush_fp(); glDrawBuffer_fp (GL_BACK); #endif /* DRAW_PROGRESSBARS */ } #endif //======================================================== // Video menu stuff //======================================================== static int vid_menunum; static int vid_cursor; static qboolean need_apply; static qboolean vid_menu_firsttime = true; enum { VID_RESOLUTION, VID_MULTITEXTURE, VID_NPOT, VID_PALTEX, VID_BLANKLINE, // spacer line VID_RESET, VID_APPLY, VID_ITEMS }; static void M_DrawYesNo (int x, int y, int on, int white) { if (on) { if (white) M_PrintWhite (x, y, "yes"); else M_Print (x, y, "yes"); } else { if (white) M_PrintWhite (x, y, "no"); else M_Print (x, y, "no"); } } /* ================ VID_MenuDraw ================ */ static void VID_MenuDraw (void) { ScrollTitle("gfx/menu/title7.lmp"); if (vid_menu_firsttime) { // settings for entering the menu first time vid_menunum = vid_modenum; vid_cursor = 0; vid_menu_firsttime = false; } need_apply = (vid_menunum != vid_modenum) || (have_mtex && (gl_mtexable != !!gl_multitexture.integer)) || (have_NPOT && (gl_tex_NPOT != !!gl_texture_NPOT.integer)) || (have8bit && (is8bit != !!vid_config_gl8bit.integer)); M_Print (76, 92 + 8*VID_RESOLUTION, "Resolution: "); if (vid_menunum == vid_modenum) M_PrintWhite (76+12*8, 92 + 8*VID_RESOLUTION, modelist[vid_menunum].modedesc); else M_Print (76+12*8, 92 + 8*VID_RESOLUTION, modelist[vid_menunum].modedesc); M_Print (76, 92 + 8*VID_MULTITEXTURE, "Multitexturing:"); if (have_mtex) M_DrawYesNo (76+16*8, 92 + 8*VID_MULTITEXTURE, gl_multitexture.integer, (gl_mtexable == !!gl_multitexture.integer)); else M_PrintWhite (76+16*8, 92 + 8*VID_MULTITEXTURE, "Not found"); M_Print (76, 92 + 8*VID_NPOT, "NPOT textures :"); if (have_NPOT) M_DrawYesNo (76+16*8, 92 + 8*VID_NPOT, gl_texture_NPOT.integer, (gl_tex_NPOT == !!gl_texture_NPOT.integer)); else M_PrintWhite (76+16*8, 92 + 8*VID_NPOT, "Not found"); M_Print (76, 92 + 8*VID_PALTEX, "8 bit textures:"); if (have8bit) M_DrawYesNo (76+16*8, 92 + 8*VID_PALTEX, vid_config_gl8bit.integer, (is8bit == !!vid_config_gl8bit.integer)); else M_PrintWhite (76+16*8, 92 + 8*VID_PALTEX, "Not found"); if (need_apply) { M_Print (76, 92 + 8*VID_RESET, "RESET CHANGES"); M_Print (76, 92 + 8*VID_APPLY, "APPLY CHANGES"); } M_DrawCharacter (64, 92 + vid_cursor*8, 12+((int)(realtime*4)&1)); } /* ================ VID_MenuKey ================ */ static void VID_MenuKey (int key) { switch (key) { case K_ESCAPE: vid_cursor = 0; M_Menu_Options_f (); break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); vid_cursor--; if (vid_cursor < 0) { vid_cursor = (need_apply) ? VID_ITEMS-1 : VID_BLANKLINE-1; } else if (vid_cursor == VID_BLANKLINE) { vid_cursor--; } break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); vid_cursor++; if (vid_cursor >= VID_ITEMS) { vid_cursor = 0; break; } if (vid_cursor >= VID_BLANKLINE) { if (need_apply) { if (vid_cursor == VID_BLANKLINE) vid_cursor++; } else { vid_cursor = 0; } } break; case K_ENTER: switch (vid_cursor) { case VID_RESET: vid_menunum = vid_modenum; Cvar_SetValueQuick (&vid_config_gl8bit, is8bit); vid_cursor = 0; break; case VID_APPLY: if (need_apply) { Cvar_SetValueQuick(&vid_mode, vid_menunum); VID_Restart_f(); } vid_cursor = 0; break; } return; case K_LEFTARROW: switch (vid_cursor) { case VID_RESOLUTION: S_LocalSound ("raven/menu1.wav"); vid_menunum--; if (vid_menunum < 0) vid_menunum = 0; break; case VID_MULTITEXTURE: if (have_mtex) Cvar_SetQuick (&gl_multitexture, gl_multitexture.integer ? "0" : "1"); break; case VID_NPOT: if (have_NPOT) Cvar_SetQuick (&gl_texture_NPOT, gl_texture_NPOT.integer ? "0" : "1"); break; case VID_PALTEX: if (have8bit) Cvar_SetQuick (&vid_config_gl8bit, vid_config_gl8bit.integer ? "0" : "1"); break; } return; case K_RIGHTARROW: switch (vid_cursor) { case VID_RESOLUTION: S_LocalSound ("raven/menu1.wav"); vid_menunum++; if (vid_menunum >= nummodes) vid_menunum = nummodes - 1; break; case VID_MULTITEXTURE: if (have_mtex) Cvar_SetQuick (&gl_multitexture, gl_multitexture.integer ? "0" : "1"); break; case VID_NPOT: if (have_NPOT) Cvar_SetQuick (&gl_texture_NPOT, gl_texture_NPOT.integer ? "0" : "1"); break; case VID_PALTEX: if (have8bit) Cvar_SetQuick (&vid_config_gl8bit, vid_config_gl8bit.integer ? "0" : "1"); break; } return; default: break; } } engine/h2shared/gl_vidnt.c000066400000000000000000002311021444734033100157770ustar00rootroot00000000000000/* * gl_vidnt.c -- NT GL vid component * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2005-2016 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __GL_FUNC_EXTERN #include "quakedef.h" #include "winquake.h" #include #include "cfgfile.h" #include "bgmusic.h" #include "cdaudio.h" #include "resource.h" #include "wgl_func.h" #define WARP_WIDTH 320 #define WARP_HEIGHT 200 #define MAXWIDTH 10000 #define MAXHEIGHT 10000 #define MIN_WIDTH 320 //#define MIN_HEIGHT 200 #define MIN_HEIGHT 240 #define MAX_DESC 33 #define MAX_NUMBPP 8 typedef struct { modestate_t type; int width; int height; int modenum; int dib; int fullscreen; int bpp; int halfscreen; char modedesc[MAX_DESC]; } vmode_t; typedef struct { int width; int height; } stdmode_t; #define RES_640X480 3 static const stdmode_t std_modes[] = { // NOTE: keep this list in order {320, 240}, // 0 {400, 300}, // 1 {512, 384}, // 2 {640, 480}, // 3 == RES_640X480, this is our default, below // this is the lowresmodes region. // either do not change its order, // or change the above define, too {800, 600}, // 4, RES_640X480 + 1 {1024, 768}, // 5, RES_640X480 + 2 {1280, 1024}, // 6 {1600, 1200} // 7 }; #define MAX_MODE_LIST 128 #define MAX_STDMODES (sizeof(std_modes) / sizeof(std_modes[0])) #define NUM_LOWRESMODES (RES_640X480) static vmode_t fmodelist[MAX_MODE_LIST+1]; // list of enumerated fullscreen modes static vmode_t wmodelist[MAX_STDMODES +1]; // list of standart 4:3 windowed modes static vmode_t *modelist; // modelist in use, points to one of the above lists static int bpplist[MAX_NUMBPP][2]; static int num_fmodes; static int num_wmodes; static int *nummodes; static vmode_t badmode; #if defined(H2W) #define WM_CLASSNAME "HexenWorld" #define WM_WINDOWNAME "HexenWorld" #else #define WM_CLASSNAME "HexenII" #define WM_WINDOWNAME "HexenII" #endif static HGLRC baseRC; static HDC maindc; static DEVMODE gdevmode; static qboolean classregistered; HWND mainwindow; static HICON hIcon; static int DIBWidth, DIBHeight; static RECT WindowRect; int window_center_x, window_center_y, window_x, window_y, window_width, window_height; RECT window_rect; static LONG WindowStyle, ExWindowStyle; static PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd 1, // version number PFD_DRAW_TO_WINDOW // support window | PFD_SUPPORT_OPENGL // support OpenGL | PFD_DOUBLEBUFFER , // double buffered PFD_TYPE_RGBA, // RGBA type 24, // 24-bit color depth 0, 0, 0, 0, 0, 0, // color bits ignored 0, // no alpha buffer 0, // shift bit ignored 0, // no accumulation buffer 0, 0, 0, 0, // accum bits ignored 32, // 32-bit z-buffer 0, // no stencil buffer 0, // no auxiliary buffer PFD_MAIN_PLANE, // main layer 0, // reserved 0, 0, 0 // layer masks ignored }; typedef struct { int red, green, blue, alpha, depth, stencil; } attributes_t; static attributes_t vid_attribs; // main vid functions static LRESULT WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static void AppActivate(BOOL fActive, BOOL minimize); static void VID_UpdateWindowStatus (void); static const char *VID_GetModeDescription (int mode); // vars for vid state viddef_t vid; // global video state modestate_t modestate = MS_UNINIT; static int vid_default = MODE_WINDOWED; static int vid_modenum = NO_MODE; // current video mode, set after mode setting succeeds static int vid_deskwidth, vid_deskheight, vid_deskbpp, vid_deskmode; static qboolean vid_conscale = false; static qboolean vid_initialized = false; static qboolean vid_canalttab = false; static qboolean vid_wassuspended = false; // cvar vid_mode must be set before calling // VID_SetMode, VID_ChangeVideoMode or VID_Restart_f static cvar_t vid_mode = {"vid_mode", "0", CVAR_NONE}; static cvar_t vid_config_consize = {"vid_config_consize", "640", CVAR_ARCHIVE}; static cvar_t vid_config_glx = {"vid_config_glx", "640", CVAR_ARCHIVE}; static cvar_t vid_config_gly = {"vid_config_gly", "480", CVAR_ARCHIVE}; static cvar_t vid_config_bpp = {"vid_config_bpp", "16", CVAR_ARCHIVE}; static cvar_t vid_config_fscr= {"vid_config_fscr", "1", CVAR_ARCHIVE}; // cvars for compatibility with the software version static cvar_t vid_wait = {"vid_wait", "-1", CVAR_ARCHIVE}; static cvar_t vid_maxpages = {"vid_maxpages", "3", CVAR_ARCHIVE}; static cvar_t vid_nopageflip = {"vid_nopageflip", "1", CVAR_ARCHIVE}; // Note that 0 is MODE_WINDOWED // Note that 3 is MODE_FULLSCREEN_DEFAULT static cvar_t _vid_default_mode = {"_vid_default_mode", "0", CVAR_ARCHIVE}; static cvar_t _vid_default_mode_win = {"_vid_default_mode_win", "3", CVAR_ARCHIVE}; static cvar_t vid_stretch_by_2 = {"vid_stretch_by_2", "1", CVAR_ARCHIVE}; static cvar_t vid_config_x = {"vid_config_x", "800", CVAR_ARCHIVE}; static cvar_t vid_config_y = {"vid_config_y", "600", CVAR_ARCHIVE}; byte globalcolormap[VID_GRADES*256]; float RTint[256], GTint[256], BTint[256]; unsigned short d_8to16table[256]; unsigned int d_8to24table[256]; unsigned int d_8to24TranslucentTable[256]; unsigned char *inverse_pal; // gl stuff static void GL_Init (void); static HINSTANCE hInstGL; #ifdef GL_DLSYM static const char *gl_library; #endif static const char *gl_vendor; static const char *gl_renderer; static const char *gl_version; static const char *gl_extensions; qboolean is_3dfx = false; GLint gl_max_size = 256; static qboolean have_NPOT = false; qboolean gl_tex_NPOT = false; static cvar_t gl_texture_NPOT = {"gl_texture_NPOT", "0", CVAR_ARCHIVE}; GLfloat gl_max_anisotropy; float gldepthmin, gldepthmax; // palettized textures static qboolean have8bit = false; qboolean is8bit = false; static cvar_t vid_config_gl8bit = {"vid_config_gl8bit", "0", CVAR_ARCHIVE}; // Gamma stuff typedef BOOL (WINAPI *GAMMA_RAMP_FN)(HDC, LPVOID); static GAMMA_RAMP_FN GetDeviceGammaRamp_f; static GAMMA_RAMP_FN SetDeviceGammaRamp_f; extern unsigned short ramps[3][256]; // for hw- or 3dfx-gamma static unsigned short orig_ramps[3][256]; // for hw- or 3dfx-gamma static qboolean gammaworks = false; // whether hw-gamma works // multitexturing qboolean gl_mtexable = false; static GLint num_tmus = 1; static qboolean have_mtex = false; static cvar_t gl_multitexture = {"gl_multitexture", "0", CVAR_ARCHIVE}; // stencil buffer qboolean have_stencil = false; // this is useless: things aren't like those in quake //static qboolean fullsbardraw = false; // menu drawing static void VID_MenuDraw (void); static void VID_MenuKey (int key); // input stuff static void ClearAllStates (void); static int enable_mouse; cvar_t _enable_mouse = {"_enable_mouse", "0", CVAR_ARCHIVE}; //==================================== static qboolean GL_ParseExtensionList (const char *list, const char *name) { const char *start; const char *where, *terminator; if (!list || !name || !*name) return false; if (strchr(name, ' ') != NULL) return false; // extension names must not have spaces start = list; while (1) { where = strstr (start, name); if (!where) break; terminator = where + strlen (name); if (where == start || where[-1] == ' ') if (*terminator == ' ' || *terminator == '\0') return true; start = terminator; } return false; } //==================================== void VID_LockBuffer (void) { // nothing to do } void VID_UnlockBuffer (void) { // nothing to do } void VID_HandlePause (qboolean paused) { if ((modestate == MS_WINDOWED) && _enable_mouse.integer) { if (paused) { IN_DeactivateMouse (); IN_ShowMouse (); } else { IN_ActivateMouse (); IN_HideMouse (); } } } //==================================== static void CenterWindow (HWND hWndCenter, int width, int height) { int CenterX, CenterY; CenterX = (GetSystemMetrics(SM_CXSCREEN) - width) / 2; CenterY = (GetSystemMetrics(SM_CYSCREEN) - height) / 2; if (CenterX > 2*CenterY) CenterX >>= 1; // dual screen? if (CenterX < 0) CenterX = 0; if (CenterY < 0) CenterY = 0; SetWindowPos (hWndCenter, NULL, CenterX, CenterY, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); } static void VID_ConWidth (int modenum) { int w, h; if (!vid_conscale) { Cvar_SetValueQuick (&vid_config_consize, modelist[modenum].width); return; } w = vid_config_consize.integer; w &= ~7; /* make it a multiple of eight */ if (w < MIN_WIDTH) w = MIN_WIDTH; else if (w > modelist[modenum].width) w = modelist[modenum].width; h = w * modelist[modenum].height / modelist[modenum].width; if (h < 200 /* MIN_HEIGHT */ || h > modelist[modenum].height || w > modelist[modenum].width) { vid_conscale = false; Cvar_SetValueQuick (&vid_config_consize, modelist[modenum].width); return; } vid.width = vid.conwidth = w; vid.height = vid.conheight = h; if (w != modelist[modenum].width) vid_conscale = true; else vid_conscale = false; } void VID_ChangeConsize (int dir) { int w, h; switch (dir) { case -1: /* smaller text */ w = ((float)vid.conwidth/(float)vid.width + 0.05f) * vid.width; /* use 0.10f increment ?? */ w &= ~7; /* make it a multiple of eight */ if (w > modelist[vid_modenum].width) w = modelist[vid_modenum].width; break; case 1: /* bigger text */ w = ((float)vid.conwidth/(float)vid.width - 0.05f) * vid.width; w &= ~7; /* make it a multiple of eight */ if (w < MIN_WIDTH) w = MIN_WIDTH; break; default: /* bad key */ return; } h = w * modelist[vid_modenum].height / modelist[vid_modenum].width; if (h < 200) return; vid.width = vid.conwidth = w; vid.height = vid.conheight = h; Cvar_SetValueQuick (&vid_config_consize, vid.conwidth); vid.recalc_refdef = 1; if (vid.conwidth != modelist[vid_modenum].width) vid_conscale = true; else vid_conscale = false; } float VID_ReportConsize(void) { return (float)modelist[vid_modenum].width/vid.conwidth; } static qboolean VID_SetWindowedMode (int modenum) { int width, height; RECT rect; pfd.cColorBits = 24; pfd.cRedBits = 0; pfd.cGreenBits = 0; pfd.cBlueBits = 0; pfd.cAlphaBits = 0; pfd.cDepthBits = 32; pfd.cStencilBits = 0; if (vid_deskbpp >= 32) { pfd.cRedBits = 8; pfd.cGreenBits = 8; pfd.cBlueBits = 8; pfd.cAlphaBits = 8; pfd.cDepthBits = 24; pfd.cStencilBits = 8; } // Pa3PyX: set the original fullscreen mode if // we are switching to window from fullscreen. if (modestate == MS_FULLDIB) ChangeDisplaySettings(NULL, 0); WindowRect.top = WindowRect.left = 0; WindowRect.right = modelist[modenum].width; WindowRect.bottom = modelist[modenum].height; DIBWidth = modelist[modenum].width; DIBHeight = modelist[modenum].height; WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE; ExWindowStyle = 0; rect = WindowRect; AdjustWindowRectEx(&rect, WindowStyle, FALSE, 0); width = rect.right - rect.left; height = rect.bottom - rect.top; mainwindow = CreateWindowEx (ExWindowStyle, WM_CLASSNAME, WM_WINDOWNAME, WindowStyle, rect.left, rect.top, width, height, NULL, NULL, global_hInstance, NULL); if (!mainwindow) Sys_Error ("Couldn't create DIB window"); // center the DIB window CenterWindow(mainwindow, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top); modestate = MS_WINDOWED; Cvar_SetQuick (&vid_config_fscr, "0"); return true; } static qboolean VID_SetFullDIBMode (int modenum) { int width, height; RECT rect; pfd.cColorBits = modelist[modenum].bpp; if (modelist[modenum].bpp >= 32) { pfd.cRedBits = 8; pfd.cGreenBits = 8; pfd.cBlueBits = 8; pfd.cAlphaBits = 8; pfd.cDepthBits = 24; pfd.cStencilBits = 8; } else { pfd.cRedBits = 5; pfd.cGreenBits = 5; pfd.cBlueBits = 5; pfd.cAlphaBits = 0; pfd.cDepthBits = 16; pfd.cStencilBits = 0; } gdevmode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; if (!Win95old) { gdevmode.dmFields |= DM_BITSPERPEL; gdevmode.dmBitsPerPel = modelist[modenum].bpp; } gdevmode.dmPelsWidth = modelist[modenum].width << modelist[modenum].halfscreen; gdevmode.dmPelsHeight = modelist[modenum].height; gdevmode.dmSize = sizeof (gdevmode); if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) Sys_Error ("Couldn't set fullscreen DIB mode"); WindowRect.top = WindowRect.left = 0; WindowRect.right = modelist[modenum].width; WindowRect.bottom = modelist[modenum].height; DIBWidth = modelist[modenum].width; DIBHeight = modelist[modenum].height; WindowStyle = WS_POPUP | WS_SYSMENU | WS_VISIBLE; ExWindowStyle = WS_EX_TOPMOST; rect = WindowRect; AdjustWindowRectEx(&rect, WindowStyle, FALSE, 0); width = rect.right - rect.left; height = rect.bottom - rect.top; mainwindow = CreateWindowEx (ExWindowStyle, WM_CLASSNAME, WM_WINDOWNAME, WindowStyle, rect.left, rect.top, width, height, NULL, NULL, global_hInstance, NULL); if (!mainwindow) Sys_Error ("Couldn't create DIB window"); modestate = MS_FULLDIB; Cvar_SetQuick (&vid_config_fscr, "1"); // needed because we're not getting WM_MOVE messages fullscreen on NT window_x = 0; window_y = 0; return true; } static qboolean VID_SetMode (int modenum, const unsigned char *palette) { qboolean status = false; MSG msg; HDC hdc; if (modenum < 0 || modenum >= *nummodes) Sys_Error ("Bad video mode\n"); CDAudio_Pause (); // Set either the fullscreen or windowed mode if (modelist[modenum].type == MS_WINDOWED) { if (_enable_mouse.integer) { status = VID_SetWindowedMode(modenum); IN_ActivateMouse (); IN_HideMouse (); } else { status = VID_SetWindowedMode(modenum); IN_DeactivateMouse (); IN_ShowMouse (); } } else if (modelist[modenum].type == MS_FULLDIB) { status = VID_SetFullDIBMode(modenum); IN_ActivateMouse (); IN_HideMouse (); } else { Sys_Error ("%s: Bad mode type in modelist", __thisfunc__); } if (!status) { Sys_Error ("Couldn't set video mode"); } ShowWindow (mainwindow, SW_SHOWDEFAULT); UpdateWindow (mainwindow); SendMessage (mainwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon); SendMessage (mainwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon); // Because we have set the background brush for the window to NULL // (to avoid flickering when re-sizing the window on the desktop), // we clear the window to black when created, otherwise it will be // empty while Quake starts up. hdc = GetDC(mainwindow); PatBlt(hdc, 0, 0, WindowRect.right, WindowRect.bottom, BLACKNESS); ReleaseDC(mainwindow, hdc); vid.numpages = 2; vid.width = vid.conwidth = modelist[modenum].width; vid.height = vid.conheight = modelist[modenum].height; // setup the effective console width VID_ConWidth(modenum); window_width = DIBWidth; window_height = DIBHeight; VID_UpdateWindowStatus (); // now we try to make sure we get the focus on the mode switch, because // sometimes in some systems we don't. We grab the foreground, then // finish setting up, pump all our messages, and sleep for a little while // to let messages finish bouncing around the system, then we put // ourselves at the top of the z order, then grab the foreground again, // Who knows if it helps, but it probably doesn't hurt SetForegroundWindow (mainwindow); vid_modenum = modenum; Cvar_SetValueQuick (&vid_config_glx, modelist[vid_modenum].width); Cvar_SetValueQuick (&vid_config_gly, modelist[vid_modenum].height); if (modestate != MS_WINDOWED) Cvar_SetValueQuick (&vid_config_bpp, modelist[vid_modenum].bpp); while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage (&msg); DispatchMessage (&msg); } Sleep (100); SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOCOPYBITS); SetForegroundWindow (mainwindow); // fix the leftover Alt from any Alt-Tab or the like that switched us away ClearAllStates (); CDAudio_Resume (); return true; } //==================================== #if 0 /* No.. */ static void CheckSetGlobalPalette (void) { gl3DfxSetPaletteEXT_f gl3DfxSetPaletteEXT_fp; if (GL_ParseExtensionList(gl_extensions, "3DFX_set_global_palette")) { gl3DfxSetPaletteEXT_fp = (gl3DfxSetPaletteEXT_f) wglGetProcAddress_fp("gl3DfxSetPaletteEXT"); if (!gl3DfxSetPaletteEXT_fp) gl3DfxSetPaletteEXT_fp = (gl3DfxSetPaletteEXT_f) wglGetProcAddress_fp("3DFX_set_global_palette"); if (!gl3DfxSetPaletteEXT_fp) return; Con_SafePrintf("Found 3DFX_set_global_palette\n"); } else if (GL_ParseExtensionList(gl_extensions, "POWERVR_set_global_palette")) { gl3DfxSetPaletteEXT_fp = (gl3DfxSetPaletteEXT_f) wglGetProcAddress_fp("glSetGlobalPalettePOWERVR"); if (!gl3DfxSetPaletteEXT_fp) gl3DfxSetPaletteEXT_fp = (gl3DfxSetPaletteEXT_f) wglGetProcAddress_fp("POWERVR_set_global_palette"); if (!gl3DfxSetPaletteEXT_fp) return; Con_SafePrintf("Found POWERVR_set_global_palette\n"); } else { return; } have8bit = true; if (!vid_config_gl8bit.integer) return; else { int i; GLubyte table[256][4]; char *oldpal; is8bit = true; oldpal = (char *) d_8to24table; for (i = 0; i < 256; i++) { table[i][2] = *oldpal++; table[i][1] = *oldpal++; table[i][0] = *oldpal++; table[i][3] = 255; oldpal++; } glEnable_fp (GL_SHARED_TEXTURE_PALETTE_EXT); gl3DfxSetPaletteEXT_fp ((GLuint *)table); } } #endif /* #if 0 */ static void CheckSharedTexturePalette (void) { glColorTableEXT_f glColorTableEXT_fp; if (!GL_ParseExtensionList(gl_extensions, "GL_EXT_shared_texture_palette")) return; glColorTableEXT_fp = (glColorTableEXT_f) wglGetProcAddress_fp("glColorTableEXT"); if (glColorTableEXT_fp == NULL) return; have8bit = true; Con_SafePrintf("Found GL_EXT_shared_texture_palette\n"); if (!vid_config_gl8bit.integer) return; else { int i; char thePalette[256*3]; char *oldPalette, *newPalette; is8bit = true; oldPalette = (char *) d_8to24table; newPalette = thePalette; for (i = 0; i < 256; i++) { *newPalette++ = *oldPalette++; *newPalette++ = *oldPalette++; *newPalette++ = *oldPalette++; oldPalette++; } glEnable_fp (GL_SHARED_TEXTURE_PALETTE_EXT); glColorTableEXT_fp (GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, (void *) thePalette); } } static void VID_Init8bitPalette (void) { have8bit = false; is8bit = false; /* Check for 8bit Extensions and initialize them */ CheckSharedTexturePalette(); #if 0 /* No.. */ if (!have8bit) CheckSetGlobalPalette(); #endif if (is8bit) Con_SafePrintf("8-bit palettized textures enabled\n"); } static void VID_Check3dfxGamma (void) { if (!GL_ParseExtensionList(gl_extensions, "WGL_3DFX_gamma_control")) { GetDeviceGammaRamp_f = GetDeviceGammaRamp; SetDeviceGammaRamp_f = SetDeviceGammaRamp; } else { GetDeviceGammaRamp_f = (GAMMA_RAMP_FN) wglGetProcAddress_fp("wglGetDeviceGammaRamp3DFX"); SetDeviceGammaRamp_f = (GAMMA_RAMP_FN) wglGetProcAddress_fp("wglSetDeviceGammaRamp3DFX"); if (GetDeviceGammaRamp_f && SetDeviceGammaRamp_f) Con_SafePrintf("Using 3Dfx specific gamma control\n"); else { GetDeviceGammaRamp_f = GetDeviceGammaRamp; SetDeviceGammaRamp_f = SetDeviceGammaRamp; } } } static void VID_InitGamma (void) { GetDeviceGammaRamp_f = NULL; SetDeviceGammaRamp_f = NULL; gammaworks = false; VID_Check3dfxGamma (); if (GetDeviceGammaRamp_f) gammaworks = GetDeviceGammaRamp_f(maindc, orig_ramps); if (!gammaworks) Con_SafePrintf("gamma adjustment not available\n"); } void VID_ShiftPalette (const unsigned char *palette) { if (gammaworks && SetDeviceGammaRamp_f) SetDeviceGammaRamp_f (maindc, ramps); } static void CheckMultiTextureExtensions (void) { gl_mtexable = have_mtex = false; if (COM_CheckParm("-nomtex")) { Con_SafePrintf("Multitexture extensions disabled\n"); } else if (GL_ParseExtensionList(gl_extensions, "GL_ARB_multitexture")) { Con_SafePrintf("ARB Multitexture extensions found\n"); glGetIntegerv_fp(GL_MAX_TEXTURE_UNITS_ARB, &num_tmus); if (num_tmus < 2) { Con_SafePrintf("ignoring multitexture (%i TMUs)\n", (int) num_tmus); return; } glMultiTexCoord2fARB_fp = (glMultiTexCoord2fARB_f) wglGetProcAddress_fp("glMultiTexCoord2fARB"); glActiveTextureARB_fp = (glActiveTextureARB_f) wglGetProcAddress_fp("glActiveTextureARB"); if (glMultiTexCoord2fARB_fp == NULL || glActiveTextureARB_fp == NULL) { Con_SafePrintf ("Couldn't link to multitexture functions\n"); return; } have_mtex = true; if (!gl_multitexture.integer) { Con_SafePrintf("ignoring multitexture (cvar disabled)\n"); return; } Con_SafePrintf("Found %i TMUs support\n", num_tmus); gl_mtexable = true; glDisable_fp(GL_TEXTURE_2D); glActiveTextureARB_fp(GL_TEXTURE0_ARB); } else { Con_SafePrintf("GL_ARB_multitexture not found\n"); } } static void CheckAnisotropyExtensions (void) { gl_max_anisotropy = 1; Con_SafePrintf("Anisotropic filtering "); if (GL_ParseExtensionList(gl_extensions, "GL_EXT_texture_filter_anisotropic")) { GLfloat test1 = 0, test2 = 0; GLuint tex; glGetTexParameterfv_f glGetTexParameterfv_fp; glGetTexParameterfv_fp = (glGetTexParameterfv_f) GetProcAddress(hInstGL, "glGetTexParameterfv"); if (glGetTexParameterfv_fp == NULL) { Con_SafePrintf("... can't check driver-lock status\n... "); goto _skiptest; } // test to make sure we really have control over it // 1.0 and 2.0 should always be legal values. glGenTextures_fp(1, &tex); glBindTexture_fp(GL_TEXTURE_2D, tex); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); glGetTexParameterfv_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &test1); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 2.0f); glGetTexParameterfv_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &test2); glDeleteTextures_fp(1, &tex); if (test1 != 1 || test2 != 2) { Con_SafePrintf("driver-locked @ %.1f\n", test1); } else { _skiptest: glGetFloatv_fp(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_max_anisotropy); if (gl_max_anisotropy < 2) Con_SafePrintf("broken\n"); else Con_SafePrintf("found, max %.1f\n", gl_max_anisotropy); } } else { Con_SafePrintf("not found\n"); } } static void CheckNonPowerOfTwoTextures (void) { /* On Mac OS X, old Radeons lie about NPOT textures capability, they * fallback to software with mipmap NPOT textures. see, e.g.: * http://lists.apple.com/archives/mac-opengl/2006/Dec/msg00000.html * http://lists.apple.com/archives/mac-opengl/2009/Oct/msg00040.html * http://www.idevgames.com/forums/printthread.php?tid=3814&page=2 * MH says NVIDIA once did the same with their GeForce FX on Windows: * http://forums.inside3d.com/viewtopic.php?f=10&t=4832 * Therefore, advertisement of this extension is an unreliable way of * detecting the actual capability. */ gl_tex_NPOT = have_NPOT = false; if (GL_ParseExtensionList(gl_extensions, "GL_ARB_texture_non_power_of_two")) { have_NPOT = true; Con_SafePrintf("Found ARB_texture_non_power_of_two\n"); if (!gl_texture_NPOT.integer) { Con_SafePrintf("ignoring texture_NPOT (cvar disabled)\n"); } else { gl_tex_NPOT = true; } } else { Con_SafePrintf("GL_ARB_texture_non_power_of_two not found\n"); } } static void CheckStencilBuffer (void) { have_stencil = !!vid_attribs.stencil; } #ifdef GL_DLSYM static qboolean GL_OpenLibrary (const char *name) { int drv_standalone = q_strcasecmp(name, "opengl32.dll"); Con_SafePrintf("Loading OpenGL library %s\n", name); // open the library if (!(hInstGL = LoadLibrary(name))) { Con_SafePrintf("Unable to LoadLibrary %s\n", name); return false; } // link to necessary wgl functions #define GL_FUNCTION(ret, func, params) \ do { \ func##_fp = (func##_f) GetProcAddress(hInstGL, #func); \ if (func##_fp == NULL) \ Sys_Error("Couldn't link to %s", #func); \ } while (0); #define GL_FUNCTION_OPT(ret,func,def,params) \ do { \ func##_fp = (drv_standalone)? \ (func##_f) GetProcAddress(hInstGL, #func) : def; \ if (func##_fp == NULL) \ Sys_Error("Couldn't link to %s", #func); \ } while (0); #include "wgl_func.h" return true; } static void GL_CloseLibrary (void) { // clear the wgl function pointers #define GL_FUNCTION(ret, func, params) \ func##_fp = NULL; #define GL_FUNCTION_OPT(ret,func,def,params) \ func##_fp = NULL; #include "wgl_func.h" // free the library if (hInstGL != NULL) FreeLibrary(hInstGL); hInstGL = NULL; } static void GL_Init_Functions (void) { #define GL_FUNCTION(ret, func, params) \ do { \ func##_fp = (func##_f) GetProcAddress(hInstGL, #func); \ if (func##_fp == NULL) \ Sys_Error("%s not found in GL library", #func); \ } while (0); #define GL_FUNCTION_OPT(ret, func, params) #include "gl_func.h" } #endif /* GL_DLSYM */ static void GL_ResetFunctions (void) { #ifdef GL_DLSYM #define GL_FUNCTION(ret, func, params) \ func##_fp = NULL; #endif #define GL_FUNCTION_OPT(ret, func, params) \ func##_fp = NULL; #include "gl_func.h" GetDeviceGammaRamp_f = NULL; SetDeviceGammaRamp_f = NULL; have_stencil = false; gl_mtexable = false; have_mtex = false; have8bit = false; is8bit = false; have_NPOT = false; gl_tex_NPOT = false; } /* =============== GL_Init =============== */ static void GL_Init (void) { PIXELFORMATDESCRIPTOR new_pfd; Con_SafePrintf ("Video mode %s initialized\n", VID_GetModeDescription (vid_modenum)); #ifdef GL_DLSYM // initialize gl function pointers GL_Init_Functions(); #endif // collect the visual attributes memset (&new_pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); memset (&vid_attribs, 0, sizeof(attributes_t)); glGetIntegerv_fp(GL_RED_BITS, &vid_attribs.red); glGetIntegerv_fp(GL_GREEN_BITS, &vid_attribs.green); glGetIntegerv_fp(GL_BLUE_BITS, &vid_attribs.blue); glGetIntegerv_fp(GL_ALPHA_BITS, &vid_attribs.alpha); glGetIntegerv_fp(GL_DEPTH_BITS, &vid_attribs.depth); glGetIntegerv_fp(GL_STENCIL_BITS, &vid_attribs.stencil); Con_SafePrintf ("R:%d G:%d B:%d A:%d, Z:%d, S:%d\n", vid_attribs.red, vid_attribs.green, vid_attribs.blue, vid_attribs.alpha, vid_attribs.depth, vid_attribs.stencil); /* FIXME: DescribePixelFormat() used to fail with old 3dfx minigl drivers ??? */ if (wglDescribePixelFormat_fp(maindc, wglGetPixelFormat_fp(maindc), sizeof(PIXELFORMATDESCRIPTOR), &new_pfd)) { Con_SafeDPrintf ("PFD: C:%d, R:%d G:%d B:%d A:%d, Z:%d, S:%d\n", new_pfd.cColorBits, new_pfd.cRedBits, new_pfd.cGreenBits, new_pfd.cBlueBits, new_pfd.cAlphaBits, new_pfd.cDepthBits, new_pfd.cStencilBits); if ((new_pfd.dwFlags & PFD_GENERIC_FORMAT) && !(new_pfd.dwFlags & PFD_GENERIC_ACCELERATED)) Con_SafePrintf ("WARNING: Hardware acceleration not present\n"); else if (new_pfd.dwFlags & PFD_GENERIC_ACCELERATED) Con_SafePrintf ("OpenGL: MCD acceleration found\n"); } gl_vendor = (const char *)glGetString_fp (GL_VENDOR); Con_SafePrintf ("GL_VENDOR: %s\n", gl_vendor); gl_renderer = (const char *)glGetString_fp (GL_RENDERER); Con_SafePrintf ("GL_RENDERER: %s\n", gl_renderer); gl_version = (const char *)glGetString_fp (GL_VERSION); Con_SafePrintf ("GL_VERSION: %s\n", gl_version); gl_extensions = (const char *)glGetString_fp (GL_EXTENSIONS); Con_SafeDPrintf ("GL_EXTENSIONS: %s\n", gl_extensions); glGetIntegerv_fp(GL_MAX_TEXTURE_SIZE, &gl_max_size); if (gl_max_size < 256) // Refuse to work when less than 256 Sys_Error ("hardware capable of min. 256k opengl texture size needed"); Con_SafePrintf("OpenGL max.texture size: %i\n", gl_max_size); is_3dfx = false; if (!q_strncasecmp(gl_renderer, "3dfx", 4) || !q_strncasecmp(gl_renderer, "SAGE Glide", 10) || !q_strncasecmp(gl_renderer, "Glide ", 6) || /* possible with Mesa 3.x/4.x/5.0.x */ !q_strncasecmp(gl_renderer, "Mesa Glide", 10)) { Con_SafePrintf("3dfx Voodoo found\n"); is_3dfx = true; } // if (!q_strncasecmp(gl_renderer, "PowerVR", 7)) // fullsbardraw = true; VID_InitGamma (); CheckMultiTextureExtensions(); CheckAnisotropyExtensions(); CheckNonPowerOfTwoTextures(); CheckStencilBuffer(); // glClearColor_fp(1,0,0,0); glCullFace_fp(GL_FRONT); glEnable_fp(GL_TEXTURE_2D); glEnable_fp(GL_ALPHA_TEST); glAlphaFunc_fp(GL_GREATER, 0.632); // 1 - e^-1 : replaced 0.666 to avoid clipping of smaller fonts/graphics #if 0 /* causes side effects at least in 16 bpp. */ /* Get rid of Z-fighting for textures by offsetting the * drawing of entity models compared to normal polygons. * (See: R_DrawBrushModel.) * (Only works if gl_ztrick is turned off) */ glPolygonOffset_fp(0.05f, 25.0f); #endif /* #if 0 */ glPolygonMode_fp (GL_FRONT_AND_BACK, GL_FILL); glShadeModel_fp (GL_FLAT); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glBlendFunc_fp (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } /* ================= GL_BeginRendering ================= */ void GL_BeginRendering (int *x, int *y, int *width, int *height) { *x = *y = 0; *width = WindowRect.right - WindowRect.left; *height = WindowRect.bottom - WindowRect.top; // if (!wglMakeCurrent_fp( maindc, baseRC )) // Sys_Error ("wglMakeCurrent failed"); // glViewport_fp (*x, *y, *width, *height); } void GL_EndRendering (void) { if (!scr_skipupdate) wglSwapBuffers_fp(maindc); // handle the mouse state when windowed if that's changed if (modestate == MS_WINDOWED) { if (_enable_mouse.integer != enable_mouse) { if (_enable_mouse.integer) { IN_ActivateMouse (); IN_HideMouse (); } else { IN_DeactivateMouse (); IN_ShowMouse (); } enable_mouse = _enable_mouse.integer; } } // if (fullsbardraw) // Sbar_Changed(); } const int ColorIndex[16] = { 0, 31, 47, 63, 79, 95, 111, 127, 143, 159, 175, 191, 199, 207, 223, 231 }; const unsigned int ColorPercent[16] = { 25, 51, 76, 102, 114, 127, 140, 153, 165, 178, 191, 204, 216, 229, 237, 247 }; #define INVERSE_PALNAME "gfx/invpal.lmp" static int ConvertTrueColorToPal (const unsigned char *true_color, const unsigned char *palette) { int i; long min_dist; int min_index; long r, g, b; min_dist = 256 * 256 + 256 * 256 + 256 * 256; min_index = -1; r = (long) true_color[0]; g = (long) true_color[1]; b = (long) true_color[2]; for (i = 0; i < 256; i++) { long palr, palg, palb, dist; long dr, dg, db; palr = palette[3*i]; palg = palette[3*i+1]; palb = palette[3*i+2]; dr = palr - r; dg = palg - g; db = palb - b; dist = dr * dr + dg * dg + db * db; if (dist < min_dist) { min_dist = dist; min_index = i; } } return min_index; } static void VID_CreateInversePalette (const unsigned char *palette) { long r, g, b; long idx = 0; unsigned char true_color[3]; Con_SafePrintf ("Creating inverse palette\n"); for (r = 0; r < (1 << INVERSE_PAL_R_BITS); r++) { for (g = 0; g < (1 << INVERSE_PAL_G_BITS); g++) { for (b = 0; b < (1 << INVERSE_PAL_B_BITS); b++) { true_color[0] = (unsigned char)(r << (8 - INVERSE_PAL_R_BITS)); true_color[1] = (unsigned char)(g << (8 - INVERSE_PAL_G_BITS)); true_color[2] = (unsigned char)(b << (8 - INVERSE_PAL_B_BITS)); inverse_pal[idx] = ConvertTrueColorToPal(true_color, palette); idx++; } } } FS_CreatePath(FS_MakePath(FS_USERDIR, NULL, INVERSE_PALNAME)); FS_WriteFile (INVERSE_PALNAME, inverse_pal, INVERSE_PAL_SIZE); } static void VID_InitPalette (const unsigned char *palette) { const unsigned char *pal; unsigned short r, g, b; unsigned short i, p, c; unsigned int v, *table; int mark; #if ENDIAN_RUNTIME_DETECT switch (host_byteorder) { case BIG_ENDIAN: /* R G B A */ MASK_r = 0xff000000; MASK_g = 0x00ff0000; MASK_b = 0x0000ff00; MASK_a = 0x000000ff; SHIFT_r = 24; SHIFT_g = 16; SHIFT_b = 8; SHIFT_a = 0; break; case LITTLE_ENDIAN: /* A B G R */ MASK_r = 0x000000ff; MASK_g = 0x0000ff00; MASK_b = 0x00ff0000; MASK_a = 0xff000000; SHIFT_r = 0; SHIFT_g = 8; SHIFT_b = 16; SHIFT_a = 24; break; default: break; } MASK_rgb = (MASK_r|MASK_g|MASK_b); #endif /* ENDIAN_RUNTIME_DETECT */ // // 8 8 8 encoding // pal = palette; table = d_8to24table; for (i = 0; i < 256; i++) { r = pal[0]; g = pal[1]; b = pal[2]; pal += 3; v = (255 << SHIFT_a) + (r << SHIFT_r) + (g << SHIFT_g) + (b << SHIFT_b); *table++ = v; } d_8to24table[255] &= MASK_rgb; // 255 is transparent pal = palette; table = d_8to24TranslucentTable; for (i = 0; i < 16; i++) { c = ColorIndex[i] * 3; r = pal[c]; g = pal[c + 1]; b = pal[c + 2]; for (p = 0; p < 16; p++) { v = (ColorPercent[15 - p] << SHIFT_a) + (r << SHIFT_r) + (g << SHIFT_g) + (b << SHIFT_b); *table++ = v; RTint[i*16 + p] = ((float)r) / ((float)ColorPercent[15-p]); GTint[i*16 + p] = ((float)g) / ((float)ColorPercent[15-p]); BTint[i*16 + p] = ((float)b) / ((float)ColorPercent[15-p]); } } // Initialize the palettized textures data mark = Hunk_LowMark (); inverse_pal = (unsigned char *) FS_LoadHunkFile (INVERSE_PALNAME, NULL); if (inverse_pal != NULL && fs_filesize != INVERSE_PAL_SIZE) { Hunk_FreeToLowMark (mark); inverse_pal = NULL; } if (inverse_pal == NULL) { inverse_pal = (unsigned char *) Hunk_AllocName (INVERSE_PAL_SIZE + 1, INVERSE_PALNAME); VID_CreateInversePalette (palette); } } void VID_SetPalette (const unsigned char *palette) { // nothing to do } //========================================================================== static BOOL bSetupPixelFormat (HDC hDC) { int pixelformat; if ( (pixelformat = wglChoosePixelFormat_fp(hDC, &pfd)) == 0 ) { MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK); return FALSE; } if (wglSetPixelFormat_fp(hDC, pixelformat, &pfd) == FALSE) { MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK); return FALSE; } return TRUE; } /* ======= MapKey Map from windows to quake keynums ======= */ static byte scantokey[128] = { // 0 1 2 3 4 5 6 7 // 8 9 A B C D E F 0 , 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', K_ENTER, K_CTRL, 'a', 's', // 1 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', K_SHIFT, '\\', 'z', 'x', 'c', 'v', // 2 'b', 'n', 'm', ',', '.', '/', K_SHIFT, K_KP_STAR, K_ALT, ' ', 0 , K_F1, K_F2, K_F3, K_F4, K_F5, // 3 K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, 0 , K_HOME, K_UPARROW,K_PGUP,K_KP_MINUS,K_LEFTARROW,K_KP_5,K_RIGHTARROW,K_KP_PLUS,K_END, // 4 K_DOWNARROW,K_PGDN,K_INS,K_DEL, 0 , 0 , 0 , K_F11, K_F12, 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 5 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 6 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 }; #if 0 /* not used */ static byte shiftscantokey[128] = { // 0 1 2 3 4 5 6 7 // 8 9 A B C D E F 0 , 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', K_BACKSPACE, 9, // 0 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', K_ENTER, K_CTRL, 'A', 'S', // 1 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"' , '~', K_SHIFT, '|', 'Z', 'X', 'C', 'V', // 2 'B', 'N', 'M', '<', '>', '?', K_SHIFT, K_KP_STAR, K_ALT, ' ', 0 , K_F1, K_F2, K_F3, K_F4, K_F5, // 3 K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, 0 , K_HOME, K_UPARROW,K_PGUP,K_KP_MINUS,K_LEFTARROW,K_KP_5,K_RIGHTARROW,K_KP_PLUS,K_END, // 4 K_DOWNARROW,K_PGDN,K_INS,K_DEL, 0 , 0 , 0 , K_F11, K_F12, 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 5 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 6 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 }; #endif static int MapKey (int key) { int result = (key >> 16) & 255; if (result > 127) return 0; result = scantokey[result]; if (key & (1 << 24)) /* extended */ { switch (result) { case K_PAUSE: return (Key_IsGameKey()) ? K_KP_NUMLOCK : 0; case K_ENTER: return (Key_IsGameKey()) ? K_KP_ENTER : K_ENTER; case '/': return (Key_IsGameKey()) ? K_KP_SLASH : '/'; } } else /* standart */ { switch (result) { case K_KP_STAR: return (Key_IsGameKey()) ? K_KP_STAR : '*'; case K_KP_PLUS: return (Key_IsGameKey()) ? K_KP_PLUS : '+'; case K_KP_MINUS: return (Key_IsGameKey()) ? K_KP_MINUS : '-'; case K_HOME: return (Key_IsGameKey()) ? K_KP_HOME : (GetKeyState(VK_NUMLOCK) & 0x01) ? '7' : K_HOME; case K_UPARROW: return (Key_IsGameKey()) ? K_KP_UPARROW : (GetKeyState(VK_NUMLOCK) & 0x01) ? '8' : K_UPARROW; case K_PGUP: return (Key_IsGameKey()) ? K_KP_PGUP : (GetKeyState(VK_NUMLOCK) & 0x01) ? '9' : K_PGUP; case K_LEFTARROW: return (Key_IsGameKey()) ? K_KP_LEFTARROW : (GetKeyState(VK_NUMLOCK) & 0x01) ? '4' : K_LEFTARROW; case K_KP_5: return (Key_IsGameKey()) ? K_KP_5 : '5'; case K_RIGHTARROW: return (Key_IsGameKey()) ? K_KP_RIGHTARROW : (GetKeyState(VK_NUMLOCK) & 0x01) ? '6' : K_RIGHTARROW; case K_END: return (Key_IsGameKey()) ? K_KP_END : (GetKeyState(VK_NUMLOCK) & 0x01) ? '1' : K_END; case K_DOWNARROW: return (Key_IsGameKey()) ? K_KP_DOWNARROW : (GetKeyState(VK_NUMLOCK) & 0x01) ? '2' : K_DOWNARROW; case K_PGDN: return (Key_IsGameKey()) ? K_KP_PGDN : (GetKeyState(VK_NUMLOCK) & 0x01) ? '3' : K_PGDN; case K_INS: return (Key_IsGameKey()) ? K_KP_INS : (GetKeyState(VK_NUMLOCK) & 0x01) ? '0' : K_INS; case K_DEL: return (Key_IsGameKey()) ? K_KP_DEL : (GetKeyState(VK_NUMLOCK) & 0x01) ? '.' : K_DEL; } } return result; } /* =================================================================== MAIN WINDOW =================================================================== */ /* ================ ClearAllStates ================ */ static void ClearAllStates (void) { Key_ClearStates (); IN_ClearStates (); } /* ================ VID_UpdateWindowStatus ================ */ static void VID_UpdateWindowStatus (void) { window_rect.left = window_x; window_rect.top = window_y; window_rect.right = window_x + window_width; window_rect.bottom = window_y + window_height; window_center_x = (window_rect.left + window_rect.right) / 2; window_center_y = (window_rect.top + window_rect.bottom) / 2; IN_UpdateClipCursor (); } static void AppActivate (BOOL fActive, BOOL minimize) /**************************************************************************** * * Function: AppActivate * Parameters: fActive - True if app is activating * * Description: If the application is activating, then swap the system * into SYSPAL_NOSTATIC mode so that our palettes will display * correctly. * ****************************************************************************/ { static BOOL sound_active; ActiveApp = fActive; Minimized = minimize; // enable/disable sound on focus gain/loss if (!ActiveApp && sound_active) { S_BlockSound (); sound_active = false; } else if (ActiveApp && !sound_active) { S_UnblockSound (); sound_active = true; } if (fActive) { if (modestate == MS_FULLDIB) { if (vid_canalttab && vid_wassuspended) { vid_wassuspended = false; ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN); ShowWindow(mainwindow, SW_SHOWNORMAL); // Fix for alt-tab bug in NVidia drivers, from quakeforge MoveWindow(mainwindow, 0, 0, gdevmode.dmPelsWidth, gdevmode.dmPelsHeight, false); } IN_ActivateMouse (); IN_HideMouse (); } else if (modestate == MS_WINDOWED && _enable_mouse.integer) { // with winmouse, we may fail having our // window back from the iconified state. yuck... if (dinput_init) { IN_ActivateMouse (); IN_HideMouse (); } } if (host_initialized && !draw_reinit) // paranoia, but just in case.. VID_ShiftPalette(NULL); } if (!fActive) { if (maindc && gammaworks && SetDeviceGammaRamp_f) { SetDeviceGammaRamp_f(maindc, orig_ramps); } if (modestate == MS_FULLDIB) { IN_DeactivateMouse (); IN_ShowMouse (); if (vid_canalttab) { ChangeDisplaySettings (NULL, 0); vid_wassuspended = true; } } else if (modestate == MS_WINDOWED && _enable_mouse.integer) { IN_DeactivateMouse (); IN_ShowMouse (); } } } static int MWheelAccumulator; static UINT uMSG_MOUSEWHEEL = 0; extern cvar_t mwheelthreshold; /* main window procedure */ static LRESULT WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT ret = 0; int fActive, fMinimized, temp; if (uMSG_MOUSEWHEEL && uMsg == uMSG_MOUSEWHEEL) { /* Win95/WinNT-3.51 code using MSH_MOUSEWHEEL, see: * http://msdn.microsoft.com/en-us/library/ms645617.aspx */ if (mwheelthreshold.integer >= 1) { MWheelAccumulator += (int) wParam; while (MWheelAccumulator >= mwheelthreshold.integer) { Key_Event(K_MWHEELUP, true); Key_Event(K_MWHEELUP, false); MWheelAccumulator -= mwheelthreshold.integer; } while (MWheelAccumulator <= -mwheelthreshold.integer) { Key_Event(K_MWHEELDOWN, true); Key_Event(K_MWHEELDOWN, false); MWheelAccumulator += mwheelthreshold.integer; } } return DefWindowProc (hWnd, uMsg, wParam, lParam); } switch (uMsg) { case WM_ERASEBKGND: return 1; case WM_KILLFOCUS: if (modestate == MS_FULLDIB) ShowWindow(mainwindow, SW_SHOWMINNOACTIVE); break; case WM_CREATE: if (Win95) { uMSG_MOUSEWHEEL = RegisterWindowMessage("MSWHEEL_ROLLMSG"); if (!uMSG_MOUSEWHEEL) Con_SafePrintf ("couldn't register mousewheel\n"); } break; case WM_MOVE: // ignore when fullscreen if (modestate == MS_FULLDIB) break; window_x = (short) LOWORD(lParam); window_y = (short) HIWORD(lParam); VID_UpdateWindowStatus (); break; case WM_SIZE: break; case WM_SYSCHAR: // keep Alt-Space from happening break; case WM_ACTIVATE: fActive = LOWORD(wParam); fMinimized = (BOOL) HIWORD(wParam); AppActivate(!(fActive == WA_INACTIVE), fMinimized); // fix the leftover Alt from any Alt-Tab or the like that switched us away ClearAllStates (); break; case WM_KEYDOWN: case WM_SYSKEYDOWN: Key_Event (MapKey(lParam), true); break; case WM_KEYUP: case WM_SYSKEYUP: Key_Event (MapKey(lParam), false); break; // this is complicated because Win32 seems to pack multiple mouse // events into one update sometimes, so we always check all states // and look for events case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_XBUTTONDOWN: case WM_XBUTTONUP: case WM_MOUSEMOVE: temp = 0; if (wParam & MK_LBUTTON) temp |= 1; if (wParam & MK_RBUTTON) temp |= 2; if (wParam & MK_MBUTTON) temp |= 4; // intellimouse explorer if (wParam & MK_XBUTTON1) temp |= 8; if (wParam & MK_XBUTTON2) temp |= 16; IN_MouseEvent (temp); break; case WM_MOUSEWHEEL: if ((short) HIWORD(wParam) > 0) { Key_Event(K_MWHEELUP, true); Key_Event(K_MWHEELUP, false); } else { Key_Event(K_MWHEELDOWN, true); Key_Event(K_MWHEELDOWN, false); } return 0; case WM_CLOSE: if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit", MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES) { Sys_Quit (); } break; case WM_DESTROY: // PostQuitMessage (0); break; case MM_MCINOTIFY: #if !defined(_NO_CDAUDIO) ret = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); #endif /* ! _NO_CDAUDIO */ break; #if !defined(_NO_MIDIDRV) case WM_MSTREAM_UPDATEVOLUME: MIDI_SetChannelVolume((DWORD)wParam, (DWORD)lParam); return 1; case WM_MSTREAM_UPDATEVOLUMES: MIDI_SetAllChannelVolumes((DWORD) wParam); return 1; #endif /* ! _NO_MIDIDRV */ default: /* pass all unhandled messages to DefWindowProc */ ret = DefWindowProc (hWnd, uMsg, wParam, lParam); break; } /* return 1 if handled message, 0 if not */ return ret; } /* ================= VID_NumModes ================= */ static int VID_NumModes (void) { return *nummodes; } /* ================= VID_GetModePtr ================= */ static vmode_t *VID_GetModePtr (int modenum) { if ((modenum >= 0) && (modenum < *nummodes)) return &modelist[modenum]; else return &badmode; } /* ================= VID_GetModeDescription ================= */ static const char *VID_GetModeDescription (int mode) { const char *pinfo; vmode_t *pv; if ((mode < 0) || (mode >= *nummodes)) return NULL; pv = VID_GetModePtr (mode); pinfo = pv->modedesc; return pinfo; } // KJB: Added this to return the mode driver name in description for console static const char *VID_GetExtModeDescription (int mode) { static char pinfo[100]; vmode_t *pv; if ((mode < 0) || (mode >= *nummodes)) return NULL; pv = VID_GetModePtr (mode); if (modelist[mode].type == MS_FULLDIB) { q_snprintf(pinfo, sizeof(pinfo), "%s fullscreen", pv->modedesc); } else { if (modestate == MS_WINDOWED) q_snprintf(pinfo, sizeof(pinfo), "%s windowed", pv->modedesc); else q_snprintf(pinfo, sizeof(pinfo), "windowed"); } return pinfo; } /* ================= VID_DescribeCurrentMode_f ================= */ static void VID_DescribeCurrentMode_f (void) { Con_Printf ("%s\n", VID_GetExtModeDescription (vid_modenum)); } /* ================= VID_NumModes_f ================= */ static void VID_NumModes_f (void) { if (*nummodes == 1) Con_Printf ("1 video mode is available\n"); else Con_Printf ("%d video modes are available\n", *nummodes); } /* ================= VID_DescribeMode_f ================= */ static void VID_DescribeMode_f (void) { int modenum; modenum = atoi (Cmd_Argv(1)); Con_Printf ("%s\n", VID_GetExtModeDescription (modenum)); } /* ================= VID_DescribeModes_f ================= */ static void VID_DescribeModes_f (void) { int i, lnummodes; const char *pinfo; vmode_t *pv; lnummodes = VID_NumModes (); for (i = 0; i < lnummodes; i++) { pv = VID_GetModePtr (i); pinfo = VID_GetExtModeDescription (i); Con_Printf ("%2d: %s\n", i, pinfo); } } static void VID_RegisterWndClass (HINSTANCE hInstance) { WNDCLASS wc; wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = LoadCursor (NULL, IDC_ARROW); wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = WM_CLASSNAME; if (!RegisterClass(&wc)) Sys_Error ("Couldn't register main window class"); classregistered = true; } static void VID_InitDIB (HINSTANCE hInstance) { int i; HDC tempDC; // get desktop settings, hope that the user don't do a silly // thing like going back to desktop and changing its settings //vid_deskwidth = GetSystemMetrics (SM_CXSCREEN); //vid_deskheight = GetSystemMetrics (SM_CXSCREEN); tempDC = GetDC (GetDesktopWindow()); vid_deskwidth = GetDeviceCaps (tempDC, HORZRES); vid_deskheight = GetDeviceCaps(tempDC, VERTRES); vid_deskbpp = GetDeviceCaps (tempDC, BITSPIXEL); ReleaseDC (GetDesktopWindow(), tempDC); // refuse to run if vid_deskbpp < 15 if (vid_deskbpp < 15) Sys_Error ("Desktop color depth too low\n" "Make sure you are running at 16 bpp or better"); /* Register the frame class */ VID_RegisterWndClass(hInstance); // initialize standart windowed modes list num_wmodes = 0; for (i = 0, num_wmodes = 0; i < (int)MAX_STDMODES; i++) { if (std_modes[i].width <= vid_deskwidth && std_modes[i].height <= vid_deskheight) { wmodelist[num_wmodes].type = MS_WINDOWED; wmodelist[num_wmodes].width = std_modes[i].width; wmodelist[num_wmodes].height = std_modes[i].height; q_snprintf (wmodelist[num_wmodes].modedesc, MAX_DESC, "%dx%d", wmodelist[num_wmodes].width, wmodelist[num_wmodes].height); wmodelist[num_wmodes].modenum = MODE_WINDOWED; wmodelist[num_wmodes].dib = 1; wmodelist[num_wmodes].fullscreen = 0; wmodelist[num_wmodes].halfscreen = 0; wmodelist[num_wmodes].bpp = 0; num_wmodes++; } } } /* ================= VID_InitFullDIB ================= */ static void VID_InitFullDIB (HINSTANCE hInstance) { DEVMODE devmode; int i, modenum, existingmode; int j, bpp, done; BOOL status; num_fmodes = 0; modenum = 0; // enumerate >8 bpp modes do { status = EnumDisplaySettings (NULL, modenum, &devmode); if ((devmode.dmBitsPerPel >= 15) && (devmode.dmPelsWidth <= MAXWIDTH) && (devmode.dmPelsHeight <= MAXHEIGHT) && (num_fmodes < MAX_MODE_LIST)) { devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL) { fmodelist[num_fmodes].type = MS_FULLDIB; fmodelist[num_fmodes].width = devmode.dmPelsWidth; fmodelist[num_fmodes].height = devmode.dmPelsHeight; fmodelist[num_fmodes].modenum = 0; fmodelist[num_fmodes].halfscreen = 0; fmodelist[num_fmodes].dib = 1; fmodelist[num_fmodes].fullscreen = 1; fmodelist[num_fmodes].bpp = devmode.dmBitsPerPel; q_snprintf (fmodelist[num_fmodes].modedesc, MAX_DESC, "%dx%dx%d", (int)devmode.dmPelsWidth, (int)devmode.dmPelsHeight, (int)devmode.dmBitsPerPel); // if the width is more than twice the height, reduce it by half because this // is probably a dual-screen monitor if (!COM_CheckParm("-noadjustaspect")) { if (fmodelist[num_fmodes].width > (fmodelist[num_fmodes].height << 1)) { fmodelist[num_fmodes].width >>= 1; fmodelist[num_fmodes].halfscreen = 1; q_snprintf (fmodelist[num_fmodes].modedesc, MAX_DESC, "%dx%dx%d", fmodelist[num_fmodes].width, fmodelist[num_fmodes].height, fmodelist[num_fmodes].bpp); } } for (i = 0, existingmode = 0; i < num_fmodes; i++) { if ((fmodelist[num_fmodes].width == fmodelist[i].width) && (fmodelist[num_fmodes].height == fmodelist[i].height) && (fmodelist[num_fmodes].bpp == fmodelist[i].bpp)) { existingmode = 1; break; } } if (!existingmode) { num_fmodes++; } } } modenum++; } while (status); // see if there are any low-res modes that aren't being reported bpp = 16; done = 0; do { for (j = 0; (j < NUM_LOWRESMODES) && (num_fmodes < MAX_MODE_LIST); j++) { devmode.dmBitsPerPel = bpp; devmode.dmPelsWidth = std_modes[j].width; devmode.dmPelsHeight = std_modes[j].height; devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL) { fmodelist[num_fmodes].type = MS_FULLDIB; fmodelist[num_fmodes].width = devmode.dmPelsWidth; fmodelist[num_fmodes].height = devmode.dmPelsHeight; fmodelist[num_fmodes].modenum = 0; fmodelist[num_fmodes].halfscreen = 0; fmodelist[num_fmodes].dib = 1; fmodelist[num_fmodes].fullscreen = 1; fmodelist[num_fmodes].bpp = devmode.dmBitsPerPel; q_snprintf (fmodelist[num_fmodes].modedesc, MAX_DESC, "%dx%dx%d", (int)devmode.dmPelsWidth, (int)devmode.dmPelsHeight, (int)devmode.dmBitsPerPel); for (i = 0, existingmode = 0; i < num_fmodes; i++) { if ((fmodelist[num_fmodes].width == fmodelist[i].width) && (fmodelist[num_fmodes].height == fmodelist[i].height) && (fmodelist[num_fmodes].bpp == fmodelist[i].bpp)) { existingmode = 1; break; } } if (!existingmode) { num_fmodes++; } } } switch (bpp) { case 16: bpp = 32; break; case 32: #if 0 /* O.S: don't mess with silly 24 bit lowres modes, they may not work correctly */ bpp = 24; break; case 24: #endif done = 1; break; } } while (!done); if (num_fmodes == 0) Con_SafePrintf ("No fullscreen DIB modes found\n"); } /* ================= VID_ChangeVideoMode intended only as a callback for VID_Restart_f ================= */ static void VID_ChangeVideoMode (int newmode) { int temp, temp2; // Avoid window updates and alt+tab handling (which sets modes back) temp = scr_disabled_for_loading; temp2 = vid_canalttab; scr_disabled_for_loading = true; vid_canalttab = false; // restore gamma if (maindc && gammaworks && SetDeviceGammaRamp_f) SetDeviceGammaRamp_f(maindc, orig_ramps); CDAudio_Pause (); BGM_Pause (); S_ClearBuffer (); // Unload all textures and reset texture counts D_ClearOpenGLTextures(0); memset (lightmap_textures, 0, sizeof(lightmap_textures)); // reset all opengl function pointers GL_ResetFunctions(); // Avoid re-registering commands and re-allocating memory draw_reinit = true; // temporarily disable input devices IN_DeactivateMouse(); // Kill device and rendering contexts wglMakeCurrent_fp(NULL, NULL); if (baseRC) wglDeleteContext_fp(baseRC); baseRC = NULL; if (maindc && mainwindow) ReleaseDC(mainwindow, maindc); maindc = NULL; // Destroy main window and unregister its class if (mainwindow) { ShowWindow(mainwindow, SW_HIDE); DestroyWindow(mainwindow); } if (classregistered) UnregisterClass(WM_CLASSNAME, global_hInstance); mainwindow = NULL; classregistered = false; #if 0 /* some setups (Vista) don't like this: */ /* the dll already loaded at program init is still valid, we aren't changing the opengl library, so no problems... */ #ifdef GL_DLSYM // reload the opengl library GL_CloseLibrary(); Sleep (100); if (!GL_OpenLibrary(gl_library)) Sys_Error ("Unable to load GL library %s", gl_library); #endif #endif /* #if 0 */ // Register main window class and create main window VID_RegisterWndClass(global_hInstance); VID_SetMode(newmode, host_basepal); // Obtain device context and set up pixel format maindc = GetDC(mainwindow); bSetupPixelFormat(maindc); // Create OpenGL rendering context and make it current baseRC = wglCreateContext_fp(maindc); if (!baseRC) Sys_Error("wglCreateContext failed"); if (!wglMakeCurrent_fp(maindc, baseRC )) Sys_Error("wglMakeCurrent failed"); // Reload graphics wad file (Draw_PicFromWad writes glpic_t data (sizes, // texnums) right on top of the original pic data, so the pic data will // be dirty after gl textures are loaded the first time; we need to load // a clean version) W_LoadWadFile ("gfx.wad"); // Initialize extensions and default OpenGL parameters GL_Init(); VID_Init8bitPalette(); // Reload pre-map pics, fonts, console, etc Draw_Init(); SCR_Init(); // R_Init() stuff: R_InitParticleTexture(); R_InitExtraTextures (); #if defined(H2W) R_InitNetgraphTexture(); #endif /* H2W */ Sbar_Init(); vid.recalc_refdef = 1; IN_ReInit (); CDAudio_Resume (); BGM_Resume (); // Reload model textures and player skins Mod_ReloadTextures(); // rebuild the lightmaps GL_BuildLightmaps(); // finished reloading all images draw_reinit = false; scr_disabled_for_loading = temp; // apply our gamma VID_ShiftPalette(NULL); vid_canalttab = temp2; } static void VID_Restart_f (void) { if (vid_mode.integer < 0 || vid_mode.integer >= *nummodes) { Con_Printf ("Bad video mode %d\n", vid_mode.integer); Cvar_SetValueQuick (&vid_mode, vid_modenum); return; } Con_Printf ("Re-initializing video:\n"); VID_ChangeVideoMode (vid_mode.integer); } static int sort_modes (const void *arg1, const void *arg2) { const vmode_t *a1, *a2; a1 = (vmode_t *) arg1; a2 = (vmode_t *) arg2; /* low to high bpp ? */ if (a1->bpp != a2->bpp) return a1->bpp - a2->bpp; /* lowres to highres */ if (a1->width == a2->width) return a1->height - a2->height; return a1->width - a2->width; } static void VID_SortModes (void) { int i, j; if (num_fmodes == 0) return; // sort the fullscreen modes list if (num_fmodes > 1) qsort(fmodelist, num_fmodes, sizeof fmodelist[0], sort_modes); // find which bpp values are reported to us for (i = 0; i < MAX_NUMBPP; i++) { bpplist[i][0] = 0; bpplist[i][1] = 0; } bpplist[0][0] = fmodelist[0].bpp; bpplist[0][1] = 0; for (i = 1, j = 0; i < num_fmodes && j < MAX_NUMBPP; i++) { if (fmodelist[i-1].bpp != fmodelist[i].bpp) { bpplist[++j][0] = fmodelist[i].bpp; bpplist[j][1] = i; } } vid_deskmode = -1; // find the desktop mode number. shouldn't fail! for (i = 1; i < num_fmodes; i++) { if ((fmodelist[i].width == vid_deskwidth) && (fmodelist[i].height == vid_deskheight) && (fmodelist[i].bpp == vid_deskbpp)) { vid_deskmode = i; break; } } if (vid_deskmode < 0) Con_SafePrintf ("WARNING: desktop resolution not found in modelist\n"); } /* =================== VID_Init =================== */ void VID_Init (const unsigned char *palette) { static char fxmesa_env_multitex[32] = "FX_DONT_FAKE_MULTITEX=1"; static char fxglide_env_nosplash[32] = "FX_GLIDE_NO_SPLASH=1"; int i, j, existingmode; int width, height, bpp, findbpp, done; HDC hdc; const char *read_vars[] = { "vid_config_fscr", "vid_config_gl8bit", "vid_config_bpp", "vid_config_glx", "vid_config_gly", "vid_config_consize", "gl_texture_NPOT", "gl_multitexture", "gl_lightmapfmt" }; #define num_readvars ( sizeof(read_vars)/sizeof(read_vars[0]) ) Cvar_RegisterVariable (&vid_config_gl8bit); Cvar_RegisterVariable (&vid_config_fscr); Cvar_RegisterVariable (&vid_config_bpp); Cvar_RegisterVariable (&vid_config_gly); Cvar_RegisterVariable (&vid_config_glx); Cvar_RegisterVariable (&vid_config_consize); Cvar_RegisterVariable (&vid_mode); Cvar_RegisterVariable (&_enable_mouse); Cvar_RegisterVariable (&gl_texture_NPOT); Cvar_RegisterVariable (&gl_lightmapfmt); Cvar_RegisterVariable (&gl_multitexture); // these are for compatibility with the software version Cvar_RegisterVariable (&vid_wait); Cvar_RegisterVariable (&_vid_default_mode); Cvar_RegisterVariable (&_vid_default_mode_win); Cvar_RegisterVariable (&vid_config_x); Cvar_RegisterVariable (&vid_config_y); Cvar_RegisterVariable (&vid_stretch_by_2); Cvar_RegisterVariable (&vid_maxpages); Cvar_RegisterVariable (&vid_nopageflip); Cmd_AddCommand ("vid_nummodes", VID_NumModes_f); Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f); Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f); Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f); Cmd_AddCommand ("vid_restart", VID_Restart_f); VID_InitPalette (palette); // don't let fxMesa cheat multitexturing _putenv (fxmesa_env_multitex); // disable the 3dfx splash screen. _putenv (fxglide_env_nosplash); #ifdef GL_DLSYM i = COM_CheckParm ("-gllibrary"); if (i == 0) i = COM_CheckParm ("-g"); if (i && i < com_argc - 1) gl_library = com_argv[i+1]; else gl_library = "opengl32.dll"; if (!GL_OpenLibrary(gl_library)) Sys_Error ("Unable to load GL library %s", gl_library); #else hInstGL = GetModuleHandle("opengl32.dll"); #endif hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON2)); VID_InitDIB (global_hInstance); VID_InitFullDIB (global_hInstance); Con_SafePrintf ("Desktop settings: %d x %d x %d\n", vid_deskwidth, vid_deskheight, vid_deskbpp); // sort the modes VID_SortModes(); // make sure our vid_config_bpp default is supported by the OS // (we have a findbpp code below, but let's be on the safe side..) for (i = 0; i < MAX_NUMBPP; i++) { if (!bpplist[i][0]) break; if (vid_config_bpp.integer == bpplist[i][0]) break; // OK. } if (i == MAX_NUMBPP || vid_config_bpp.integer != bpplist[i][0]) // not OK Cvar_SetValueQuick(&vid_config_bpp, bpplist[0][0]); // perform an early read of config.cfg CFG_ReadCvars (read_vars, num_readvars); width = vid_config_glx.integer; height = vid_config_gly.integer; if (COM_CheckParm("-window") || COM_CheckParm("-w")) { Cvar_SetQuick (&vid_config_fscr, "0"); } else if (COM_CheckParm("-fullscreen") || COM_CheckParm("-f")) { Cvar_SetQuick (&vid_config_fscr, "1"); } if (vid_config_consize.integer != width) vid_conscale = true; if (!vid_config_fscr.integer) { hdc = GetDC (NULL); if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) { Sys_Error ("Can't run in non-RGB mode"); } ReleaseDC (NULL, hdc); modelist = wmodelist; nummodes = &num_wmodes; vid_default = RES_640X480; // start parsing any dimension request from user i = COM_CheckParm("-width"); if (i && i < com_argc-1) { j = atoi(com_argv[i+1]); // don't allow requests larger than desktop if (j <= vid_deskwidth && j >= 320) { width = j; height = width * 3 / 4; i = COM_CheckParm("-height"); if (i && i < com_argc-1) { j = atoi(com_argv[i+1]); if (j <= vid_deskheight && j >= 240) { height= j; } } } } // end of parsing user commandline wmodelist[num_wmodes].width = width; wmodelist[num_wmodes].height = height; wmodelist[num_wmodes].type = MS_WINDOWED; q_snprintf (wmodelist[num_wmodes].modedesc, MAX_DESC, "%dx%d", wmodelist[num_wmodes].width, wmodelist[num_wmodes].height); wmodelist[num_wmodes].modenum = MODE_WINDOWED; wmodelist[num_wmodes].dib = 1; wmodelist[num_wmodes].fullscreen = 0; wmodelist[num_wmodes].halfscreen = 0; wmodelist[num_wmodes].bpp = 0; for (i = 0, existingmode = 0; i < num_wmodes; i++) { if ((wmodelist[num_wmodes].width == wmodelist[i].width) && (wmodelist[num_wmodes].height == wmodelist[i].height)) { existingmode = 1; vid_default = i; break; } } if (!existingmode) { q_strlcat (wmodelist[num_wmodes].modedesc, " (user mode)", MAX_DESC); vid_default = num_wmodes; num_wmodes++; } } else // fullscreen, default { if (num_fmodes == 0) { Sys_Error ("No RGB fullscreen modes available"); } modelist = fmodelist; nummodes = &num_fmodes; vid_default = -1; findbpp = 1; bpp = vid_config_bpp.integer; if (Win95old) { // don't bother with multiple bpp values on // windows versions older than win95-osr2. // in fact, should we stop supporting it? bpp = vid_deskbpp; findbpp = 0; } if (COM_CheckParm("-current")) { // user wants fullscreen and // with desktop dimensions if (vid_deskmode >= 0) { q_strlcat (modelist[vid_deskmode].modedesc, " (desktop)", MAX_DESC); vid_default = vid_deskmode; } else { Con_SafePrintf ("WARNING: desktop mode not available for the -current switch\n"); } } else { // start parsing any dimension/bpp request from user i = COM_CheckParm("-width"); if (i && i < com_argc-1) { width = atoi(com_argv[i+1]); if (width < 320) width = 320; height = 3 * width / 4; i = COM_CheckParm("-height"); if (i && i < com_argc-1) { height = atoi(com_argv[i+1]); if (height < 240) height = 240; } } i = COM_CheckParm("-bpp"); if (i && i < com_argc-1 && !Win95old) { bpp = atoi(com_argv[i+1]); findbpp = 0; } // if they want to force it, add the specified mode to the list if (COM_CheckParm("-force")) { fmodelist[num_fmodes].type = MS_FULLDIB; fmodelist[num_fmodes].width = width; fmodelist[num_fmodes].height = height; fmodelist[num_fmodes].modenum = 0; fmodelist[num_fmodes].halfscreen = 0; fmodelist[num_fmodes].dib = 1; fmodelist[num_fmodes].fullscreen = 1; fmodelist[num_fmodes].bpp = bpp; q_snprintf (fmodelist[num_fmodes].modedesc, MAX_DESC, "%dx%dx%d", fmodelist[num_fmodes].width, fmodelist[num_fmodes].height, fmodelist[num_fmodes].bpp); for (i = 0, existingmode = 0; i < num_fmodes; i++) { if ((fmodelist[num_fmodes].width == fmodelist[i].width) && (fmodelist[num_fmodes].height == fmodelist[i].height) && (fmodelist[num_fmodes].bpp == modelist[i].bpp)) { existingmode = 1; break; } } if (!existingmode) { q_strlcat (fmodelist[num_fmodes].modedesc, " (user mode)", MAX_DESC); num_fmodes++; // re-sort the modes VID_SortModes(); if (findbpp) bpp = bpplist[0][0]; } } if (vid_deskmode >= 0) q_strlcat (fmodelist[vid_deskmode].modedesc, " (desktop)", MAX_DESC); j = done = 0; do { for (i = 0; i < *nummodes; i++) { if ((modelist[i].width == width) && (modelist[i].height == height) && (modelist[i].bpp == bpp)) { vid_default = i; done = 1; break; } } if (!done) { if (findbpp) { if (bpp == bpplist[j][0]) j++; if (j >= MAX_NUMBPP || !bpplist[j][0]) done = 1; if (!done) bpp = bpplist[j][0]; j++; } else { done = 1; } } } while (!done); if (vid_default < 0) { Sys_Error ("Specified video mode not available:\n\n" "If you used -width,-height or -bpp commandline arguments,\n" "try changing their values. If your config.cfg has bad or stale\n" "video options, try changing them, or delete your config.cfg\n" "altogether and try again."); } } } // end of fullscreen parsing if (!vid_conscale) Cvar_SetValueQuick (&vid_config_consize, width); // This will display a bigger hud and readable fonts at high // resolutions. The fonts will be somewhat distorted, though i = COM_CheckParm("-conwidth"); if (i != 0 && i < com_argc-1) i = atoi(com_argv[i + 1]); else i = vid_config_consize.integer; if (i < MIN_WIDTH) i = MIN_WIDTH; else if (i > width) i = width; Cvar_SetValueQuick(&vid_config_consize, i); if (vid_config_consize.integer != width) vid_conscale = true; vid_initialized = true; vid.maxwarpwidth = WARP_WIDTH; vid.maxwarpheight = WARP_HEIGHT; vid.colormap = host_colormap; vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); #if !defined(NO_SPLASHES) DestroyWindow (hwnd_dialog); #endif if (COM_CheckParm("-paltex")) Cvar_SetQuick (&vid_config_gl8bit, "1"); j = scr_disabled_for_loading; scr_disabled_for_loading = true; Cvar_SetValueQuick (&vid_mode, vid_default); VID_SetMode (vid_default, palette); maindc = GetDC(mainwindow); bSetupPixelFormat(maindc); baseRC = wglCreateContext_fp( maindc ); if (!baseRC) Sys_Error ("Could not initialize GL (wglCreateContext failed).\n\nMake sure you in are 65535 color mode, and try running -window."); if (!wglMakeCurrent_fp( maindc, baseRC )) Sys_Error ("wglMakeCurrent failed"); GL_SetupLightmapFmt(); GL_Init (); VID_Init8bitPalette(); // lock the early-read cvars until Host_Init is finished for (i = 0; i < (int)num_readvars; i++) Cvar_LockVar (read_vars[i]); scr_disabled_for_loading = j; vid.recalc_refdef = 1; vid_menudrawfn = VID_MenuDraw; vid_menukeyfn = VID_MenuKey; q_strlcpy (badmode.modedesc,"Bad mode", MAX_DESC); vid_canalttab = true; // if (COM_CheckParm("-fullsbar")) // fullsbardraw = true; } void VID_Shutdown (void) { HGLRC hRC; HDC hDC; if (vid_initialized) { vid_canalttab = false; hRC = wglGetCurrentContext_fp(); hDC = wglGetCurrentDC_fp(); if (maindc && gammaworks && SetDeviceGammaRamp_f) SetDeviceGammaRamp_f(maindc, orig_ramps); wglMakeCurrent_fp(NULL, NULL); if (hRC) wglDeleteContext_fp(hRC); if (hDC && mainwindow) ReleaseDC(mainwindow, hDC); if (modestate == MS_FULLDIB) ChangeDisplaySettings (NULL, 0); if (maindc && mainwindow) ReleaseDC (mainwindow, maindc); maindc = NULL; AppActivate(false, false); if (mainwindow) { ShowWindow(mainwindow, SW_HIDE); DestroyWindow(mainwindow); } if (classregistered) UnregisterClass(WM_CLASSNAME, global_hInstance); mainwindow = NULL; classregistered = false; #ifdef GL_DLSYM GL_CloseLibrary(); #endif } } void VID_ToggleFullscreen (void) { } #ifndef H2W /* unused in hexenworld */ void D_ShowLoadingSize (void) { #if defined(DRAW_PROGRESSBARS) int cur_perc; static int prev_perc; if (!vid_initialized) return; cur_perc = loading_stage * 100; if (total_loading_size) cur_perc += current_loading_size * 100 / total_loading_size; if (cur_perc == prev_perc) return; prev_perc = cur_perc; glDrawBuffer_fp (GL_FRONT); SCR_DrawLoading(); glFlush_fp(); glDrawBuffer_fp (GL_BACK); #endif /* DRAW_PROGRESSBARS */ } #endif //======================================================== // Video menu stuff //======================================================== static int vid_menunum; static int vid_cursor; static vmode_t *vid_menulist; // this changes when vid_menu_fs changes static int vid_menubpp; // if this changes, vid_menunum already changes static qboolean vid_menu_fs; static qboolean want_fstoggle, need_apply; static qboolean vid_menu_firsttime = true; enum { VID_FULLSCREEN, VID_RESOLUTION, VID_BPP, VID_MULTITEXTURE, VID_NPOT, VID_PALTEX, VID_BLANKLINE, // spacer line VID_RESET, VID_APPLY, VID_ITEMS }; static void M_DrawYesNo (int x, int y, int on, int white) { if (on) { if (white) M_PrintWhite (x, y, "yes"); else M_Print (x, y, "yes"); } else { if (white) M_PrintWhite (x, y, "no"); else M_Print (x, y, "no"); } } /* ================ VID_MenuDraw ================ */ static void VID_MenuDraw (void) { ScrollTitle("gfx/menu/title7.lmp"); if (vid_menu_firsttime) { // settings for entering the menu first time vid_menunum = vid_modenum; vid_menubpp = modelist[vid_modenum].bpp; vid_menu_fs = (modestate != MS_WINDOWED); vid_menulist = (modestate == MS_WINDOWED) ? wmodelist : fmodelist; vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; vid_menu_firsttime = false; } want_fstoggle = ( ((modestate == MS_WINDOWED) && vid_menu_fs) || ((modestate != MS_WINDOWED) && !vid_menu_fs) ); need_apply = (vid_menunum != vid_modenum) || want_fstoggle || (have_mtex && (gl_mtexable != !!gl_multitexture.integer)) || (have_NPOT && (gl_tex_NPOT != !!gl_texture_NPOT.integer)) || (have8bit && (is8bit != !!vid_config_gl8bit.integer)); M_Print (76, 92 + 8*VID_FULLSCREEN, "Fullscreen: "); M_DrawYesNo (76+12*8, 92 + 8*VID_FULLSCREEN, vid_menu_fs, !want_fstoggle); M_Print (76, 92 + 8*VID_RESOLUTION, "Resolution: "); if (vid_menunum == vid_modenum) M_PrintWhite (76+12*8, 92 + 8*VID_RESOLUTION, vid_menulist[vid_menunum].modedesc); else M_Print (76+12*8, 92 + 8*VID_RESOLUTION, vid_menulist[vid_menunum].modedesc); if (vid_menu_fs && num_fmodes && !Win95old) { M_Print (76, 92 + 8*VID_BPP, "Color BPP : "); if (vid_menubpp == modelist[vid_modenum].bpp) M_PrintWhite (76+12*8, 92 + 8*VID_BPP, va("%d",vid_menubpp)); else M_Print (76+12*8, 92 + 8*VID_BPP, va("%d",vid_menubpp)); } M_Print (76, 92 + 8*VID_MULTITEXTURE, "Multitexturing:"); if (have_mtex) M_DrawYesNo (76+16*8, 92 + 8*VID_MULTITEXTURE, gl_multitexture.integer, (gl_mtexable == !!gl_multitexture.integer)); else M_PrintWhite (76+16*8, 92 + 8*VID_MULTITEXTURE, "Not found"); M_Print (76, 92 + 8*VID_NPOT, "NPOT textures :"); if (have_NPOT) M_DrawYesNo (76+16*8, 92 + 8*VID_NPOT, gl_texture_NPOT.integer, (gl_tex_NPOT == !!gl_texture_NPOT.integer)); else M_PrintWhite (76+16*8, 92 + 8*VID_NPOT, "Not found"); M_Print (76, 92 + 8*VID_PALTEX, "8 bit textures:"); if (have8bit) M_DrawYesNo (76+16*8, 92 + 8*VID_PALTEX, vid_config_gl8bit.integer, (is8bit == !!vid_config_gl8bit.integer)); else M_PrintWhite (76+16*8, 92 + 8*VID_PALTEX, "Not found"); if (need_apply) { M_Print (76, 92 + 8*VID_RESET, "RESET CHANGES"); M_Print (76, 92 + 8*VID_APPLY, "APPLY CHANGES"); } M_DrawCharacter (64, 92 + vid_cursor*8, 12+((int)(realtime*4)&1)); } static int find_bppnum (int incr) { int j, pos = -1; if (!vid_menu_fs) // then it doesn't matter return 0; for (j = 0; j < MAX_NUMBPP; j++) { // find the pos in the bpplist if (vid_menubpp == bpplist[j][0]) { pos = j; j = j+incr; break; } } if (pos < 0) Sys_Error ("bpp unexpectedly not found in list"); if (incr == 0) return pos; // find the next available bpp while (1) { if (j >= MAX_NUMBPP) j = 0; if (j < 0) j = MAX_NUMBPP-1; if (bpplist[j][0]) break; j = j + incr; } return j; } static int match_mode_to_bpp (int bppnum) { int k, l; k = bpplist[bppnum][1]; l = bpplist[bppnum][1] + 1; for ( ; l < num_fmodes && fmodelist[l].bpp == vid_menubpp; l++) { if (fmodelist[vid_menunum].width == fmodelist[l].width && fmodelist[vid_menunum].height == fmodelist[l].height) { k = l; break; } } return k; } static int match_windowed_fullscr_modes (void) { int l; vmode_t *tmplist; int *tmpcount; // choose the new mode tmplist = (vid_menu_fs) ? fmodelist : wmodelist; tmpcount = (vid_menu_fs) ? &num_fmodes : &num_wmodes; for (l = 0; l < *tmpcount; l++) { if (tmplist[l].width == vid_menulist[vid_menunum].width && tmplist[l].height == vid_menulist[vid_menunum].height) { return l; } } return 0; } /* ================ VID_MenuKey ================ */ static void VID_MenuKey (int key) { int i; int *tmpnum; switch (key) { case K_ESCAPE: vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; M_Menu_Options_f (); break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); vid_cursor--; if (vid_cursor < 0) { vid_cursor = (need_apply) ? VID_ITEMS-1 : VID_BLANKLINE-1; } else if (vid_cursor == VID_BLANKLINE) { vid_cursor--; } if (!num_fmodes && vid_cursor == 0) { vid_cursor = VID_RESOLUTION; } if ( vid_cursor == VID_BPP && (!vid_menu_fs || !num_fmodes || Win95old )) { vid_cursor--; } break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); vid_cursor++; if (vid_cursor >= VID_ITEMS) { vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; break; } if ( vid_cursor == VID_BPP && (!vid_menu_fs || !num_fmodes || Win95old )) vid_cursor++; if (vid_cursor >= VID_BLANKLINE) { if (need_apply) { if (vid_cursor == VID_BLANKLINE) vid_cursor++; } else { vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; } } break; case K_ENTER: switch (vid_cursor) { case VID_RESET: vid_menunum = vid_modenum; vid_menubpp = modelist[vid_modenum].bpp; vid_menu_fs = (modestate != MS_WINDOWED); vid_menulist = (modestate == MS_WINDOWED) ? wmodelist : fmodelist; Cvar_SetValueQuick (&vid_config_gl8bit, is8bit); vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; break; case VID_APPLY: if (need_apply) { Cvar_SetValueQuick(&vid_mode, vid_menunum); modelist = (vid_menu_fs) ? fmodelist : wmodelist; nummodes = (vid_menu_fs) ? &num_fmodes : &num_wmodes; VID_Restart_f(); } vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; break; } return; case K_LEFTARROW: switch (vid_cursor) { case VID_FULLSCREEN: if (!num_fmodes) break; vid_menu_fs = !vid_menu_fs; vid_menunum = match_windowed_fullscr_modes(); vid_menulist = (vid_menu_fs) ? fmodelist : wmodelist; vid_menubpp = vid_menulist[vid_menunum].bpp; break; case VID_RESOLUTION: S_LocalSound ("raven/menu1.wav"); vid_menunum--; if (vid_menunum < 0 || vid_menubpp != vid_menulist[vid_menunum].bpp) vid_menunum++; break; case VID_BPP: i = find_bppnum (-1); if (vid_menubpp == bpplist[i][0]) break; vid_menubpp = bpplist[i][0]; //find a matching video mode for this bpp vid_menunum = match_mode_to_bpp(i); break; case VID_MULTITEXTURE: if (have_mtex) Cvar_SetQuick (&gl_multitexture, gl_multitexture.integer ? "0" : "1"); break; case VID_NPOT: if (have_NPOT) Cvar_SetQuick (&gl_texture_NPOT, gl_texture_NPOT.integer ? "0" : "1"); break; case VID_PALTEX: if (have8bit) Cvar_SetQuick (&vid_config_gl8bit, vid_config_gl8bit.integer ? "0" : "1"); break; } return; case K_RIGHTARROW: switch (vid_cursor) { case VID_FULLSCREEN: if (!num_fmodes) break; vid_menu_fs = !vid_menu_fs; vid_menunum = match_windowed_fullscr_modes(); vid_menulist = (vid_menu_fs) ? fmodelist : wmodelist; vid_menubpp = vid_menulist[vid_menunum].bpp; break; case VID_RESOLUTION: S_LocalSound ("raven/menu1.wav"); tmpnum = (vid_menu_fs) ? &num_fmodes : &num_wmodes; vid_menunum++; if (vid_menunum >= *tmpnum || vid_menubpp != vid_menulist[vid_menunum].bpp) vid_menunum--; break; case VID_BPP: i = find_bppnum (1); if (vid_menubpp == bpplist[i][0]) break; vid_menubpp = bpplist[i][0]; //find a matching video mode for this bpp vid_menunum = match_mode_to_bpp(i); break; case VID_MULTITEXTURE: if (have_mtex) Cvar_SetQuick (&gl_multitexture, gl_multitexture.integer ? "0" : "1"); break; case VID_NPOT: if (have_NPOT) Cvar_SetQuick (&gl_texture_NPOT, gl_texture_NPOT.integer ? "0" : "1"); break; case VID_PALTEX: if (have8bit) Cvar_SetQuick (&vid_config_gl8bit, vid_config_gl8bit.integer ? "0" : "1"); break; } return; default: break; } } engine/h2shared/gl_vidsdl.c000066400000000000000000001551371444734033100161550ustar00rootroot00000000000000/* * gl_vidsdl.c -- SDL GL vid component * Select window size and mode and init SDL in GL mode. * * Changed 7/11/04 by S.A. * - Fixed fullscreen opengl mode, window sizes * - Options are now: -fullscreen, -height, -width, -bpp * - The "-mode" option has been removed * * Changed 7/01/06 by O.S * - Added video modes enumeration via SDL * - Added video mode changing on the fly. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2004-2005 Steven Atkinson * Copyright (C) 2005-2016 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __GL_FUNC_EXTERN #include "quakedef.h" #include "cfgfile.h" #include "bgmusic.h" #include "cdaudio.h" #include "sdl_inc.h" #include "filenames.h" #define WARP_WIDTH 320 #define WARP_HEIGHT 200 #define MAXWIDTH 10000 #define MAXHEIGHT 10000 #define MIN_WIDTH 320 //#define MIN_HEIGHT 200 #define MIN_HEIGHT 240 #define MAX_DESC 33 typedef struct { modestate_t type; int width; int height; int modenum; int fullscreen; int bpp; int halfscreen; char modedesc[MAX_DESC]; } vmode_t; typedef struct { int width; int height; } stdmode_t; #define RES_640X480 3 static const stdmode_t std_modes[] = { // NOTE: keep this list in order {320, 240}, // 0 {400, 300}, // 1 {512, 384}, // 2 {640, 480}, // 3 == RES_640X480, this is our default, below // this is the lowresmodes region. // either do not change its order, // or change the above define, too {800, 600}, // 4, RES_640X480 + 1 {1024, 768}, // 5, RES_640X480 + 2 {1280, 1024}, // 6 {1600, 1200} // 7 }; #define MAX_MODE_LIST 128 #define MAX_STDMODES (sizeof(std_modes) / sizeof(std_modes[0])) #define NUM_LOWRESMODES (RES_640X480) static vmode_t fmodelist[MAX_MODE_LIST+1]; // list of enumerated fullscreen modes static vmode_t wmodelist[MAX_STDMODES +1]; // list of standart 4:3 windowed modes static vmode_t *modelist; // modelist in use, points to one of the above lists static int num_fmodes; static int num_wmodes; static int *nummodes; static int bpp = 16; #if defined(H2W) # define WM_TITLEBAR_TEXT "HexenWorld" # define WM_ICON_TEXT "HexenWorld" //#elif defined(H2MP) //# define WM_TITLEBAR_TEXT "Hexen II+" //# define WM_ICON_TEXT "HEXEN2MP" #else # define WM_TITLEBAR_TEXT "Hexen II" # define WM_ICON_TEXT "HEXEN2" #endif typedef struct { int red, green, blue, alpha, depth, stencil; } attributes_t; static attributes_t vid_attribs; static const SDL_VideoInfo *vid_info; static SDL_Surface *screen; static qboolean vid_menu_fs; static qboolean fs_toggle_works = true; // vars for vid state viddef_t vid; // global video state modestate_t modestate = MS_UNINIT; static int vid_default = -1; // modenum of 640x480 as a safe default static int vid_modenum = -1; // current video mode, set after mode setting succeeds static int vid_maxwidth = 640, vid_maxheight = 480; static qboolean vid_conscale = false; static int WRHeight, WRWidth; static qboolean vid_initialized = false; qboolean in_mode_set = false; // cvar vid_mode must be set before calling // VID_SetMode, VID_ChangeVideoMode or VID_Restart_f static cvar_t vid_mode = {"vid_mode", "0", CVAR_NONE}; static cvar_t vid_config_consize = {"vid_config_consize", "640", CVAR_ARCHIVE}; static cvar_t vid_config_glx = {"vid_config_glx", "640", CVAR_ARCHIVE}; static cvar_t vid_config_gly = {"vid_config_gly", "480", CVAR_ARCHIVE}; static cvar_t vid_config_fscr= {"vid_config_fscr", "0", CVAR_ARCHIVE}; // cvars for compatibility with the software version static cvar_t vid_config_swx = {"vid_config_swx", "320", CVAR_ARCHIVE}; static cvar_t vid_config_swy = {"vid_config_swy", "240", CVAR_ARCHIVE}; byte globalcolormap[VID_GRADES*256]; float RTint[256], GTint[256], BTint[256]; unsigned short d_8to16table[256]; unsigned int d_8to24table[256]; unsigned int d_8to24TranslucentTable[256]; unsigned char *inverse_pal; // gl stuff static void GL_Init (void); #ifdef GL_DLSYM static const char *gl_library; #endif static const char *gl_vendor; static const char *gl_renderer; static const char *gl_version; static const char *gl_extensions; qboolean is_3dfx = false; GLint gl_max_size = 256; static qboolean have_NPOT = false; qboolean gl_tex_NPOT = false; static cvar_t gl_texture_NPOT = {"gl_texture_NPOT", "0", CVAR_ARCHIVE}; GLfloat gl_max_anisotropy; float gldepthmin, gldepthmax; /* palettized textures */ static qboolean have8bit = false; qboolean is8bit = false; static cvar_t vid_config_gl8bit = {"vid_config_gl8bit", "0", CVAR_ARCHIVE}; /* Gamma stuff */ #define USE_GAMMA_RAMPS 0 /* 3dfx gamma hacks: see fx_gamma.c */ #define USE_3DFX_RAMPS 0 #if defined(USE_3DFXGAMMA) #include "fx_gamma.h" #endif #if (USE_GAMMA_RAMPS) || (defined(USE_3DFXGAMMA) && (USE_3DFX_RAMPS)) static unsigned short orig_ramps[3][256]; #endif static qboolean fx_gamma = false; // 3dfx-specific gamma control static qboolean gammaworks = false; // whether hw-gamma works // multitexturing qboolean gl_mtexable = false; static GLint num_tmus = 1; static qboolean have_mtex = false; static cvar_t gl_multitexture = {"gl_multitexture", "0", CVAR_ARCHIVE}; // multisampling static int multisample = 0; // do not set this if SDL cannot multisample static qboolean sdl_has_multisample = false; static cvar_t vid_config_fsaa = {"vid_config_fsaa", "0", CVAR_ARCHIVE}; // stencil buffer qboolean have_stencil = false; // this is useless: things aren't like those in quake //static qboolean fullsbardraw = false; // menu drawing static void VID_MenuDraw (void); static void VID_MenuKey (int key); // input stuff static void ClearAllStates (void); static int enable_mouse; cvar_t _enable_mouse = {"_enable_mouse", "1", CVAR_ARCHIVE}; //==================================== static qboolean GL_ParseExtensionList (const char *list, const char *name) { const char *start; const char *where, *terminator; if (!list || !name || !*name) return false; if (strchr(name, ' ') != NULL) return false; // extension names must not have spaces start = list; while (1) { where = strstr (start, name); if (!where) break; terminator = where + strlen (name); if (where == start || where[-1] == ' ') if (*terminator == ' ' || *terminator == '\0') return true; start = terminator; } return false; } //==================================== void VID_LockBuffer (void) { // nothing to do } void VID_UnlockBuffer (void) { // nothing to do } void VID_HandlePause (qboolean paused) { if (_enable_mouse.integer/* && (modestate == MS_WINDOWED)*/) { // for consistency, don't show pointer - S.A if (paused) { IN_DeactivateMouse (); // IN_ShowMouse (); } else { IN_ActivateMouse (); // IN_HideMouse (); } } } qboolean VID_HasMouseOrInputFocus (void) { return (SDL_GetAppState() & (SDL_APPMOUSEFOCUS | SDL_APPINPUTFOCUS)) != 0; } qboolean VID_IsMinimized (void) { return !(SDL_GetAppState() & SDL_APPACTIVE); } //==================================== static void VID_SetIcon (void) { /* from Kristian Duske: "AFAIK, the application icon must be present in * Contents/Resources and it must be set in the Info.plist file. It will * then be used by Finder and Dock as well as for individual windows * unless overridden by a document icon. So SDL_WM_SetIcon() is probably * not necessary, and will likely use a low-res image anyway." */ #if !defined(PLATFORM_OSX) # include "xbm_icon.h" /* the xbm data */ SDL_Surface *icon; SDL_Color color; Uint8 *ptr; int i, mask; icon = SDL_CreateRGBSurface(SDL_SWSURFACE, hx2icon_width, hx2icon_height, 8, 0, 0, 0, 0); if (icon == NULL) return; SDL_SetColorKey(icon, SDL_SRCCOLORKEY, 0); color.r = 255; color.g = 255; color.b = 255; SDL_SetColors(icon, &color, 0, 1); /* just in case */ color.r = 192; color.g = 0; color.b = 0; SDL_SetColors(icon, &color, 1, 1); ptr = (Uint8 *)icon->pixels; /* one bit represents a pixel, black or white: each * byte in the xbm array contains data for 8 pixels. */ for (i = 0; i < (int) sizeof(hx2icon_bits); i++) { for (mask = 1; mask != 0x100; mask <<= 1) { *ptr = (hx2icon_bits[i] & mask) ? 1 : 0; ptr++; } } SDL_WM_SetIcon(icon, NULL); SDL_FreeSurface(icon); #endif /* !OSX */ } static void VID_ConWidth (int modenum) { int w, h; if (!vid_conscale) { Cvar_SetValueQuick (&vid_config_consize, modelist[modenum].width); return; } w = vid_config_consize.integer; w &= ~7; /* make it a multiple of eight */ if (w < MIN_WIDTH) w = MIN_WIDTH; else if (w > modelist[modenum].width) w = modelist[modenum].width; h = w * modelist[modenum].height / modelist[modenum].width; if (h < 200 /* MIN_HEIGHT */ || h > modelist[modenum].height || w > modelist[modenum].width) { vid_conscale = false; Cvar_SetValueQuick (&vid_config_consize, modelist[modenum].width); return; } vid.width = vid.conwidth = w; vid.height = vid.conheight = h; if (w != modelist[modenum].width) vid_conscale = true; else vid_conscale = false; } void VID_ChangeConsize (int dir) { int w, h; switch (dir) { case -1: /* smaller text */ w = ((float)vid.conwidth/(float)vid.width + 0.05f) * vid.width; /* use 0.10f increment ?? */ w &= ~7; /* make it a multiple of eight */ if (w > modelist[vid_modenum].width) w = modelist[vid_modenum].width; break; case 1: /* bigger text */ w = ((float)vid.conwidth/(float)vid.width - 0.05f) * vid.width; w &= ~7; /* make it a multiple of eight */ if (w < MIN_WIDTH) w = MIN_WIDTH; break; default: /* bad key */ return; } h = w * modelist[vid_modenum].height / modelist[vid_modenum].width; if (h < 200) return; vid.width = vid.conwidth = w; vid.height = vid.conheight = h; Cvar_SetValueQuick (&vid_config_consize, vid.conwidth); vid.recalc_refdef = 1; if (vid.conwidth != modelist[vid_modenum].width) vid_conscale = true; else vid_conscale = false; } float VID_ReportConsize(void) { return (float)modelist[vid_modenum].width/vid.conwidth; } static qboolean VID_SetMode (int modenum) { Uint32 flags; int i, is_fullscreen; in_mode_set = true; //flags = (SDL_OPENGL|SDL_NOFRAME); flags = (SDL_OPENGL); if (vid_config_fscr.integer) flags |= SDL_FULLSCREEN; // setup the attributes if (bpp >= 32) { SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 ); SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 8 ); } else { SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 ); SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 ); SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 ); SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 ); } SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); if (multisample && !sdl_has_multisample) { multisample = 0; Con_SafePrintf ("SDL ver < %d, multisampling disabled\n", SDL_VER_WITH_MULTISAMPLING); } if (multisample) { SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, multisample); } Con_SafePrintf ("Requested mode %d: %dx%dx%d\n", modenum, modelist[modenum].width, modelist[modenum].height, bpp); VID_SetIcon(); screen = SDL_SetVideoMode (modelist[modenum].width, modelist[modenum].height, bpp, flags); if (!screen) { if (!multisample) { Sys_Error ("Couldn't set video mode: %s", SDL_GetError()); } else { Con_SafePrintf ("multisample window failed\n"); multisample = 0; SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, multisample); screen = SDL_SetVideoMode (modelist[modenum].width, modelist[modenum].height, bpp, flags); if (!screen) Sys_Error ("Couldn't set video mode: %s", SDL_GetError()); } } // set vid_modenum properly and adjust other vars vid_modenum = modenum; is_fullscreen = (screen->flags & SDL_FULLSCREEN) ? 1 : 0; modestate = (is_fullscreen) ? MS_FULLDIB : MS_WINDOWED; Cvar_SetValueQuick (&vid_config_glx, modelist[vid_modenum].width); Cvar_SetValueQuick (&vid_config_gly, modelist[vid_modenum].height); Cvar_SetValueQuick (&vid_config_fscr, is_fullscreen); WRWidth = vid.width = vid.conwidth = modelist[modenum].width; WRHeight = vid.height = vid.conheight = modelist[modenum].height; // setup the effective console width VID_ConWidth(modenum); SDL_GL_GetAttribute(SDL_GL_BUFFER_SIZE, &i); Con_SafePrintf ("Video Mode Set : %dx%dx%d\n", modelist[modenum].width, modelist[modenum].height, i); if (multisample) { SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &multisample); Con_SafePrintf ("multisample buffer with %i samples\n", multisample); } Cvar_SetValueQuick (&vid_config_fsaa, multisample); SDL_WM_SetCaption(WM_TITLEBAR_TEXT, WM_ICON_TEXT); IN_HideMouse (); in_mode_set = false; return true; } //==================================== #if 0 /* No.. */ static void CheckSetGlobalPalette (void) { gl3DfxSetPaletteEXT_f gl3DfxSetPaletteEXT_fp; if (GL_ParseExtensionList(gl_extensions, "3DFX_set_global_palette")) { gl3DfxSetPaletteEXT_fp = (gl3DfxSetPaletteEXT_f) SDL_GL_GetProcAddress("gl3DfxSetPaletteEXT"); if (!gl3DfxSetPaletteEXT_fp) gl3DfxSetPaletteEXT_fp = (gl3DfxSetPaletteEXT_f) SDL_GL_GetProcAddress("3DFX_set_global_palette"); if (!gl3DfxSetPaletteEXT_fp) return; Con_SafePrintf("Found 3DFX_set_global_palette\n"); } else if (GL_ParseExtensionList(gl_extensions, "POWERVR_set_global_palette")) { gl3DfxSetPaletteEXT_fp = (gl3DfxSetPaletteEXT_f) SDL_GL_GetProcAddress("glSetGlobalPalettePOWERVR"); if (!gl3DfxSetPaletteEXT_fp) gl3DfxSetPaletteEXT_fp = (gl3DfxSetPaletteEXT_f) SDL_GL_GetProcAddress("POWERVR_set_global_palette"); if (!gl3DfxSetPaletteEXT_fp) return; Con_SafePrintf("Found POWERVR_set_global_palette\n"); } else { return; } have8bit = true; if (!vid_config_gl8bit.integer) return; else { int i; GLubyte table[256][4]; char *oldpal; is8bit = true; oldpal = (char *) d_8to24table; for (i = 0; i < 256; i++) { table[i][2] = *oldpal++; table[i][1] = *oldpal++; table[i][0] = *oldpal++; table[i][3] = 255; oldpal++; } glEnable_fp (GL_SHARED_TEXTURE_PALETTE_EXT); gl3DfxSetPaletteEXT_fp ((GLuint *)table); } } #endif /* #if 0 */ static void CheckSharedTexturePalette (void) { glColorTableEXT_f glColorTableEXT_fp; if (!GL_ParseExtensionList(gl_extensions, "GL_EXT_shared_texture_palette")) return; glColorTableEXT_fp = (glColorTableEXT_f) SDL_GL_GetProcAddress("glColorTableEXT"); if (glColorTableEXT_fp == NULL) return; have8bit = true; Con_SafePrintf("Found GL_EXT_shared_texture_palette\n"); if (!vid_config_gl8bit.integer) return; else { int i; char thePalette[256*3]; char *oldPalette, *newPalette; is8bit = true; oldPalette = (char *) d_8to24table; newPalette = thePalette; for (i = 0; i < 256; i++) { *newPalette++ = *oldPalette++; *newPalette++ = *oldPalette++; *newPalette++ = *oldPalette++; oldPalette++; } glEnable_fp (GL_SHARED_TEXTURE_PALETTE_EXT); glColorTableEXT_fp (GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, (void *) thePalette); } } static void VID_Init8bitPalette (void) { have8bit = false; is8bit = false; /* Check for 8bit Extensions and initialize them */ CheckSharedTexturePalette(); #if 0 /* No.. */ if (!have8bit) CheckSetGlobalPalette(); #endif if (is8bit) Con_SafePrintf("8-bit palettized textures enabled\n"); } #if !defined(USE_3DFXGAMMA) static inline int FUNC_UNUSED Init_3dfxGammaCtrl (void) { return 0; } static inline void FUNC_UNUSED Shutdown_3dfxGamma (void) {/*nothing*/} static inline int FUNC_UNUSED do3dfxGammaCtrl (float _f) { return 0; } static inline int FUNC_UNUSED glGetDeviceGammaRamp3DFX (void *_p) { return 0; } static inline int FUNC_UNUSED glSetDeviceGammaRamp3DFX (void *_p) { return 0; } static inline qboolean FUNC_UNUSED VID_Check3dfxGamma (void) { return false; } #else static qboolean VID_Check3dfxGamma (void) { int ret; if (COM_CheckParm("-no3dfxgamma")) return false; /* refuse 3dfxgamma with DRI drivers */ if (!q_strncasecmp(gl_renderer, "Mesa DRI", 8) || !q_strncasecmp(gl_renderer, "Mesa Glide - DRI", 16)) return false; #if USE_3DFX_RAMPS /* not recommended for Voodoo1, currently crashes */ ret = glGetDeviceGammaRamp3DFX(orig_ramps); if (ret != 0) { Con_SafePrintf ("Using 3dfx glide3 specific gamma ramps\n"); return true; } #else ret = Init_3dfxGammaCtrl(); if (ret > 0) { Con_SafePrintf ("Using 3dfx glide%d gamma controls\n", ret); return true; } #endif return false; } #endif /* USE_3DFXGAMMA */ static void VID_InitGamma (void) { gammaworks = fx_gamma = false; /* we don't have WGL_3DFX_gamma_control or an equivalent in unix. * assuming is_3dfx means Voodoo1 or Voodoo2, this means we dont * have hw-gamma. */ /* Here is an evil hack abusing the exposed Glide symbols: */ if (is_3dfx) fx_gamma = VID_Check3dfxGamma(); if (!fx_gamma) { #if USE_GAMMA_RAMPS gammaworks = (SDL_GetGammaRamp(orig_ramps[0], orig_ramps[1], orig_ramps[2]) == 0); if (gammaworks) gammaworks = (SDL_SetGammaRamp(orig_ramps[0], orig_ramps[1], orig_ramps[2]) == 0); #else gammaworks = (SDL_SetGamma(1, 1, 1) == 0); #endif } if (!gammaworks && !fx_gamma) Con_SafePrintf("gamma adjustment not available\n"); } static void VID_ShutdownGamma (void) { #if USE_3DFX_RAMPS if (fx_gamma) glSetDeviceGammaRamp3DFX(orig_ramps); #else /* if (fx_gamma) do3dfxGammaCtrl(1);*/ #endif Shutdown_3dfxGamma(); #if USE_GAMMA_RAMPS /* restore hw-gamma */ if (gammaworks) SDL_SetGammaRamp(orig_ramps[0], orig_ramps[1], orig_ramps[2]); #else if (gammaworks) SDL_SetGamma (1,1,1); #endif } static void VID_SetGamma (void) { #if (!USE_GAMMA_RAMPS) || (!USE_3DFX_RAMPS) float value = (v_gamma.value > (1.0 / GAMMA_MAX))? (1.0 / v_gamma.value) : GAMMA_MAX; #endif #if USE_3DFX_RAMPS if (fx_gamma) glSetDeviceGammaRamp3DFX(ramps); #else if (fx_gamma) do3dfxGammaCtrl(value); #endif #if USE_GAMMA_RAMPS if (gammaworks) SDL_SetGammaRamp(ramps[0], ramps[1], ramps[2]); #else if (gammaworks) SDL_SetGamma(value,value,value); #endif } void VID_ShiftPalette (const unsigned char *palette) { VID_SetGamma(); } static void CheckMultiTextureExtensions (void) { gl_mtexable = have_mtex = false; if (COM_CheckParm("-nomtex")) { Con_SafePrintf("Multitexture extensions disabled\n"); } else if (GL_ParseExtensionList(gl_extensions, "GL_ARB_multitexture")) { Con_SafePrintf("ARB Multitexture extensions found\n"); glGetIntegerv_fp(GL_MAX_TEXTURE_UNITS_ARB, &num_tmus); if (num_tmus < 2) { Con_SafePrintf("ignoring multitexture (%i TMUs)\n", (int) num_tmus); return; } glMultiTexCoord2fARB_fp = (glMultiTexCoord2fARB_f) SDL_GL_GetProcAddress("glMultiTexCoord2fARB"); glActiveTextureARB_fp = (glActiveTextureARB_f) SDL_GL_GetProcAddress("glActiveTextureARB"); if (glMultiTexCoord2fARB_fp == NULL || glActiveTextureARB_fp == NULL) { Con_SafePrintf ("Couldn't link to multitexture functions\n"); return; } have_mtex = true; if (!gl_multitexture.integer) { Con_SafePrintf("ignoring multitexture (cvar disabled)\n"); return; } Con_SafePrintf("Found %i TMUs support\n", (int) num_tmus); gl_mtexable = true; glDisable_fp(GL_TEXTURE_2D); glActiveTextureARB_fp(GL_TEXTURE0_ARB); } else { Con_SafePrintf("GL_ARB_multitexture not found\n"); } } static void CheckAnisotropyExtensions (void) { gl_max_anisotropy = 1; Con_SafePrintf("Anisotropic filtering "); if (GL_ParseExtensionList(gl_extensions, "GL_EXT_texture_filter_anisotropic")) { GLfloat test1 = 0, test2 = 0; GLuint tex; glGetTexParameterfv_f glGetTexParameterfv_fp; glGetTexParameterfv_fp = (glGetTexParameterfv_f) SDL_GL_GetProcAddress("glGetTexParameterfv"); if (glGetTexParameterfv_fp == NULL) { Con_SafePrintf("... can't check driver-lock status\n... "); goto _skiptest; } // test to make sure we really have control over it // 1.0 and 2.0 should always be legal values. glGenTextures_fp(1, &tex); glBindTexture_fp(GL_TEXTURE_2D, tex); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); glGetTexParameterfv_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &test1); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 2.0f); glGetTexParameterfv_fp(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &test2); glDeleteTextures_fp(1, &tex); if (test1 != 1 || test2 != 2) { Con_SafePrintf("driver-locked @ %.1f\n", test1); } else { _skiptest: glGetFloatv_fp(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_max_anisotropy); if (gl_max_anisotropy < 2) Con_SafePrintf("broken\n"); else Con_SafePrintf("found, max %.1f\n", gl_max_anisotropy); } } else { Con_SafePrintf("not found\n"); } } static void CheckNonPowerOfTwoTextures (void) { /* On Mac OS X, old Radeons lie about NPOT textures capability, they * fallback to software with mipmap NPOT textures. see, e.g.: * http://lists.apple.com/archives/mac-opengl/2006/Dec/msg00000.html * http://lists.apple.com/archives/mac-opengl/2009/Oct/msg00040.html * http://www.idevgames.com/forums/printthread.php?tid=3814&page=2 * MH says NVIDIA once did the same with their GeForce FX on Windows: * http://forums.inside3d.com/viewtopic.php?f=10&t=4832 * Therefore, advertisement of this extension is an unreliable way of * detecting the actual capability. */ gl_tex_NPOT = have_NPOT = false; if (GL_ParseExtensionList(gl_extensions, "GL_ARB_texture_non_power_of_two")) { have_NPOT = true; Con_SafePrintf("Found ARB_texture_non_power_of_two\n"); if (!gl_texture_NPOT.integer) { Con_SafePrintf("ignoring texture_NPOT (cvar disabled)\n"); } else { gl_tex_NPOT = true; } } else { Con_SafePrintf("GL_ARB_texture_non_power_of_two not found\n"); } } static void CheckStencilBuffer (void) { have_stencil = !!vid_attribs.stencil; } #ifdef GL_DLSYM static qboolean GL_OpenLibrary (const char *name) { int ret; char gl_liblocal[MAX_OSPATH]; ret = SDL_GL_LoadLibrary(name); if (ret == -1) { // In case of user-specified gl library, look for it under the // installation directory, too: the user may forget providing // a valid path information. In that case, make sure it doesnt // contain any path information and exists in our own basedir, // then try loading it if (name != NULL && FIND_FIRST_DIRSEP(name) == NULL && !HAS_DRIVE_SPEC(name)) { FS_MakePath_BUF (FS_BASEDIR, NULL, gl_liblocal, MAX_OSPATH, name); if (! (Sys_FileType(gl_liblocal) & FS_ENT_FILE)) return false; Con_SafePrintf ("Failed loading gl library %s\n" "Trying to load %s\n", name, gl_liblocal); ret = SDL_GL_LoadLibrary(gl_liblocal); if (ret == -1) return false; Con_SafePrintf("Using GL library: %s\n", gl_liblocal); return true; } return false; } if (name) Con_SafePrintf("Using GL library: %s\n", name); else Con_SafePrintf("Using system GL library\n"); return true; } #endif /* GL_DLSYM */ #ifdef GL_DLSYM static void GL_Init_Functions (void) { #define GL_FUNCTION(ret, func, params) \ do { \ func##_fp = (func##_f) SDL_GL_GetProcAddress(#func); \ if (func##_fp == NULL) \ Sys_Error("%s not found in GL library", #func); \ } while (0); #define GL_FUNCTION_OPT(ret, func, params) #include "gl_func.h" } #endif /* GL_DLSYM */ static void GL_ResetFunctions (void) { #ifdef GL_DLSYM #define GL_FUNCTION(ret, func, params) \ func##_fp = NULL; #endif #define GL_FUNCTION_OPT(ret, func, params) \ func##_fp = NULL; #include "gl_func.h" have_stencil = false; gl_mtexable = false; have_mtex = false; have8bit = false; is8bit = false; have_NPOT = false; gl_tex_NPOT = false; } /* =============== GL_Init =============== */ static void GL_Init (void) { #ifdef GL_DLSYM // initialize gl function pointers GL_Init_Functions(); #endif // collect the visual attributes memset (&vid_attribs, 0, sizeof(attributes_t)); SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &vid_attribs.red); SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &vid_attribs.green); SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &vid_attribs.blue); SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &vid_attribs.alpha); SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &vid_attribs.depth); SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &vid_attribs.stencil); Con_SafePrintf ("R:%d G:%d B:%d A:%d, Z:%d, S:%d\n", vid_attribs.red, vid_attribs.green, vid_attribs.blue, vid_attribs.alpha, vid_attribs.depth, vid_attribs.stencil); gl_vendor = (const char *)glGetString_fp (GL_VENDOR); Con_SafePrintf ("GL_VENDOR: %s\n", gl_vendor); gl_renderer = (const char *)glGetString_fp (GL_RENDERER); Con_SafePrintf ("GL_RENDERER: %s\n", gl_renderer); gl_version = (const char *)glGetString_fp (GL_VERSION); Con_SafePrintf ("GL_VERSION: %s\n", gl_version); gl_extensions = (const char *)glGetString_fp (GL_EXTENSIONS); Con_SafeDPrintf ("GL_EXTENSIONS: %s\n", gl_extensions); glGetIntegerv_fp(GL_MAX_TEXTURE_SIZE, &gl_max_size); if (gl_max_size < 256) // Refuse to work when less than 256 Sys_Error ("hardware capable of min. 256k opengl texture size needed"); Con_SafePrintf("OpenGL max.texture size: %i\n", (int) gl_max_size); is_3dfx = false; if (!q_strncasecmp(gl_renderer, "3dfx", 4) || !q_strncasecmp(gl_renderer, "SAGE Glide", 10) || !q_strncasecmp(gl_renderer, "Glide ", 6) || /* possible with Mesa 3.x/4.x/5.0.x */ !q_strncasecmp(gl_renderer, "Mesa Glide", 10)) { // This should hopefully detect Voodoo1 and Voodoo2 // hardware and possibly Voodoo Rush. // Voodoo Banshee, Voodoo3 and later are hw-accelerated // by DRI in XFree86-4.x and should be: is_3dfx = false. Con_SafePrintf("3dfx Voodoo found\n"); is_3dfx = true; } // if (!q_strncasecmp(gl_renderer, "PowerVR", 7)) // fullsbardraw = true; CheckMultiTextureExtensions(); CheckAnisotropyExtensions(); CheckNonPowerOfTwoTextures(); CheckStencilBuffer(); // glClearColor_fp(1,0,0,0); glCullFace_fp(GL_FRONT); glEnable_fp(GL_TEXTURE_2D); glEnable_fp(GL_ALPHA_TEST); glAlphaFunc_fp(GL_GREATER, 0.632); // 1 - e^-1 : replaced 0.666 to avoid clipping of smaller fonts/graphics #if 0 /* causes side effects at least in 16 bpp. */ /* Get rid of Z-fighting for textures by offsetting the * drawing of entity models compared to normal polygons. * (See: R_DrawBrushModel.) * (Only works if gl_ztrick is turned off) */ glPolygonOffset_fp(0.05f, 25.0f); #endif /* #if 0 */ glPolygonMode_fp (GL_FRONT_AND_BACK, GL_FILL); glShadeModel_fp (GL_FLAT); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glBlendFunc_fp (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); if (multisample) { glEnable_fp (GL_MULTISAMPLE_ARB); Con_SafePrintf ("enabled %i sample fsaa\n", multisample); } } /* ================= GL_BeginRendering ================= */ void GL_BeginRendering (int *x, int *y, int *width, int *height) { *x = *y = 0; *width = WRWidth; *height = WRHeight; // glViewport_fp (*x, *y, *width, *height); } void GL_EndRendering (void) { if (!scr_skipupdate) SDL_GL_SwapBuffers(); // handle the mouse state when windowed if that's changed if (_enable_mouse.integer != enable_mouse /*&& modestate == MS_WINDOWED*/) { if (_enable_mouse.integer) IN_ActivateMouse (); else IN_DeactivateMouse (); enable_mouse = _enable_mouse.integer; } // if (fullsbardraw) // Sbar_Changed(); } const int ColorIndex[16] = { 0, 31, 47, 63, 79, 95, 111, 127, 143, 159, 175, 191, 199, 207, 223, 231 }; const unsigned int ColorPercent[16] = { 25, 51, 76, 102, 114, 127, 140, 153, 165, 178, 191, 204, 216, 229, 237, 247 }; #define INVERSE_PALNAME "gfx/invpal.lmp" static int ConvertTrueColorToPal (const unsigned char *true_color, const unsigned char *palette) { int i; long min_dist; int min_index; long r, g, b; min_dist = 256 * 256 + 256 * 256 + 256 * 256; min_index = -1; r = (long) true_color[0]; g = (long) true_color[1]; b = (long) true_color[2]; for (i = 0; i < 256; i++) { long palr, palg, palb, dist; long dr, dg, db; palr = palette[3*i]; palg = palette[3*i+1]; palb = palette[3*i+2]; dr = palr - r; dg = palg - g; db = palb - b; dist = dr * dr + dg * dg + db * db; if (dist < min_dist) { min_dist = dist; min_index = i; } } return min_index; } static void VID_CreateInversePalette (const unsigned char *palette) { long r, g, b; long idx = 0; unsigned char true_color[3]; Con_SafePrintf ("Creating inverse palette\n"); for (r = 0; r < (1 << INVERSE_PAL_R_BITS); r++) { for (g = 0; g < (1 << INVERSE_PAL_G_BITS); g++) { for (b = 0; b < (1 << INVERSE_PAL_B_BITS); b++) { true_color[0] = (unsigned char)(r << (8 - INVERSE_PAL_R_BITS)); true_color[1] = (unsigned char)(g << (8 - INVERSE_PAL_G_BITS)); true_color[2] = (unsigned char)(b << (8 - INVERSE_PAL_B_BITS)); inverse_pal[idx] = ConvertTrueColorToPal(true_color, palette); idx++; } } } FS_CreatePath(FS_MakePath(FS_USERDIR, NULL, INVERSE_PALNAME)); FS_WriteFile (INVERSE_PALNAME, inverse_pal, INVERSE_PAL_SIZE); } static void VID_InitPalette (const unsigned char *palette) { const unsigned char *pal; unsigned short r, g, b; unsigned short i, p, c; unsigned int v, *table; int mark; #if ENDIAN_RUNTIME_DETECT switch (host_byteorder) { case BIG_ENDIAN: /* R G B A */ MASK_r = 0xff000000; MASK_g = 0x00ff0000; MASK_b = 0x0000ff00; MASK_a = 0x000000ff; SHIFT_r = 24; SHIFT_g = 16; SHIFT_b = 8; SHIFT_a = 0; break; case LITTLE_ENDIAN: /* A B G R */ MASK_r = 0x000000ff; MASK_g = 0x0000ff00; MASK_b = 0x00ff0000; MASK_a = 0xff000000; SHIFT_r = 0; SHIFT_g = 8; SHIFT_b = 16; SHIFT_a = 24; break; default: break; } MASK_rgb = (MASK_r|MASK_g|MASK_b); #endif /* ENDIAN_RUNTIME_DETECT */ // // 8 8 8 encoding // pal = palette; table = d_8to24table; for (i = 0; i < 256; i++) { r = pal[0]; g = pal[1]; b = pal[2]; pal += 3; v = (255 << SHIFT_a) + (r << SHIFT_r) + (g << SHIFT_g) + (b << SHIFT_b); *table++ = v; } d_8to24table[255] &= MASK_rgb; // 255 is transparent pal = palette; table = d_8to24TranslucentTable; for (i = 0; i < 16; i++) { c = ColorIndex[i] * 3; r = pal[c]; g = pal[c + 1]; b = pal[c + 2]; for (p = 0; p < 16; p++) { v = (ColorPercent[15 - p] << SHIFT_a) + (r << SHIFT_r) + (g << SHIFT_g) + (b << SHIFT_b); *table++ = v; RTint[i*16 + p] = ((float)r) / ((float)ColorPercent[15-p]); GTint[i*16 + p] = ((float)g) / ((float)ColorPercent[15-p]); BTint[i*16 + p] = ((float)b) / ((float)ColorPercent[15-p]); } } // Initialize the palettized textures data mark = Hunk_LowMark (); inverse_pal = (unsigned char *) FS_LoadHunkFile (INVERSE_PALNAME, NULL); if (inverse_pal != NULL && fs_filesize != INVERSE_PAL_SIZE) { Hunk_FreeToLowMark (mark); inverse_pal = NULL; } if (inverse_pal == NULL) { inverse_pal = (unsigned char *) Hunk_AllocName (INVERSE_PAL_SIZE + 1, INVERSE_PALNAME); VID_CreateInversePalette (palette); } } void VID_SetPalette (const unsigned char *palette) { // nothing to do } /* =================================================================== MAIN WINDOW =================================================================== */ /* ================ ClearAllStates ================ */ static void ClearAllStates (void) { Key_ClearStates (); IN_ClearStates (); } /* ================= VID_ChangeVideoMode intended only as a callback for VID_Restart_f ================= */ static void VID_ChangeVideoMode (int newmode) { int temp; if (!screen) return; temp = scr_disabled_for_loading; scr_disabled_for_loading = true; // restore gamma, reset gamma function pointers VID_ShutdownGamma(); CDAudio_Pause (); BGM_Pause (); S_ClearBuffer (); // Unload all textures and reset texture counts D_ClearOpenGLTextures(0); memset (lightmap_textures, 0, sizeof(lightmap_textures)); // reset all opengl function pointers GL_ResetFunctions(); // Avoid re-registering commands and re-allocating memory draw_reinit = true; // temporarily disable input devices IN_DeactivateMouse(); IN_ShowMouse (); // Kill device and rendering contexts SDL_FreeSurface(screen); SDL_QuitSubSystem(SDL_INIT_VIDEO); // also unloads the opengl driver // re-init sdl_video, set the mode and re-init opengl if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) Sys_Error ("Couldn't init video: %s", SDL_GetError()); #ifdef GL_DLSYM if (!GL_OpenLibrary(gl_library)) Sys_Error ("Unable to load GL library %s", (gl_library != NULL) ? gl_library : SDL_GetError()); #endif VID_SetMode (newmode); // re-get the video info since we re-inited sdl_video vid_info = SDL_GetVideoInfo(); // Reload graphics wad file (Draw_PicFromWad writes glpic_t data (sizes, // texnums) right on top of the original pic data, so the pic data will // be dirty after gl textures are loaded the first time; we need to load // a clean version) W_LoadWadFile ("gfx.wad"); // Initialize extensions and default OpenGL parameters GL_Init(); VID_InitGamma(); VID_Init8bitPalette(); // Reload pre-map pics, fonts, console, etc Draw_Init(); SCR_Init(); // R_Init() stuff: R_InitParticleTexture(); R_InitExtraTextures (); #if defined(H2W) R_InitNetgraphTexture(); #endif /* H2W */ Sbar_Init(); vid.recalc_refdef = 1; IN_ReInit (); ClearAllStates (); CDAudio_Resume (); BGM_Resume (); // Reload model textures and player skins Mod_ReloadTextures(); // rebuild the lightmaps GL_BuildLightmaps(); // finished reloading all images draw_reinit = false; scr_disabled_for_loading = temp; // apply our gamma VID_ShiftPalette(NULL); } static void VID_Restart_f (void) { if (vid_mode.integer < 0 || vid_mode.integer >= *nummodes) { Con_Printf ("Bad video mode %d\n", vid_mode.integer); Cvar_SetValueQuick (&vid_mode, vid_modenum); return; } Con_Printf ("Re-initializing video:\n"); VID_ChangeVideoMode (vid_mode.integer); } static int sort_modes (const void *arg1, const void *arg2) { const vmode_t *a1, *a2; a1 = (vmode_t *) arg1; a2 = (vmode_t *) arg2; #if 0 /* low to high bpp ? */ if (a1->bpp != a2->bpp) return a1->bpp - a2->bpp; #endif /* lowres to highres */ if (a1->width == a2->width) return a1->height - a2->height; return a1->width - a2->width; } static void VID_PrepareModes (SDL_Rect **sdl_modes) { int i, j; qboolean not_multiple; num_fmodes = 0; num_wmodes = 0; // Add the standart 4:3 modes to the windowed modes list // In an unlikely case that we receive no fullscreen modes, // this will be our modes list (kind of...) for (i = 0; i < (int)MAX_STDMODES; i++) { wmodelist[num_wmodes].width = std_modes[i].width; wmodelist[num_wmodes].height = std_modes[i].height; wmodelist[num_wmodes].halfscreen = 0; wmodelist[num_wmodes].fullscreen = 0; wmodelist[num_wmodes].bpp = 16; q_snprintf (wmodelist[num_wmodes].modedesc, MAX_DESC, "%d x %d", std_modes[i].width, std_modes[i].height); num_wmodes++; } // disaster scenario #1: no fullscreen modes. bind to the // windowed modes list. limit it to 640x480 max. because // we don't know the desktop dimensions if (sdl_modes == (SDL_Rect **)0) { no_fmodes: Con_SafePrintf ("No fullscreen video modes available\n"); num_wmodes = RES_640X480 + 1; modelist = wmodelist; nummodes = &num_wmodes; vid_default = RES_640X480; Cvar_SetValueQuick (&vid_config_glx, modelist[vid_default].width); Cvar_SetValueQuick (&vid_config_gly, modelist[vid_default].height); return; } // another disaster scenario (#2) if (sdl_modes == (SDL_Rect **)-1) { // Really should NOT HAVE happened! this return value is // for windowed modes! Since this means all resolutions // are supported, use our standart modes as modes list. Con_SafePrintf ("Unexpectedly received -1 from SDL_ListModes\n"); vid_maxwidth = MAXWIDTH; vid_maxheight = MAXHEIGHT; // num_fmodes = -1; num_fmodes = num_wmodes; nummodes = &num_wmodes; modelist = wmodelist; vid_default = RES_640X480; Cvar_SetValueQuick (&vid_config_glx, modelist[vid_default].width); Cvar_SetValueQuick (&vid_config_gly, modelist[vid_default].height); return; } #if 0 // print the un-processed modelist as reported by SDL for (j = 0; sdl_modes[j]; ++j) { Con_SafePrintf ("%d x %d\n", sdl_modes[j]->w, sdl_modes[j]->h); } Con_SafePrintf ("Total %d entries\n", j); #endif for (i = 0; sdl_modes[i] && num_fmodes < MAX_MODE_LIST; ++i) { // avoid multiple listings of the same dimension not_multiple = true; for (j = 0; j < num_fmodes; ++j) { if (fmodelist[j].width == sdl_modes[i]->w && fmodelist[j].height == sdl_modes[i]->h) { not_multiple = false; break; } } // avoid resolutions < 320x240 if (not_multiple && sdl_modes[i]->w >= MIN_WIDTH && sdl_modes[i]->h >= MIN_HEIGHT) { fmodelist[num_fmodes].width = sdl_modes[i]->w; fmodelist[num_fmodes].height = sdl_modes[i]->h; // FIXME: look at gl_vidnt.c and learn how to // really functionalize the halfscreen field? fmodelist[num_fmodes].halfscreen = 0; fmodelist[num_fmodes].fullscreen = 1; fmodelist[num_fmodes].bpp = 16; q_snprintf (fmodelist[num_fmodes].modedesc, MAX_DESC, "%d x %d", sdl_modes[i]->w, sdl_modes[i]->h); num_fmodes++; } } if (!num_fmodes) goto no_fmodes; // At his point, we have a list of valid fullscreen modes: // Let's bind to it and use it for windowed modes, as well. // The only downside is that if SDL doesn't report any low // resolutions to us, we shall not have any for windowed // rendering where they would be perfectly legitimate... // Since our fullscreen/windowed toggling is instant and // doesn't require a vid_restart, switching lists won't be // feasible, either. The -width/-height commandline args // remain as the user's trusty old friends here. nummodes = &num_fmodes; modelist = fmodelist; // SDL versions older than 1.2.8 have sorting problems if (num_fmodes > 1) qsort(fmodelist, num_fmodes, sizeof fmodelist[0], sort_modes); vid_maxwidth = fmodelist[num_fmodes-1].width; vid_maxheight = fmodelist[num_fmodes-1].height; // find the 640x480 default resolution. this shouldn't fail // at all (for any adapter suporting the VGA/XGA legacy). for (i = 0; i < num_fmodes; i++) { if (fmodelist[i].width == 640 && fmodelist[i].height == 480) { vid_default = i; break; } } if (vid_default < 0) { // No 640x480? Unexpected, at least today.. // Easiest thing is to set the default mode // as the highest reported one. Con_SafePrintf ("WARNING: 640x480 not found in fullscreen modes\n" "Using the largest reported dimension as default\n"); vid_default = num_fmodes-1; } // limit the windowed (standart) modes list to desktop dimensions for (i = 0; i < num_wmodes; i++) { if (wmodelist[i].width > vid_maxwidth || wmodelist[i].height > vid_maxheight) break; } if (i < num_wmodes) num_wmodes = i; Cvar_SetValueQuick (&vid_config_glx, modelist[vid_default].width); Cvar_SetValueQuick (&vid_config_gly, modelist[vid_default].height); } static void VID_ListModes_f (void) { int i; Con_Printf ("Maximum allowed mode: %d x %d\n", vid_maxwidth, vid_maxheight); Con_Printf ("Windowed modes enabled:\n"); for (i = 0; i < num_wmodes; i++) Con_Printf ("%2d: %d x %d\n", i, wmodelist[i].width, wmodelist[i].height); Con_Printf ("Fullscreen modes enumerated:"); if (num_fmodes) { Con_Printf ("\n"); for (i = 0; i < num_fmodes; i++) Con_Printf ("%2d: %d x %d\n", i, fmodelist[i].width, fmodelist[i].height); } else { Con_Printf (" None\n"); } } static void VID_NumModes_f (void) { Con_Printf ("%d video modes in current list\n", *nummodes); } /* =================== VID_Init =================== */ void VID_Init (const unsigned char *palette) { #ifndef __MORPHOS__ static char nvidia_env_vsync[32] = "__GL_SYNC_TO_VBLANK=1"; static char fxmesa_env_fullscreen[32] = "MESA_GLX_FX=f"; static char fxmesa_env_multitex[32] = "FX_DONT_FAKE_MULTITEX=1"; static char fxglide_env_nosplash[32] = "FX_GLIDE_NO_SPLASH=1"; #endif int i, temp, width, height; SDL_Rect **enumlist; const SDL_version *sdl_version; const char *read_vars[] = { "vid_config_fscr", "vid_config_gl8bit", "vid_config_fsaa", "vid_config_glx", "vid_config_gly", "vid_config_consize", "gl_texture_NPOT", "gl_multitexture", "gl_lightmapfmt" }; #define num_readvars ( sizeof(read_vars)/sizeof(read_vars[0]) ) Cvar_RegisterVariable (&vid_config_gl8bit); Cvar_RegisterVariable (&vid_config_fsaa); Cvar_RegisterVariable (&vid_config_fscr); Cvar_RegisterVariable (&vid_config_swy); Cvar_RegisterVariable (&vid_config_swx); Cvar_RegisterVariable (&vid_config_gly); Cvar_RegisterVariable (&vid_config_glx); Cvar_RegisterVariable (&vid_config_consize); Cvar_RegisterVariable (&vid_mode); Cvar_RegisterVariable (&_enable_mouse); Cvar_RegisterVariable (&gl_texture_NPOT); Cvar_RegisterVariable (&gl_lightmapfmt); Cvar_RegisterVariable (&gl_multitexture); Cmd_AddCommand ("vid_listmodes", VID_ListModes_f); Cmd_AddCommand ("vid_nummodes", VID_NumModes_f); Cmd_AddCommand ("vid_restart", VID_Restart_f); VID_InitPalette (palette); vid.numpages = 2; // see if the SDL version we linked to is multisampling-capable sdl_version = SDL_Linked_Version(); if (SDL_VERSIONNUM(sdl_version->major,sdl_version->minor,sdl_version->patch) >= SDL_VER_WITH_MULTISAMPLING) sdl_has_multisample = true; #ifndef __MORPHOS__ // enable vsync for nvidia geforce or newer - S.A if (COM_CheckParm("-sync") || COM_CheckParm("-vsync")) { putenv(nvidia_env_vsync); Con_SafePrintf ("Nvidia GL vsync enabled\n"); } // set fxMesa mode to fullscreen, don't let it cheat multitexturing putenv (fxmesa_env_fullscreen); putenv (fxmesa_env_multitex); // disable the 3dfx splash screen. putenv (fxglide_env_nosplash); #endif // init sdl // the first check is actually unnecessary if ((SDL_WasInit(SDL_INIT_VIDEO)) == 0) { if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) Sys_Error ("Couldn't init video: %s", SDL_GetError()); } #ifdef GL_DLSYM i = COM_CheckParm("--gllibrary"); if (i == 0) i = COM_CheckParm ("-gllibrary"); if (i == 0) i = COM_CheckParm ("-g"); if (i && i < com_argc - 1) gl_library = com_argv[i+1]; else gl_library = NULL; // trust SDL's wisdom here // load the opengl library if (!GL_OpenLibrary(gl_library)) Sys_Error ("Unable to load GL library %s", (gl_library != NULL) ? gl_library : SDL_GetError()); #endif // this will contain the "best bpp" for the current display // make sure to re-retrieve it if you ever re-init sdl_video vid_info = SDL_GetVideoInfo(); // retrieve the list of fullscreen modes enumlist = SDL_ListModes(NULL, SDL_OPENGL|SDL_FULLSCREEN); i = COM_CheckParm("-bpp"); if (i && i < com_argc-1) { bpp = atoi(com_argv[i+1]); } // prepare the modelists, find the actual modenum for vid_default VID_PrepareModes(enumlist); // set vid_mode to our safe default first Cvar_SetValueQuick (&vid_mode, vid_default); // perform an early read of config.cfg CFG_ReadCvars (read_vars, num_readvars); // windowed mode is default // see if the user wants fullscreen if (COM_CheckParm("-fullscreen") || COM_CheckParm("-f")) { Cvar_SetQuick (&vid_config_fscr, "1"); } else if (COM_CheckParm("-window") || COM_CheckParm("-w")) { Cvar_SetQuick (&vid_config_fscr, "0"); } if (vid_config_fscr.integer && !num_fmodes) // FIXME: see below, as well Sys_Error ("No fullscreen modes available at this color depth"); width = vid_config_glx.integer; height = vid_config_gly.integer; if (vid_config_consize.integer != width) vid_conscale = true; // user is always right ... i = COM_CheckParm("-width"); if (i && i < com_argc-1) { // FIXME: this part doesn't know about a disaster case // like we aren't reported any fullscreen modes. width = atoi(com_argv[i+1]); i = COM_CheckParm("-height"); if (i && i < com_argc-1) height = atoi(com_argv[i+1]); else // proceed with 4/3 ratio height = 3 * width / 4; } // user requested a mode either from the config or from the // command line // scan existing modes to see if this is already available // if not, add this as the last "valid" video mode and set // vid_mode to it only if it doesn't go beyond vid_maxwidth i = 0; while (i < *nummodes) { if (modelist[i].width == width && modelist[i].height == height) break; i++; } if (i < *nummodes) { Cvar_SetValueQuick (&vid_mode, i); } else if ( (width <= vid_maxwidth && width >= MIN_WIDTH && height <= vid_maxheight && height >= MIN_HEIGHT) || COM_CheckParm("-force") ) { modelist[*nummodes].width = width; modelist[*nummodes].height = height; modelist[*nummodes].halfscreen = 0; modelist[*nummodes].fullscreen = 1; modelist[*nummodes].bpp = 16; q_snprintf (modelist[*nummodes].modedesc, MAX_DESC, "%d x %d (user mode)", width, height); Cvar_SetValueQuick (&vid_mode, *nummodes); (*nummodes)++; } else { Con_SafePrintf ("ignoring invalid -width and/or -height arguments\n"); } if (!vid_conscale) Cvar_SetValueQuick (&vid_config_consize, width); // This will display a bigger hud and readable fonts at high // resolutions. The fonts will be somewhat distorted, though i = COM_CheckParm("-conwidth"); if (i != 0 && i < com_argc-1) i = atoi(com_argv[i + 1]); else i = vid_config_consize.integer; if (i < MIN_WIDTH) i = MIN_WIDTH; else if (i > width) i = width; Cvar_SetValueQuick(&vid_config_consize, i); if (vid_config_consize.integer != width) vid_conscale = true; multisample = vid_config_fsaa.integer; i = COM_CheckParm ("-fsaa"); if (i && i < com_argc-1) multisample = atoi(com_argv[i+1]); if (COM_CheckParm("-paltex")) Cvar_SetQuick (&vid_config_gl8bit, "1"); vid.maxwarpwidth = WARP_WIDTH; vid.maxwarpheight = WARP_HEIGHT; vid.colormap = host_colormap; vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); temp = scr_disabled_for_loading; scr_disabled_for_loading = true; //set the mode VID_SetMode (vid_mode.integer); ClearAllStates (); GL_SetupLightmapFmt(); GL_Init (); VID_InitGamma(); VID_Init8bitPalette(); // lock the early-read cvars until Host_Init is finished for (i = 0; i < (int)num_readvars; i++) Cvar_LockVar (read_vars[i]); vid_initialized = true; scr_disabled_for_loading = temp; vid.recalc_refdef = 1; Con_SafePrintf ("Video initialized.\n"); vid_menudrawfn = VID_MenuDraw; vid_menukeyfn = VID_MenuKey; // if (COM_CheckParm("-fullsbar")) // fullsbardraw = true; } void VID_Shutdown (void) { VID_ShutdownGamma(); SDL_QuitSubSystem(SDL_INIT_VIDEO); } /* ================ VID_ToggleFullscreen Handles switching between fullscreen/windowed modes and brings the mouse to a proper state afterwards ================ */ extern qboolean menu_disabled_mouse; void VID_ToggleFullscreen (void) { int is_fullscreen; if (!screen) return; if (!fs_toggle_works) return; if (!num_fmodes) return; S_ClearBuffer (); // This doesn't seem to cause any trouble even // with is_3dfx == true and FX_GLX_MESA == f fs_toggle_works = (SDL_WM_ToggleFullScreen(screen) == 1); if (fs_toggle_works) { is_fullscreen = (screen->flags & SDL_FULLSCREEN) ? 1 : 0; Cvar_SetValueQuick(&vid_config_fscr, is_fullscreen); modestate = (is_fullscreen) ? MS_FULLDIB : MS_WINDOWED; if (is_fullscreen) { // if (!_enable_mouse.integer) // Cvar_SetQuick (&_enable_mouse, "1"); // activate mouse in fullscreen mode // in_sdl.c handles other non-moused cases if (menu_disabled_mouse) IN_ActivateMouse(); } else { // windowed mode: // deactivate mouse if we are in menus if (menu_disabled_mouse) IN_DeactivateMouse(); } // update the video menu option vid_menu_fs = (modestate != MS_WINDOWED); } else { Con_Printf ("SDL_WM_ToggleFullScreen failed\n"); } } #ifndef H2W /* unused in hexenworld */ void D_ShowLoadingSize (void) { #if defined(DRAW_PROGRESSBARS) int cur_perc; static int prev_perc; if (!vid_initialized) return; cur_perc = loading_stage * 100; if (total_loading_size) cur_perc += current_loading_size * 100 / total_loading_size; if (cur_perc == prev_perc) return; prev_perc = cur_perc; glDrawBuffer_fp (GL_FRONT); SCR_DrawLoading(); glFlush_fp(); glDrawBuffer_fp (GL_BACK); #endif /* DRAW_PROGRESSBARS */ } #endif //======================================================== // Video menu stuff //======================================================== static int vid_menunum; static int vid_cursor; static qboolean want_fstoggle, need_apply; static qboolean vid_menu_firsttime = true; enum { VID_FULLSCREEN, // make sure the fullscreen entry (0) VID_RESOLUTION, // is lower than resolution entry (1) VID_MULTISAMPLE, VID_MULTITEXTURE, VID_NPOT, VID_PALTEX, VID_BLANKLINE, // spacer line VID_RESET, VID_APPLY, VID_ITEMS }; static void M_DrawYesNo (int x, int y, int on, int white) { if (on) { if (white) M_PrintWhite (x, y, "yes"); else M_Print (x, y, "yes"); } else { if (white) M_PrintWhite (x, y, "no"); else M_Print (x, y, "no"); } } /* ================ VID_MenuDraw ================ */ static void VID_MenuDraw (void) { ScrollTitle("gfx/menu/title7.lmp"); if (vid_menu_firsttime) { // settings for entering the menu first time vid_menunum = vid_modenum; vid_menu_fs = (modestate != MS_WINDOWED); vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; vid_menu_firsttime = false; } want_fstoggle = ( ((modestate == MS_WINDOWED) && vid_menu_fs) || ((modestate != MS_WINDOWED) && !vid_menu_fs) ); need_apply = (vid_menunum != vid_modenum) || want_fstoggle || (have8bit && (is8bit != !!vid_config_gl8bit.integer)) || (have_mtex && (gl_mtexable != !!gl_multitexture.integer)) || (have_NPOT && (gl_tex_NPOT != !!gl_texture_NPOT.integer)) || (multisample != vid_config_fsaa.integer); M_Print (76, 92 + 8*VID_FULLSCREEN, "Fullscreen: "); M_DrawYesNo (76+12*8, 92 + 8*VID_FULLSCREEN, vid_menu_fs, !want_fstoggle); M_Print (76, 92 + 8*VID_RESOLUTION, "Resolution: "); if (vid_menunum == vid_modenum) M_PrintWhite (76+12*8, 92 + 8*VID_RESOLUTION, modelist[vid_menunum].modedesc); else M_Print (76+12*8, 92 + 8*VID_RESOLUTION, modelist[vid_menunum].modedesc); M_Print (76, 92 + 8*VID_MULTISAMPLE, "Antialiasing :"); if (sdl_has_multisample) { if (multisample == vid_config_fsaa.integer) M_PrintWhite (76+16*8, 92 + 8*VID_MULTISAMPLE, va("%d",multisample)); else M_Print (76+16*8, 92 + 8*VID_MULTISAMPLE, va("%d",multisample)); } else M_PrintWhite (76+16*8, 92 + 8*VID_MULTISAMPLE, "Not found"); M_Print (76, 92 + 8*VID_MULTITEXTURE, "Multitexturing:"); if (have_mtex) M_DrawYesNo (76+16*8, 92 + 8*VID_MULTITEXTURE, gl_multitexture.integer, (gl_mtexable == !!gl_multitexture.integer)); else M_PrintWhite (76+16*8, 92 + 8*VID_MULTITEXTURE, "Not found"); M_Print (76, 92 + 8*VID_NPOT, "NPOT textures :"); if (have_NPOT) M_DrawYesNo (76+16*8, 92 + 8*VID_NPOT, gl_texture_NPOT.integer, (gl_tex_NPOT == !!gl_texture_NPOT.integer)); else M_PrintWhite (76+16*8, 92 + 8*VID_NPOT, "Not found"); M_Print (76, 92 + 8*VID_PALTEX, "8 bit textures:"); if (have8bit) M_DrawYesNo (76+16*8, 92 + 8*VID_PALTEX, vid_config_gl8bit.integer, (is8bit == !!vid_config_gl8bit.integer)); else M_PrintWhite (76+16*8, 92 + 8*VID_PALTEX, "Not found"); if (need_apply) { M_Print (76, 92 + 8*VID_RESET, "RESET CHANGES"); M_Print (76, 92 + 8*VID_APPLY, "APPLY CHANGES"); } M_DrawCharacter (64, 92 + vid_cursor*8, 12+((int)(realtime*4)&1)); } /* ================ VID_MenuKey ================ */ static void VID_MenuKey (int key) { switch (key) { case K_ESCAPE: vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; M_Menu_Options_f (); break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); vid_cursor--; if (vid_cursor < 0) { vid_cursor = (need_apply) ? VID_ITEMS-1 : VID_BLANKLINE-1; } else if (vid_cursor == VID_BLANKLINE) { vid_cursor--; } break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); vid_cursor++; if (vid_cursor >= VID_ITEMS) { vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; break; } if (vid_cursor >= VID_BLANKLINE) { if (need_apply) { if (vid_cursor == VID_BLANKLINE) vid_cursor++; } else { vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; } } break; case K_ENTER: switch (vid_cursor) { case VID_RESET: vid_menu_fs = (modestate != MS_WINDOWED); vid_menunum = vid_modenum; multisample = vid_config_fsaa.integer; Cvar_SetValueQuick (&vid_config_gl8bit, is8bit); vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; break; case VID_APPLY: if (need_apply) { Cvar_SetValueQuick(&vid_mode, vid_menunum); Cvar_SetValueQuick(&vid_config_fscr, vid_menu_fs); VID_Restart_f(); } vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; break; } return; case K_LEFTARROW: switch (vid_cursor) { case VID_FULLSCREEN: vid_menu_fs = !vid_menu_fs; if (fs_toggle_works) VID_ToggleFullscreen(); break; case VID_RESOLUTION: S_LocalSound ("raven/menu1.wav"); vid_menunum--; if (vid_menunum < 0) vid_menunum = 0; break; case VID_MULTISAMPLE: if (!sdl_has_multisample) break; if (multisample <= 2) multisample = 0; else if (multisample <= 4) multisample = 2; else multisample = 4; break; case VID_MULTITEXTURE: if (have_mtex) Cvar_SetQuick (&gl_multitexture, gl_multitexture.integer ? "0" : "1"); break; case VID_NPOT: if (have_NPOT) Cvar_SetQuick (&gl_texture_NPOT, gl_texture_NPOT.integer ? "0" : "1"); break; case VID_PALTEX: if (have8bit) Cvar_SetQuick (&vid_config_gl8bit, vid_config_gl8bit.integer ? "0" : "1"); break; } return; case K_RIGHTARROW: switch (vid_cursor) { case VID_FULLSCREEN: vid_menu_fs = !vid_menu_fs; if (fs_toggle_works) VID_ToggleFullscreen(); break; case VID_RESOLUTION: S_LocalSound ("raven/menu1.wav"); vid_menunum++; if (vid_menunum >= *nummodes) vid_menunum = *nummodes - 1; break; case VID_MULTISAMPLE: if (!sdl_has_multisample) break; if (multisample < 2) multisample = 2; else if (multisample < 4) multisample = 4; else if (multisample < 8) multisample = 8; break; case VID_MULTITEXTURE: if (have_mtex) Cvar_SetQuick (&gl_multitexture, gl_multitexture.integer ? "0" : "1"); break; case VID_NPOT: if (have_NPOT) Cvar_SetQuick (&gl_texture_NPOT, gl_texture_NPOT.integer ? "0" : "1"); break; case VID_PALTEX: if (have8bit) Cvar_SetQuick (&vid_config_gl8bit, vid_config_gl8bit.integer ? "0" : "1"); break; } return; default: break; } } engine/h2shared/gl_warp.c000066400000000000000000000545771444734033100156470ustar00rootroot00000000000000/* * gl_warp.c -- sky and water polygons * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" int skytexturenum; static GLuint solidskytexture, alphaskytexture; static float speedscale; // for top sky and bottom sky static msurface_t *warpface; #define SUBDIVIDE_SIZE 64 static void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs) { int i, j; float *v; mins[0] = mins[1] = mins[2] = 9999999; /* FIXME: change these two to FLT_MAX/-FLT_MAX */ maxs[0] = maxs[1] = maxs[2] = -9999999; v = verts; for (i = 0; i < numverts; i++) { for (j = 0; j < 3; j++, v++) { if (*v < mins[j]) mins[j] = *v; if (*v > maxs[j]) maxs[j] = *v; } } } static void SubdividePolygon (int numverts, float *verts) { int i, j, k; vec3_t mins, maxs; float m; float *v; vec3_t front[64], back[64]; int f, b; float dist[64]; float frac; glpoly_t *poly; float s, t; if (numverts > 60) Sys_Error ("numverts = %i", numverts); BoundPoly (numverts, verts, mins, maxs); for (i = 0; i < 3; i++) { m = (mins[i] + maxs[i]) * 0.5; m = SUBDIVIDE_SIZE * floor (m/SUBDIVIDE_SIZE + 0.5); if (maxs[i] - m < 8) continue; if (m - mins[i] < 8) continue; // cut it v = verts + i; for (j = 0; j < numverts; j++, v += 3) dist[j] = *v - m; // wrap cases dist[j] = dist[0]; v -= i; VectorCopy (verts, v); f = b = 0; v = verts; for (j = 0; j < numverts; j++, v += 3) { if (dist[j] >= 0) { VectorCopy (v, front[f]); f++; } if (dist[j] <= 0) { VectorCopy (v, back[b]); b++; } if (dist[j] == 0 || dist[j+1] == 0) continue; if ( (dist[j] > 0) != (dist[j+1] > 0) ) { // clip point frac = dist[j] / (dist[j] - dist[j+1]); for (k = 0; k < 3; k++) front[f][k] = back[b][k] = v[k] + frac*(v[3+k] - v[k]); f++; b++; } } SubdividePolygon (f, front[0]); SubdividePolygon (b, back[0]); return; } poly = (glpoly_t *) Hunk_AllocName (sizeof(glpoly_t) + (numverts-4) * VERTEXSIZE*sizeof(float), "subdv_poly"); poly->next = warpface->polys; warpface->polys = poly; poly->numverts = numverts; for (i = 0; i < numverts; i++, verts += 3) { VectorCopy (verts, poly->verts[i]); s = DotProduct (verts, warpface->texinfo->vecs[0]); t = DotProduct (verts, warpface->texinfo->vecs[1]); poly->verts[i][3] = s; poly->verts[i][4] = t; } } /* ================ GL_SubdivideSurface Breaks a polygon up along axial 64 unit boundaries so that turbulent and sky warps can be done reasonably. ================ */ void GL_SubdivideSurface (qmodel_t *mod, msurface_t *fa) { vec3_t verts[64]; int numverts; int i; int lindex; float *vec; warpface = fa; // // convert edges back to a normal polygon // numverts = 0; for (i = 0; i < fa->numedges; i++) { lindex = mod->surfedges[fa->firstedge + i]; if (lindex > 0) vec = mod->vertexes[mod->edges[lindex].v[0]].position; else vec = mod->vertexes[mod->edges[-lindex].v[1]].position; VectorCopy (vec, verts[numverts]); numverts++; } SubdividePolygon (numverts, verts[0]); } //========================================================= // speed up sin calculations - Ed static float turbsin[] = { #include "gl_warp_sin.h" }; #define TURBSCALE (256.0 / (2 * M_PI)) /* ============= EmitWaterPolys Does a water warp on the pre-fragmented glpoly_t chain ============= */ void EmitWaterPolys (msurface_t *fa) { glpoly_t *p; float *v; int i; float s, t, os, ot; vec3_t nv; // waterripple if (gl_waterripple.value < 0) gl_waterripple.value = 0; else if (gl_waterripple.value > 10) gl_waterripple.value = 10; for (p = fa->polys ; p ; p = p->next) { glBegin_fp (GL_POLYGON); for (i = 0, v = p->verts[0]; i < p->numverts; i++, v += VERTEXSIZE) { os = v[3]; ot = v[4]; nv[0] = v[0]; nv[1] = v[1]; nv[2] = v[2] + gl_waterripple.value*sin(v[0]*0.05 + realtime)*sin(v[2]*0.05 + realtime); s = os + turbsin[(int)((ot*0.125 + realtime) * TURBSCALE) & 255]; s *= (1.0/64); t = ot + turbsin[(int)((os*0.125 + realtime) * TURBSCALE) & 255]; t *= (1.0/64); glTexCoord2f_fp (s, t); //glVertex3fv_fp (v); glVertex3fv_fp (nv); } glEnd_fp (); } } /* ============= EmitSkyPolys ============= */ static void EmitSkyPolysMulti (msurface_t *fa) { glpoly_t *p; float *v; int i; float s, ss, t, tt; vec3_t dir; float length; glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_Bind (solidskytexture); glActiveTextureARB_fp (GL_TEXTURE1_ARB); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glEnable_fp(GL_TEXTURE_2D); GL_Bind (alphaskytexture); for (p = fa->polys ; p ; p = p->next) { glBegin_fp (GL_POLYGON); for (i = 0, v = p->verts[0]; i < p->numverts; i++, v += VERTEXSIZE) { VectorSubtract (v, r_origin, dir); dir[2] *= 3; // flatten the sphere length = dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]; length = sqrt (length); length = 6*63/length; dir[0] *= length; dir[1] *= length; s = (realtime*8 + dir[0]) * (1.0/128); t = (realtime*8 + dir[1]) * (1.0/128); ss = (realtime*16 + dir[0]) * (1.0/128); tt = (realtime*16 + dir[1]) * (1.0/128); glMultiTexCoord2fARB_fp (GL_TEXTURE0_ARB, s, t); glMultiTexCoord2fARB_fp (GL_TEXTURE1_ARB, ss, tt); glVertex3fv_fp (v); } glEnd_fp (); } glDisable_fp(GL_TEXTURE_2D); glActiveTextureARB_fp (GL_TEXTURE0_ARB); } static void EmitSkyPolys (msurface_t *fa) { glpoly_t *p; float *v; int i; float s, t; vec3_t dir; float length; for (p = fa->polys ; p ; p = p->next) { glBegin_fp (GL_POLYGON); for (i = 0, v = p->verts[0]; i < p->numverts; i++, v += VERTEXSIZE) { VectorSubtract (v, r_origin, dir); dir[2] *= 3; // flatten the sphere length = dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]; length = sqrt (length); length = 6*63/length; dir[0] *= length; dir[1] *= length; s = (speedscale + dir[0]) * (1.0/128); t = (speedscale + dir[1]) * (1.0/128); glTexCoord2f_fp (s, t); glVertex3fv_fp (v); } glEnd_fp (); } } /* =============== EmitBothSkyLayers Does a sky warp on the pre-fragmented glpoly_t chain This will be called for brushmodels, the world will have them chained together. =============== */ void EmitBothSkyLayers (msurface_t *fa) { // 3dfx doesn't like GL_DECAL: if (!is_3dfx && gl_mtexable) { EmitSkyPolysMulti (fa); return; } GL_Bind(solidskytexture); speedscale = realtime*8; speedscale -= (int)speedscale & ~127; EmitSkyPolys (fa); glEnable_fp (GL_BLEND); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f_fp(1.0f, 1.0f, 1.0f, r_skyalpha.value); GL_Bind (alphaskytexture); speedscale = realtime*16; speedscale -= (int)speedscale & ~127; EmitSkyPolys (fa); glDisable_fp (GL_BLEND); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } #ifndef QUAKE2 /* ================= R_DrawSkyChain ================= */ void R_DrawSkyChain (msurface_t *s) { msurface_t *fa; // 3dfx doesn't like GL_DECAL: if (!is_3dfx && gl_mtexable) { for (fa = s ; fa ; fa = fa->texturechain) EmitSkyPolysMulti (fa); return; } GL_Bind(solidskytexture); speedscale = realtime*8; speedscale -= (int)speedscale & ~127; for (fa = s ; fa ; fa = fa->texturechain) EmitSkyPolys (fa); glEnable_fp (GL_BLEND); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f_fp(1.0f, 1.0f, 1.0f, r_skyalpha.value); GL_Bind (alphaskytexture); speedscale = realtime*16; speedscale -= (int)speedscale & ~127; for (fa = s ; fa ; fa = fa->texturechain) EmitSkyPolys (fa); glDisable_fp (GL_BLEND); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } #endif /* ================================================================= Quake 2 environment sky ================================================================= */ #ifdef QUAKE2 static GLuint sky_tex[6]; /* ================================================================= PCX Loading ================================================================= */ typedef struct { char manufacturer; char version; char encoding; char bits_per_pixel; unsigned short xmin,ymin,xmax,ymax; unsigned short hres,vres; unsigned char palette[48]; char reserved; char color_planes; unsigned short bytes_per_line; unsigned short palette_type; char filler[58]; unsigned int data; // unbounded } pcx_t; static byte *pcx_rgb; /* ============ LoadPCX ============ */ void LoadPCX (FILE *f) { pcx_t *pcx, pcxbuf; byte palette[768]; byte *pix; int x, y; int dataByte, runLength; int count; // // parse the PCX file // fread (&pcxbuf, 1, sizeof(pcxbuf), f); pcx = &pcxbuf; if (pcx->manufacturer != 0x0a || pcx->version != 5 || pcx->encoding != 1 || pcx->bits_per_pixel != 8 || pcx->xmax >= 320 || pcx->ymax >= 256) { Con_Printf ("Bad pcx file\n"); return; } // seek to palette fseek (f, -768, SEEK_END); fread (palette, 1, 768, f); fseek (f, sizeof(pcxbuf) - 4, SEEK_SET); count = (pcx->xmax + 1) * (pcx->ymax + 1); pcx_rgb = Hunk_AllocName(count * 4, "pcxfile_data"); for (y = 0 ; y <= pcx->ymax ; y++) { pix = pcx_rgb + 4*y*(pcx->xmax + 1); for (x = 0 ; x <= pcx->ymax ; ) { dataByte = fgetc(f); if ((dataByte & 0xC0) == 0xC0) { runLength = dataByte & 0x3F; dataByte = fgetc(f); } else runLength = 1; while (runLength-- > 0) { pix[0] = palette[dataByte*3]; pix[1] = palette[dataByte*3+1]; pix[2] = palette[dataByte*3+2]; pix[3] = 255; pix += 4; x++; } } } } /* ========================================================= TARGA LOADING ========================================================= */ typedef struct _TargaHeader { unsigned char id_length, colormap_type, image_type; unsigned short colormap_index, colormap_length; unsigned char colormap_size; unsigned short x_origin, y_origin, width, height; unsigned char pixel_size, attributes; } TargaHeader; static TargaHeader targa_header; static byte *targa_rgba; int fgetLittleShort (FILE *f) { byte b1, b2; b1 = fgetc(f); b2 = fgetc(f); return (short)(b1 + b2*256); } int fgetLittleLong (FILE *f) { byte b1, b2, b3, b4; b1 = fgetc(f); b2 = fgetc(f); b3 = fgetc(f); b4 = fgetc(f); return b1 + (b2<<8) + (b3<<16) + (b4<<24); } /* ============= LoadTGA ============= */ void LoadTGA (FILE *fin) { int columns, rows, numPixels; byte *pixbuf; int row, column; targa_header.id_length = fgetc(fin); targa_header.colormap_type = fgetc(fin); targa_header.image_type = fgetc(fin); targa_header.colormap_index = fgetLittleShort(fin); targa_header.colormap_length = fgetLittleShort(fin); targa_header.colormap_size = fgetc(fin); targa_header.x_origin = fgetLittleShort(fin); targa_header.y_origin = fgetLittleShort(fin); targa_header.width = fgetLittleShort(fin); targa_header.height = fgetLittleShort(fin); targa_header.pixel_size = fgetc(fin); targa_header.attributes = fgetc(fin); if (targa_header.image_type != 2 && targa_header.image_type != 10) Sys_Error ("%s: Only type 2 and 10 targa RGB images supported", __thisfunc__); if ((targa_header.pixel_size != 32 && targa_header.pixel_size != 24) || targa_header.colormap_type !=0) Sys_Error ("%s: Only 32 or 24 bit images supported (no colormaps)", __thisfunc__); columns = targa_header.width; rows = targa_header.height; numPixels = columns * rows; targa_rgba = Hunk_AllocName(numPixels * 4, "tgafile_data"); if (targa_header.id_length != 0) // skip TARGA image comment fseek(fin, targa_header.id_length, SEEK_CUR); if (targa_header.image_type == 2) { // Uncompressed, RGB images for (row = rows-1; row >= 0; row--) { pixbuf = targa_rgba + row*columns*4; for (column = 0; column < columns; column++) { unsigned char red, green, blue, alphabyte; switch (targa_header.pixel_size) { case 24: blue = getc(fin); green = getc(fin); red = getc(fin); *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = 255; break; case 32: blue = getc(fin); green = getc(fin); red = getc(fin); alphabyte = getc(fin); *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = alphabyte; break; } } } } else if (targa_header.image_type == 10) { // Runlength encoded RGB images unsigned char red, green, blue, alphabyte; unsigned char packetHeader, packetSize, j; for (row = rows-1; row >= 0; row--) { pixbuf = targa_rgba + row*columns*4; for (column = 0 ; column < columns ; ) { packetHeader = getc(fin); packetSize = 1 + (packetHeader & 0x7f); if (packetHeader & 0x80) { // run-length packet switch (targa_header.pixel_size) { case 24: blue = getc(fin); green = getc(fin); red = getc(fin); alphabyte = 255; break; case 32: blue = getc(fin); green = getc(fin); red = getc(fin); alphabyte = getc(fin); break; } for (j = 0; j < packetSize; j++) { *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = alphabyte; column++; if (column == columns) { // run spans across rows column = 0; if (row > 0) row--; else goto breakOut; pixbuf = targa_rgba + row*columns*4; } } } else { // non run-length packet for (j = 0; j < packetSize; j++) { switch (targa_header.pixel_size) { case 24: blue = getc(fin); green = getc(fin); red = getc(fin); *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = 255; break; case 32: blue = getc(fin); green = getc(fin); red = getc(fin); alphabyte = getc(fin); *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = alphabyte; break; } column++; if (column == columns) { // pixel packet run spans across rows column = 0; if (row > 0) row--; else goto breakOut; pixbuf = targa_rgba + row*columns*4; } } } } breakOut:; } } fclose(fin); } /* ================== R_LoadSkys ================== */ static char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; void R_LoadSkys (void) { int i, mark; FILE *f; char name[64], texname[20]; for (i = 0; i < 6; i++) { q_snprintf (name, sizeof(name), "gfx/env/bkgtst%s.tga", suf[i]); FS_OpenFile (name, &f, NULL); if (!f) { Con_Printf ("Couldn't load %s\n", name); continue; } mark = Hunk_LowMark(); LoadTGA (f); // LoadPCX (f); q_snprintf(texname, sizeof(texname), "skybox%i", i); sky_tex[i] = GL_LoadTexture(texname, targa_rgba, 256, 256, TEX_RGBA|TEX_LINEAR); Hunk_FreeToLowMark(mark); } } static vec3_t skyclip[6] = { { 1, 1, 0 }, { 1, -1, 0 }, { 0, -1, 1 }, { 0, 1, 1 }, { 1, 0, 1 }, { -1, 0, 1 } }; //int c_sky; // 1 = s, 2 = t, 3 = 2048 static int st_to_vec[6][3] = { { 3, -1, 2 }, { -3, 1, 2 }, { 1, 3, 2 }, { -1, -3, 2 }, { -2, -1, 3 }, // 0 degrees yaw, look straight up { 2, -1, -3 } // look straight down // { -1, 2, 3 }, // { 1, 2, -3 } }; // s = [0]/[2], t = [1]/[2] static int vec_to_st[6][3] = { { -2, 3, 1 }, { 2, 3, -1 }, { 1, 3, 2 }, { -1, 3, -2 }, { -2, -1, 3 }, { -2, 1, -3 } // { -1, 2, 3 }, // { 1, 2, -3 } }; static float skymins[2][6], skymaxs[2][6]; static void DrawSkyPolygon (int nump, vec3_t vecs) { int i, j; vec3_t v, av; float s, t, dv; int axis; float *vp; // c_sky++; #if 0 glBegin_fp (GL_POLYGON); for (i = 0; i < nump; i++, vecs += 3) { VectorAdd(vecs, r_origin, v); glVertex3fv_fp (v); } glEnd_fp(); return; #endif // decide which face it maps to VectorClear (v); for (i = 0, vp = vecs; i < nump; i++, vp += 3) { VectorAdd (vp, v, v); } av[0] = fabs(v[0]); av[1] = fabs(v[1]); av[2] = fabs(v[2]); if (av[0] > av[1] && av[0] > av[2]) { if (v[0] < 0) axis = 1; else axis = 0; } else if (av[1] > av[2] && av[1] > av[0]) { if (v[1] < 0) axis = 3; else axis = 2; } else { if (v[2] < 0) axis = 5; else axis = 4; } // project new texture coords for (i = 0; i < nump; i++, vecs += 3) { j = vec_to_st[axis][2]; if (j > 0) dv = vecs[j - 1]; else dv = -vecs[-j - 1]; j = vec_to_st[axis][0]; if (j < 0) s = -vecs[-j -1] / dv; else s = vecs[j-1] / dv; j = vec_to_st[axis][1]; if (j < 0) t = -vecs[-j -1] / dv; else t = vecs[j-1] / dv; if (s < skymins[0][axis]) skymins[0][axis] = s; if (t < skymins[1][axis]) skymins[1][axis] = t; if (s > skymaxs[0][axis]) skymaxs[0][axis] = s; if (t > skymaxs[1][axis]) skymaxs[1][axis] = t; } } #define MAX_CLIP_VERTS 64 static void ClipSkyPolygon (int nump, vec3_t vecs, int stage) { float *norm; float *v; qboolean front, back; float d, e; float dists[MAX_CLIP_VERTS]; int sides[MAX_CLIP_VERTS]; vec3_t newv[2][MAX_CLIP_VERTS]; int newc[2]; int i, j; if (nump > MAX_CLIP_VERTS-2) Sys_Error ("%s: MAX_CLIP_VERTS", __thisfunc__); if (stage == 6) { // fully clipped, so draw it DrawSkyPolygon (nump, vecs); return; } front = back = false; norm = skyclip[stage]; for (i = 0, v = vecs; i < nump; i++, v += 3) { d = DotProduct (v, norm); if (d > ON_EPSILON) { front = true; sides[i] = SIDE_FRONT; } else if (d < ON_EPSILON) { back = true; sides[i] = SIDE_BACK; } else sides[i] = SIDE_ON; dists[i] = d; } if (!front || !back) { // not clipped ClipSkyPolygon (nump, vecs, stage+1); return; } // clip it sides[i] = sides[0]; dists[i] = dists[0]; VectorCopy (vecs, (vecs+(i*3)) ); newc[0] = newc[1] = 0; for (i = 0, v = vecs; i < nump; i++, v += 3) { switch (sides[i]) { case SIDE_FRONT: VectorCopy (v, newv[0][newc[0]]); newc[0]++; break; case SIDE_BACK: VectorCopy (v, newv[1][newc[1]]); newc[1]++; break; case SIDE_ON: VectorCopy (v, newv[0][newc[0]]); newc[0]++; VectorCopy (v, newv[1][newc[1]]); newc[1]++; break; } if (sides[i] == SIDE_ON || sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue; d = dists[i] / (dists[i] - dists[i+1]); for (j = 0; j < 3; j++) { e = v[j] + d*(v[j+3] - v[j]); newv[0][newc[0]][j] = e; newv[1][newc[1]][j] = e; } newc[0]++; newc[1]++; } // continue ClipSkyPolygon (newc[0], newv[0][0], stage+1); ClipSkyPolygon (newc[1], newv[1][0], stage+1); } /* ================= R_DrawSkyChain ================= */ void R_DrawSkyChain (msurface_t *s) { msurface_t *fa; int i; vec3_t verts[MAX_CLIP_VERTS]; glpoly_t *p; // c_sky = 0; GL_Bind(solidskytexture); // calculate vertex values for sky box for (fa = s ; fa ; fa = fa->texturechain) { for (p = fa->polys ; p ; p = p->next) { for (i = 0; i < p->numverts; i++) { VectorSubtract (p->verts[i], r_origin, verts[i]); } ClipSkyPolygon (p->numverts, verts[0], 0); } } } /* ============== R_ClearSkyBox ============== */ void R_ClearSkyBox (void) { int i; for (i = 0; i < 6; i++) { skymins[0][i] = skymins[1][i] = 9999999; /* FIXME: change these two to FLT_MAX/-FLT_MAX */ skymaxs[0][i] = skymaxs[1][i] = -9999999; } } static void MakeSkyVec (float s, float t, int axis) { vec3_t v, b; int j, k; b[0] = s*2048; b[1] = t*2048; b[2] = 2048; for (j = 0; j < 3; j++) { k = st_to_vec[axis][j]; if (k < 0) v[j] = -b[-k - 1]; else v[j] = b[k - 1]; v[j] += r_origin[j]; } // avoid bilerp seam s = (s + 1)*0.5; t = (t + 1)*0.5; if (s < 1.0/512) s = 1.0/512; else if (s > 511.0/512) s = 511.0/512; if (t < 1.0/512) t = 1.0/512; else if (t > 511.0/512) t = 511.0/512; t = 1.0 - t; glTexCoord2f_fp (s, t); glVertex3fv_fp (v); } /* ============== R_DrawSkyBox ============== */ static int skytexorder[6] = {0, 2, 1, 3, 4, 5}; void R_DrawSkyBox (void) { int i; #if 0 glEnable_fp (GL_BLEND); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f_fp (1,1,1,0.5); glDisable_fp (GL_DEPTH_TEST); #endif for (i = 0; i < 6; i++) { if ( (skymins[0][i] >= skymaxs[0][i]) || (skymins[1][i] >= skymaxs[1][i]) ) continue; GL_Bind(sky_tex[skytexorder[i]]); #if 0 skymins[0][i] = -1; skymins[1][i] = -1; skymaxs[0][i] = 1; skymaxs[1][i] = 1; #endif glBegin_fp (GL_QUADS); MakeSkyVec (skymins[0][i], skymins[1][i], i); MakeSkyVec (skymins[0][i], skymaxs[1][i], i); MakeSkyVec (skymaxs[0][i], skymaxs[1][i], i); MakeSkyVec (skymaxs[0][i], skymins[1][i], i); glEnd_fp (); } #if 0 glDisable_fp (GL_BLEND); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor4f_fp (1,1,1,0.5); glEnable_fp (GL_DEPTH_TEST); #endif } #endif /* end of Quake2 sky */ //=============================================================== /* ============= R_InitSky A sky texture is 256*128, with the right side being a masked overlay ============== */ void R_InitSky (texture_t *mt) { int i, j, p; byte *src; unsigned int trans[128*128]; unsigned int transpix; int r, g, b; unsigned int *rgba; src = (byte *)mt + mt->offsets[0]; // make an average value for the back to avoid // a fringe on the top level r = g = b = 0; for (i = 0; i < 128; i++) { for (j = 0; j < 128; j++) { p = src[i*256 + j + 128]; rgba = &d_8to24table[p]; trans[(i*128) + j] = *rgba; r += ((byte *)rgba)[0]; g += ((byte *)rgba)[1]; b += ((byte *)rgba)[2]; } } ((byte *)&transpix)[0] = r / (128*128); ((byte *)&transpix)[1] = g / (128*128); ((byte *)&transpix)[2] = b / (128*128); ((byte *)&transpix)[3] = 0; solidskytexture = GL_LoadTexture("upsky", (byte *)trans, 128, 128, TEX_RGBA|TEX_LINEAR); for (i = 0; i < 128; i++) { for (j = 0; j < 128; j++) { p = src[i*256 + j]; if (p == 0) trans[(i*128) + j] = transpix; else trans[(i*128) + j] = d_8to24table[p]; } } alphaskytexture = GL_LoadTexture("lowsky", (byte *)trans, 128, 128, TEX_ALPHA|TEX_RGBA|TEX_LINEAR); } engine/h2shared/gl_warp_sin.h000066400000000000000000000065231444734033100165110ustar00rootroot00000000000000/* gl_warp_sin.h * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ 0, 0.19633, 0.392541, 0.588517, 0.784137, 0.979285, 1.17384, 1.3677, 1.56072, 1.75281, 1.94384, 2.1337, 2.32228, 2.50945, 2.69512, 2.87916, 3.06147, 3.24193, 3.42044, 3.59689, 3.77117, 3.94319, 4.11282, 4.27998, 4.44456, 4.60647, 4.76559, 4.92185, 5.07515, 5.22538, 5.37247, 5.51632, 5.65685, 5.79398, 5.92761, 6.05767, 6.18408, 6.30677, 6.42566, 6.54068, 6.65176, 6.75883, 6.86183, 6.9607, 7.05537, 7.14579, 7.23191, 7.31368, 7.39104, 7.46394, 7.53235, 7.59623, 7.65552, 7.71021, 7.76025, 7.80562, 7.84628, 7.88222, 7.91341, 7.93984, 7.96148, 7.97832, 7.99036, 7.99759, 8, 7.99759, 7.99036, 7.97832, 7.96148, 7.93984, 7.91341, 7.88222, 7.84628, 7.80562, 7.76025, 7.71021, 7.65552, 7.59623, 7.53235, 7.46394, 7.39104, 7.31368, 7.23191, 7.14579, 7.05537, 6.9607, 6.86183, 6.75883, 6.65176, 6.54068, 6.42566, 6.30677, 6.18408, 6.05767, 5.92761, 5.79398, 5.65685, 5.51632, 5.37247, 5.22538, 5.07515, 4.92185, 4.76559, 4.60647, 4.44456, 4.27998, 4.11282, 3.94319, 3.77117, 3.59689, 3.42044, 3.24193, 3.06147, 2.87916, 2.69512, 2.50945, 2.32228, 2.1337, 1.94384, 1.75281, 1.56072, 1.3677, 1.17384, 0.979285, 0.784137, 0.588517, 0.392541, 0.19633, 9.79717e-16, -0.19633, -0.392541, -0.588517, -0.784137, -0.979285, -1.17384, -1.3677, -1.56072, -1.75281, -1.94384, -2.1337, -2.32228, -2.50945, -2.69512, -2.87916, -3.06147, -3.24193, -3.42044, -3.59689, -3.77117, -3.94319, -4.11282, -4.27998, -4.44456, -4.60647, -4.76559, -4.92185, -5.07515, -5.22538, -5.37247, -5.51632, -5.65685, -5.79398, -5.92761, -6.05767, -6.18408, -6.30677, -6.42566, -6.54068, -6.65176, -6.75883, -6.86183, -6.9607, -7.05537, -7.14579, -7.23191, -7.31368, -7.39104, -7.46394, -7.53235, -7.59623, -7.65552, -7.71021, -7.76025, -7.80562, -7.84628, -7.88222, -7.91341, -7.93984, -7.96148, -7.97832, -7.99036, -7.99759, -8, -7.99759, -7.99036, -7.97832, -7.96148, -7.93984, -7.91341, -7.88222, -7.84628, -7.80562, -7.76025, -7.71021, -7.65552, -7.59623, -7.53235, -7.46394, -7.39104, -7.31368, -7.23191, -7.14579, -7.05537, -6.9607, -6.86183, -6.75883, -6.65176, -6.54068, -6.42566, -6.30677, -6.18408, -6.05767, -5.92761, -5.79398, -5.65685, -5.51632, -5.37247, -5.22538, -5.07515, -4.92185, -4.76559, -4.60647, -4.44456, -4.27998, -4.11282, -3.94319, -3.77117, -3.59689, -3.42044, -3.24193, -3.06147, -2.87916, -2.69512, -2.50945, -2.32228, -2.1337, -1.94384, -1.75281, -1.56072, -1.3677, -1.17384, -0.979285, -0.784137, -0.588517, -0.392541, -0.19633, engine/h2shared/glheader.h000066400000000000000000000033411444734033100157530ustar00rootroot00000000000000/* glheader.h: opengl system includes */ #ifndef __GLHEADER_H #define __GLHEADER_H #if defined(PLATFORM_WINDOWS) #include #include #elif defined(PLATFORM_OSX) #include #elif defined(PLATFORM_MAC) #include #elif defined(__MORPHOS__) #include #include #elif defined(__AROS__) /* ABIv0, AROSMesa */ #include #elif defined(__amigaos4__) #include #elif defined(PLATFORM_AMIGAOS3) #if defined(REFGL_MINIGL) /* Hyperion's MiniGL 1.2 */ #include #elif defined(REFGL_AMESA) /* StormMesa */ #include #else #error Which Amiga GL API to use not specified #endif #else /* other unix */ #include #endif #ifndef APIENTRY #define APIENTRY #endif /* include our function pointers */ #include "gl_func.h" #ifndef GLX_3DFX_WINDOW_MODE_MESA #define GLX_3DFX_WINDOW_MODE_MESA 0x1 #endif #ifndef GLX_3DFX_FULLSCREEN_MODE_MESA #define GLX_3DFX_FULLSCREEN_MODE_MESA 0x2 #endif #ifndef GL_TEXTURE0_ARB #define GL_TEXTURE0_ARB 0x84C0 #define GL_TEXTURE1_ARB 0x84C1 #define GL_TEXTURE2_ARB 0x84C2 #define GL_TEXTURE3_ARB 0x84C3 #define GL_TEXTURE4_ARB 0x84C4 #define GL_TEXTURE5_ARB 0x84C5 #define GL_ACTIVE_TEXTURE_ARB 0x84E0 #define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 #define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 #endif #ifndef GL_MULTISAMPLE_ARB #define GL_MULTISAMPLE_ARB 0x809D #endif #ifndef GL_SHARED_TEXTURE_PALETTE_EXT #define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB #endif #ifndef GL_COLOR_INDEX8_EXT #define GL_COLOR_INDEX8_EXT 0x80E5 #endif #ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF #endif #endif /* __GLHEADER_H */ engine/h2shared/glquake.h000066400000000000000000000234501444734033100156340ustar00rootroot00000000000000/* glquake.h -- common glquake header * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef GLQUAKE_H #define GLQUAKE_H /* ==================================================================== COMMON DEFINITIONS ================================================================== */ #define MAX_GLTEXTURES 2048 #define MAX_EXTRA_TEXTURES 156 /* 255-100+1 */ #define MAX_CACHED_PICS 256 #define MAX_LIGHTMAPS 128 /* maximum allowed size of a surface * vanilla limit was 16+1 (for linear sampling), * although glquake used 18 for some reason. * bigger values allow for less surfaces in certain places. */ #define MAX_SURFACE_LIGHTMAP 255 #define GL_UNUSED_TEXTURE (~(GLuint)0) #define gl_solid_format 3 #define gl_alpha_format 4 /* # of supported texture filter modes[] (gl_draw.c) */ #define NUM_GL_FILTERS 6 /* defs for palettized textures */ #define INVERSE_PAL_R_BITS 6 #define INVERSE_PAL_G_BITS 6 #define INVERSE_PAL_B_BITS 6 #define INVERSE_PAL_TOTAL_BITS (INVERSE_PAL_R_BITS + INVERSE_PAL_G_BITS + INVERSE_PAL_B_BITS) #define INVERSE_PAL_SIZE (1 << INVERSE_PAL_TOTAL_BITS) /* r_local.h defs */ #define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0) // normalizing factor so player model works // out to about 1 pixel per triangle #define MAX_SKIN_HEIGHT 480 #define BACKFACE_EPSILON 0.01 #define SKYSHIFT 7 #define SKYSIZE (1 << SKYSHIFT) #define SKYMASK (SKYSIZE - 1) /* ==================================================================== ENDIANNESS: RGBA ================================================================== */ #if ENDIAN_RUNTIME_DETECT /* initialized by VID_Init() */ extern unsigned int MASK_r; extern unsigned int MASK_g; extern unsigned int MASK_b; extern unsigned int MASK_a; extern unsigned int MASK_rgb; extern unsigned int SHIFT_r; extern unsigned int SHIFT_g; extern unsigned int SHIFT_b; extern unsigned int SHIFT_a; #else /* ENDIAN_RUNTIME_DETECT */ #if (BYTE_ORDER == BIG_ENDIAN) /* R G B A */ #define MASK_r 0xff000000 #define MASK_g 0x00ff0000 #define MASK_b 0x0000ff00 #define MASK_a 0x000000ff #define SHIFT_r 24 #define SHIFT_g 16 #define SHIFT_b 8 #define SHIFT_a 0 #elif (BYTE_ORDER == LITTLE_ENDIAN) /* A B G R */ #define MASK_r 0x000000ff #define MASK_g 0x0000ff00 #define MASK_b 0x00ff0000 #define MASK_a 0xff000000 #define SHIFT_r 0 #define SHIFT_g 8 #define SHIFT_b 16 #define SHIFT_a 24 #endif #define MASK_rgb (MASK_r|MASK_g|MASK_b) #endif /* ENDIAN_RUNTIME_DETECT */ /* ==================================================================== TYPES ================================================================== */ /* texture types */ typedef struct { GLuint texnum; float sl, tl, sh, th; } glpic_t; typedef struct cachepic_s { char name[MAX_QPATH]; qpic_t pic; byte padding[32]; /* for appended glpic */ } cachepic_t; typedef struct { GLuint texnum; char identifier[MAX_QPATH]; int width, height; int flags; unsigned short crc; } gltexture_t; /* texture filters */ typedef struct { const char *name; int minimize, maximize; } glmode_t; /* particle enums and types: note that hexen2 and hexenworld versions of these are different!! */ #include "particle.h" /* ==================================================================== GLOBAL VARIABLES ================================================================== */ /* gl texture objects */ extern GLuint currenttexture; extern GLuint particletexture; extern GLuint lightmap_textures[MAX_LIGHTMAPS]; extern GLuint playertextures[MAX_CLIENTS]; extern GLuint gl_extra_textures[MAX_EXTRA_TEXTURES]; // generic textures for models /* the GL_Bind macro */ #define GL_Bind(texnum) \ do { \ if (currenttexture != (texnum)) \ { \ currenttexture = (texnum); \ glBindTexture_fp(GL_TEXTURE_2D,currenttexture); \ } \ } while (0) extern int gl_texlevel; extern int numgltextures; extern qboolean flush_textures; extern gltexture_t gltextures[MAX_GLTEXTURES]; extern int gl_filter_idx; extern float gldepthmin, gldepthmax; extern int glx, gly, glwidth, glheight; extern glmode_t gl_texmodes[NUM_GL_FILTERS]; /* hardware-caps related globals */ extern GLint gl_max_size; extern GLfloat gl_max_anisotropy; extern qboolean gl_tex_NPOT; extern qboolean is_3dfx; extern qboolean is8bit; extern qboolean gl_mtexable; extern qboolean have_stencil; /* view origin */ extern vec3_t vup; extern vec3_t vpn; extern vec3_t vright; extern vec3_t r_origin; /* screen size info */ extern refdef_t r_refdef; extern vrect_t scr_vrect; extern mleaf_t *r_viewleaf, *r_oldviewleaf; extern float r_world_matrix[16]; extern entity_t r_worldentity; extern qboolean r_cache_thrash; // compatability extern vec3_t modelorg, r_entorigin; extern int r_visframecount; // ??? what difs? extern int r_framecount; extern mplane_t frustum[4]; extern int c_brush_polys, c_alias_polys; /* palette stuff */ extern const int ColorIndex[16]; extern const unsigned int ColorPercent[16]; extern float RTint[256], GTint[256], BTint[256]; extern unsigned char *inverse_pal; /* global cvars */ extern cvar_t r_norefresh; extern cvar_t r_drawentities; extern cvar_t r_drawworld; extern cvar_t r_drawviewmodel; extern cvar_t r_speeds; extern cvar_t r_waterwarp; extern cvar_t r_fullbright; extern cvar_t r_lightmap; extern cvar_t r_shadows; extern cvar_t r_mirroralpha; extern cvar_t r_wateralpha; extern cvar_t r_skyalpha; extern cvar_t r_dynamic; extern cvar_t r_novis; extern cvar_t r_wholeframe; extern cvar_t r_clearcolor; extern cvar_t r_texture_external; #if defined(H2W) extern cvar_t r_netgraph; extern cvar_t r_entdistance; extern cvar_t r_teamcolor; #endif extern cvar_t gl_playermip; extern cvar_t gl_clear; extern cvar_t gl_cull; extern cvar_t gl_poly; extern cvar_t gl_ztrick; extern cvar_t gl_zfix; extern cvar_t gl_purge_maptex; extern cvar_t gl_smoothmodels; extern cvar_t gl_affinemodels; extern cvar_t gl_polyblend; extern cvar_t gl_keeptjunctions; extern cvar_t gl_reporttjunctions; extern cvar_t gl_flashblend; extern cvar_t gl_nocolors; extern cvar_t gl_waterripple; extern cvar_t gl_glows; extern cvar_t gl_other_glows; extern cvar_t gl_missile_glows; extern cvar_t gl_coloredlight; extern cvar_t gl_colored_dynamic_lights; extern cvar_t gl_extra_dynamic_lights; extern cvar_t gl_lightmapfmt; /* other globals */ extern int gl_coloredstatic; /* value of gl_coloredlight stored at level start */ extern int gl_lightmap_format; /* value of gl_lightmapfmt stored at level start */ extern vec3_t lightcolor; extern vec3_t lightspot; extern texture_t *r_notexture_mip; extern int d_lightstylevalue[256]; // 8.8 fraction of base light value extern byte *playerTranslation; extern const int color_offsets[MAX_PLAYER_CLASS]; extern qboolean mirror; extern mplane_t *mirror_plane; extern int mirrortexturenum; /* quake texturenum, not gltexturenum */ extern int skytexturenum; /* index in cl.loadmodel, not gl texture object */ /* ==================================================================== GLOBAL FUNCTIONS ================================================================== */ void GL_BeginRendering (int *x, int *y, int *width, int *height); void GL_EndRendering (void); void GL_Set2D (void); GLuint GL_LoadTexture (const char *identifier, byte *data, int width, int height, int flags); /* LoadTexture Flags */ #define TEX_DEFAULT 0 #define TEX_MIPMAP (1 << 1) #define TEX_ALPHA (1 << 2) #define TEX_RGBA (1 << 5) /* texture is 32 bit RGBA, not 8 bit */ /* TEX_NEAREST and TEX_LINEAR aren't supposed to be ORed with TEX_MIPMAP */ #define TEX_NEAREST (1 << 6) /* force point sampled */ #define TEX_LINEAR (1 << 7) /* force linear filtering */ /* duplicated EF_ values from gl_model.h: */ #define TEX_TRANSPARENT (1 << 12) /* Transparent sprite */ #define TEX_HOLEY (1 << 14) /* Solid model with color 0 */ #define TEX_SPECIAL_TRANS (1 << 15) /* Translucency through the particle table */ GLuint GL_LoadPicTexture (qpic_t *pic); void D_ClearOpenGLTextures (int last_tex); qboolean R_CullBox (vec3_t mins, vec3_t maxs); void R_DrawBrushModel (entity_t *e, qboolean Translucent); void R_DrawWorld (void); void R_RenderBrushPoly (entity_t *e, msurface_t *fa, qboolean override); void R_RotateForEntity (entity_t *e); void R_StoreEfrags (efrag_t **ppefrag); #if defined(QUAKE2) void R_LoadSkys (void); void R_DrawSkyBox (void); void R_ClearSkyBox (void); #endif void GL_SubdivideSurface (qmodel_t *m, msurface_t *fa); void EmitWaterPolys (msurface_t *fa); void EmitBothSkyLayers (msurface_t *fa); void R_DrawSkyChain (msurface_t *s); void R_DrawWaterSurfaces (void); void R_RenderDlights (void); void R_MarkLights (dlight_t *light, int bit, mnode_t *node); void R_AnimateLight(void); int R_LightPoint (vec3_t p); float R_LightPointColor (vec3_t p); void GL_BuildLightmaps (void); void GL_SetupLightmapFmt (void); void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *hdr); void R_InitParticleTexture (void); void R_InitExtraTextures (void); #if defined(H2W) void R_NetGraph (void); void R_InitNetgraphTexture (void); #endif void R_ReadPointFile_f (void); void R_TranslatePlayerSkin (int playernum); #endif /* GLQUAKE_H */ engine/h2shared/h2config.h000066400000000000000000000243011444734033100156760ustar00rootroot00000000000000/* * h2config.h -- Compile time options for Hexen II: Hammer of Thyrion. * * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HEXEN2_OPTIONS_H #define __HEXEN2_OPTIONS_H /* ==================================================================== ENDIAN_RUNTIME_DETECT Value : 0 or 1 Affects: everywhere in all source using byte order functions. This option has to be edited in the file q_endian.h, NOT here !!! Default is off which is a little bit faster. ================================================================== */ /* ===================================================================== PARANOID Value : not a value, but a define or undef Affects: allows paranoid checks at many places: speed sapping error checking. do NOT enable this unless you REALLY know what you are doing: many of those checks may not be actually necessary and the game would error out at any time! besides, it really decreases the speed. =================================================================== */ #undef PARANOID /* ===================================================================== DO_USERDIRS Value : 0 or 1 Affects: file system, quakefs.c. Allows separating user directories on multi-user systems. We HIGHLY recommend keeping it as 1. Also see "sys.h" where DO_USERDIRS may be disabled on purpose for some platforms. =================================================================== */ #define DO_USERDIRS 1 /* ==================================================================== DISALLOW_DEMONESS_IN_OLD_GAME Value : 0 or 1 Affects: menu.c (player class selections, hexen2 only). Change the define below to 0 if you want to allow the demoness class in the old mission through the menu system. ================================================================== */ #define DISALLOW_DEMONESS_IN_OLD_GAME 1 /* ==================================================================== ENABLE_OLD_RETAIL Value : 0 or 1 Affects: filesystem init. Allow running with the old, pre-1.11 (such as 1.03 cdrom) versions of Hexen II. The game actually seems to run fine with the original cdrom version, but Raven's later patches provided several fixes for map/scripting bugs. Therefore, running with the old version may or may not result in unexpected gameplay behavior. (Here are the maps changed between v1.03 and v1.11: demo2, village1, village2, village3, meso1, meso8, egypt6, rider2c, cath, tower, eidolon, ravdm1, ravdm3, ravdm5. Models: assassin.mdl, ball.mdl, bonelump.mdl, scrbpwng.mdl. Sounds: spider/step1.wav, step2.wav, step3.wav, weapons/ric2.wav.) Default: disabled (0). ================================================================== */ #define ENABLE_OLD_RETAIL 0 /* ==================================================================== ENABLE_OLD_DEMO Value : 0 or 1 Affects: filesystem init. other places. Allow running with the old (original) version of the Hexen II Demo from 28.8.1997: It was class-restricted, paladin and assassin only, as a result it lacked certain models. It didn't include the demo3 (The Mill) level which the later (Nov. 1997, v1.11) of the demo had, but it had the Mill area merged in the demo1 map instead. It lacks certain models that our current progs precache, therefore, it must be run using ITS OWN progs which requires uhexen2 v1.5.2 or newer. Default: disabled (0). ================================================================== */ #define ENABLE_OLD_DEMO 0 /* ==================================================================== H2MP Whether we want the mission pack support to be activated directly. Value : not a value, but a define or undef Affects: quakefs.c (filesystem initialization) Default is no: player must use the -portals command line argument to activate it. If you want direct activation, change the below undef to a define : in that case, player must use the -noportals command line argument to disable mission pack support. ================================================================== */ #undef H2MP /* When building HexenWorld or demo-specific, H2MP mustn't be defined */ #if defined(H2W) || defined(DEMOBUILD) #undef H2MP #endif /* H2W || DEMOBUILD */ /* ==================================================================== ENABLE_BSP2 Value : not a value, but a define or undef Affects: many places regarding world model. Whether we want BSP2 format support. Default is enabled. NOTE: If you change this, remember to change both NASM and MASM versions worlda.inc !!! ================================================================== */ #define ENABLE_BSP2 /* ==================================================================== USE_AOT_FRICTION Value : 0 or 1 Affects: sv_user.c (SV_UserFriction(), hexen2 only) Hexen II v1.11 progs does not initialize the friction properly: always 0. In fact, applying a patch to the progs may solve this issue, but that would leave several unpatched mods as broken. Note: Compared to the AoT solution, the USE_AOT_FRICTION 0 option makes pure hexen2 to feel slightly more slippery. If you want to use the Anvil of Thyrion solution for the original hexen2 friction emulation, then define USE_AOT_FRICTION as 1, otherwise define it as 0. ================================================================== */ #define USE_AOT_FRICTION 0 /* ==================================================================== MGNET Value : not a value, but a define or undef Affects: HexenWorld, server/sv_ents.c :: SV_WritePlayersToClient This doesn't ~seem~ to be in the latest binary release of Raven, but it is in the source release and the macro is defined in qwsv.dsp ie. the MSVC project file. It uses cardioid_rating() and might send an additional server message, svc_playerskipped. The client calls CL_SavePlayer() upon receiving this message (see in cl_parse.c) but that code seems incomplete (see in cl_ents.c). Enabling this option should require bumping the protocol version. Disabled by default. ================================================================== */ #undef MGNET /* ==================================================================== ======================== MEMORY SETUP: =========================== ================================================================== */ /* ==================================================================== The amount of memory needed by Watt-32 (WatTCP) for DOS: - Hexen II server opens a socket for each client, plus it needs its accept socket and broadcast socket, so (MAX_PLAYERS + 3) * 11K is normally the maximum needed memory. - HexenWorld opens one socket, but it sends two large data, i.e. the soundlist and the modellist, in a single chunk, therefore WatTCP needs to allocate some additional memory (~ 2*64K?) to reassemble the fragmented data. Affects: sys_dos.c. ================================================================== */ #define WATT32_NEEDMEM 0x30000 /* 192 K */ /* ==================================================================== If CODECS_USE_ZONE is defined (see the Makefile), then mp3 (libmad) and ogg/vorbis codecs will allocate on the zone instead of system memory. Remember that this requires recompiling the decoder library with proper memory allocator changes to it. DOS builds are example for this case. The memory requirements for individual decoders are defined below. Affects: zone.c. ================================================================== */ #define LIBMAD_NEEDMEM 0x10000 /* 64K is fairly enough for libmad */ #if defined(VORBIS_USE_TREMOR) #define VORBIS_NEEDMEM 0x10000 /* 64K is fairly enough for vorbis */ /* with libvorbisidec (tremor) from the lowmem branch. */ #else #define VORBIS_NEEDMEM 0x60000 /* 400K enough for vorbis most times */ #endif /* ==================================================================== WAL_TEXTURES Value : not a value, but a define or undef Affects: model.c, gl_model.c (opengl & software renderers both.) Support for loading external wal mip textures instead of the ones embedded in the mdl. (cvar: r_texture_external.) ================================================================== */ #undef WAL_TEXTURES /* ==================================================================== FULLSCREEN_INTERMISSIONS Value : 0 or 1 Affects: screen.c, gl_screen.c, menu.c (hexen2 and hexenworld) If you want the intermissions and help screens to be drawn full- screen keep the define below as 1. Otherwise, if you want them to be drawn unscaled with regard to the resolution, change the define below to 0 ================================================================== */ #define FULLSCREEN_INTERMISSIONS 1 /* ==================================================================== ======================= OpenGL OPTIONS: ========================== ================================================================== */ /* ==================================================================== GL_DLSYM Value : not a value, but a define or undef Affects: all gl sources and linkage. This option has to be edited in the Makefile (see the LINK_GL_LIBS option in there), not here. This affects the final linkage of the binary. ================================================================== */ #endif /* __HEXEN2_OPTIONS_H */ engine/h2shared/hashindex.c000066400000000000000000000062151444734033100161510ustar00rootroot00000000000000/* * Fast hash table for indexes and arrays * from the Doom 3 GPL Source Code source adapted for uhexen2. * * Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "quakedef.h" #include "hashindex.h" #define NULL_INDEX (-1) static inline qboolean Hash_IsPowerOfTwo(int x) { return (x > 0) && !(x & (x - 1)); } /* ================ Hash_Allocate ================ */ void Hash_Allocate(hashindex_t *hi, int hashSize) { if (!Hash_IsPowerOfTwo(hashSize)) Sys_Error("%s: has size %d is not power of two", __thisfunc__, hashSize); if (hi->hash != NULL) Sys_Error("%s: hash is already initialized", __thisfunc__); hi->hashSize = hashSize; hi->hash = (int *) Z_Malloc(sizeof(int) * hi->hashSize, Z_MAINZONE); memset(hi->hash, NULL_INDEX, hi->hashSize * sizeof(hi->hash[0])); hi->indexChain = (int *) Z_Malloc(sizeof(int) * hi->hashSize, Z_MAINZONE); memset(hi->indexChain, NULL_INDEX, hi->hashSize * sizeof(hi->indexChain[0])); hi->hashMask = hashSize - 1; } /* ================ Hash_Free free allocated memory ================ */ void Hash_Free(hashindex_t *hi) { if (hi->hash != NULL) { Z_Free(hi->hash); hi->hash = NULL; } if (hi->indexChain != NULL) { Z_Free(hi->indexChain); hi->indexChain = NULL; } } /* ================ Hash_Add add an index to the hash, assumes the index has not yet been added to the hash ================ */ void Hash_Add(hashindex_t *hi, int key, int index) { int h; if (hi->hash == NULL) Sys_Error("%s: hash not initialized", __thisfunc__); if (index < 0 || index >= hi->hashSize) Sys_Error("%s: hash index out of range %d", __thisfunc__, index); h = key & hi->hashMask; hi->indexChain[index] = hi->hash[h]; hi->hash[h] = index; } /* ================ Hash_Remove remove an index from the hash ================ */ void Hash_Remove(hashindex_t *hi, int key, int index) { int i; int k = key & hi->hashMask; if (hi->hash == NULL) Sys_Error("%s: hash not initialized", __thisfunc__); if (hi->hash[k] == index) { hi->hash[k] = hi->indexChain[index]; } else { for (i = hi->hash[k]; i != NULL_INDEX; i = hi->indexChain[i]) { if (hi->indexChain[i] == index) { hi->indexChain[i] = hi->indexChain[index]; break; } } } hi->indexChain[index] = NULL_INDEX; } /* ================ Hash_Clear clear the hash ================ */ void Hash_Clear(hashindex_t *hi) { if (hi->hash == NULL) return; // only clear the hash table because clearing the indexChain is not really needed memset(hi->hash, NULL_INDEX, hi->hashSize * sizeof(hi->hash[0])); } engine/h2shared/hashindex.h000066400000000000000000000044071444734033100161570ustar00rootroot00000000000000/* * Fast hash table for indexes and arrays * from the Doom 3 GPL Source Code source adapted for uhexen2. * * Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef HASHINDEX_H_ #define HASHINDEX_H_ #include "q_ctype.h" typedef struct hashindex_s { int hashSize; int *hash; int *indexChain; int hashMask; } hashindex_t; void Hash_Allocate(hashindex_t *hi, int hashSize); void Hash_Free(hashindex_t *hi); void Hash_Add(hashindex_t *hi, int key, int index); void Hash_Remove(hashindex_t *hi, int key, int index); void Hash_Clear(hashindex_t *hi); /* ================ Hash_First get the first index from the hash, returns -1 if empty hash entry ================ */ static inline int Hash_First(hashindex_t *hi, int key) { return hi->hash[key & hi->hashMask]; } /* ================ Hash_Next get the next index from the hash, returns -1 if at the end of the hash chain ================ */ static inline int Hash_Next(hashindex_t *hi, int index) { //assert(index >= 0 && index < hi->indexSize); return hi->indexChain[index]; } /* ================ Hash_GenerateKeyString ================ */ static inline int Hash_GenerateKeyString(hashindex_t *hi, const char *string, qboolean caseSensitive) { int i, hash = 0; if (caseSensitive) { for (i = 0; *string != '\0'; i++) { hash += (*string++) * (i + 119); } } else { for (i = 0; *string != '\0'; i++) { hash += q_tolower(*string++) * (i + 119); } } return hash & hi->hashMask; } /* ================ Hash_GenerateKeyInt ================ */ static inline int Hash_GenerateKeyInt(hashindex_t *hi, int n) { return n & hi->hashMask; } #endif /* !HASHINDEX_H_ */ engine/h2shared/hashindex.license000066400000000000000000001063011444734033100173460ustar00rootroot00000000000000hashindex.c / hashindex.h adapted from the Doom 3 GPL Source Code, Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. Its GPL v3 license with additional terms follows: GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS ADDITIONAL TERMS APPLICABLE TO THE Doom 3 BFG Edition GPL Source Code. The following additional terms ("Additional Terms") supplement and modify the GNU General Public License, Version 3 ("GPL") applicable to the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). In addition to the terms and conditions of the GPL, the Doom 3 BFG Edition Source Code is subject to the further restrictions below. 1. Replacement of Section 15. Section 15 of the GPL shall be deleted in its entirety and replaced with the following: "15. Disclaimer of Warranty. THE PROGRAM IS PROVIDED WITHOUT ANY WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, TITLE AND MERCHANTABILITY. THE PROGRAM IS BEING DELIVERED OR MADE AVAILABLE "AS IS", "WITH ALL FAULTS" AND WITHOUT WARRANTY OR REPRESENTATION. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION." 2. Replacement of Section 16. Section 16 of the GPL shall be deleted in its entirety and replaced with the following: "16. LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES SHALL ANY COPYRIGHT HOLDER OR ITS AFFILIATES, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, FOR ANY DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, DIRECT, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL OR PUNITIVE DAMAGES ARISING FROM, OUT OF OR IN CONNECTION WITH THE USE OR INABILITY TO USE THE PROGRAM OR OTHER DEALINGS WITH THE PROGRAM(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), WHETHER OR NOT ANY COPYRIGHT HOLDER OR SUCH OTHER PARTY RECEIVES NOTICE OF ANY SUCH DAMAGES AND WHETHER OR NOT SUCH DAMAGES COULD HAVE BEEN FORESEEN." 3. LEGAL NOTICES; NO TRADEMARK LICENSE; ORIGIN. You must reproduce faithfully all trademark, copyright and other proprietary and legal notices on any copies of the Program or any other required author attributions. This license does not grant you rights to use any copyright holder or any other party’s name, logo, or trademarks. Neither the name of the copyright holder or its affiliates, or any other party who modifies and/or conveys the Program may be used to endorse or promote products derived from this software without specific prior written permission. The origin of the Program must not be misrepresented; you must not claim that you wrote the original Program. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original Program. 4. INDEMNIFICATION. IF YOU CONVEY A COVERED WORK AND AGREE WITH ANY RECIPIENT OF THAT COVERED WORK THAT YOU WILL ASSUME ANY LIABILITY FOR THAT COVERED WORK, YOU HEREBY AGREE TO INDEMNIFY, DEFEND AND HOLD HARMLESS THE OTHER LICENSORS AND AUTHORS OF THAT COVERED WORK FOR ANY DAMAEGS, DEMANDS, CLAIMS, LOSSES, CAUSES OF ACTION, LAWSUITS, JUDGMENTS EXPENSES (INCLUDING WITHOUT LIMITATION REASONABLE ATTORNEYS' FEES AND EXPENSES) OR ANY OTHER LIABLITY ARISING FROM, RELATED TO OR IN CONNECTION WITH YOUR ASSUMPTIONS OF LIABILITY. engine/h2shared/host_string.c000066400000000000000000000043721444734033100165430ustar00rootroot00000000000000/* * host_string.c -- * internationalized string resource shared between client and server * * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" static char *host_strings = NULL; static int *host_string_index = NULL; int host_string_count = 0; void Host_LoadStrings (void) { int i, count, start; signed char newline_char; host_strings = (char *)FS_LoadHunkFile ("strings.txt", NULL); if (!host_strings) Host_Error ("%s: couldn't load strings.txt", __thisfunc__); newline_char = -1; for (i = count = 0; host_strings[i] != 0; i++) { if (host_strings[i] == '\r' || host_strings[i] == '\n') { if (newline_char == host_strings[i] || newline_char == -1) { newline_char = host_strings[i]; count++; } } } if (!count) { Host_Error ("%s: no string lines found", __thisfunc__); } host_string_index = (int *)Hunk_AllocName ((count + 1)*sizeof(int), "string_index"); for (i = count = start = 0; host_strings[i] != 0; i++) { if (host_strings[i] == '\r' || host_strings[i] == '\n') { if (newline_char == host_strings[i]) { host_string_index[count] = start; start = i + 1; count++; } else { start++; } host_strings[i] = 0; } #if defined(H2W) /* Hexenworld: translate '^' to * '\n' for indexed prints */ else if (host_strings[i] == '^') { host_strings[i] = '\n'; } #endif /* H2W */ } host_string_count = count; Con_DPrintf("Read in %d string lines\n", count); } const char *Host_GetString (int idx) { return &host_strings[host_string_index[idx]]; } engine/h2shared/host_string.h000066400000000000000000000020211444734033100165350ustar00rootroot00000000000000/* host_string.h - * internationalized string resource shared between client and server * * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef HOST_STRING_H #define HOST_STRING_H extern int host_string_count; void Host_LoadStrings (void); const char *Host_GetString (int idx); #endif /* HOST_STRING_H */ engine/h2shared/in_amiga.c000066400000000000000000000701021444734033100157360ustar00rootroot00000000000000/* in_amiga.c -- Intuition game input code for Amiga & co. * * Copyright (C) 2005-2010 Mark Olsen * Copyright (C) 2012-2016 Szilard Biro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #if defined __AROS__ #include #define NM_WHEEL_UP RAWKEY_NM_WHEEL_UP #define NM_WHEEL_DOWN RAWKEY_NM_WHEEL_DOWN #define NM_BUTTON_FOURTH RAWKEY_NM_BUTTON_FOURTH #elif !defined __MORPHOS__ #include #endif #include #include #include #include #include #include #ifdef __AROS__ #include #elif !defined __MORPHOS__ #include #endif #ifdef __AMIGA__ #include #endif #include "quakedef.h" #ifdef PLATFORM_AMIGAOS3 #include #include #else #include #endif struct Library *LowLevelBase = NULL; #ifdef __CLIB2__ struct Library *KeymapBase = NULL; #endif extern struct Window *window; static struct Interrupt InputHandler; static struct MsgPort *inputport; static struct IOStdReq *inputreq; static UWORD *pointermem; static int mx; static int my; #define MAXIMSGS 32 static struct InputEvent imsgs[MAXIMSGS]; static int imsglow; static int imsghigh; /* mouse variables */ static cvar_t m_filter = {"m_filter", "0", CVAR_NONE}; static int mouse_x, mouse_y, old_mouse_x, old_mouse_y; static qboolean mouseactive = false; static qboolean mouseinitialized = false; static qboolean mouseactivatetoggle = false; /*static qboolean mouseshowtoggle = true;*/ /* joystick support: */ static int joy_port = -1; static int joy_available = 0; static ULONG oldjoyflag = 0; static int joynumaxes = 0; static int joyaxis[4]; #ifdef PLATFORM_AMIGAOS3 static int analog_centered=FALSE; static int analog_clx, analog_cly; static int analog_crx, analog_cry; static struct GamePortTrigger gameport_gpt = { GPTF_UPKEYS | GPTF_DOWNKEYS, /* gpt_Keys */ 0, /* gpt_Timeout */ 1, /* gpt_XDelta */ 1 /* gpt_YDelta */ }; static struct MsgPort *gameport_mp = NULL; static struct IOStdReq *gameport_io = NULL; static struct InputEvent gameport_ie; static int gameport_is_open = FALSE; #endif static cvar_t in_joystick = {"joystick", "1", CVAR_ARCHIVE}; /* enable/disable joystick */ static cvar_t joy_index = {"joy_index", "1", CVAR_NONE}; /* joystick to use when have multiple */ static cvar_t joy_axisforward = {"joy_axisforward", "1", CVAR_NONE}; /* axis for forward/backward movement */ static cvar_t joy_axisside = {"joy_axisside", "0", CVAR_NONE}; /* axis for right/left movement */ static cvar_t joy_axisup = {"joy_axisup", "-1", CVAR_NONE}; /* axis for up/down movement */ static cvar_t joy_axispitch = {"joy_axispitch", "3", CVAR_NONE}; /* axis for looking up/down" */ static cvar_t joy_axisyaw = {"joy_axisyaw", "2", CVAR_NONE}; /* axis for looking right/left */ static cvar_t joy_deadzoneforward = {"joy_deadzoneforward", "0", CVAR_NONE}; /* deadzone tolerance, suggested 0 to 0.01 */ static cvar_t joy_deadzoneside = {"joy_deadzoneside", "0", CVAR_NONE}; /* deadzone tolerance */ static cvar_t joy_deadzoneup = {"joy_deadzoneup", "0", CVAR_NONE}; /* deadzone tolerance */ static cvar_t joy_deadzonepitch = {"joy_deadzonepitch", "0", CVAR_NONE}; /* deadzone tolerance */ static cvar_t joy_deadzoneyaw = {"joy_deadzoneyaw", "0", CVAR_NONE}; /* deadzone tolerance */ static cvar_t joy_sensitivityforward = {"joy_sensitivityforward", "-1", CVAR_NONE}; /* movement multiplier */ static cvar_t joy_sensitivityside = {"joy_sensitivityside", "1", CVAR_NONE}; /* movement multiplier */ static cvar_t joy_sensitivityup = {"joy_sensitivityup", "1", CVAR_NONE}; /* movement multiplier */ static cvar_t joy_sensitivitypitch = {"joy_sensitivitypitch", "1", CVAR_NONE}; /* movement multiplier */ static cvar_t joy_sensitivityyaw = {"joy_sensitivityyaw", "-1", CVAR_NONE}; /* movement multiplier */ /* forward-referenced functions */ static void IN_StartupJoystick (void); static void IN_Callback_JoyEnable (cvar_t *var); static void IN_Callback_JoyIndex (cvar_t *var); #ifdef PLATFORM_AMIGAOS3 static unsigned char keyconv[] = { '`', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', /* 10 */ '-', '=', '\\', 0, K_KP_INS, 'q', 'w', 'e', 'r', 't', /* 20 */ 'y', 'u', 'i', 'o', 'p', '[', ']', 0, K_KP_END, K_KP_DOWNARROW, /* 30 */ K_KP_PGDN, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', /* 40 */ ';', '\'', 0, 0, K_KP_LEFTARROW, K_KP_5, K_KP_RIGHTARROW, '<', 'z', 'x', /* 50 */ 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, K_KP_DEL, /* 60 */ K_KP_HOME, K_KP_UPARROW, K_KP_PGUP, ' ', K_BACKSPACE, K_TAB, K_KP_ENTER, K_ENTER, K_ESCAPE, K_DEL, /* 70 */ 0, 0, 0, K_KP_MINUS, 0, K_UPARROW, K_DOWNARROW, K_RIGHTARROW, K_LEFTARROW, K_F1, /* 80 */ K_F2, K_F3, K_F4, K_F5, K_F6, K_F7, K_F8, K_F9, K_F10, K_KP_NUMLOCK, /* 90 */ 0, K_KP_SLASH, K_KP_STAR, K_KP_PLUS, K_PAUSE, K_SHIFT, K_SHIFT, 0, K_CTRL, K_ALT, /* 100 */ K_ALT, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 110 */ }; #else static unsigned char keyconv[] = { '`', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', /* 10 */ '-', '=', 0, 0, K_KP_INS, 'q', 'w', 'e', 'r', 't', /* 20 */ 'y', 'u', 'i', 'o', 'p', '[', ']', 0, K_KP_END, K_KP_DOWNARROW, /* 30 */ K_KP_PGDN, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', /* 40 */ ';', '\'', '\\', 0, K_KP_LEFTARROW, K_KP_5, K_KP_RIGHTARROW, '<', 'z', 'x', /* 50 */ 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, K_KP_DEL, /* 60 */ K_KP_HOME, K_KP_UPARROW, K_KP_PGUP, ' ', K_BACKSPACE, K_TAB, K_KP_ENTER, K_ENTER, K_ESCAPE, K_DEL, /* 70 */ K_INS, K_PGUP, K_PGDN, K_KP_MINUS, K_F11, K_UPARROW, K_DOWNARROW, K_RIGHTARROW, K_LEFTARROW, K_F1, /* 80 */ K_F2, K_F3, K_F4, K_F5, K_F6, K_F7, K_F8, K_F9, K_F10, 0, /* 90 */ 0, K_KP_SLASH, K_KP_STAR, K_KP_PLUS, 0, K_SHIFT, K_SHIFT, 0, K_CTRL, K_ALT, /* 100 */ K_ALT, 0, 0, 0, 0, 0, 0, 0, K_KP_NUMLOCK, K_PAUSE, /* 110 */ K_F12, K_HOME, K_END, 0, 0, 0, 0, 0, 0, 0, /* 120 */ }; #endif #define MAX_KEYCONV (sizeof keyconv / sizeof keyconv[0]) /* =========== Force_CenterView_f =========== */ static void Force_CenterView_f (void) { cl.viewangles[PITCH] = 0; } /* =========== IN_ShowMouse =========== */ void IN_ShowMouse (void) { if (window && window->Pointer == pointermem) { ClearPointer(window); //Con_Printf("IN_ShowMouseOK\n"); } } /* =========== IN_HideMouse =========== */ void IN_HideMouse (void) { if (window && pointermem && window->Pointer != pointermem) { SetPointer(window, pointermem, 1, 1, 0, 0); //Con_Printf("IN_HideMouseOK\n"); } } /* ============================================================ NOTES on enabling-disabling the mouse: - In windowed mode, mouse is temporarily disabled in main menu, so the un-grabbed pointer can be used for desktop This state is stored in menu_disabled_mouse as true - In fullscreen mode, we don't disable the mouse in menus, if we toggle windowed/fullscreen, the above state variable is used to correct this in VID_ToggleFullscreen() - In the console mode and in the options menu-group, mouse is not disabled, and menu_disabled_mouse is set to false - Starting a or connecting to a server activates the mouse and sets menu_disabled_mouse to false - Pausing the game disables (so un-grabs) the mouse, unpausing activates it. We don't play with menu_disabled_mouse in such cases */ /* =========== IN_ActivateMouse =========== */ void IN_ActivateMouse (void) { mouseactivatetoggle = true; if (mouseinitialized && _enable_mouse.integer) { IN_HideMouse(); mouseactive = true; } } /* =========== IN_DeactivateMouse =========== */ void IN_DeactivateMouse (void) { mouseactivatetoggle = false; if (mouseinitialized) { IN_ShowMouse(); mouseactive = false; } } /* =========== IN_StartupMouse =========== */ static void IN_StartupMouse (void) { mx = my = old_mouse_x = old_mouse_y = 0; /* IN_HideMouse ();*/ if (safemode || COM_CheckParm ("-nomouse")) { IN_DeactivateMouse(); return; } mouseinitialized = true; // if a fullscreen video mode was set before the mouse was initialized, // set the mouse state appropriately if (mouseactivatetoggle) IN_ActivateMouse (); else IN_DeactivateMouse (); } /* =================== IN_ClearStates =================== */ void IN_ClearStates (void) { } /* =================== IN_KeyboardHandler =================== */ static void IN_AddEvent(struct InputEvent *coin) { if ((imsghigh > imsglow && !(imsghigh == MAXIMSGS - 1 && imsglow == 0)) || (imsghigh < imsglow && imsghigh != imsglow - 1) || (imsglow == imsghigh)) { memcpy(&imsgs[imsghigh], coin, sizeof(struct InputEvent)); imsghigh++; imsghigh %= MAXIMSGS; } } static struct InputEvent *IN_GetNextEvent(void) { struct InputEvent *ie = NULL; if (imsglow != imsghigh) { ie = &imsgs[imsglow]; imsglow++; imsglow %= MAXIMSGS; } return ie; } #ifdef __MORPHOS__ /* MorphOS SDI handler macros are messed up */ static struct InputEvent *IN_KeyboardHandlerFunc(void); static struct EmulLibEntry IN_KeyboardHandler = { TRAP_LIB, 0, (void (*)(void))IN_KeyboardHandlerFunc }; static struct InputEvent *IN_KeyboardHandlerFunc() { struct InputEvent *moo = (struct InputEvent *)REG_A0; //struct inputdata *id = (struct inputdata *)REG_A1; #else #if defined(__AROS__) && !defined(HANDLERPROTO) /* ABIv1 ? */ #define HANDLERPROTO(name, ret, obj, data) \ SAVEDS ASM ret name(REG(a0, obj), REG(a1, data)) #endif HANDLERPROTO(IN_KeyboardHandler, struct InputEvent *, struct InputEvent *moo, APTR id) { #endif struct InputEvent *coin; ULONG screeninfront, handlemouse; if (!window || !(window->Flags & WFLG_WINDOWACTIVE)) return moo; if (window->WScreen) { #ifdef __MORPHOS__ if (IntuitionBase->LibNode.lib_Version > 50 || (IntuitionBase->LibNode.lib_Version == 50 && IntuitionBase->LibNode.lib_Revision >= 56)) GetAttr(SA_Displayed, window->WScreen, &screeninfront); else #endif screeninfront = (window->WScreen == IntuitionBase->FirstScreen); } else screeninfront = 1; handlemouse = screeninfront && mouseactive; for (coin = moo; coin; coin = coin->ie_NextEvent) { if (coin->ie_Class == IECLASS_RAWKEY) { int code; // mouse button 4, mouse wheel and keyboard code = coin->ie_Code & ~IECODE_UP_PREFIX; if (code >= NM_WHEEL_UP && code <= NM_BUTTON_FOURTH) { // we don't need these, they will be handled under IECLASS_NEWMOUSE /*if (mouseactive && screeninfront) { IN_AddEvent(coin); coin->ie_Code = IECODE_NOBUTTON; }*/ } else { IN_AddEvent(coin); } } else if (handlemouse) { if (coin->ie_Class == IECLASS_RAWMOUSE) { // mouse buttons 1-3 if (coin->ie_Code != IECODE_NOBUTTON) { IN_AddEvent(coin); coin->ie_Code = IECODE_NOBUTTON; } // mouse movement mx += coin->ie_position.ie_xy.ie_x; my += coin->ie_position.ie_xy.ie_y; coin->ie_position.ie_xy.ie_x = 0; coin->ie_position.ie_xy.ie_y = 0; } else if (coin->ie_Class == IECLASS_NEWMOUSE) { // mouse button 4, mouse wheel IN_AddEvent(coin); coin->ie_Code = IECODE_NOBUTTON; } } } return moo; } //MakeHandlerPri(InputHandler, &IN_KeyboardHandler, "Hexen II input handler", NULL, 100); /* =========== IN_Init =========== */ void IN_Init (void) { static char handler_name[] = "Hexen II input handler"; /* mouse variables */ Cvar_RegisterVariable (&m_filter); /* joystick variables */ Cvar_RegisterVariable (&in_joystick); Cvar_RegisterVariable (&joy_index); Cvar_RegisterVariable (&joy_axisforward); Cvar_RegisterVariable (&joy_axisside); Cvar_RegisterVariable (&joy_axisup); Cvar_RegisterVariable (&joy_axispitch); Cvar_RegisterVariable (&joy_axisyaw); Cvar_RegisterVariable (&joy_deadzoneforward); Cvar_RegisterVariable (&joy_deadzoneside); Cvar_RegisterVariable (&joy_deadzoneup); Cvar_RegisterVariable (&joy_deadzonepitch); Cvar_RegisterVariable (&joy_deadzoneyaw); Cvar_RegisterVariable (&joy_sensitivityforward); Cvar_RegisterVariable (&joy_sensitivityside); Cvar_RegisterVariable (&joy_sensitivityup); Cvar_RegisterVariable (&joy_sensitivitypitch); Cvar_RegisterVariable (&joy_sensitivityyaw); Cvar_SetCallback (&in_joystick, IN_Callback_JoyEnable); Cvar_SetCallback (&joy_index, IN_Callback_JoyIndex); Cmd_AddCommand ("force_centerview", Force_CenterView_f); pointermem = (UWORD *) AllocVec(2 * 6, MEMF_CHIP | MEMF_CLEAR); IN_StartupMouse (); IN_StartupJoystick (); imsglow = imsghigh = 0; inputport = CreateMsgPort(); if (inputport) { inputreq = (struct IOStdReq *) CreateIORequest(inputport, sizeof(*inputreq)); if (inputreq) { if (!OpenDevice("input.device", 0, (struct IORequest *)inputreq, 0)) { InputHandler.is_Node.ln_Type = NT_INTERRUPT; InputHandler.is_Node.ln_Pri = 100; InputHandler.is_Node.ln_Name = handler_name; InputHandler.is_Code = (void (*)())&IN_KeyboardHandler; inputreq->io_Data = (void *)&InputHandler; inputreq->io_Command = IND_ADDHANDLER; DoIO((struct IORequest *)inputreq); return; } DeleteIORequest(inputreq); } DeleteMsgPort(inputport); } Sys_Error ("Couldn't install input handler"); } /* =========== IN_Shutdown =========== */ void IN_Shutdown (void) { if (inputreq) { inputreq->io_Data = (void *)&InputHandler; inputreq->io_Command = IND_REMHANDLER; DoIO((struct IORequest *)inputreq); CloseDevice((struct IORequest *)inputreq); DeleteIORequest(inputreq); } if (inputport) { DeleteMsgPort(inputport); } IN_DeactivateMouse (); /*IN_ShowMouse ();*/ if (pointermem) { FreeVec(pointermem); } mouseinitialized = false; if (LowLevelBase) { CloseLibrary(LowLevelBase); LowLevelBase = NULL; } #ifdef PLATFORM_AMIGAOS3 if (gameport_is_open) { BYTE gameport_ct = GPCT_NOCONTROLLER; AbortIO((struct IORequest *)gameport_io); WaitIO((struct IORequest *)gameport_io); gameport_io->io_Command = GPD_SETCTYPE; gameport_io->io_Length = 1; gameport_io->io_Data = &gameport_ct; DoIO((struct IORequest *)gameport_io); CloseDevice((struct IORequest *)gameport_io); gameport_is_open = FALSE; } if (gameport_io != NULL) { DeleteIORequest((struct IORequest *)gameport_io); gameport_io = NULL; } if (gameport_mp != NULL) { DeleteMsgPort(gameport_mp); gameport_mp = NULL; } #endif joy_port = -1; joy_available = 0; oldjoyflag = 0; #ifdef __CLIB2__ if (KeymapBase) { CloseLibrary(KeymapBase); KeymapBase = NULL; } #endif } /* =========== IN_ReInit =========== */ void IN_ReInit (void) { IN_StartupMouse (); } /* =========== IN_MouseMove =========== */ static void IN_MouseMove (usercmd_t *cmd/*, int mx, int my*/) { if (m_filter.integer) { mouse_x = (mx + old_mouse_x) * 0.5; mouse_y = (my + old_mouse_y) * 0.5; } else { mouse_x = mx; mouse_y = my; } old_mouse_x = mx; old_mouse_y = my; mouse_x *= sensitivity.value; mouse_y *= sensitivity.value; /* add mouse X/Y movement to cmd */ if ( (in_strafe.state & 1) || (lookstrafe.integer && (in_mlook.state & 1) )) cmd->sidemove += m_side.value * mouse_x; else cl.viewangles[YAW] -= m_yaw.value * mouse_x; if (in_mlook.state & 1) { if (mx || my) V_StopPitchDrift (); } if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) { cl.viewangles[PITCH] += m_pitch.value * mouse_y; if (cl.viewangles[PITCH] > 80) cl.viewangles[PITCH] = 80; if (cl.viewangles[PITCH] < -70) cl.viewangles[PITCH] = -70; } else { if ((in_strafe.state & 1) && (cl.v.movetype == MOVETYPE_NOCLIP)) cmd->upmove -= m_forward.value * mouse_y; else cmd->forwardmove -= m_forward.value * mouse_y; } if (cl.idealroll == 0) /* Did keyboard set it already?? */ { if (cl.v.movetype == MOVETYPE_FLY) { if (mouse_x < 0) cl.idealroll = -10; else if (mouse_x > 0) cl.idealroll = 10; } } } static void IN_DiscardMove (void) { if (mouseinitialized) { mx = my = old_mouse_x = old_mouse_y = 0; } } static float IN_JoystickGetAxis (int axis, float sensitivity, float deadzone) { float value; if (axis < 0 || axis >= joynumaxes) return 0; /* no such axis on this joystick */ value = joyaxis[axis] * (1.0f / 128.0f); if (value < -1) value = -1; else if (value > 1) value = 1; if (fabs(value) < deadzone) return 0; /* within deadzone around center */ return value * sensitivity; } /* =========== IN_JoyMove =========== */ static void IN_JoyMove (usercmd_t *cmd) { float speed, aspeed; float value; if (window->WScreen != IntuitionBase->FirstScreen) return; if (!joynumaxes) return; if (in_speed.state & 1) speed = cl_movespeedkey.value; else speed = 1; aspeed = speed * host_frametime; /* axes */ value = IN_JoystickGetAxis(joy_axisforward.integer, joy_sensitivityforward.value, joy_deadzoneforward.value); value *= speed * 200; /*cl_forwardspeed.value*/ cmd->forwardmove += value; value = IN_JoystickGetAxis(joy_axisside.integer, joy_sensitivityside.value, joy_deadzoneside.value); value *= speed * 225; /*cl_sidespeed.value*/ cmd->sidemove += value; value = IN_JoystickGetAxis(joy_axisup.integer, joy_sensitivityup.value, joy_deadzoneup.value); value *= speed * 200; /*cl_upspeed.value*/ cmd->upmove += value; value = IN_JoystickGetAxis(joy_axispitch.integer, joy_sensitivitypitch.value, joy_deadzonepitch.value); value *= aspeed * cl_pitchspeed.value; cl.viewangles[PITCH] += value; if (value) V_StopPitchDrift (); value = IN_JoystickGetAxis(joy_axisyaw.integer, joy_sensitivityyaw.value, joy_deadzoneyaw.value); value *= aspeed * cl_yawspeed.value; cl.viewangles[YAW] += value; /* bounds check pitch */ if (cl.viewangles[PITCH] > 80.0) cl.viewangles[PITCH] = 80.0; if (cl.viewangles[PITCH] < -70.0) cl.viewangles[PITCH] = -70.0; } /* =========== IN_Move =========== */ void IN_Move (usercmd_t *cmd) { if (cl.v.cameramode) { /* stuck in a different camera so don't move */ memset (cmd, 0, sizeof(*cmd)); /* ignore any mouse movements in camera mode */ IN_DiscardMove (); return; } if (mx != 0 || my != 0) { IN_MouseMove (cmd/*, x, y*/); mx = my = 0; } IN_JoyMove (cmd); } static const char *JoystickName(int port) { ULONG joyflag = ReadJoyPort(port); switch (joyflag & JP_TYPE_MASK) { case JP_TYPE_GAMECTLR: return "game controller"; case JP_TYPE_JOYSTK: return "joystick controller"; case JP_TYPE_MOUSE: return "mouse"; case JP_TYPE_NOTAVAIL: return "not available"; case JP_TYPE_UNKNOWN: return "unknown device"; default: return ""; } } /* =============== IN_StartupJoystick =============== */ static void IN_StartupJoystick (void) { int i; if (safemode || COM_CheckParm ("-nojoy")) return; #ifdef __CLIB2__ KeymapBase = OpenLibrary("keymap.library", 37); #endif #ifdef PLATFORM_AMIGAOS3 if ((gameport_mp = CreateMsgPort())) { if ((gameport_io = (struct IOStdReq *)CreateIORequest(gameport_mp, sizeof(struct IOStdReq)))) { BYTE gameport_ct; for (i=0; i<4; i++) { if (!OpenDevice((STRPTR)"psxport.device", i, (struct IORequest *)gameport_io, 0)) { Con_Printf("psxport.device unit %d opened.\n", i); Forbid(); gameport_io->io_Command = GPD_ASKCTYPE; gameport_io->io_Length = 1; gameport_io->io_Data = &gameport_ct; DoIO((struct IORequest *)gameport_io); if (gameport_ct == GPCT_NOCONTROLLER) { gameport_ct = GPCT_ALLOCATED; gameport_io->io_Command = GPD_SETCTYPE; gameport_io->io_Length = 1; gameport_io->io_Data = &gameport_ct; DoIO((struct IORequest *)gameport_io); Permit(); gameport_io->io_Command = GPD_SETTRIGGER; gameport_io->io_Length = sizeof(struct GamePortTrigger); gameport_io->io_Data = &gameport_gpt; DoIO((struct IORequest *)gameport_io); gameport_io->io_Command = GPD_READEVENT; gameport_io->io_Length = sizeof(struct InputEvent); gameport_io->io_Data = &gameport_ie; SendIO((struct IORequest *)gameport_io); gameport_is_open = TRUE; joynumaxes = 4; joy_available = 1; break; } else { Permit(); Con_Printf("psxport.device unit %d in use.\n", i); CloseDevice((struct IORequest *)gameport_io); } } else { Con_Printf("psxport.device unit %d won't open.\n", i); } } } } #endif if (!LowLevelBase) LowLevelBase = OpenLibrary("lowlevel.library", 37); if (LowLevelBase) { /* ULONG joyflag; joy_available = 0; while (joy_available < 4) { joyflag = ReadJoyPort(joy_available); if ((joyflag & JP_TYPE_MASK) == JP_TYPE_NOTAVAIL) break; joy_available++; } */ joynumaxes = 0; #ifndef PLATFORM_AMIGAOS3 if (LowLevelBase->lib_Version > 50 || (LowLevelBase->lib_Version >= 50 && LowLevelBase->lib_Revision >= 17)) joynumaxes = 2; #endif joy_available = 4; } else { Con_Printf("Unable to open lowlevel.library\n"); return; } /* if (joy_available == 0) { CloseLibrary(LowLevelBase); LowLevelBase = NULL; Con_Printf ("No joystick devices found\n"); return; } */ Con_Printf ("lowlevel.library: %d devices are reported\n", joy_available); #ifndef PLATFORM_AMIGAOS3 for (i = 0; i < joy_available; i++) { Con_Printf("#%d: \"%s\"\n", i, JoystickName(i)); } #endif if (in_joystick.integer) IN_Callback_JoyIndex(&joy_index); } static void IN_Callback_JoyEnable (cvar_t *var) { if (!LowLevelBase) return; if (var->integer) IN_Callback_JoyIndex(&joy_index); else joy_port = -1; } static void IN_Callback_JoyIndex (cvar_t *var) { int idx = var->integer; if (!LowLevelBase) return; if (idx < 0 || idx >= joy_available) { Con_Printf ("joystick #%d doesn't exist\n", idx); } else if (in_joystick.integer) { //ULONG joyflag; //joy_port = -1; //joyflag = ReadJoyPort(idx); //if (joyflag & (JP_TYPE_GAMECTLR | JP_TYPE_JOYSTK)) joy_port = idx; if (joy_port != -1) { Con_Printf("joystick open "); Con_Printf("#%d: \"%s\"\n", idx, JoystickName(idx)); } else { Con_Printf("cannot open joystick, unsupported type: %s\n", JoystickName(idx)); } } } #define Check_Joy_Event(key, flag, oldflag, mask) \ if ((flag & mask) != (oldflag & mask)) { Key_Event(key, (flag & mask) ? true : false); } static void IN_HandleJoystick (void) { ULONG joyflag; if (window->WScreen != IntuitionBase->FirstScreen) return; #ifdef PLATFORM_AMIGAOS3 if (gameport_is_open) { if (GetMsg(gameport_mp) != NULL) { if ((PSX_CLASS(gameport_ie) == PSX_CLASS_JOYPAD) || (PSX_CLASS(gameport_ie) == PSX_CLASS_WHEEL)) analog_centered = FALSE; if (PSX_CLASS(gameport_ie) != PSX_CLASS_MOUSE) { ULONG joyflag = ~PSX_BUTTONS(gameport_ie); if (joyflag != oldjoyflag) { ULONG oldflag = oldjoyflag; Check_Joy_Event(K_JOY1, joyflag, oldflag, PSX_TRIANGLE); Check_Joy_Event(K_JOY2, joyflag, oldflag, PSX_CIRCLE); Check_Joy_Event(K_JOY3, joyflag, oldflag, PSX_CROSS); Check_Joy_Event(K_JOY4, joyflag, oldflag, PSX_SQUARE); Check_Joy_Event(K_AUX1, joyflag, oldflag, PSX_START); Check_Joy_Event(K_AUX2, joyflag, oldflag, PSX_SELECT); Check_Joy_Event(K_AUX3, joyflag, oldflag, PSX_L1); Check_Joy_Event(K_AUX4, joyflag, oldflag, PSX_R1); Check_Joy_Event(K_AUX5, joyflag, oldflag, PSX_L2); Check_Joy_Event(K_AUX6, joyflag, oldflag, PSX_R2); Check_Joy_Event(K_AUX7, joyflag, oldflag, PSX_L3); Check_Joy_Event(K_AUX8, joyflag, oldflag, PSX_R3); Check_Joy_Event(K_AUX29, joyflag, oldflag, PSX_UP); Check_Joy_Event(K_AUX30, joyflag, oldflag, PSX_DOWN); Check_Joy_Event(K_AUX31, joyflag, oldflag, PSX_LEFT); Check_Joy_Event(K_AUX32, joyflag, oldflag, PSX_RIGHT); oldjoyflag = joyflag; } } if ((PSX_CLASS(gameport_ie) == PSX_CLASS_ANALOG) || (PSX_CLASS(gameport_ie) == PSX_CLASS_ANALOG2) || (PSX_CLASS(gameport_ie) == PSX_CLASS_ANALOG_MODE2)) { int analog_lx = PSX_LEFTX(gameport_ie); int analog_ly = PSX_LEFTY(gameport_ie); int analog_rx = PSX_RIGHTX(gameport_ie); int analog_ry = PSX_RIGHTY(gameport_ie); if (!analog_centered) { analog_clx = analog_lx; analog_cly = analog_ly; analog_crx = analog_rx; analog_cry = analog_ry; analog_centered = TRUE; } joyaxis[0] = analog_lx - analog_clx; joyaxis[1] = analog_ly - analog_cly; joyaxis[2] = analog_rx - analog_crx; joyaxis[3] = analog_ry - analog_cry; } gameport_io->io_Command = GPD_READEVENT; gameport_io->io_Length = sizeof(struct InputEvent); gameport_io->io_Data = &gameport_ie; SendIO((struct IORequest *)gameport_io); } return; } #endif if (!LowLevelBase || joy_port == -1) return; joyflag = ReadJoyPort(joy_port); if (joyflag != oldjoyflag) { ULONG oldflag = oldjoyflag; Check_Joy_Event(K_JOY1, joyflag, oldflag, JPF_BUTTON_BLUE); Check_Joy_Event(K_JOY2, joyflag, oldflag, JPF_BUTTON_RED); Check_Joy_Event(K_JOY3, joyflag, oldflag, JPF_BUTTON_YELLOW); Check_Joy_Event(K_JOY4, joyflag, oldflag, JPF_BUTTON_GREEN); Check_Joy_Event(K_AUX1, joyflag, oldflag, JPF_BUTTON_FORWARD); Check_Joy_Event(K_AUX2, joyflag, oldflag, JPF_BUTTON_REVERSE); Check_Joy_Event(K_AUX3, joyflag, oldflag, JPF_BUTTON_PLAY); Check_Joy_Event(K_AUX29, joyflag, oldflag, JPF_JOY_UP); Check_Joy_Event(K_AUX30, joyflag, oldflag, JPF_JOY_DOWN); Check_Joy_Event(K_AUX31, joyflag, oldflag, JPF_JOY_LEFT); Check_Joy_Event(K_AUX32, joyflag, oldflag, JPF_JOY_RIGHT); oldjoyflag = joyflag; } #ifndef PLATFORM_AMIGAOS3 if (joynumaxes > 0) { joyflag = ReadJoyPort(joy_port + JP_ANALOGUE_PORT_MAGIC); if (joyflag & JP_TYPE_ANALOGUE) { joyaxis[0] = (int)(joyflag & JP_XAXIS_MASK) - 128; joyaxis[1] = (int)((joyflag & JP_YAXIS_MASK) >> 8) - 128; } } #endif } /* =========== IN_Commands =========== */ void IN_Commands (void) { /* all button events handled by IN_SendKeyEvents() */ } void IN_SendKeyEvents (void) { struct IntuiMessage *intuimsg; struct InputEvent *inputev; int sym, state, code; if (!window) return;/* dedicated server? */ while ((intuimsg = (struct IntuiMessage *) GetMsg(window->UserPort))) { switch (intuimsg->Class) { case IDCMP_ACTIVEWINDOW: S_UnblockSound(); break; case IDCMP_INACTIVEWINDOW: S_BlockSound(); break; case IDCMP_CLOSEWINDOW: CL_Disconnect (); Sys_Quit (); break; } ReplyMsg((struct Message *)intuimsg); } while ((inputev = IN_GetNextEvent())) { sym = 0; code = inputev->ie_Code & ~IECODE_UP_PREFIX; state = !(inputev->ie_Code & IECODE_UP_PREFIX); if (inputev->ie_Class == IECLASS_RAWKEY) { if (!Key_IsGameKey()) { UBYTE bufascii; if (KeymapBase && MapRawKey(inputev, (STRPTR) &bufascii, sizeof(bufascii), NULL) > 0) { //Con_Printf("%d\n", bufascii); sym = (bufascii == 8) ? K_BACKSPACE : bufascii; } } if (!sym && code < MAX_KEYCONV) sym = keyconv[code]; //Con_Printf("rawkey code %d sym %d state %d\n", code, sym, state); } else if (inputev->ie_Class == IECLASS_RAWMOUSE) { switch (code) { case IECODE_LBUTTON: sym = K_MOUSE1; break; case IECODE_RBUTTON: sym = K_MOUSE2; break; case IECODE_MBUTTON: sym = K_MOUSE3; break; } //Con_Printf("rawmouse code %d sym %d state %d\n", code, sym, state); } else if (inputev->ie_Class == IECLASS_NEWMOUSE) { switch (code) { case NM_WHEEL_UP: sym = K_MWHEELUP; break; case NM_WHEEL_DOWN: sym = K_MWHEELDOWN; break; case NM_BUTTON_FOURTH: sym = K_MOUSE4; break; } //Con_Printf("newmouse code %d sym %d state %d\n", code, sym, state); } if (sym) { if (sym == K_MWHEELUP || sym == K_MWHEELDOWN) { /* the mouse wheel doesn't generate a key up event */ Key_Event(sym, true); Key_Event(sym, false); } else { Key_Event(sym, state); } } } IN_HandleJoystick(); } engine/h2shared/in_dos.c000066400000000000000000000352121444734033100154500ustar00rootroot00000000000000/* in_dos.c -- dos input devices code. * from quake1 source with minor adaptations for uhexen2. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include #include "dosisms.h" #define AUX_FLAG_FREELOOK 0x00000001 typedef struct { long interruptVector; char deviceName[16]; long numAxes; long numButtons; long flags; vec3_t viewangles; // intended velocities float forwardmove; float sidemove; float upmove; long buttons; } externControl_t; #if 0 #define AUX_FLAG_FORCEFREELOOK 0x00000001 /* r/o */ #define AUX_FLAG_EXTENDED 0x00000002 /* r/o */ #define AUX_FLAG_RUN 0x00000004 /* w/o */ #define AUX_FLAG_STRAFE 0x00000008 /* w/o */ #define AUX_FLAG_FREELOOK 0x00000010 /* w/o */ #define AUX_MAP_UNDEFINED 0 #define AUX_MAP_PITCH 1 #define AUX_MAP_YAW 2 #define AUX_MAP_ROLL 3 #define AUX_MAP_FORWARD 4 #define AUX_MAP_SIDE 5 #define AUX_MAP_UP 6 typedef struct { long interruptVector; /* r/o */ char deviceName[16]; /* r/o */ long numAxes; /* r/o 1-6 */ long numButtons; /* r/o 0-32 */ long flags; /* see above */ byte axisMapping[6]; /* w/o default = p,y,r,f,s,u */ float axisValue[6]; /* r/w */ float sensitivity[6]; /* w/o default = 1.0 */ long buttons; /* r/o */ float last_frame_time; /* w/o */ } externControl_t; #endif // mouse variables static cvar_t m_filter = {"m_filter", "1", CVAR_NONE}; static qboolean mouse_avail; static qboolean mouseactive; static qboolean mouse_wheel; static int mouse_buttons; static int mouse_oldbuttonstate; static int mouse_buttonstate; static int mouse_wheelcounter; static float mouse_x, mouse_y; static float old_mouse_x, old_mouse_y; static void IN_ReadMouseMove (int *, int *); // joystick defines and variables static cvar_t in_joystick = {"joystick", "1", CVAR_ARCHIVE}; static cvar_t joy_numbuttons = {"joybuttons", "4", CVAR_ARCHIVE}; static qboolean joy_avail; static int joy_oldbuttonstate; static int joy_buttonstate; static int joyxl, joyxh, joyyl, joyyh; static int joystickx, joysticky; static qboolean extern_avail; static int extern_buttons; static int extern_oldbuttonstate; static int extern_buttonstate; static cvar_t aux_look = {"auxlook", "1", CVAR_ARCHIVE}; static externControl_t *extern_control; static void IN_StartupExternal (void); static void IN_ExternalMove (usercmd_t *cmd); static void IN_StartupJoystick (void); static qboolean IN_ReadJoystick (int *x, int *y); static void Toggle_AuxLook_f (void) { Cvar_SetQuick (&aux_look, (aux_look.integer) ? "0" : "1"); } static void Force_CenterView_f (void) { cl.viewangles[PITCH] = 0; } /* =========== IN_StartupMouse =========== */ static void IN_StartupMouse (void) { if (safemode || COM_CheckParm ("-nomouse")) return; // check for mouse regs.x.ax = 0; dos_int86(0x33); mouse_avail = regs.x.ax; if (!mouse_avail) { Con_Printf ("No mouse found\n"); return; } mouse_buttons = regs.x.bx; if (mouse_buttons > 3) mouse_buttons = 3; Con_Printf("%d-button mouse available\n", mouse_buttons); mouseactive = true; if (COM_CheckParm ("-nowheel")) return; regs.x.ax = 0x11; dos_int86(0x33); if (regs.x.ax == 0x574D && regs.h.cl == 1) { mouse_wheel = true; Con_Printf("mouse wheel support available\n"); } } void IN_ActivateMouse (void) { if (mouse_avail) { old_mouse_x = old_mouse_y = 0; IN_ReadMouseMove (NULL, NULL); mouseactive = true; } } void IN_DeactivateMouse (void) { mouseactive = false; } void IN_ShowMouse (void) { } void IN_HideMouse (void) { } void IN_ClearStates (void) { } /* =========== IN_Init =========== */ void IN_Init (void) { int i; Cvar_RegisterVariable (&m_filter); Cvar_RegisterVariable (&in_joystick); Cvar_RegisterVariable (&joy_numbuttons); Cvar_RegisterVariable (&aux_look); Cmd_AddCommand ("toggle_auxlook", Toggle_AuxLook_f); Cmd_AddCommand ("force_centerview", Force_CenterView_f); IN_StartupMouse (); IN_StartupJoystick (); i = COM_CheckParm ("-control"); if (i && i < com_argc - 1 && !safemode) { /* this needs the controller address as the argument, yes?? - O.S. */ extern_control = (externControl_t *) real2ptr(atoi (com_argv[i+1])); IN_StartupExternal (); } } /* =========== IN_Shutdown =========== */ void IN_Shutdown (void) { } /* =========== IN_Commands =========== */ void IN_Commands (void) { int i; if (mouse_avail) { regs.x.ax = 3; // read buttons dos_int86(0x33); mouse_buttonstate = regs.x.bx; // regs.h.bl mouse_wheelcounter = (signed char) regs.h.bh; // perform button actions for (i = 0; i < mouse_buttons; i++) { if ( (mouse_buttonstate & (1< 0) { Key_Event (K_MWHEELDOWN, true); Key_Event (K_MWHEELDOWN, false); } } mouse_oldbuttonstate = mouse_buttonstate; } if (joy_avail) { joy_buttonstate = ((dos_inportb(0x201) >> 4) & 15) ^ 15; // perform button actions for (i = 0; i < joy_numbuttons.integer; i++) { if ( (joy_buttonstate & (1<buttons; // perform button actions for (i = 0; i < extern_buttons; i++) { if ( (extern_buttonstate & (1<sidemove += m_side.value * mouse_x; else cl.viewangles[YAW] -= m_yaw.value * mouse_x; if (in_mlook.state & 1) V_StopPitchDrift (); if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) { cl.viewangles[PITCH] += m_pitch.value * mouse_y; if (cl.viewangles[PITCH] > 80) cl.viewangles[PITCH] = 80; if (cl.viewangles[PITCH] < -70) cl.viewangles[PITCH] = -70; } else { if ((in_strafe.state & 1) && (cl.v.movetype == MOVETYPE_NOCLIP)) cmd->upmove -= m_forward.value * mouse_y; else cmd->forwardmove -= m_forward.value * mouse_y; } if (cl.idealroll == 0) // Did keyboard set it already?? { if (cl.v.movetype == MOVETYPE_FLY) { if (mouse_x < 0) cl.idealroll = -10; else if (mouse_x > 0) cl.idealroll = 10; } } } /* =========== IN_JoyMove =========== */ static void IN_JoyMove (usercmd_t *cmd) { float speed, aspeed; if (!joy_avail || !in_joystick.integer) return; joystickx = 0; joysticky = 0; IN_ReadJoystick (&joystickx, &joysticky); if (joysticky > joyyh * 2 || joystickx > joyxh * 2) return; // assume something jumped in and messed up the joystick // reading time (win 95) if (in_speed.state & 1) speed = cl_movespeedkey.value; else speed = 1; aspeed = speed * host_frametime; if (in_strafe.state & 1) { if (joystickx < joyxl) { // cmd->sidemove -= speed * cl_sidespeed.value; cmd->sidemove -= speed * 225; } else if (joystickx > joyxh) { // cmd->sidemove += speed * cl_sidespeed.value; cmd->sidemove += speed * 225; } } else { if (joystickx < joyxl) cl.viewangles[YAW] += aspeed * cl_yawspeed.value; else if (joystickx > joyxh) cl.viewangles[YAW] -= aspeed * cl_yawspeed.value; cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]); } if (in_mlook.state & 1) { if (m_pitch.value < 0) speed *= -1; if (joysticky < joyyl) cl.viewangles[PITCH] += aspeed * cl_pitchspeed.value; else if (joysticky > joyyh) cl.viewangles[PITCH] -= aspeed * cl_pitchspeed.value; } else { if (joysticky < joyyl) { // cmd->forwardmove += speed * cl_forwardspeed.value; cmd->forwardmove += speed * 200; } else if (joysticky > joyyh) { // cmd->forwardmove -= speed * cl_backspeed.value; cmd->forwardmove -= speed * 200; } } } static void IN_DiscardMove (void) { if (mouse_avail) { old_mouse_x = old_mouse_y = 0; IN_ReadMouseMove (NULL, NULL); } if (joy_avail && in_joystick.integer) { IN_ReadJoystick (NULL, NULL); } if (extern_avail) { dos_int86(extern_control->interruptVector); } } /* =========== IN_Move =========== */ void IN_Move (usercmd_t *cmd) { if (cl.v.cameramode) { /* stuck in a different camera so don't move */ memset (cmd, 0, sizeof(*cmd)); /* ignore any mouse movements in camera mode */ IN_DiscardMove (); return; } IN_MouseMove (cmd); IN_JoyMove (cmd); IN_ExternalMove (cmd); } /* ============================================================================ JOYSTICK ============================================================================ */ static qboolean IN_ReadJoystick (int *x, int *y) { int b; int count; count = 0; b = dos_inportb(0x201); dos_outportb(0x201, b); // clear counters while (++count < 10000) { b = dos_inportb(0x201); if (x) *x += b&1; if (y) *y += (b&2)>>1; if ( !(b&3) ) return true; } Con_Printf ("%s: no response\n", __thisfunc__); joy_avail = false; return false; } /* ============= WaitJoyButton ============= */ static qboolean WaitJoyButton (void) { int oldbuttons, buttons; oldbuttons = 0; do { key_count = -1; Sys_SendKeyEvents (); key_count = 0; if (key_lastpress == K_ESCAPE) { Con_Printf ("aborted.\n"); return false; } key_lastpress = 0; SCR_UpdateScreen (); buttons = ((dos_inportb(0x201) >> 4)&1) ^ 1; if (buttons != oldbuttons) { oldbuttons = buttons; continue; } } while ( !buttons); do { key_count = -1; Sys_SendKeyEvents (); key_count = 0; if (key_lastpress == K_ESCAPE) { Con_Printf ("aborted.\n"); return false; } key_lastpress = 0; SCR_UpdateScreen (); buttons = ((dos_inportb(0x201) >> 4)&1) ^ 1; if (buttons != oldbuttons) { oldbuttons = buttons; continue; } } while ( buttons); return true; } /* =============== IN_StartupJoystick =============== */ static void IN_StartupJoystick (void) { int centerx, centery; Con_Printf ("\n"); joy_avail = false; if (safemode || COM_CheckParm ("-nojoy")) return; if (!IN_ReadJoystick (NULL, NULL)) { joy_avail = false; Con_Printf ("joystick not found\n"); return; } Con_Printf ("joystick found\n"); Con_Printf ("CENTER the joystick\nand press button 1 (ESC to skip):\n"); if (!WaitJoyButton ()) return; IN_ReadJoystick (&joystickx, &joysticky); centerx = joystickx; centery = joysticky; Con_Printf ("Push the joystick to the UPPER LEFT\nand press button 1 (ESC to skip):\n"); if (!WaitJoyButton ()) return; IN_ReadJoystick (&joystickx, &joysticky); joyxl = (centerx + joystickx) / 2; joyyl = (centerx + joysticky) / 2; Con_Printf ("Push the joystick to the LOWER RIGHT\nand press button 1 (ESC to skip):\n"); if (!WaitJoyButton ()) return; IN_ReadJoystick (&joystickx, &joysticky); joyxh = (centerx + joystickx) / 2; joyyh = (centery + joysticky) / 2; joy_avail = true; Con_Printf ("joystick configured.\n"); Con_Printf ("\n"); } /* ============================================================================ EXTERNAL ============================================================================ */ /* =============== IN_StartupExternal =============== */ static void IN_StartupExternal (void) { if (extern_control->numButtons > 32) extern_control->numButtons = 32; Con_Printf("%s Initialized\n", extern_control->deviceName); Con_Printf(" %ld axes %ld buttons\n", extern_control->numAxes, extern_control->numButtons); extern_avail = true; extern_buttons = extern_control->numButtons; } /* =========== IN_ExternalMove =========== */ static void IN_ExternalMove (usercmd_t *cmd) { qboolean freelook; if (! extern_avail) return; extern_control->viewangles[YAW] = cl.viewangles[YAW]; extern_control->viewangles[PITCH] = cl.viewangles[PITCH]; extern_control->viewangles[ROLL] = cl.viewangles[ROLL]; extern_control->forwardmove = cmd->forwardmove; extern_control->sidemove = cmd->sidemove; extern_control->upmove = cmd->upmove; Con_DPrintf("IN: y:%f p:%f r:%f f:%f s:%f u:%f\n", extern_control->viewangles[YAW], extern_control->viewangles[PITCH], extern_control->viewangles[ROLL], extern_control->forwardmove, extern_control->sidemove, extern_control->upmove); dos_int86(extern_control->interruptVector); Con_DPrintf("OUT: y:%f p:%f r:%f f:%f s:%f u:%f\n", extern_control->viewangles[YAW], extern_control->viewangles[PITCH], extern_control->viewangles[ROLL], extern_control->forwardmove, extern_control->sidemove, extern_control->upmove); cl.viewangles[YAW] = extern_control->viewangles[YAW]; cl.viewangles[PITCH] = extern_control->viewangles[PITCH]; cl.viewangles[ROLL] = extern_control->viewangles[ROLL]; cmd->forwardmove = extern_control->forwardmove; cmd->sidemove = extern_control->sidemove; cmd->upmove = extern_control->upmove; if (cl.viewangles[PITCH] > 80) cl.viewangles[PITCH] = 80; if (cl.viewangles[PITCH] < -70) cl.viewangles[PITCH] = -70; freelook = (extern_control->flags & AUX_FLAG_FREELOOK || aux_look.integer || in_mlook.state & 1); if (freelook) V_StopPitchDrift (); } engine/h2shared/in_sdl.c000066400000000000000000000667521444734033100154620ustar00rootroot00000000000000/* in_sdl.c -- SDL game input code. * * Copyright (C) 2001 contributors of the Anvil of Thyrion project * Joystick code taken from the darkplaces project with modifications * to make it work in uHexen2: Copyright (C) 2006-2011 Forest Hale * Copyright (C) 2005-2012 Steven Atkinson, O.Sezer, Sander van Dijk * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "sdl_inc.h" #include "quakedef.h" static qboolean prev_gamekey; /* mouse variables */ static cvar_t m_filter = {"m_filter", "0", CVAR_NONE}; static int mouse_x, mouse_y, old_mouse_x, old_mouse_y; static qboolean mouseactive = false; static qboolean mouseinitialized = false; static qboolean mouseactivatetoggle = false; static qboolean mouseshowtoggle = true; static int buttonremap[] = { K_MOUSE1, K_MOUSE3, /* right button */ K_MOUSE2, /* middle button */ K_MWHEELUP, K_MWHEELDOWN, K_MOUSE4, K_MOUSE5 }; /* joystick support: */ static SDL_Joystick *joy_id = NULL; static int joy_available; static cvar_t in_joystick = {"joystick", "0", CVAR_ARCHIVE}; /* enable/disable joystick */ static cvar_t joy_index = {"joy_index", "0", CVAR_NONE}; /* joystick to use when have multiple */ static cvar_t joy_axisforward = {"joy_axisforward", "1", CVAR_NONE}; /* axis for forward/backward movement */ static cvar_t joy_axisside = {"joy_axisside", "0", CVAR_NONE}; /* axis for right/left movement */ static cvar_t joy_axisup = {"joy_axisup", "-1", CVAR_NONE}; /* axis for up/down movement */ static cvar_t joy_axispitch = {"joy_axispitch", "3", CVAR_NONE}; /* axis for looking up/down" */ static cvar_t joy_axisyaw = {"joy_axisyaw", "2", CVAR_NONE}; /* axis for looking right/left */ /* joy_axisroll & co. (for tilting head right/left??): Nope. */ static cvar_t joy_deadzoneforward = {"joy_deadzoneforward", "0", CVAR_NONE}; /* deadzone tolerance, suggested 0 to 0.01 */ static cvar_t joy_deadzoneside = {"joy_deadzoneside", "0", CVAR_NONE}; /* deadzone tolerance */ static cvar_t joy_deadzoneup = {"joy_deadzoneup", "0", CVAR_NONE}; /* deadzone tolerance */ static cvar_t joy_deadzonepitch = {"joy_deadzonepitch", "0", CVAR_NONE}; /* deadzone tolerance */ static cvar_t joy_deadzoneyaw = {"joy_deadzoneyaw", "0", CVAR_NONE}; /* deadzone tolerance */ static cvar_t joy_sensitivityforward = {"joy_sensitivityforward", "-1", CVAR_NONE}; /* movement multiplier */ static cvar_t joy_sensitivityside = {"joy_sensitivityside", "1", CVAR_NONE}; /* movement multiplier */ static cvar_t joy_sensitivityup = {"joy_sensitivityup", "1", CVAR_NONE}; /* movement multiplier */ static cvar_t joy_sensitivitypitch = {"joy_sensitivitypitch", "1", CVAR_NONE}; /* movement multiplier */ static cvar_t joy_sensitivityyaw = {"joy_sensitivityyaw", "-1", CVAR_NONE}; /* movement multiplier */ /* hack to generate uparrow/leftarrow etc. key events * for axes if the stick driver isn't generating them. */ /* might be useful for menu navigation etc. */ #define JOY_KEYEVENT_FOR_AXES 0 /* not for now */ #if (JOY_KEYEVENT_FOR_AXES) static cvar_t joy_axiskeyevents = {"joy_axiskeyevents", "0", CVAR_ARCHIVE}; static cvar_t joy_axiskeyevents_deadzone = {"joy_axiskeyevents_deadzone", "0.5", CVAR_ARCHIVE}; /* joystick axes state */ #define MAX_JOYSTICK_AXES 16 typedef struct { float oldmove; float move; double keytime; } joy_axiscache_t; static joy_axiscache_t joy_axescache[MAX_JOYSTICK_AXES]; #endif /* JOY_KEYEVENT_FOR_AXES */ /* HACKITY HACKITY HACK: since joystick trackballs convert to mousemove, * make IN_ActivateMouse()/IN_DeactivateMouse() to affect the trackball: */ static qboolean trackballactive = false; /* translate hat events into keypresses. the 4 * highest buttons are used for the first hat */ static int hat_keys[16] = { K_AUX29, K_AUX30, K_AUX31, K_AUX32, K_AUX25, K_AUX26, K_AUX27, K_AUX28, K_AUX21, K_AUX22, K_AUX23, K_AUX24, K_AUX17, K_AUX18, K_AUX19, K_AUX20 }; static Uint32 oldhats = 0; /* old hat state */ /* forward-referenced functions */ static void IN_StartupJoystick (void); static void IN_JoyMove (usercmd_t *cmd); static void IN_JoyTrackballMove (int *ballx, int *bally); /* adds to x/y args */ static void IN_JoyHatMove (void); static float IN_JoystickGetAxis (int axis, float sensitivity, float deadzone); static void IN_Callback_JoyEnable (cvar_t *var); static void IN_Callback_JoyIndex (cvar_t *var); /* =========== Force_CenterView_f =========== */ static void Force_CenterView_f (void) { cl.viewangles[PITCH] = 0; } /* =========== IN_ShowMouse =========== */ void IN_ShowMouse (void) { /* no need to check mouseinitialized here */ if (!mouseshowtoggle) { SDL_ShowCursor(1); mouseshowtoggle = true; } } /* =========== IN_HideMouse =========== */ void IN_HideMouse (void) { /* no need to check mouseinitialized here */ if (mouseshowtoggle) { SDL_ShowCursor (0); mouseshowtoggle = false; } } /* ============================================================ NOTES on enabling-disabling the mouse: - In windowed mode, mouse is temporarily disabled in main menu, so the un-grabbed pointer can be used for desktop This state is stored in menu_disabled_mouse as true - In fullscreen mode, we don't disable the mouse in menus, if we toggle windowed/fullscreen, the above state variable is used to correct this in VID_ToggleFullscreen() - In the console mode and in the options menu-group, mouse is not disabled, and menu_disabled_mouse is set to false - Starting a or connecting to a server activates the mouse and sets menu_disabled_mouse to false - Pausing the game disables (so un-grabs) the mouse, unpausing activates it. We don't play with menu_disabled_mouse in such cases */ /* =========== IN_ActivateMouse =========== */ void IN_ActivateMouse (void) { if (mouseinitialized) { if (!mouseactivatetoggle) { if (_enable_mouse.integer /*|| (modestate != MS_WINDOWED)*/) { mouseactivatetoggle = true; mouseactive = true; SDL_WM_GrabInput (SDL_GRAB_ON); } } } trackballactive = true; /* HACK... */ /* nuke events from when mouse was disabled: */ SDL_PumpEvents (); if (mouseinitialized) SDL_GetRelativeMouseState (NULL, NULL); IN_JoyTrackballMove (NULL, NULL); } /* =========== IN_DeactivateMouse =========== */ void IN_DeactivateMouse (void) { if (mouseinitialized) { if (mouseactivatetoggle) { mouseactivatetoggle = false; mouseactive = false; SDL_WM_GrabInput (SDL_GRAB_OFF); } } trackballactive = false; /* HACK... */ } /* =========== IN_StartupMouse =========== */ static void IN_StartupMouse (void) { /* IN_HideMouse ();*/ if (safemode || COM_CheckParm ("-nomouse")) { SDL_WM_GrabInput (SDL_GRAB_OFF); return; } old_mouse_x = old_mouse_y = 0; mouseinitialized = true; if (_enable_mouse.integer /*|| (modestate != MS_WINDOWED)*/) { mouseactivatetoggle = true; mouseactive = true; SDL_WM_GrabInput (SDL_GRAB_ON); SDL_GetRelativeMouseState (NULL, NULL); } } /* =================== IN_ClearStates =================== */ void IN_ClearStates (void) { } /* =========== IN_Init =========== */ void IN_Init (void) { /* mouse variables */ Cvar_RegisterVariable (&m_filter); /* joystick variables */ Cvar_RegisterVariable (&in_joystick); Cvar_RegisterVariable (&joy_index); Cvar_RegisterVariable (&joy_axisforward); Cvar_RegisterVariable (&joy_axisside); Cvar_RegisterVariable (&joy_axisup); Cvar_RegisterVariable (&joy_axispitch); Cvar_RegisterVariable (&joy_axisyaw); Cvar_RegisterVariable (&joy_deadzoneforward); Cvar_RegisterVariable (&joy_deadzoneside); Cvar_RegisterVariable (&joy_deadzoneup); Cvar_RegisterVariable (&joy_deadzonepitch); Cvar_RegisterVariable (&joy_deadzoneyaw); Cvar_RegisterVariable (&joy_sensitivityforward); Cvar_RegisterVariable (&joy_sensitivityside); Cvar_RegisterVariable (&joy_sensitivityup); Cvar_RegisterVariable (&joy_sensitivitypitch); Cvar_RegisterVariable (&joy_sensitivityyaw); #if (JOY_KEYEVENT_FOR_AXES) Cvar_RegisterVariable (&joy_axiskeyevents); Cvar_RegisterVariable (&joy_axiskeyevents_deadzone); #endif Cvar_SetCallback (&in_joystick, IN_Callback_JoyEnable); Cvar_SetCallback (&joy_index, IN_Callback_JoyIndex); Cmd_AddCommand ("force_centerview", Force_CenterView_f); IN_StartupMouse (); IN_StartupJoystick (); prev_gamekey = Key_IsGameKey(); SDL_EnableUNICODE (!prev_gamekey); SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL*2); } /* =========== IN_Shutdown =========== */ void IN_Shutdown (void) { IN_DeactivateMouse (); IN_ShowMouse (); mouseinitialized = false; if (joy_id) SDL_JoystickClose(joy_id); joy_id = NULL; joy_available = 0; if (SDL_WasInit(SDL_INIT_JOYSTICK)) SDL_QuitSubSystem(SDL_INIT_JOYSTICK); } /* =========== IN_ReInit =========== */ void IN_ReInit (void) { IN_StartupMouse (); prev_gamekey = Key_IsGameKey(); SDL_EnableUNICODE (!prev_gamekey); SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL*2); /* no need for joystick to reinit */ } /* =========== IN_MouseMove =========== */ static void IN_MouseMove (usercmd_t *cmd, int mx, int my) { if (m_filter.integer) { mouse_x = (mx + old_mouse_x) * 0.5; mouse_y = (my + old_mouse_y) * 0.5; } else { mouse_x = mx; mouse_y = my; } old_mouse_x = mx; old_mouse_y = my; mouse_x *= sensitivity.value; mouse_y *= sensitivity.value; /* add mouse X/Y movement to cmd */ if ( (in_strafe.state & 1) || (lookstrafe.integer && (in_mlook.state & 1) )) cmd->sidemove += m_side.value * mouse_x; else cl.viewangles[YAW] -= m_yaw.value * mouse_x; if (in_mlook.state & 1) { if (mx || my) V_StopPitchDrift (); } if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) { cl.viewangles[PITCH] += m_pitch.value * mouse_y; if (cl.viewangles[PITCH] > 80) cl.viewangles[PITCH] = 80; if (cl.viewangles[PITCH] < -70) cl.viewangles[PITCH] = -70; } else { if ((in_strafe.state & 1) && (cl.v.movetype == MOVETYPE_NOCLIP)) cmd->upmove -= m_forward.value * mouse_y; else cmd->forwardmove -= m_forward.value * mouse_y; } if (cl.idealroll == 0) /* Did keyboard set it already?? */ { if (cl.v.movetype == MOVETYPE_FLY) { if (mouse_x < 0) cl.idealroll = -10; else if (mouse_x > 0) cl.idealroll = 10; } } } static void IN_DiscardMove (void) { if (mouseinitialized) { old_mouse_x = old_mouse_y = 0; SDL_GetRelativeMouseState (NULL, NULL); } IN_JoyTrackballMove (NULL, NULL); /* JOY AXES ??? */ } /* =========== IN_Move =========== */ void IN_Move (usercmd_t *cmd) { int x, y; qboolean app_active; if (cl.v.cameramode) { /* stuck in a different camera so don't move */ memset (cmd, 0, sizeof(*cmd)); /* ignore any mouse movements in camera mode */ IN_DiscardMove (); return; } app_active = !VID_IsMinimized(); x = 0; y = 0; if (mouseactive) SDL_GetRelativeMouseState(&x, &y); if (app_active && trackballactive) IN_JoyTrackballMove (&x, &y); if (x != 0 || y != 0) IN_MouseMove (cmd, x, y); if (app_active) IN_JoyMove (cmd); } /* =============== IN_StartupJoystick =============== */ static void IN_StartupJoystick (void) { int i; if (safemode || COM_CheckParm ("-nojoy")) return; if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) { Con_Printf("Couldn't init SDL joystick: %s\n", SDL_GetError()); return; } joy_available = SDL_NumJoysticks(); if (joy_available == 0) { SDL_QuitSubSystem(SDL_INIT_JOYSTICK); Con_Printf ("No joystick devices found\n"); return; } Con_Printf ("SDL_Joystick: %d devices are reported:\n", joy_available); for (i = 0; i < joy_available; i++) { Con_Printf("#%d: \"%s\"\n", i, SDL_JoystickName(i)); } trackballactive = true; if (in_joystick.integer) IN_Callback_JoyIndex(&joy_index); } static void IN_Callback_JoyEnable (cvar_t *var) { if (var->integer) IN_Callback_JoyIndex(&joy_index); else { if (joy_id) { SDL_JoystickClose(joy_id); joy_id = NULL; } } } static void IN_Callback_JoyIndex (cvar_t *var) { int idx = var->integer; #if (JOY_KEYEVENT_FOR_AXES) memset (joy_axescache, 0, MAX_JOYSTICK_AXES * sizeof(joy_axiscache_t)); #endif oldhats = 0; if (joy_id) { SDL_JoystickClose(joy_id); joy_id = NULL; } if (idx < 0 || idx >= joy_available) { Con_Printf ("joystick #%d doesn't exist\n", idx); } else if (in_joystick.integer) { joy_id = SDL_JoystickOpen(idx); if (joy_id == NULL) { Con_Printf("joystick #%d open failed: %s\n", idx, SDL_GetError()); } else { int numaxes, numbtns, numballs, numhats; Con_Printf("joystick open "); Con_Printf("#%d: \"%s\"\n", idx, SDL_JoystickName(idx)); numaxes = SDL_JoystickNumAxes(joy_id); numbtns = SDL_JoystickNumButtons(joy_id); numballs= SDL_JoystickNumBalls(joy_id); numhats = SDL_JoystickNumHats(joy_id); Con_Printf(" %d axes, %d buttons, %d balls, %d hats\n", numaxes, numbtns, numballs, numhats); IN_JoyTrackballMove (NULL, NULL); if (numhats > 4) numhats = 4; for (idx = 0; idx < numhats; ++idx) ((Uint8 *)&oldhats)[idx] = SDL_JoystickGetHat(joy_id, idx); for (idx = 0; idx < numaxes; ++idx) SDL_JoystickGetAxis(joy_id, idx); } } } static void IN_JoyTrackballMove (int *ballx, int *bally) { int i, numballs; int x, y; if (!joy_id) return; numballs = SDL_JoystickNumBalls(joy_id); for (i = 0; i < numballs; i++) { SDL_JoystickGetBall(joy_id, i, &x, &y); if (ballx) *ballx += x; if (bally) *bally += y; } } static void IN_JoyHatMove (void) { int i, k, numhats; Uint32 hats; if (!joy_id) return; numhats = SDL_JoystickNumHats(joy_id); if (!numhats) return; if (numhats > 4) numhats = 4; hats = 0; for (i = 0; i < numhats; ++i) ((Uint8 *)&hats)[i] = SDL_JoystickGetHat(joy_id, i); if (hats == oldhats) return; for (i = 0; i < numhats; i++ ) { if (((Uint8 *)&hats)[i] == ((Uint8 *)&oldhats)[i]) continue; k = 4 * i; /* release event */ switch (((Uint8 *)&oldhats)[i]) { case SDL_HAT_UP: Key_Event(hat_keys[k + 0], false); break; case SDL_HAT_RIGHT: Key_Event(hat_keys[k + 1], false); break; case SDL_HAT_DOWN: Key_Event(hat_keys[k + 2], false); break; case SDL_HAT_LEFT: Key_Event(hat_keys[k + 3], false); break; case SDL_HAT_RIGHTUP: Key_Event(hat_keys[k + 0], false); Key_Event(hat_keys[k + 1], false); break; case SDL_HAT_RIGHTDOWN: Key_Event(hat_keys[k + 2], false); Key_Event(hat_keys[k + 1], false); break; case SDL_HAT_LEFTUP: Key_Event(hat_keys[k + 0], false); Key_Event(hat_keys[k + 3], false); break; case SDL_HAT_LEFTDOWN: Key_Event(hat_keys[k + 2], false); Key_Event(hat_keys[k + 3], false); break; default: break; } /* press event */ switch (((Uint8 *)&hats)[i]) { case SDL_HAT_UP: Key_Event(hat_keys[k + 0], true); break; case SDL_HAT_RIGHT: Key_Event(hat_keys[k + 1], true); break; case SDL_HAT_DOWN: Key_Event(hat_keys[k + 2], true); break; case SDL_HAT_LEFT: Key_Event(hat_keys[k + 3], true); break; case SDL_HAT_RIGHTUP: Key_Event(hat_keys[k + 0], true); Key_Event(hat_keys[k + 1], true); break; case SDL_HAT_RIGHTDOWN: Key_Event(hat_keys[k + 2], true); Key_Event(hat_keys[k + 1], true); break; case SDL_HAT_LEFTUP: Key_Event(hat_keys[k + 0], true); Key_Event(hat_keys[k + 3], true); break; case SDL_HAT_LEFTDOWN: Key_Event(hat_keys[k + 2], true); Key_Event(hat_keys[k + 3], true); break; default: break; } } oldhats = hats; /* save hat state */ } static float IN_JoystickGetAxis (int axis, float sensitivity, float deadzone) { float value; if (axis < 0 || axis >= SDL_JoystickNumAxes(joy_id)) return 0; /* no such axis on this joystick */ value = SDL_JoystickGetAxis(joy_id, axis) * (1.0f / 32767.0f); if (value < -1) value = -1; else if (value > 1) value = 1; if (fabs(value) < deadzone) return 0; /* within deadzone around center */ return value * sensitivity; } #if !(JOY_KEYEVENT_FOR_AXES) # define IN_JoystickBlockDoubledKeyEvents(S) (false) #else /* Joystick axis key events: a sort of hack emulating Arrow keys for * joystick axes as some drives dont send such key events for them. * additionally, we should block drivers that do send arrow key events * to prevent double events. */ static inline void DEBUG_KeyeventForAxis (float move, int key_pos, int key_neg) { /* Con_Printf("joy %s %f\n", Key_KeynumToString((move > 0) ? key_pos : key_neg), cl.time);*/ } static void IN_JoystickKeyeventForAxis (int axis, int key_pos, int key_neg) { double joytime; if (axis < 0 || axis >= SDL_JoystickNumAxes(joy_id)) return; /* no such axis on this joystick */ joytime = Sys_DoubleTime(); /* no key event, continuous keydown event */ if (joy_axescache[axis].move == joy_axescache[axis].oldmove) { if (joy_axescache[axis].move != 0 && joytime > joy_axescache[axis].keytime) { DEBUG_KeyeventForAxis (joy_axescache[axis].move, key_pos, key_neg); Key_Event((joy_axescache[axis].move > 0) ? key_pos : key_neg, true); joy_axescache[axis].keytime = joytime + 0.5 / 20; } return; } /* generate key up event */ if (joy_axescache[axis].oldmove) { DEBUG_KeyeventForAxis (joy_axescache[axis].oldmove, key_pos, key_neg); Key_Event((joy_axescache[axis].oldmove > 0) ? key_pos : key_neg, false); } /* generate key down event */ if (joy_axescache[axis].move) { DEBUG_KeyeventForAxis (joy_axescache[axis].move, key_pos, key_neg); Key_Event((joy_axescache[axis].move > 0) ? key_pos : key_neg, true); joy_axescache[axis].keytime = joytime + 0.5; } } static qboolean IN_JoystickBlockDoubledKeyEvents (int keycode) { if (!joy_id || !joy_axiskeyevents.integer) return false; /* block keyevent if it's going to be provided by joystick keyevent system */ if (keycode == K_UPARROW || keycode == K_DOWNARROW) if (IN_JoystickGetAxis(joy_axisforward.integer, 1, joy_axiskeyevents_deadzone.value) || joy_axescache[joy_axisforward.integer].move || joy_axescache[joy_axisforward.integer].oldmove) return true; if (keycode == K_RIGHTARROW || keycode == K_LEFTARROW) if (IN_JoystickGetAxis(joy_axisside.integer, 1, joy_axiskeyevents_deadzone.value) || joy_axescache[joy_axisside.integer].move || joy_axescache[joy_axisside.integer].oldmove) return true; return false; } #endif /* JOY_KEYEVENT_FOR_AXES */ /* =========== IN_JoyMove =========== */ static void IN_JoyMove (usercmd_t *cmd) { float speed, aspeed; float value; #if (JOY_KEYEVENT_FOR_AXES) int i, numaxes; #endif if (!joy_id) return; if (in_speed.state & 1) speed = cl_movespeedkey.value; else speed = 1; aspeed = speed * host_frametime; /* axes */ value = IN_JoystickGetAxis(joy_axisforward.integer, joy_sensitivityforward.value, joy_deadzoneforward.value); value *= speed * 200; /*cl_forwardspeed.value*/ cmd->forwardmove += value; value = IN_JoystickGetAxis(joy_axisside.integer, joy_sensitivityside.value, joy_deadzoneside.value); value *= speed * 225; /*cl_sidespeed.value*/ cmd->sidemove += value; value = IN_JoystickGetAxis(joy_axisup.integer, joy_sensitivityup.value, joy_deadzoneup.value); value *= speed * 200; /*cl_upspeed.value*/ cmd->upmove += value; value = IN_JoystickGetAxis(joy_axispitch.integer, joy_sensitivitypitch.value, joy_deadzonepitch.value); value *= aspeed * cl_pitchspeed.value; cl.viewangles[PITCH] += value; if (value) V_StopPitchDrift (); value = IN_JoystickGetAxis(joy_axisyaw.integer, joy_sensitivityyaw.value, joy_deadzoneyaw.value); value *= aspeed * cl_yawspeed.value; cl.viewangles[YAW] += value; /* bounds check pitch */ if (cl.viewangles[PITCH] > 80.0) cl.viewangles[PITCH] = 80.0; if (cl.viewangles[PITCH] < -70.0) cl.viewangles[PITCH] = -70.0; /* hats (pov): */ IN_JoyHatMove (); #if (JOY_KEYEVENT_FOR_AXES) /* cache state of axes to emulate button events for them */ numaxes = SDL_JoystickNumAxes(joy_id); if (numaxes > MAX_JOYSTICK_AXES) numaxes = MAX_JOYSTICK_AXES; for (i = 0; i < numaxes; i++) { joy_axescache[i].oldmove = joy_axescache[i].move; joy_axescache[i].move = IN_JoystickGetAxis(i, 1, joy_axiskeyevents_deadzone.value); } /* run keyevents */ if (joy_axiskeyevents.integer) { IN_JoystickKeyeventForAxis(joy_axisforward.integer, K_DOWNARROW, K_UPARROW); IN_JoystickKeyeventForAxis(joy_axisside.integer, K_RIGHTARROW, K_LEFTARROW); } #endif /* JOY_KEYEVENT_FOR_AXES */ } /* =========== IN_Commands =========== */ void IN_Commands (void) { /* all button events handled by IN_SendKeyEvents() */ } void IN_SendKeyEvents (void) { SDL_Event event; int sym, state, modstate; qboolean gamekey; if ((gamekey = Key_IsGameKey()) != prev_gamekey) { prev_gamekey = gamekey; SDL_EnableUNICODE(!gamekey); } while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_ACTIVEEVENT: if (event.active.state & (SDL_APPINPUTFOCUS|SDL_APPACTIVE)) { if (event.active.gain) S_UnblockSound(); else S_BlockSound(); } break; case SDL_KEYDOWN: if ((event.key.keysym.sym == SDLK_RETURN) && (event.key.keysym.mod & KMOD_ALT)) { VID_ToggleFullscreen(); break; } if ((event.key.keysym.sym == SDLK_ESCAPE) && (event.key.keysym.mod & KMOD_SHIFT)) { Con_ToggleConsole_f(); break; } if ((event.key.keysym.sym == SDLK_g) && (event.key.keysym.mod & KMOD_CTRL)) { SDL_WM_GrabInput((SDL_WM_GrabInput (SDL_GRAB_QUERY) == SDL_GRAB_ON) ? SDL_GRAB_OFF : SDL_GRAB_ON); break; } /* fallthrough */ case SDL_KEYUP: sym = event.key.keysym.sym; state = event.key.state; modstate = SDL_GetModState(); if (event.key.keysym.unicode != 0) { if ((event.key.keysym.unicode & 0xFF80) == 0) { int usym = event.key.keysym.unicode & 0x7F; if (modstate & KMOD_CTRL && usym < 32 && sym >= 32) { /* control characters */ if (modstate & KMOD_SHIFT) usym += 64; else usym += 96; } #if defined(__APPLE__) && defined(__MACH__) if (sym == SDLK_BACKSPACE) usym = sym; /* avoid change to SDLK_DELETE */ #endif #if defined(__QNX__) || defined(__QNXNTO__) if (sym == SDLK_BACKSPACE || sym == SDLK_RETURN) usym = sym; /* S.A: fixes QNX weirdness */ #endif /* only use unicode for ` and ~ in game mode */ if (!gamekey || usym == '`' || usym == '~') sym = usym; } /* else: it's an international character */ } /*printf("You pressed %s (%d) (%c)\n", SDL_GetKeyName(sym), sym, sym);*/ switch (sym) { case SDLK_DELETE: sym = K_DEL; break; case SDLK_BACKSPACE: sym = K_BACKSPACE; break; case SDLK_F1: sym = K_F1; break; case SDLK_F2: sym = K_F2; break; case SDLK_F3: sym = K_F3; break; case SDLK_F4: sym = K_F4; break; case SDLK_F5: sym = K_F5; break; case SDLK_F6: sym = K_F6; break; case SDLK_F7: sym = K_F7; break; case SDLK_F8: sym = K_F8; break; case SDLK_F9: sym = K_F9; break; case SDLK_F10: sym = K_F10; break; case SDLK_F11: sym = K_F11; break; case SDLK_F12: sym = K_F12; break; case SDLK_BREAK: case SDLK_PAUSE: sym = K_PAUSE; break; case SDLK_UP: sym = K_UPARROW; break; case SDLK_DOWN: sym = K_DOWNARROW; break; case SDLK_RIGHT: sym = K_RIGHTARROW; break; case SDLK_LEFT: sym = K_LEFTARROW; break; case SDLK_INSERT: sym = K_INS; break; case SDLK_HOME: sym = K_HOME; break; case SDLK_END: sym = K_END; break; case SDLK_PAGEUP: sym = K_PGUP; break; case SDLK_PAGEDOWN: sym = K_PGDN; break; case SDLK_RSHIFT: case SDLK_LSHIFT: sym = K_SHIFT; break; case SDLK_RCTRL: case SDLK_LCTRL: sym = K_CTRL; break; case SDLK_RALT: case SDLK_LALT: sym = K_ALT; break; case SDLK_RMETA: case SDLK_LMETA: sym = K_COMMAND; break; case SDLK_NUMLOCK: if (gamekey) sym = K_KP_NUMLOCK; else sym = 0; break; case SDLK_KP0: if (gamekey) sym = K_KP_INS; else sym = (modstate & KMOD_NUM) ? SDLK_0 : K_INS; break; case SDLK_KP1: if (gamekey) sym = K_KP_END; else sym = (modstate & KMOD_NUM) ? SDLK_1 : K_END; break; case SDLK_KP2: if (gamekey) sym = K_KP_DOWNARROW; else sym = (modstate & KMOD_NUM) ? SDLK_2 : K_DOWNARROW; break; case SDLK_KP3: if (gamekey) sym = K_KP_PGDN; else sym = (modstate & KMOD_NUM) ? SDLK_3 : K_PGDN; break; case SDLK_KP4: if (gamekey) sym = K_KP_LEFTARROW; else sym = (modstate & KMOD_NUM) ? SDLK_4 : K_LEFTARROW; break; case SDLK_KP5: if (gamekey) sym = K_KP_5; else sym = SDLK_5; break; case SDLK_KP6: if (gamekey) sym = K_KP_RIGHTARROW; else sym = (modstate & KMOD_NUM) ? SDLK_6 : K_RIGHTARROW; break; case SDLK_KP7: if (gamekey) sym = K_KP_HOME; else sym = (modstate & KMOD_NUM) ? SDLK_7 : K_HOME; break; case SDLK_KP8: if (gamekey) sym = K_KP_UPARROW; else sym = (modstate & KMOD_NUM) ? SDLK_8 : K_UPARROW; break; case SDLK_KP9: if (gamekey) sym = K_KP_PGUP; else sym = (modstate & KMOD_NUM) ? SDLK_9 : K_PGUP; break; case SDLK_KP_PERIOD: if (gamekey) sym = K_KP_DEL; else sym = (modstate & KMOD_NUM) ? SDLK_PERIOD : K_DEL; break; case SDLK_KP_DIVIDE: if (gamekey) sym = K_KP_SLASH; else sym = SDLK_SLASH; break; case SDLK_KP_MULTIPLY: if (gamekey) sym = K_KP_STAR; else sym = SDLK_ASTERISK; break; case SDLK_KP_MINUS: if (gamekey) sym = K_KP_MINUS; else sym = SDLK_MINUS; break; case SDLK_KP_PLUS: if (gamekey) sym = K_KP_PLUS; else sym = SDLK_PLUS; break; case SDLK_KP_ENTER: if (gamekey) sym = K_KP_ENTER; else sym = SDLK_RETURN; break; case SDLK_KP_EQUALS: if (gamekey) sym = 0; else sym = SDLK_EQUALS; break; case 178: /* the '²' key */ sym = '~'; break; default: /* If we are not directly handled and still above 255, * just force it to 0. kill unsupported international * characters, too. */ if ((sym >= SDLK_WORLD_0 && sym <= SDLK_WORLD_95) || sym > 255) sym = 0; break; } if (!IN_JoystickBlockDoubledKeyEvents(sym)) Key_Event(sym, state); break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: if (!mouseactive || in_mode_set) break; if (event.button.button < 1 || event.button.button > sizeof(buttonremap) / sizeof(buttonremap[0])) { Con_Printf ("Ignored event for mouse button %d\n", event.button.button); break; } Key_Event(buttonremap[event.button.button - 1], event.button.state == SDL_PRESSED); break; case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: if (in_mode_set) break; if (event.jbutton.button > K_AUX28 - K_JOY1) { Con_Printf ("Ignored event for joystick button %d\n", event.jbutton.button); break; } Key_Event(K_JOY1 + event.jbutton.button, event.jbutton.state == SDL_PRESSED); break; /* mouse/trackball motion handled by IN_MouseMove() */ /* axes and hat (pov) motion handled by IN_JoyMove() */ case SDL_MOUSEMOTION: case SDL_JOYBALLMOTION: case SDL_JOYHATMOTION: case SDL_JOYAXISMOTION: break; case SDL_QUIT: CL_Disconnect (); Sys_Quit (); break; default: break; } } } engine/h2shared/in_svgalib.c000066400000000000000000000224751444734033100163210ustar00rootroot00000000000000/* in_svgalib.c: Linux SVGALIB specific input driver. * from quake1 source with minor adaptations for uhexen2. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include "vga.h" /* vga_getmousetype() */ #include "vgakeyboard.h" #include "vgamouse.h" #include "quakedef.h" static qboolean mouse_initialized = false; static qboolean kbd_initialized = false; static unsigned char scantokey[128]; /* static int mouserate = MOUSE_DEFAULTSAMPLERATE; */ static char default_mouse[] = "/dev/mouse"; static int mouse_buttonstate; static int mouse_oldbuttonstate; static float mouse_x, mouse_y; static float old_mouse_x, old_mouse_y; static int mx, my; cvar_t m_filter = {"m_filter", "0", CVAR_NONE}; static void Force_CenterView_f (void) { cl.viewangles[PITCH] = 0; } static void keyhandler (int scancode, int state) { int sc; sc = scancode & 0x7f; // Con_Printf("scancode=%x (%d%s)\n", scancode, sc, (scancode & 0x80) ? "+128" : ""); Key_Event(scantokey[sc], state == KEY_EVENTPRESS); } static void mousehandler (int buttonstate, int dx, int dy, int dz, int drx, int dry, int drz) { if (cl.v.cameramode) { /* ignore any mouse movements in camera mode */ return; } mouse_buttonstate = buttonstate; mx += dx; my += dy; if (drx > 0) { Key_Event (K_MWHEELUP, true); Key_Event (K_MWHEELUP, false); } else if (drx < 0) { Key_Event (K_MWHEELDOWN, true); Key_Event (K_MWHEELDOWN, false); } } static void IN_InitKeyboard (void) { int i; for (i = 0; i < 128; i++) scantokey[i] = ' '; scantokey[ 1] = K_ESCAPE; scantokey[ 2] = '1'; scantokey[ 3] = '2'; scantokey[ 4] = '3'; scantokey[ 5] = '4'; scantokey[ 6] = '5'; scantokey[ 7] = '6'; scantokey[ 8] = '7'; scantokey[ 9] = '8'; scantokey[ 10] = '9'; scantokey[ 11] = '0'; scantokey[ 12] = '-'; scantokey[ 13] = '='; scantokey[ 14] = K_BACKSPACE; scantokey[ 15] = K_TAB; scantokey[ 16] = 'q'; scantokey[ 17] = 'w'; scantokey[ 18] = 'e'; scantokey[ 19] = 'r'; scantokey[ 20] = 't'; scantokey[ 21] = 'y'; scantokey[ 22] = 'u'; scantokey[ 23] = 'i'; scantokey[ 24] = 'o'; scantokey[ 25] = 'p'; scantokey[ 26] = '['; scantokey[ 27] = ']'; scantokey[ 28] = K_ENTER; scantokey[ 29] = K_CTRL; /* left */ scantokey[ 30] = 'a'; scantokey[ 31] = 's'; scantokey[ 32] = 'd'; scantokey[ 33] = 'f'; scantokey[ 34] = 'g'; scantokey[ 35] = 'h'; scantokey[ 36] = 'j'; scantokey[ 37] = 'k'; scantokey[ 38] = 'l'; scantokey[ 39] = ';'; scantokey[ 40] = '\''; scantokey[ 41] = '`'; scantokey[ 42] = K_SHIFT; /* left */ scantokey[ 43] = '\\'; scantokey[ 44] = 'z'; scantokey[ 45] = 'x'; scantokey[ 46] = 'c'; scantokey[ 47] = 'v'; scantokey[ 48] = 'b'; scantokey[ 49] = 'n'; scantokey[ 50] = 'm'; scantokey[ 51] = ','; scantokey[ 52] = '.'; scantokey[ 53] = '/'; scantokey[ 54] = K_SHIFT; /* right */ scantokey[ 55] = '*'; /* keypad */ scantokey[ 56] = K_ALT; /* left */ scantokey[ 57] = ' '; /* 58 caps lock */ scantokey[ 59] = K_F1; scantokey[ 60] = K_F2; scantokey[ 61] = K_F3; scantokey[ 62] = K_F4; scantokey[ 63] = K_F5; scantokey[ 64] = K_F6; scantokey[ 65] = K_F7; scantokey[ 66] = K_F8; scantokey[ 67] = K_F9; scantokey[ 68] = K_F10; /* 69 numlock */ /* 70 scrollock */ scantokey[ 71] = K_HOME; scantokey[ 72] = K_UPARROW; scantokey[ 73] = K_PGUP; scantokey[ 74] = '-'; scantokey[ 75] = K_LEFTARROW; scantokey[ 76] = '5'; scantokey[ 77] = K_RIGHTARROW; scantokey[ 79] = K_END; scantokey[ 78] = '+'; scantokey[ 80] = K_DOWNARROW; scantokey[ 81] = K_PGDN; scantokey[ 82] = K_INS; scantokey[ 83] = K_DEL; /* 84 to 86 not used */ scantokey[ 87] = K_F11; scantokey[ 88] = K_F12; /* 89 to 95 not used */ scantokey[ 96] = K_ENTER; /* keypad */ scantokey[ 97] = K_CTRL; /* right */ scantokey[ 98] = '/'; scantokey[ 99] = K_F12; /* print screen, bind to screenshot by default */ scantokey[100] = K_ALT; /* right */ scantokey[101] = K_PAUSE; /* break */ scantokey[102] = K_HOME; scantokey[103] = K_UPARROW; scantokey[104] = K_PGUP; scantokey[105] = K_LEFTARROW; scantokey[106] = K_RIGHTARROW; scantokey[107] = K_END; scantokey[108] = K_DOWNARROW; scantokey[109] = K_PGDN; scantokey[110] = K_INS; scantokey[111] = K_DEL; scantokey[119] = K_PAUSE; if (keyboard_init()) Sys_Error("keyboard_init() failed"); keyboard_seteventhandler(keyhandler); kbd_initialized = true; } static void IN_InitMouse (void) { int mtype; char *mousedev; int mouserate; int i; mtype = vga_getmousetype(); Sys_Printf("SVGALib reports MTYPE: %d\n", mtype); mousedev = default_mouse; if (getenv("MOUSEDEV")) mousedev = getenv("MOUSEDEV"); i = COM_CheckParm("-mdev"); if (i && i < com_argc - 1) mousedev = com_argv[i+1]; mouserate = 1200; if (getenv("MOUSERATE")) mouserate = atoi(getenv("MOUSERATE")); i = COM_CheckParm("-mrate"); if (i && i < com_argc - 1) mouserate = atoi(com_argv[i+1]); /* vga_init() opens the mouse automoatically, closing it to ensure it * is opened the way we want it (from quakeforge.) * FIXME: should I check for a return value >= 0 from mouse_update() * before doing this? */ mouse_close(); if (mouse_init(mousedev, mtype, mouserate)) Con_Printf("No mouse found\n"); else { mouse_seteventhandler((void *) mousehandler); mouse_initialized = true; } } void IN_ActivateMouse (void) { if (mouse_initialized) { old_mouse_x = old_mouse_y = 0; mx = my = 0; mouse_buttonstate = mouse_oldbuttonstate = 0; mouse_seteventhandler((void *) mousehandler); } } void IN_DeactivateMouse (void) { if (mouse_initialized) { mouse_setdefaulteventhandler(); } } void IN_ShowMouse (void) { } void IN_HideMouse (void) { } void IN_ClearStates (void) { } void IN_Init (void) { Cmd_AddCommand ("force_centerview", Force_CenterView_f); Cvar_RegisterVariable (&m_filter); if (!COM_CheckParm("-nokbd")) { IN_InitKeyboard(); } if (!(safemode || COM_CheckParm("-nomouse"))) { IN_InitMouse(); } } void IN_Shutdown (void) { if (mouse_initialized) { mouse_close(); mouse_initialized = false; } if (kbd_initialized) { keyboard_close(); kbd_initialized = false; } } /* =========== IN_SendKeyEvents =========== */ void IN_SendKeyEvents (void) { if (kbd_initialized) { while (keyboard_update()) ; } } /* =========== IN_Commands =========== */ void IN_Commands (void) { if (mouse_initialized) { /* poll mouse values */ while (mouse_update()) ; /* perform button actions */ if ((mouse_buttonstate & MOUSE_LEFTBUTTON) && !(mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) Key_Event (K_MOUSE1, true); else if (!(mouse_buttonstate & MOUSE_LEFTBUTTON) && (mouse_oldbuttonstate & MOUSE_LEFTBUTTON)) Key_Event (K_MOUSE1, false); if ((mouse_buttonstate & MOUSE_RIGHTBUTTON) && !(mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) Key_Event (K_MOUSE2, true); else if (!(mouse_buttonstate & MOUSE_RIGHTBUTTON) && (mouse_oldbuttonstate & MOUSE_RIGHTBUTTON)) Key_Event (K_MOUSE2, false); if ((mouse_buttonstate & MOUSE_MIDDLEBUTTON) && !(mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) Key_Event (K_MOUSE3, true); else if (!(mouse_buttonstate & MOUSE_MIDDLEBUTTON) && (mouse_oldbuttonstate & MOUSE_MIDDLEBUTTON)) Key_Event (K_MOUSE3, false); mouse_oldbuttonstate = mouse_buttonstate; } } /* =========== IN_Move =========== */ static void IN_MouseMove (usercmd_t *cmd) { if (!mouse_initialized) return; /* poll mouse values */ while (mouse_update()) ; if (m_filter.integer) { mouse_x = (mx + old_mouse_x) * 0.5; mouse_y = (my + old_mouse_y) * 0.5; } else { mouse_x = mx; mouse_y = my; } old_mouse_x = mx; old_mouse_y = my; mx = my = 0; /* clear for next update */ mouse_x *= sensitivity.value; mouse_y *= sensitivity.value; /* add mouse X/Y movement to cmd */ if ( (in_strafe.state & 1) || (lookstrafe.integer && (in_mlook.state & 1) )) cmd->sidemove += m_side.value * mouse_x; else cl.viewangles[YAW] -= m_yaw.value * mouse_x; if (in_mlook.state & 1) V_StopPitchDrift (); if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) { cl.viewangles[PITCH] += m_pitch.value * mouse_y; if (cl.viewangles[PITCH] > 80) cl.viewangles[PITCH] = 80; if (cl.viewangles[PITCH] < -70) cl.viewangles[PITCH] = -70; } else { if ((in_strafe.state & 1) && (cl.v.movetype == MOVETYPE_NOCLIP)) cmd->upmove -= m_forward.value * mouse_y; else cmd->forwardmove -= m_forward.value * mouse_y; } if (cl.idealroll == 0) /* Did keyboard set it already?? */ { if (cl.v.movetype == MOVETYPE_FLY) { if (mouse_x < 0) cl.idealroll = -10; else if (mouse_x > 0) cl.idealroll = 10; } } } void IN_Move (usercmd_t *cmd) { if (cl.v.cameramode) { /* stuck in a different camera so don't move */ memset (cmd, 0, sizeof(*cmd)); return; } IN_MouseMove(cmd); } engine/h2shared/in_win.c000066400000000000000000000746701444734033100154730ustar00rootroot00000000000000/* in_win.c -- windows 95 mouse and joystick code * 02/21/97 JCB Added extended DirectInput code to support * external controllers * * Copyright (C) 1996-2001 Id Software, Inc. * Copyright (C) 2005-2012 O.Sezer * Several bits from darkplaces and the ioquake3 projects * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "winquake.h" #include #include // mouse variables static cvar_t m_filter = {"m_filter", "0", CVAR_NONE}; static int mouse_oldbuttonstate; static POINT current_pos; static int mouse_x, mouse_y, old_mouse_x, old_mouse_y, mx_accum, my_accum; static qboolean restore_spi; static int originalmouseparms[3], newmouseparms[3] = {0, 0, 0}; /* [0] threshold to double movement (only if accel level is >= 1) * [1] threshold to quadruple movement (only if accel level is >= 2) * [2] maximum level of acceleration (0 = off) * maximum acceleration was set to 1 in the original engine: * disabling it for better operation on Windows XP and newer. * players can change their sensitivity setting if necessary. */ static qboolean mouseactive; static qboolean mouseinitialized; static qboolean mouseparmsvalid, mouseactivatetoggle; static qboolean mouseshowtoggle = 1; /* do NOT include the wheel enums below */ static int buttonremap[] = { K_MOUSE1, K_MOUSE2, K_MOUSE3, K_MOUSE4, K_MOUSE5 }; #define NUM_MOUSEBUTTONS (sizeof(buttonremap) / sizeof(buttonremap[0])) /* DirectInput mouse control: */ qboolean dinput_init; static qboolean dinput_acquired; static unsigned int mstate_di; static LPDIRECTINPUT g_pdi; static LPDIRECTINPUTDEVICE g_pMouse; #define DINPUT_BUFFERSIZE 16 #if defined(DX_DLSYM) /* dynamic loading of dinput symbols */ #undef DEFINE_GUID #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ const GUID name = \ { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } DEFINE_GUID(GUID_SysMouse, 0x6F1D2B60,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); DEFINE_GUID(GUID_XAxis, 0xA36D02E0,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); DEFINE_GUID(GUID_YAxis, 0xA36D02E1,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); DEFINE_GUID(GUID_ZAxis, 0xA36D02E2,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); typedef struct MYDATA { LONG lX; // X axis goes here LONG lY; // Y axis goes here LONG lZ; // Z axis goes here BYTE bButtonA; // One button goes here BYTE bButtonB; // Another button goes here BYTE bButtonC; // Another button goes here BYTE bButtonD; // Another button goes here } MYDATA; static DIOBJECTDATAFORMAT rgodf[] = { { &GUID_XAxis, FIELD_OFFSET(MYDATA, lX), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, { &GUID_YAxis, FIELD_OFFSET(MYDATA, lY), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, { &GUID_ZAxis, FIELD_OFFSET(MYDATA, lZ), 0x80000000 | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,}, { 0, FIELD_OFFSET(MYDATA, bButtonA), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, { 0, FIELD_OFFSET(MYDATA, bButtonB), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, { 0, FIELD_OFFSET(MYDATA, bButtonC), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, { 0, FIELD_OFFSET(MYDATA, bButtonD), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,}, }; #define NUM_OBJECTS (sizeof(rgodf) / sizeof(rgodf[0])) static DIDATAFORMAT diformat = { sizeof(DIDATAFORMAT), // this structure sizeof(DIOBJECTDATAFORMAT), // size of object data format DIDF_RELAXIS, // absolute axis coordinates sizeof(MYDATA), // device data size NUM_OBJECTS, // number of objects rgodf, // and here they are }; static HINSTANCE hInstDI; static HRESULT (WINAPI *pDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUT *lplpDirectInput, LPUNKNOWN punkOuter); #else /* ! DX_DLSYM : we're linked to dinput */ #define diformat c_dfDIMouse #define pDirectInputCreate DirectInputCreateA #endif /* DX_DLSYM */ // joystick defines and variables // where should defines be moved? #define JOY_ABSOLUTE_AXIS 0x00000000 // control like a joystick #define JOY_RELATIVE_AXIS 0x00000010 // control like a mouse, spinner, trackball #define JOY_MAX_AXES 6 // X, Y, Z, R, U, V #define JOY_AXIS_X 0 #define JOY_AXIS_Y 1 #define JOY_AXIS_Z 2 #define JOY_AXIS_R 3 #define JOY_AXIS_U 4 #define JOY_AXIS_V 5 enum _ControlList { AxisNada = 0, AxisForward, AxisLook, AxisSide, AxisTurn, AxisUp }; static DWORD dwAxisFlags[JOY_MAX_AXES] = { JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ, JOY_RETURNR, JOY_RETURNU, JOY_RETURNV }; static DWORD dwAxisMap[JOY_MAX_AXES]; static DWORD dwControlMap[JOY_MAX_AXES]; static PDWORD pdwRawValue[JOY_MAX_AXES]; // none of these cvars are saved over a session // this means that advanced controller configuration needs to be executed // each time. this avoids any problems with getting back to a default usage // or when changing from one controller to another. this way at least something // works. static cvar_t in_joystick = {"joystick", "0", CVAR_ARCHIVE}; static cvar_t joy_name = {"joyname", "joystick", CVAR_NONE}; static cvar_t joy_advanced = {"joyadvanced", "0", CVAR_NONE}; static cvar_t joy_advaxisx = {"joyadvaxisx", "0", CVAR_NONE}; static cvar_t joy_advaxisy = {"joyadvaxisy", "0", CVAR_NONE}; static cvar_t joy_advaxisz = {"joyadvaxisz", "0", CVAR_NONE}; static cvar_t joy_advaxisr = {"joyadvaxisr", "0", CVAR_NONE}; static cvar_t joy_advaxisu = {"joyadvaxisu", "0", CVAR_NONE}; static cvar_t joy_advaxisv = {"joyadvaxisv", "0", CVAR_NONE}; static cvar_t joy_forwardthreshold = {"joyforwardthreshold", "0.15", CVAR_NONE}; static cvar_t joy_sidethreshold = {"joysidethreshold", "0.15", CVAR_NONE}; static cvar_t joy_upthreshold = {"joyupthreshold", "0.15", CVAR_NONE}; static cvar_t joy_pitchthreshold = {"joypitchthreshold", "0.15", CVAR_NONE}; static cvar_t joy_yawthreshold = {"joyyawthreshold", "0.15", CVAR_NONE}; static cvar_t joy_forwardsensitivity = {"joyforwardsensitivity", "-1.0", CVAR_NONE}; static cvar_t joy_sidesensitivity = {"joysidesensitivity", "-1.0", CVAR_NONE}; static cvar_t joy_upsensitivity = {"joyupsensitivity", "-1.0", CVAR_NONE}; static cvar_t joy_pitchsensitivity = {"joypitchsensitivity", "1.0", CVAR_NONE}; static cvar_t joy_yawsensitivity = {"joyyawsensitivity", "-1.0", CVAR_NONE}; static cvar_t joy_wwhack1 = {"joywwhack1", "0.0", CVAR_NONE}; static cvar_t joy_wwhack2 = {"joywwhack2", "0.0", CVAR_NONE}; static qboolean joy_avail, joy_advancedinit, joy_haspov; static DWORD joy_oldbuttonstate, joy_oldpovstate; static int joy_id; static DWORD joy_flags; static DWORD joy_numbuttons; static JOYINFOEX ji; // forward-referenced functions static void IN_StartupJoystick (void); static void Joy_AdvancedUpdate_f (void); static void IN_JoyMove (usercmd_t *cmd); /* =========== Force_CenterView_f =========== */ static void Force_CenterView_f (void) { cl.viewangles[PITCH] = 0; } /* =========== IN_UpdateClipCursor =========== */ void IN_UpdateClipCursor (void) { if (dinput_init) return; if (mouseinitialized && mouseactive) { ClipCursor (&window_rect); } } /* =========== IN_ShowMouse =========== */ void IN_ShowMouse (void) { if (!mouseshowtoggle) { ShowCursor (TRUE); mouseshowtoggle = 1; } } /* =========== IN_HideMouse =========== */ void IN_HideMouse (void) { if (mouseshowtoggle) { ShowCursor (FALSE); mouseshowtoggle = 0; } } /* =========== IN_ActivateMouse =========== */ void IN_ActivateMouse (void) { mouseactivatetoggle = true; if (mouseinitialized && _enable_mouse.integer) { IN_HideMouse(); if (dinput_init) { if (g_pMouse) { if (!dinput_acquired) { IDirectInputDevice_Acquire(g_pMouse); dinput_acquired = true; } } else { return; } } else { if (mouseparmsvalid) restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0); SetCursorPos (window_center_x, window_center_y); SetCapture (mainwindow); ClipCursor (&window_rect); } mouseactive = true; } } /* =========== IN_SetQuakeMouseState =========== */ void IN_SetQuakeMouseState (void) { if (mouseactivatetoggle) IN_ActivateMouse (); } /* =========== IN_DeactivateMouse =========== */ void IN_DeactivateMouse (void) { mouseactivatetoggle = false; if (mouseinitialized) { IN_ShowMouse(); if (dinput_init) { if (g_pMouse) { if (dinput_acquired) { IDirectInputDevice_Unacquire(g_pMouse); dinput_acquired = false; } } } else { if (restore_spi) SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0); ClipCursor (NULL); ReleaseCapture (); } mouseactive = false; } } /* =========== IN_RestoreOriginalMouseState =========== */ void IN_RestoreOriginalMouseState (void) { if (mouseactivatetoggle) { IN_DeactivateMouse (); mouseactivatetoggle = true; } // try to redraw the cursor so it gets reinitialized, because sometimes it // has garbage after the mode switch ShowCursor (TRUE); ShowCursor (FALSE); } /* =========== IN_InitDInput =========== */ static qboolean IN_InitDInput (void) { HRESULT hr; DIPROPDWORD dipdw = { { sizeof(DIPROPDWORD), // diph.dwSize sizeof(DIPROPHEADER), // diph.dwHeaderSize 0, // diph.dwObj DIPH_DEVICE, // diph.dwHow }, DINPUT_BUFFERSIZE, // dwData }; #if defined(DX_DLSYM) /* dynamic loading of dinput symbols */ if (!hInstDI) { hInstDI = LoadLibrary("dinput.dll"); if (hInstDI == NULL) { Con_SafePrintf ("Couldn't load dinput.dll\n"); return false; } } if (!pDirectInputCreate) { pDirectInputCreate = (HRESULT (WINAPI *)(HINSTANCE, DWORD, LPDIRECTINPUT *, LPUNKNOWN)) GetProcAddress(hInstDI, "DirectInputCreateA"); if (!pDirectInputCreate) { Con_SafePrintf ("Couldn't get DI proc addr\n"); return false; } } #endif /* DX_DLSYM */ // register with DirectInput and get an IDirectInput to play with. hr = pDirectInputCreate(global_hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL); if (FAILED(hr)) { return false; } // obtain an interface to the system mouse device. #if defined(__cplusplus) hr = IDirectInput_CreateDevice(g_pdi, GUID_SysMouse, &g_pMouse, NULL); #else hr = IDirectInput_CreateDevice(g_pdi, &GUID_SysMouse, &g_pMouse, NULL); #endif if (FAILED(hr)) { Con_SafePrintf ("Couldn't open DI mouse device\n"); return false; } // set the data format to "mouse format". hr = IDirectInputDevice_SetDataFormat(g_pMouse, &diformat); if (FAILED(hr)) { Con_SafePrintf ("Couldn't set DI mouse format\n"); return false; } // set the cooperativity level. hr = IDirectInputDevice_SetCooperativeLevel(g_pMouse, mainwindow, DISCL_EXCLUSIVE | DISCL_FOREGROUND); if (FAILED(hr)) { Con_SafePrintf ("Couldn't set DI coop level\n"); return false; } // set the buffer size to DINPUT_BUFFERSIZE elements. // the buffer size is a DWORD property associated with the device hr = IDirectInputDevice_SetProperty(g_pMouse, DIPROP_BUFFERSIZE, &dipdw.diph); if (FAILED(hr)) { Con_SafePrintf ("Couldn't set DI buffersize\n"); return false; } Con_SafePrintf ("DirectInput initialized\n"); return true; } static void IN_InitWinMouse (void) { mouseparmsvalid = SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0); if (mouseparmsvalid) { if ( COM_CheckParm ("-noforcemspd") ) newmouseparms[2] = originalmouseparms[2]; if ( COM_CheckParm ("-noforcemaccel") ) { newmouseparms[0] = originalmouseparms[0]; newmouseparms[1] = originalmouseparms[1]; } if ( COM_CheckParm ("-noforcemparms") ) { newmouseparms[0] = originalmouseparms[0]; newmouseparms[1] = originalmouseparms[1]; newmouseparms[2] = originalmouseparms[2]; } } } /* =========== IN_StartupMouse =========== */ static void IN_StartupMouse (void) { if (safemode || COM_CheckParm ("-nomouse")) return; mouseinitialized = true; if (COM_CheckParm ("-dinput")) { dinput_init = IN_InitDInput (); if (!dinput_init) Con_SafePrintf ("DirectInput initialization failed\n"); } if (!dinput_init) { IN_InitWinMouse (); } // if a fullscreen video mode was set before the mouse was initialized, // set the mouse state appropriately if (mouseactivatetoggle) IN_ActivateMouse (); } /* =========== IN_Init =========== */ void IN_Init (void) { // mouse variables Cvar_RegisterVariable (&m_filter); // joystick variables Cvar_RegisterVariable (&in_joystick); Cvar_RegisterVariable (&joy_name); Cvar_RegisterVariable (&joy_advanced); Cvar_RegisterVariable (&joy_advaxisx); Cvar_RegisterVariable (&joy_advaxisy); Cvar_RegisterVariable (&joy_advaxisz); Cvar_RegisterVariable (&joy_advaxisr); Cvar_RegisterVariable (&joy_advaxisu); Cvar_RegisterVariable (&joy_advaxisv); Cvar_RegisterVariable (&joy_forwardthreshold); Cvar_RegisterVariable (&joy_sidethreshold); Cvar_RegisterVariable (&joy_upthreshold); Cvar_RegisterVariable (&joy_pitchthreshold); Cvar_RegisterVariable (&joy_yawthreshold); Cvar_RegisterVariable (&joy_forwardsensitivity); Cvar_RegisterVariable (&joy_sidesensitivity); Cvar_RegisterVariable (&joy_upsensitivity); Cvar_RegisterVariable (&joy_pitchsensitivity); Cvar_RegisterVariable (&joy_yawsensitivity); Cvar_RegisterVariable (&joy_wwhack1); Cvar_RegisterVariable (&joy_wwhack2); Cmd_AddCommand ("force_centerview", Force_CenterView_f); Cmd_AddCommand ("joyadvancedupdate", Joy_AdvancedUpdate_f); IN_StartupMouse (); IN_StartupJoystick (); } /* =========== IN_Shutdown =========== */ void IN_Shutdown (void) { IN_DeactivateMouse (); IN_ShowMouse (); dinput_init = mouseinitialized = false; if (g_pMouse) { IDirectInputDevice_Release(g_pMouse); g_pMouse = NULL; } if (g_pdi) { IDirectInput_Release(g_pdi); g_pdi = NULL; } } /* =========== IN_ReInit =========== */ void IN_ReInit (void) { if (safemode || COM_CheckParm ("-nomouse")) return; if (g_pMouse) { IDirectInputDevice_Release(g_pMouse); g_pMouse = NULL; } if (g_pdi) { IDirectInput_Release(g_pdi); g_pdi = NULL; } old_mouse_x = old_mouse_y = mx_accum = my_accum = 0; // we only need to re-initialize direct input. // if winmouse is active, nothing is necessary. if (!dinput_init) return; dinput_init = IN_InitDInput (); if (!dinput_init) { Con_SafePrintf ("DirectInput initialization failed\n"); IN_InitWinMouse (); } } /* =========== IN_MouseEvent =========== */ void IN_MouseEvent (int mstate) { int i; if (mouseactive && !dinput_init) { // perform button actions for (i = 0; i < (int)NUM_MOUSEBUTTONS; i++) { if ((mstate ^ mouse_oldbuttonstate) & (1< 0) { Key_Event (K_MWHEELUP, true); Key_Event (K_MWHEELUP, false); } } else if ((LONG)od.dwOfs == DIMOFS_BUTTON0) { if (od.dwData & 0x80) mstate_di |= 1; else mstate_di &= ~1; } else if ((LONG)od.dwOfs == DIMOFS_BUTTON1) { if (od.dwData & 0x80) mstate_di |= (1<<1); else mstate_di &= ~(1<<1); } else if ((LONG)od.dwOfs == DIMOFS_BUTTON2) { if (od.dwData & 0x80) mstate_di |= (1<<2); else mstate_di &= ~(1<<2); } else if ((LONG)od.dwOfs == DIMOFS_BUTTON3) { if (od.dwData & 0x80) mstate_di |= (1<<3); else mstate_di &= ~(1<<3); } } // perform button actions for (i = 0; i < (int)NUM_MOUSEBUTTONS; i++) { if ((mstate_di ^ mouse_oldbuttonstate) & (1<sidemove += m_side.value * mouse_x; else cl.viewangles[YAW] -= m_yaw.value * mouse_x; if (in_mlook.state & 1) { if (mx || my) V_StopPitchDrift (); } if ( (in_mlook.state & 1) && !(in_strafe.state & 1)) { cl.viewangles[PITCH] += m_pitch.value * mouse_y; if (cl.viewangles[PITCH] > 80) cl.viewangles[PITCH] = 80; if (cl.viewangles[PITCH] < -70) cl.viewangles[PITCH] = -70; } else { if ((in_strafe.state & 1) && (cl.v.movetype == MOVETYPE_NOCLIP)) cmd->upmove -= m_forward.value * mouse_y; else cmd->forwardmove -= m_forward.value * mouse_y; } if (cl.idealroll == 0) // Did keyboard set it already?? { if (cl.v.movetype == MOVETYPE_FLY) { if (mouse_x < 0) cl.idealroll = -10; else if (mouse_x > 0) cl.idealroll = 10; } } if (dinput_init) return; // if the mouse has moved, force it to the center, so there's room to move if (mx || my) { SetCursorPos (window_center_x, window_center_y); } } static void IN_DiscardMove (void) { if (mouseactive && !dinput_init) { /* nuke data collected by IN_Accumulate(): */ old_mouse_x = old_mouse_y = mx_accum = my_accum = 0; } /* need I mess with the joystick here too? */ } /* =========== IN_Move =========== */ void IN_Move (usercmd_t *cmd) { if (cl.v.cameramode) { /* stuck in a different camera so don't move */ memset (cmd, 0, sizeof(*cmd)); /* ignore any mouse movements in camera mode */ IN_DiscardMove (); return; } if (ActiveApp && !Minimized) { IN_MouseMove (cmd); IN_JoyMove (cmd); } } /* =========== IN_Accumulate =========== */ void IN_Accumulate (void) { if (dinput_init) return; if (mouseactive) { GetCursorPos (¤t_pos); mx_accum += current_pos.x - window_center_x; my_accum += current_pos.y - window_center_y; // force the mouse to the center, so there's room to move SetCursorPos (window_center_x, window_center_y); } } /* =================== IN_ClearStates =================== */ void IN_ClearStates (void) { if (mouseactive) { mx_accum = 0; my_accum = 0; mouse_oldbuttonstate = 0; } } /* =============== IN_StartupJoystick =============== */ static void IN_StartupJoystick (void) { int numdevs; JOYCAPS jc; MMRESULT mmr; // assume no joystick joy_avail = false; mmr = ~JOYERR_NOERROR; // shut up the compiler // abort startup if user requests no joystick if (safemode || COM_CheckParm ("-nojoy")) return; // verify joystick driver is present if ((numdevs = joyGetNumDevs ()) == 0) { Con_SafePrintf ("\njoystick not found -- driver not present\n\n"); return; } // cycle through the joystick ids for the first valid one for (joy_id = 0; joy_id < numdevs; joy_id++) { memset (&ji, 0, sizeof(ji)); ji.dwSize = sizeof(ji); ji.dwFlags = JOY_RETURNCENTERED; if ((mmr = joyGetPosEx (joy_id, &ji)) == JOYERR_NOERROR) break; } // abort startup if we didn't find a valid joystick if (mmr != JOYERR_NOERROR) { Con_SafePrintf ("\njoystick not found -- no valid joysticks (%x)\n\n", mmr); return; } // get the capabilities of the selected joystick // abort startup if command fails memset (&jc, 0, sizeof(jc)); if ((mmr = joyGetDevCaps (joy_id, &jc, sizeof(jc))) != JOYERR_NOERROR) { Con_SafePrintf ("\njoystick not found -- invalid joystick capabilities (%x)\n\n", mmr); return; } // save the joystick's number of buttons and POV status joy_numbuttons = jc.wNumButtons; joy_haspov = jc.wCaps & JOYCAPS_HASPOV; // old button and POV states default to no buttons pressed joy_oldbuttonstate = joy_oldpovstate = 0; // mark the joystick as available and advanced initialization not completed // this is needed as cvars are not available during initialization joy_avail = true; joy_advancedinit = false; Con_SafePrintf ("\njoystick detected\n\n"); } /* =========== RawValuePointer =========== */ static PDWORD RawValuePointer (int axis) { switch (axis) { case JOY_AXIS_X: return &ji.dwXpos; case JOY_AXIS_Y: return &ji.dwYpos; case JOY_AXIS_Z: return &ji.dwZpos; case JOY_AXIS_R: return &ji.dwRpos; case JOY_AXIS_U: return &ji.dwUpos; case JOY_AXIS_V: return &ji.dwVpos; } return NULL; } /* =========== Joy_AdvancedUpdate_f =========== */ static void Joy_AdvancedUpdate_f (void) { // called once by IN_ReadJoystick and by user whenever an update is needed // cvars are now available int i; DWORD dwTemp; // initialize all the maps for (i = 0; i < JOY_MAX_AXES; i++) { dwAxisMap[i] = AxisNada; dwControlMap[i] = JOY_ABSOLUTE_AXIS; pdwRawValue[i] = RawValuePointer(i); } if (!joy_advanced.integer) { // default joystick initialization // 2 axes only with joystick control dwAxisMap[JOY_AXIS_X] = AxisTurn; // dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS; dwAxisMap[JOY_AXIS_Y] = AxisForward; // dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS; } else { if (strcmp (joy_name.string, "joystick") != 0) { // notify user of advanced controller Con_Printf ("\n%s configured\n\n", joy_name.string); } // advanced initialization here // data supplied by user via joy_axisn cvars dwTemp = (DWORD) joy_advaxisx.value; dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS; dwTemp = (DWORD) joy_advaxisy.value; dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS; dwTemp = (DWORD) joy_advaxisz.value; dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS; dwTemp = (DWORD) joy_advaxisr.value; dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS; dwTemp = (DWORD) joy_advaxisu.value; dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS; dwTemp = (DWORD) joy_advaxisv.value; dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f; dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS; } // compute the axes to collect from DirectInput joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV; for (i = 0; i < JOY_MAX_AXES; i++) { if (dwAxisMap[i] != AxisNada) { joy_flags |= dwAxisFlags[i]; } } } /* =============== IN_ReadJoystick =============== */ static qboolean IN_ReadJoystick (void) { memset (&ji, 0, sizeof(ji)); ji.dwSize = sizeof(ji); ji.dwFlags = joy_flags; if (joyGetPosEx (joy_id, &ji) == JOYERR_NOERROR) { // this is a hack -- there is a bug in the Logitech WingMan Warrior DirectInput Driver // rather than having 32768 be the zero point, they have the zero point at 32668 // go figure -- anyway, now we get the full resolution out of the device if (joy_wwhack1.integer) { ji.dwUpos += 100; } return true; } else { // read error occurred // turning off the joystick seems too harsh for 1 read error, // but what should be done? // Con_Printf ("%s: no response\n", __thisfunc__); // joy_avail = false; return false; } } /* =========== IN_Commands =========== */ void IN_Commands (void) { int i, key_index; DWORD buttonstate, povstate; if (!joy_avail || !in_joystick.integer) { return; } #ifndef H2W /* FIXME: why not in HexenWorld ? */ if (cls.state != ca_connected || cls.signon != SIGNONS) { if (joy_advancedinit != true) { Joy_AdvancedUpdate_f(); joy_advancedinit = true; } IN_ReadJoystick (); } #endif /* H2W */ // loop through the joystick buttons // key a joystick event or auxillary event for higher number buttons for each state change buttonstate = ji.dwButtons; for (i = 0; i < (int)joy_numbuttons; i++) { if ( (buttonstate & (1< 14000.0) fTemp = 14000.0; // restore direction information fAxisValue = (fAxisValue > 0.0) ? fTemp : -fTemp; } } // convert range from -32768..32767 to -1..1 fAxisValue /= 32768.0; switch (dwAxisMap[i]) { case AxisForward: if (!joy_advanced.integer && (in_mlook.state & 1)) { // user wants forward control to become look control if (fabs(fAxisValue) > joy_pitchthreshold.value) { // if mouse invert is on, invert the joystick pitch value // only absolute control support here (joy_advanced is false) if (m_pitch.value < 0.0) { cl.viewangles[PITCH] -= (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; } else { cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; } V_StopPitchDrift(); } } else { // user wants forward control to be forward control if (fabs(fAxisValue) > joy_forwardthreshold.value) { // cmd->forwardmove += (fAxisValue * joy_forwardsensitivity.value) * speed * cl_forwardspeed.value; cmd->forwardmove += (fAxisValue * joy_forwardsensitivity.value) * speed * 200; } } break; case AxisSide: if (fabs(fAxisValue) > joy_sidethreshold.value) { // cmd->sidemove += (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value; cmd->sidemove += (fAxisValue * joy_sidesensitivity.value) * speed * 225; } break; case AxisUp: if (fabs(fAxisValue) > joy_upthreshold.value) { // cmd->upmove += (fAxisValue * joy_upsensitivity.value) * speed * cl_upspeed.value; cmd->upmove += (fAxisValue * joy_upsensitivity.value) * speed * 200; } break; case AxisTurn: if ((in_strafe.state & 1) || (lookstrafe.integer && (in_mlook.state & 1))) { // user wants turn control to become side control if (fabs(fAxisValue) > joy_sidethreshold.value) { // cmd->sidemove -= (fAxisValue * joy_sidesensitivity.value) * speed * cl_sidespeed.value; cmd->sidemove -= (fAxisValue * joy_sidesensitivity.value) * speed * 225; } } else { // user wants turn control to be turn control if (fabs(fAxisValue) > joy_yawthreshold.value) { if (dwControlMap[i] == JOY_ABSOLUTE_AXIS) { cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * aspeed * cl_yawspeed.value; } else { cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity.value) * speed * 180.0; } } } break; case AxisLook: if (in_mlook.state & 1) { if (fabs(fAxisValue) > joy_pitchthreshold.value) { // pitch movement detected and pitch movement desired by user if (dwControlMap[i] == JOY_ABSOLUTE_AXIS) { cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * aspeed * cl_pitchspeed.value; } else { cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity.value) * speed * 180.0; } V_StopPitchDrift(); } } break; default: break; } } // bounds check pitch if (cl.viewangles[PITCH] > 80.0) cl.viewangles[PITCH] = 80.0; if (cl.viewangles[PITCH] < -70.0) cl.viewangles[PITCH] = -70.0; } engine/h2shared/input.h000066400000000000000000000035721444734033100153450ustar00rootroot00000000000000/* input.h -- external (non-keyboard) input devices * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_INPUT_H #define __HX2_INPUT_H void IN_Init (void); void IN_ReInit (void); void IN_Shutdown (void); void IN_Commands (void); /* for devices to add button commands on the script buffer */ void IN_Move (usercmd_t *cmd); /* add additional movement on top of the keyboard move cmd */ void IN_SendKeyEvents (void); /* used as a callback for Sys_SendKeyEvents() by some drivers */ void IN_ClearStates (void); #define IN_Accumulate() do {} while (0) void IN_ActivateMouse (void); void IN_DeactivateMouse (void); void IN_ShowMouse (void); void IN_HideMouse (void); #if defined(PLATFORM_WINDOWS) #undef IN_Accumulate void IN_Accumulate (void); /* accumulate winmouse movements during frame updates */ void IN_UpdateClipCursor (void); /* clip the mouse cursor to the window rectangle */ void IN_MouseEvent (int mstate); /* called from the window procedure */ void IN_SetQuakeMouseState (void); /* used by Scitech MGL video driver */ void IN_RestoreOriginalMouseState (void); /* used by Scitech MGL video driver */ #endif /* PLATFORM_WINDOWS */ #endif /* __HX2_INPUT_H */ engine/h2shared/keys.h000066400000000000000000000104511444734033100151530ustar00rootroot00000000000000/* * keys.h -- key definitions and keyboard public functions * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_KEYS_H #define __HX2_KEYS_H // // these are the key numbers that should be passed to Key_Event // #define K_TAB 9 #define K_ENTER 13 #define K_ESCAPE 27 #define K_SPACE 32 // normal keys should be passed as lowercased ascii #define K_BACKSPACE 127 #define K_UPARROW 128 #define K_DOWNARROW 129 #define K_LEFTARROW 130 #define K_RIGHTARROW 131 #define K_ALT 132 #define K_CTRL 133 #define K_SHIFT 134 #define K_F1 135 #define K_F2 136 #define K_F3 137 #define K_F4 138 #define K_F5 139 #define K_F6 140 #define K_F7 141 #define K_F8 142 #define K_F9 143 #define K_F10 144 #define K_F11 145 #define K_F12 146 #define K_INS 147 #define K_DEL 148 #define K_PGDN 149 #define K_PGUP 150 #define K_HOME 151 #define K_END 152 #define K_KP_NUMLOCK 153 #define K_KP_SLASH 154 #define K_KP_STAR 155 #define K_KP_MINUS 156 #define K_KP_HOME 157 #define K_KP_UPARROW 158 #define K_KP_PGUP 159 #define K_KP_PLUS 160 #define K_KP_LEFTARROW 161 #define K_KP_5 162 #define K_KP_RIGHTARROW 163 #define K_KP_END 164 #define K_KP_DOWNARROW 165 #define K_KP_PGDN 166 #define K_KP_ENTER 167 #define K_KP_INS 168 #define K_KP_DEL 169 #define K_COMMAND 170 #define K_PAUSE 255 // // mouse buttons generate virtual keys // #define K_MOUSE1 200 #define K_MOUSE2 201 /* right mouse button */ #define K_MOUSE3 202 /* middle mouse button */ #define K_MWHEELUP 203 /* wheel-up as a virtual button */ #define K_MWHEELDOWN 204 /* wheel-down as a virtual button */ #define K_MOUSE4 205 /* thumb buttons */ #define K_MOUSE5 206 /* thumb buttons */ // // joystick buttons // #define K_JOY1 207 #define K_JOY2 208 #define K_JOY3 209 #define K_JOY4 210 // aux keys are for multi-buttoned joysticks to generate so they can use // the normal binding process // aux29-32: reserved for the HAT (POV) switch motion #define K_AUX1 211 #define K_AUX2 212 #define K_AUX3 213 #define K_AUX4 214 #define K_AUX5 215 #define K_AUX6 216 #define K_AUX7 217 #define K_AUX8 218 #define K_AUX9 219 #define K_AUX10 220 #define K_AUX11 221 #define K_AUX12 222 #define K_AUX13 223 #define K_AUX14 224 #define K_AUX15 225 #define K_AUX16 226 #define K_AUX17 227 #define K_AUX18 228 #define K_AUX19 229 #define K_AUX20 230 #define K_AUX21 231 #define K_AUX22 232 #define K_AUX23 233 #define K_AUX24 234 #define K_AUX25 235 #define K_AUX26 236 #define K_AUX27 237 #define K_AUX28 238 #define K_AUX29 239 #define K_AUX30 240 #define K_AUX31 241 #define K_AUX32 242 #define MAXCMDLINE 256 #define key_game 0 #define key_console (1 << 0) #define key_message (1 << 1) #define key_menu (1 << 2) /* last valid keydest */ #define key_bindbit (key_menu << 1) #define key_menubind (key_menu | key_bindbit) typedef int keydest_t; extern char *keybindings[256]; extern int key_count; /* incremented every key event */ extern int key_lastpress; extern char key_lines[32][MAXCMDLINE]; extern int edit_line; extern int key_linepos; extern int key_insert; extern qboolean chat_team; void Key_Init (void); void Key_ClearStates (void); void Key_Event (int key, qboolean down); qboolean Key_IsGameKey (void); keydest_t Key_GetDest (void); void Key_SetDest (keydest_t dest); void Key_SetBinding (int keynum, const char *binding); const char *Key_KeynumToString (int keynum); void Key_WriteBindings (FILE *f); void Key_EndChat (void); const char *Key_GetChatBuffer (void); int Key_GetChatMsgLen (void); #endif /* __HX2_KEYS_H */ engine/h2shared/link_ops.c000066400000000000000000000024371444734033100160160ustar00rootroot00000000000000/* link_ops.c -- linked list stuff * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include "link_ops.h" /* ClearLink is used for new headnodes */ void ClearLink (link_t *l) { l->prev = l->next = l; } void RemoveLink (link_t *l) { l->next->prev = l->prev; l->prev->next = l->next; } void InsertLinkBefore (link_t *l, link_t *before) { l->next = before; l->prev = before->prev; l->prev->next = l; l->next->prev = l; } void InsertLinkAfter (link_t *l, link_t *after) { l->next = after->next; l->prev = after; l->prev->next = l; l->next->prev = l; } engine/h2shared/link_ops.h000066400000000000000000000024161444734033100160200ustar00rootroot00000000000000/* link_ops.h -- linked list stuff * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef LINKOPS_H #define LINKOPS_H typedef struct link_s { struct link_s *prev, *next; } link_t; // (type *)STRUCT_FROM_LINK(link_t *link, type, member) // ent = STRUCT_FROM_LINK(link,entity_t,order) // FIXME: remove this mess! #define STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - offsetof(t,m))) void ClearLink (link_t *l); void RemoveLink (link_t *l); void InsertLinkBefore (link_t *l, link_t *before); void InsertLinkAfter (link_t *l, link_t *after); #endif /* LINKOPS_H */ engine/h2shared/masm/000077500000000000000000000000001444734033100147635ustar00rootroot00000000000000engine/h2shared/masm/d_draw.asm000066400000000000000000000467421444734033100167420ustar00rootroot00000000000000; ; d_draw.asm -- for MASM ; x86 assembly-language horizontal 8-bpp span-drawing code. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _d_zistepu:dword externdef _d_pzbuffer:dword externdef _d_zistepv:dword externdef _d_zrowbytes:dword externdef _d_ziorigin:dword externdef _d_sdivzstepu:dword externdef _d_tdivzstepu:dword externdef _d_sdivzstepv:dword externdef _d_tdivzstepv:dword externdef _d_sdivzorigin:dword externdef _d_tdivzorigin:dword externdef _sadjust:dword externdef _tadjust:dword externdef _bbextents:dword externdef _bbextentt:dword externdef _cacheblock:dword externdef _d_viewbuffer:dword externdef _cachewidth:dword externdef _d_scantable:dword externdef _scanList:dword externdef _ZScanCount:dword ; externs from ASM-only code externdef float_point5:dword externdef Float2ToThe31nd:dword externdef izistep:dword externdef izi:dword externdef FloatMinus2ToThe31nd:dword externdef float_1:dword externdef float_particle_z_clip:dword externdef float_minus_1:dword externdef float_0:dword externdef fp_16:dword externdef fp_64k:dword externdef fp_1m:dword externdef fp_1m_minus_1:dword externdef fp_8:dword externdef entryvec_table:dword externdef advancetable:dword externdef sstep:dword externdef tstep:dword externdef pspantemp:dword externdef counttemp:dword externdef jumptemp:dword externdef reciprocal_table:dword externdef pbase:dword externdef s:dword externdef t:dword externdef sfracf:dword externdef tfracf:dword externdef snext:dword externdef tnext:dword externdef spancountminus1:dword externdef zi16stepu:dword externdef sdivz16stepu:dword externdef tdivz16stepu:dword externdef zi8stepu:dword externdef sdivz8stepu:dword externdef tdivz8stepu:dword externdef reciprocal_table_16:dword externdef entryvec_table_16:dword externdef fp_64kx64k:dword _TEXT SEGMENT LClampHigh0: mov esi,ds:dword ptr[_bbextents] jmp LClampReentry0 LClampHighOrLow0: jg LClampHigh0 xor esi,esi jmp LClampReentry0 LClampHigh1: mov edx,ds:dword ptr[_bbextentt] jmp LClampReentry1 LClampHighOrLow1: jg LClampHigh1 xor edx,edx jmp LClampReentry1 LClampLow2: mov ebp,2048 jmp LClampReentry2 LClampHigh2: mov ebp,ds:dword ptr[_bbextents] jmp LClampReentry2 LClampLow3: mov ecx,2048 jmp LClampReentry3 LClampHigh3: mov ecx,ds:dword ptr[_bbextentt] jmp LClampReentry3 LClampLow4: mov eax,2048 jmp LClampReentry4 LClampHigh4: mov eax,ds:dword ptr[_bbextents] jmp LClampReentry4 LClampLow5: mov ebx,2048 jmp LClampReentry5 LClampHigh5: mov ebx,ds:dword ptr[_bbextentt] jmp LClampReentry5 align 4 public _D_DrawSpans8 _D_DrawSpans8: push ebp push edi push esi push ebx fld ds:dword ptr[_d_sdivzstepu] fmul ds:dword ptr[fp_8] mov edx,ds:dword ptr[_cacheblock] fld ds:dword ptr[_d_tdivzstepu] fmul ds:dword ptr[fp_8] mov ebx,ds:dword ptr[4+16+esp] fld ds:dword ptr[_d_zistepu] fmul ds:dword ptr[fp_8] mov ds:dword ptr[pbase],edx fstp ds:dword ptr[zi8stepu] fstp ds:dword ptr[tdivz8stepu] fstp ds:dword ptr[sdivz8stepu] LSpanLoop: fild ds:dword ptr[4+ebx] fild ds:dword ptr[0+ebx] fld st(1) fmul ds:dword ptr[_d_sdivzstepv] fld st(1) fmul ds:dword ptr[_d_sdivzstepu] fld st(2) fmul ds:dword ptr[_d_tdivzstepu] fxch st(1) faddp st(2),st(0) fxch st(1) fld st(3) fmul ds:dword ptr[_d_tdivzstepv] fxch st(1) fadd ds:dword ptr[_d_sdivzorigin] fxch st(4) fmul ds:dword ptr[_d_zistepv] fxch st(1) faddp st(2),st(0) fxch st(2) fmul ds:dword ptr[_d_zistepu] fxch st(1) fadd ds:dword ptr[_d_tdivzorigin] fxch st(2) faddp st(1),st(0) fld ds:dword ptr[fp_64k] fxch st(1) fadd ds:dword ptr[_d_ziorigin] fdiv st(1),st(0) mov ecx,ds:dword ptr[_d_viewbuffer] mov eax,ds:dword ptr[4+ebx] mov ds:dword ptr[pspantemp],ebx mov edx,ds:dword ptr[_tadjust] mov esi,ds:dword ptr[_sadjust] mov edi,ds:dword ptr[_d_scantable+eax*4] add edi,ecx mov ecx,ds:dword ptr[0+ebx] add edi,ecx mov ecx,ds:dword ptr[8+ebx] cmp ecx,8 ja LSetupNotLast1 dec ecx jz LCleanup1 mov ds:dword ptr[spancountminus1],ecx fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] fild ds:dword ptr[spancountminus1] fld ds:dword ptr[_d_tdivzstepu] fld ds:dword ptr[_d_zistepu] fmul st(0),st(2) fxch st(1) fmul st(0),st(2) fxch st(2) fmul ds:dword ptr[_d_sdivzstepu] fxch st(1) faddp st(3),st(0) fxch st(1) faddp st(3),st(0) faddp st(3),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) jmp LFDIVInFlight1 LCleanup1: fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] jmp LFDIVInFlight1 align 4 LSetupNotLast1: fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] fadd ds:dword ptr[zi8stepu] fxch st(2) fadd ds:dword ptr[sdivz8stepu] fxch st(2) fld ds:dword ptr[tdivz8stepu] faddp st(2),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) LFDIVInFlight1: add esi,ds:dword ptr[s] add edx,ds:dword ptr[t] mov ebx,ds:dword ptr[_bbextents] mov ebp,ds:dword ptr[_bbextentt] cmp esi,ebx ja LClampHighOrLow0 LClampReentry0: mov ds:dword ptr[s],esi mov ebx,ds:dword ptr[pbase] shl esi,16 cmp edx,ebp mov ds:dword ptr[sfracf],esi ja LClampHighOrLow1 LClampReentry1: mov ds:dword ptr[t],edx mov esi,ds:dword ptr[s] shl edx,16 mov eax,ds:dword ptr[t] sar esi,16 mov ds:dword ptr[tfracf],edx sar eax,16 mov edx,ds:dword ptr[_cachewidth] imul eax,edx add esi,ebx add esi,eax cmp ecx,8 jna LLastSegment LNotLastSegment: fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[snext] fistp ds:dword ptr[tnext] mov eax,ds:dword ptr[snext] mov edx,ds:dword ptr[tnext] mov bl,ds:byte ptr[esi] sub ecx,8 mov ebp,ds:dword ptr[_sadjust] mov ds:dword ptr[counttemp],ecx mov ecx,ds:dword ptr[_tadjust] mov ds:byte ptr[edi],bl add ebp,eax add ecx,edx mov eax,ds:dword ptr[_bbextents] mov edx,ds:dword ptr[_bbextentt] cmp ebp,2048 jl LClampLow2 cmp ebp,eax ja LClampHigh2 LClampReentry2: cmp ecx,2048 jl LClampLow3 cmp ecx,edx ja LClampHigh3 LClampReentry3: mov ds:dword ptr[snext],ebp mov ds:dword ptr[tnext],ecx sub ebp,ds:dword ptr[s] sub ecx,ds:dword ptr[t] mov eax,ecx mov edx,ebp sar eax,19 jz LZero sar edx,19 mov ebx,ds:dword ptr[_cachewidth] imul eax,ebx jmp LSetUp1 LZero: sar edx,19 mov ebx,ds:dword ptr[_cachewidth] LSetUp1: add eax,edx mov edx,ds:dword ptr[tfracf] mov ds:dword ptr[advancetable+4],eax add eax,ebx shl ebp,13 mov ebx,ds:dword ptr[sfracf] shl ecx,13 mov ds:dword ptr[advancetable],eax mov ds:dword ptr[tstep],ecx add edx,ecx sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov al,ds:byte ptr[esi] add ebx,ebp mov ds:byte ptr[1+edi],al adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[2+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[3+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] mov ecx,ds:dword ptr[counttemp] cmp ecx,8 ja LSetupNotLast2 dec ecx jz LFDIVInFlight2 mov ds:dword ptr[spancountminus1],ecx fild ds:dword ptr[spancountminus1] fld ds:dword ptr[_d_zistepu] fmul st(0),st(1) fld ds:dword ptr[_d_tdivzstepu] fmul st(0),st(2) fxch st(1) faddp st(3),st(0) fxch st(1) fmul ds:dword ptr[_d_sdivzstepu] fxch st(1) faddp st(3),st(0) fld ds:dword ptr[fp_64k] fxch st(1) faddp st(4),st(0) fdiv st(0),st(1) jmp LFDIVInFlight2 align 4 LSetupNotLast2: fadd ds:dword ptr[zi8stepu] fxch st(2) fadd ds:dword ptr[sdivz8stepu] fxch st(2) fld ds:dword ptr[tdivz8stepu] faddp st(2),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) LFDIVInFlight2: mov ds:dword ptr[counttemp],ecx add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[4+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[5+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[6+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edi,8 mov ds:dword ptr[tfracf],edx mov edx,ds:dword ptr[snext] mov ds:dword ptr[sfracf],ebx mov ebx,ds:dword ptr[tnext] mov ds:dword ptr[s],edx mov ds:dword ptr[t],ebx mov ecx,ds:dword ptr[counttemp] cmp ecx,8 mov ds:byte ptr[-1+edi],al ja LNotLastSegment LLastSegment: test ecx,ecx jz LNoSteps fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[snext] fistp ds:dword ptr[tnext] mov al,ds:byte ptr[esi] mov ebx,ds:dword ptr[_tadjust] mov ds:byte ptr[edi],al mov eax,ds:dword ptr[_sadjust] add eax,ds:dword ptr[snext] add ebx,ds:dword ptr[tnext] mov ebp,ds:dword ptr[_bbextents] mov edx,ds:dword ptr[_bbextentt] cmp eax,2048 jl LClampLow4 cmp eax,ebp ja LClampHigh4 LClampReentry4: mov ds:dword ptr[snext],eax cmp ebx,2048 jl LClampLow5 cmp ebx,edx ja LClampHigh5 LClampReentry5: cmp ecx,1 je LOnlyOneStep sub eax,ds:dword ptr[s] sub ebx,ds:dword ptr[t] add eax,eax add ebx,ebx imul ds:dword ptr[reciprocal_table-8+ecx*4] mov ebp,edx mov eax,ebx imul ds:dword ptr[reciprocal_table-8+ecx*4] LSetEntryvec: mov ebx,ds:dword ptr[entryvec_table+ecx*4] mov eax,edx mov ds:dword ptr[jumptemp],ebx mov ecx,ebp sar edx,16 mov ebx,ds:dword ptr[_cachewidth] sar ecx,16 imul edx,ebx add edx,ecx mov ecx,ds:dword ptr[tfracf] mov ds:dword ptr[advancetable+4],edx add edx,ebx shl ebp,16 mov ebx,ds:dword ptr[sfracf] shl eax,16 mov ds:dword ptr[advancetable],edx mov ds:dword ptr[tstep],eax mov edx,ecx add edx,eax sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] jmp dword ptr[jumptemp] LNoSteps: mov al,ds:byte ptr[esi] sub edi,7 jmp LEndSpan LOnlyOneStep: sub eax,ds:dword ptr[s] sub ebx,ds:dword ptr[t] mov ebp,eax mov edx,ebx jmp LSetEntryvec public Entry2_8 Entry2_8: sub edi,6 mov al,ds:byte ptr[esi] jmp LLEntry2_8 public Entry3_8 Entry3_8: sub edi,5 add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] jmp LLEntry3_8 public Entry4_8 Entry4_8: sub edi,4 add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LLEntry4_8 public Entry5_8 Entry5_8: sub edi,3 add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LLEntry5_8 public Entry6_8 Entry6_8: sub edi,2 add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LLEntry6_8 public Entry7_8 Entry7_8: dec edi add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LLEntry7_8 public Entry8_8 Entry8_8: add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[1+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LLEntry7_8: sbb ecx,ecx mov ds:byte ptr[2+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LLEntry6_8: sbb ecx,ecx mov ds:byte ptr[3+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LLEntry5_8: sbb ecx,ecx mov ds:byte ptr[4+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LLEntry4_8: sbb ecx,ecx mov ds:byte ptr[5+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] LLEntry3_8: mov ds:byte ptr[6+edi],al mov al,ds:byte ptr[esi] LLEntry2_8: LEndSpan: fstp st(0) fstp st(0) fstp st(0) mov ebx,ds:dword ptr[pspantemp] mov ebx,ds:dword ptr[12+ebx] test ebx,ebx mov ds:byte ptr[7+edi],al jnz LSpanLoop pop ebx pop esi pop edi pop ebp ret LClamp: mov edx,040000000h xor ebx,ebx fstp st(0) jmp LZDraw LClampNeg: mov edx,040000000h xor ebx,ebx fstp st(0) jmp LZDrawNeg public _D_DrawZSpans _D_DrawZSpans: push ebp push edi push esi push ebx fld ds:dword ptr[_d_zistepu] mov eax,ds:dword ptr[_d_zistepu] mov esi,ds:dword ptr[4+16+esp] test eax,eax jz LFNegSpan fmul ds:dword ptr[Float2ToThe31nd] fistp ds:dword ptr[izistep] mov ebx,ds:dword ptr[izistep] LFSpanLoop: fild ds:dword ptr[4+esi] fild ds:dword ptr[0+esi] mov ecx,ds:dword ptr[4+esi] mov edi,ds:dword ptr[_d_pzbuffer] fmul ds:dword ptr[_d_zistepu] fxch st(1) fmul ds:dword ptr[_d_zistepv] fxch st(1) fadd ds:dword ptr[_d_ziorigin] imul ecx,ds:dword ptr[_d_zrowbytes] faddp st(1),st(0) fcom ds:dword ptr[float_point5] add edi,ecx mov edx,ds:dword ptr[0+esi] add edx,edx mov ecx,ds:dword ptr[8+esi] add edi,edx push esi fnstsw ax test ah,045h jz LClamp fmul ds:dword ptr[Float2ToThe31nd] fistp ds:dword ptr[izi] mov edx,ds:dword ptr[izi] LZDraw: test edi,2 jz LFMiddle mov eax,edx add edx,ebx shr eax,16 dec ecx mov ds:word ptr[edi],ax add edi,2 LFMiddle: push ecx shr ecx,1 jz LFLast shr ecx,1 jnc LFMiddleLoop mov eax,edx add edx,ebx shr eax,16 mov esi,edx add edx,ebx and esi,0FFFF0000h or eax,esi mov ds:dword ptr[edi],eax add edi,4 and ecx,ecx jz LFLast LFMiddleLoop: mov eax,edx add edx,ebx shr eax,16 mov esi,edx add edx,ebx and esi,0FFFF0000h or eax,esi mov ebp,edx mov ds:dword ptr[edi],eax add edx,ebx shr ebp,16 mov esi,edx add edx,ebx and esi,0FFFF0000h or ebp,esi mov ds:dword ptr[4+edi],ebp add edi,8 dec ecx jnz LFMiddleLoop LFLast: pop ecx pop esi and ecx,1 jz LFSpanDone shr edx,16 mov ds:word ptr[edi],dx LFSpanDone: mov esi,ds:dword ptr[12+esi] test esi,esi jnz LFSpanLoop jmp LFDone LFNegSpan: fmul ds:dword ptr[FloatMinus2ToThe31nd] fistp ds:dword ptr[izistep] mov ebx,ds:dword ptr[izistep] LFNegSpanLoop: fild ds:dword ptr[4+esi] fild ds:dword ptr[0+esi] mov ecx,ds:dword ptr[4+esi] mov edi,ds:dword ptr[_d_pzbuffer] fmul ds:dword ptr[_d_zistepu] fxch st(1) fmul ds:dword ptr[_d_zistepv] fxch st(1) fadd ds:dword ptr[_d_ziorigin] imul ecx,ds:dword ptr[_d_zrowbytes] faddp st(1),st(0) fcom ds:dword ptr[float_point5] add edi,ecx mov edx,ds:dword ptr[0+esi] add edx,edx mov ecx,ds:dword ptr[8+esi] add edi,edx push esi fnstsw ax test ah,045h jz LClampNeg fmul ds:dword ptr[Float2ToThe31nd] fistp ds:dword ptr[izi] mov edx,ds:dword ptr[izi] LZDrawNeg: test edi,2 jz LFNegMiddle mov eax,edx sub edx,ebx shr eax,16 dec ecx mov ds:word ptr[edi],ax add edi,2 LFNegMiddle: push ecx shr ecx,1 jz LFNegLast shr ecx,1 jnc LFNegMiddleLoop mov eax,edx sub edx,ebx shr eax,16 mov esi,edx sub edx,ebx and esi,0FFFF0000h or eax,esi mov ds:dword ptr[edi],eax add edi,4 and ecx,ecx jz LFNegLast LFNegMiddleLoop: mov eax,edx sub edx,ebx shr eax,16 mov esi,edx sub edx,ebx and esi,0FFFF0000h or eax,esi mov ebp,edx mov ds:dword ptr[edi],eax sub edx,ebx shr ebp,16 mov esi,edx sub edx,ebx and esi,0FFFF0000h or ebp,esi mov ds:dword ptr[4+edi],ebp add edi,8 dec ecx jnz LFNegMiddleLoop LFNegLast: pop ecx pop esi and ecx,1 jz LFNegSpanDone shr edx,16 mov ds:word ptr[edi],dx LFNegSpanDone: mov esi,ds:dword ptr[12+esi] test esi,esi jnz LFNegSpanLoop LFDone: pop ebx pop esi pop edi pop ebp ret LClamp2: mov edx,040000000h xor ebx,ebx fstp st(0) jmp LZDraw2 LClampNeg2: mov edx,040000000h xor ebx,ebx fstp st(0) jmp LZDrawNeg2 public _D_DrawSingleZSpans _D_DrawSingleZSpans: push ebp push edi push esi push ebx mov ds:dword ptr[_ZScanCount],0 fld ds:dword ptr[_d_zistepu] mov eax,ds:dword ptr[_d_zistepu] mov esi,ds:dword ptr[4+16+esp] test eax,eax jz LFNegSpan2 fmul ds:dword ptr[Float2ToThe31nd] fistp ds:dword ptr[izistep] mov ebx,ds:dword ptr[izistep] LFSpanLoop2: fild ds:dword ptr[4+esi] fild ds:dword ptr[0+esi] mov ecx,ds:dword ptr[4+esi] mov edi,ds:dword ptr[_d_pzbuffer] fmul ds:dword ptr[_d_zistepu] fxch st(1) fmul ds:dword ptr[_d_zistepv] fxch st(1) fadd ds:dword ptr[_d_ziorigin] imul ecx,ds:dword ptr[_d_zrowbytes] faddp st(1),st(0) fcom ds:dword ptr[float_point5] add edi,ecx mov edx,ds:dword ptr[0+esi] add edx,edx mov ecx,ds:dword ptr[8+esi] add edi,edx push esi fnstsw ax test ah,045h jz LClamp2 fmul ds:dword ptr[Float2ToThe31nd] fistp ds:dword ptr[izi] mov edx,ds:dword ptr[izi] LZDraw2: mov eax,edx add edx,ebx shr eax,16 mov ds:byte ptr[_scanList + ecx - 1], 1 cmp ds:word ptr[edi],ax jle LZSkip mov ds:byte ptr[_scanList + ecx - 1], 0 add ds:dword ptr[_ZScanCount],1 LZSkip: add edi,2 dec ecx jnz LZDraw2 pop esi jmp LFDone2 LFNegSpan2: fmul ds:dword ptr[FloatMinus2ToThe31nd] fistp ds:dword ptr[izistep] mov ebx,ds:dword ptr[izistep] LFNegSpanLoop2: fild ds:dword ptr[4+esi] fild ds:dword ptr[0+esi] mov ecx,ds:dword ptr[4+esi] mov edi,ds:dword ptr[_d_pzbuffer] fmul ds:dword ptr[_d_zistepu] fxch st(1) fmul ds:dword ptr[_d_zistepv] fxch st(1) fadd ds:dword ptr[_d_ziorigin] imul ecx,ds:dword ptr[_d_zrowbytes] faddp st(1),st(0) fcom ds:dword ptr[float_point5] add edi,ecx mov edx,ds:dword ptr[0+esi] add edx,edx mov ecx,ds:dword ptr[8+esi] add edi,edx push esi fnstsw ax test ah,045h jz LClampNeg2 fmul ds:dword ptr[Float2ToThe31nd] fistp ds:dword ptr[izi] mov edx,ds:dword ptr[izi] LZDrawNeg2: mov eax,edx sub edx,ebx shr eax,16 mov ds:byte ptr[_scanList + ecx - 1], 1 cmp ds:word ptr[edi],ax jle LZSkip2 mov ds:byte ptr[_scanList + ecx - 1], 0 LZSkip2: add edi,2 dec ecx jnz LZDraw2 pop esi LFDone2: pop ebx pop esi pop edi pop ebp ret _TEXT ENDS END engine/h2shared/masm/d_draw16.asm000066400000000000000000000433471444734033100171070ustar00rootroot00000000000000; ; d_draw16.asm -- for MASM ; x86 assembly-language horizontal 8-bpp span-drawing code, with 16-pixel ; subdivision. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _d_zistepu:dword externdef _d_pzbuffer:dword externdef _d_zistepv:dword externdef _d_zrowbytes:dword externdef _d_ziorigin:dword externdef _d_sdivzstepu:dword externdef _d_tdivzstepu:dword externdef _d_sdivzstepv:dword externdef _d_tdivzstepv:dword externdef _d_sdivzorigin:dword externdef _d_tdivzorigin:dword externdef _sadjust:dword externdef _tadjust:dword externdef _bbextents:dword externdef _bbextentt:dword externdef _cacheblock:dword externdef _d_viewbuffer:dword externdef _cachewidth:dword externdef _d_scantable:dword ; externs from ASM-only code externdef float_point5:dword externdef Float2ToThe31nd:dword externdef izistep:dword externdef izi:dword externdef FloatMinus2ToThe31nd:dword externdef float_1:dword externdef float_particle_z_clip:dword externdef float_minus_1:dword externdef float_0:dword externdef fp_16:dword externdef fp_64k:dword externdef fp_1m:dword externdef fp_1m_minus_1:dword externdef fp_8:dword externdef entryvec_table:dword externdef advancetable:dword externdef sstep:dword externdef tstep:dword externdef pspantemp:dword externdef counttemp:dword externdef jumptemp:dword externdef reciprocal_table:dword externdef pbase:dword externdef s:dword externdef t:dword externdef sfracf:dword externdef tfracf:dword externdef snext:dword externdef tnext:dword externdef spancountminus1:dword externdef zi16stepu:dword externdef sdivz16stepu:dword externdef tdivz16stepu:dword externdef zi8stepu:dword externdef sdivz8stepu:dword externdef tdivz8stepu:dword externdef reciprocal_table_16:dword externdef entryvec_table_16:dword externdef fp_64kx64k:dword _DATA SEGMENT _DATA ENDS _TEXT SEGMENT LClampHigh0: mov esi,ds:dword ptr[_bbextents] jmp LClampReentry0 LClampHighOrLow0: jg LClampHigh0 xor esi,esi jmp LClampReentry0 LClampHigh1: mov edx,ds:dword ptr[_bbextentt] jmp LClampReentry1 LClampHighOrLow1: jg LClampHigh1 xor edx,edx jmp LClampReentry1 LClampLow2: mov ebp,4096 jmp LClampReentry2 LClampHigh2: mov ebp,ds:dword ptr[_bbextents] jmp LClampReentry2 LClampLow3: mov ecx,4096 jmp LClampReentry3 LClampHigh3: mov ecx,ds:dword ptr[_bbextentt] jmp LClampReentry3 LClampLow4: mov eax,4096 jmp LClampReentry4 LClampHigh4: mov eax,ds:dword ptr[_bbextents] jmp LClampReentry4 LClampLow5: mov ebx,4096 jmp LClampReentry5 LClampHigh5: mov ebx,ds:dword ptr[_bbextentt] jmp LClampReentry5 align 4 public _D_DrawSpans16 _D_DrawSpans16: push ebp push edi push esi push ebx fld ds:dword ptr[_d_sdivzstepu] fmul ds:dword ptr[fp_16] mov edx,ds:dword ptr[_cacheblock] fld ds:dword ptr[_d_tdivzstepu] fmul ds:dword ptr[fp_16] mov ebx,ds:dword ptr[4+16+esp] fld ds:dword ptr[_d_zistepu] fmul ds:dword ptr[fp_16] mov ds:dword ptr[pbase],edx fstp ds:dword ptr[zi16stepu] fstp ds:dword ptr[tdivz16stepu] fstp ds:dword ptr[sdivz16stepu] LSpanLoop: fild ds:dword ptr[4+ebx] fild ds:dword ptr[0+ebx] fld st(1) fmul ds:dword ptr[_d_sdivzstepv] fld st(1) fmul ds:dword ptr[_d_sdivzstepu] fld st(2) fmul ds:dword ptr[_d_tdivzstepu] fxch st(1) faddp st(2),st(0) fxch st(1) fld st(3) fmul ds:dword ptr[_d_tdivzstepv] fxch st(1) fadd ds:dword ptr[_d_sdivzorigin] fxch st(4) fmul ds:dword ptr[_d_zistepv] fxch st(1) faddp st(2),st(0) fxch st(2) fmul ds:dword ptr[_d_zistepu] fxch st(1) fadd ds:dword ptr[_d_tdivzorigin] fxch st(2) faddp st(1),st(0) fld ds:dword ptr[fp_64k] fxch st(1) fadd ds:dword ptr[_d_ziorigin] fdiv st(1),st(0) mov ecx,ds:dword ptr[_d_viewbuffer] mov eax,ds:dword ptr[4+ebx] mov ds:dword ptr[pspantemp],ebx mov edx,ds:dword ptr[_tadjust] mov esi,ds:dword ptr[_sadjust] mov edi,ds:dword ptr[_d_scantable+eax*4] add edi,ecx mov ecx,ds:dword ptr[0+ebx] add edi,ecx mov ecx,ds:dword ptr[8+ebx] cmp ecx,16 ja LSetupNotLast1 dec ecx jz LCleanup1 mov ds:dword ptr[spancountminus1],ecx fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] fild ds:dword ptr[spancountminus1] fld ds:dword ptr[_d_tdivzstepu] fld ds:dword ptr[_d_zistepu] fmul st(0),st(2) fxch st(1) fmul st(0),st(2) fxch st(2) fmul ds:dword ptr[_d_sdivzstepu] fxch st(1) faddp st(3),st(0) fxch st(1) faddp st(3),st(0) faddp st(3),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) jmp LFDIVInFlight1 LCleanup1: fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] jmp LFDIVInFlight1 align 4 LSetupNotLast1: fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] fadd ds:dword ptr[zi16stepu] fxch st(2) fadd ds:dword ptr[sdivz16stepu] fxch st(2) fld ds:dword ptr[tdivz16stepu] faddp st(2),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) LFDIVInFlight1: add esi,ds:dword ptr[s] add edx,ds:dword ptr[t] mov ebx,ds:dword ptr[_bbextents] mov ebp,ds:dword ptr[_bbextentt] cmp esi,ebx ja LClampHighOrLow0 LClampReentry0: mov ds:dword ptr[s],esi mov ebx,ds:dword ptr[pbase] shl esi,16 cmp edx,ebp mov ds:dword ptr[sfracf],esi ja LClampHighOrLow1 LClampReentry1: mov ds:dword ptr[t],edx mov esi,ds:dword ptr[s] shl edx,16 mov eax,ds:dword ptr[t] sar esi,16 mov ds:dword ptr[tfracf],edx sar eax,16 mov edx,ds:dword ptr[_cachewidth] imul eax,edx add esi,ebx add esi,eax cmp ecx,16 jna LLastSegment LNotLastSegment: fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[snext] fistp ds:dword ptr[tnext] mov eax,ds:dword ptr[snext] mov edx,ds:dword ptr[tnext] mov bl,ds:byte ptr[esi] sub ecx,16 mov ebp,ds:dword ptr[_sadjust] mov ds:dword ptr[counttemp],ecx mov ecx,ds:dword ptr[_tadjust] mov ds:byte ptr[edi],bl add ebp,eax add ecx,edx mov eax,ds:dword ptr[_bbextents] mov edx,ds:dword ptr[_bbextentt] cmp ebp,4096 jl LClampLow2 cmp ebp,eax ja LClampHigh2 LClampReentry2: cmp ecx,4096 jl LClampLow3 cmp ecx,edx ja LClampHigh3 LClampReentry3: mov ds:dword ptr[snext],ebp mov ds:dword ptr[tnext],ecx sub ebp,ds:dword ptr[s] sub ecx,ds:dword ptr[t] mov eax,ecx mov edx,ebp sar eax,20 jz LZero sar edx,20 mov ebx,ds:dword ptr[_cachewidth] imul eax,ebx jmp LSetUp1 LZero: sar edx,20 mov ebx,ds:dword ptr[_cachewidth] LSetUp1: add eax,edx mov edx,ds:dword ptr[tfracf] mov ds:dword ptr[advancetable+4],eax add eax,ebx shl ebp,12 mov ebx,ds:dword ptr[sfracf] shl ecx,12 mov ds:dword ptr[advancetable],eax mov ds:dword ptr[tstep],ecx add edx,ecx sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov al,ds:byte ptr[esi] add ebx,ebp mov ds:byte ptr[1+edi],al adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[2+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[3+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[4+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[5+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[6+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[7+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] mov ecx,ds:dword ptr[counttemp] cmp ecx,16 ja LSetupNotLast2 dec ecx jz LFDIVInFlight2 mov ds:dword ptr[spancountminus1],ecx fild ds:dword ptr[spancountminus1] fld ds:dword ptr[_d_zistepu] fmul st(0),st(1) fld ds:dword ptr[_d_tdivzstepu] fmul st(0),st(2) fxch st(1) faddp st(3),st(0) fxch st(1) fmul ds:dword ptr[_d_sdivzstepu] fxch st(1) faddp st(3),st(0) fld ds:dword ptr[fp_64k] fxch st(1) faddp st(4),st(0) fdiv st(0),st(1) jmp LFDIVInFlight2 align 4 LSetupNotLast2: fadd ds:dword ptr[zi16stepu] fxch st(2) fadd ds:dword ptr[sdivz16stepu] fxch st(2) fld ds:dword ptr[tdivz16stepu] faddp st(2),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) LFDIVInFlight2: mov ds:dword ptr[counttemp],ecx add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[8+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[9+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[10+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[11+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[12+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[13+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[14+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edi,16 mov ds:dword ptr[tfracf],edx mov edx,ds:dword ptr[snext] mov ds:dword ptr[sfracf],ebx mov ebx,ds:dword ptr[tnext] mov ds:dword ptr[s],edx mov ds:dword ptr[t],ebx mov ecx,ds:dword ptr[counttemp] cmp ecx,16 mov ds:byte ptr[-1+edi],al ja LNotLastSegment LLastSegment: test ecx,ecx jz LNoSteps fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[snext] fistp ds:dword ptr[tnext] mov al,ds:byte ptr[esi] mov ebx,ds:dword ptr[_tadjust] mov ds:byte ptr[edi],al mov eax,ds:dword ptr[_sadjust] add eax,ds:dword ptr[snext] add ebx,ds:dword ptr[tnext] mov ebp,ds:dword ptr[_bbextents] mov edx,ds:dword ptr[_bbextentt] cmp eax,4096 jl LClampLow4 cmp eax,ebp ja LClampHigh4 LClampReentry4: mov ds:dword ptr[snext],eax cmp ebx,4096 jl LClampLow5 cmp ebx,edx ja LClampHigh5 LClampReentry5: cmp ecx,1 je LOnlyOneStep sub eax,ds:dword ptr[s] sub ebx,ds:dword ptr[t] add eax,eax add ebx,ebx imul ds:dword ptr[reciprocal_table_16-8+ecx*4] mov ebp,edx mov eax,ebx imul ds:dword ptr[reciprocal_table_16-8+ecx*4] LSetEntryvec: mov ebx,ds:dword ptr[entryvec_table_16+ecx*4] mov eax,edx mov ds:dword ptr[jumptemp],ebx mov ecx,ebp sar edx,16 mov ebx,ds:dword ptr[_cachewidth] sar ecx,16 imul edx,ebx add edx,ecx mov ecx,ds:dword ptr[tfracf] mov ds:dword ptr[advancetable+4],edx add edx,ebx shl ebp,16 mov ebx,ds:dword ptr[sfracf] shl eax,16 mov ds:dword ptr[advancetable],edx mov ds:dword ptr[tstep],eax mov edx,ecx add edx,eax sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] jmp dword ptr[jumptemp] LNoSteps: mov al,ds:byte ptr[esi] sub edi,15 jmp LEndSpan LOnlyOneStep: sub eax,ds:dword ptr[s] sub ebx,ds:dword ptr[t] mov ebp,eax mov edx,ebx jmp LSetEntryvec public Entry2_16, Entry3_16, Entry4_16, Entry5_16 public Entry6_16, Entry7_16, Entry8_16, Entry9_16 public Entry10_16, Entry11_16, Entry12_16, Entry13_16 public Entry14_16, Entry15_16, Entry16_16 Entry2_16: sub edi,14 mov al,ds:byte ptr[esi] jmp LEntry2_16 Entry3_16: sub edi,13 add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] jmp LEntry3_16 Entry4_16: sub edi,12 add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry4_16 Entry5_16: sub edi,11 add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry5_16 Entry6_16: sub edi,10 add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry6_16 Entry7_16: sub edi,9 add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry7_16 Entry8_16: sub edi,8 add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry8_16 Entry9_16: sub edi,7 add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry9_16 Entry10_16: sub edi,6 add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry10_16 Entry11_16: sub edi,5 add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry11_16 Entry12_16: sub edi,4 add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry12_16 Entry13_16: sub edi,3 add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry13_16 Entry14_16: sub edi,2 add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry14_16 Entry15_16: dec edi add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry15_16 Entry16_16: add edx,eax mov al,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ds:byte ptr[1+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry15_16: sbb ecx,ecx mov ds:byte ptr[2+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry14_16: sbb ecx,ecx mov ds:byte ptr[3+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry13_16: sbb ecx,ecx mov ds:byte ptr[4+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry12_16: sbb ecx,ecx mov ds:byte ptr[5+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry11_16: sbb ecx,ecx mov ds:byte ptr[6+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry10_16: sbb ecx,ecx mov ds:byte ptr[7+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry9_16: sbb ecx,ecx mov ds:byte ptr[8+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry8_16: sbb ecx,ecx mov ds:byte ptr[9+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry7_16: sbb ecx,ecx mov ds:byte ptr[10+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry6_16: sbb ecx,ecx mov ds:byte ptr[11+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry5_16: sbb ecx,ecx mov ds:byte ptr[12+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry4_16: sbb ecx,ecx mov ds:byte ptr[13+edi],al add ebx,ebp mov al,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] LEntry3_16: mov ds:byte ptr[14+edi],al mov al,ds:byte ptr[esi] LEntry2_16: LEndSpan: fstp st(0) fstp st(0) fstp st(0) mov ebx,ds:dword ptr[pspantemp] mov ebx,ds:dword ptr[12+ebx] test ebx,ebx mov ds:byte ptr[15+edi],al jnz LSpanLoop pop ebx pop esi pop edi pop ebp ret _TEXT ENDS END engine/h2shared/masm/d_draw16t.asm000066400000000000000000000634151444734033100172710ustar00rootroot00000000000000; ; d_draw16t.asm -- for MASM ; x86 assembly-language horizontal 8-bpp span-drawing code, with 16-pixel ; subdivision and translucency handling. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _d_zistepu:dword externdef _d_pzbuffer:dword externdef _d_zistepv:dword externdef _d_zrowbytes:dword externdef _d_ziorigin:dword externdef _d_sdivzstepu:dword externdef _d_tdivzstepu:dword externdef _d_sdivzstepv:dword externdef _d_tdivzstepv:dword externdef _d_sdivzorigin:dword externdef _d_tdivzorigin:dword externdef _sadjust:dword externdef _tadjust:dword externdef _bbextents:dword externdef _bbextentt:dword externdef _cacheblock:dword externdef _d_viewbuffer:dword externdef _cachewidth:dword externdef _d_scantable:dword externdef _mainTransTable:dword externdef _scanList:dword externdef _D_DrawSingleZSpans:dword ; externs from ASM-only code externdef float_point5:dword externdef Float2ToThe31nd:dword externdef izistep:dword externdef izi:dword externdef FloatMinus2ToThe31nd:dword externdef float_1:dword externdef float_particle_z_clip:dword externdef float_minus_1:dword externdef float_0:dword externdef fp_16:dword externdef fp_64k:dword externdef fp_1m:dword externdef fp_1m_minus_1:dword externdef fp_8:dword externdef entryvec_table:dword externdef advancetable:dword externdef sstep:dword externdef tstep:dword externdef pspantemp:dword externdef counttemp:dword externdef jumptemp:dword externdef reciprocal_table:dword externdef pbase:dword externdef s:dword externdef t:dword externdef sfracf:dword externdef tfracf:dword externdef snext:dword externdef tnext:dword externdef spancountminus1:dword externdef zi16stepu:dword externdef sdivz16stepu:dword externdef tdivz16stepu:dword externdef zi8stepu:dword externdef sdivz8stepu:dword externdef tdivz8stepu:dword externdef reciprocal_table_16:dword externdef entryvec_table_16T:dword externdef fp_64kx64k:dword _DATA SEGMENT masktemp dw 0 _DATA ENDS _TEXT SEGMENT public _D_Draw16StartT _D_Draw16StartT: LClampHigh0: mov esi,ds:dword ptr[_bbextents] jmp LClampReentry0 LClampHighOrLow0: jg LClampHigh0 xor esi,esi jmp LClampReentry0 LClampHigh1: mov edx,ds:dword ptr[_bbextentt] jmp LClampReentry1 LClampHighOrLow1: jg LClampHigh1 xor edx,edx jmp LClampReentry1 LClampLow2: mov ebp,4096 jmp LClampReentry2 LClampHigh2: mov ebp,ds:dword ptr[_bbextents] jmp LClampReentry2 LClampLow3: mov ecx,4096 jmp LClampReentry3 LClampHigh3: mov ecx,ds:dword ptr[_bbextentt] jmp LClampReentry3 LClampLow4: mov eax,4096 jmp LClampReentry4 LClampHigh4: mov eax,ds:dword ptr[_bbextents] jmp LClampReentry4 LClampLow5: mov ebx,4096 jmp LClampReentry5 LClampHigh5: mov ebx,ds:dword ptr[_bbextentt] jmp LClampReentry5 align 4 public _D_DrawSpans16T _D_DrawSpans16T: push ebp push edi push esi push ebx fld ds:dword ptr[_d_sdivzstepu] fmul ds:dword ptr[fp_16] mov edx,ds:dword ptr[_cacheblock] fld ds:dword ptr[_d_tdivzstepu] fmul ds:dword ptr[fp_16] mov ebx,ds:dword ptr[4+16+esp] fld ds:dword ptr[_d_zistepu] fmul ds:dword ptr[fp_16] mov ds:dword ptr[pbase],edx fstp ds:dword ptr[zi16stepu] fstp ds:dword ptr[tdivz16stepu] fstp ds:dword ptr[sdivz16stepu] LSpanLoop: fild ds:dword ptr[4+ebx] fild ds:dword ptr[0+ebx] fld st(1) fmul ds:dword ptr[_d_sdivzstepv] fld st(1) fmul ds:dword ptr[_d_sdivzstepu] fld st(2) fmul ds:dword ptr[_d_tdivzstepu] fxch st(1) faddp st(2),st(0) fxch st(1) fld st(3) fmul ds:dword ptr[_d_tdivzstepv] fxch st(1) fadd ds:dword ptr[_d_sdivzorigin] fxch st(4) fmul ds:dword ptr[_d_zistepv] fxch st(1) faddp st(2),st(0) fxch st(2) fmul ds:dword ptr[_d_zistepu] fxch st(1) fadd ds:dword ptr[_d_tdivzorigin] fxch st(2) faddp st(1),st(0) fld ds:dword ptr[fp_64k] fxch st(1) fadd ds:dword ptr[_d_ziorigin] fdiv st(1),st(0) mov ecx,ds:dword ptr[_d_viewbuffer] mov eax,ds:dword ptr[4+ebx] mov ds:dword ptr[pspantemp],ebx push eax push ecx push edx push ebx call near ptr _D_DrawSingleZSpans pop ebx pop edx pop ecx pop eax mov edx,ds:dword ptr[_tadjust] mov esi,ds:dword ptr[_sadjust] mov edi,ds:dword ptr[_d_scantable+eax*4] add edi,ecx mov ecx,ds:dword ptr[0+ebx] add edi,ecx mov ecx,ds:dword ptr[8+ebx] cmp ecx,16 ja LSetupNotLast1 dec ecx jz LCleanup1 mov ds:dword ptr[spancountminus1],ecx fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] fild ds:dword ptr[spancountminus1] fld ds:dword ptr[_d_tdivzstepu] fld ds:dword ptr[_d_zistepu] fmul st(0),st(2) fxch st(1) fmul st(0),st(2) fxch st(2) fmul ds:dword ptr[_d_sdivzstepu] fxch st(1) faddp st(3),st(0) fxch st(1) faddp st(3),st(0) faddp st(3),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) jmp LFDIVInFlight1 LCleanup1: fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] jmp LFDIVInFlight1 align 4 LSetupNotLast1: fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] fadd ds:dword ptr[zi16stepu] fxch st(2) fadd ds:dword ptr[sdivz16stepu] fxch st(2) fld ds:dword ptr[tdivz16stepu] faddp st(2),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) LFDIVInFlight1: add esi,ds:dword ptr[s] add edx,ds:dword ptr[t] mov ebx,ds:dword ptr[_bbextents] mov ebp,ds:dword ptr[_bbextentt] cmp esi,ebx ja LClampHighOrLow0 LClampReentry0: mov ds:dword ptr[s],esi mov ebx,ds:dword ptr[pbase] shl esi,16 cmp edx,ebp mov ds:dword ptr[sfracf],esi ja LClampHighOrLow1 LClampReentry1: mov ds:dword ptr[t],edx mov esi,ds:dword ptr[s] shl edx,16 mov eax,ds:dword ptr[t] sar esi,16 mov ds:dword ptr[tfracf],edx sar eax,16 mov edx,ds:dword ptr[_cachewidth] imul eax,edx add esi,ebx add esi,eax cmp ecx,16 jna LLastSegment LNotLastSegment: fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[snext] fistp ds:dword ptr[tnext] mov eax,ds:dword ptr[snext] mov edx,ds:dword ptr[tnext] xor ebx,ebx add bl,ds:byte ptr[_scanList + ecx - 1] shl ebx,1 add bl,ds:byte ptr[_scanList + ecx - 2] shl ebx,1 add bl,ds:byte ptr[_scanList + ecx - 3] shl ebx,1 add bl,ds:byte ptr[_scanList + ecx - 4] shl ebx,1 add bl,ds:byte ptr[_scanList + ecx - 5] shl ebx,1 add bl,ds:byte ptr[_scanList + ecx - 6] shl ebx,1 add bl,ds:byte ptr[_scanList + ecx - 7] shl ebx,1 add bl,ds:byte ptr[_scanList + ecx - 8] shl ebx,1 add bl,ds:byte ptr[_scanList + ecx - 9] shl ebx,1 add bl,ds:byte ptr[_scanList + ecx - 10] shl ebx,1 add bl,ds:byte ptr[_scanList + ecx - 11] shl ebx,1 add bl,ds:byte ptr[_scanList + ecx - 12] shl ebx,1 add bl,ds:byte ptr[_scanList + ecx - 13] shl ebx,1 add bl,ds:byte ptr[_scanList + ecx - 14] shl ebx,1 add bl,ds:byte ptr[_scanList + ecx - 15] shl ebx,1 add bl,ds:byte ptr[_scanList + ecx - 16] ;mov bx, 8000h ; mov bx, 0ffffh ; mov bx, 0h mov masktemp, bx mov bh,ds:byte ptr[esi] sub ecx,16 mov ebp,ds:dword ptr[_sadjust] mov ds:dword ptr[counttemp],ecx mov ecx,ds:dword ptr[_tadjust] ;and masktemp, 8000h bt masktemp, 15 jnc SkipTran1 ;rj mov bl,ds:byte ptr[edi] and ebx, 0ffffh mov bl,ds:byte ptr[12345678h + ebx] TranPatch1: mov ds:byte ptr[edi],bl SkipTran1: add ebp,eax add ecx,edx mov eax,ds:dword ptr[_bbextents] mov edx,ds:dword ptr[_bbextentt] cmp ebp,4096 jl LClampLow2 cmp ebp,eax ja LClampHigh2 LClampReentry2: cmp ecx,4096 jl LClampLow3 cmp ecx,edx ja LClampHigh3 LClampReentry3: mov ds:dword ptr[snext],ebp mov ds:dword ptr[tnext],ecx sub ebp,ds:dword ptr[s] sub ecx,ds:dword ptr[t] mov eax,ecx mov edx,ebp sar eax,20 jz LZero sar edx,20 mov ebx,ds:dword ptr[_cachewidth] imul eax,ebx jmp LSetUp1 LZero: sar edx,20 mov ebx,ds:dword ptr[_cachewidth] LSetUp1: add eax,edx mov edx,ds:dword ptr[tfracf] mov ds:dword ptr[advancetable+4],eax add eax,ebx shl ebp,12 mov ebx,ds:dword ptr[sfracf] shl ecx,12 mov ds:dword ptr[advancetable],eax mov ds:dword ptr[tstep],ecx add edx,ecx sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] mov ah,ds:byte ptr[esi] ; and masktemp, 4000h bt masktemp, 14 jnc SkipTran2 ;rj mov al,ds:byte ptr[1+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch2: mov ds:byte ptr[1+edi],al SkipTran2: add edx,ds:dword ptr[tstep] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ah,ds:byte ptr[esi] ; and masktemp, 2000h bt masktemp, 13 jnc SkipTran3 ;rj mov al,ds:byte ptr[2+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch3: mov ds:byte ptr[2+edi],al SkipTran3: add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ah,ds:byte ptr[esi] ; and masktemp, 1000h bt masktemp, 12 jnc SkipTran4 ;rj mov al,ds:byte ptr[3+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch4: mov ds:byte ptr[3+edi],al SkipTran4: add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ah,ds:byte ptr[esi] ; and masktemp, 0800h bt masktemp, 11 jnc SkipTran5 ;rj mov al,ds:byte ptr[4+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch5: mov ds:byte ptr[4+edi],al SkipTran5: add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ah,ds:byte ptr[esi] ; and masktemp, 0400h bt masktemp, 10 jnc SkipTran6 ;rj mov al,ds:byte ptr[5+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch6: mov ds:byte ptr[5+edi],al SkipTran6: ; rj speed test add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ah,ds:byte ptr[esi] ; and masktemp, 0200h bt masktemp, 9 jnc SkipTran7 ;rj mov al,ds:byte ptr[6+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch7: mov ds:byte ptr[6+edi],al ; add ebx,ebp ; adc esi,ds:dword ptr[advancetable+4+ecx*4] ; xor eax, eax ; add edx,ds:dword ptr[tstep] ; mov al,ds:byte ptr[6+edi] ; sbb ecx,ecx ; mov ah,ds:byte ptr[esi] ;rj ; add eax,ds:dword ptr[_mainTransTable] ; mov al,ds:byte ptr[eax + _mainTransTable] ; mov ds:byte ptr[6+edi],al SkipTran7: add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ah,ds:byte ptr[esi] ; and masktemp, 0100h bt masktemp, 8 jnc SkipTran8 ;rj mov al,ds:byte ptr[7+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch8: mov ds:byte ptr[7+edi],al SkipTran8: add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx add ebx,ebp mov ah,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] mov ecx,ds:dword ptr[counttemp] cmp ecx,16 ja LSetupNotLast2 dec ecx jz LFDIVInFlight2 mov ds:dword ptr[spancountminus1],ecx fild ds:dword ptr[spancountminus1] fld ds:dword ptr[_d_zistepu] fmul st(0),st(1) fld ds:dword ptr[_d_tdivzstepu] fmul st(0),st(2) fxch st(1) faddp st(3),st(0) fxch st(1) fmul ds:dword ptr[_d_sdivzstepu] fxch st(1) faddp st(3),st(0) fld ds:dword ptr[fp_64k] fxch st(1) faddp st(4),st(0) fdiv st(0),st(1) jmp LFDIVInFlight2 align 4 LSetupNotLast2: fadd ds:dword ptr[zi16stepu] fxch st(2) fadd ds:dword ptr[sdivz16stepu] fxch st(2) fld ds:dword ptr[tdivz16stepu] faddp st(2),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) LFDIVInFlight2: mov ds:dword ptr[counttemp],ecx add edx,ds:dword ptr[tstep] sbb ecx,ecx bt masktemp, 7 jnc SkipTran9 ;rj mov al,ds:byte ptr[8+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch9: mov ds:byte ptr[8+edi],al SkipTran9: mov ah,ds:byte ptr[esi] bt masktemp, 6 jnc SkipTran10 ;rj mov al,ds:byte ptr[9+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch10: mov ds:byte ptr[9+edi],al SkipTran10: add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ah,ds:byte ptr[esi] bt masktemp, 5 jnc SkipTran11 ;rj mov al,ds:byte ptr[10+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch11: mov ds:byte ptr[10+edi],al SkipTran11: add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ah,ds:byte ptr[esi] bt masktemp, 4 jnc SkipTran12 ;rj mov al,ds:byte ptr[11+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch12: mov ds:byte ptr[11+edi],al SkipTran12: add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ah,ds:byte ptr[esi] bt masktemp, 3 jnc SkipTran13 ;rj mov al,ds:byte ptr[12+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch13: mov ds:byte ptr[12+edi],al SkipTran13: add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ah,ds:byte ptr[esi] bt masktemp, 2 jnc SkipTran14 ;rj mov al,ds:byte ptr[13+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch14: mov ds:byte ptr[13+edi],al SkipTran14: add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx mov ah,ds:byte ptr[esi] bt masktemp, 1 jnc SkipTran15 ;rj mov al,ds:byte ptr[14+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch15: mov ds:byte ptr[14+edi],al SkipTran15: add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx add ebx,ebp mov ah,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edi,16 mov ds:dword ptr[tfracf],edx mov edx,ds:dword ptr[snext] mov ds:dword ptr[sfracf],ebx mov ebx,ds:dword ptr[tnext] mov ds:dword ptr[s],edx mov ds:dword ptr[t],ebx mov ecx,ds:dword ptr[counttemp] bt masktemp, 0 jnc SkipTran16 ;rj mov al,ds:byte ptr[-1+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch16: mov ds:byte ptr[-1+edi],al SkipTran16: cmp ecx,16 ja LNotLastSegment LLastSegment: test ecx,ecx jz LNoSteps fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[snext] fistp ds:dword ptr[tnext] mov ah,ds:byte ptr[esi] mov ebx,ds:dword ptr[_tadjust] cmp ds:byte ptr[_scanList + ecx - 1], 1 jnz SkipTran17 ;rj mov al,ds:byte ptr[edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch17: mov ds:byte ptr[edi],al SkipTran17: mov eax,ds:dword ptr[_sadjust] add eax,ds:dword ptr[snext] add ebx,ds:dword ptr[tnext] mov ebp,ds:dword ptr[_bbextents] mov edx,ds:dword ptr[_bbextentt] cmp eax,4096 jl LClampLow4 cmp eax,ebp ja LClampHigh4 LClampReentry4: mov ds:dword ptr[snext],eax cmp ebx,4096 jl LClampLow5 cmp ebx,edx ja LClampHigh5 LClampReentry5: cmp ecx,1 je LOnlyOneStep sub eax,ds:dword ptr[s] sub ebx,ds:dword ptr[t] add eax,eax add ebx,ebx imul ds:dword ptr[reciprocal_table_16-8+ecx*4] mov ebp,edx mov eax,ebx imul ds:dword ptr[reciprocal_table_16-8+ecx*4] LSetEntryvec: mov ebx,ds:dword ptr[entryvec_table_16T+ecx*4] mov eax,edx mov ds:dword ptr[jumptemp],ebx mov ecx,ebp sar edx,16 mov ebx,ds:dword ptr[_cachewidth] sar ecx,16 imul edx,ebx add edx,ecx mov ecx,ds:dword ptr[tfracf] mov ds:dword ptr[advancetable+4],edx add edx,ebx shl ebp,16 mov ebx,ds:dword ptr[sfracf] shl eax,16 mov ds:dword ptr[advancetable],edx mov ds:dword ptr[tstep],eax mov edx,ecx add edx,eax sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] jmp dword ptr[jumptemp] LNoSteps: mov ah,ds:byte ptr[esi] sub edi,15 jmp LEndSpan LOnlyOneStep: sub eax,ds:dword ptr[s] sub ebx,ds:dword ptr[t] mov ebp,eax mov edx,ebx jmp LSetEntryvec public Entry2_16T, Entry3_16T, Entry4_16T, Entry5_16T public Entry6_16T, Entry7_16T, Entry8_16T, Entry9_16T public Entry10_16T, Entry11_16T, Entry12_16T, Entry13_16T public Entry14_16T, Entry15_16T, Entry16_16T Entry2_16T: sub edi,14 mov ah,ds:byte ptr[esi] jmp LEntry2_16 Entry3_16T: sub edi,13 add edx,eax mov ah,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] jmp LEntry3_16 Entry4_16T: sub edi,12 add edx,eax mov ah,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry4_16 Entry5_16T: sub edi,11 add edx,eax mov ah,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry5_16 Entry6_16T: sub edi,10 add edx,eax mov ah,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry6_16 Entry7_16T: sub edi,9 add edx,eax mov ah,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry7_16 Entry8_16T: sub edi,8 add edx,eax mov ah,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry8_16 Entry9_16T: sub edi,7 add edx,eax mov ah,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry9_16 Entry10_16T: sub edi,6 add edx,eax mov ah,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry10_16 Entry11_16T: sub edi,5 add edx,eax mov ah,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry11_16 Entry12_16T: sub edi,4 add edx,eax mov ah,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry12_16 Entry13_16T: sub edi,3 add edx,eax mov ah,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry13_16 Entry14_16T: sub edi,2 add edx,eax mov ah,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry14_16 Entry15_16T: dec edi add edx,eax mov ah,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] jmp LEntry15_16 Entry16_16T: add edx,eax mov ah,ds:byte ptr[esi] sbb ecx,ecx add ebx,ebp adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] sbb ecx,ecx cmp ds:byte ptr[_scanList + 14], 1 jnz SkipTran18 ;rj mov al,ds:byte ptr[1+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch18: mov ds:byte ptr[1+edi],al SkipTran18: add ebx,ebp mov ah,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry15_16: sbb ecx,ecx cmp ds:byte ptr[_scanList + 13], 1 jnz SkipTran19 ;rj mov al,ds:byte ptr[2+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch19: mov ds:byte ptr[2+edi],al SkipTran19: add ebx,ebp mov ah,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry14_16: sbb ecx,ecx cmp ds:byte ptr[_scanList + 12], 1 jnz SkipTran20 ;rj mov al,ds:byte ptr[3+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch20: mov ds:byte ptr[3+edi],al SkipTran20: add ebx,ebp mov ah,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry13_16: sbb ecx,ecx cmp ds:byte ptr[_scanList + 11], 1 jnz SkipTran21 ;rj mov al,ds:byte ptr[4+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch21: mov ds:byte ptr[4+edi],al SkipTran21: add ebx,ebp mov ah,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry12_16: sbb ecx,ecx cmp ds:byte ptr[_scanList + 10], 1 jnz SkipTran22 ;rj mov al,ds:byte ptr[5+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch22: mov ds:byte ptr[5+edi],al SkipTran22: add ebx,ebp mov ah,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry11_16: sbb ecx,ecx cmp ds:byte ptr[_scanList + 9], 1 jnz SkipTran23 ;rj mov al,ds:byte ptr[6+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch23: mov ds:byte ptr[6+edi],al SkipTran23: add ebx,ebp mov ah,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry10_16: sbb ecx,ecx cmp ds:byte ptr[_scanList + 8], 1 jnz SkipTran24 ;rj mov al,ds:byte ptr[7+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch24: mov ds:byte ptr[7+edi],al SkipTran24: add ebx,ebp mov ah,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry9_16: sbb ecx,ecx cmp ds:byte ptr[_scanList + 7], 1 jnz SkipTran25 ;rj mov al,ds:byte ptr[8+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch25: mov ds:byte ptr[8+edi],al SkipTran25: add ebx,ebp mov ah,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry8_16: sbb ecx,ecx cmp ds:byte ptr[_scanList + 6], 1 jnz SkipTran26 ;rj mov al,ds:byte ptr[9+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch26: mov ds:byte ptr[9+edi],al SkipTran26: add ebx,ebp mov ah,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry7_16: sbb ecx,ecx cmp ds:byte ptr[_scanList + 5], 1 jnz SkipTran27 ;rj mov al,ds:byte ptr[10+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch27: mov ds:byte ptr[10+edi],al SkipTran27: add ebx,ebp mov ah,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry6_16: sbb ecx,ecx cmp ds:byte ptr[_scanList + 4], 1 jnz SkipTran28 ;rj mov al,ds:byte ptr[11+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch28: mov ds:byte ptr[11+edi],al SkipTran28: add ebx,ebp mov ah,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry5_16: sbb ecx,ecx cmp ds:byte ptr[_scanList + 3], 1 jnz SkipTran29 ;rj mov al,ds:byte ptr[12+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch29: mov ds:byte ptr[12+edi],al SkipTran29: add ebx,ebp mov ah,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] add edx,ds:dword ptr[tstep] LEntry4_16: sbb ecx,ecx cmp ds:byte ptr[_scanList + 2], 1 jnz SkipTran30 ;rj mov al,ds:byte ptr[13+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch30: mov ds:byte ptr[13+edi],al SkipTran30: add ebx,ebp mov ah,ds:byte ptr[esi] adc esi,ds:dword ptr[advancetable+4+ecx*4] LEntry3_16: cmp ds:byte ptr[_scanList + 1], 1 jnz SkipTran31 ;rj mov al,ds:byte ptr[14+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch31: mov ds:byte ptr[14+edi],al SkipTran31: mov ah,ds:byte ptr[esi] LEntry2_16: LEndSpan: fstp st(0) fstp st(0) fstp st(0) mov ebx,ds:dword ptr[pspantemp] mov ebx,ds:dword ptr[12+ebx] cmp ds:byte ptr[_scanList + 0], 1 jnz SkipTran32 ;rj mov al,ds:byte ptr[15+edi] and eax, 0ffffh mov al,ds:byte ptr[12345678h + eax] TranPatch32: mov ds:byte ptr[15+edi],al SkipTran32: test ebx,ebx jnz LSpanLoop pop ebx pop esi pop edi pop ebp ret public _D_Draw16EndT _D_Draw16EndT: _TEXT ENDS _DATA SEGMENT align 4 LPatchTable: dd TranPatch1-4 dd TranPatch2-4 dd TranPatch3-4 dd TranPatch4-4 dd TranPatch5-4 dd TranPatch6-4 dd TranPatch7-4 dd TranPatch8-4 dd TranPatch9-4 dd TranPatch10-4 dd TranPatch11-4 dd TranPatch12-4 dd TranPatch13-4 dd TranPatch14-4 dd TranPatch15-4 dd TranPatch16-4 dd TranPatch17-4 dd TranPatch18-4 dd TranPatch19-4 dd TranPatch20-4 dd TranPatch21-4 dd TranPatch22-4 dd TranPatch23-4 dd TranPatch24-4 dd TranPatch25-4 dd TranPatch26-4 dd TranPatch27-4 dd TranPatch28-4 dd TranPatch29-4 dd TranPatch30-4 dd TranPatch31-4 dd TranPatch32-4 _DATA ENDS _TEXT SEGMENT align 4 public _R_TranPatch3 _R_TranPatch3: push ebx mov eax,ds:dword ptr[_mainTransTable] mov ebx,offset LPatchTable mov ecx,32 LPatchLoop: mov edx,ds:dword ptr[ebx] add ebx,4 mov ds:dword ptr[edx],eax dec ecx jnz LPatchLoop pop ebx ret _TEXT ENDS END engine/h2shared/masm/d_parta.asm000066400000000000000000000370461444734033100171110ustar00rootroot00000000000000; ; d_parta.asm -- for MASM ; x86 assembly-language 8-bpp particle-drawing code. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _d_pzbuffer:dword externdef _d_zrowbytes:dword externdef _d_viewbuffer:dword externdef _d_scantable:dword externdef _r_origin:dword externdef _r_ppn:dword externdef _r_pup:dword externdef _r_pright:dword externdef _ycenter:dword externdef _xcenter:dword externdef _d_vrectbottom_particle:dword externdef _d_vrectright_particle:dword externdef _d_vrecty:dword externdef _d_vrectx:dword externdef _d_pix_shift:dword externdef _d_pix_min:dword externdef _d_pix_max:dword externdef _d_y_aspect_shift:dword externdef _screenwidth:dword externdef _transTable:dword ; externs from ASM-only code externdef float_point5:dword externdef izistep:dword externdef izi:dword externdef float_1:dword externdef float_particle_z_clip:dword externdef float_minus_1:dword externdef float_0:dword externdef DP_Count:dword externdef DP_u:dword externdef DP_v:dword externdef DP_32768:dword externdef DP_Color:dword externdef DP_Pix:dword externdef DP_EntryTable:dword externdef DP_EntryTransTable:dword _TEXT SEGMENT align 4 public _D_DrawParticle _D_DrawParticle: push ebp push edi push ebx mov edi,ds:dword ptr[12+4+esp] fld ds:dword ptr[_r_origin] fsubr ds:dword ptr[0+edi] fld ds:dword ptr[0+4+edi] fsub ds:dword ptr[_r_origin+4] fld ds:dword ptr[0+8+edi] fsub ds:dword ptr[_r_origin+8] fxch st(2) fld ds:dword ptr[_r_ppn] fmul st(0),st(1) fld ds:dword ptr[_r_ppn+4] fmul st(0),st(3) fld ds:dword ptr[_r_ppn+8] fmul st(0),st(5) fxch st(2) faddp st(1),st(0) faddp st(1),st(0) fld st(0) fdivr ds:dword ptr[float_1] fxch st(1) fcomp ds:dword ptr[float_particle_z_clip] fxch st(3) fld ds:dword ptr[_r_pup] fmul st(0),st(2) fld ds:dword ptr[_r_pup+4] fnstsw ax test ah,1 jnz LPop6AndDone fmul st(0),st(4) fld ds:dword ptr[_r_pup+8] fmul st(0),st(3) fxch st(2) faddp st(1),st(0) faddp st(1),st(0) fxch st(3) fmul ds:dword ptr[_r_pright+4] fxch st(2) fmul ds:dword ptr[_r_pright] fxch st(1) fmul ds:dword ptr[_r_pright+8] fxch st(2) faddp st(1),st(0) faddp st(1),st(0) fxch st(1) fmul st(0),st(2) fxch st(1) fmul st(0),st(2) fxch st(1) fsubr ds:dword ptr[_ycenter] fxch st(1) fadd ds:dword ptr[_xcenter] fxch st(1) fadd ds:dword ptr[float_point5] fxch st(1) fadd ds:dword ptr[float_point5] fxch st(2) fmul ds:dword ptr[DP_32768] fxch st(2) fistp ds:dword ptr[DP_u] fistp ds:dword ptr[DP_v] mov eax,ds:dword ptr[DP_u] mov edx,ds:dword ptr[DP_v] mov ebx,ds:dword ptr[_d_vrectbottom_particle] mov ecx,ds:dword ptr[_d_vrectright_particle] cmp edx,ebx jg LPop1AndDone cmp eax,ecx jg LPop1AndDone mov ebx,ds:dword ptr[_d_vrecty] mov ecx,ds:dword ptr[_d_vrectx] cmp edx,ebx jl LPop1AndDone cmp eax,ecx jl LPop1AndDone fld ds:dword ptr[12+edi] fistp ds:dword ptr[DP_Color] mov ebx,ds:dword ptr[_d_viewbuffer] add ebx,eax mov edi,ds:dword ptr[_d_scantable+edx*4] imul edx,ds:dword ptr[_d_zrowbytes] lea edx,ds:dword ptr[edx+eax*2] mov eax,ds:dword ptr[_d_pzbuffer] fistp ds:dword ptr[izi] add edi,ebx add edx,eax mov eax,ds:dword ptr[izi] mov ecx,ds:dword ptr[_d_pix_shift] shr eax,cl mov ebp,ds:dword ptr[izi] mov ebx,ds:dword ptr[_d_pix_min] mov ecx,ds:dword ptr[_d_pix_max] cmp eax,ebx jnl LTestPixMax mov eax,ebx jmp LTestDone LTestPixMax: cmp eax,ecx jng LTestDone mov eax,ecx LTestDone: mov cx,ds:word ptr[DP_Color] mov ebx,ds:dword ptr[_d_y_aspect_shift] test ebx,ebx jnz LDefault cmp eax,4 ja LDefault test ch,ch jnz Trans jmp dword ptr[DP_EntryTable-4+eax*4] Trans: and ecx, 0ffh mov ch, cl jmp dword ptr[DP_EntryTransTable-4+eax*4] public DP_1x1 DP_1x1: cmp ds:word ptr[edx],bp jg LDone mov ds:word ptr[edx],bp mov ds:byte ptr[edi],cl jmp LDone public DP_2x2 DP_2x2: push esi mov ebx,ds:dword ptr[_screenwidth] mov esi,ds:dword ptr[_d_zrowbytes] cmp ds:word ptr[edx],bp jg L2x2_1 mov ds:word ptr[edx],bp mov ds:byte ptr[edi],cl L2x2_1: cmp ds:word ptr[2+edx],bp jg L2x2_2 mov ds:word ptr[2+edx],bp mov ds:byte ptr[1+edi],cl L2x2_2: cmp ds:word ptr[edx+esi*1],bp jg L2x2_3 mov ds:word ptr[edx+esi*1],bp mov ds:byte ptr[edi+ebx*1],cl L2x2_3: cmp ds:word ptr[2+edx+esi*1],bp jg L2x2_4 mov ds:word ptr[2+edx+esi*1],bp mov ds:byte ptr[1+edi+ebx*1],cl L2x2_4: pop esi jmp LDone public DP_3x3 DP_3x3: push esi mov ebx,ds:dword ptr[_screenwidth] mov esi,ds:dword ptr[_d_zrowbytes] cmp ds:word ptr[edx],bp jg L3x3_1 mov ds:word ptr[edx],bp mov ds:byte ptr[edi],cl L3x3_1: cmp ds:word ptr[2+edx],bp jg L3x3_2 mov ds:word ptr[2+edx],bp mov ds:byte ptr[1+edi],cl L3x3_2: cmp ds:word ptr[4+edx],bp jg L3x3_3 mov ds:word ptr[4+edx],bp mov ds:byte ptr[2+edi],cl L3x3_3: cmp ds:word ptr[edx+esi*1],bp jg L3x3_4 mov ds:word ptr[edx+esi*1],bp mov ds:byte ptr[edi+ebx*1],cl L3x3_4: cmp ds:word ptr[2+edx+esi*1],bp jg L3x3_5 mov ds:word ptr[2+edx+esi*1],bp mov ds:byte ptr[1+edi+ebx*1],cl L3x3_5: cmp ds:word ptr[4+edx+esi*1],bp jg L3x3_6 mov ds:word ptr[4+edx+esi*1],bp mov ds:byte ptr[2+edi+ebx*1],cl L3x3_6: cmp ds:word ptr[edx+esi*2],bp jg L3x3_7 mov ds:word ptr[edx+esi*2],bp mov ds:byte ptr[edi+ebx*2],cl L3x3_7: cmp ds:word ptr[2+edx+esi*2],bp jg L3x3_8 mov ds:word ptr[2+edx+esi*2],bp mov ds:byte ptr[1+edi+ebx*2],cl L3x3_8: cmp ds:word ptr[4+edx+esi*2],bp jg L3x3_9 mov ds:word ptr[4+edx+esi*2],bp mov ds:byte ptr[2+edi+ebx*2],cl L3x3_9: pop esi jmp LDone public DP_4x4 DP_4x4: push esi mov ebx,ds:dword ptr[_screenwidth] mov esi,ds:dword ptr[_d_zrowbytes] cmp ds:word ptr[edx],bp jg L4x4_1 mov ds:word ptr[edx],bp mov ds:byte ptr[edi],cl L4x4_1: cmp ds:word ptr[2+edx],bp jg L4x4_2 mov ds:word ptr[2+edx],bp mov ds:byte ptr[1+edi],cl L4x4_2: cmp ds:word ptr[4+edx],bp jg L4x4_3 mov ds:word ptr[4+edx],bp mov ds:byte ptr[2+edi],cl L4x4_3: cmp ds:word ptr[6+edx],bp jg L4x4_4 mov ds:word ptr[6+edx],bp mov ds:byte ptr[3+edi],cl L4x4_4: cmp ds:word ptr[edx+esi*1],bp jg L4x4_5 mov ds:word ptr[edx+esi*1],bp mov ds:byte ptr[edi+ebx*1],cl L4x4_5: cmp ds:word ptr[2+edx+esi*1],bp jg L4x4_6 mov ds:word ptr[2+edx+esi*1],bp mov ds:byte ptr[1+edi+ebx*1],cl L4x4_6: cmp ds:word ptr[4+edx+esi*1],bp jg L4x4_7 mov ds:word ptr[4+edx+esi*1],bp mov ds:byte ptr[2+edi+ebx*1],cl L4x4_7: cmp ds:word ptr[6+edx+esi*1],bp jg L4x4_8 mov ds:word ptr[6+edx+esi*1],bp mov ds:byte ptr[3+edi+ebx*1],cl L4x4_8: lea edx,ds:dword ptr[edx+esi*2] lea edi,ds:dword ptr[edi+ebx*2] cmp ds:word ptr[edx],bp jg L4x4_9 mov ds:word ptr[edx],bp mov ds:byte ptr[edi],cl L4x4_9: cmp ds:word ptr[2+edx],bp jg L4x4_10 mov ds:word ptr[2+edx],bp mov ds:byte ptr[1+edi],cl L4x4_10: cmp ds:word ptr[4+edx],bp jg L4x4_11 mov ds:word ptr[4+edx],bp mov ds:byte ptr[2+edi],cl L4x4_11: cmp ds:word ptr[6+edx],bp jg L4x4_12 mov ds:word ptr[6+edx],bp mov ds:byte ptr[3+edi],cl L4x4_12: cmp ds:word ptr[edx+esi*1],bp jg L4x4_13 mov ds:word ptr[edx+esi*1],bp mov ds:byte ptr[edi+ebx*1],cl L4x4_13: cmp ds:word ptr[2+edx+esi*1],bp jg L4x4_14 mov ds:word ptr[2+edx+esi*1],bp mov ds:byte ptr[1+edi+ebx*1],cl L4x4_14: cmp ds:word ptr[4+edx+esi*1],bp jg L4x4_15 mov ds:word ptr[4+edx+esi*1],bp mov ds:byte ptr[2+edi+ebx*1],cl L4x4_15: cmp ds:word ptr[6+edx+esi*1],bp jg L4x4_16 mov ds:word ptr[6+edx+esi*1],bp mov ds:byte ptr[3+edi+ebx*1],cl L4x4_16: pop esi jmp LDone public DP_T1x1 DP_T1x1: mov eax, ds:dword ptr[_transTable] cmp ds:word ptr[edx],bp jg LDone mov ds:word ptr[edx],bp mov cl, ds:byte ptr[edi] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[edi],cl jmp LDone public DP_T2x2 DP_T2x2: mov eax, ds:dword ptr[_transTable] push esi mov ebx,ds:dword ptr[_screenwidth] mov esi,ds:dword ptr[_d_zrowbytes] cmp ds:word ptr[edx],bp jg LT2x2_1 mov ds:word ptr[edx],bp mov cl, ds:byte ptr[edi] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[edi],cl LT2x2_1: cmp ds:word ptr[2+edx],bp jg LT2x2_2 mov ds:word ptr[2+edx],bp mov cl, ds:byte ptr[1+edi] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[1+edi],cl LT2x2_2: cmp ds:word ptr[edx+esi*1],bp jg LT2x2_3 mov ds:word ptr[edx+esi*1],bp mov cl, ds:byte ptr[edi+ebx*1] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[edi+ebx*1],cl LT2x2_3: cmp ds:word ptr[2+edx+esi*1],bp jg LT2x2_4 mov ds:word ptr[2+edx+esi*1],bp mov cl, ds:byte ptr[1+edi+ebx*1] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[1+edi+ebx*1],cl LT2x2_4: pop esi jmp LDone public DP_T3x3 DP_T3x3: mov eax, ds:dword ptr[_transTable] push esi mov ebx,ds:dword ptr[_screenwidth] mov esi,ds:dword ptr[_d_zrowbytes] cmp ds:word ptr[edx],bp jg LT3x3_1 mov ds:word ptr[edx],bp mov cl, ds:byte ptr[edi] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[edi],cl LT3x3_1: cmp ds:word ptr[2+edx],bp jg LT3x3_2 mov ds:word ptr[2+edx],bp mov cl, ds:byte ptr[1+edi] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[1+edi],cl LT3x3_2: cmp ds:word ptr[4+edx],bp jg LT3x3_3 mov ds:word ptr[4+edx],bp mov cl, ds:byte ptr[2+edi] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[2+edi],cl LT3x3_3: cmp ds:word ptr[edx+esi*1],bp jg LT3x3_4 mov ds:word ptr[edx+esi*1],bp mov cl, ds:byte ptr[edi+ebx*1] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[edi+ebx*1],cl LT3x3_4: cmp ds:word ptr[2+edx+esi*1],bp jg LT3x3_5 mov ds:word ptr[2+edx+esi*1],bp mov cl, ds:byte ptr[1+edi+ebx*1] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[1+edi+ebx*1],cl LT3x3_5: cmp ds:word ptr[4+edx+esi*1],bp jg LT3x3_6 mov ds:word ptr[4+edx+esi*1],bp mov cl, ds:byte ptr[2+edi+ebx*1] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[2+edi+ebx*1],cl LT3x3_6: cmp ds:word ptr[edx+esi*2],bp jg LT3x3_7 mov ds:word ptr[edx+esi*2],bp mov cl, ds:byte ptr[edi+ebx*2] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[edi+ebx*2],cl LT3x3_7: cmp ds:word ptr[2+edx+esi*2],bp jg LT3x3_8 mov ds:word ptr[2+edx+esi*2],bp mov cl, ds:byte ptr[1+edi+ebx*2] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[1+edi+ebx*2],cl LT3x3_8: cmp ds:word ptr[4+edx+esi*2],bp jg LT3x3_9 mov ds:word ptr[4+edx+esi*2],bp mov cl, ds:byte ptr[2+edi+ebx*2] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[2+edi+ebx*2],cl LT3x3_9: pop esi jmp LDone public DP_T4x4 DP_T4x4: mov eax, ds:dword ptr[_transTable] push esi mov ebx,ds:dword ptr[_screenwidth] mov esi,ds:dword ptr[_d_zrowbytes] cmp ds:word ptr[edx],bp jg LT4x4_1 mov ds:word ptr[edx],bp mov cl, ds:byte ptr[edi] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[edi],cl LT4x4_1: cmp ds:word ptr[2+edx],bp jg LT4x4_2 mov ds:word ptr[2+edx],bp mov cl, ds:byte ptr[1+edi] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[1+edi],cl LT4x4_2: cmp ds:word ptr[4+edx],bp jg LT4x4_3 mov ds:word ptr[4+edx],bp mov cl, ds:byte ptr[2+edi] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[2+edi],cl LT4x4_3: cmp ds:word ptr[6+edx],bp jg LT4x4_4 mov ds:word ptr[6+edx],bp mov cl, ds:byte ptr[3+edi] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[3+edi],cl LT4x4_4: cmp ds:word ptr[edx+esi*1],bp jg LT4x4_5 mov ds:word ptr[edx+esi*1],bp mov cl, ds:byte ptr[edi+ebx*1] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[edi+ebx*1],cl LT4x4_5: cmp ds:word ptr[2+edx+esi*1],bp jg LT4x4_6 mov ds:word ptr[2+edx+esi*1],bp mov cl, ds:byte ptr[1+edi+ebx*1] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[1+edi+ebx*1],cl LT4x4_6: cmp ds:word ptr[4+edx+esi*1],bp jg LT4x4_7 mov ds:word ptr[4+edx+esi*1],bp mov cl, ds:byte ptr[2+edi+ebx*1] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[2+edi+ebx*1],cl LT4x4_7: cmp ds:word ptr[6+edx+esi*1],bp jg LT4x4_8 mov ds:word ptr[6+edx+esi*1],bp mov cl, ds:byte ptr[3+edi+ebx*1] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[3+edi+ebx*1],cl LT4x4_8: lea edx,ds:dword ptr[edx+esi*2] lea edi,ds:dword ptr[edi+ebx*2] cmp ds:word ptr[edx],bp jg LT4x4_9 mov ds:word ptr[edx],bp mov cl, ds:byte ptr[edi] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[edi],cl LT4x4_9: cmp ds:word ptr[2+edx],bp jg LT4x4_10 mov ds:word ptr[2+edx],bp mov cl, ds:byte ptr[1+edi] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[1+edi],cl LT4x4_10: cmp ds:word ptr[4+edx],bp jg LT4x4_11 mov ds:word ptr[4+edx],bp mov cl, ds:byte ptr[2+edi] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[2+edi],cl LT4x4_11: cmp ds:word ptr[6+edx],bp jg LT4x4_12 mov ds:word ptr[6+edx],bp mov cl, ds:byte ptr[3+edi] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[3+edi],cl LT4x4_12: cmp ds:word ptr[edx+esi*1],bp jg LT4x4_13 mov ds:word ptr[edx+esi*1],bp mov cl, ds:byte ptr[edi+ebx*1] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[edi+ebx*1],cl LT4x4_13: cmp ds:word ptr[2+edx+esi*1],bp jg LT4x4_14 mov ds:word ptr[2+edx+esi*1],bp mov cl, ds:byte ptr[1+edi+ebx*1] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[1+edi+ebx*1],cl LT4x4_14: cmp ds:word ptr[4+edx+esi*1],bp jg LT4x4_15 mov ds:word ptr[4+edx+esi*1],bp mov cl, ds:byte ptr[2+edi+ebx*1] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[2+edi+ebx*1],cl LT4x4_15: cmp ds:word ptr[6+edx+esi*1],bp jg LT4x4_16 mov ds:word ptr[6+edx+esi*1],bp mov cl, ds:byte ptr[3+edi+ebx*1] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[3+edi+ebx*1],cl LT4x4_16: pop esi jmp LDone LDefault: test ch,ch jnz LTDefault mov ebx,eax mov ds:dword ptr[DP_Pix],eax push cx mov cl,ds:byte ptr[_d_y_aspect_shift] shl ebx,cl pop cx LGenRowLoop: mov eax,ds:dword ptr[DP_Pix] LGenColLoop: cmp ds:word ptr[-2+edx+eax*2],bp jg LGSkip mov ds:word ptr[-2+edx+eax*2],bp mov ds:byte ptr[-1+edi+eax*1],cl LGSkip: dec eax jnz LGenColLoop add edx,ds:dword ptr[_d_zrowbytes] add edi,ds:dword ptr[_screenwidth] dec ebx jnz LGenRowLoop jmp LDone LTDefault: push esi and ecx, 0ffh mov ch, cl mov esi, ds:dword ptr[_transTable] mov ebx,eax mov ds:dword ptr[DP_Pix],eax mov cl,ds:byte ptr[_d_y_aspect_shift] shl ebx,cl LTGenRowLoop: mov eax,ds:dword ptr[DP_Pix] LTGenColLoop: cmp ds:word ptr[-2+edx+eax*2],bp jg LTGSkip mov ds:word ptr[-2+edx+eax*2],bp mov cl, ds:byte ptr[-1+edi+eax*1] mov cl, ds:byte ptr[esi+ecx] mov ds:byte ptr[-1+edi+eax*1],cl LTGSkip: dec eax jnz LTGenColLoop add edx,ds:dword ptr[_d_zrowbytes] add edi,ds:dword ptr[_screenwidth] dec ebx jnz LTGenRowLoop pop esi LDone: pop ebx pop edi pop ebp ret LPop6AndDone: fstp st(0) fstp st(0) fstp st(0) fstp st(0) fstp st(0) LPop1AndDone: fstp st(0) jmp LDone _TEXT ENDS END engine/h2shared/masm/d_partb.asm000066400000000000000000000137511444734033100171070ustar00rootroot00000000000000; ; d_partb.asm -- for MASM ; x86 assembly-language 8-bpp particle-drawing code. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _d_pzbuffer:dword externdef _d_zrowbytes:dword externdef _d_viewbuffer:dword externdef _d_scantable:dword externdef _r_origin:dword externdef _r_ppn:dword externdef _r_pup:dword externdef _r_pright:dword externdef _ycenter:dword externdef _xcenter:dword externdef _d_vrectbottom_particle:dword externdef _d_vrectright_particle:dword externdef _d_vrecty:dword externdef _d_vrectx:dword externdef _d_pix_shift:dword externdef _d_pix_min:dword externdef _d_pix_max:dword externdef _d_y_aspect_shift:dword externdef _screenwidth:dword externdef _transTable:dword ; externs from ASM-only code externdef float_point5:dword externdef izistep:dword externdef izi:dword externdef float_1:dword externdef float_particle_z_clip:dword externdef float_minus_1:dword externdef float_0:dword externdef DP_Count:dword externdef DP_u:dword externdef DP_v:dword externdef DP_32768:dword externdef DP_Color:dword externdef DP_Pix:dword externdef DP_EntryTable:dword _TEXT SEGMENT align 4 public _D_DrawParticle1x1b _D_DrawParticle1x1b: push ebp push edi push ebx mov edi,ds:dword ptr[12+4+esp] fld ds:dword ptr[_r_origin] fsubr ds:dword ptr[0+edi] fld ds:dword ptr[0+4+edi] fsub ds:dword ptr[_r_origin+4] fld ds:dword ptr[0+8+edi] fsub ds:dword ptr[_r_origin+8] fxch st(2) fld ds:dword ptr[_r_ppn] fmul st(0),st(1) fld ds:dword ptr[_r_ppn+4] fmul st(0),st(3) fld ds:dword ptr[_r_ppn+8] fmul st(0),st(5) fxch st(2) faddp st(1),st(0) faddp st(1),st(0) fld st(0) fdivr ds:dword ptr[float_1] fxch st(1) fcomp ds:dword ptr[float_particle_z_clip] fxch st(3) fld ds:dword ptr[_r_pup] fmul st(0),st(2) fld ds:dword ptr[_r_pup+4] fnstsw ax test ah,1 jnz LPop6AndDone fmul st(0),st(4) fld ds:dword ptr[_r_pup+8] fmul st(0),st(3) fxch st(2) faddp st(1),st(0) faddp st(1),st(0) fxch st(3) fmul ds:dword ptr[_r_pright+4] fxch st(2) fmul ds:dword ptr[_r_pright] fxch st(1) fmul ds:dword ptr[_r_pright+8] fxch st(2) faddp st(1),st(0) faddp st(1),st(0) fxch st(1) fmul st(0),st(2) fxch st(1) fmul st(0),st(2) fxch st(1) fsubr ds:dword ptr[_ycenter] fxch st(1) fadd ds:dword ptr[_xcenter] fxch st(1) fadd ds:dword ptr[float_point5] fxch st(1) fadd ds:dword ptr[float_point5] fxch st(2) fmul ds:dword ptr[DP_32768] fxch st(2) fistp ds:dword ptr[DP_u] fistp ds:dword ptr[DP_v] mov eax,ds:dword ptr[DP_u] mov edx,ds:dword ptr[DP_v] mov ebx,ds:dword ptr[_d_vrectbottom_particle] mov ecx,ds:dword ptr[_d_vrectright_particle] cmp edx,ebx jg LPop1AndDone cmp eax,ecx jg LPop1AndDone mov ebx,ds:dword ptr[_d_vrecty] mov ecx,ds:dword ptr[_d_vrectx] cmp edx,ebx jl LPop1AndDone cmp eax,ecx jl LPop1AndDone fld ds:dword ptr[12+edi] fistp ds:dword ptr[DP_Color] mov ebx,ds:dword ptr[_d_viewbuffer] add ebx,eax mov edi,ds:dword ptr[_d_scantable+edx*4] imul edx,ds:dword ptr[_d_zrowbytes] lea edx,ds:dword ptr[edx+eax*2] mov eax,ds:dword ptr[_d_pzbuffer] fistp ds:dword ptr[izi] add edi,ebx add edx,eax mov eax,ds:dword ptr[izi] mov ecx,ds:dword ptr[_d_pix_shift] shr eax,cl mov ebp,ds:dword ptr[izi] mov ebx,ds:dword ptr[_d_pix_min] mov ecx,ds:dword ptr[_d_pix_max] cmp eax,ebx jnl LTestPixMax mov eax,ebx jmp LTestDone LTestPixMax: cmp eax,ecx jng LTestDone mov eax,ecx LTestDone: mov cx,ds:word ptr[DP_Color] ; get color mov ebx,ds:dword ptr[_d_y_aspect_shift] test ebx,ebx jnz LDefault cmp eax,4 ja LDefault public DP_1x1b DP_1x1b: cmp ds:word ptr[edx],bp jg LDone ; mov ds:word ptr[edx],bp test ch,ch jnz Trans mov ds:byte ptr[edi],cl jmp LDone Trans: and ecx, 0ffh mov ch, cl mov eax, ds:dword ptr[_transTable] mov cl, ds:byte ptr[edi] mov cl, ds:byte ptr[eax+ecx] mov ds:byte ptr[edi], cl jmp LDone LDefault: test ch,ch jnz LTDefault mov ebx,eax mov ds:dword ptr[DP_Pix],eax push cx mov cl,ds:byte ptr[_d_y_aspect_shift] shl ebx,cl pop cx LGenRowLoop: mov eax,ds:dword ptr[DP_Pix] LGenColLoop: cmp ds:word ptr[-2+edx+eax*2],bp jg LGSkip mov ds:word ptr[-2+edx+eax*2],bp mov ds:byte ptr[-1+edi+eax*1],cl LGSkip: dec eax jnz LGenColLoop add edx,ds:dword ptr[_d_zrowbytes] add edi,ds:dword ptr[_screenwidth] dec ebx jnz LGenRowLoop jmp LDone LTDefault: push esi and ecx, 0ffh mov ch, cl mov esi, ds:dword ptr[_transTable] mov ebx,eax mov ds:dword ptr[DP_Pix],eax mov cl,ds:byte ptr[_d_y_aspect_shift] shl ebx,cl LTGenRowLoop: mov eax,ds:dword ptr[DP_Pix] LTGenColLoop: cmp ds:word ptr[-2+edx+eax*2],bp jg LTGSkip mov ds:word ptr[-2+edx+eax*2],bp mov cl, ds:byte ptr[-1+edi+eax*1] mov cl, ds:byte ptr[esi+ecx] mov ds:byte ptr[-1+edi+eax*1],cl LTGSkip: dec eax jnz LTGenColLoop add edx,ds:dword ptr[_d_zrowbytes] add edi,ds:dword ptr[_screenwidth] dec ebx jnz LTGenRowLoop pop esi LDone: pop ebx pop edi pop ebp ret LPop6AndDone: fstp st(0) fstp st(0) fstp st(0) fstp st(0) fstp st(0) LPop1AndDone: fstp st(0) jmp LDone _TEXT ENDS END engine/h2shared/masm/d_polysa.asm000066400000000000000000000556711444734033100173150ustar00rootroot00000000000000; ; d_polysa.asm -- for MASM ; x86 assembly-language polygon model drawing code ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _d_viewbuffer:dword externdef _d_scantable:dword externdef _r_refdef:dword externdef _a_sstepxfrac:dword externdef _r_affinetridesc:dword externdef _acolormap:dword externdef _d_pcolormap:dword externdef _d_sfrac:dword externdef _d_ptex:dword externdef _d_pedgespanpackage:dword externdef _d_tfrac:dword externdef _d_light:dword externdef _d_zi:dword externdef _d_pdest:dword externdef _d_pz:dword externdef _d_aspancount:dword externdef _erroradjustup:dword externdef _erroradjustdown:dword externdef _errorterm:dword externdef _d_xdenom:dword externdef _r_p0:dword externdef _r_p1:dword externdef _r_p2:dword externdef _a_tstepxfrac:dword externdef _a_ststepxwhole:dword externdef _zspantable:dword externdef _skintable:dword externdef _d_countextrastep:dword externdef _ubasestep:dword externdef _a_spans:dword externdef _d_pdestextrastep:dword externdef _d_pzextrastep:dword externdef _d_sfracextrastep:dword externdef _d_ptexextrastep:dword externdef _d_tfracextrastep:dword externdef _d_lightextrastep:dword externdef _d_ziextrastep:dword externdef _d_pdestbasestep:dword externdef _d_pzbasestep:dword externdef _d_sfracbasestep:dword externdef _d_ptexbasestep:dword externdef _d_tfracbasestep:dword externdef _d_lightbasestep:dword externdef _d_zibasestep:dword externdef _r_lstepx:dword externdef _r_lstepy:dword externdef _r_sstepx:dword externdef _r_sstepy:dword externdef _r_tstepx:dword externdef _r_tstepy:dword externdef _r_zistepx:dword externdef _r_zistepy:dword externdef _D_PolysetSetEdgeTable:dword externdef _D_RasterizeAliasPolySmooth:dword ; externs from ASM-only code externdef float_point5:dword externdef float_1:dword externdef float_minus_1:dword externdef float_0:dword externdef advancetable:dword externdef sstep:dword externdef tstep:dword externdef ceil_cw:dword externdef single_cw:dword _DATA SEGMENT align 4 p10_minus_p20 dd 0 p01_minus_p21 dd 0 temp0 dd 0 temp1 dd 0 Ltemp dd 0 aff8entryvec_table dd LDraw8, LDraw7, LDraw6, LDraw5 dd LDraw4, LDraw3, LDraw2, LDraw1 lzistepx dd 0 _DATA ENDS _TEXT SEGMENT externdef _D_PolysetSetEdgeTable:dword externdef _D_RasterizeAliasPolySmooth:dword public _D_PolysetCalcGradients _D_PolysetCalcGradients: fild ds:dword ptr[_r_p0+0] fild ds:dword ptr[_r_p2+0] fild ds:dword ptr[_r_p0+4] fild ds:dword ptr[_r_p2+4] fild ds:dword ptr[_r_p1+0] fild ds:dword ptr[_r_p1+4] fxch st(3) fsub st(0),st(2) fxch st(1) fsub st(0),st(4) fxch st(5) fsubrp st(4),st(0) fxch st(2) fsubrp st(1),st(0) fxch st(1) fld ds:dword ptr[_d_xdenom] fxch st(4) fstp ds:dword ptr[p10_minus_p20] fstp ds:dword ptr[p01_minus_p21] fxch st(2) fild ds:dword ptr[_r_p2+16] fild ds:dword ptr[_r_p0+16] fild ds:dword ptr[_r_p1+16] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmul st(0),st(5) fxch st(2) fld st(0) fmul ds:dword ptr[p01_minus_p21] fxch st(2) fmul ds:dword ptr[p10_minus_p20] fxch st(1) fmul st(0),st(5) fxch st(2) fsubrp st(3),st(0) fsubp st(1),st(0) fld st(2) fmul ds:dword ptr[float_minus_1] fxch st(2) fmul st(0),st(3) fxch st(1) fmul st(0),st(2) fldcw ds:word ptr[ceil_cw] fistp ds:dword ptr[_r_lstepy] fistp ds:dword ptr[_r_lstepx] fldcw ds:word ptr[single_cw] fild ds:dword ptr[_r_p2+8] fild ds:dword ptr[_r_p0+8] fild ds:dword ptr[_r_p1+8] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmul st(0),st(6) fxch st(2) fld st(0) fmul ds:dword ptr[p01_minus_p21] fxch st(2) fmul ds:dword ptr[p10_minus_p20] fxch st(1) fmul st(0),st(6) fxch st(2) fsubrp st(3),st(0) fsubp st(1),st(0) fmul st(0),st(2) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[_r_sstepy] fistp ds:dword ptr[_r_sstepx] fild ds:dword ptr[_r_p2+12] fild ds:dword ptr[_r_p0+12] fild ds:dword ptr[_r_p1+12] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmul st(0),st(6) fxch st(2) fld st(0) fmul ds:dword ptr[p01_minus_p21] fxch st(2) fmul ds:dword ptr[p10_minus_p20] fxch st(1) fmul st(0),st(6) fxch st(2) fsubrp st(3),st(0) fsubp st(1),st(0) fmul st(0),st(2) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[_r_tstepy] fistp ds:dword ptr[_r_tstepx] fild ds:dword ptr[_r_p2+20] fild ds:dword ptr[_r_p0+20] fild ds:dword ptr[_r_p1+20] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmulp st(6),st(0) fxch st(1) fld st(0) fmul ds:dword ptr[p01_minus_p21] fxch st(2) fmul ds:dword ptr[p10_minus_p20] fxch st(1) fmulp st(5),st(0) fxch st(5) fsubp st(1),st(0) fxch st(3) fsubrp st(4),st(0) fxch st(1) fmulp st(2),st(0) fmulp st(2),st(0) fistp ds:dword ptr[_r_zistepx] fistp ds:dword ptr[_r_zistepy] mov eax,ds:dword ptr[_r_sstepx] mov edx,ds:dword ptr[_r_tstepx] shl eax,16 shl edx,16 mov ds:dword ptr[_a_sstepxfrac],eax mov ds:dword ptr[_a_tstepxfrac],edx mov ecx,ds:dword ptr[_r_sstepx] mov eax,ds:dword ptr[_r_tstepx] sar ecx,16 sar eax,16 imul ds:dword ptr[4+0+esp] add eax,ecx mov ds:dword ptr[_a_ststepxwhole],eax ret public _D_PolysetRecursiveTriangle _D_PolysetRecursiveTriangle: push ebp push esi push edi push ebx mov esi,ds:dword ptr[8+16+esp] mov ebx,ds:dword ptr[4+16+esp] mov edi,ds:dword ptr[12+16+esp] mov eax,ds:dword ptr[0+esi] mov edx,ds:dword ptr[0+ebx] mov ebp,ds:dword ptr[4+esi] sub eax,edx mov ecx,ds:dword ptr[4+ebx] sub ebp,ecx inc eax cmp eax,2 ja LSplit mov eax,ds:dword ptr[0+edi] inc ebp cmp ebp,2 ja LSplit mov edx,ds:dword ptr[0+esi] mov ebp,ds:dword ptr[4+edi] sub eax,edx mov ecx,ds:dword ptr[4+esi] sub ebp,ecx inc eax cmp eax,2 ja LSplit2 mov eax,ds:dword ptr[0+ebx] inc ebp cmp ebp,2 ja LSplit2 mov edx,ds:dword ptr[0+edi] mov ebp,ds:dword ptr[4+ebx] sub eax,edx mov ecx,ds:dword ptr[4+edi] sub ebp,ecx inc eax inc ebp mov edx,ebx cmp eax,2 ja LSplit3 cmp ebp,2 jna LDone LSplit3: mov ebx,edi mov edi,esi mov esi,edx jmp LSplit LSplit2: mov eax,ebx mov ebx,esi mov esi,edi mov edi,eax LSplit: sub esp,24 mov eax,ds:dword ptr[8+ebx] mov edx,ds:dword ptr[8+esi] mov ecx,ds:dword ptr[12+ebx] add eax,edx mov edx,ds:dword ptr[12+esi] sar eax,1 add ecx,edx mov ds:dword ptr[8+esp],eax mov eax,ds:dword ptr[20+ebx] sar ecx,1 mov edx,ds:dword ptr[20+esi] mov ds:dword ptr[12+esp],ecx add eax,edx mov ecx,ds:dword ptr[0+ebx] mov edx,ds:dword ptr[0+esi] sar eax,1 add edx,ecx mov ds:dword ptr[20+esp],eax mov eax,ds:dword ptr[4+ebx] sar edx,1 mov ebp,ds:dword ptr[4+esi] mov ds:dword ptr[0+esp],edx add ebp,eax sar ebp,1 mov ds:dword ptr[4+esp],ebp cmp ds:dword ptr[4+esi],eax jg LNoDraw mov edx,ds:dword ptr[0+esi] jnz LDraw cmp edx,ecx jl LNoDraw LDraw: mov edx,ds:dword ptr[20+esp] mov ecx,ds:dword ptr[4+esp] sar edx,16 mov ebp,ds:dword ptr[0+esp] mov eax,ds:dword ptr[_zspantable+ecx*4] cmp dx,ds:word ptr[eax+ebp*2] jnge LNoDraw mov ds:word ptr[eax+ebp*2],dx mov eax,ds:dword ptr[12+esp] sar eax,16 mov edx,ds:dword ptr[8+esp] sar edx,16 sub ecx,ecx mov eax,ds:dword ptr[_skintable+eax*4] mov ebp,ds:dword ptr[4+esp] mov cl,ds:byte ptr[eax+edx] mov edx,ds:dword ptr[_d_pcolormap] mov dl,ds:byte ptr[edx+ecx] mov ecx,ds:dword ptr[0+esp] mov eax,ds:dword ptr[_d_scantable+ebp*4] add ecx,eax mov eax,ds:dword ptr[_d_viewbuffer] mov ds:byte ptr[eax+ecx*1],dl LNoDraw: push esp push ebx push edi call near ptr _D_PolysetRecursiveTriangle mov ebx,esp push esi push ebx push edi call near ptr _D_PolysetRecursiveTriangle add esp,24 LDone: pop ebx pop edi pop esi pop ebp ret 12 public _D_PolysetAff8Start _D_PolysetAff8Start: public _D_PolysetDrawSpans8 _D_PolysetDrawSpans8: push esi push ebx mov esi,ds:dword ptr[4+8+esp] mov ecx,ds:dword ptr[_r_zistepx] push ebp push edi ror ecx,16 mov edx,ds:dword ptr[8+esi] mov ds:dword ptr[lzistepx],ecx LSpanLoop: mov eax,ds:dword ptr[_d_aspancount] sub eax,edx mov edx,ds:dword ptr[_erroradjustup] mov ebx,ds:dword ptr[_errorterm] add ebx,edx js LNoTurnover mov edx,ds:dword ptr[_erroradjustdown] mov edi,ds:dword ptr[_d_countextrastep] sub ebx,edx mov ebp,ds:dword ptr[_d_aspancount] mov ds:dword ptr[_errorterm],ebx add ebp,edi mov ds:dword ptr[_d_aspancount],ebp jmp LRightEdgeStepped LNoTurnover: mov edi,ds:dword ptr[_d_aspancount] mov edx,ds:dword ptr[_ubasestep] mov ds:dword ptr[_errorterm],ebx add edi,edx mov ds:dword ptr[_d_aspancount],edi LRightEdgeStepped: cmp eax,1 jl LNextSpan jz LExactlyOneLong mov ecx,ds:dword ptr[_a_ststepxwhole] mov edx,ds:dword ptr[_r_affinetridesc+8] mov ds:dword ptr[advancetable+4],ecx add ecx,edx mov ds:dword ptr[advancetable],ecx mov ecx,ds:dword ptr[_a_tstepxfrac] mov cx,ds:word ptr[_r_lstepx] mov edx,eax mov ds:dword ptr[tstep],ecx add edx,7 shr edx,3 mov ebx,ds:dword ptr[16+esi] mov bx,dx mov ecx,ds:dword ptr[4+esi] neg eax mov edi,ds:dword ptr[0+esi] and eax,7 sub edi,eax sub ecx,eax sub ecx,eax mov edx,ds:dword ptr[20+esi] mov dx,ds:word ptr[24+esi] mov ebp,ds:dword ptr[28+esi] ror ebp,16 push esi mov esi,ds:dword ptr[12+esi] jmp dword ptr[aff8entryvec_table+eax*4] LDrawLoop: LDraw8: cmp bp,ds:word ptr[ecx] jl Lp1 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] mov ds:word ptr[ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch8: mov ds:byte ptr[edi],al Lp1: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw7: cmp bp,ds:word ptr[2+ecx] jl Lp2 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] mov ds:word ptr[2+ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch7: mov ds:byte ptr[1+edi],al Lp2: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw6: cmp bp,ds:word ptr[4+ecx] jl Lp3 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] mov ds:word ptr[4+ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch6: mov ds:byte ptr[2+edi],al Lp3: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw5: cmp bp,ds:word ptr[6+ecx] jl Lp4 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] mov ds:word ptr[6+ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch5: mov ds:byte ptr[3+edi],al Lp4: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw4: cmp bp,ds:word ptr[8+ecx] jl Lp5 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] mov ds:word ptr[8+ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch4: mov ds:byte ptr[4+edi],al Lp5: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw3: cmp bp,ds:word ptr[10+ecx] jl Lp6 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] mov ds:word ptr[10+ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch3: mov ds:byte ptr[5+edi],al Lp6: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw2: cmp bp,ds:word ptr[12+ecx] jl Lp7 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] mov ds:word ptr[12+ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch2: mov ds:byte ptr[6+edi],al Lp7: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw1: cmp bp,ds:word ptr[14+ecx] jl Lp8 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] mov ds:word ptr[14+ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch1: mov ds:byte ptr[7+edi],al Lp8: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] add edi,8 add ecx,16 dec bx jnz LDrawLoop pop esi LNextSpan: add esi,32 LNextSpanESISet: mov edx,ds:dword ptr[8+esi] cmp edx,offset -999999 jnz LSpanLoop pop edi pop ebp pop ebx pop esi ret LExactlyOneLong: mov ecx,ds:dword ptr[4+esi] mov ebp,ds:dword ptr[28+esi] ror ebp,16 mov ebx,ds:dword ptr[12+esi] cmp bp,ds:word ptr[ecx] jl LNextSpan xor eax,eax mov edi,ds:dword ptr[0+esi] mov ah,ds:byte ptr[24+1+esi] add esi,32 mov al,ds:byte ptr[ebx] mov ds:word ptr[ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch9: mov ds:byte ptr[edi],al jmp LNextSpanESISet public _D_PolysetAff8End _D_PolysetAff8End: public _D_Aff8Patch _D_Aff8Patch: mov eax,ds:dword ptr[4+esp] mov ds:dword ptr[LPatch1-4],eax mov ds:dword ptr[LPatch2-4],eax mov ds:dword ptr[LPatch3-4],eax mov ds:dword ptr[LPatch4-4],eax mov ds:dword ptr[LPatch5-4],eax mov ds:dword ptr[LPatch6-4],eax mov ds:dword ptr[LPatch7-4],eax mov ds:dword ptr[LPatch8-4],eax mov ds:dword ptr[LPatch9-4],eax ret public _D_PolysetDraw _D_PolysetDraw: sub esp,offset (((1024+1 + 1 + ((32 - 1) / 32)) + 1) * 32) mov eax,esp add eax,32 - 1 and eax,offset not (32 - 1) mov ds:dword ptr[_a_spans],eax mov eax,ds:dword ptr[_r_affinetridesc+28] test eax,eax jz _D_DrawNonSubdiv ;push ebp ;mov ebp,ds:dword ptr[_r_affinetridesc+24] push esi ;shl ebp,4 push ebx mov ebx,ds:dword ptr[_r_affinetridesc+16] push edi mov edi,ds:dword ptr[_r_affinetridesc+20] Llooptop: xor ecx,ecx xor esi,esi mov cx,word ptr[4+0+ebx] mov si,word ptr[4+2+ebx] xor edx,edx mov dx,word ptr[4+4+ebx] shl ecx,5 add ecx,edi shl esi,5 shl edx,5 add esi,edi add edx,edi fild ds:dword ptr[0+4+ecx] fild ds:dword ptr[0+4+esi] fild ds:dword ptr[0+0+ecx] fild ds:dword ptr[0+0+edx] fxch st(2) fsubr st(0),st(3) fild ds:dword ptr[0+0+esi] fxch st(2) fsubr st(3),st(0) fild ds:dword ptr[0+4+edx] fxch st(1) fsubrp st(3),st(0) fxch st(1) fmulp st(3),st(0) fsubp st(3),st(0) mov eax,ds:dword ptr[0+16+ecx] and eax,0FF00h fmulp st(2),st(0) add eax,ds:dword ptr[_acolormap] fsubrp st(1),st(0) mov ds:dword ptr[_d_pcolormap],eax fstp ds:dword ptr[Ltemp] mov eax,ds:dword ptr[Ltemp] sub eax,080000001h jc Lskip mov eax,ds:dword ptr[0+ebx] test eax,eax jz Lfacesback push edx push esi push ecx call near ptr _D_PolysetRecursiveTriangle ; sub ebp,16 ; jnz Llooptop jmp Ldone2 Lfacesback: mov eax,ds:dword ptr[0+8+ecx] push eax mov eax,ds:dword ptr[0+8+esi] push eax mov eax,ds:dword ptr[0+8+edx] push eax push ecx push edx mov eax,ds:dword ptr[_r_affinetridesc+32] test ds:dword ptr[24+ecx],00020h jz Lp11 add ds:dword ptr[0+8+ecx],eax Lp11: test ds:dword ptr[24+esi],00020h jz Lp12 add ds:dword ptr[0+8+esi],eax Lp12: test ds:dword ptr[24+edx],00020h jz Lp13 add ds:dword ptr[0+8+edx],eax Lp13: push edx push esi push ecx call near ptr _D_PolysetRecursiveTriangle pop edx pop ecx pop eax mov ds:dword ptr[0+8+edx],eax pop eax mov ds:dword ptr[0+8+esi],eax pop eax mov ds:dword ptr[0+8+ecx],eax Lskip: ; sub ebp,16 ; jnz Llooptop Ldone2: pop edi pop ebx pop esi ; pop ebp add esp,offset (((1024+1 + 1 + ((32 - 1) / 32)) + 1) * 32) ret public _D_PolysetScanLeftEdge _D_PolysetScanLeftEdge: push ebp push esi push edi push ebx mov eax,ds:dword ptr[4+16+esp] mov ecx,ds:dword ptr[_d_sfrac] and eax,0FFFFh mov ebx,ds:dword ptr[_d_ptex] or ecx,eax mov esi,ds:dword ptr[_d_pedgespanpackage] mov edx,ds:dword ptr[_d_tfrac] mov edi,ds:dword ptr[_d_light] mov ebp,ds:dword ptr[_d_zi] LScanLoop: mov ds:dword ptr[12+esi],ebx mov eax,ds:dword ptr[_d_pdest] mov ds:dword ptr[0+esi],eax mov eax,ds:dword ptr[_d_pz] mov ds:dword ptr[4+esi],eax mov eax,ds:dword ptr[_d_aspancount] mov ds:dword ptr[8+esi],eax mov ds:dword ptr[24+esi],edi mov ds:dword ptr[28+esi],ebp mov ds:dword ptr[16+esi],ecx mov ds:dword ptr[20+esi],edx mov al,ds:byte ptr[32+esi] add esi,32 mov eax,ds:dword ptr[_erroradjustup] mov ds:dword ptr[_d_pedgespanpackage],esi mov esi,ds:dword ptr[_errorterm] add esi,eax mov eax,ds:dword ptr[_d_pdest] js LNoLeftEdgeTurnover sub esi,ds:dword ptr[_erroradjustdown] add eax,ds:dword ptr[_d_pdestextrastep] mov ds:dword ptr[_errorterm],esi mov ds:dword ptr[_d_pdest],eax mov eax,ds:dword ptr[_d_pz] mov esi,ds:dword ptr[_d_aspancount] add eax,ds:dword ptr[_d_pzextrastep] add ecx,ds:dword ptr[_d_sfracextrastep] adc ebx,ds:dword ptr[_d_ptexextrastep] add esi,ds:dword ptr[_d_countextrastep] mov ds:dword ptr[_d_pz],eax mov eax,ds:dword ptr[_d_tfracextrastep] mov ds:dword ptr[_d_aspancount],esi add edx,eax jnc LSkip1 add ebx,ds:dword ptr[_r_affinetridesc+8] LSkip1: add edi,ds:dword ptr[_d_lightextrastep] add ebp,ds:dword ptr[_d_ziextrastep] mov esi,ds:dword ptr[_d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz LScanLoop pop ebx pop edi pop esi pop ebp ret LNoLeftEdgeTurnover: mov ds:dword ptr[_errorterm],esi add eax,ds:dword ptr[_d_pdestbasestep] mov ds:dword ptr[_d_pdest],eax mov eax,ds:dword ptr[_d_pz] mov esi,ds:dword ptr[_d_aspancount] add eax,ds:dword ptr[_d_pzbasestep] add ecx,ds:dword ptr[_d_sfracbasestep] adc ebx,ds:dword ptr[_d_ptexbasestep] add esi,ds:dword ptr[_ubasestep] mov ds:dword ptr[_d_pz],eax mov ds:dword ptr[_d_aspancount],esi mov esi,ds:dword ptr[_d_tfracbasestep] add edx,esi jnc LSkip2 add ebx,ds:dword ptr[_r_affinetridesc+8] LSkip2: add edi,ds:dword ptr[_d_lightbasestep] add ebp,ds:dword ptr[_d_zibasestep] mov esi,ds:dword ptr[_d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz LScanLoop pop ebx pop edi pop esi pop ebp ret _L_PDFVert: push esi push edi mov eax,ds:dword ptr[0+0+ebx] mov edx,ds:dword ptr[_r_refdef+40] cmp eax,edx jge LNextVert mov esi,ds:dword ptr[0+4+ebx] mov edx,ds:dword ptr[_r_refdef+44] cmp esi,edx jge LNextVert mov edi,ds:dword ptr[_zspantable+esi*4] mov edx,ds:dword ptr[0+20+ebx] shr edx,16 cmp dx,ds:word ptr[edi+eax*2] jl LNextVert mov ds:word ptr[edi+eax*2],dx mov edi,ds:dword ptr[0+12+ebx] shr edi,16 mov edi,ds:dword ptr[_skintable+edi*4] mov edx,ds:dword ptr[0+8+ebx] shr edx,16 mov dl,ds:byte ptr[edi+edx] mov edi,ds:dword ptr[0+16+ebx] and edi,0FF00h and edx,000FFh add edi,edx mov edx,ds:dword ptr[_acolormap] mov dl,ds:byte ptr[edx+edi*1] mov edi,ds:dword ptr[_d_scantable+esi*4] mov esi,ds:dword ptr[_d_viewbuffer] add edi,eax mov ds:byte ptr[esi+edi],dl LNextVert: pop edi pop esi ret public _D_PolysetDrawFinalVerts _D_PolysetDrawFinalVerts: push ebp push ebx mov ebx,dword ptr[4+8+esp] ;pv1 call _L_PDFVert mov ebx,dword ptr[8+8+esp] ;pv2 call _L_PDFVert mov ebx,dword ptr[12+8+esp];pv3 call _L_PDFVert pop ebx pop ebp ret public _D_DrawNonSubdiv _D_DrawNonSubdiv: ;push ebp ;mov ebp,ds:dword ptr[_r_affinetridesc+24] ;lnumtriangles push ebx ;shl ebp,4 push esi mov esi,ds:dword ptr[_r_affinetridesc+16] ;ptri push edi LNDLoop: mov edi,ds:dword ptr[_r_affinetridesc+20] ;pfv xor ecx,ecx; //clear i1 xor edx,edx; //clear i2 mov cx, word ptr[4+0+esi] ;ptri->vertindex[0] xor ebx,ebx; //clear i3 mov dx, word ptr[4+2+esi] ;ptri->vertindex[1] shl ecx,5 mov bx, word ptr[4+4+esi] ;ptri->vertindex[2] shl edx,5 add ecx,edi shl ebx,5 add edx,edi add ebx,edi mov eax,ds:dword ptr[0+4+ecx] mov esi,ds:dword ptr[0+0+ecx] sub eax,ds:dword ptr[0+4+edx] sub esi,ds:dword ptr[0+0+ebx] imul eax,esi mov esi,ds:dword ptr[0+0+ecx] mov edi,ds:dword ptr[0+4+ecx] sub esi,ds:dword ptr[0+0+edx] sub edi,ds:dword ptr[0+4+ebx] imul edi,esi sub eax,edi jns LNextTri mov ds:dword ptr[_d_xdenom],eax fild ds:dword ptr[_d_xdenom] mov eax,ds:dword ptr[0+0+ecx] mov esi,ds:dword ptr[0+4+ecx] mov ds:dword ptr[_r_p0+0],eax mov ds:dword ptr[_r_p0+4],esi mov eax,ds:dword ptr[0+8+ecx] mov esi,ds:dword ptr[0+12+ecx] mov ds:dword ptr[_r_p0+8],eax mov ds:dword ptr[_r_p0+12],esi mov eax,ds:dword ptr[0+16+ecx] mov esi,ds:dword ptr[0+20+ecx] mov ds:dword ptr[_r_p0+16],eax mov ds:dword ptr[_r_p0+20],esi fdivr ds:dword ptr[float_1] mov eax,ds:dword ptr[0+0+edx] mov esi,ds:dword ptr[0+4+edx] mov ds:dword ptr[_r_p1+0],eax mov ds:dword ptr[_r_p1+4],esi mov eax,ds:dword ptr[0+8+edx] mov esi,ds:dword ptr[0+12+edx] mov ds:dword ptr[_r_p1+8],eax mov ds:dword ptr[_r_p1+12],esi mov eax,ds:dword ptr[0+16+edx] mov esi,ds:dword ptr[0+20+edx] mov ds:dword ptr[_r_p1+16],eax mov ds:dword ptr[_r_p1+20],esi mov eax,ds:dword ptr[0+0+ebx] mov esi,ds:dword ptr[0+4+ebx] mov ds:dword ptr[_r_p2+0],eax mov ds:dword ptr[_r_p2+4],esi mov eax,ds:dword ptr[0+8+ebx] mov esi,ds:dword ptr[0+12+ebx] mov ds:dword ptr[_r_p2+8],eax mov ds:dword ptr[_r_p2+12],esi mov eax,ds:dword ptr[0+16+ebx] mov esi,ds:dword ptr[0+20+ebx] mov ds:dword ptr[_r_p2+16],eax mov edi,ds:dword ptr[_r_affinetridesc+16] mov ds:dword ptr[_r_p2+20],esi mov eax,ds:dword ptr[0+edi] test eax,eax jnz LFacesFront mov eax,ds:dword ptr[24+ecx] mov esi,ds:dword ptr[24+edx] mov edi,ds:dword ptr[24+ebx] test eax,00020h mov eax,ds:dword ptr[_r_affinetridesc+32] jz LOnseamDone0 add ds:dword ptr[_r_p0+8],eax LOnseamDone0: test esi,00020h jz LOnseamDone1 add ds:dword ptr[_r_p1+8],eax LOnseamDone1: test edi,00020h jz LOnseamDone2 add ds:dword ptr[_r_p2+8],eax LOnseamDone2: LFacesFront: fstp ds:dword ptr[_d_xdenom] call near ptr _D_PolysetSetEdgeTable call near ptr _D_RasterizeAliasPolySmooth LNextTri: mov esi,ds:dword ptr[_r_affinetridesc+16] ; sub ebp,16 ; jnz LNDLoop pop edi pop esi pop ebx ; pop ebp add esp,offset (((1024+1 + 1 + ((32 - 1) / 32)) + 1) * 32) ret _TEXT ENDS END engine/h2shared/masm/d_polysa2.asm000066400000000000000000000635471444734033100174000ustar00rootroot00000000000000; ; d_polysa2.asm -- for MASM ; x86 assembly-language polygon model drawing code ; with translucency handling, #1. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _d_viewbuffer:dword externdef _d_scantable:dword externdef _r_refdef:dword externdef _a_sstepxfrac:dword externdef _r_affinetridesc:dword externdef _acolormap:dword externdef _d_pcolormap:dword externdef _d_sfrac:dword externdef _d_ptex:dword externdef _d_pedgespanpackage:dword externdef _d_tfrac:dword externdef _d_light:dword externdef _d_zi:dword externdef _d_pdest:dword externdef _d_pz:dword externdef _d_aspancount:dword externdef _erroradjustup:dword externdef _erroradjustdown:dword externdef _errorterm:dword externdef _d_xdenom:dword externdef _r_p0:dword externdef _r_p1:dword externdef _r_p2:dword externdef _a_tstepxfrac:dword externdef _a_ststepxwhole:dword externdef _zspantable:dword externdef _skintable:dword externdef _d_countextrastep:dword externdef _ubasestep:dword externdef _a_spans:dword externdef _d_pdestextrastep:dword externdef _d_pzextrastep:dword externdef _d_sfracextrastep:dword externdef _d_ptexextrastep:dword externdef _d_tfracextrastep:dword externdef _d_lightextrastep:dword externdef _d_ziextrastep:dword externdef _d_pdestbasestep:dword externdef _d_pzbasestep:dword externdef _d_sfracbasestep:dword externdef _d_ptexbasestep:dword externdef _d_tfracbasestep:dword externdef _d_lightbasestep:dword externdef _d_zibasestep:dword externdef _r_lstepx:dword externdef _r_lstepy:dword externdef _r_sstepx:dword externdef _r_sstepy:dword externdef _r_tstepx:dword externdef _r_tstepy:dword externdef _r_zistepx:dword externdef _r_zistepy:dword externdef _mainTransTable:dword externdef _D_PolysetSetEdgeTable:dword externdef _D_RasterizeAliasPolySmooth:dword ; externs from ASM-only code externdef float_point5:dword externdef float_1:dword externdef float_minus_1:dword externdef float_0:dword externdef advancetable:dword externdef sstep:dword externdef tstep:dword externdef ceil_cw:dword externdef single_cw:dword _DATA SEGMENT align 4 p10_minus_p20 dd 0 p01_minus_p21 dd 0 temp0 dd 0 temp1 dd 0 Ltemp dd 0 aff8entryvec_table dd LDraw8, LDraw7, LDraw6, LDraw5 dd LDraw4, LDraw3, LDraw2, LDraw1 lzistepx dd 0 _DATA ENDS _TEXT SEGMENT externdef _D_PolysetSetEdgeTable:dword externdef _D_RasterizeAliasPolySmooth:dword public _D_PolysetAff8StartT _D_PolysetAff8StartT: public _D_PolysetCalcGradientsT _D_PolysetCalcGradientsT: fild ds:dword ptr[_r_p0+0] fild ds:dword ptr[_r_p2+0] fild ds:dword ptr[_r_p0+4] fild ds:dword ptr[_r_p2+4] fild ds:dword ptr[_r_p1+0] fild ds:dword ptr[_r_p1+4] fxch st(3) fsub st(0),st(2) fxch st(1) fsub st(0),st(4) fxch st(5) fsubrp st(4),st(0) fxch st(2) fsubrp st(1),st(0) fxch st(1) fld ds:dword ptr[_d_xdenom] fxch st(4) fstp ds:dword ptr[p10_minus_p20] fstp ds:dword ptr[p01_minus_p21] fxch st(2) fild ds:dword ptr[_r_p2+16] fild ds:dword ptr[_r_p0+16] fild ds:dword ptr[_r_p1+16] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmul st(0),st(5) fxch st(2) fld st(0) fmul ds:dword ptr[p01_minus_p21] fxch st(2) fmul ds:dword ptr[p10_minus_p20] fxch st(1) fmul st(0),st(5) fxch st(2) fsubrp st(3),st(0) fsubp st(1),st(0) fld st(2) fmul ds:dword ptr[float_minus_1] fxch st(2) fmul st(0),st(3) fxch st(1) fmul st(0),st(2) fldcw ds:word ptr[ceil_cw] fistp ds:dword ptr[_r_lstepy] fistp ds:dword ptr[_r_lstepx] fldcw ds:word ptr[single_cw] fild ds:dword ptr[_r_p2+8] fild ds:dword ptr[_r_p0+8] fild ds:dword ptr[_r_p1+8] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmul st(0),st(6) fxch st(2) fld st(0) fmul ds:dword ptr[p01_minus_p21] fxch st(2) fmul ds:dword ptr[p10_minus_p20] fxch st(1) fmul st(0),st(6) fxch st(2) fsubrp st(3),st(0) fsubp st(1),st(0) fmul st(0),st(2) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[_r_sstepy] fistp ds:dword ptr[_r_sstepx] fild ds:dword ptr[_r_p2+12] fild ds:dword ptr[_r_p0+12] fild ds:dword ptr[_r_p1+12] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmul st(0),st(6) fxch st(2) fld st(0) fmul ds:dword ptr[p01_minus_p21] fxch st(2) fmul ds:dword ptr[p10_minus_p20] fxch st(1) fmul st(0),st(6) fxch st(2) fsubrp st(3),st(0) fsubp st(1),st(0) fmul st(0),st(2) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[_r_tstepy] fistp ds:dword ptr[_r_tstepx] fild ds:dword ptr[_r_p2+20] fild ds:dword ptr[_r_p0+20] fild ds:dword ptr[_r_p1+20] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmulp st(6),st(0) fxch st(1) fld st(0) fmul ds:dword ptr[p01_minus_p21] fxch st(2) fmul ds:dword ptr[p10_minus_p20] fxch st(1) fmulp st(5),st(0) fxch st(5) fsubp st(1),st(0) fxch st(3) fsubrp st(4),st(0) fxch st(1) fmulp st(2),st(0) fmulp st(2),st(0) fistp ds:dword ptr[_r_zistepx] fistp ds:dword ptr[_r_zistepy] mov eax,ds:dword ptr[_r_sstepx] mov edx,ds:dword ptr[_r_tstepx] shl eax,16 shl edx,16 mov ds:dword ptr[_a_sstepxfrac],eax mov ds:dword ptr[_a_tstepxfrac],edx mov ecx,ds:dword ptr[_r_sstepx] mov eax,ds:dword ptr[_r_tstepx] sar ecx,16 sar eax,16 imul ds:dword ptr[4+0+esp] add eax,ecx mov ds:dword ptr[_a_ststepxwhole],eax ret public _D_PolysetRecursiveTriangleT _D_PolysetRecursiveTriangleT: push ebp push esi push edi push ebx mov esi,ds:dword ptr[8+16+esp] mov ebx,ds:dword ptr[4+16+esp] mov edi,ds:dword ptr[12+16+esp] mov eax,ds:dword ptr[0+esi] mov edx,ds:dword ptr[0+ebx] mov ebp,ds:dword ptr[4+esi] sub eax,edx mov ecx,ds:dword ptr[4+ebx] sub ebp,ecx inc eax cmp eax,2 ja LSplit mov eax,ds:dword ptr[0+edi] inc ebp cmp ebp,2 ja LSplit mov edx,ds:dword ptr[0+esi] mov ebp,ds:dword ptr[4+edi] sub eax,edx mov ecx,ds:dword ptr[4+esi] sub ebp,ecx inc eax cmp eax,2 ja LSplit2 mov eax,ds:dword ptr[0+ebx] inc ebp cmp ebp,2 ja LSplit2 mov edx,ds:dword ptr[0+edi] mov ebp,ds:dword ptr[4+ebx] sub eax,edx mov ecx,ds:dword ptr[4+edi] sub ebp,ecx inc eax inc ebp mov edx,ebx cmp eax,2 ja LSplit3 cmp ebp,2 jna LDone LSplit3: mov ebx,edi mov edi,esi mov esi,edx jmp LSplit LSplit2: mov eax,ebx mov ebx,esi mov esi,edi mov edi,eax LSplit: sub esp,24 mov eax,ds:dword ptr[8+ebx] mov edx,ds:dword ptr[8+esi] mov ecx,ds:dword ptr[12+ebx] add eax,edx mov edx,ds:dword ptr[12+esi] sar eax,1 add ecx,edx mov ds:dword ptr[8+esp],eax mov eax,ds:dword ptr[20+ebx] sar ecx,1 mov edx,ds:dword ptr[20+esi] mov ds:dword ptr[12+esp],ecx add eax,edx mov ecx,ds:dword ptr[0+ebx] mov edx,ds:dword ptr[0+esi] sar eax,1 add edx,ecx mov ds:dword ptr[20+esp],eax mov eax,ds:dword ptr[4+ebx] sar edx,1 mov ebp,ds:dword ptr[4+esi] mov ds:dword ptr[0+esp],edx add ebp,eax sar ebp,1 mov ds:dword ptr[4+esp],ebp cmp ds:dword ptr[4+esi],eax jg LNoDraw mov edx,ds:dword ptr[0+esi] jnz LDraw cmp edx,ecx jl LNoDraw LDraw: mov edx,ds:dword ptr[20+esp] mov ecx,ds:dword ptr[4+esp] sar edx,16 mov ebp,ds:dword ptr[0+esp] mov eax,ds:dword ptr[_zspantable+ecx*4] cmp dx,ds:word ptr[eax+ebp*2] jnge LNoDraw ;rj2 mov ds:word ptr[eax+ebp*2],dx mov eax,ds:dword ptr[12+esp] sar eax,16 mov edx,ds:dword ptr[8+esp] sar edx,16 sub ecx,ecx mov eax,ds:dword ptr[_skintable+eax*4] mov ebp,ds:dword ptr[4+esp] mov cl,ds:byte ptr[eax+edx] ; texture pixel or cl,cl jz Skip1B ; color 0 = no draw mov edx,ds:dword ptr[_d_pcolormap] mov dh,ds:byte ptr[edx+ecx] mov ecx,ds:dword ptr[0+esp] mov eax,ds:dword ptr[_d_scantable+ebp*4] add ecx,eax mov eax,ds:dword ptr[_d_viewbuffer] ; trans stuff mov dl,ds:byte ptr[eax+ecx] and edx, 0ffffh mov dh,ds:byte ptr[12345678h + edx] TranPatch1: mov ds:byte ptr[eax+ecx],dh ; rjr distance ; mov ds:byte ptr[eax+ecx],0 Skip1B: LNoDraw: push esp push ebx push edi call near ptr _D_PolysetRecursiveTriangleT mov ebx,esp push esi push ebx push edi call near ptr _D_PolysetRecursiveTriangleT add esp,24 LDone: pop ebx pop edi pop esi pop ebp ret 12 public _D_PolysetDrawSpans8T _D_PolysetDrawSpans8T: push esi push ebx mov esi,ds:dword ptr[4+8+esp] mov ecx,ds:dword ptr[_r_zistepx] push ebp push edi ror ecx,16 mov edx,ds:dword ptr[8+esi] mov ds:dword ptr[lzistepx],ecx LSpanLoop: mov eax,ds:dword ptr[_d_aspancount] sub eax,edx mov edx,ds:dword ptr[_erroradjustup] mov ebx,ds:dword ptr[_errorterm] add ebx,edx js LNoTurnover mov edx,ds:dword ptr[_erroradjustdown] mov edi,ds:dword ptr[_d_countextrastep] sub ebx,edx mov ebp,ds:dword ptr[_d_aspancount] mov ds:dword ptr[_errorterm],ebx add ebp,edi mov ds:dword ptr[_d_aspancount],ebp jmp LRightEdgeStepped LNoTurnover: mov edi,ds:dword ptr[_d_aspancount] mov edx,ds:dword ptr[_ubasestep] mov ds:dword ptr[_errorterm],ebx add edi,edx mov ds:dword ptr[_d_aspancount],edi LRightEdgeStepped: cmp eax,1 jl LNextSpan jz LExactlyOneLong mov ecx,ds:dword ptr[_a_ststepxwhole] mov edx,ds:dword ptr[_r_affinetridesc+8] mov ds:dword ptr[advancetable+4],ecx add ecx,edx mov ds:dword ptr[advancetable],ecx mov ecx,ds:dword ptr[_a_tstepxfrac] mov cx,ds:word ptr[_r_lstepx] mov edx,eax mov ds:dword ptr[tstep],ecx add edx,7 shr edx,3 mov ebx,ds:dword ptr[16+esi] mov bx,dx mov ecx,ds:dword ptr[4+esi] neg eax mov edi,ds:dword ptr[0+esi] and eax,7 sub edi,eax sub ecx,eax sub ecx,eax mov edx,ds:dword ptr[20+esi] mov dx,ds:word ptr[24+esi] mov ebp,ds:dword ptr[28+esi] ror ebp,16 push esi mov esi,ds:dword ptr[12+esi] jmp dword ptr[aff8entryvec_table+eax*4] LDrawLoop: LDraw8: cmp bp,ds:word ptr[ecx] jl Lp1 xor eax,eax mov ah,dh ; light mov al,ds:byte ptr[esi] ; texture pixel or al,al jz SkipA2 ; color 0 = no draw ;rj2 mov ds:word ptr[ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch8: ; trans stuff mov al,ds:byte ptr[edi] mov ah,ds:byte ptr[12345678h + eax] TranPatch2: mov ds:byte ptr[edi],ah ; rj ;mov ds:byte ptr[edi],0 SkipA2: Lp1: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw7: cmp bp,ds:word ptr[2+ecx] jl Lp2 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] ; texture pixel or al,al jz SkipB2 ; color 0 = no draw ;rj2 mov ds:word ptr[2+ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch7: ; trans stuff mov al,ds:byte ptr[edi+1] mov ah,ds:byte ptr[12345678h + eax] TranPatch3: mov ds:byte ptr[1+edi],ah ; rj ;mov ds:byte ptr[1+edi],0 SkipB2: Lp2: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw6: cmp bp,ds:word ptr[4+ecx] jl Lp3 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] ; texture pixel or al,al jz SkipC2 ; color 0 = no draw ;rj2 mov ds:word ptr[4+ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch6: ; trans stuff mov al,ds:byte ptr[edi+2] mov ah,ds:byte ptr[12345678h + eax] TranPatch4: mov ds:byte ptr[2+edi],ah ; rj ;mov ds:byte ptr[2+edi],0 SkipC2: Lp3: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw5: cmp bp,ds:word ptr[6+ecx] jl Lp4 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] ; texture pixel or al,al jz SkipD2 ; color 0 = no draw ;rj2 mov ds:word ptr[6+ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch5: ; trans stuff mov al,ds:byte ptr[edi+3] mov ah,ds:byte ptr[12345678h + eax] TranPatch5: mov ds:byte ptr[3+edi],ah ; rj ;mov ds:byte ptr[3+edi],0 SkipD2: Lp4: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw4: cmp bp,ds:word ptr[8+ecx] jl Lp5 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] ; texture pixel or al,al jz SkipE2 ; color 0 = no draw ;rj2 mov ds:word ptr[8+ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch4: ; trans stuff mov al,ds:byte ptr[edi+4] mov ah,ds:byte ptr[12345678h + eax] TranPatch6: mov ds:byte ptr[4+edi],ah ; rj ;mov ds:byte ptr[4+edi],0 SkipE2: Lp5: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw3: cmp bp,ds:word ptr[10+ecx] jl Lp6 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] ; texture pixel or al,al jz SkipF2 ; color 0 = no draw ;rj2 mov ds:word ptr[10+ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch3: ; trans stuff mov al,ds:byte ptr[edi+5] mov ah,ds:byte ptr[12345678h + eax] TranPatch7: mov ds:byte ptr[5+edi],ah ; rj ;mov ds:byte ptr[5+edi],0 SkipF2: Lp6: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw2: cmp bp,ds:word ptr[12+ecx] jl Lp7 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] ; texture pixel or al,al jz SkipG2 ; color 0 = no draw ;rj2 mov ds:word ptr[12+ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch2: ; trans stuff mov al,ds:byte ptr[edi+6] mov ah,ds:byte ptr[12345678h + eax] TranPatch8: mov ds:byte ptr[6+edi],ah ; rj ;mov ds:byte ptr[6+edi],0 SkipG2: Lp7: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw1: cmp bp,ds:word ptr[14+ecx] jl Lp8 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] ; texture pixel or al,al jz SkipH2 ; color 0 = no draw ;rj2 mov ds:word ptr[14+ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch1: ; trans stuff mov al,ds:byte ptr[edi+7] mov ah,ds:byte ptr[12345678h + eax] TranPatch9: mov ds:byte ptr[7+edi],ah ; rj ;mov ds:byte ptr[7+edi],0 SkipH2: Lp8: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] add edi,8 add ecx,16 dec bx jnz LDrawLoop pop esi LNextSpan: add esi,32 LNextSpanESISet: mov edx,ds:dword ptr[8+esi] cmp edx,offset -999999 jnz LSpanLoop pop edi pop ebp pop ebx pop esi ret LExactlyOneLong: mov ecx,ds:dword ptr[4+esi] mov ebp,ds:dword ptr[28+esi] ror ebp,16 mov ebx,ds:dword ptr[12+esi] cmp bp,ds:word ptr[ecx] jl LNextSpan xor eax,eax mov edi,ds:dword ptr[0+esi] mov ah,ds:byte ptr[24+1+esi] add esi,32 mov al,ds:byte ptr[ebx] ; texture pixel or al,al jz SkipI2 ; color 0 = no draw ;rj2 mov ds:word ptr[ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch9: ; trans stuff mov al,ds:byte ptr[edi] mov ah,ds:byte ptr[12345678h + eax] TranPatch10: mov ds:byte ptr[edi],ah ; rjr ;mov ds:byte ptr[edi],0 SkipI2: jmp LNextSpanESISet public _D_Aff8PatchT _D_Aff8PatchT: mov eax,ds:dword ptr[4+esp] mov ds:dword ptr[LPatch1-4],eax mov ds:dword ptr[LPatch2-4],eax mov ds:dword ptr[LPatch3-4],eax mov ds:dword ptr[LPatch4-4],eax mov ds:dword ptr[LPatch5-4],eax mov ds:dword ptr[LPatch6-4],eax mov ds:dword ptr[LPatch7-4],eax mov ds:dword ptr[LPatch8-4],eax mov ds:dword ptr[LPatch9-4],eax ret public _D_PolysetDrawT _D_PolysetDrawT: sub esp,offset (((1024+1 + 1 + ((32 - 1) / 32)) + 1) * 32) mov eax,esp add eax,32 - 1 and eax,offset not (32 - 1) mov ds:dword ptr[_a_spans],eax mov eax,ds:dword ptr[_r_affinetridesc+28] test eax,eax jz _D_DrawNonSubdivT ;push ebp ;mov ebp,ds:dword ptr[_r_affinetridesc+24] push esi ;shl ebp,4 push ebx mov ebx,ds:dword ptr[_r_affinetridesc+16] push edi mov edi,ds:dword ptr[_r_affinetridesc+20] Llooptop: xor ecx,ecx xor esi,esi xor edx,edx mov cx,word ptr[4+0+ebx] mov si,word ptr[4+2+ebx] mov dx,word ptr[4+4+ebx] shl ecx,5 shl esi,5 add ecx,edi shl edx,5 add esi,edi add edx,edi fild ds:dword ptr[0+4+ecx] fild ds:dword ptr[0+4+esi] fild ds:dword ptr[0+0+ecx] fild ds:dword ptr[0+0+edx] fxch st(2) fsubr st(0),st(3) fild ds:dword ptr[0+0+esi] fxch st(2) fsubr st(3),st(0) fild ds:dword ptr[0+4+edx] fxch st(1) fsubrp st(3),st(0) fxch st(1) fmulp st(3),st(0) fsubp st(3),st(0) mov eax,ds:dword ptr[0+16+ecx] and eax,0FF00h fmulp st(2),st(0) add eax,ds:dword ptr[_acolormap] fsubrp st(1),st(0) mov ds:dword ptr[_d_pcolormap],eax fstp ds:dword ptr[Ltemp] mov eax,ds:dword ptr[Ltemp] sub eax,080000001h jc Lskip mov eax,ds:dword ptr[0+ebx] test eax,eax jz Lfacesback push edx push esi push ecx call near ptr _D_PolysetRecursiveTriangleT ; sub ebp,16 ; jnz Llooptop jmp Ldone2 Lfacesback: mov eax,ds:dword ptr[0+8+ecx] push eax mov eax,ds:dword ptr[0+8+esi] push eax mov eax,ds:dword ptr[0+8+edx] push eax push ecx push edx mov eax,ds:dword ptr[_r_affinetridesc+32] test ds:dword ptr[24+ecx],00020h jz Lp11 add ds:dword ptr[0+8+ecx],eax Lp11: test ds:dword ptr[24+esi],00020h jz Lp12 add ds:dword ptr[0+8+esi],eax Lp12: test ds:dword ptr[24+edx],00020h jz Lp13 add ds:dword ptr[0+8+edx],eax Lp13: push edx push esi push ecx call near ptr _D_PolysetRecursiveTriangleT pop edx pop ecx pop eax mov ds:dword ptr[0+8+edx],eax pop eax mov ds:dword ptr[0+8+esi],eax pop eax mov ds:dword ptr[0+8+ecx],eax Lskip: ; sub ebp,16 ; jnz Llooptop Ldone2: pop edi pop ebx pop esi ; pop ebp add esp,offset (((1024+1 + 1 + ((32 - 1) / 32)) + 1) * 32) ret public _D_PolysetScanLeftEdgeT _D_PolysetScanLeftEdgeT: push ebp push esi push edi push ebx mov eax,ds:dword ptr[4+16+esp] mov ecx,ds:dword ptr[_d_sfrac] and eax,0FFFFh mov ebx,ds:dword ptr[_d_ptex] or ecx,eax mov esi,ds:dword ptr[_d_pedgespanpackage] mov edx,ds:dword ptr[_d_tfrac] mov edi,ds:dword ptr[_d_light] mov ebp,ds:dword ptr[_d_zi] LScanLoop: mov ds:dword ptr[12+esi],ebx mov eax,ds:dword ptr[_d_pdest] mov ds:dword ptr[0+esi],eax mov eax,ds:dword ptr[_d_pz] mov ds:dword ptr[4+esi],eax mov eax,ds:dword ptr[_d_aspancount] mov ds:dword ptr[8+esi],eax mov ds:dword ptr[24+esi],edi mov ds:dword ptr[28+esi],ebp mov ds:dword ptr[16+esi],ecx mov ds:dword ptr[20+esi],edx mov al,ds:byte ptr[32+esi] add esi,32 mov eax,ds:dword ptr[_erroradjustup] mov ds:dword ptr[_d_pedgespanpackage],esi mov esi,ds:dword ptr[_errorterm] add esi,eax mov eax,ds:dword ptr[_d_pdest] js LNoLeftEdgeTurnover sub esi,ds:dword ptr[_erroradjustdown] add eax,ds:dword ptr[_d_pdestextrastep] mov ds:dword ptr[_errorterm],esi mov ds:dword ptr[_d_pdest],eax mov eax,ds:dword ptr[_d_pz] mov esi,ds:dword ptr[_d_aspancount] add eax,ds:dword ptr[_d_pzextrastep] add ecx,ds:dword ptr[_d_sfracextrastep] adc ebx,ds:dword ptr[_d_ptexextrastep] add esi,ds:dword ptr[_d_countextrastep] mov ds:dword ptr[_d_pz],eax mov eax,ds:dword ptr[_d_tfracextrastep] mov ds:dword ptr[_d_aspancount],esi add edx,eax jnc LSkip1 add ebx,ds:dword ptr[_r_affinetridesc+8] LSkip1: add edi,ds:dword ptr[_d_lightextrastep] add ebp,ds:dword ptr[_d_ziextrastep] mov esi,ds:dword ptr[_d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz LScanLoop pop ebx pop edi pop esi pop ebp ret LNoLeftEdgeTurnover: mov ds:dword ptr[_errorterm],esi add eax,ds:dword ptr[_d_pdestbasestep] mov ds:dword ptr[_d_pdest],eax mov eax,ds:dword ptr[_d_pz] mov esi,ds:dword ptr[_d_aspancount] add eax,ds:dword ptr[_d_pzbasestep] add ecx,ds:dword ptr[_d_sfracbasestep] adc ebx,ds:dword ptr[_d_ptexbasestep] add esi,ds:dword ptr[_ubasestep] mov ds:dword ptr[_d_pz],eax mov ds:dword ptr[_d_aspancount],esi mov esi,ds:dword ptr[_d_tfracbasestep] add edx,esi jnc LSkip2 add ebx,ds:dword ptr[_r_affinetridesc+8] LSkip2: add edi,ds:dword ptr[_d_lightbasestep] add ebp,ds:dword ptr[_d_zibasestep] mov esi,ds:dword ptr[_d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz LScanLoop pop ebx pop edi pop esi pop ebp ret _L_PDFVertT: push esi push edi mov eax,ds:dword ptr[0+0+ebx] mov edx,ds:dword ptr[_r_refdef+40] cmp eax,edx jge LNextVert mov esi,ds:dword ptr[0+4+ebx] mov edx,ds:dword ptr[_r_refdef+44] cmp esi,edx jge LNextVert mov edi,ds:dword ptr[_zspantable+esi*4] mov edx,ds:dword ptr[0+20+ebx] shr edx,16 cmp dx,ds:word ptr[edi+eax*2] jl LNextVert ;rj2 mov ds:word ptr[edi+eax*2],dx mov edi,ds:dword ptr[0+12+ebx] shr edi,16 mov edi,ds:dword ptr[_skintable+edi*4] mov edx,ds:dword ptr[0+8+ebx] shr edx,16 mov dl,ds:byte ptr[edi+edx] ; texture pixel or dl,dl jz Skip2B ; color 0 = no draw mov edi,ds:dword ptr[0+16+ebx] and edi,0FF00h and edx,000FFh add edi,edx mov edx,ds:dword ptr[_acolormap] mov dh,ds:byte ptr[edx+edi*1] mov edi,ds:dword ptr[_d_scantable+esi*4] mov esi,ds:dword ptr[_d_viewbuffer] add edi,eax ; trans stuff mov dl,ds:byte ptr[esi+edi] and edx, 0ffffh mov dh,ds:byte ptr[12345678h + edx] TranPatch11: mov ds:byte ptr[esi+edi],dh ; rjr distance ;mov ds:byte ptr[esi+edi],0 Skip2B: LNextVert: pop edi pop esi ret public _D_PolysetDrawFinalVertsT _D_PolysetDrawFinalVertsT: push ebp push ebx mov ebx,dword ptr[4+8+esp] ;pv1 call _L_PDFVertT mov ebx,dword ptr[8+8+esp] ;pv2 call _L_PDFVertT mov ebx,dword ptr[12+8+esp];pv3 call _L_PDFVertT pop ebx pop ebp ret public _D_DrawNonSubdivT _D_DrawNonSubdivT: ; push ebp ; mov ebp,ds:dword ptr[_r_affinetridesc+24] push ebx ; shl ebp,4 push esi mov esi,ds:dword ptr[_r_affinetridesc+16] push edi LNDLoop: mov edi,ds:dword ptr[_r_affinetridesc+20] xor ecx,ecx; //clear i1 xor edx,edx; //clear i2 xor ebx,ebx; //clear i3 mov cx, word ptr[4+0+esi] ;ptri->vertindex[0] mov dx, word ptr[4+2+esi] ;ptri->vertindex[1] mov bx, word ptr[4+4+esi] ;ptri->vertindex[2] shl ecx,5 shl edx,5 shl ebx,5 add ecx,edi add edx,edi add ebx,edi mov eax,ds:dword ptr[0+4+ecx] mov esi,ds:dword ptr[0+0+ecx] sub eax,ds:dword ptr[0+4+edx] sub esi,ds:dword ptr[0+0+ebx] imul eax,esi mov esi,ds:dword ptr[0+0+ecx] mov edi,ds:dword ptr[0+4+ecx] sub esi,ds:dword ptr[0+0+edx] sub edi,ds:dword ptr[0+4+ebx] imul edi,esi sub eax,edi jns LNextTri mov ds:dword ptr[_d_xdenom],eax fild ds:dword ptr[_d_xdenom] mov eax,ds:dword ptr[0+0+ecx] mov esi,ds:dword ptr[0+4+ecx] mov ds:dword ptr[_r_p0+0],eax mov ds:dword ptr[_r_p0+4],esi mov eax,ds:dword ptr[0+8+ecx] mov esi,ds:dword ptr[0+12+ecx] mov ds:dword ptr[_r_p0+8],eax mov ds:dword ptr[_r_p0+12],esi mov eax,ds:dword ptr[0+16+ecx] mov esi,ds:dword ptr[0+20+ecx] mov ds:dword ptr[_r_p0+16],eax mov ds:dword ptr[_r_p0+20],esi fdivr ds:dword ptr[float_1] mov eax,ds:dword ptr[0+0+edx] mov esi,ds:dword ptr[0+4+edx] mov ds:dword ptr[_r_p1+0],eax mov ds:dword ptr[_r_p1+4],esi mov eax,ds:dword ptr[0+8+edx] mov esi,ds:dword ptr[0+12+edx] mov ds:dword ptr[_r_p1+8],eax mov ds:dword ptr[_r_p1+12],esi mov eax,ds:dword ptr[0+16+edx] mov esi,ds:dword ptr[0+20+edx] mov ds:dword ptr[_r_p1+16],eax mov ds:dword ptr[_r_p1+20],esi mov eax,ds:dword ptr[0+0+ebx] mov esi,ds:dword ptr[0+4+ebx] mov ds:dword ptr[_r_p2+0],eax mov ds:dword ptr[_r_p2+4],esi mov eax,ds:dword ptr[0+8+ebx] mov esi,ds:dword ptr[0+12+ebx] mov ds:dword ptr[_r_p2+8],eax mov ds:dword ptr[_r_p2+12],esi mov eax,ds:dword ptr[0+16+ebx] mov esi,ds:dword ptr[0+20+ebx] mov ds:dword ptr[_r_p2+16],eax mov edi,ds:dword ptr[_r_affinetridesc+16] mov ds:dword ptr[_r_p2+20],esi mov eax,ds:dword ptr[0+edi] test eax,eax jnz LFacesFront mov eax,ds:dword ptr[24+ecx] mov esi,ds:dword ptr[24+edx] mov edi,ds:dword ptr[24+ebx] test eax,00020h mov eax,ds:dword ptr[_r_affinetridesc+32] jz LOnseamDone0 add ds:dword ptr[_r_p0+8],eax LOnseamDone0: test esi,00020h jz LOnseamDone1 add ds:dword ptr[_r_p1+8],eax LOnseamDone1: test edi,00020h jz LOnseamDone2 add ds:dword ptr[_r_p2+8],eax LOnseamDone2: LFacesFront: fstp ds:dword ptr[_d_xdenom] call near ptr _D_PolysetSetEdgeTable call near ptr _D_RasterizeAliasPolySmooth LNextTri: mov esi,ds:dword ptr[_r_affinetridesc+16] ; sub ebp,16 ; jnz LNDLoop pop edi pop esi pop ebx ; pop ebp add esp,offset (((1024+1 + 1 + ((32 - 1) / 32)) + 1) * 32) ret public _D_PolysetAff8EndT _D_PolysetAff8EndT: _TEXT ENDS _DATA SEGMENT align 4 LPatchTable: dd TranPatch1-4 dd TranPatch2-4 dd TranPatch3-4 dd TranPatch4-4 dd TranPatch5-4 dd TranPatch6-4 dd TranPatch7-4 dd TranPatch8-4 dd TranPatch9-4 dd TranPatch10-4 dd TranPatch11-4 _DATA ENDS _TEXT SEGMENT align 4 public _R_TranPatch1 _R_TranPatch1: push ebx mov eax,ds:dword ptr[_mainTransTable] mov ebx,offset LPatchTable mov ecx,11 LPatchLoop: mov edx,ds:dword ptr[ebx] add ebx,4 mov ds:dword ptr[edx],eax dec ecx jnz LPatchLoop pop ebx ret _TEXT ENDS END engine/h2shared/masm/d_polysa3.asm000066400000000000000000000642061444734033100173720ustar00rootroot00000000000000; ; d_polysa3.asm -- for MASM ; x86 assembly-language polygon model drawing code ; with translucency handling, #2. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _d_viewbuffer:dword externdef _d_scantable:dword externdef _r_refdef:dword externdef _a_sstepxfrac:dword externdef _r_affinetridesc:dword externdef _acolormap:dword externdef _d_pcolormap:dword externdef _d_sfrac:dword externdef _d_ptex:dword externdef _d_pedgespanpackage:dword externdef _d_tfrac:dword externdef _d_light:dword externdef _d_zi:dword externdef _d_pdest:dword externdef _d_pz:dword externdef _d_aspancount:dword externdef _erroradjustup:dword externdef _erroradjustdown:dword externdef _errorterm:dword externdef _d_xdenom:dword externdef _r_p0:dword externdef _r_p1:dword externdef _r_p2:dword externdef _a_tstepxfrac:dword externdef _a_ststepxwhole:dword externdef _zspantable:dword externdef _skintable:dword externdef _d_countextrastep:dword externdef _ubasestep:dword externdef _a_spans:dword externdef _d_pdestextrastep:dword externdef _d_pzextrastep:dword externdef _d_sfracextrastep:dword externdef _d_ptexextrastep:dword externdef _d_tfracextrastep:dword externdef _d_lightextrastep:dword externdef _d_ziextrastep:dword externdef _d_pdestbasestep:dword externdef _d_pzbasestep:dword externdef _d_sfracbasestep:dword externdef _d_ptexbasestep:dword externdef _d_tfracbasestep:dword externdef _d_lightbasestep:dword externdef _d_zibasestep:dword externdef _r_lstepx:dword externdef _r_lstepy:dword externdef _r_sstepx:dword externdef _r_sstepy:dword externdef _r_tstepx:dword externdef _r_tstepy:dword externdef _r_zistepx:dword externdef _r_zistepy:dword externdef _mainTransTable:dword externdef _D_PolysetSetEdgeTable:dword externdef _D_RasterizeAliasPolySmooth:dword ; externs from ASM-only code externdef float_point5:dword externdef float_1:dword externdef float_minus_1:dword externdef float_0:dword externdef advancetable:dword externdef sstep:dword externdef tstep:dword externdef ceil_cw:dword externdef single_cw:dword _DATA SEGMENT align 4 p10_minus_p20 dd 0 p01_minus_p21 dd 0 temp0 dd 0 temp1 dd 0 Ltemp dd 0 aff8entryvec_table dd LDraw8, LDraw7, LDraw6, LDraw5 dd LDraw4, LDraw3, LDraw2, LDraw1 lzistepx dd 0 _DATA ENDS _TEXT SEGMENT externdef _D_PolysetSetEdgeTable:dword externdef _D_RasterizeAliasPolySmooth:dword public _D_PolysetAff8StartT2 _D_PolysetAff8StartT2: public _D_PolysetCalcGradientsT2 _D_PolysetCalcGradientsT2: fild ds:dword ptr[_r_p0+0] fild ds:dword ptr[_r_p2+0] fild ds:dword ptr[_r_p0+4] fild ds:dword ptr[_r_p2+4] fild ds:dword ptr[_r_p1+0] fild ds:dword ptr[_r_p1+4] fxch st(3) fsub st(0),st(2) fxch st(1) fsub st(0),st(4) fxch st(5) fsubrp st(4),st(0) fxch st(2) fsubrp st(1),st(0) fxch st(1) fld ds:dword ptr[_d_xdenom] fxch st(4) fstp ds:dword ptr[p10_minus_p20] fstp ds:dword ptr[p01_minus_p21] fxch st(2) fild ds:dword ptr[_r_p2+16] fild ds:dword ptr[_r_p0+16] fild ds:dword ptr[_r_p1+16] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmul st(0),st(5) fxch st(2) fld st(0) fmul ds:dword ptr[p01_minus_p21] fxch st(2) fmul ds:dword ptr[p10_minus_p20] fxch st(1) fmul st(0),st(5) fxch st(2) fsubrp st(3),st(0) fsubp st(1),st(0) fld st(2) fmul ds:dword ptr[float_minus_1] fxch st(2) fmul st(0),st(3) fxch st(1) fmul st(0),st(2) fldcw ds:word ptr[ceil_cw] fistp ds:dword ptr[_r_lstepy] fistp ds:dword ptr[_r_lstepx] fldcw ds:word ptr[single_cw] fild ds:dword ptr[_r_p2+8] fild ds:dword ptr[_r_p0+8] fild ds:dword ptr[_r_p1+8] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmul st(0),st(6) fxch st(2) fld st(0) fmul ds:dword ptr[p01_minus_p21] fxch st(2) fmul ds:dword ptr[p10_minus_p20] fxch st(1) fmul st(0),st(6) fxch st(2) fsubrp st(3),st(0) fsubp st(1),st(0) fmul st(0),st(2) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[_r_sstepy] fistp ds:dword ptr[_r_sstepx] fild ds:dword ptr[_r_p2+12] fild ds:dword ptr[_r_p0+12] fild ds:dword ptr[_r_p1+12] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmul st(0),st(6) fxch st(2) fld st(0) fmul ds:dword ptr[p01_minus_p21] fxch st(2) fmul ds:dword ptr[p10_minus_p20] fxch st(1) fmul st(0),st(6) fxch st(2) fsubrp st(3),st(0) fsubp st(1),st(0) fmul st(0),st(2) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[_r_tstepy] fistp ds:dword ptr[_r_tstepx] fild ds:dword ptr[_r_p2+20] fild ds:dword ptr[_r_p0+20] fild ds:dword ptr[_r_p1+20] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmulp st(6),st(0) fxch st(1) fld st(0) fmul ds:dword ptr[p01_minus_p21] fxch st(2) fmul ds:dword ptr[p10_minus_p20] fxch st(1) fmulp st(5),st(0) fxch st(5) fsubp st(1),st(0) fxch st(3) fsubrp st(4),st(0) fxch st(1) fmulp st(2),st(0) fmulp st(2),st(0) fistp ds:dword ptr[_r_zistepx] fistp ds:dword ptr[_r_zistepy] mov eax,ds:dword ptr[_r_sstepx] mov edx,ds:dword ptr[_r_tstepx] shl eax,16 shl edx,16 mov ds:dword ptr[_a_sstepxfrac],eax mov ds:dword ptr[_a_tstepxfrac],edx mov ecx,ds:dword ptr[_r_sstepx] mov eax,ds:dword ptr[_r_tstepx] sar ecx,16 sar eax,16 imul ds:dword ptr[4+0+esp] add eax,ecx mov ds:dword ptr[_a_ststepxwhole],eax ret public _D_PolysetRecursiveTriangleT2 _D_PolysetRecursiveTriangleT2: push ebp push esi push edi push ebx mov esi,ds:dword ptr[8+16+esp] mov ebx,ds:dword ptr[4+16+esp] mov edi,ds:dword ptr[12+16+esp] mov eax,ds:dword ptr[0+esi] mov edx,ds:dword ptr[0+ebx] mov ebp,ds:dword ptr[4+esi] sub eax,edx mov ecx,ds:dword ptr[4+ebx] sub ebp,ecx inc eax cmp eax,2 ja LSplit mov eax,ds:dword ptr[0+edi] inc ebp cmp ebp,2 ja LSplit mov edx,ds:dword ptr[0+esi] mov ebp,ds:dword ptr[4+edi] sub eax,edx mov ecx,ds:dword ptr[4+esi] sub ebp,ecx inc eax cmp eax,2 ja LSplit2 mov eax,ds:dword ptr[0+ebx] inc ebp cmp ebp,2 ja LSplit2 mov edx,ds:dword ptr[0+edi] mov ebp,ds:dword ptr[4+ebx] sub eax,edx mov ecx,ds:dword ptr[4+edi] sub ebp,ecx inc eax inc ebp mov edx,ebx cmp eax,2 ja LSplit3 cmp ebp,2 jna LDone LSplit3: mov ebx,edi mov edi,esi mov esi,edx jmp LSplit LSplit2: mov eax,ebx mov ebx,esi mov esi,edi mov edi,eax LSplit: sub esp,24 mov eax,ds:dword ptr[8+ebx] mov edx,ds:dword ptr[8+esi] mov ecx,ds:dword ptr[12+ebx] add eax,edx mov edx,ds:dword ptr[12+esi] sar eax,1 add ecx,edx mov ds:dword ptr[8+esp],eax mov eax,ds:dword ptr[20+ebx] sar ecx,1 mov edx,ds:dword ptr[20+esi] mov ds:dword ptr[12+esp],ecx add eax,edx mov ecx,ds:dword ptr[0+ebx] mov edx,ds:dword ptr[0+esi] sar eax,1 add edx,ecx mov ds:dword ptr[20+esp],eax mov eax,ds:dword ptr[4+ebx] sar edx,1 mov ebp,ds:dword ptr[4+esi] mov ds:dword ptr[0+esp],edx add ebp,eax sar ebp,1 mov ds:dword ptr[4+esp],ebp cmp ds:dword ptr[4+esi],eax jg LNoDraw mov edx,ds:dword ptr[0+esi] jnz LDraw cmp edx,ecx jl LNoDraw LDraw: mov edx,ds:dword ptr[20+esp] mov ecx,ds:dword ptr[4+esp] sar edx,16 mov ebp,ds:dword ptr[0+esp] mov eax,ds:dword ptr[_zspantable+ecx*4] cmp dx,ds:word ptr[eax+ebp*2] jnge LNoDraw mov ds:word ptr[eax+ebp*2],dx mov eax,ds:dword ptr[12+esp] sar eax,16 mov edx,ds:dword ptr[8+esp] sar edx,16 sub ecx,ecx mov eax,ds:dword ptr[_skintable+eax*4] mov ebp,ds:dword ptr[4+esp] mov cl,ds:byte ptr[eax+edx] ; texture pixel or cl,cl jz Skip1B ; color 0 = no draw mov edx,ds:dword ptr[_d_pcolormap] mov dh,ds:byte ptr[edx+ecx] mov eax,ds:dword ptr[0+esp] add eax,ds:dword ptr[_d_scantable+ebp*4] add eax,ds:dword ptr[_d_viewbuffer] bt cx,0 jnc Skip1 ; trans stuff mov dl,ds:byte ptr[eax] and edx, 0ffffh mov dh,ds:byte ptr[12345678h + edx] TranPatch1: Skip1: mov ds:byte ptr[eax],dh ; rjr distance ; mov ds:byte ptr[eax],0 Skip1B: LNoDraw: push esp push ebx push edi call near ptr _D_PolysetRecursiveTriangleT2 mov ebx,esp push esi push ebx push edi call near ptr _D_PolysetRecursiveTriangleT2 add esp,24 LDone: pop ebx pop edi pop esi pop ebp ret 12 public _D_PolysetDrawSpans8T2 _D_PolysetDrawSpans8T2: push esi push ebx mov esi,ds:dword ptr[4+8+esp] mov ecx,ds:dword ptr[_r_zistepx] push ebp push edi ror ecx,16 mov edx,ds:dword ptr[8+esi] mov ds:dword ptr[lzistepx],ecx LSpanLoop: mov eax,ds:dword ptr[_d_aspancount] sub eax,edx mov edx,ds:dword ptr[_erroradjustup] mov ebx,ds:dword ptr[_errorterm] add ebx,edx js LNoTurnover mov edx,ds:dword ptr[_erroradjustdown] mov edi,ds:dword ptr[_d_countextrastep] sub ebx,edx mov ebp,ds:dword ptr[_d_aspancount] mov ds:dword ptr[_errorterm],ebx add ebp,edi mov ds:dword ptr[_d_aspancount],ebp jmp LRightEdgeStepped LNoTurnover: mov edi,ds:dword ptr[_d_aspancount] mov edx,ds:dword ptr[_ubasestep] mov ds:dword ptr[_errorterm],ebx add edi,edx mov ds:dword ptr[_d_aspancount],edi LRightEdgeStepped: cmp eax,1 jl LNextSpan jz LExactlyOneLong mov ecx,ds:dword ptr[_a_ststepxwhole] mov edx,ds:dword ptr[_r_affinetridesc+8] mov ds:dword ptr[advancetable+4],ecx add ecx,edx mov ds:dword ptr[advancetable],ecx mov ecx,ds:dword ptr[_a_tstepxfrac] mov cx,ds:word ptr[_r_lstepx] mov edx,eax mov ds:dword ptr[tstep],ecx add edx,7 shr edx,3 mov ebx,ds:dword ptr[16+esi] mov bx,dx mov ecx,ds:dword ptr[4+esi] neg eax mov edi,ds:dword ptr[0+esi] and eax,7 sub edi,eax sub ecx,eax sub ecx,eax mov edx,ds:dword ptr[20+esi] mov dx,ds:word ptr[24+esi] mov ebp,ds:dword ptr[28+esi] ror ebp,16 push esi mov esi,ds:dword ptr[12+esi] jmp dword ptr[aff8entryvec_table+eax*4] LDrawLoop: LDraw8: cmp bp,ds:word ptr[ecx] jl Lp1 xor eax,eax mov ah,dh ; light mov al,ds:byte ptr[esi] ; texture pixel or al,al jz SkipA2 ; color 0 = no draw mov ds:word ptr[ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch8: bt ax,0 jnc SkipA ; trans stuff mov al,ds:byte ptr[edi] mov ah,ds:byte ptr[12345678h + eax] TranPatch2: SkipA: mov ds:byte ptr[edi],ah ; rj ;mov ds:byte ptr[edi],0 SkipA2: Lp1: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw7: cmp bp,ds:word ptr[2+ecx] jl Lp2 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] ; texture pixel or al,al jz SkipB2 ; color 0 = no draw mov ds:word ptr[2+ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch7: bt ax,0 jnc SkipB ; trans stuff mov al,ds:byte ptr[edi+1] mov ah,ds:byte ptr[12345678h + eax] TranPatch3: SkipB: mov ds:byte ptr[1+edi],ah ; rj ;mov ds:byte ptr[1+edi],0 SkipB2: Lp2: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw6: cmp bp,ds:word ptr[4+ecx] jl Lp3 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] ; texture pixel or al,al jz SkipC2 ; color 0 = no draw mov ds:word ptr[4+ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch6: bt ax,0 jnc SkipC ; trans stuff mov al,ds:byte ptr[edi+2] mov ah,ds:byte ptr[12345678h + eax] TranPatch4: SkipC: mov ds:byte ptr[2+edi],ah ; rj ;mov ds:byte ptr[2+edi],0 SkipC2: Lp3: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw5: cmp bp,ds:word ptr[6+ecx] jl Lp4 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] ; texture pixel or al,al jz SkipD2 ; color 0 = no draw mov ds:word ptr[6+ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch5: bt ax,0 jnc SkipD ; trans stuff mov al,ds:byte ptr[edi+3] mov ah,ds:byte ptr[12345678h + eax] TranPatch5: SkipD: mov ds:byte ptr[3+edi],ah ; rj ;mov ds:byte ptr[3+edi],0 SkipD2: Lp4: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw4: cmp bp,ds:word ptr[8+ecx] jl Lp5 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] ; texture pixel or al,al jz SkipE2 ; color 0 = no draw mov ds:word ptr[8+ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch4: bt ax,0 jnc SkipE ; trans stuff mov al,ds:byte ptr[edi+4] mov ah,ds:byte ptr[12345678h + eax] TranPatch6: SkipE: mov ds:byte ptr[4+edi],ah ; rj ;mov ds:byte ptr[4+edi],0 SkipE2: Lp5: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw3: cmp bp,ds:word ptr[10+ecx] jl Lp6 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] ; texture pixel or al,al jz SkipF2 ; color 0 = no draw mov ds:word ptr[10+ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch3: bt ax,0 jnc SkipF ; trans stuff mov al,ds:byte ptr[edi+5] mov ah,ds:byte ptr[12345678h + eax] TranPatch7: SkipF: mov ds:byte ptr[5+edi],ah ; rj ;mov ds:byte ptr[5+edi],0 SkipF2: Lp6: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw2: cmp bp,ds:word ptr[12+ecx] jl Lp7 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] ; texture pixel or al,al jz SkipG2 ; color 0 = no draw mov ds:word ptr[12+ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch2: bt ax,0 jnc SkipG ; trans stuff mov al,ds:byte ptr[edi+6] mov ah,ds:byte ptr[12345678h + eax] TranPatch8: SkipG: mov ds:byte ptr[6+edi],ah ; rj ;mov ds:byte ptr[6+edi],0 SkipG2: Lp7: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw1: cmp bp,ds:word ptr[14+ecx] jl Lp8 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] ; texture pixel or al,al jz SkipH2 ; color 0 = no draw mov ds:word ptr[14+ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch1: bt ax,0 jnc SkipH ; trans stuff mov al,ds:byte ptr[edi+7] mov ah,ds:byte ptr[12345678h + eax] TranPatch9: SkipH: mov ds:byte ptr[7+edi],ah ; rj ;mov ds:byte ptr[7+edi],0 SkipH2: Lp8: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] add edi,8 add ecx,16 dec bx jnz LDrawLoop pop esi LNextSpan: add esi,32 LNextSpanESISet: mov edx,ds:dword ptr[8+esi] cmp edx,offset -999999 jnz LSpanLoop pop edi pop ebp pop ebx pop esi ret LExactlyOneLong: mov ecx,ds:dword ptr[4+esi] mov ebp,ds:dword ptr[28+esi] ror ebp,16 mov ebx,ds:dword ptr[12+esi] cmp bp,ds:word ptr[ecx] jl LNextSpan xor eax,eax mov edi,ds:dword ptr[0+esi] mov ah,ds:byte ptr[24+1+esi] add esi,32 mov al,ds:byte ptr[ebx] ; texture pixel or al,al jz SkipI2 ; color 0 = no draw mov ds:word ptr[ecx],bp mov ah,ds:byte ptr[12345678h+eax] LPatch9: bt ax,0 jnc SkipI ; trans stuff mov al,ds:byte ptr[edi] mov ah,ds:byte ptr[12345678h + eax] TranPatch10: SkipI: mov ds:byte ptr[edi],ah ; rjr ;mov ds:byte ptr[edi],0 SkipI2: jmp LNextSpanESISet public _D_Aff8PatchT2 _D_Aff8PatchT2: mov eax,ds:dword ptr[4+esp] mov ds:dword ptr[LPatch1-4],eax mov ds:dword ptr[LPatch2-4],eax mov ds:dword ptr[LPatch3-4],eax mov ds:dword ptr[LPatch4-4],eax mov ds:dword ptr[LPatch5-4],eax mov ds:dword ptr[LPatch6-4],eax mov ds:dword ptr[LPatch7-4],eax mov ds:dword ptr[LPatch8-4],eax mov ds:dword ptr[LPatch9-4],eax ret public _D_PolysetDrawT2 _D_PolysetDrawT2: sub esp,offset (((1024+1 + 1 + ((32 - 1) / 32)) + 1) * 32) mov eax,esp add eax,32 - 1 and eax,offset not (32 - 1) mov ds:dword ptr[_a_spans],eax mov eax,ds:dword ptr[_r_affinetridesc+28] test eax,eax jz _D_DrawNonSubdivT2 ; push ebp ; mov ebp,ds:dword ptr[_r_affinetridesc+24] push esi ; shl ebp,4 push ebx mov ebx,ds:dword ptr[_r_affinetridesc+16] push edi mov edi,ds:dword ptr[_r_affinetridesc+20] Llooptop: xor ecx,ecx xor esi,esi xor edx,edx mov cx,word ptr[4+0+ebx] mov si,word ptr[4+2+ebx] mov dx,word ptr[4+4+ebx] shl ecx,5 shl esi,5 add ecx,edi shl edx,5 add esi,edi add edx,edi fild ds:dword ptr[0+4+ecx] fild ds:dword ptr[0+4+esi] fild ds:dword ptr[0+0+ecx] fild ds:dword ptr[0+0+edx] fxch st(2) fsubr st(0),st(3) fild ds:dword ptr[0+0+esi] fxch st(2) fsubr st(3),st(0) fild ds:dword ptr[0+4+edx] fxch st(1) fsubrp st(3),st(0) fxch st(1) fmulp st(3),st(0) fsubp st(3),st(0) mov eax,ds:dword ptr[0+16+ecx] and eax,0FF00h fmulp st(2),st(0) add eax,ds:dword ptr[_acolormap] fsubrp st(1),st(0) mov ds:dword ptr[_d_pcolormap],eax fstp ds:dword ptr[Ltemp] mov eax,ds:dword ptr[Ltemp] sub eax,080000001h jc Lskip mov eax,ds:dword ptr[0+ebx] test eax,eax jz Lfacesback push edx push esi push ecx call near ptr _D_PolysetRecursiveTriangleT2 ; sub ebp,16 ; jnz Llooptop jmp Ldone2 Lfacesback: mov eax,ds:dword ptr[0+8+ecx] push eax mov eax,ds:dword ptr[0+8+esi] push eax mov eax,ds:dword ptr[0+8+edx] push eax push ecx push edx mov eax,ds:dword ptr[_r_affinetridesc+32] test ds:dword ptr[24+ecx],00020h jz Lp11 add ds:dword ptr[0+8+ecx],eax Lp11: test ds:dword ptr[24+esi],00020h jz Lp12 add ds:dword ptr[0+8+esi],eax Lp12: test ds:dword ptr[24+edx],00020h jz Lp13 add ds:dword ptr[0+8+edx],eax Lp13: push edx push esi push ecx call near ptr _D_PolysetRecursiveTriangleT2 pop edx pop ecx pop eax mov ds:dword ptr[0+8+edx],eax pop eax mov ds:dword ptr[0+8+esi],eax pop eax mov ds:dword ptr[0+8+ecx],eax Lskip: ; sub ebp,16 ; jnz Llooptop Ldone2: pop edi pop ebx pop esi ; pop ebp add esp,offset (((1024+1 + 1 + ((32 - 1) / 32)) + 1) * 32) ret public _D_PolysetScanLeftEdgeT2 _D_PolysetScanLeftEdgeT2: push ebp push esi push edi push ebx mov eax,ds:dword ptr[4+16+esp] mov ecx,ds:dword ptr[_d_sfrac] and eax,0FFFFh mov ebx,ds:dword ptr[_d_ptex] or ecx,eax mov esi,ds:dword ptr[_d_pedgespanpackage] mov edx,ds:dword ptr[_d_tfrac] mov edi,ds:dword ptr[_d_light] mov ebp,ds:dword ptr[_d_zi] LScanLoop: mov ds:dword ptr[12+esi],ebx mov eax,ds:dword ptr[_d_pdest] mov ds:dword ptr[0+esi],eax mov eax,ds:dword ptr[_d_pz] mov ds:dword ptr[4+esi],eax mov eax,ds:dword ptr[_d_aspancount] mov ds:dword ptr[8+esi],eax mov ds:dword ptr[24+esi],edi mov ds:dword ptr[28+esi],ebp mov ds:dword ptr[16+esi],ecx mov ds:dword ptr[20+esi],edx mov al,ds:byte ptr[32+esi] add esi,32 mov eax,ds:dword ptr[_erroradjustup] mov ds:dword ptr[_d_pedgespanpackage],esi mov esi,ds:dword ptr[_errorterm] add esi,eax mov eax,ds:dword ptr[_d_pdest] js LNoLeftEdgeTurnover sub esi,ds:dword ptr[_erroradjustdown] add eax,ds:dword ptr[_d_pdestextrastep] mov ds:dword ptr[_errorterm],esi mov ds:dword ptr[_d_pdest],eax mov eax,ds:dword ptr[_d_pz] mov esi,ds:dword ptr[_d_aspancount] add eax,ds:dword ptr[_d_pzextrastep] add ecx,ds:dword ptr[_d_sfracextrastep] adc ebx,ds:dword ptr[_d_ptexextrastep] add esi,ds:dword ptr[_d_countextrastep] mov ds:dword ptr[_d_pz],eax mov eax,ds:dword ptr[_d_tfracextrastep] mov ds:dword ptr[_d_aspancount],esi add edx,eax jnc LSkip1 add ebx,ds:dword ptr[_r_affinetridesc+8] LSkip1: add edi,ds:dword ptr[_d_lightextrastep] add ebp,ds:dword ptr[_d_ziextrastep] mov esi,ds:dword ptr[_d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz LScanLoop pop ebx pop edi pop esi pop ebp ret LNoLeftEdgeTurnover: mov ds:dword ptr[_errorterm],esi add eax,ds:dword ptr[_d_pdestbasestep] mov ds:dword ptr[_d_pdest],eax mov eax,ds:dword ptr[_d_pz] mov esi,ds:dword ptr[_d_aspancount] add eax,ds:dword ptr[_d_pzbasestep] add ecx,ds:dword ptr[_d_sfracbasestep] adc ebx,ds:dword ptr[_d_ptexbasestep] add esi,ds:dword ptr[_ubasestep] mov ds:dword ptr[_d_pz],eax mov ds:dword ptr[_d_aspancount],esi mov esi,ds:dword ptr[_d_tfracbasestep] add edx,esi jnc LSkip2 add ebx,ds:dword ptr[_r_affinetridesc+8] LSkip2: add edi,ds:dword ptr[_d_lightbasestep] add ebp,ds:dword ptr[_d_zibasestep] mov esi,ds:dword ptr[_d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz LScanLoop pop ebx pop edi pop esi pop ebp ret _L_PDFVertT2: push esi push edi mov eax,ds:dword ptr[0+0+ebx] mov edx,ds:dword ptr[_r_refdef+40] cmp eax,edx jge LNextVert mov esi,ds:dword ptr[0+4+ebx] mov edx,ds:dword ptr[_r_refdef+44] cmp esi,edx jge LNextVert mov edi,ds:dword ptr[_zspantable+esi*4] mov edx,ds:dword ptr[0+20+ebx] shr edx,16 cmp dx,ds:word ptr[edi+eax*2] jl LNextVert mov ds:word ptr[edi+eax*2],dx mov edi,ds:dword ptr[0+12+ebx] shr edi,16 mov edi,ds:dword ptr[_skintable+edi*4] mov edx,ds:dword ptr[0+8+ebx] shr edx,16 mov dl,ds:byte ptr[edi+edx] ; texture pixel or dl,dl jz Skip2B ; color 0 = no draw mov edi,ds:dword ptr[0+16+ebx] and edi,0FF00h and edx,000FFh add edi,edx add edi,ds:dword ptr[_acolormap] mov dh,ds:byte ptr[edi] mov edi,ds:dword ptr[_d_scantable+esi*4] mov esi,ds:dword ptr[_d_viewbuffer] add edi,eax bt dx,0 jnc Skip2 ; trans stuff mov dl,ds:byte ptr[esi+edi] and edx, 0ffffh mov dh,ds:byte ptr[12345678h + edx] TranPatch11: Skip2: mov ds:byte ptr[esi+edi],dh ; rjr distance ;mov ds:byte ptr[esi+edi],0 Skip2B: LNextVert: pop edi pop esi ret public _D_PolysetDrawFinalVertsT2 _D_PolysetDrawFinalVertsT2: push ebp push ebx mov ebx,dword ptr[4+8+esp] ;pv1 call _L_PDFVertT2 mov ebx,dword ptr[8+8+esp] ;pv2 call _L_PDFVertT2 mov ebx,dword ptr[12+8+esp];pv3 call _L_PDFVertT2 pop ebx pop ebp ret public _D_DrawNonSubdivT2 _D_DrawNonSubdivT2: ; push ebp ; mov ebp,ds:dword ptr[_r_affinetridesc+24] push ebx ; shl ebp,4 push esi mov esi,ds:dword ptr[_r_affinetridesc+16] push edi LNDLoop: mov edi,ds:dword ptr[_r_affinetridesc+20] xor ecx,ecx; //clear i1 xor edx,edx; //clear i2 xor ebx,ebx; //clear i3 mov cx, word ptr[4+0+esi] ;ptri->vertindex[0] mov dx, word ptr[4+2+esi] ;ptri->vertindex[1] mov bx, word ptr[4+4+esi] ;ptri->vertindex[2] shl ecx,5 shl edx,5 shl ebx,5 add ecx,edi add edx,edi add ebx,edi mov eax,ds:dword ptr[0+4+ecx] mov esi,ds:dword ptr[0+0+ecx] sub eax,ds:dword ptr[0+4+edx] sub esi,ds:dword ptr[0+0+ebx] imul eax,esi mov esi,ds:dword ptr[0+0+ecx] mov edi,ds:dword ptr[0+4+ecx] sub esi,ds:dword ptr[0+0+edx] sub edi,ds:dword ptr[0+4+ebx] imul edi,esi sub eax,edi jns LNextTri mov ds:dword ptr[_d_xdenom],eax fild ds:dword ptr[_d_xdenom] mov eax,ds:dword ptr[0+0+ecx] mov esi,ds:dword ptr[0+4+ecx] mov ds:dword ptr[_r_p0+0],eax mov ds:dword ptr[_r_p0+4],esi mov eax,ds:dword ptr[0+8+ecx] mov esi,ds:dword ptr[0+12+ecx] mov ds:dword ptr[_r_p0+8],eax mov ds:dword ptr[_r_p0+12],esi mov eax,ds:dword ptr[0+16+ecx] mov esi,ds:dword ptr[0+20+ecx] mov ds:dword ptr[_r_p0+16],eax mov ds:dword ptr[_r_p0+20],esi fdivr ds:dword ptr[float_1] mov eax,ds:dword ptr[0+0+edx] mov esi,ds:dword ptr[0+4+edx] mov ds:dword ptr[_r_p1+0],eax mov ds:dword ptr[_r_p1+4],esi mov eax,ds:dword ptr[0+8+edx] mov esi,ds:dword ptr[0+12+edx] mov ds:dword ptr[_r_p1+8],eax mov ds:dword ptr[_r_p1+12],esi mov eax,ds:dword ptr[0+16+edx] mov esi,ds:dword ptr[0+20+edx] mov ds:dword ptr[_r_p1+16],eax mov ds:dword ptr[_r_p1+20],esi mov eax,ds:dword ptr[0+0+ebx] mov esi,ds:dword ptr[0+4+ebx] mov ds:dword ptr[_r_p2+0],eax mov ds:dword ptr[_r_p2+4],esi mov eax,ds:dword ptr[0+8+ebx] mov esi,ds:dword ptr[0+12+ebx] mov ds:dword ptr[_r_p2+8],eax mov ds:dword ptr[_r_p2+12],esi mov eax,ds:dword ptr[0+16+ebx] mov esi,ds:dword ptr[0+20+ebx] mov ds:dword ptr[_r_p2+16],eax mov edi,ds:dword ptr[_r_affinetridesc+16] mov ds:dword ptr[_r_p2+20],esi mov eax,ds:dword ptr[0+edi] test eax,eax jnz LFacesFront mov eax,ds:dword ptr[24+ecx] mov esi,ds:dword ptr[24+edx] mov edi,ds:dword ptr[24+ebx] test eax,00020h mov eax,ds:dword ptr[_r_affinetridesc+32] jz LOnseamDone0 add ds:dword ptr[_r_p0+8],eax LOnseamDone0: test esi,00020h jz LOnseamDone1 add ds:dword ptr[_r_p1+8],eax LOnseamDone1: test edi,00020h jz LOnseamDone2 add ds:dword ptr[_r_p2+8],eax LOnseamDone2: LFacesFront: fstp ds:dword ptr[_d_xdenom] call near ptr _D_PolysetSetEdgeTable call near ptr _D_RasterizeAliasPolySmooth LNextTri: mov esi,ds:dword ptr[_r_affinetridesc+16] ; sub ebp,16 ; jnz LNDLoop pop edi pop esi pop ebx ; pop ebp add esp,offset (((1024+1 + 1 + ((32 - 1) / 32)) + 1) * 32) ret public _D_PolysetAff8EndT2 _D_PolysetAff8EndT2: _TEXT ENDS _DATA SEGMENT align 4 LPatchTable: dd TranPatch1-4 dd TranPatch2-4 dd TranPatch3-4 dd TranPatch4-4 dd TranPatch5-4 dd TranPatch6-4 dd TranPatch7-4 dd TranPatch8-4 dd TranPatch9-4 dd TranPatch10-4 dd TranPatch11-4 _DATA ENDS _TEXT SEGMENT align 4 public _R_TranPatch2 _R_TranPatch2: push ebx mov eax,ds:dword ptr[_mainTransTable] mov ebx,offset LPatchTable mov ecx,11 LPatchLoop: mov edx,ds:dword ptr[ebx] add ebx,4 mov ds:dword ptr[edx],eax dec ecx jnz LPatchLoop pop ebx ret _TEXT ENDS END engine/h2shared/masm/d_polysa4.asm000066400000000000000000000570711444734033100173750ustar00rootroot00000000000000; ; d_polysa4.asm -- for MASM ; x86 assembly-language polygon model drawing code ; with translucency handling, #3. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _d_viewbuffer:dword externdef _d_scantable:dword externdef _r_refdef:dword externdef _a_sstepxfrac:dword externdef _r_affinetridesc:dword externdef _acolormap:dword externdef _d_pcolormap:dword externdef _d_sfrac:dword externdef _d_ptex:dword externdef _d_pedgespanpackage:dword externdef _d_tfrac:dword externdef _d_light:dword externdef _d_zi:dword externdef _d_pdest:dword externdef _d_pz:dword externdef _d_aspancount:dword externdef _erroradjustup:dword externdef _erroradjustdown:dword externdef _errorterm:dword externdef _d_xdenom:dword externdef _r_p0:dword externdef _r_p1:dword externdef _r_p2:dword externdef _a_tstepxfrac:dword externdef _a_ststepxwhole:dword externdef _zspantable:dword externdef _skintable:dword externdef _d_countextrastep:dword externdef _ubasestep:dword externdef _a_spans:dword externdef _d_pdestextrastep:dword externdef _d_pzextrastep:dword externdef _d_sfracextrastep:dword externdef _d_ptexextrastep:dword externdef _d_tfracextrastep:dword externdef _d_lightextrastep:dword externdef _d_ziextrastep:dword externdef _d_pdestbasestep:dword externdef _d_pzbasestep:dword externdef _d_sfracbasestep:dword externdef _d_ptexbasestep:dword externdef _d_tfracbasestep:dword externdef _d_lightbasestep:dword externdef _d_zibasestep:dword externdef _r_lstepx:dword externdef _r_lstepy:dword externdef _r_sstepx:dword externdef _r_sstepy:dword externdef _r_tstepx:dword externdef _r_tstepy:dword externdef _r_zistepx:dword externdef _r_zistepy:dword externdef _D_PolysetSetEdgeTable:dword externdef _D_RasterizeAliasPolySmooth:dword ; externs from ASM-only code externdef float_point5:dword externdef float_1:dword externdef float_minus_1:dword externdef float_0:dword externdef advancetable:dword externdef sstep:dword externdef tstep:dword externdef ceil_cw:dword externdef single_cw:dword _DATA SEGMENT align 4 p10_minus_p20 dd 0 p01_minus_p21 dd 0 temp0 dd 0 temp1 dd 0 Ltemp dd 0 aff8entryvec_table dd LDraw8, LDraw7, LDraw6, LDraw5 dd LDraw4, LDraw3, LDraw2, LDraw1 lzistepx dd 0 _DATA ENDS _TEXT SEGMENT externdef _D_PolysetSetEdgeTable:dword externdef _D_RasterizeAliasPolySmooth:dword public _D_PolysetCalcGradientsT3 _D_PolysetCalcGradientsT3: fild ds:dword ptr[_r_p0+0] fild ds:dword ptr[_r_p2+0] fild ds:dword ptr[_r_p0+4] fild ds:dword ptr[_r_p2+4] fild ds:dword ptr[_r_p1+0] fild ds:dword ptr[_r_p1+4] fxch st(3) fsub st(0),st(2) fxch st(1) fsub st(0),st(4) fxch st(5) fsubrp st(4),st(0) fxch st(2) fsubrp st(1),st(0) fxch st(1) fld ds:dword ptr[_d_xdenom] fxch st(4) fstp ds:dword ptr[p10_minus_p20] fstp ds:dword ptr[p01_minus_p21] fxch st(2) fild ds:dword ptr[_r_p2+16] fild ds:dword ptr[_r_p0+16] fild ds:dword ptr[_r_p1+16] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmul st(0),st(5) fxch st(2) fld st(0) fmul ds:dword ptr[p01_minus_p21] fxch st(2) fmul ds:dword ptr[p10_minus_p20] fxch st(1) fmul st(0),st(5) fxch st(2) fsubrp st(3),st(0) fsubp st(1),st(0) fld st(2) fmul ds:dword ptr[float_minus_1] fxch st(2) fmul st(0),st(3) fxch st(1) fmul st(0),st(2) fldcw ds:word ptr[ceil_cw] fistp ds:dword ptr[_r_lstepy] fistp ds:dword ptr[_r_lstepx] fldcw ds:word ptr[single_cw] fild ds:dword ptr[_r_p2+8] fild ds:dword ptr[_r_p0+8] fild ds:dword ptr[_r_p1+8] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmul st(0),st(6) fxch st(2) fld st(0) fmul ds:dword ptr[p01_minus_p21] fxch st(2) fmul ds:dword ptr[p10_minus_p20] fxch st(1) fmul st(0),st(6) fxch st(2) fsubrp st(3),st(0) fsubp st(1),st(0) fmul st(0),st(2) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[_r_sstepy] fistp ds:dword ptr[_r_sstepx] fild ds:dword ptr[_r_p2+12] fild ds:dword ptr[_r_p0+12] fild ds:dword ptr[_r_p1+12] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmul st(0),st(6) fxch st(2) fld st(0) fmul ds:dword ptr[p01_minus_p21] fxch st(2) fmul ds:dword ptr[p10_minus_p20] fxch st(1) fmul st(0),st(6) fxch st(2) fsubrp st(3),st(0) fsubp st(1),st(0) fmul st(0),st(2) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[_r_tstepy] fistp ds:dword ptr[_r_tstepx] fild ds:dword ptr[_r_p2+20] fild ds:dword ptr[_r_p0+20] fild ds:dword ptr[_r_p1+20] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmulp st(6),st(0) fxch st(1) fld st(0) fmul ds:dword ptr[p01_minus_p21] fxch st(2) fmul ds:dword ptr[p10_minus_p20] fxch st(1) fmulp st(5),st(0) fxch st(5) fsubp st(1),st(0) fxch st(3) fsubrp st(4),st(0) fxch st(1) fmulp st(2),st(0) fmulp st(2),st(0) fistp ds:dword ptr[_r_zistepx] fistp ds:dword ptr[_r_zistepy] mov eax,ds:dword ptr[_r_sstepx] mov edx,ds:dword ptr[_r_tstepx] shl eax,16 shl edx,16 mov ds:dword ptr[_a_sstepxfrac],eax mov ds:dword ptr[_a_tstepxfrac],edx mov ecx,ds:dword ptr[_r_sstepx] mov eax,ds:dword ptr[_r_tstepx] sar ecx,16 sar eax,16 imul ds:dword ptr[4+0+esp] add eax,ecx mov ds:dword ptr[_a_ststepxwhole],eax ret public _D_PolysetRecursiveTriangleT3 _D_PolysetRecursiveTriangleT3: push ebp push esi push edi push ebx mov esi,ds:dword ptr[8+16+esp] mov ebx,ds:dword ptr[4+16+esp] mov edi,ds:dword ptr[12+16+esp] mov eax,ds:dword ptr[0+esi] mov edx,ds:dword ptr[0+ebx] mov ebp,ds:dword ptr[4+esi] sub eax,edx mov ecx,ds:dword ptr[4+ebx] sub ebp,ecx inc eax cmp eax,2 ja LSplit mov eax,ds:dword ptr[0+edi] inc ebp cmp ebp,2 ja LSplit mov edx,ds:dword ptr[0+esi] mov ebp,ds:dword ptr[4+edi] sub eax,edx mov ecx,ds:dword ptr[4+esi] sub ebp,ecx inc eax cmp eax,2 ja LSplit2 mov eax,ds:dword ptr[0+ebx] inc ebp cmp ebp,2 ja LSplit2 mov edx,ds:dword ptr[0+edi] mov ebp,ds:dword ptr[4+ebx] sub eax,edx mov ecx,ds:dword ptr[4+edi] sub ebp,ecx inc eax inc ebp mov edx,ebx cmp eax,2 ja LSplit3 cmp ebp,2 jna LDone LSplit3: mov ebx,edi mov edi,esi mov esi,edx jmp LSplit LSplit2: mov eax,ebx mov ebx,esi mov esi,edi mov edi,eax LSplit: sub esp,24 mov eax,ds:dword ptr[8+ebx] mov edx,ds:dword ptr[8+esi] mov ecx,ds:dword ptr[12+ebx] add eax,edx mov edx,ds:dword ptr[12+esi] sar eax,1 add ecx,edx mov ds:dword ptr[8+esp],eax mov eax,ds:dword ptr[20+ebx] sar ecx,1 mov edx,ds:dword ptr[20+esi] mov ds:dword ptr[12+esp],ecx add eax,edx mov ecx,ds:dword ptr[0+ebx] mov edx,ds:dword ptr[0+esi] sar eax,1 add edx,ecx mov ds:dword ptr[20+esp],eax mov eax,ds:dword ptr[4+ebx] sar edx,1 mov ebp,ds:dword ptr[4+esi] mov ds:dword ptr[0+esp],edx add ebp,eax sar ebp,1 mov ds:dword ptr[4+esp],ebp cmp ds:dword ptr[4+esi],eax jg LNoDraw mov edx,ds:dword ptr[0+esi] jnz LDraw cmp edx,ecx jl LNoDraw LDraw: mov edx,ds:dword ptr[20+esp] mov ecx,ds:dword ptr[4+esp] sar edx,16 mov ebp,ds:dword ptr[0+esp] mov eax,ds:dword ptr[_zspantable+ecx*4] cmp dx,ds:word ptr[eax+ebp*2] jnge LNoDraw mov ds:word ptr[eax+ebp*2],dx mov eax,ds:dword ptr[12+esp] sar eax,16 mov edx,ds:dword ptr[8+esp] sar edx,16 sub ecx,ecx mov eax,ds:dword ptr[_skintable+eax*4] mov ebp,ds:dword ptr[4+esp] mov cl,ds:byte ptr[eax+edx] or cl,cl jz Skip1B ; color 0 = no draw mov edx,ds:dword ptr[_d_pcolormap] mov dl,ds:byte ptr[edx+ecx] mov ecx,ds:dword ptr[0+esp] mov eax,ds:dword ptr[_d_scantable+ebp*4] add ecx,eax mov eax,ds:dword ptr[_d_viewbuffer] mov ds:byte ptr[eax+ecx*1],dl Skip1B: LNoDraw: push esp push ebx push edi call near ptr _D_PolysetRecursiveTriangleT3 mov ebx,esp push esi push ebx push edi call near ptr _D_PolysetRecursiveTriangleT3 add esp,24 LDone: pop ebx pop edi pop esi pop ebp ret 12 public _D_PolysetAff8StartT3 _D_PolysetAff8StartT3: public _D_PolysetDrawSpans8T3 _D_PolysetDrawSpans8T3: push esi push ebx mov esi,ds:dword ptr[4+8+esp] mov ecx,ds:dword ptr[_r_zistepx] push ebp push edi ror ecx,16 mov edx,ds:dword ptr[8+esi] mov ds:dword ptr[lzistepx],ecx LSpanLoop: mov eax,ds:dword ptr[_d_aspancount] sub eax,edx mov edx,ds:dword ptr[_erroradjustup] mov ebx,ds:dword ptr[_errorterm] add ebx,edx js LNoTurnover mov edx,ds:dword ptr[_erroradjustdown] mov edi,ds:dword ptr[_d_countextrastep] sub ebx,edx mov ebp,ds:dword ptr[_d_aspancount] mov ds:dword ptr[_errorterm],ebx add ebp,edi mov ds:dword ptr[_d_aspancount],ebp jmp LRightEdgeStepped LNoTurnover: mov edi,ds:dword ptr[_d_aspancount] mov edx,ds:dword ptr[_ubasestep] mov ds:dword ptr[_errorterm],ebx add edi,edx mov ds:dword ptr[_d_aspancount],edi LRightEdgeStepped: cmp eax,1 jl LNextSpan jz LExactlyOneLong mov ecx,ds:dword ptr[_a_ststepxwhole] mov edx,ds:dword ptr[_r_affinetridesc+8] mov ds:dword ptr[advancetable+4],ecx add ecx,edx mov ds:dword ptr[advancetable],ecx mov ecx,ds:dword ptr[_a_tstepxfrac] mov cx,ds:word ptr[_r_lstepx] mov edx,eax mov ds:dword ptr[tstep],ecx add edx,7 shr edx,3 mov ebx,ds:dword ptr[16+esi] mov bx,dx mov ecx,ds:dword ptr[4+esi] neg eax mov edi,ds:dword ptr[0+esi] and eax,7 sub edi,eax sub ecx,eax sub ecx,eax mov edx,ds:dword ptr[20+esi] mov dx,ds:word ptr[24+esi] mov ebp,ds:dword ptr[28+esi] ror ebp,16 push esi mov esi,ds:dword ptr[12+esi] jmp dword ptr[aff8entryvec_table+eax*4] LDrawLoop: LDraw8: cmp bp,ds:word ptr[ecx] jl Lp1 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] or al,al jz SkipA2 ; color 0 = no draw mov ds:word ptr[ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch8: mov ds:byte ptr[edi],al SkipA2: Lp1: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw7: cmp bp,ds:word ptr[2+ecx] jl Lp2 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] or al,al jz SkipB2 ; color 0 = no draw mov ds:word ptr[2+ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch7: mov ds:byte ptr[1+edi],al SkipB2: Lp2: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw6: cmp bp,ds:word ptr[4+ecx] jl Lp3 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] or al,al jz SkipC2 ; color 0 = no draw mov ds:word ptr[4+ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch6: mov ds:byte ptr[2+edi],al SkipC2: Lp3: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw5: cmp bp,ds:word ptr[6+ecx] jl Lp4 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] or al,al jz SkipD2 ; color 0 = no draw mov ds:word ptr[6+ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch5: mov ds:byte ptr[3+edi],al SkipD2: Lp4: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw4: cmp bp,ds:word ptr[8+ecx] jl Lp5 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] or al,al jz SkipE2 ; color 0 = no draw mov ds:word ptr[8+ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch4: mov ds:byte ptr[4+edi],al SkipE2: Lp5: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw3: cmp bp,ds:word ptr[10+ecx] jl Lp6 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] or al,al jz SkipF2 ; color 0 = no draw mov ds:word ptr[10+ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch3: mov ds:byte ptr[5+edi],al SkipF2: Lp6: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw2: cmp bp,ds:word ptr[12+ecx] jl Lp7 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] or al,al jz SkipG2 ; color 0 = no draw mov ds:word ptr[12+ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch2: mov ds:byte ptr[6+edi],al SkipG2: Lp7: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] LDraw1: cmp bp,ds:word ptr[14+ecx] jl Lp8 xor eax,eax mov ah,dh mov al,ds:byte ptr[esi] or al,al jz SkipH2 ; color 0 = no draw mov ds:word ptr[14+ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch1: mov ds:byte ptr[7+edi],al SkipH2: Lp8: add edx,ds:dword ptr[tstep] sbb eax,eax add ebp,ds:dword ptr[lzistepx] adc ebp,0 add ebx,ds:dword ptr[_a_sstepxfrac] adc esi,ds:dword ptr[advancetable+4+eax*4] add edi,8 add ecx,16 dec bx jnz LDrawLoop pop esi LNextSpan: add esi,32 LNextSpanESISet: mov edx,ds:dword ptr[8+esi] cmp edx,offset -999999 jnz LSpanLoop pop edi pop ebp pop ebx pop esi ret LExactlyOneLong: mov ecx,ds:dword ptr[4+esi] mov ebp,ds:dword ptr[28+esi] ror ebp,16 mov ebx,ds:dword ptr[12+esi] cmp bp,ds:word ptr[ecx] jl LNextSpan xor eax,eax mov edi,ds:dword ptr[0+esi] mov ah,ds:byte ptr[24+1+esi] add esi,32 mov al,ds:byte ptr[ebx] or al,al jz SkipI2 ; color 0 = no draw mov ds:word ptr[ecx],bp mov al,ds:byte ptr[12345678h+eax] LPatch9: mov ds:byte ptr[edi],al SkipI2: jmp LNextSpanESISet public _D_PolysetAff8EndT3 _D_PolysetAff8EndT3: public _D_Aff8PatchT3 _D_Aff8PatchT3: mov eax,ds:dword ptr[4+esp] mov ds:dword ptr[LPatch1-4],eax mov ds:dword ptr[LPatch2-4],eax mov ds:dword ptr[LPatch3-4],eax mov ds:dword ptr[LPatch4-4],eax mov ds:dword ptr[LPatch5-4],eax mov ds:dword ptr[LPatch6-4],eax mov ds:dword ptr[LPatch7-4],eax mov ds:dword ptr[LPatch8-4],eax mov ds:dword ptr[LPatch9-4],eax ret public _D_PolysetDrawT3 _D_PolysetDrawT3: sub esp,offset (((1024+1 + 1 + ((32 - 1) / 32)) + 1) * 32) mov eax,esp add eax,32 - 1 and eax,offset not (32 - 1) mov ds:dword ptr[_a_spans],eax mov eax,ds:dword ptr[_r_affinetridesc+28] test eax,eax jz _D_DrawNonSubdivT3 ; push ebp ; mov ebp,ds:dword ptr[_r_affinetridesc+24] push esi ; shl ebp,4 push ebx mov ebx,ds:dword ptr[_r_affinetridesc+16] push edi mov edi,ds:dword ptr[_r_affinetridesc+20] Llooptop: xor ecx,ecx xor esi,esi xor edx,edx mov cx,word ptr[4+0+ebx] mov si,word ptr[4+2+ebx] mov dx,word ptr[4+4+ebx] shl ecx,5 shl esi,5 add ecx,edi shl edx,5 add esi,edi add edx,edi fild ds:dword ptr[0+4+ecx] fild ds:dword ptr[0+4+esi] fild ds:dword ptr[0+0+ecx] fild ds:dword ptr[0+0+edx] fxch st(2) fsubr st(0),st(3) fild ds:dword ptr[0+0+esi] fxch st(2) fsubr st(3),st(0) fild ds:dword ptr[0+4+edx] fxch st(1) fsubrp st(3),st(0) fxch st(1) fmulp st(3),st(0) fsubp st(3),st(0) mov eax,ds:dword ptr[0+16+ecx] and eax,0FF00h fmulp st(2),st(0) add eax,ds:dword ptr[_acolormap] fsubrp st(1),st(0) mov ds:dword ptr[_d_pcolormap],eax fstp ds:dword ptr[Ltemp] mov eax,ds:dword ptr[Ltemp] sub eax,080000001h jc Lskip mov eax,ds:dword ptr[0+ebx] test eax,eax jz Lfacesback push edx push esi push ecx call near ptr _D_PolysetRecursiveTriangleT3 ; sub ebp,16 ; jnz Llooptop jmp Ldone2 Lfacesback: mov eax,ds:dword ptr[0+8+ecx] push eax mov eax,ds:dword ptr[0+8+esi] push eax mov eax,ds:dword ptr[0+8+edx] push eax push ecx push edx mov eax,ds:dword ptr[_r_affinetridesc+32] test ds:dword ptr[24+ecx],00020h jz Lp11 add ds:dword ptr[0+8+ecx],eax Lp11: test ds:dword ptr[24+esi],00020h jz Lp12 add ds:dword ptr[0+8+esi],eax Lp12: test ds:dword ptr[24+edx],00020h jz Lp13 add ds:dword ptr[0+8+edx],eax Lp13: push edx push esi push ecx call near ptr _D_PolysetRecursiveTriangleT3 pop edx pop ecx pop eax mov ds:dword ptr[0+8+edx],eax pop eax mov ds:dword ptr[0+8+esi],eax pop eax mov ds:dword ptr[0+8+ecx],eax Lskip: ; sub ebp,16 ; jnz Llooptop Ldone2: pop edi pop ebx pop esi ; pop ebp add esp,offset (((1024+1 + 1 + ((32 - 1) / 32)) + 1) * 32) ret public _D_PolysetScanLeftEdgeT3 _D_PolysetScanLeftEdgeT3: push ebp push esi push edi push ebx mov eax,ds:dword ptr[4+16+esp] mov ecx,ds:dword ptr[_d_sfrac] and eax,0FFFFh mov ebx,ds:dword ptr[_d_ptex] or ecx,eax mov esi,ds:dword ptr[_d_pedgespanpackage] mov edx,ds:dword ptr[_d_tfrac] mov edi,ds:dword ptr[_d_light] mov ebp,ds:dword ptr[_d_zi] LScanLoop: mov ds:dword ptr[12+esi],ebx mov eax,ds:dword ptr[_d_pdest] mov ds:dword ptr[0+esi],eax mov eax,ds:dword ptr[_d_pz] mov ds:dword ptr[4+esi],eax mov eax,ds:dword ptr[_d_aspancount] mov ds:dword ptr[8+esi],eax mov ds:dword ptr[24+esi],edi mov ds:dword ptr[28+esi],ebp mov ds:dword ptr[16+esi],ecx mov ds:dword ptr[20+esi],edx mov al,ds:byte ptr[32+esi] add esi,32 mov eax,ds:dword ptr[_erroradjustup] mov ds:dword ptr[_d_pedgespanpackage],esi mov esi,ds:dword ptr[_errorterm] add esi,eax mov eax,ds:dword ptr[_d_pdest] js LNoLeftEdgeTurnover sub esi,ds:dword ptr[_erroradjustdown] add eax,ds:dword ptr[_d_pdestextrastep] mov ds:dword ptr[_errorterm],esi mov ds:dword ptr[_d_pdest],eax mov eax,ds:dword ptr[_d_pz] mov esi,ds:dword ptr[_d_aspancount] add eax,ds:dword ptr[_d_pzextrastep] add ecx,ds:dword ptr[_d_sfracextrastep] adc ebx,ds:dword ptr[_d_ptexextrastep] add esi,ds:dword ptr[_d_countextrastep] mov ds:dword ptr[_d_pz],eax mov eax,ds:dword ptr[_d_tfracextrastep] mov ds:dword ptr[_d_aspancount],esi add edx,eax jnc LSkip1 add ebx,ds:dword ptr[_r_affinetridesc+8] LSkip1: add edi,ds:dword ptr[_d_lightextrastep] add ebp,ds:dword ptr[_d_ziextrastep] mov esi,ds:dword ptr[_d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz LScanLoop pop ebx pop edi pop esi pop ebp ret LNoLeftEdgeTurnover: mov ds:dword ptr[_errorterm],esi add eax,ds:dword ptr[_d_pdestbasestep] mov ds:dword ptr[_d_pdest],eax mov eax,ds:dword ptr[_d_pz] mov esi,ds:dword ptr[_d_aspancount] add eax,ds:dword ptr[_d_pzbasestep] add ecx,ds:dword ptr[_d_sfracbasestep] adc ebx,ds:dword ptr[_d_ptexbasestep] add esi,ds:dword ptr[_ubasestep] mov ds:dword ptr[_d_pz],eax mov ds:dword ptr[_d_aspancount],esi mov esi,ds:dword ptr[_d_tfracbasestep] add edx,esi jnc LSkip2 add ebx,ds:dword ptr[_r_affinetridesc+8] LSkip2: add edi,ds:dword ptr[_d_lightbasestep] add ebp,ds:dword ptr[_d_zibasestep] mov esi,ds:dword ptr[_d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz LScanLoop pop ebx pop edi pop esi pop ebp ret _L_PDFVertT3: push esi push edi mov eax,ds:dword ptr[0+0+ebx] mov edx,ds:dword ptr[_r_refdef+40] cmp eax,edx jge LNextVert mov esi,ds:dword ptr[0+4+ebx] mov edx,ds:dword ptr[_r_refdef+44] cmp esi,edx jge LNextVert mov edi,ds:dword ptr[_zspantable+esi*4] mov edx,ds:dword ptr[0+20+ebx] shr edx,16 cmp dx,ds:word ptr[edi+eax*2] jl LNextVert mov ds:word ptr[edi+eax*2],dx mov edi,ds:dword ptr[0+12+ebx] shr edi,16 mov edi,ds:dword ptr[_skintable+edi*4] mov edx,ds:dword ptr[0+8+ebx] shr edx,16 mov dl,ds:byte ptr[edi+edx] or dl,dl jz Skip2B ; color 0 = no draw mov edi,ds:dword ptr[0+16+ebx] and edi,0FF00h and edx,000FFh add edi,edx mov edx,ds:dword ptr[_acolormap] mov dl,ds:byte ptr[edx+edi*1] mov edi,ds:dword ptr[_d_scantable+esi*4] mov esi,ds:dword ptr[_d_viewbuffer] add edi,eax mov ds:byte ptr[esi+edi],dl Skip2B: LNextVert: pop edi pop esi ret public _D_PolysetDrawFinalVertsT3 _D_PolysetDrawFinalVertsT3: push ebp push ebx mov ebx,dword ptr[4+8+esp] ;pv1 call _L_PDFVertT3 mov ebx,dword ptr[8+8+esp] ;pv2 call _L_PDFVertT3 mov ebx,dword ptr[12+8+esp];pv3 call _L_PDFVertT3 pop ebx pop ebp ret public _D_DrawNonSubdivT3 _D_DrawNonSubdivT3: ; push ebp ; mov ebp,ds:dword ptr[_r_affinetridesc+24] push ebx ; shl ebp,4 push esi mov esi,ds:dword ptr[_r_affinetridesc+16] push edi LNDLoop: mov edi,ds:dword ptr[_r_affinetridesc+20] xor ecx,ecx; //clear i1 xor edx,edx; //clear i2 xor ebx,ebx; //clear i3 mov cx, word ptr[4+0+esi] ;ptri->vertindex[0] mov dx, word ptr[4+2+esi] ;ptri->vertindex[1] mov bx, word ptr[4+4+esi] ;ptri->vertindex[2] shl ecx,5 shl edx,5 shl ebx,5 add ecx,edi add edx,edi add ebx,edi mov eax,ds:dword ptr[0+4+ecx] mov esi,ds:dword ptr[0+0+ecx] sub eax,ds:dword ptr[0+4+edx] sub esi,ds:dword ptr[0+0+ebx] imul eax,esi mov esi,ds:dword ptr[0+0+ecx] mov edi,ds:dword ptr[0+4+ecx] sub esi,ds:dword ptr[0+0+edx] sub edi,ds:dword ptr[0+4+ebx] imul edi,esi sub eax,edi jns LNextTri mov ds:dword ptr[_d_xdenom],eax fild ds:dword ptr[_d_xdenom] mov eax,ds:dword ptr[0+0+ecx] mov esi,ds:dword ptr[0+4+ecx] mov ds:dword ptr[_r_p0+0],eax mov ds:dword ptr[_r_p0+4],esi mov eax,ds:dword ptr[0+8+ecx] mov esi,ds:dword ptr[0+12+ecx] mov ds:dword ptr[_r_p0+8],eax mov ds:dword ptr[_r_p0+12],esi mov eax,ds:dword ptr[0+16+ecx] mov esi,ds:dword ptr[0+20+ecx] mov ds:dword ptr[_r_p0+16],eax mov ds:dword ptr[_r_p0+20],esi fdivr ds:dword ptr[float_1] mov eax,ds:dword ptr[0+0+edx] mov esi,ds:dword ptr[0+4+edx] mov ds:dword ptr[_r_p1+0],eax mov ds:dword ptr[_r_p1+4],esi mov eax,ds:dword ptr[0+8+edx] mov esi,ds:dword ptr[0+12+edx] mov ds:dword ptr[_r_p1+8],eax mov ds:dword ptr[_r_p1+12],esi mov eax,ds:dword ptr[0+16+edx] mov esi,ds:dword ptr[0+20+edx] mov ds:dword ptr[_r_p1+16],eax mov ds:dword ptr[_r_p1+20],esi mov eax,ds:dword ptr[0+0+ebx] mov esi,ds:dword ptr[0+4+ebx] mov ds:dword ptr[_r_p2+0],eax mov ds:dword ptr[_r_p2+4],esi mov eax,ds:dword ptr[0+8+ebx] mov esi,ds:dword ptr[0+12+ebx] mov ds:dword ptr[_r_p2+8],eax mov ds:dword ptr[_r_p2+12],esi mov eax,ds:dword ptr[0+16+ebx] mov esi,ds:dword ptr[0+20+ebx] mov ds:dword ptr[_r_p2+16],eax mov edi,ds:dword ptr[_r_affinetridesc+16] mov ds:dword ptr[_r_p2+20],esi mov eax,ds:dword ptr[0+edi] test eax,eax jnz LFacesFront mov eax,ds:dword ptr[24+ecx] mov esi,ds:dword ptr[24+edx] mov edi,ds:dword ptr[24+ebx] test eax,00020h mov eax,ds:dword ptr[_r_affinetridesc+32] jz LOnseamDone0 add ds:dword ptr[_r_p0+8],eax LOnseamDone0: test esi,00020h jz LOnseamDone1 add ds:dword ptr[_r_p1+8],eax LOnseamDone1: test edi,00020h jz LOnseamDone2 add ds:dword ptr[_r_p2+8],eax LOnseamDone2: LFacesFront: fstp ds:dword ptr[_d_xdenom] call near ptr _D_PolysetSetEdgeTable call near ptr _D_RasterizeAliasPolySmooth LNextTri: mov esi,ds:dword ptr[_r_affinetridesc+16] ; sub ebp,16 ; jnz LNDLoop pop edi pop esi pop ebx ; pop ebp add esp,offset (((1024+1 + 1 + ((32 - 1) / 32)) + 1) * 32) ret _TEXT ENDS END engine/h2shared/masm/d_polysa5.asm000066400000000000000000000612721444734033100173740ustar00rootroot00000000000000; ; d_polysa5.asm -- for MASM ; x86 assembly-language polygon model drawing code ; with translucency handling, #5. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _d_viewbuffer:dword externdef _d_scantable:dword externdef _r_refdef:dword externdef _a_sstepxfrac:dword externdef _r_affinetridesc:dword externdef _acolormap:dword externdef _d_pcolormap:dword externdef _d_sfrac:dword externdef _d_ptex:dword externdef _d_pedgespanpackage:dword externdef _d_tfrac:dword externdef _d_light:dword externdef _d_zi:dword externdef _d_pdest:dword externdef _d_pz:dword externdef _d_aspancount:dword externdef _erroradjustup:dword externdef _erroradjustdown:dword externdef _errorterm:dword externdef _d_xdenom:dword externdef _r_p0:dword externdef _r_p1:dword externdef _r_p2:dword externdef _a_tstepxfrac:dword externdef _a_ststepxwhole:dword externdef _zspantable:dword externdef _skintable:dword externdef _d_countextrastep:dword externdef _ubasestep:dword externdef _a_spans:dword externdef _d_pdestextrastep:dword externdef _d_pzextrastep:dword externdef _d_sfracextrastep:dword externdef _d_ptexextrastep:dword externdef _d_tfracextrastep:dword externdef _d_lightextrastep:dword externdef _d_ziextrastep:dword externdef _d_pdestbasestep:dword externdef _d_pzbasestep:dword externdef _d_sfracbasestep:dword externdef _d_ptexbasestep:dword externdef _d_tfracbasestep:dword externdef _d_lightbasestep:dword externdef _d_zibasestep:dword externdef _r_lstepx:dword externdef _r_lstepy:dword externdef _r_sstepx:dword externdef _r_sstepy:dword externdef _r_tstepx:dword externdef _r_tstepy:dword externdef _r_zistepx:dword externdef _r_zistepy:dword externdef _transTable:dword externdef _D_PolysetSetEdgeTable:dword externdef _D_RasterizeAliasPolySmooth:dword ; externs from ASM-only code externdef float_point5:dword externdef float_1:dword externdef float_minus_1:dword externdef float_0:dword externdef advancetable:dword externdef sstep:dword externdef tstep:dword externdef ceil_cw:dword externdef single_cw:dword _DATA SEGMENT align 4 p10_minus_p20 dd 0 p01_minus_p21 dd 0 temp0 dd 0 temp1 dd 0 Ltemp dd 0 aff8entryvec_table dd LDraw8, LDraw7, LDraw6, LDraw5 dd LDraw4, LDraw3, LDraw2, LDraw1 lzistepx dd 0 _DATA ENDS _TEXT SEGMENT externdef _D_PolysetSetEdgeTable:dword externdef _D_RasterizeAliasPolySmooth:dword public _D_PolysetAff8StartT5 _D_PolysetAff8StartT5: public _D_PolysetCalcGradientsT5 _D_PolysetCalcGradientsT5: fild dword ptr[_r_p0+0] fild dword ptr[_r_p2+0] fild dword ptr[_r_p0+4] fild dword ptr[_r_p2+4] fild dword ptr[_r_p1+0] fild dword ptr[_r_p1+4] fxch st(3) fsub st(0),st(2) fxch st(1) fsub st(0),st(4) fxch st(5) fsubrp st(4),st(0) fxch st(2) fsubrp st(1),st(0) fxch st(1) fld dword ptr[_d_xdenom] fxch st(4) fstp dword ptr[p10_minus_p20] fstp dword ptr[p01_minus_p21] fxch st(2) fild dword ptr[_r_p2+16] fild dword ptr[_r_p0+16] fild dword ptr[_r_p1+16] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmul st(0),st(5) fxch st(2) fld st(0) fmul dword ptr[p01_minus_p21] fxch st(2) fmul dword ptr[p10_minus_p20] fxch st(1) fmul st(0),st(5) fxch st(2) fsubrp st(3),st(0) fsubp st(1),st(0) fld st(2) fmul dword ptr[float_minus_1] fxch st(2) fmul st(0),st(3) fxch st(1) fmul st(0),st(2) fldcw word ptr[ceil_cw] fistp dword ptr[_r_lstepy] fistp dword ptr[_r_lstepx] fldcw word ptr[single_cw] fild dword ptr[_r_p2+8] fild dword ptr[_r_p0+8] fild dword ptr[_r_p1+8] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmul st(0),st(6) fxch st(2) fld st(0) fmul dword ptr[p01_minus_p21] fxch st(2) fmul dword ptr[p10_minus_p20] fxch st(1) fmul st(0),st(6) fxch st(2) fsubrp st(3),st(0) fsubp st(1),st(0) fmul st(0),st(2) fxch st(1) fmul st(0),st(3) fxch st(1) fistp dword ptr[_r_sstepy] fistp dword ptr[_r_sstepx] fild dword ptr[_r_p2+12] fild dword ptr[_r_p0+12] fild dword ptr[_r_p1+12] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmul st(0),st(6) fxch st(2) fld st(0) fmul dword ptr[p01_minus_p21] fxch st(2) fmul dword ptr[p10_minus_p20] fxch st(1) fmul st(0),st(6) fxch st(2) fsubrp st(3),st(0) fsubp st(1),st(0) fmul st(0),st(2) fxch st(1) fmul st(0),st(3) fxch st(1) fistp dword ptr[_r_tstepy] fistp dword ptr[_r_tstepx] fild dword ptr[_r_p2+20] fild dword ptr[_r_p0+20] fild dword ptr[_r_p1+20] fxch st(2) fld st(0) fsubp st(2),st(0) fsubp st(2),st(0) fld st(0) fmulp st(6),st(0) fxch st(1) fld st(0) fmul dword ptr[p01_minus_p21] fxch st(2) fmul dword ptr[p10_minus_p20] fxch st(1) fmulp st(5),st(0) fxch st(5) fsubp st(1),st(0) fxch st(3) fsubrp st(4),st(0) fxch st(1) fmulp st(2),st(0) fmulp st(2),st(0) fistp dword ptr[_r_zistepx] fistp dword ptr[_r_zistepy] mov eax,dword ptr[_r_sstepx] mov edx,dword ptr[_r_tstepx] shl eax,16 shl edx,16 mov dword ptr[_a_sstepxfrac],eax mov dword ptr[_a_tstepxfrac],edx mov ecx,dword ptr[_r_sstepx] mov eax,dword ptr[_r_tstepx] sar ecx,16 sar eax,16 imul dword ptr[4+0+esp] add eax,ecx mov dword ptr[_a_ststepxwhole],eax ret public _D_PolysetRecursiveTriangleT5 _D_PolysetRecursiveTriangleT5: push ebp push esi push edi push ebx mov esi,dword ptr[8+16+esp] mov ebx,dword ptr[4+16+esp] mov edi,dword ptr[12+16+esp] mov eax,dword ptr[0+esi] mov edx,dword ptr[0+ebx] mov ebp,dword ptr[4+esi] sub eax,edx mov ecx,dword ptr[4+ebx] sub ebp,ecx inc eax cmp eax,2 ja LSplit mov eax,dword ptr[0+edi] inc ebp cmp ebp,2 ja LSplit mov edx,dword ptr[0+esi] mov ebp,dword ptr[4+edi] sub eax,edx mov ecx,dword ptr[4+esi] sub ebp,ecx inc eax cmp eax,2 ja LSplit2 mov eax,dword ptr[0+ebx] inc ebp cmp ebp,2 ja LSplit2 mov edx,dword ptr[0+edi] mov ebp,dword ptr[4+ebx] sub eax,edx mov ecx,dword ptr[4+edi] sub ebp,ecx inc eax inc ebp mov edx,ebx cmp eax,2 ja LSplit3 cmp ebp,2 jna LDone LSplit3: mov ebx,edi mov edi,esi mov esi,edx jmp LSplit LSplit2: mov eax,ebx mov ebx,esi mov esi,edi mov edi,eax LSplit: sub esp,24 mov eax,dword ptr[8+ebx] mov edx,dword ptr[8+esi] mov ecx,dword ptr[12+ebx] add eax,edx mov edx,dword ptr[12+esi] sar eax,1 add ecx,edx mov dword ptr[8+esp],eax mov eax,dword ptr[20+ebx] sar ecx,1 mov edx,dword ptr[20+esi] mov dword ptr[12+esp],ecx add eax,edx mov ecx,dword ptr[0+ebx] mov edx,dword ptr[0+esi] sar eax,1 add edx,ecx mov dword ptr[20+esp],eax mov eax,dword ptr[4+ebx] sar edx,1 mov ebp,dword ptr[4+esi] mov dword ptr[0+esp],edx add ebp,eax sar ebp,1 mov dword ptr[4+esp],ebp cmp dword ptr[4+esi],eax jg LNoDraw mov edx,dword ptr[0+esi] jnz LDraw cmp edx,ecx jl LNoDraw LDraw: mov edx,dword ptr[20+esp] mov ecx,dword ptr[4+esp] sar edx,16 mov ebp,dword ptr[0+esp] mov eax,dword ptr[_zspantable+ecx*4] cmp dx,word ptr[eax+ebp*2] jnge LNoDraw ;mov word ptr[eax+ebp*2],dx mov eax,dword ptr[12+esp] sar eax,16 mov edx,dword ptr[8+esp] sar edx,16 sub ecx,ecx mov eax,dword ptr[_skintable+eax*4] mov ebp,dword ptr[4+esp] mov dh,byte ptr[eax+edx] ; texture pixel or dh,dh jz Skip1B ; color 0 = no draw ;mov edx,dword ptr[_d_pcolormap] ;mov dh,byte ptr[edx+ecx] mov ecx,dword ptr[0+esp] mov eax,dword ptr[_d_scantable+ebp*4] add ecx,eax mov eax,dword ptr[_d_viewbuffer] ; trans stuff mov dl,byte ptr[eax+ecx] and edx, 0ffffh mov dh,byte ptr[12345678h + edx] TranPatch1: mov byte ptr[eax+ecx],dh ; rjr distance ; mov byte ptr[eax+ecx],0 Skip1B: LNoDraw: push esp push ebx push edi call near ptr _D_PolysetRecursiveTriangleT5 mov ebx,esp push esi push ebx push edi call near ptr _D_PolysetRecursiveTriangleT5 add esp,24 LDone: pop ebx pop edi pop esi pop ebp ret 12 public _D_PolysetDrawSpans8T5 _D_PolysetDrawSpans8T5: push esi push ebx mov esi,dword ptr[4+8+esp] mov ecx,dword ptr[_r_zistepx] push ebp push edi ror ecx,16 mov edx,dword ptr[8+esi] mov dword ptr[lzistepx],ecx LSpanLoop: mov eax,dword ptr[_d_aspancount] sub eax,edx mov edx,dword ptr[_erroradjustup] mov ebx,dword ptr[_errorterm] add ebx,edx js LNoTurnover mov edx,dword ptr[_erroradjustdown] mov edi,dword ptr[_d_countextrastep] sub ebx,edx mov ebp,dword ptr[_d_aspancount] mov dword ptr[_errorterm],ebx add ebp,edi mov dword ptr[_d_aspancount],ebp jmp LRightEdgeStepped LNoTurnover: mov edi,dword ptr[_d_aspancount] mov edx,dword ptr[_ubasestep] mov dword ptr[_errorterm],ebx add edi,edx mov dword ptr[_d_aspancount],edi LRightEdgeStepped: cmp eax,1 jl LNextSpan jz LExactlyOneLong mov ecx,dword ptr[_a_ststepxwhole] mov edx,dword ptr[_r_affinetridesc+8] mov dword ptr[advancetable+4],ecx add ecx,edx mov dword ptr[advancetable],ecx mov ecx,dword ptr[_a_tstepxfrac] mov cx,word ptr[_r_lstepx] mov edx,eax mov dword ptr[tstep],ecx add edx,7 shr edx,3 mov ebx,dword ptr[16+esi] mov bx,dx mov ecx,dword ptr[4+esi] neg eax mov edi,dword ptr[0+esi] and eax,7 sub edi,eax sub ecx,eax sub ecx,eax mov edx,dword ptr[20+esi] mov dx,word ptr[24+esi] mov ebp,dword ptr[28+esi] ror ebp,16 push esi mov esi,dword ptr[12+esi] jmp dword ptr[aff8entryvec_table+eax*4] LDrawLoop: LDraw8: cmp bp,word ptr[ecx] jl Lp1 xor eax,eax ; mov ah,dh ; light mov ah,byte ptr[esi] ; texture pixel or ah,ah jz SkipA2 ; color 0 = no draw ; mov word ptr[ecx],bp ; mov ah,byte ptr[12345678h+eax] ;LPatch8: ; trans stuff mov al,byte ptr[edi] mov ah,byte ptr[12345678h + eax] TranPatch2: mov byte ptr[edi],ah ; rj ;mov byte ptr[edi],0 SkipA2: Lp1: add edx,dword ptr[tstep] sbb eax,eax add ebp,dword ptr[lzistepx] adc ebp,0 add ebx,dword ptr[_a_sstepxfrac] adc esi,dword ptr[advancetable+4+eax*4] LDraw7: cmp bp,word ptr[2+ecx] jl Lp2 xor eax,eax ; mov ah,dh mov ah,byte ptr[esi] ; texture pixel or ah,ah jz SkipB2 ; color 0 = no draw ; mov word ptr[2+ecx],bp ; mov ah,byte ptr[12345678h+eax] ;LPatch7: ; trans stuff mov al,byte ptr[edi+1] mov ah,byte ptr[12345678h + eax] TranPatch3: mov byte ptr[1+edi],ah ; rj ;mov byte ptr[1+edi],0 SkipB2: Lp2: add edx,dword ptr[tstep] sbb eax,eax add ebp,dword ptr[lzistepx] adc ebp,0 add ebx,dword ptr[_a_sstepxfrac] adc esi,dword ptr[advancetable+4+eax*4] LDraw6: cmp bp,word ptr[4+ecx] jl Lp3 xor eax,eax ; mov ah,dh mov ah,byte ptr[esi] ; texture pixel or ah,ah jz SkipC2 ; color 0 = no draw ; mov word ptr[4+ecx],bp ; mov ah,byte ptr[12345678h+eax] ;LPatch6: ; trans stuff mov al,byte ptr[edi+2] mov ah,byte ptr[12345678h + eax] TranPatch4: mov byte ptr[2+edi],ah ; rj ;mov byte ptr[2+edi],0 SkipC2: Lp3: add edx,dword ptr[tstep] sbb eax,eax add ebp,dword ptr[lzistepx] adc ebp,0 add ebx,dword ptr[_a_sstepxfrac] adc esi,dword ptr[advancetable+4+eax*4] LDraw5: cmp bp,word ptr[6+ecx] jl Lp4 xor eax,eax ; mov ah,dh mov ah,byte ptr[esi] ; texture pixel or ah,ah jz SkipD2 ; color 0 = no draw ; mov word ptr[6+ecx],bp ; mov ah,byte ptr[12345678h+eax] ;LPatch5: ; trans stuff mov al,byte ptr[edi+3] mov ah,byte ptr[12345678h + eax] TranPatch5: mov byte ptr[3+edi],ah ; rj ;mov byte ptr[3+edi],0 SkipD2: Lp4: add edx,dword ptr[tstep] sbb eax,eax add ebp,dword ptr[lzistepx] adc ebp,0 add ebx,dword ptr[_a_sstepxfrac] adc esi,dword ptr[advancetable+4+eax*4] LDraw4: cmp bp,word ptr[8+ecx] jl Lp5 xor eax,eax ; mov ah,dh mov ah,byte ptr[esi] ; texture pixel or ah,ah jz SkipE2 ; color 0 = no draw ; mov word ptr[8+ecx],bp ; mov ah,byte ptr[12345678h+eax] ;LPatch4: ; trans stuff mov al,byte ptr[edi+4] mov ah,byte ptr[12345678h + eax] TranPatch6: mov byte ptr[4+edi],ah ; rj ;mov byte ptr[4+edi],0 SkipE2: Lp5: add edx,dword ptr[tstep] sbb eax,eax add ebp,dword ptr[lzistepx] adc ebp,0 add ebx,dword ptr[_a_sstepxfrac] adc esi,dword ptr[advancetable+4+eax*4] LDraw3: cmp bp,word ptr[10+ecx] jl Lp6 xor eax,eax ; mov ah,dh mov ah,byte ptr[esi] ; texture pixel or ah,ah jz SkipF2 ; color 0 = no draw ; mov word ptr[10+ecx],bp ; mov ah,byte ptr[12345678h+eax] ;LPatch3: ; trans stuff mov al,byte ptr[edi+5] mov ah,byte ptr[12345678h + eax] TranPatch7: mov byte ptr[5+edi],ah ; rj ;mov byte ptr[5+edi],0 SkipF2: Lp6: add edx,dword ptr[tstep] sbb eax,eax add ebp,dword ptr[lzistepx] adc ebp,0 add ebx,dword ptr[_a_sstepxfrac] adc esi,dword ptr[advancetable+4+eax*4] LDraw2: cmp bp,word ptr[12+ecx] jl Lp7 xor eax,eax ; mov ah,dh mov ah,byte ptr[esi] ; texture pixel or ah,ah jz SkipG2 ; color 0 = no draw ; mov word ptr[12+ecx],bp ; mov ah,byte ptr[12345678h+eax] ;LPatch2: ; trans stuff mov al,byte ptr[edi+6] mov ah,byte ptr[12345678h + eax] TranPatch8: mov byte ptr[6+edi],ah ; rj ;mov byte ptr[6+edi],0 SkipG2: Lp7: add edx,dword ptr[tstep] sbb eax,eax add ebp,dword ptr[lzistepx] adc ebp,0 add ebx,dword ptr[_a_sstepxfrac] adc esi,dword ptr[advancetable+4+eax*4] LDraw1: cmp bp,word ptr[14+ecx] jl Lp8 xor eax,eax ; mov ah,dh mov ah,byte ptr[esi] ; texture pixel or ah,ah jz SkipH2 ; color 0 = no draw ; mov word ptr[14+ecx],bp ; mov ah,byte ptr[12345678h+eax] ;LPatch1: ; trans stuff mov al,byte ptr[edi+7] mov ah,byte ptr[12345678h + eax] TranPatch9: mov byte ptr[7+edi],ah ; rj ;mov byte ptr[7+edi],0 SkipH2: Lp8: add edx,dword ptr[tstep] sbb eax,eax add ebp,dword ptr[lzistepx] adc ebp,0 add ebx,dword ptr[_a_sstepxfrac] adc esi,dword ptr[advancetable+4+eax*4] add edi,8 add ecx,16 dec bx jnz LDrawLoop pop esi LNextSpan: add esi,32 LNextSpanESISet: mov edx,dword ptr[8+esi] cmp edx,offset -999999 jnz LSpanLoop pop edi pop ebp pop ebx pop esi ret LExactlyOneLong: mov ecx,dword ptr[4+esi] mov ebp,dword ptr[28+esi] ror ebp,16 mov ebx,dword ptr[12+esi] cmp bp,word ptr[ecx] jl LNextSpan xor eax,eax mov edi,dword ptr[0+esi] ; mov ah,byte ptr[24+1+esi] add esi,32 mov ah,byte ptr[ebx] ; texture pixel or ah,ah jz SkipI2 ; color 0 = no draw ; mov word ptr[ecx],bp ; mov ah,byte ptr[12345678h+eax] ;LPatch9: ; trans stuff mov al,byte ptr[edi] mov ah,byte ptr[12345678h + eax] TranPatch10: mov byte ptr[edi],ah ; rjr ;mov byte ptr[edi],0 SkipI2: jmp LNextSpanESISet public _D_Aff8PatchT5 _D_Aff8PatchT5: ret ; mov eax,dword ptr[4+esp] ; mov dword ptr[LPatch1-4],eax ; mov dword ptr[LPatch2-4],eax ; mov dword ptr[LPatch3-4],eax ; mov dword ptr[LPatch4-4],eax ; mov dword ptr[LPatch5-4],eax ; mov dword ptr[LPatch6-4],eax ; mov dword ptr[LPatch7-4],eax ; mov dword ptr[LPatch8-4],eax ; mov dword ptr[LPatch9-4],eax ret public _D_PolysetDrawT5 _D_PolysetDrawT5: sub esp,offset (((1024+1 + 1 + ((32 - 1) / 32)) + 1) * 32) mov eax,esp add eax,32 - 1 and eax,offset not (32 - 1) mov dword ptr[_a_spans],eax mov eax,dword ptr[_r_affinetridesc+28] test eax,eax jz _D_DrawNonSubdivT5 ; push ebp ; mov ebp,dword ptr[_r_affinetridesc+24] push esi ; shl ebp,4 push ebx mov ebx,dword ptr[_r_affinetridesc+16] push edi mov edi,dword ptr[_r_affinetridesc+20] Llooptop: xor ecx,ecx xor esi,esi xor edx,edx mov cx,word ptr[4+0+ebx] mov si,word ptr[4+2+ebx] mov dx,word ptr[4+4+ebx] shl ecx,5 shl esi,5 add ecx,edi shl edx,5 add esi,edi add edx,edi fild dword ptr[0+4+ecx] fild dword ptr[0+4+esi] fild dword ptr[0+0+ecx] fild dword ptr[0+0+edx] fxch st(2) fsubr st(0),st(3) fild dword ptr[0+0+esi] fxch st(2) fsubr st(3),st(0) fild dword ptr[0+4+edx] fxch st(1) fsubrp st(3),st(0) fxch st(1) fmulp st(3),st(0) fsubp st(3),st(0) mov eax,dword ptr[0+16+ecx] and eax,0FF00h fmulp st(2),st(0) add eax,dword ptr[_acolormap] fsubrp st(1),st(0) mov dword ptr[_d_pcolormap],eax fstp dword ptr[Ltemp] mov eax,dword ptr[Ltemp] sub eax,080000001h jc Lskip mov eax,dword ptr[0+ebx] test eax,eax jz Lfacesback push edx push esi push ecx call near ptr _D_PolysetRecursiveTriangleT5 ;sub ebp,16 ;jnz Llooptop jmp Ldone2 Lfacesback: mov eax,dword ptr[0+8+ecx] push eax mov eax,dword ptr[0+8+esi] push eax mov eax,dword ptr[0+8+edx] push eax push ecx push edx mov eax,dword ptr[_r_affinetridesc+32] test dword ptr[24+ecx],00020h jz Lp11 add dword ptr[0+8+ecx],eax Lp11: test dword ptr[24+esi],00020h jz Lp12 add dword ptr[0+8+esi],eax Lp12: test dword ptr[24+edx],00020h jz Lp13 add dword ptr[0+8+edx],eax Lp13: push edx push esi push ecx call near ptr _D_PolysetRecursiveTriangleT5 pop edx pop ecx pop eax mov dword ptr[0+8+edx],eax pop eax mov dword ptr[0+8+esi],eax pop eax mov dword ptr[0+8+ecx],eax Lskip: ;sub ebp,16 ;jnz Llooptop Ldone2: pop edi pop ebx pop esi ;pop ebp add esp,offset (((1024+1 + 1 + ((32 - 1) / 32)) + 1) * 32) ret public _D_PolysetScanLeftEdgeT5 _D_PolysetScanLeftEdgeT5: push ebp push esi push edi push ebx mov eax,dword ptr[4+16+esp] mov ecx,dword ptr[_d_sfrac] and eax,0FFFFh mov ebx,dword ptr[_d_ptex] or ecx,eax mov esi,dword ptr[_d_pedgespanpackage] mov edx,dword ptr[_d_tfrac] mov edi,dword ptr[_d_light] mov ebp,dword ptr[_d_zi] LScanLoop: mov dword ptr[12+esi],ebx mov eax,dword ptr[_d_pdest] mov dword ptr[0+esi],eax mov eax,dword ptr[_d_pz] mov dword ptr[4+esi],eax mov eax,dword ptr[_d_aspancount] mov dword ptr[8+esi],eax mov dword ptr[24+esi],edi mov dword ptr[28+esi],ebp mov dword ptr[16+esi],ecx mov dword ptr[20+esi],edx mov al,byte ptr[32+esi] add esi,32 mov eax,dword ptr[_erroradjustup] mov dword ptr[_d_pedgespanpackage],esi mov esi,dword ptr[_errorterm] add esi,eax mov eax,dword ptr[_d_pdest] js LNoLeftEdgeTurnover sub esi,dword ptr[_erroradjustdown] add eax,dword ptr[_d_pdestextrastep] mov dword ptr[_errorterm],esi mov dword ptr[_d_pdest],eax mov eax,dword ptr[_d_pz] mov esi,dword ptr[_d_aspancount] add eax,dword ptr[_d_pzextrastep] add ecx,dword ptr[_d_sfracextrastep] adc ebx,dword ptr[_d_ptexextrastep] add esi,dword ptr[_d_countextrastep] mov dword ptr[_d_pz],eax mov eax,dword ptr[_d_tfracextrastep] mov dword ptr[_d_aspancount],esi add edx,eax jnc LSkip1 add ebx,dword ptr[_r_affinetridesc+8] LSkip1: add edi,dword ptr[_d_lightextrastep] add ebp,dword ptr[_d_ziextrastep] mov esi,dword ptr[_d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz LScanLoop pop ebx pop edi pop esi pop ebp ret LNoLeftEdgeTurnover: mov dword ptr[_errorterm],esi add eax,dword ptr[_d_pdestbasestep] mov dword ptr[_d_pdest],eax mov eax,dword ptr[_d_pz] mov esi,dword ptr[_d_aspancount] add eax,dword ptr[_d_pzbasestep] add ecx,dword ptr[_d_sfracbasestep] adc ebx,dword ptr[_d_ptexbasestep] add esi,dword ptr[_ubasestep] mov dword ptr[_d_pz],eax mov dword ptr[_d_aspancount],esi mov esi,dword ptr[_d_tfracbasestep] add edx,esi jnc LSkip2 add ebx,dword ptr[_r_affinetridesc+8] LSkip2: add edi,dword ptr[_d_lightbasestep] add ebp,dword ptr[_d_zibasestep] mov esi,dword ptr[_d_pedgespanpackage] dec ecx test ecx,0FFFFh jnz LScanLoop pop ebx pop edi pop esi pop ebp ret _L_PSDFVertT5 PROC NEAR push esi push edi mov eax,dword ptr[0+0+ebx] mov edx,dword ptr[_r_refdef+40] cmp eax,edx jge LNextVert mov esi,dword ptr[0+4+ebx] mov edx,dword ptr[_r_refdef+44] cmp esi,edx jge LNextVert mov edi,dword ptr[_zspantable+esi*4] mov edx,dword ptr[0+20+ebx] shr edx,16 cmp dx,word ptr[edi+eax*2] jl LNextVert ;mov word ptr[edi+eax*2],dx mov edi,dword ptr[0+12+ebx] shr edi,16 mov edi,dword ptr[_skintable+edi*4] mov edx,dword ptr[0+8+ebx] shr edx,16 mov dh,byte ptr[edi+edx] ; texture pixel or dh,dh jz Skip2B ; color 0 = no draw mov edi,dword ptr[0+16+ebx] and edi,0FF00h ;and edx,000FFh add edi,edx ;mov edx,dword ptr[_acolormap] ;mov dh,byte ptr[edx+edi*1] mov edi,dword ptr[_d_scantable+esi*4] mov esi,dword ptr[_d_viewbuffer] add edi,eax ; trans stuff mov dl,byte ptr[esi+edi] and edx, 0ffffh mov dh,byte ptr[12345678h + edx] TranPatch11: mov byte ptr[esi+edi],dh ; rjr distance ;mov byte ptr[esi+edi],0 Skip2B: LNextVert: pop edi pop esi ret _L_PSDFVertT5 ENDP public _D_PolysetDrawFinalVertsT5 _D_PolysetDrawFinalVertsT5: push ebp push ebx mov ebx,dword ptr[4+8+esp] ;pv1 call _L_PSDFVertT5 mov ebx,dword ptr[8+8+esp] ;pv2 call _L_PSDFVertT5 mov ebx,dword ptr[12+8+esp];pv3 call _L_PSDFVertT5 pop ebx pop ebp ret public _D_DrawNonSubdivT5 _D_DrawNonSubdivT5: ; push ebp ; mov ebp,dword ptr[_r_affinetridesc+24] push ebx ; shl ebp,4 push esi mov esi,dword ptr[_r_affinetridesc+16] push edi LNDLoop: mov edi,dword ptr[_r_affinetridesc+20] xor ecx,ecx; //clear i1 xor edx,edx; //clear i2 xor ebx,ebx; //clear i3 mov cx, word ptr[4+0+esi] ;ptri->vertindex[0] mov dx, word ptr[4+2+esi] ;ptri->vertindex[1] mov bx, word ptr[4+4+esi] ;ptri->vertindex[2] shl ecx,5 shl edx,5 shl ebx,5 add ecx,edi add edx,edi add ebx,edi mov eax,dword ptr[0+4+ecx] mov esi,dword ptr[0+0+ecx] sub eax,dword ptr[0+4+edx] sub esi,dword ptr[0+0+ebx] imul eax,esi mov esi,dword ptr[0+0+ecx] mov edi,dword ptr[0+4+ecx] sub esi,dword ptr[0+0+edx] sub edi,dword ptr[0+4+ebx] imul edi,esi sub eax,edi jns LNextTri mov dword ptr[_d_xdenom],eax fild dword ptr[_d_xdenom] mov eax,dword ptr[0+0+ecx] mov esi,dword ptr[0+4+ecx] mov dword ptr[_r_p0+0],eax mov dword ptr[_r_p0+4],esi mov eax,dword ptr[0+8+ecx] mov esi,dword ptr[0+12+ecx] mov dword ptr[_r_p0+8],eax mov dword ptr[_r_p0+12],esi mov eax,dword ptr[0+16+ecx] mov esi,dword ptr[0+20+ecx] mov dword ptr[_r_p0+16],eax mov dword ptr[_r_p0+20],esi fdivr dword ptr[float_1] mov eax,dword ptr[0+0+edx] mov esi,dword ptr[0+4+edx] mov dword ptr[_r_p1+0],eax mov dword ptr[_r_p1+4],esi mov eax,dword ptr[0+8+edx] mov esi,dword ptr[0+12+edx] mov dword ptr[_r_p1+8],eax mov dword ptr[_r_p1+12],esi mov eax,dword ptr[0+16+edx] mov esi,dword ptr[0+20+edx] mov dword ptr[_r_p1+16],eax mov dword ptr[_r_p1+20],esi mov eax,dword ptr[0+0+ebx] mov esi,dword ptr[0+4+ebx] mov dword ptr[_r_p2+0],eax mov dword ptr[_r_p2+4],esi mov eax,dword ptr[0+8+ebx] mov esi,dword ptr[0+12+ebx] mov dword ptr[_r_p2+8],eax mov dword ptr[_r_p2+12],esi mov eax,dword ptr[0+16+ebx] mov esi,dword ptr[0+20+ebx] mov dword ptr[_r_p2+16],eax mov edi,dword ptr[_r_affinetridesc+16] mov dword ptr[_r_p2+20],esi mov eax,dword ptr[0+edi] test eax,eax jnz LFacesFront mov eax,dword ptr[24+ecx] mov esi,dword ptr[24+edx] mov edi,dword ptr[24+ebx] test eax,00020h mov eax,dword ptr[_r_affinetridesc+32] jz LOnseamDone0 add dword ptr[_r_p0+8],eax LOnseamDone0: test esi,00020h jz LOnseamDone1 add dword ptr[_r_p1+8],eax LOnseamDone1: test edi,00020h jz LOnseamDone2 add dword ptr[_r_p2+8],eax LOnseamDone2: LFacesFront: fstp dword ptr[_d_xdenom] call near ptr _D_PolysetSetEdgeTable call near ptr _D_RasterizeAliasPolySmooth LNextTri: mov esi,dword ptr[_r_affinetridesc+16] ; sub ebp,16 ; jnz LNDLoop pop edi pop esi pop ebx ; pop ebp add esp,offset (((1024+1 + 1 + ((32 - 1) / 32)) + 1) * 32) ret public _D_PolysetAff8EndT5 _D_PolysetAff8EndT5: _TEXT ENDS _DATA SEGMENT align 4 LPatchTable: dd TranPatch1-4 dd TranPatch2-4 dd TranPatch3-4 dd TranPatch4-4 dd TranPatch5-4 dd TranPatch6-4 dd TranPatch7-4 dd TranPatch8-4 dd TranPatch9-4 dd TranPatch10-4 dd TranPatch11-4 _DATA ENDS _TEXT SEGMENT align 4 public _R_TranPatch6 _R_TranPatch6: push ebx mov eax,dword ptr[_transTable] mov ebx,offset LPatchTable mov ecx,11 LPatchLoop: mov edx,dword ptr[ebx] add ebx,4 mov dword ptr[edx],eax dec ecx jnz LPatchLoop pop ebx ret _TEXT ENDS END engine/h2shared/masm/d_scana.asm000066400000000000000000000112501444734033100170540ustar00rootroot00000000000000; ; d_scana.asm -- for MASM ; x86 assembly-language turbulent texture mapping code ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _r_turb_s:dword externdef _r_turb_t:dword externdef _r_turb_pdest:dword externdef _r_turb_spancount:dword externdef _r_turb_turb:dword externdef _r_turb_pbase:dword externdef _r_turb_sstep:dword externdef _r_turb_tstep:dword externdef _mainTransTable:dword externdef _scanList:dword ; externs from ASM-only code _DATA SEGMENT _DATA ENDS _TEXT SEGMENT align 4 public _D_DrawTurbulent8Span _D_DrawTurbulent8Span: push ebp push esi push edi push ebx mov esi,ds:dword ptr[_r_turb_s] mov ecx,ds:dword ptr[_r_turb_t] mov edi,ds:dword ptr[_r_turb_pdest] mov ebx,ds:dword ptr[_r_turb_spancount] Llp: mov eax,ecx mov edx,esi sar eax,16 mov ebp,ds:dword ptr[_r_turb_turb] sar edx,16 and eax,128-1 and edx,128-1 mov eax,ds:dword ptr[ebp+eax*4] mov edx,ds:dword ptr[ebp+edx*4] add eax,esi sar eax,16 add edx,ecx sar edx,16 and eax,64-1 and edx,64-1 shl edx,6 mov ebp,ds:dword ptr[_r_turb_pbase] add edx,eax inc edi add esi,ds:dword ptr[_r_turb_sstep] add ecx,ds:dword ptr[_r_turb_tstep] mov dl,ds:byte ptr[ebp+edx*1] dec ebx mov ds:byte ptr[-1+edi],dl jnz Llp mov ds:dword ptr[_r_turb_pdest],edi pop ebx pop edi pop esi pop ebp ret align 4 public _D_DrawTurbulent8TSpan _D_DrawTurbulent8TSpan: push ebp push esi push edi push ebx mov esi,ds:dword ptr[_r_turb_s] mov ecx,ds:dword ptr[_r_turb_t] mov edi,ds:dword ptr[_r_turb_pdest] mov ebx,ds:dword ptr[_r_turb_spancount] LlpT: mov eax,ecx mov edx,esi sar eax,16 mov ebp,ds:dword ptr[_r_turb_turb] sar edx,16 and eax,128-1 and edx,128-1 mov eax,ds:dword ptr[ebp+eax*4] mov edx,ds:dword ptr[ebp+edx*4] add eax,esi sar eax,16 add edx,ecx sar edx,16 and eax,64-1 and edx,64-1 shl edx,6 mov ebp,ds:dword ptr[_r_turb_pbase] add edx,eax cmp ds:byte ptr[_scanList + ebx - 1], 1 jnz skip1 inc edi mov dh,ds:byte ptr[ebp+edx*1] add esi,ds:dword ptr[_r_turb_sstep] mov dl,ds:byte ptr[-1+edi] add ecx,ds:dword ptr[_r_turb_tstep] mov dl,ds:byte ptr[12345678h + edx] TranPatch1: dec ebx mov ds:byte ptr[-1+edi],dl ;mov ds:byte ptr[-1+edi],255 jnz LlpT skip2: mov ds:dword ptr[_r_turb_pdest],edi pop ebx pop edi pop esi pop ebp ret skip1: inc edi dec ebx jnz LlpT jmp skip2 align 4 public _D_DrawTurbulent8TQuickSpan _D_DrawTurbulent8TQuickSpan: push ebp push esi push edi push ebx mov esi,ds:dword ptr[_r_turb_s] mov ecx,ds:dword ptr[_r_turb_t] mov edi,ds:dword ptr[_r_turb_pdest] mov ebx,ds:dword ptr[_r_turb_spancount] LlpTQ: mov eax,ecx mov edx,esi sar eax,16 mov ebp,ds:dword ptr[_r_turb_turb] sar edx,16 and eax,128-1 and edx,128-1 mov eax,ds:dword ptr[ebp+eax*4] mov edx,ds:dword ptr[ebp+edx*4] add eax,esi sar eax,16 add edx,ecx sar edx,16 and eax,64-1 and edx,64-1 shl edx,6 mov ebp,ds:dword ptr[_r_turb_pbase] add edx,eax ;cmp ds:byte ptr[_scanList + ebx - 1], 1 ;jnz skip1 inc edi mov dh,ds:byte ptr[ebp+edx*1] add esi,ds:dword ptr[_r_turb_sstep] mov dl,ds:byte ptr[-1+edi] add ecx,ds:dword ptr[_r_turb_tstep] mov dl,ds:byte ptr[12345678h + edx] TranPatch2: dec ebx mov ds:byte ptr[-1+edi],dl jnz LlpTQ mov ds:dword ptr[_r_turb_pdest],edi pop ebx pop edi pop esi pop ebp ret public _D_DrawTurbulent8TSpanEnd _D_DrawTurbulent8TSpanEnd: _TEXT ENDS _DATA SEGMENT align 4 LPatchTable: dd TranPatch1-4 dd TranPatch2-4 _DATA ENDS _TEXT SEGMENT align 4 public _R_TranPatch7 _R_TranPatch7: push ebx mov eax,ds:dword ptr[_mainTransTable] mov ebx,offset LPatchTable mov ecx,2 LPatchLoop: mov edx,ds:dword ptr[ebx] add ebx,4 mov ds:dword ptr[edx],eax dec ecx jnz LPatchLoop pop ebx ret _TEXT ENDS END engine/h2shared/masm/d_spr8.asm000066400000000000000000000373061444734033100166750ustar00rootroot00000000000000; ; d_spr8.asm -- for MASM. ; x86 assembly-language horizontal 8-bpp sprite span-drawing code. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _d_zistepu:dword externdef _d_pzbuffer:dword externdef _d_zistepv:dword externdef _d_zrowbytes:dword externdef _d_ziorigin:dword externdef _d_sdivzstepu:dword externdef _d_tdivzstepu:dword externdef _d_sdivzstepv:dword externdef _d_tdivzstepv:dword externdef _d_sdivzorigin:dword externdef _d_tdivzorigin:dword externdef _sadjust:dword externdef _tadjust:dword externdef _bbextents:dword externdef _bbextentt:dword externdef _cacheblock:dword externdef _d_viewbuffer:dword externdef _cachewidth:dword externdef _d_scantable:dword ; externs from ASM-only code externdef fp_1m:dword externdef fp_1m_minus_1:dword externdef fp_8:dword externdef fp_16:dword externdef fp_64k:dword externdef fp_64kx64k:dword externdef izi:dword externdef izistep:dword externdef sstep:dword externdef tstep:dword externdef advancetable:dword externdef spr8entryvec_table:dword externdef reciprocal_table:dword externdef reciprocal_table_16:dword externdef pbase:dword externdef s:dword externdef t:dword externdef sfracf:dword externdef tfracf:dword externdef snext:dword externdef tnext:dword externdef spancountminus1:dword externdef zi16stepu:dword externdef sdivz16stepu:dword externdef tdivz16stepu:dword externdef zi8stepu:dword externdef sdivz8stepu:dword externdef tdivz8stepu:dword externdef pz:dword _TEXT SEGMENT LClampHigh0: mov esi,ds:dword ptr[_bbextents] jmp LClampReentry0 LClampHighOrLow0: jg LClampHigh0 xor esi,esi jmp LClampReentry0 LClampHigh1: mov edx,ds:dword ptr[_bbextentt] jmp LClampReentry1 LClampHighOrLow1: jg LClampHigh1 xor edx,edx jmp LClampReentry1 LClampLow2: mov ebp,2048 jmp LClampReentry2 LClampHigh2: mov ebp,ds:dword ptr[_bbextents] jmp LClampReentry2 LClampLow3: mov ecx,2048 jmp LClampReentry3 LClampHigh3: mov ecx,ds:dword ptr[_bbextentt] jmp LClampReentry3 LClampLow4: mov eax,2048 jmp LClampReentry4 LClampHigh4: mov eax,ds:dword ptr[_bbextents] jmp LClampReentry4 LClampLow5: mov ebx,2048 jmp LClampReentry5 LClampHigh5: mov ebx,ds:dword ptr[_bbextentt] jmp LClampReentry5 align 4 public _D_SpriteDrawSpans _D_SpriteDrawSpans: push ebp push edi push esi push ebx fld ds:dword ptr[_d_sdivzstepu] fmul ds:dword ptr[fp_8] mov edx,ds:dword ptr[_cacheblock] fld ds:dword ptr[_d_tdivzstepu] fmul ds:dword ptr[fp_8] mov ebx,ds:dword ptr[4+16+esp] fld ds:dword ptr[_d_zistepu] fmul ds:dword ptr[fp_8] mov ds:dword ptr[pbase],edx fld ds:dword ptr[_d_zistepu] fmul ds:dword ptr[fp_64kx64k] fxch st(3) fstp ds:dword ptr[sdivz8stepu] fstp ds:dword ptr[zi8stepu] fstp ds:dword ptr[tdivz8stepu] fistp ds:dword ptr[izistep] mov eax,ds:dword ptr[izistep] ror eax,16 mov ecx,ds:dword ptr[8+ebx] mov ds:dword ptr[izistep],eax cmp ecx,0 jle LNextSpan LSpanLoop: fild ds:dword ptr[4+ebx] fild ds:dword ptr[0+ebx] fld st(1) fmul ds:dword ptr[_d_sdivzstepv] fld st(1) fmul ds:dword ptr[_d_sdivzstepu] fld st(2) fmul ds:dword ptr[_d_tdivzstepu] fxch st(1) faddp st(2),st(0) fxch st(1) fld st(3) fmul ds:dword ptr[_d_tdivzstepv] fxch st(1) fadd ds:dword ptr[_d_sdivzorigin] fxch st(4) fmul ds:dword ptr[_d_zistepv] fxch st(1) faddp st(2),st(0) fxch st(2) fmul ds:dword ptr[_d_zistepu] fxch st(1) fadd ds:dword ptr[_d_tdivzorigin] fxch st(2) faddp st(1),st(0) fld ds:dword ptr[fp_64k] fxch st(1) fadd ds:dword ptr[_d_ziorigin] fld st(0) fmul ds:dword ptr[fp_64kx64k] fxch st(1) fdiv st(2),st(0) fxch st(1) fistp ds:dword ptr[izi] mov ebp,ds:dword ptr[izi] ror ebp,16 mov eax,ds:dword ptr[4+ebx] mov ds:dword ptr[izi],ebp mov ebp,ds:dword ptr[0+ebx] imul ds:dword ptr[_d_zrowbytes] shl ebp,1 add eax,ds:dword ptr[_d_pzbuffer] add eax,ebp mov ds:dword ptr[pz],eax mov ebp,ds:dword ptr[_d_viewbuffer] mov eax,ds:dword ptr[4+ebx] push ebx mov edx,ds:dword ptr[_tadjust] mov esi,ds:dword ptr[_sadjust] mov edi,ds:dword ptr[_d_scantable+eax*4] add edi,ebp mov ebp,ds:dword ptr[0+ebx] add edi,ebp cmp ecx,8 ja LSetupNotLast1 dec ecx jz LCleanup1 mov ds:dword ptr[spancountminus1],ecx fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] fild ds:dword ptr[spancountminus1] fld ds:dword ptr[_d_tdivzstepu] fld ds:dword ptr[_d_zistepu] fmul st(0),st(2) fxch st(1) fmul st(0),st(2) fxch st(2) fmul ds:dword ptr[_d_sdivzstepu] fxch st(1) faddp st(3),st(0) fxch st(1) faddp st(3),st(0) faddp st(3),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) jmp LFDIVInFlight1 LCleanup1: fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] jmp LFDIVInFlight1 align 4 LSetupNotLast1: fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] fadd ds:dword ptr[zi8stepu] fxch st(2) fadd ds:dword ptr[sdivz8stepu] fxch st(2) fld ds:dword ptr[tdivz8stepu] faddp st(2),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) LFDIVInFlight1: add esi,ds:dword ptr[s] add edx,ds:dword ptr[t] mov ebx,ds:dword ptr[_bbextents] mov ebp,ds:dword ptr[_bbextentt] cmp esi,ebx ja LClampHighOrLow0 LClampReentry0: mov ds:dword ptr[s],esi mov ebx,ds:dword ptr[pbase] shl esi,16 cmp edx,ebp mov ds:dword ptr[sfracf],esi ja LClampHighOrLow1 LClampReentry1: mov ds:dword ptr[t],edx mov esi,ds:dword ptr[s] shl edx,16 mov eax,ds:dword ptr[t] sar esi,16 mov ds:dword ptr[tfracf],edx sar eax,16 add esi,ebx imul eax,ds:dword ptr[_cachewidth] add esi,eax cmp ecx,8 jna LLastSegment LNotLastSegment: fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[snext] fistp ds:dword ptr[tnext] mov eax,ds:dword ptr[snext] mov edx,ds:dword ptr[tnext] sub ecx,8 mov ebp,ds:dword ptr[_sadjust] push ecx mov ecx,ds:dword ptr[_tadjust] add ebp,eax add ecx,edx mov eax,ds:dword ptr[_bbextents] mov edx,ds:dword ptr[_bbextentt] cmp ebp,2048 jl LClampLow2 cmp ebp,eax ja LClampHigh2 LClampReentry2: cmp ecx,2048 jl LClampLow3 cmp ecx,edx ja LClampHigh3 LClampReentry3: mov ds:dword ptr[snext],ebp mov ds:dword ptr[tnext],ecx sub ebp,ds:dword ptr[s] sub ecx,ds:dword ptr[t] mov eax,ecx mov edx,ebp sar edx,19 mov ebx,ds:dword ptr[_cachewidth] sar eax,19 jz LIsZero imul eax,ebx LIsZero: add eax,edx mov edx,ds:dword ptr[tfracf] mov ds:dword ptr[advancetable+4],eax add eax,ebx shl ebp,13 mov ds:dword ptr[sstep],ebp mov ebx,ds:dword ptr[sfracf] shl ecx,13 mov ds:dword ptr[advancetable],eax mov ds:dword ptr[tstep],ecx mov ecx,ds:dword ptr[pz] mov ebp,ds:dword ptr[izi] cmp bp,ds:word ptr[ecx] jl Lp1 mov al,ds:byte ptr[esi] cmp al,255 jz Lp1 mov ds:word ptr[ecx],bp mov ds:byte ptr[edi],al Lp1: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[2+ecx] jl Lp2 mov al,ds:byte ptr[esi] cmp al,255 jz Lp2 mov ds:word ptr[2+ecx],bp mov ds:byte ptr[1+edi],al Lp2: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[4+ecx] jl Lp3 mov al,ds:byte ptr[esi] cmp al,255 jz Lp3 mov ds:word ptr[4+ecx],bp mov ds:byte ptr[2+edi],al Lp3: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[6+ecx] jl Lp4 mov al,ds:byte ptr[esi] cmp al,255 jz Lp4 mov ds:word ptr[6+ecx],bp mov ds:byte ptr[3+edi],al Lp4: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[8+ecx] jl Lp5 mov al,ds:byte ptr[esi] cmp al,255 jz Lp5 mov ds:word ptr[8+ecx],bp mov ds:byte ptr[4+edi],al Lp5: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] pop eax cmp eax,8 ja LSetupNotLast2 dec eax jz LFDIVInFlight2 mov ds:dword ptr[spancountminus1],eax fild ds:dword ptr[spancountminus1] fld ds:dword ptr[_d_zistepu] fmul st(0),st(1) fld ds:dword ptr[_d_tdivzstepu] fmul st(0),st(2) fxch st(1) faddp st(3),st(0) fxch st(1) fmul ds:dword ptr[_d_sdivzstepu] fxch st(1) faddp st(3),st(0) fld ds:dword ptr[fp_64k] fxch st(1) faddp st(4),st(0) fdiv st(0),st(1) jmp LFDIVInFlight2 align 4 LSetupNotLast2: fadd ds:dword ptr[zi8stepu] fxch st(2) fadd ds:dword ptr[sdivz8stepu] fxch st(2) fld ds:dword ptr[tdivz8stepu] faddp st(2),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) LFDIVInFlight2: push eax cmp bp,ds:word ptr[10+ecx] jl Lp6 mov al,ds:byte ptr[esi] cmp al,255 jz Lp6 mov ds:word ptr[10+ecx],bp mov ds:byte ptr[5+edi],al Lp6: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[12+ecx] jl Lp7 mov al,ds:byte ptr[esi] cmp al,255 jz Lp7 mov ds:word ptr[12+ecx],bp mov ds:byte ptr[6+edi],al Lp7: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[14+ecx] jl Lp8 mov al,ds:byte ptr[esi] cmp al,255 jz Lp8 mov ds:word ptr[14+ecx],bp mov ds:byte ptr[7+edi],al Lp8: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] add edi,8 add ecx,16 mov ds:dword ptr[tfracf],edx mov edx,ds:dword ptr[snext] mov ds:dword ptr[sfracf],ebx mov ebx,ds:dword ptr[tnext] mov ds:dword ptr[s],edx mov ds:dword ptr[t],ebx mov ds:dword ptr[pz],ecx mov ds:dword ptr[izi],ebp pop ecx cmp ecx,8 ja LNotLastSegment LLastSegment: test ecx,ecx jz LNoSteps fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[snext] fistp ds:dword ptr[tnext] mov ebx,ds:dword ptr[_tadjust] mov eax,ds:dword ptr[_sadjust] add eax,ds:dword ptr[snext] add ebx,ds:dword ptr[tnext] mov ebp,ds:dword ptr[_bbextents] mov edx,ds:dword ptr[_bbextentt] cmp eax,2048 jl LClampLow4 cmp eax,ebp ja LClampHigh4 LClampReentry4: mov ds:dword ptr[snext],eax cmp ebx,2048 jl LClampLow5 cmp ebx,edx ja LClampHigh5 LClampReentry5: cmp ecx,1 je LOnlyOneStep sub eax,ds:dword ptr[s] sub ebx,ds:dword ptr[t] add eax,eax add ebx,ebx imul ds:dword ptr[reciprocal_table-8+ecx*4] mov ebp,edx mov eax,ebx imul ds:dword ptr[reciprocal_table-8+ecx*4] LSetEntryvec: mov ebx,ds:dword ptr[spr8entryvec_table+ecx*4] mov eax,edx push ebx mov ecx,ebp sar ecx,16 mov ebx,ds:dword ptr[_cachewidth] sar edx,16 jz LIsZeroLast imul edx,ebx LIsZeroLast: add edx,ecx mov ecx,ds:dword ptr[tfracf] mov ds:dword ptr[advancetable+4],edx add edx,ebx shl ebp,16 mov ebx,ds:dword ptr[sfracf] shl eax,16 mov ds:dword ptr[advancetable],edx mov ds:dword ptr[tstep],eax mov ds:dword ptr[sstep],ebp mov edx,ecx mov ecx,ds:dword ptr[pz] mov ebp,ds:dword ptr[izi] ret LNoSteps: mov ecx,ds:dword ptr[pz] sub edi,7 sub ecx,14 jmp LEndSpan LOnlyOneStep: sub eax,ds:dword ptr[s] sub ebx,ds:dword ptr[t] mov ebp,eax mov edx,ebx jmp LSetEntryvec public Spr8Entry2_8 Spr8Entry2_8: sub edi,6 sub ecx,12 mov al,ds:byte ptr[esi] jmp LLEntry2_8 public Spr8Entry3_8 Spr8Entry3_8: sub edi,5 sub ecx,10 jmp LLEntry3_8 public Spr8Entry4_8 Spr8Entry4_8: sub edi,4 sub ecx,8 jmp LLEntry4_8 public Spr8Entry5_8 Spr8Entry5_8: sub edi,3 sub ecx,6 jmp LLEntry5_8 public Spr8Entry6_8 Spr8Entry6_8: sub edi,2 sub ecx,4 jmp LLEntry6_8 public Spr8Entry7_8 Spr8Entry7_8: dec edi sub ecx,2 jmp LLEntry7_8 public Spr8Entry8_8 Spr8Entry8_8: cmp bp,ds:word ptr[ecx] jl Lp9 mov al,ds:byte ptr[esi] cmp al,255 jz Lp9 mov ds:word ptr[ecx],bp mov ds:byte ptr[edi],al Lp9: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry7_8: cmp bp,ds:word ptr[2+ecx] jl Lp10 mov al,ds:byte ptr[esi] cmp al,255 jz Lp10 mov ds:word ptr[2+ecx],bp mov ds:byte ptr[1+edi],al Lp10: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry6_8: cmp bp,ds:word ptr[4+ecx] jl Lp11 mov al,ds:byte ptr[esi] cmp al,255 jz Lp11 mov ds:word ptr[4+ecx],bp mov ds:byte ptr[2+edi],al Lp11: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry5_8: cmp bp,ds:word ptr[6+ecx] jl Lp12 mov al,ds:byte ptr[esi] cmp al,255 jz Lp12 mov ds:word ptr[6+ecx],bp mov ds:byte ptr[3+edi],al Lp12: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry4_8: cmp bp,ds:word ptr[8+ecx] jl Lp13 mov al,ds:byte ptr[esi] cmp al,255 jz Lp13 mov ds:word ptr[8+ecx],bp mov ds:byte ptr[4+edi],al Lp13: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry3_8: cmp bp,ds:word ptr[10+ecx] jl Lp14 mov al,ds:byte ptr[esi] cmp al,255 jz Lp14 mov ds:word ptr[10+ecx],bp mov ds:byte ptr[5+edi],al Lp14: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry2_8: cmp bp,ds:word ptr[12+ecx] jl Lp15 mov al,ds:byte ptr[esi] cmp al,255 jz Lp15 mov ds:word ptr[12+ecx],bp mov ds:byte ptr[6+edi],al Lp15: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LEndSpan: cmp bp,ds:word ptr[14+ecx] jl Lp16 mov al,ds:byte ptr[esi] cmp al,255 jz Lp16 mov ds:word ptr[14+ecx],bp mov ds:byte ptr[7+edi],al Lp16: fstp st(0) fstp st(0) fstp st(0) pop ebx LNextSpan: add ebx,12 mov ecx,ds:dword ptr[8+ebx] cmp ecx,0 jg LSpanLoop jz LNextSpan pop ebx pop esi pop edi pop ebp ret _TEXT ENDS END engine/h2shared/masm/d_spr8t.asm000066400000000000000000000445461444734033100170650ustar00rootroot00000000000000; ; d_spr8t.asm -- for MASM. ; x86 assembly-language horizontal 8-bpp sprite span-drawing code. ; with translucency handling, #1. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _d_zistepu:dword externdef _d_pzbuffer:dword externdef _d_zistepv:dword externdef _d_zrowbytes:dword externdef _d_ziorigin:dword externdef _d_sdivzstepu:dword externdef _d_tdivzstepu:dword externdef _d_sdivzstepv:dword externdef _d_tdivzstepv:dword externdef _d_sdivzorigin:dword externdef _d_tdivzorigin:dword externdef _sadjust:dword externdef _tadjust:dword externdef _bbextents:dword externdef _bbextentt:dword externdef _cacheblock:dword externdef _d_viewbuffer:dword externdef _cachewidth:dword externdef _d_scantable:dword externdef _mainTransTable:dword ; externs from ASM-only code externdef fp_1m:dword externdef fp_1m_minus_1:dword externdef fp_8:dword externdef fp_16:dword externdef fp_64k:dword externdef fp_64kx64k:dword externdef izi:dword externdef izistep:dword externdef sstep:dword externdef tstep:dword externdef advancetable:dword externdef spr8Tentryvec_table:dword externdef reciprocal_table:dword externdef reciprocal_table_16:dword externdef pbase:dword externdef s:dword externdef t:dword externdef sfracf:dword externdef tfracf:dword externdef snext:dword externdef tnext:dword externdef spancountminus1:dword externdef zi16stepu:dword externdef sdivz16stepu:dword externdef tdivz16stepu:dword externdef zi8stepu:dword externdef sdivz8stepu:dword externdef tdivz8stepu:dword externdef pz:dword _TEXT SEGMENT LClampHigh0: mov esi,ds:dword ptr[_bbextents] jmp LClampReentry0 LClampHighOrLow0: jg LClampHigh0 xor esi,esi jmp LClampReentry0 LClampHigh1: mov edx,ds:dword ptr[_bbextentt] jmp LClampReentry1 LClampHighOrLow1: jg LClampHigh1 xor edx,edx jmp LClampReentry1 LClampLow2: mov ebp,2048 jmp LClampReentry2 LClampHigh2: mov ebp,ds:dword ptr[_bbextents] jmp LClampReentry2 LClampLow3: mov ecx,2048 jmp LClampReentry3 LClampHigh3: mov ecx,ds:dword ptr[_bbextentt] jmp LClampReentry3 LClampLow4: mov eax,2048 jmp LClampReentry4 LClampHigh4: mov eax,ds:dword ptr[_bbextents] jmp LClampReentry4 LClampLow5: mov ebx,2048 jmp LClampReentry5 LClampHigh5: mov ebx,ds:dword ptr[_bbextentt] jmp LClampReentry5 align 4 public _D_SpriteSpansStartT _D_SpriteSpansStartT: public _D_SpriteDrawSpansT _D_SpriteDrawSpansT: push ebp push edi push esi push ebx fld ds:dword ptr[_d_sdivzstepu] fmul ds:dword ptr[fp_8] mov edx,ds:dword ptr[_cacheblock] fld ds:dword ptr[_d_tdivzstepu] fmul ds:dword ptr[fp_8] mov ebx,ds:dword ptr[4+16+esp] fld ds:dword ptr[_d_zistepu] fmul ds:dword ptr[fp_8] mov ds:dword ptr[pbase],edx fld ds:dword ptr[_d_zistepu] fmul ds:dword ptr[fp_64kx64k] fxch st(3) fstp ds:dword ptr[sdivz8stepu] fstp ds:dword ptr[zi8stepu] fstp ds:dword ptr[tdivz8stepu] fistp ds:dword ptr[izistep] mov eax,ds:dword ptr[izistep] ror eax,16 mov ecx,ds:dword ptr[8+ebx] mov ds:dword ptr[izistep],eax cmp ecx,0 jle LNextSpan LSpanLoop: fild ds:dword ptr[4+ebx] fild ds:dword ptr[0+ebx] fld st(1) fmul ds:dword ptr[_d_sdivzstepv] fld st(1) fmul ds:dword ptr[_d_sdivzstepu] fld st(2) fmul ds:dword ptr[_d_tdivzstepu] fxch st(1) faddp st(2),st(0) fxch st(1) fld st(3) fmul ds:dword ptr[_d_tdivzstepv] fxch st(1) fadd ds:dword ptr[_d_sdivzorigin] fxch st(4) fmul ds:dword ptr[_d_zistepv] fxch st(1) faddp st(2),st(0) fxch st(2) fmul ds:dword ptr[_d_zistepu] fxch st(1) fadd ds:dword ptr[_d_tdivzorigin] fxch st(2) faddp st(1),st(0) fld ds:dword ptr[fp_64k] fxch st(1) fadd ds:dword ptr[_d_ziorigin] fld st(0) fmul ds:dword ptr[fp_64kx64k] fxch st(1) fdiv st(2),st(0) fxch st(1) fistp ds:dword ptr[izi] mov ebp,ds:dword ptr[izi] ror ebp,16 mov eax,ds:dword ptr[4+ebx] mov ds:dword ptr[izi],ebp mov ebp,ds:dword ptr[0+ebx] imul ds:dword ptr[_d_zrowbytes] shl ebp,1 add eax,ds:dword ptr[_d_pzbuffer] add eax,ebp mov ds:dword ptr[pz],eax mov ebp,ds:dword ptr[_d_viewbuffer] mov eax,ds:dword ptr[4+ebx] push ebx mov edx,ds:dword ptr[_tadjust] mov esi,ds:dword ptr[_sadjust] mov edi,ds:dword ptr[_d_scantable+eax*4] add edi,ebp mov ebp,ds:dword ptr[0+ebx] add edi,ebp cmp ecx,8 ja LSetupNotLast1 dec ecx jz LCleanup1 mov ds:dword ptr[spancountminus1],ecx fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] fild ds:dword ptr[spancountminus1] fld ds:dword ptr[_d_tdivzstepu] fld ds:dword ptr[_d_zistepu] fmul st(0),st(2) fxch st(1) fmul st(0),st(2) fxch st(2) fmul ds:dword ptr[_d_sdivzstepu] fxch st(1) faddp st(3),st(0) fxch st(1) faddp st(3),st(0) faddp st(3),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) jmp LFDIVInFlight1 LCleanup1: fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] jmp LFDIVInFlight1 align 4 LSetupNotLast1: fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] fadd ds:dword ptr[zi8stepu] fxch st(2) fadd ds:dword ptr[sdivz8stepu] fxch st(2) fld ds:dword ptr[tdivz8stepu] faddp st(2),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) LFDIVInFlight1: add esi,ds:dword ptr[s] add edx,ds:dword ptr[t] mov ebx,ds:dword ptr[_bbextents] mov ebp,ds:dword ptr[_bbextentt] cmp esi,ebx ja LClampHighOrLow0 LClampReentry0: mov ds:dword ptr[s],esi mov ebx,ds:dword ptr[pbase] shl esi,16 cmp edx,ebp mov ds:dword ptr[sfracf],esi ja LClampHighOrLow1 LClampReentry1: mov ds:dword ptr[t],edx mov esi,ds:dword ptr[s] shl edx,16 mov eax,ds:dword ptr[t] sar esi,16 mov ds:dword ptr[tfracf],edx sar eax,16 add esi,ebx imul eax,ds:dword ptr[_cachewidth] add esi,eax cmp ecx,8 jna LLastSegment LNotLastSegment: fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[snext] fistp ds:dword ptr[tnext] mov eax,ds:dword ptr[snext] mov edx,ds:dword ptr[tnext] sub ecx,8 mov ebp,ds:dword ptr[_sadjust] push ecx mov ecx,ds:dword ptr[_tadjust] add ebp,eax add ecx,edx mov eax,ds:dword ptr[_bbextents] mov edx,ds:dword ptr[_bbextentt] cmp ebp,2048 jl LClampLow2 cmp ebp,eax ja LClampHigh2 LClampReentry2: cmp ecx,2048 jl LClampLow3 cmp ecx,edx ja LClampHigh3 LClampReentry3: mov ds:dword ptr[snext],ebp mov ds:dword ptr[tnext],ecx sub ebp,ds:dword ptr[s] sub ecx,ds:dword ptr[t] mov eax,ecx mov edx,ebp sar edx,19 mov ebx,ds:dword ptr[_cachewidth] sar eax,19 jz LIsZero imul eax,ebx LIsZero: add eax,edx mov edx,ds:dword ptr[tfracf] mov ds:dword ptr[advancetable+4],eax add eax,ebx shl ebp,13 mov ds:dword ptr[sstep],ebp mov ebx,ds:dword ptr[sfracf] shl ecx,13 mov ds:dword ptr[advancetable],eax mov ds:dword ptr[tstep],ecx mov ecx,ds:dword ptr[pz] mov ebp,ds:dword ptr[izi] cmp bp,ds:word ptr[ecx] jl Lp1 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp1 ; mov ds:word ptr[ecx],bp ; trans stuff mov al,ds:byte ptr[edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch1: mov ds:byte ptr[edi],ah Lp1: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[2+ecx] jl Lp2 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp2 ; mov ds:word ptr[2+ecx],bp ; trans stuff mov al,ds:byte ptr[1+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch2: mov ds:byte ptr[1+edi],ah Lp2: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[4+ecx] jl Lp3 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp3 ; mov ds:word ptr[4+ecx],bp ; trans stuff mov al,ds:byte ptr[2+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch3: mov ds:byte ptr[2+edi],ah Lp3: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[6+ecx] jl Lp4 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp4 ; mov ds:word ptr[6+ecx],bp ; trans stuff mov al,ds:byte ptr[3+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch4: mov ds:byte ptr[3+edi],ah Lp4: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[8+ecx] jl Lp5 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp5 ; mov ds:word ptr[8+ecx],bp ; trans stuff mov al,ds:byte ptr[4+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch5: mov ds:byte ptr[4+edi],ah Lp5: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] pop eax cmp eax,8 ja LSetupNotLast2 dec eax jz LFDIVInFlight2 mov ds:dword ptr[spancountminus1],eax fild ds:dword ptr[spancountminus1] fld ds:dword ptr[_d_zistepu] fmul st(0),st(1) fld ds:dword ptr[_d_tdivzstepu] fmul st(0),st(2) fxch st(1) faddp st(3),st(0) fxch st(1) fmul ds:dword ptr[_d_sdivzstepu] fxch st(1) faddp st(3),st(0) fld ds:dword ptr[fp_64k] fxch st(1) faddp st(4),st(0) fdiv st(0),st(1) jmp LFDIVInFlight2 align 4 LSetupNotLast2: fadd ds:dword ptr[zi8stepu] fxch st(2) fadd ds:dword ptr[sdivz8stepu] fxch st(2) fld ds:dword ptr[tdivz8stepu] faddp st(2),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) LFDIVInFlight2: push eax cmp bp,ds:word ptr[10+ecx] jl Lp6 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp6 ; mov ds:word ptr[10+ecx],bp ; trans stuff mov al,ds:byte ptr[5+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch6: mov ds:byte ptr[5+edi],ah Lp6: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[12+ecx] jl Lp7 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp7 ; mov ds:word ptr[12+ecx],bp ; trans stuff mov al,ds:byte ptr[6+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch7: mov ds:byte ptr[6+edi],ah Lp7: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[14+ecx] jl Lp8 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp8 ; mov ds:word ptr[14+ecx],bp ; trans stuff mov al,ds:byte ptr[7+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch8: mov ds:byte ptr[7+edi],ah Lp8: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] add edi,8 add ecx,16 mov ds:dword ptr[tfracf],edx mov edx,ds:dword ptr[snext] mov ds:dword ptr[sfracf],ebx mov ebx,ds:dword ptr[tnext] mov ds:dword ptr[s],edx mov ds:dword ptr[t],ebx mov ds:dword ptr[pz],ecx mov ds:dword ptr[izi],ebp pop ecx cmp ecx,8 ja LNotLastSegment LLastSegment: test ecx,ecx jz LNoSteps fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[snext] fistp ds:dword ptr[tnext] mov ebx,ds:dword ptr[_tadjust] mov eax,ds:dword ptr[_sadjust] add eax,ds:dword ptr[snext] add ebx,ds:dword ptr[tnext] mov ebp,ds:dword ptr[_bbextents] mov edx,ds:dword ptr[_bbextentt] cmp eax,2048 jl LClampLow4 cmp eax,ebp ja LClampHigh4 LClampReentry4: mov ds:dword ptr[snext],eax cmp ebx,2048 jl LClampLow5 cmp ebx,edx ja LClampHigh5 LClampReentry5: cmp ecx,1 je LOnlyOneStep sub eax,ds:dword ptr[s] sub ebx,ds:dword ptr[t] add eax,eax add ebx,ebx imul ds:dword ptr[reciprocal_table-8+ecx*4] mov ebp,edx mov eax,ebx imul ds:dword ptr[reciprocal_table-8+ecx*4] LSetEntryvec: mov ebx,ds:dword ptr[spr8Tentryvec_table+ecx*4] mov eax,edx push ebx mov ecx,ebp sar ecx,16 mov ebx,ds:dword ptr[_cachewidth] sar edx,16 jz LIsZeroLast imul edx,ebx LIsZeroLast: add edx,ecx mov ecx,ds:dword ptr[tfracf] mov ds:dword ptr[advancetable+4],edx add edx,ebx shl ebp,16 mov ebx,ds:dword ptr[sfracf] shl eax,16 mov ds:dword ptr[advancetable],edx mov ds:dword ptr[tstep],eax mov ds:dword ptr[sstep],ebp mov edx,ecx mov ecx,ds:dword ptr[pz] mov ebp,ds:dword ptr[izi] ret LNoSteps: mov ecx,ds:dword ptr[pz] sub edi,7 sub ecx,14 jmp LEndSpan LOnlyOneStep: sub eax,ds:dword ptr[s] sub ebx,ds:dword ptr[t] mov ebp,eax mov edx,ebx jmp LSetEntryvec public Spr8Entry2_8T Spr8Entry2_8T: sub edi,6 sub ecx,12 mov al,ds:byte ptr[esi] jmp LLEntry2_8 public Spr8Entry3_8T Spr8Entry3_8T: sub edi,5 sub ecx,10 jmp LLEntry3_8 public Spr8Entry4_8T Spr8Entry4_8T: sub edi,4 sub ecx,8 jmp LLEntry4_8 public Spr8Entry5_8T Spr8Entry5_8T: sub edi,3 sub ecx,6 jmp LLEntry5_8 public Spr8Entry6_8T Spr8Entry6_8T: sub edi,2 sub ecx,4 jmp LLEntry6_8 public Spr8Entry7_8T Spr8Entry7_8T: dec edi sub ecx,2 jmp LLEntry7_8 public Spr8Entry8_8T Spr8Entry8_8T: cmp bp,ds:word ptr[ecx] jl Lp9 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp9 ; mov ds:word ptr[ecx],bp ; trans stuff mov al,ds:byte ptr[edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch9: mov ds:byte ptr[edi],ah Lp9: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry7_8: cmp bp,ds:word ptr[2+ecx] jl Lp10 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp10 ; mov ds:word ptr[2+ecx],bp ; trans stuff mov al,ds:byte ptr[1+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch10: mov ds:byte ptr[1+edi],ah Lp10: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry6_8: cmp bp,ds:word ptr[4+ecx] jl Lp11 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp11 ; mov ds:word ptr[4+ecx],bp ; trans stuff mov al,ds:byte ptr[2+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch11: mov ds:byte ptr[2+edi],ah Lp11: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry5_8: cmp bp,ds:word ptr[6+ecx] jl Lp12 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp12 ; mov ds:word ptr[6+ecx],bp ; trans stuff mov al,ds:byte ptr[3+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch12: mov ds:byte ptr[3+edi],ah Lp12: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry4_8: cmp bp,ds:word ptr[8+ecx] jl Lp13 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp13 ; mov ds:word ptr[8+ecx],bp ; trans stuff mov al,ds:byte ptr[4+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch13: mov ds:byte ptr[4+edi],ah Lp13: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry3_8: cmp bp,ds:word ptr[10+ecx] jl Lp14 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp14 ; mov ds:word ptr[10+ecx],bp ; trans stuff mov al,ds:byte ptr[5+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch14: mov ds:byte ptr[5+edi],ah Lp14: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry2_8: cmp bp,ds:word ptr[12+ecx] jl Lp15 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp15 ; mov ds:word ptr[12+ecx],bp ; trans stuff mov al,ds:byte ptr[6+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch15: mov ds:byte ptr[6+edi],ah Lp15: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LEndSpan: cmp bp,ds:word ptr[14+ecx] jl Lp16 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp16 ; mov ds:word ptr[14+ecx],bp ; trans stuff mov al,ds:byte ptr[7+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch16: mov ds:byte ptr[7+edi],ah Lp16: fstp st(0) fstp st(0) fstp st(0) pop ebx LNextSpan: add ebx,12 mov ecx,ds:dword ptr[8+ebx] cmp ecx,0 jg LSpanLoop jz LNextSpan pop ebx pop esi pop edi pop ebp ret public _D_SpriteSpansEndT _D_SpriteSpansEndT: _TEXT ENDS _DATA SEGMENT align 4 LPatchTable: dd TranPatch1-4 dd TranPatch2-4 dd TranPatch3-4 dd TranPatch4-4 dd TranPatch5-4 dd TranPatch6-4 dd TranPatch7-4 dd TranPatch8-4 dd TranPatch9-4 dd TranPatch10-4 dd TranPatch11-4 dd TranPatch12-4 dd TranPatch13-4 dd TranPatch14-4 dd TranPatch15-4 dd TranPatch16-4 _DATA ENDS _TEXT SEGMENT align 4 public _R_TranPatch4 _R_TranPatch4: push ebx mov eax,ds:dword ptr[_mainTransTable] mov ebx,offset LPatchTable mov ecx,16 LPatchLoop: mov edx,ds:dword ptr[ebx] add ebx,4 mov ds:dword ptr[edx],eax dec ecx jnz LPatchLoop pop ebx ret _TEXT ENDS END engine/h2shared/masm/d_spr8t2.asm000066400000000000000000000456531444734033100171470ustar00rootroot00000000000000; ; d_spr8t2.asm -- for MASM. ; x86 assembly-language horizontal 8-bpp sprite span-drawing code. ; with translucency handling, #2. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _d_zistepu:dword externdef _d_pzbuffer:dword externdef _d_zistepv:dword externdef _d_zrowbytes:dword externdef _d_ziorigin:dword externdef _d_sdivzstepu:dword externdef _d_tdivzstepu:dword externdef _d_sdivzstepv:dword externdef _d_tdivzstepv:dword externdef _d_sdivzorigin:dword externdef _d_tdivzorigin:dword externdef _sadjust:dword externdef _tadjust:dword externdef _bbextents:dword externdef _bbextentt:dword externdef _cacheblock:dword externdef _d_viewbuffer:dword externdef _cachewidth:dword externdef _d_scantable:dword externdef _mainTransTable:dword ; externs from ASM-only code externdef fp_1m:dword externdef fp_1m_minus_1:dword externdef fp_8:dword externdef fp_16:dword externdef fp_64k:dword externdef fp_64kx64k:dword externdef izi:dword externdef izistep:dword externdef sstep:dword externdef tstep:dword externdef advancetable:dword externdef spr8T2entryvec_table:dword externdef reciprocal_table:dword externdef reciprocal_table_16:dword externdef pbase:dword externdef s:dword externdef t:dword externdef sfracf:dword externdef tfracf:dword externdef snext:dword externdef tnext:dword externdef spancountminus1:dword externdef zi16stepu:dword externdef sdivz16stepu:dword externdef tdivz16stepu:dword externdef zi8stepu:dword externdef sdivz8stepu:dword externdef tdivz8stepu:dword externdef pz:dword _TEXT SEGMENT LClampHigh0: mov esi,ds:dword ptr[_bbextents] jmp LClampReentry0 LClampHighOrLow0: jg LClampHigh0 xor esi,esi jmp LClampReentry0 LClampHigh1: mov edx,ds:dword ptr[_bbextentt] jmp LClampReentry1 LClampHighOrLow1: jg LClampHigh1 xor edx,edx jmp LClampReentry1 LClampLow2: mov ebp,2048 jmp LClampReentry2 LClampHigh2: mov ebp,ds:dword ptr[_bbextents] jmp LClampReentry2 LClampLow3: mov ecx,2048 jmp LClampReentry3 LClampHigh3: mov ecx,ds:dword ptr[_bbextentt] jmp LClampReentry3 LClampLow4: mov eax,2048 jmp LClampReentry4 LClampHigh4: mov eax,ds:dword ptr[_bbextents] jmp LClampReentry4 LClampLow5: mov ebx,2048 jmp LClampReentry5 LClampHigh5: mov ebx,ds:dword ptr[_bbextentt] jmp LClampReentry5 align 4 public _D_SpriteSpansStartT2 _D_SpriteSpansStartT2: public _D_SpriteDrawSpansT2 _D_SpriteDrawSpansT2: push ebp push edi push esi push ebx fld ds:dword ptr[_d_sdivzstepu] fmul ds:dword ptr[fp_8] mov edx,ds:dword ptr[_cacheblock] fld ds:dword ptr[_d_tdivzstepu] fmul ds:dword ptr[fp_8] mov ebx,ds:dword ptr[4+16+esp] fld ds:dword ptr[_d_zistepu] fmul ds:dword ptr[fp_8] mov ds:dword ptr[pbase],edx fld ds:dword ptr[_d_zistepu] fmul ds:dword ptr[fp_64kx64k] fxch st(3) fstp ds:dword ptr[sdivz8stepu] fstp ds:dword ptr[zi8stepu] fstp ds:dword ptr[tdivz8stepu] fistp ds:dword ptr[izistep] mov eax,ds:dword ptr[izistep] ror eax,16 mov ecx,ds:dword ptr[8+ebx] mov ds:dword ptr[izistep],eax cmp ecx,0 jle LNextSpan LSpanLoop: fild ds:dword ptr[4+ebx] fild ds:dword ptr[0+ebx] fld st(1) fmul ds:dword ptr[_d_sdivzstepv] fld st(1) fmul ds:dword ptr[_d_sdivzstepu] fld st(2) fmul ds:dword ptr[_d_tdivzstepu] fxch st(1) faddp st(2),st(0) fxch st(1) fld st(3) fmul ds:dword ptr[_d_tdivzstepv] fxch st(1) fadd ds:dword ptr[_d_sdivzorigin] fxch st(4) fmul ds:dword ptr[_d_zistepv] fxch st(1) faddp st(2),st(0) fxch st(2) fmul ds:dword ptr[_d_zistepu] fxch st(1) fadd ds:dword ptr[_d_tdivzorigin] fxch st(2) faddp st(1),st(0) fld ds:dword ptr[fp_64k] fxch st(1) fadd ds:dword ptr[_d_ziorigin] fld st(0) fmul ds:dword ptr[fp_64kx64k] fxch st(1) fdiv st(2),st(0) fxch st(1) fistp ds:dword ptr[izi] mov ebp,ds:dword ptr[izi] ror ebp,16 mov eax,ds:dword ptr[4+ebx] mov ds:dword ptr[izi],ebp mov ebp,ds:dword ptr[0+ebx] imul ds:dword ptr[_d_zrowbytes] shl ebp,1 add eax,ds:dword ptr[_d_pzbuffer] add eax,ebp mov ds:dword ptr[pz],eax mov ebp,ds:dword ptr[_d_viewbuffer] mov eax,ds:dword ptr[4+ebx] push ebx mov edx,ds:dword ptr[_tadjust] mov esi,ds:dword ptr[_sadjust] mov edi,ds:dword ptr[_d_scantable+eax*4] add edi,ebp mov ebp,ds:dword ptr[0+ebx] add edi,ebp cmp ecx,8 ja LSetupNotLast1 dec ecx jz LCleanup1 mov ds:dword ptr[spancountminus1],ecx fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] fild ds:dword ptr[spancountminus1] fld ds:dword ptr[_d_tdivzstepu] fld ds:dword ptr[_d_zistepu] fmul st(0),st(2) fxch st(1) fmul st(0),st(2) fxch st(2) fmul ds:dword ptr[_d_sdivzstepu] fxch st(1) faddp st(3),st(0) fxch st(1) faddp st(3),st(0) faddp st(3),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) jmp LFDIVInFlight1 LCleanup1: fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] jmp LFDIVInFlight1 align 4 LSetupNotLast1: fxch st(1) fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[s] fistp ds:dword ptr[t] fadd ds:dword ptr[zi8stepu] fxch st(2) fadd ds:dword ptr[sdivz8stepu] fxch st(2) fld ds:dword ptr[tdivz8stepu] faddp st(2),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) LFDIVInFlight1: add esi,ds:dword ptr[s] add edx,ds:dword ptr[t] mov ebx,ds:dword ptr[_bbextents] mov ebp,ds:dword ptr[_bbextentt] cmp esi,ebx ja LClampHighOrLow0 LClampReentry0: mov ds:dword ptr[s],esi mov ebx,ds:dword ptr[pbase] shl esi,16 cmp edx,ebp mov ds:dword ptr[sfracf],esi ja LClampHighOrLow1 LClampReentry1: mov ds:dword ptr[t],edx mov esi,ds:dword ptr[s] shl edx,16 mov eax,ds:dword ptr[t] sar esi,16 mov ds:dword ptr[tfracf],edx sar eax,16 add esi,ebx imul eax,ds:dword ptr[_cachewidth] add esi,eax cmp ecx,8 jna LLastSegment LNotLastSegment: fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[snext] fistp ds:dword ptr[tnext] mov eax,ds:dword ptr[snext] mov edx,ds:dword ptr[tnext] sub ecx,8 mov ebp,ds:dword ptr[_sadjust] push ecx mov ecx,ds:dword ptr[_tadjust] add ebp,eax add ecx,edx mov eax,ds:dword ptr[_bbextents] mov edx,ds:dword ptr[_bbextentt] cmp ebp,2048 jl LClampLow2 cmp ebp,eax ja LClampHigh2 LClampReentry2: cmp ecx,2048 jl LClampLow3 cmp ecx,edx ja LClampHigh3 LClampReentry3: mov ds:dword ptr[snext],ebp mov ds:dword ptr[tnext],ecx sub ebp,ds:dword ptr[s] sub ecx,ds:dword ptr[t] mov eax,ecx mov edx,ebp sar edx,19 mov ebx,ds:dword ptr[_cachewidth] sar eax,19 jz LIsZero imul eax,ebx LIsZero: add eax,edx mov edx,ds:dword ptr[tfracf] mov ds:dword ptr[advancetable+4],eax add eax,ebx shl ebp,13 mov ds:dword ptr[sstep],ebp mov ebx,ds:dword ptr[sfracf] shl ecx,13 mov ds:dword ptr[advancetable],eax mov ds:dword ptr[tstep],ecx mov ecx,ds:dword ptr[pz] mov ebp,ds:dword ptr[izi] cmp bp,ds:word ptr[ecx] jl Lp1 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp1 bt ax,8 jnc Skip1 ; mov ds:word ptr[ecx],bp ; trans stuff mov al,ds:byte ptr[edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch1: Skip1: mov ds:byte ptr[edi],ah Lp1: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[2+ecx] jl Lp2 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp2 bt ax,8 jnc Skip2 ; mov ds:word ptr[2+ecx],bp ; trans stuff mov al,ds:byte ptr[1+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch2: Skip2: mov ds:byte ptr[1+edi],ah Lp2: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[4+ecx] jl Lp3 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp3 bt ax,8 jnc Skip3 ; mov ds:word ptr[4+ecx],bp ; trans stuff mov al,ds:byte ptr[2+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch3: Skip3: mov ds:byte ptr[2+edi],ah Lp3: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[6+ecx] jl Lp4 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp4 bt ax,8 jnc Skip4 ; mov ds:word ptr[6+ecx],bp ; trans stuff mov al,ds:byte ptr[3+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch4: Skip4: mov ds:byte ptr[3+edi],ah Lp4: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[8+ecx] jl Lp5 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp5 bt ax,8 jnc Skip5 ; mov ds:word ptr[8+ecx],bp ; trans stuff mov al,ds:byte ptr[4+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch5: Skip5: mov ds:byte ptr[4+edi],ah Lp5: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] pop eax cmp eax,8 ja LSetupNotLast2 dec eax jz LFDIVInFlight2 mov ds:dword ptr[spancountminus1],eax fild ds:dword ptr[spancountminus1] fld ds:dword ptr[_d_zistepu] fmul st(0),st(1) fld ds:dword ptr[_d_tdivzstepu] fmul st(0),st(2) fxch st(1) faddp st(3),st(0) fxch st(1) fmul ds:dword ptr[_d_sdivzstepu] fxch st(1) faddp st(3),st(0) fld ds:dword ptr[fp_64k] fxch st(1) faddp st(4),st(0) fdiv st(0),st(1) jmp LFDIVInFlight2 align 4 LSetupNotLast2: fadd ds:dword ptr[zi8stepu] fxch st(2) fadd ds:dword ptr[sdivz8stepu] fxch st(2) fld ds:dword ptr[tdivz8stepu] faddp st(2),st(0) fld ds:dword ptr[fp_64k] fdiv st(0),st(1) LFDIVInFlight2: push eax cmp bp,ds:word ptr[10+ecx] jl Lp6 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp6 bt ax,8 jnc Skip6 ; mov ds:word ptr[10+ecx],bp ; trans stuff mov al,ds:byte ptr[5+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch6: Skip6: mov ds:byte ptr[5+edi],ah Lp6: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[12+ecx] jl Lp7 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp7 bt ax,8 jnc Skip7 ; mov ds:word ptr[12+ecx],bp ; trans stuff mov al,ds:byte ptr[6+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch7: Skip7: mov ds:byte ptr[6+edi],ah Lp7: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] cmp bp,ds:word ptr[14+ecx] jl Lp8 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp8 bt ax,8 jnc Skip8 ; mov ds:word ptr[14+ecx],bp ; trans stuff mov al,ds:byte ptr[7+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch8: Skip8: mov ds:byte ptr[7+edi],ah Lp8: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] add edi,8 add ecx,16 mov ds:dword ptr[tfracf],edx mov edx,ds:dword ptr[snext] mov ds:dword ptr[sfracf],ebx mov ebx,ds:dword ptr[tnext] mov ds:dword ptr[s],edx mov ds:dword ptr[t],ebx mov ds:dword ptr[pz],ecx mov ds:dword ptr[izi],ebp pop ecx cmp ecx,8 ja LNotLastSegment LLastSegment: test ecx,ecx jz LNoSteps fld st(0) fmul st(0),st(4) fxch st(1) fmul st(0),st(3) fxch st(1) fistp ds:dword ptr[snext] fistp ds:dword ptr[tnext] mov ebx,ds:dword ptr[_tadjust] mov eax,ds:dword ptr[_sadjust] add eax,ds:dword ptr[snext] add ebx,ds:dword ptr[tnext] mov ebp,ds:dword ptr[_bbextents] mov edx,ds:dword ptr[_bbextentt] cmp eax,2048 jl LClampLow4 cmp eax,ebp ja LClampHigh4 LClampReentry4: mov ds:dword ptr[snext],eax cmp ebx,2048 jl LClampLow5 cmp ebx,edx ja LClampHigh5 LClampReentry5: cmp ecx,1 je LOnlyOneStep sub eax,ds:dword ptr[s] sub ebx,ds:dword ptr[t] add eax,eax add ebx,ebx imul ds:dword ptr[reciprocal_table-8+ecx*4] mov ebp,edx mov eax,ebx imul ds:dword ptr[reciprocal_table-8+ecx*4] LSetEntryvec: mov ebx,ds:dword ptr[spr8T2entryvec_table+ecx*4] mov eax,edx push ebx mov ecx,ebp sar ecx,16 mov ebx,ds:dword ptr[_cachewidth] sar edx,16 jz LIsZeroLast imul edx,ebx LIsZeroLast: add edx,ecx mov ecx,ds:dword ptr[tfracf] mov ds:dword ptr[advancetable+4],edx add edx,ebx shl ebp,16 mov ebx,ds:dword ptr[sfracf] shl eax,16 mov ds:dword ptr[advancetable],edx mov ds:dword ptr[tstep],eax mov ds:dword ptr[sstep],ebp mov edx,ecx mov ecx,ds:dword ptr[pz] mov ebp,ds:dword ptr[izi] ret LNoSteps: mov ecx,ds:dword ptr[pz] sub edi,7 sub ecx,14 jmp LEndSpan LOnlyOneStep: sub eax,ds:dword ptr[s] sub ebx,ds:dword ptr[t] mov ebp,eax mov edx,ebx jmp LSetEntryvec public Spr8Entry2_8T2 Spr8Entry2_8T2: sub edi,6 sub ecx,12 mov al,ds:byte ptr[esi] jmp LLEntry2_8 public Spr8Entry3_8T2 Spr8Entry3_8T2: sub edi,5 sub ecx,10 jmp LLEntry3_8 public Spr8Entry4_8T2 Spr8Entry4_8T2: sub edi,4 sub ecx,8 jmp LLEntry4_8 public Spr8Entry5_8T2 Spr8Entry5_8T2: sub edi,3 sub ecx,6 jmp LLEntry5_8 public Spr8Entry6_8T2 Spr8Entry6_8T2: sub edi,2 sub ecx,4 jmp LLEntry6_8 public Spr8Entry7_8T2 Spr8Entry7_8T2: dec edi sub ecx,2 jmp LLEntry7_8 public Spr8Entry8_8T2 Spr8Entry8_8T2: cmp bp,ds:word ptr[ecx] jl Lp9 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp9 bt ax,8 jnc Skip9 ; mov ds:word ptr[ecx],bp ; trans stuff mov al,ds:byte ptr[edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch9: Skip9: mov ds:byte ptr[edi],ah Lp9: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry7_8: cmp bp,ds:word ptr[2+ecx] jl Lp10 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp10 bt ax,8 jnc Skip10 ; mov ds:word ptr[2+ecx],bp ; trans stuff mov al,ds:byte ptr[1+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch10: Skip10: mov ds:byte ptr[1+edi],ah Lp10: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry6_8: cmp bp,ds:word ptr[4+ecx] jl Lp11 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp11 bt ax,8 jnc Skip11 ; mov ds:word ptr[4+ecx],bp ; trans stuff mov al,ds:byte ptr[2+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch11: Skip11: mov ds:byte ptr[2+edi],ah Lp11: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry5_8: cmp bp,ds:word ptr[6+ecx] jl Lp12 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp12 bt ax,8 jnc Skip12 ; mov ds:word ptr[6+ecx],bp ; trans stuff mov al,ds:byte ptr[3+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch12: Skip12: mov ds:byte ptr[3+edi],ah Lp12: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry4_8: cmp bp,ds:word ptr[8+ecx] jl Lp13 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp13 bt ax,8 jnc Skip13 ; mov ds:word ptr[8+ecx],bp ; trans stuff mov al,ds:byte ptr[4+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch13: Skip13: mov ds:byte ptr[4+edi],ah Lp13: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry3_8: cmp bp,ds:word ptr[10+ecx] jl Lp14 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp14 bt ax,8 jnc Skip14 ; mov ds:word ptr[10+ecx],bp ; trans stuff mov al,ds:byte ptr[5+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch14: Skip14: mov ds:byte ptr[5+edi],ah Lp14: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LLEntry2_8: cmp bp,ds:word ptr[12+ecx] jl Lp15 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp15 bt ax,8 jnc Skip15 ; mov ds:word ptr[12+ecx],bp ; trans stuff mov al,ds:byte ptr[6+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch15: Skip15: mov ds:byte ptr[6+edi],ah Lp15: add ebp,ds:dword ptr[izistep] adc ebp,0 add edx,ds:dword ptr[tstep] sbb eax,eax add ebx,ds:dword ptr[sstep] adc esi,ds:dword ptr[advancetable+4+eax*4] LEndSpan: cmp bp,ds:word ptr[14+ecx] jl Lp16 mov ah,ds:byte ptr[esi] cmp ah,255 jz Lp16 bt ax,8 jnc Skip16 ; mov ds:word ptr[14+ecx],bp ; trans stuff mov al,ds:byte ptr[7+edi] and eax, 0ffffh mov ah,ds:byte ptr[12345678h + eax] TranPatch16: Skip16: mov ds:byte ptr[7+edi],ah Lp16: fstp st(0) fstp st(0) fstp st(0) pop ebx LNextSpan: add ebx,12 mov ecx,ds:dword ptr[8+ebx] cmp ecx,0 jg LSpanLoop jz LNextSpan pop ebx pop esi pop edi pop ebp ret public _D_SpriteSpansEndT2 _D_SpriteSpansEndT2: _TEXT ENDS _DATA SEGMENT align 4 LPatchTable: dd TranPatch1-4 dd TranPatch2-4 dd TranPatch3-4 dd TranPatch4-4 dd TranPatch5-4 dd TranPatch6-4 dd TranPatch7-4 dd TranPatch8-4 dd TranPatch9-4 dd TranPatch10-4 dd TranPatch11-4 dd TranPatch12-4 dd TranPatch13-4 dd TranPatch14-4 dd TranPatch15-4 dd TranPatch16-4 _DATA ENDS _TEXT SEGMENT align 4 public _R_TranPatch5 _R_TranPatch5: push ebx mov eax,ds:dword ptr[_mainTransTable] mov ebx,offset LPatchTable mov ecx,16 LPatchLoop: mov edx,ds:dword ptr[ebx] add ebx,4 mov ds:dword ptr[edx],eax dec ecx jnz LPatchLoop pop ebx ret _TEXT ENDS END engine/h2shared/masm/d_varsa.asm000066400000000000000000000135031444734033100171060ustar00rootroot00000000000000; d_varsa.asm -- for MASM. ; rasterization driver global variables ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT _DATA SEGMENT align 4 public _d_sdivzstepu public _d_tdivzstepu public _d_zistepu public _d_sdivzstepv public _d_tdivzstepv public _d_zistepv public _d_sdivzorigin public _d_tdivzorigin public _d_ziorigin _d_sdivzstepu dd 0 _d_tdivzstepu dd 0 _d_zistepu dd 0 _d_sdivzstepv dd 0 _d_tdivzstepv dd 0 _d_zistepv dd 0 _d_sdivzorigin dd 0 _d_tdivzorigin dd 0 _d_ziorigin dd 0 public _sadjust public _tadjust public _bbextents public _bbextentt _sadjust dd 0 _tadjust dd 0 _bbextents dd 0 _bbextentt dd 0 public _cacheblock public _d_viewbuffer public _cachewidth public _d_pzbuffer public _d_zrowbytes public _d_zwidth _cacheblock dd 0 _cachewidth dd 0 _d_viewbuffer dd 0 _d_pzbuffer dd 0 _d_zrowbytes dd 0 _d_zwidth dd 0 public izi izi dd 0 public pbase, s, t, sfracf, tfracf, snext, tnext public spancountminus1, zi16stepu, sdivz16stepu, tdivz16stepu public zi8stepu, sdivz8stepu, tdivz8stepu, pz s dd 0 t dd 0 snext dd 0 tnext dd 0 sfracf dd 0 tfracf dd 0 pbase dd 0 zi8stepu dd 0 sdivz8stepu dd 0 tdivz8stepu dd 0 zi16stepu dd 0 sdivz16stepu dd 0 tdivz16stepu dd 0 spancountminus1 dd 0 pz dd 0 public izistep izistep dd 0 public reciprocal_table_16, entryvec_table_16, entryvec_table_16T reciprocal_table_16 dd 040000000h, 02aaaaaaah, 020000000h dd 019999999h, 015555555h, 012492492h dd 010000000h, 0e38e38eh, 0ccccccch, 0ba2e8bah dd 0aaaaaaah, 09d89d89h, 09249249h, 08888888h externdef Entry2_16:dword externdef Entry3_16:dword externdef Entry4_16:dword externdef Entry5_16:dword externdef Entry6_16:dword externdef Entry7_16:dword externdef Entry8_16:dword externdef Entry9_16:dword externdef Entry10_16:dword externdef Entry11_16:dword externdef Entry12_16:dword externdef Entry13_16:dword externdef Entry14_16:dword externdef Entry15_16:dword externdef Entry16_16:dword entryvec_table_16 dd 0, Entry2_16, Entry3_16, Entry4_16 dd Entry5_16, Entry6_16, Entry7_16, Entry8_16 dd Entry9_16, Entry10_16, Entry11_16, Entry12_16 dd Entry13_16, Entry14_16, Entry15_16, Entry16_16 externdef Entry2_16T:dword externdef Entry3_16T:dword externdef Entry4_16T:dword externdef Entry5_16T:dword externdef Entry6_16T:dword externdef Entry7_16T:dword externdef Entry8_16T:dword externdef Entry9_16T:dword externdef Entry10_16T:dword externdef Entry11_16T:dword externdef Entry12_16T:dword externdef Entry13_16T:dword externdef Entry14_16T:dword externdef Entry15_16T:dword externdef Entry16_16T:dword entryvec_table_16T dd 0, Entry2_16T, Entry3_16T, Entry4_16T dd Entry5_16T, Entry6_16T, Entry7_16T, Entry8_16T dd Entry9_16T, Entry10_16T, Entry11_16T, Entry12_16T dd Entry13_16T, Entry14_16T, Entry15_16T, Entry16_16T public DP_Count, DP_u, DP_v, DP_32768, DP_Color, DP_Pix, DP_EntryTable, DP_EntryTransTable DP_Count dd 0 DP_u dd 0 DP_v dd 0 DP_32768 dd 32768.0 DP_Color dd 0 DP_Pix dd 0 externdef DP_1x1:dword externdef DP_2x2:dword externdef DP_3x3:dword externdef DP_4x4:dword DP_EntryTable dd DP_1x1, DP_2x2, DP_3x3, DP_4x4 externdef DP_T1x1:dword externdef DP_T2x2:dword externdef DP_T3x3:dword externdef DP_T4x4:dword DP_EntryTransTable dd DP_T1x1, DP_T2x2, DP_T3x3, DP_T4x4 public advancetable, sstep, tstep, pspantemp, counttemp, jumptemp advancetable dd 0, 0 sstep dd 0 tstep dd 0 pspantemp dd 0 counttemp dd 0 jumptemp dd 0 public reciprocal_table, entryvec_table reciprocal_table dd 040000000h, 02aaaaaaah, 020000000h dd 019999999h, 015555555h, 012492492h externdef Entry2_8:dword externdef Entry3_8:dword externdef Entry4_8:dword externdef Entry5_8:dword externdef Entry6_8:dword externdef Entry7_8:dword externdef Entry8_8:dword entryvec_table dd 0, Entry2_8, Entry3_8, Entry4_8 dd Entry5_8, Entry6_8, Entry7_8, Entry8_8 externdef Spr8Entry2_8:dword externdef Spr8Entry3_8:dword externdef Spr8Entry4_8:dword externdef Spr8Entry5_8:dword externdef Spr8Entry6_8:dword externdef Spr8Entry7_8:dword externdef Spr8Entry8_8:dword public spr8entryvec_table spr8entryvec_table dd 0, Spr8Entry2_8, Spr8Entry3_8, Spr8Entry4_8 dd Spr8Entry5_8, Spr8Entry6_8, Spr8Entry7_8, Spr8Entry8_8 externdef Spr8Entry2_8T:dword externdef Spr8Entry3_8T:dword externdef Spr8Entry4_8T:dword externdef Spr8Entry5_8T:dword externdef Spr8Entry6_8T:dword externdef Spr8Entry7_8T:dword externdef Spr8Entry8_8T:dword public spr8Tentryvec_table spr8Tentryvec_table dd 0, Spr8Entry2_8T, Spr8Entry3_8T, Spr8Entry4_8T dd Spr8Entry5_8T, Spr8Entry6_8T, Spr8Entry7_8T, Spr8Entry8_8T externdef Spr8Entry2_8T2:dword externdef Spr8Entry3_8T2:dword externdef Spr8Entry4_8T2:dword externdef Spr8Entry5_8T2:dword externdef Spr8Entry6_8T2:dword externdef Spr8Entry7_8T2:dword externdef Spr8Entry8_8T2:dword public spr8T2entryvec_table spr8T2entryvec_table dd 0, Spr8Entry2_8T2, Spr8Entry3_8T2, Spr8Entry4_8T2 dd Spr8Entry5_8T2, Spr8Entry6_8T2, Spr8Entry7_8T2, Spr8Entry8_8T2 _DATA ENDS END engine/h2shared/masm/ftol.asm000066400000000000000000000021641444734033100164340ustar00rootroot00000000000000; ftol.asm -- for MASM. ; Replacement for Visual C++ ftol. Doesn't muck with the fpu control word ; or any of that other time consuming stuff. ; ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386 .MODEL FLAT OPTION OLDSTRUCTS .DATA ftolData dq ? .CODE PUBLIC __ftol __ftol: fistp QWORD PTR [ftolData] mov eax, DWORD PTR [ftolData] mov edx, DWORD PTR [ftolData+4] ret END engine/h2shared/masm/math.asm000066400000000000000000000136371444734033100164300ustar00rootroot00000000000000; ; math.asm -- for MASM ; x86 assembly-language math routines. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _vright:dword externdef _vup:dword externdef _vpn:dword externdef _BOPS_Error:dword ; externs from ASM-only code _DATA SEGMENT align 4 Ljmptab dd Lcase0, Lcase1, Lcase2, Lcase3 dd Lcase4, Lcase5, Lcase6, Lcase7 _DATA ENDS _TEXT SEGMENT public _Invert24To16 _Invert24To16: mov ecx,ds:dword ptr[4+esp] mov edx,0100h cmp ecx,edx jle LOutOfRange sub eax,eax div ecx ret LOutOfRange: mov eax,0FFFFFFFFh ret align 2 public _TransformVector _TransformVector: mov eax,ds:dword ptr[4+esp] mov edx,ds:dword ptr[8+esp] fld ds:dword ptr[eax] fmul ds:dword ptr[_vright] fld ds:dword ptr[eax] fmul ds:dword ptr[_vup] fld ds:dword ptr[eax] fmul ds:dword ptr[_vpn] fld ds:dword ptr[4+eax] fmul ds:dword ptr[_vright+4] fld ds:dword ptr[4+eax] fmul ds:dword ptr[_vup+4] fld ds:dword ptr[4+eax] fmul ds:dword ptr[_vpn+4] fxch st(2) faddp st(5),st(0) faddp st(3),st(0) faddp st(1),st(0) fld ds:dword ptr[8+eax] fmul ds:dword ptr[_vright+8] fld ds:dword ptr[8+eax] fmul ds:dword ptr[_vup+8] fld ds:dword ptr[8+eax] fmul ds:dword ptr[_vpn+8] fxch st(2) faddp st(5),st(0) faddp st(3),st(0) faddp st(1),st(0) fstp ds:dword ptr[8+edx] fstp ds:dword ptr[4+edx] fstp ds:dword ptr[edx] ret align 2 public _BoxOnPlaneSide _BoxOnPlaneSide: push ebx mov edx,ds:dword ptr[4+12+esp] mov ecx,ds:dword ptr[4+4+esp] xor eax,eax mov ebx,ds:dword ptr[4+8+esp] mov al,ds:byte ptr[17+edx] cmp al,8 jge Lerror fld ds:dword ptr[0+edx] fld st(0) jmp dword ptr[Ljmptab+eax*4] Lcase0: fmul ds:dword ptr[ebx] fld ds:dword ptr[0+4+edx] fxch st(2) fmul ds:dword ptr[ecx] fxch st(2) fld st(0) fmul ds:dword ptr[4+ebx] fld ds:dword ptr[0+8+edx] fxch st(2) fmul ds:dword ptr[4+ecx] fxch st(2) fld st(0) fmul ds:dword ptr[8+ebx] fxch st(5) faddp st(3),st(0) fmul ds:dword ptr[8+ecx] fxch st(1) faddp st(3),st(0) fxch st(3) faddp st(2),st(0) jmp LSetSides Lcase1: fmul ds:dword ptr[ecx] fld ds:dword ptr[0+4+edx] fxch st(2) fmul ds:dword ptr[ebx] fxch st(2) fld st(0) fmul ds:dword ptr[4+ebx] fld ds:dword ptr[0+8+edx] fxch st(2) fmul ds:dword ptr[4+ecx] fxch st(2) fld st(0) fmul ds:dword ptr[8+ebx] fxch st(5) faddp st(3),st(0) fmul ds:dword ptr[8+ecx] fxch st(1) faddp st(3),st(0) fxch st(3) faddp st(2),st(0) jmp LSetSides Lcase2: fmul ds:dword ptr[ebx] fld ds:dword ptr[0+4+edx] fxch st(2) fmul ds:dword ptr[ecx] fxch st(2) fld st(0) fmul ds:dword ptr[4+ecx] fld ds:dword ptr[0+8+edx] fxch st(2) fmul ds:dword ptr[4+ebx] fxch st(2) fld st(0) fmul ds:dword ptr[8+ebx] fxch st(5) faddp st(3),st(0) fmul ds:dword ptr[8+ecx] fxch st(1) faddp st(3),st(0) fxch st(3) faddp st(2),st(0) jmp LSetSides Lcase3: fmul ds:dword ptr[ecx] fld ds:dword ptr[0+4+edx] fxch st(2) fmul ds:dword ptr[ebx] fxch st(2) fld st(0) fmul ds:dword ptr[4+ecx] fld ds:dword ptr[0+8+edx] fxch st(2) fmul ds:dword ptr[4+ebx] fxch st(2) fld st(0) fmul ds:dword ptr[8+ebx] fxch st(5) faddp st(3),st(0) fmul ds:dword ptr[8+ecx] fxch st(1) faddp st(3),st(0) fxch st(3) faddp st(2),st(0) jmp LSetSides Lcase4: fmul ds:dword ptr[ebx] fld ds:dword ptr[0+4+edx] fxch st(2) fmul ds:dword ptr[ecx] fxch st(2) fld st(0) fmul ds:dword ptr[4+ebx] fld ds:dword ptr[0+8+edx] fxch st(2) fmul ds:dword ptr[4+ecx] fxch st(2) fld st(0) fmul ds:dword ptr[8+ecx] fxch st(5) faddp st(3),st(0) fmul ds:dword ptr[8+ebx] fxch st(1) faddp st(3),st(0) fxch st(3) faddp st(2),st(0) jmp LSetSides Lcase5: fmul ds:dword ptr[ecx] fld ds:dword ptr[0+4+edx] fxch st(2) fmul ds:dword ptr[ebx] fxch st(2) fld st(0) fmul ds:dword ptr[4+ebx] fld ds:dword ptr[0+8+edx] fxch st(2) fmul ds:dword ptr[4+ecx] fxch st(2) fld st(0) fmul ds:dword ptr[8+ecx] fxch st(5) faddp st(3),st(0) fmul ds:dword ptr[8+ebx] fxch st(1) faddp st(3),st(0) fxch st(3) faddp st(2),st(0) jmp LSetSides Lcase6: fmul ds:dword ptr[ebx] fld ds:dword ptr[0+4+edx] fxch st(2) fmul ds:dword ptr[ecx] fxch st(2) fld st(0) fmul ds:dword ptr[4+ecx] fld ds:dword ptr[0+8+edx] fxch st(2) fmul ds:dword ptr[4+ebx] fxch st(2) fld st(0) fmul ds:dword ptr[8+ecx] fxch st(5) faddp st(3),st(0) fmul ds:dword ptr[8+ebx] fxch st(1) faddp st(3),st(0) fxch st(3) faddp st(2),st(0) jmp LSetSides Lcase7: fmul ds:dword ptr[ecx] fld ds:dword ptr[0+4+edx] fxch st(2) fmul ds:dword ptr[ebx] fxch st(2) fld st(0) fmul ds:dword ptr[4+ecx] fld ds:dword ptr[0+8+edx] fxch st(2) fmul ds:dword ptr[4+ebx] fxch st(2) fld st(0) fmul ds:dword ptr[8+ecx] fxch st(5) faddp st(3),st(0) fmul ds:dword ptr[8+ebx] fxch st(1) faddp st(3),st(0) fxch st(3) faddp st(2),st(0) LSetSides: faddp st(2),st(0) fcomp ds:dword ptr[12+edx] xor ecx,ecx fnstsw ax fcomp ds:dword ptr[12+edx] and ah,1 xor ah,1 add cl,ah fnstsw ax and ah,1 add ah,ah add cl,ah pop ebx mov eax,ecx ret Lerror: call near ptr _BOPS_Error _TEXT ENDS END engine/h2shared/masm/r_aclipa.asm000066400000000000000000000066201444734033100172430ustar00rootroot00000000000000; ; r_aclipa.asm -- for MASM ; x86 assembly-language Alias model transform and project code. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _r_refdef:dword ; externs from ASM-only code externdef float_point5:dword _DATA SEGMENT Ltemp0 dd 0 Ltemp1 dd 0 _DATA ENDS _TEXT SEGMENT public _R_Alias_clip_bottom _R_Alias_clip_bottom: push esi push edi mov esi,ds:dword ptr[8+4+esp] mov edi,ds:dword ptr[8+8+esp] mov eax,ds:dword ptr[_r_refdef+52] LDoForwardOrBackward: mov edx,ds:dword ptr[0+4+esi] mov ecx,ds:dword ptr[0+4+edi] cmp edx,ecx jl LDoForward mov ecx,ds:dword ptr[0+4+esi] mov edx,ds:dword ptr[0+4+edi] mov edi,ds:dword ptr[8+4+esp] mov esi,ds:dword ptr[8+8+esp] LDoForward: sub ecx,edx sub eax,edx mov ds:dword ptr[Ltemp1],ecx mov ds:dword ptr[Ltemp0],eax fild ds:dword ptr[Ltemp1] fild ds:dword ptr[Ltemp0] mov edx,ds:dword ptr[8+12+esp] mov eax,2 fdivrp st(1),st(0) LDo3Forward: fild ds:dword ptr[0+0+esi] fild ds:dword ptr[0+0+edi] fild ds:dword ptr[0+4+esi] fild ds:dword ptr[0+4+edi] fild ds:dword ptr[0+8+esi] fild ds:dword ptr[0+8+edi] fxch st(5) fsub st(4),st(0) fxch st(3) fsub st(2),st(0) fxch st(1) fsub st(5),st(0) fxch st(6) fmul st(4),st(0) add edi,12 fmul st(2),st(0) add esi,12 add edx,12 fmul st(5),st(0) fxch st(3) faddp st(4),st(0) faddp st(1),st(0) fxch st(4) faddp st(3),st(0) fxch st(1) fadd ds:dword ptr[float_point5] fxch st(3) fadd ds:dword ptr[float_point5] fxch st(2) fadd ds:dword ptr[float_point5] fxch st(3) fistp ds:dword ptr[0+0-12+edx] fxch st(1) fistp ds:dword ptr[0+4-12+edx] fxch st(1) fistp ds:dword ptr[0+8-12+edx] dec eax jnz LDo3Forward fstp st(0) pop edi pop esi ret public _R_Alias_clip_top _R_Alias_clip_top: push esi push edi mov esi,ds:dword ptr[8+4+esp] mov edi,ds:dword ptr[8+8+esp] mov eax,ds:dword ptr[_r_refdef+20+4] jmp LDoForwardOrBackward public _R_Alias_clip_right _R_Alias_clip_right: push esi push edi mov esi,ds:dword ptr[8+4+esp] mov edi,ds:dword ptr[8+8+esp] mov eax,ds:dword ptr[_r_refdef+48] LRightLeftEntry: mov edx,ds:dword ptr[0+4+esi] mov ecx,ds:dword ptr[0+4+edi] cmp edx,ecx mov edx,ds:dword ptr[0+0+esi] mov ecx,ds:dword ptr[0+0+edi] jl LDoForward2 mov ecx,ds:dword ptr[0+0+esi] mov edx,ds:dword ptr[0+0+edi] mov edi,ds:dword ptr[8+4+esp] mov esi,ds:dword ptr[8+8+esp] LDoForward2: jmp LDoForward public _R_Alias_clip_left _R_Alias_clip_left: push esi push edi mov esi,ds:dword ptr[8+4+esp] mov edi,ds:dword ptr[8+8+esp] mov eax,ds:dword ptr[_r_refdef+20+0] jmp LRightLeftEntry _TEXT ENDS END engine/h2shared/masm/r_aliasa.asm000066400000000000000000000076361444734033100172540ustar00rootroot00000000000000; ; r_aliasa.asm -- for MASM ; x86 assembly-language Alias model transform and project code. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _r_apverts:dword externdef _r_anumverts:dword externdef _aliastransform:dword externdef _r_avertexnormals:dword externdef _r_plightvec:dword externdef _r_ambientlight:dword externdef _r_shadelight:dword externdef _aliasxcenter:dword externdef _aliasycenter:dword ; externs from ASM-only code _DATA SEGMENT Lfloat_1 dd 1.0 Ltemp dd 0 Lcoords dd 0, 0, 0 _DATA ENDS _TEXT SEGMENT public _R_AliasTransformAndProjectFinalVerts _R_AliasTransformAndProjectFinalVerts: push ebp push edi push esi mov esi,dword ptr[_r_apverts] mov ebp,dword ptr[12+8+esp] mov edi,dword ptr[12+4+esp] mov ecx,dword ptr[_r_anumverts] sub edx,edx Lloop: mov dl,byte ptr[esi] mov byte ptr[Lcoords],dl fild dword ptr[Lcoords] mov dl,byte ptr[1+esi] mov byte ptr[Lcoords+4],dl fild dword ptr[Lcoords+4] mov dl,byte ptr[2+esi] mov byte ptr[Lcoords+8],dl fild dword ptr[Lcoords+8] fld st(2) fmul dword ptr[_aliastransform+32] fld st(2) fmul dword ptr[_aliastransform+36] fxch st(1) fadd dword ptr[_aliastransform+44] fld st(2) fmul dword ptr[_aliastransform+40] fxch st(1) faddp st(2),st(0) mov dl,byte ptr[3+esi] ; mov eax,dword ptr[4+ebp] ;load .s ; mov dword ptr[0+8+edi],eax ;store .s faddp st(1),st(0) ; mov eax,dword ptr[8+ebp] ;.t ; mov dword ptr[0+12+edi],eax ;.t fdivr dword ptr[Lfloat_1] ; mov eax,dword ptr[0+ebp] ; .onseam ; mov dword ptr[24+edi],eax mov eax,dword ptr[32+edi] mov eax,dword ptr[12+ebp] mov eax,dword ptr[4+esi] lea eax,dword ptr[edx+edx*2] fxch st(3) fld dword ptr[_r_avertexnormals+eax*4] fmul dword ptr[_r_plightvec] fld dword ptr[_r_avertexnormals+4+eax*4] fmul dword ptr[_r_plightvec+4] fld dword ptr[_r_avertexnormals+8+eax*4] fmul dword ptr[_r_plightvec+8] fxch st(1) faddp st(2),st(0) fld st(2) fmul dword ptr[_aliastransform+0] fxch st(2) faddp st(1),st(0) fst dword ptr[Ltemp] mov eax,dword ptr[_r_ambientlight] mov dl,byte ptr[Ltemp+3] test dl,080h jz Lsavelight fmul dword ptr[_r_shadelight] fistp dword ptr[Ltemp] add eax,dword ptr[Ltemp] jns Lp1 sub eax,eax Lp1: fxch st(1) fmul dword ptr[_aliastransform+16] fxch st(3) fld st(0) fmul dword ptr[_aliastransform+4] fxch st(1) mov dword ptr[0+16+edi],eax fmul dword ptr[_aliastransform+20] fxch st(2) fadd dword ptr[_aliastransform+12] fxch st(4) fadd dword ptr[_aliastransform+28] fxch st(3) fld st(0) fmul dword ptr[_aliastransform+8] fxch st(1) fmul dword ptr[_aliastransform+24] fxch st(5) faddp st(2),st(0) fxch st(3) faddp st(2),st(0) add esi,4 faddp st(2),st(0) faddp st(2),st(0) add ebp,12 fmul st(0),st(2) fxch st(1) fmul st(0),st(2) fxch st(1) fadd dword ptr[_aliasxcenter] fxch st(1) fadd dword ptr[_aliasycenter] fxch st(2) fistp dword ptr[0+20+edi] fistp dword ptr[0+0+edi] fistp dword ptr[0+4+edi] add edi,32 dec ecx jnz Lloop pop esi pop edi pop ebp ret Lsavelight: fstp st(0) jmp Lp1 _TEXT ENDS END engine/h2shared/masm/r_drawa.asm000066400000000000000000000267261444734033100171210ustar00rootroot00000000000000; ; r_drawa.asm -- for MASM ; x86 assembly-language edge clipping and emission code ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _r_refdef:dword externdef _ycenter:dword externdef _xcenter:dword externdef _r_leftclipped:dword externdef _r_leftenter:dword externdef _r_rightclipped:dword externdef _r_rightenter:dword externdef _modelorg:dword externdef _xscale:dword externdef _yscale:dword externdef _r_leftexit:dword externdef _r_rightexit:dword externdef _r_lastvertvalid:dword externdef _cacheoffset:dword externdef _newedges:dword externdef _removeedges:dword externdef _r_pedge:dword externdef _r_framecount:dword externdef _r_u1:dword externdef _r_emitted:dword externdef _edge_p:dword externdef _surface_p:dword externdef _surfaces:dword externdef _r_lzi1:dword externdef _r_v1:dword externdef _r_ceilv1:dword externdef _r_nearzi:dword externdef _r_nearzionly:dword externdef _vright:dword externdef _vup:dword externdef _vpn:dword ; externs from ASM-only code externdef float_point5:dword externdef float_1:dword externdef float_minus_1:dword externdef float_0:dword externdef fp_1m:dword externdef fp_1m_minus_1:dword externdef fp_8:dword externdef fp_16:dword externdef fp_64k:dword externdef fp_64kx64k:dword externdef ceil_cw:dword externdef single_cw:dword _DATA SEGMENT Ld0 dd 0.0 Ld1 dd 0.0 Lstack dd 0 Lfp_near_clip dd 0.01 Lceilv0 dd 0 Lv dd 0 Lu0 dd 0 Lv0 dd 0 Lzi0 dd 0 _DATA ENDS _TEXT SEGMENT align 4 public _R_ClipEdge _R_ClipEdge: push esi push edi push ebx mov ds:dword ptr[Lstack],esp mov ebx,ds:dword ptr[12+12+esp] mov esi,ds:dword ptr[4+12+esp] mov edx,ds:dword ptr[8+12+esp] test ebx,ebx jz Lemit Lcliploop: fld ds:dword ptr[0+0+esi] fmul ds:dword ptr[0+0+ebx] fld ds:dword ptr[0+4+esi] fmul ds:dword ptr[0+4+ebx] fld ds:dword ptr[0+8+esi] fmul ds:dword ptr[0+8+ebx] fxch st(1) faddp st(2),st(0) fld ds:dword ptr[0+0+edx] fmul ds:dword ptr[0+0+ebx] fld ds:dword ptr[0+4+edx] fmul ds:dword ptr[0+4+ebx] fld ds:dword ptr[0+8+edx] fmul ds:dword ptr[0+8+ebx] fxch st(1) faddp st(2),st(0) fxch st(3) faddp st(2),st(0) faddp st(2),st(0) fsub ds:dword ptr[12+ebx] fxch st(1) fsub ds:dword ptr[12+ebx] fxch st(1) fstp ds:dword ptr[Ld0] fstp ds:dword ptr[Ld1] mov eax,ds:dword ptr[Ld0] mov ecx,ds:dword ptr[Ld1] or ecx,eax js Lp2 Lcontinue: mov ebx,ds:dword ptr[16+ebx] test ebx,ebx jnz Lcliploop Lemit: fldcw ds:word ptr[ceil_cw] cmp ds:dword ptr[_r_lastvertvalid],0 jz LCalcFirst mov eax,ds:dword ptr[_r_lzi1] mov ecx,ds:dword ptr[_r_u1] mov ds:dword ptr[Lzi0],eax mov ds:dword ptr[Lu0],ecx mov ecx,ds:dword ptr[_r_v1] mov eax,ds:dword ptr[_r_ceilv1] mov ds:dword ptr[Lv0],ecx mov ds:dword ptr[Lceilv0],eax jmp LCalcSecond LCalcFirst: call near ptr LTransformAndProject fst ds:dword ptr[Lv0] fxch st(2) fstp ds:dword ptr[Lu0] fstp ds:dword ptr[Lzi0] fistp ds:dword ptr[Lceilv0] LCalcSecond: mov esi,edx call near ptr LTransformAndProject fld ds:dword ptr[Lu0] fxch st(3) fld ds:dword ptr[Lzi0] fxch st(3) fld ds:dword ptr[Lv0] fxch st(3) fist ds:dword ptr[_r_ceilv1] fldcw ds:word ptr[single_cw] fst ds:dword ptr[_r_v1] fxch st(4) fcom st(1) fnstsw ax test ah,1 jz LP0 fstp st(0) fld st(0) LP0: fxch st(1) fstp ds:dword ptr[_r_lzi1] fxch st(1) fst ds:dword ptr[_r_u1] fxch st(1) fcom ds:dword ptr[_r_nearzi] fnstsw ax test ah,045h jnz LP1 fst ds:dword ptr[_r_nearzi] LP1: mov eax,ds:dword ptr[_r_nearzionly] test eax,eax jz LP2 LPop5AndDone: mov eax,ds:dword ptr[_cacheoffset] mov edx,ds:dword ptr[_r_framecount] cmp eax,07FFFFFFFh jz LDoPop and edx,07FFFFFFFh or edx,080000000h mov ds:dword ptr[_cacheoffset],edx LDoPop: fstp st(0) fstp st(0) fstp st(0) fstp st(0) fstp st(0) jmp Ldone LP2: mov ebx,ds:dword ptr[Lceilv0] mov edi,ds:dword ptr[_edge_p] mov ecx,ds:dword ptr[_r_ceilv1] mov edx,edi mov esi,ds:dword ptr[_r_pedge] add edx,32 cmp ebx,ecx jz LPop5AndDone mov eax,ds:dword ptr[_r_pedge] mov ds:dword ptr[28+edi],eax fstp ds:dword ptr[24+edi] jc LSide0 LSide1: fsubp st(3),st(0) fsub st(0),st(1) fdivp st(2),st(0) mov ds:dword ptr[_r_emitted],1 mov ds:dword ptr[_edge_p],edx mov eax,ds:dword ptr[edx] mov eax,ecx lea ecx,ds:dword ptr[-1+ebx] mov ebx,eax mov eax,ds:dword ptr[_surface_p] mov esi,ds:dword ptr[_surfaces] sub edx,edx sub eax,esi shr eax,6 mov ds:dword ptr[16+edi],edx mov ds:dword ptr[16+2+edi],eax sub esi,esi mov ds:dword ptr[Lv],ebx fild ds:dword ptr[Lv] fsubrp st(1),st(0) fmul st(0),st(1) fadd ds:dword ptr[_r_u1] jmp LSideDone LSide0: fsub st(0),st(3) fxch st(2) fsub st(0),st(1) fdivp st(2),st(0) mov ds:dword ptr[_r_emitted],1 mov ds:dword ptr[_edge_p],edx mov eax,ds:dword ptr[edx] dec ecx mov eax,ds:dword ptr[_surface_p] mov esi,ds:dword ptr[_surfaces] sub edx,edx sub eax,esi shr eax,6 mov ds:dword ptr[16+2+edi],edx mov ds:dword ptr[16+edi],eax mov esi,1 mov ds:dword ptr[Lv],ebx fild ds:dword ptr[Lv] fsubrp st(1),st(0) fmul st(0),st(1) faddp st(2),st(0) fxch st(1) LSideDone: fmul ds:dword ptr[fp_1m] fxch st(1) fmul ds:dword ptr[fp_1m] fxch st(1) fadd ds:dword ptr[fp_1m_minus_1] fxch st(1) fistp ds:dword ptr[4+edi] fistp ds:dword ptr[0+edi] mov eax,ds:dword ptr[0+edi] mov edx,ds:dword ptr[_r_refdef+76] cmp eax,edx jl LP4 mov edx,ds:dword ptr[_r_refdef+80] cmp eax,edx jng LP5 LP4: mov ds:dword ptr[0+edi],edx mov eax,edx LP5: add eax,esi mov esi,ds:dword ptr[_newedges+ebx*4] test esi,esi jz LDoFirst cmp ds:dword ptr[0+esi],eax jl LNotFirst LDoFirst: mov ds:dword ptr[12+edi],esi mov ds:dword ptr[_newedges+ebx*4],edi jmp LSetRemove LNotFirst: LFindInsertLoop: mov edx,esi mov esi,ds:dword ptr[12+esi] test esi,esi jz LInsertFound cmp ds:dword ptr[0+esi],eax jl LFindInsertLoop LInsertFound: mov ds:dword ptr[12+edi],esi mov ds:dword ptr[12+edx],edi LSetRemove: mov eax,ds:dword ptr[_removeedges+ecx*4] mov ds:dword ptr[_removeedges+ecx*4],edi mov ds:dword ptr[20+edi],eax Ldone: mov esp,ds:dword ptr[Lstack] pop ebx pop edi pop esi ret Lp2: test eax,eax jns Lp1 mov eax,ds:dword ptr[Ld1] test eax,eax jns Lp3 mov eax,ds:dword ptr[_r_leftclipped] mov ecx,ds:dword ptr[_r_pedge] test eax,eax jnz Ldone mov eax,ds:dword ptr[_r_framecount] and eax,07FFFFFFFh or eax,080000000h mov ds:dword ptr[_cacheoffset],eax jmp Ldone Lp1: fld ds:dword ptr[Ld0] fld ds:dword ptr[Ld1] fsubr st(0),st(1) mov ds:dword ptr[_cacheoffset],07FFFFFFFh fdivp st(1),st(0) sub esp,12 fld ds:dword ptr[0+8+edx] fsub ds:dword ptr[0+8+esi] fld ds:dword ptr[0+4+edx] fsub ds:dword ptr[0+4+esi] fld ds:dword ptr[0+0+edx] fsub ds:dword ptr[0+0+esi] mov edx,esp mov eax,ds:dword ptr[20+ebx] test al,al fmul st(0),st(3) fxch st(1) fmul st(0),st(3) fxch st(2) fmulp st(3),st(0) fadd ds:dword ptr[0+0+esi] fxch st(1) fadd ds:dword ptr[0+4+esi] fxch st(2) fadd ds:dword ptr[0+8+esi] fxch st(1) fstp ds:dword ptr[0+0+esp] fstp ds:dword ptr[0+8+esp] fstp ds:dword ptr[0+4+esp] jz Ltestright mov ds:dword ptr[_r_leftclipped],1 mov eax,ds:dword ptr[0+0+esp] mov ds:dword ptr[_r_leftexit+0+0],eax mov eax,ds:dword ptr[0+4+esp] mov ds:dword ptr[_r_leftexit+0+4],eax mov eax,ds:dword ptr[0+8+esp] mov ds:dword ptr[_r_leftexit+0+8],eax jmp Lcontinue Ltestright: test ah,ah jz Lcontinue mov ds:dword ptr[_r_rightclipped],1 mov eax,ds:dword ptr[0+0+esp] mov ds:dword ptr[_r_rightexit+0+0],eax mov eax,ds:dword ptr[0+4+esp] mov ds:dword ptr[_r_rightexit+0+4],eax mov eax,ds:dword ptr[0+8+esp] mov ds:dword ptr[_r_rightexit+0+8],eax jmp Lcontinue Lp3: mov ds:dword ptr[_r_lastvertvalid],0 fld ds:dword ptr[Ld0] fld ds:dword ptr[Ld1] fsubr st(0),st(1) mov ds:dword ptr[_cacheoffset],07FFFFFFFh fdivp st(1),st(0) sub esp,12 fld ds:dword ptr[0+8+edx] fsub ds:dword ptr[0+8+esi] fld ds:dword ptr[0+4+edx] fsub ds:dword ptr[0+4+esi] fld ds:dword ptr[0+0+edx] fsub ds:dword ptr[0+0+esi] mov eax,ds:dword ptr[20+ebx] test al,al fmul st(0),st(3) fxch st(1) fmul st(0),st(3) fxch st(2) fmulp st(3),st(0) fadd ds:dword ptr[0+0+esi] fxch st(1) fadd ds:dword ptr[0+4+esi] fxch st(2) fadd ds:dword ptr[0+8+esi] fxch st(1) fstp ds:dword ptr[0+0+esp] fstp ds:dword ptr[0+8+esp] fstp ds:dword ptr[0+4+esp] mov esi,esp jz Ltestright2 mov ds:dword ptr[_r_leftclipped],1 mov eax,ds:dword ptr[0+0+esp] mov ds:dword ptr[_r_leftenter+0+0],eax mov eax,ds:dword ptr[0+4+esp] mov ds:dword ptr[_r_leftenter+0+4],eax mov eax,ds:dword ptr[0+8+esp] mov ds:dword ptr[_r_leftenter+0+8],eax jmp Lcontinue Ltestright2: test ah,ah jz Lcontinue mov ds:dword ptr[_r_rightclipped],1 mov eax,ds:dword ptr[0+0+esp] mov ds:dword ptr[_r_rightenter+0+0],eax mov eax,ds:dword ptr[0+4+esp] mov ds:dword ptr[_r_rightenter+0+4],eax mov eax,ds:dword ptr[0+8+esp] mov ds:dword ptr[_r_rightenter+0+8],eax jmp Lcontinue LTransformAndProject: fld ds:dword ptr[0+0+esi] fsub ds:dword ptr[_modelorg+0] fld ds:dword ptr[0+4+esi] fsub ds:dword ptr[_modelorg+4] fld ds:dword ptr[0+8+esi] fsub ds:dword ptr[_modelorg+8] fxch st(2) fld st(0) fmul ds:dword ptr[_vpn+0] fld st(1) fmul ds:dword ptr[_vright+0] fxch st(2) fmul ds:dword ptr[_vup+0] fld st(3) fmul ds:dword ptr[_vpn+4] fld st(4) fmul ds:dword ptr[_vright+4] fxch st(5) fmul ds:dword ptr[_vup+4] fxch st(1) faddp st(3),st(0) fxch st(3) faddp st(4),st(0) faddp st(2),st(0) fld st(3) fmul ds:dword ptr[_vpn+8] fld st(4) fmul ds:dword ptr[_vright+8] fxch st(5) fmul ds:dword ptr[_vup+8] fxch st(1) faddp st(2),st(0) fxch st(4) faddp st(3),st(0) fxch st(1) faddp st(3),st(0) fcom ds:dword ptr[Lfp_near_clip] fnstsw ax test ah,1 jz LNoClip fstp st(0) fld ds:dword ptr[Lfp_near_clip] LNoClip: fdivr ds:dword ptr[float_1] fxch st(1) fld ds:dword ptr[_xscale] fmul st(0),st(2) fmulp st(1),st(0) fadd ds:dword ptr[_xcenter] fcom ds:dword ptr[_r_refdef+68] fnstsw ax test ah,1 jz LClampP0 fstp st(0) fld ds:dword ptr[_r_refdef+68] LClampP0: fcom ds:dword ptr[_r_refdef+84] fnstsw ax test ah,045h jnz LClampP1 fstp st(0) fld ds:dword ptr[_r_refdef+84] LClampP1: fld st(1) fmul ds:dword ptr[_yscale] fmulp st(3),st(0) fxch st(2) fsubr ds:dword ptr[_ycenter] fcom ds:dword ptr[_r_refdef+72] fnstsw ax test ah,1 jz LClampP2 fstp st(0) fld ds:dword ptr[_r_refdef+72] LClampP2: fcom ds:dword ptr[_r_refdef+88] fnstsw ax test ah,045h jnz LClampP3 fstp st(0) fld ds:dword ptr[_r_refdef+88] LClampP3: ret _TEXT ENDS END engine/h2shared/masm/r_edgea.asm000066400000000000000000000275101444734033100170600ustar00rootroot00000000000000; ; r_edgea.asm -- for MASM ; x86 assembly-language edge-processing code. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _FoundTrans:dword externdef _r_bmodelactive:dword externdef _surfaces:dword externdef _edge_tail:dword externdef _edge_aftertail:dword externdef _edge_head:dword externdef _edge_head_u_shift20:dword externdef _edge_tail_u_shift20:dword externdef _current_iv:dword externdef _span_p:dword externdef _fv:dword ; externs from ASM-only code externdef _R_SurfacePatchT:dword ;externdef _R_GenerateTSpans:dword _DATA SEGMENT Ltemp dd 0 float_1_div_0100000h dd 035800000h float_point_999 dd 0.999 float_1_point_001 dd 1.001 _DATA ENDS _TEXT SEGMENT public _R_EdgeCodeStart _R_EdgeCodeStart: public _R_InsertNewEdges _R_InsertNewEdges: push edi push esi mov edx,ds:dword ptr[4+8+esp] push ebx mov ecx,ds:dword ptr[8+12+esp] LDoNextEdge: mov eax,ds:dword ptr[0+edx] mov edi,edx LContinueSearch: mov ebx,ds:dword ptr[0+ecx] mov esi,ds:dword ptr[12+ecx] cmp eax,ebx jle LAddedge mov ebx,ds:dword ptr[0+esi] mov ecx,ds:dword ptr[12+esi] cmp eax,ebx jle LAddedge2 mov ebx,ds:dword ptr[0+ecx] mov esi,ds:dword ptr[12+ecx] cmp eax,ebx jle LAddedge mov ebx,ds:dword ptr[0+esi] mov ecx,ds:dword ptr[12+esi] cmp eax,ebx jg LContinueSearch LAddedge2: mov edx,ds:dword ptr[12+edx] mov ebx,ds:dword ptr[8+esi] mov ds:dword ptr[12+edi],esi mov ds:dword ptr[8+edi],ebx mov ds:dword ptr[12+ebx],edi mov ds:dword ptr[8+esi],edi mov ecx,esi cmp edx,0 jnz LDoNextEdge jmp LDone align 4 LAddedge: mov edx,ds:dword ptr[12+edx] mov ebx,ds:dword ptr[8+ecx] mov ds:dword ptr[12+edi],ecx mov ds:dword ptr[8+edi],ebx mov ds:dword ptr[12+ebx],edi mov ds:dword ptr[8+ecx],edi cmp edx,0 jnz LDoNextEdge LDone: pop ebx pop esi pop edi ret public _R_RemoveEdges _R_RemoveEdges: push ebx mov eax,ds:dword ptr[4+4+esp] Lre_loop: mov ecx,ds:dword ptr[12+eax] mov ebx,ds:dword ptr[20+eax] mov edx,ds:dword ptr[8+eax] test ebx,ebx mov ds:dword ptr[8+ecx],edx jz Lre_done mov ds:dword ptr[12+edx],ecx mov ecx,ds:dword ptr[12+ebx] mov edx,ds:dword ptr[8+ebx] mov eax,ds:dword ptr[20+ebx] mov ds:dword ptr[8+ecx],edx test eax,eax mov ds:dword ptr[12+edx],ecx jnz Lre_loop pop ebx ret Lre_done: mov ds:dword ptr[12+edx],ecx pop ebx ret public _R_StepActiveU _R_StepActiveU: push edi mov edx,ds:dword ptr[4+4+esp] push esi push ebx mov esi,ds:dword ptr[8+edx] LNewEdge: mov edi,ds:dword ptr[0+esi] LNextEdge: mov eax,ds:dword ptr[0+edx] mov ebx,ds:dword ptr[4+edx] add eax,ebx mov esi,ds:dword ptr[12+edx] mov ds:dword ptr[0+edx],eax cmp eax,edi jl LPushBack mov edi,ds:dword ptr[0+esi] mov ebx,ds:dword ptr[4+esi] add edi,ebx mov edx,ds:dword ptr[12+esi] mov ds:dword ptr[0+esi],edi cmp edi,eax jl LPushBack2 mov eax,ds:dword ptr[0+edx] mov ebx,ds:dword ptr[4+edx] add eax,ebx mov esi,ds:dword ptr[12+edx] mov ds:dword ptr[0+edx],eax cmp eax,edi jl LPushBack mov edi,ds:dword ptr[0+esi] mov ebx,ds:dword ptr[4+esi] add edi,ebx mov edx,ds:dword ptr[12+esi] mov ds:dword ptr[0+esi],edi cmp edi,eax jnl LNextEdge LPushBack2: mov ebx,edx mov eax,edi mov edx,esi mov esi,ebx LPushBack: mov ecx,ds:dword ptr[8+edx] mov ebx,ds:dword ptr[12+edx] cmp edx,offset _edge_aftertail jz LUDone mov edi,ds:dword ptr[8+ecx] mov ds:dword ptr[8+esi],ecx mov ds:dword ptr[12+ecx],ebx LPushBackLoop: mov ecx,ds:dword ptr[8+edi] mov ebx,ds:dword ptr[0+edi] cmp eax,ebx jnl LPushBackFound mov edi,ds:dword ptr[8+ecx] mov ebx,ds:dword ptr[0+ecx] cmp eax,ebx jl LPushBackLoop mov edi,ecx LPushBackFound: mov ebx,ds:dword ptr[12+edi] mov ds:dword ptr[8+edx],edi mov ds:dword ptr[12+edx],ebx mov ds:dword ptr[12+edi],edx mov ds:dword ptr[8+ebx],edx mov edx,esi mov esi,ds:dword ptr[8+esi] cmp edx,offset _edge_tail jnz LNewEdge LUDone: pop ebx pop esi pop edi ret align 4 TrailingEdge: ;rj bt ds:dword ptr[24+esi],7 ; surf->flags & SURF_TRANSLUCENT jc LInverted2 mov eax,ds:dword ptr[20+esi] dec eax jnz LInverted mov ds:dword ptr[20+esi],eax mov ecx,ds:dword ptr[40+esi] mov edx,ds:dword ptr[12345678h] LPatch0: mov eax,ds:dword ptr[_r_bmodelactive] sub eax,ecx cmp edx,esi mov ds:dword ptr[_r_bmodelactive],eax jnz LNoEmit mov eax,ds:dword ptr[0+ebx] shr eax,20 mov edx,ds:dword ptr[16+esi] mov ecx,ds:dword ptr[0+esi] cmp eax,edx jle LNoEmit2 mov ds:dword ptr[16+ecx],eax sub eax,edx mov ds:dword ptr[0+ebp],edx mov ds:dword ptr[8+ebp],eax mov eax,ds:dword ptr[_current_iv] mov ds:dword ptr[4+ebp],eax mov eax,ds:dword ptr[8+esi] mov ds:dword ptr[12+ebp],eax mov ds:dword ptr[8+esi],ebp add ebp,16 mov edx,ds:dword ptr[0+esi] mov esi,ds:dword ptr[4+esi] mov ds:dword ptr[0+esi],edx mov ds:dword ptr[4+edx],esi ret LNoEmit3: mov ds:dword ptr[_FoundTrans],1 LNoEmit2: mov ds:dword ptr[16+ecx],eax mov edx,ds:dword ptr[0+esi] mov esi,ds:dword ptr[4+esi] mov ds:dword ptr[0+esi],edx mov ds:dword ptr[4+edx],esi ret LNoEmit: mov edx,ds:dword ptr[0+esi] mov esi,ds:dword ptr[4+esi] mov ds:dword ptr[0+esi],edx mov ds:dword ptr[4+edx],esi ret LInverted: mov ds:dword ptr[20+esi],eax ret ;rj LInverted2: mov ds:dword ptr[_FoundTrans],1 ret Lgs_trailing: push offset Lgs_nextedge jmp TrailingEdge public _R_GenerateSpans _R_GenerateSpans: push ebp push edi push esi push ebx mov ds:dword ptr[_FoundTrans],0 ; rj mov eax,ds:dword ptr[_surfaces] mov edx,ds:dword ptr[_edge_head_u_shift20] add eax,64 mov ebp,ds:dword ptr[_span_p] mov ds:dword ptr[_r_bmodelactive],0 mov ds:dword ptr[0+eax],eax mov ds:dword ptr[4+eax],eax mov ds:dword ptr[16+eax],edx mov ebx,ds:dword ptr[_edge_head+12] cmp ebx,offset _edge_tail jz Lgs_lastspan Lgs_edgeloop: mov edi,ds:dword ptr[16+ebx] mov eax,ds:dword ptr[_surfaces] mov esi,edi and edi,0FFFF0000h and esi,0FFFFh jz Lgs_leading shl esi,6 add esi,eax test edi,edi jz Lgs_trailing call near ptr TrailingEdge mov eax,ds:dword ptr[_surfaces] Lgs_leading: shr edi,16-6 mov eax,ds:dword ptr[_surfaces] add edi,eax mov esi,ds:dword ptr[12345678h] LPatch2: ;rj bt ds:dword ptr[24+edi],7 ; surf->flags & SURF_TRANSLUCENT jnc Skip mov ds:dword ptr[_FoundTrans],1 jmp Lgs_nextedge Skip: mov edx,ds:dword ptr[20+edi] mov eax,ds:dword ptr[40+edi] test eax,eax jnz Lbmodel_leading test edx,edx jnz Lxl_done inc edx mov eax,ds:dword ptr[12+edi] mov ds:dword ptr[20+edi],edx mov ecx,ds:dword ptr[12+esi] cmp eax,ecx jl Lnewtop Lsortloopnb: mov esi,ds:dword ptr[0+esi] mov ecx,ds:dword ptr[12+esi] cmp eax,ecx jge Lsortloopnb jmp LInsertAndExit align 4 Lbmodel_leading: test edx,edx jnz Lxl_done mov ecx,ds:dword ptr[_r_bmodelactive] inc edx inc ecx mov ds:dword ptr[20+edi],edx mov ds:dword ptr[_r_bmodelactive],ecx mov eax,ds:dword ptr[12+edi] mov ecx,ds:dword ptr[12+esi] cmp eax,ecx jl Lnewtop jz Lzcheck_for_newtop Lsortloop: mov esi,ds:dword ptr[0+esi] mov ecx,ds:dword ptr[12+esi] cmp eax,ecx jg Lsortloop jne LInsertAndExit mov eax,ds:dword ptr[0+ebx] sub eax,0FFFFFh mov ds:dword ptr[Ltemp],eax fild ds:dword ptr[Ltemp] fmul ds:dword ptr[float_1_div_0100000h] fld st(0) fmul ds:dword ptr[48+edi] fld ds:dword ptr[_fv] fmul ds:dword ptr[52+edi] fxch st(1) fadd ds:dword ptr[44+edi] fld ds:dword ptr[48+esi] fmul st(0),st(3) fxch st(1) faddp st(2),st(0) fld ds:dword ptr[_fv] fmul ds:dword ptr[52+esi] fld st(2) fmul ds:dword ptr[float_point_999] fxch st(2) fadd ds:dword ptr[44+esi] faddp st(1),st(0) fxch st(1) fcomp st(1) fxch st(1) fmul ds:dword ptr[float_1_point_001] fxch st(1) fnstsw ax test ah,001h jz Lgotposition_fpop3 fcomp st(1) fnstsw ax test ah,045h jz Lsortloop_fpop2 fld ds:dword ptr[48+edi] fcomp ds:dword ptr[48+esi] fnstsw ax test ah,001h jz Lgotposition_fpop2 fstp st(0) fstp st(0) mov eax,ds:dword ptr[12+edi] jmp Lsortloop Lgotposition_fpop3: fstp st(0) Lgotposition_fpop2: fstp st(0) fstp st(0) jmp LInsertAndExit Lnewtop_fpop3: fstp st(0) Lnewtop_fpop2: fstp st(0) fstp st(0) mov eax,ds:dword ptr[12+edi] Lnewtop: mov eax,ds:dword ptr[0+ebx] mov edx,ds:dword ptr[16+esi] shr eax,20 mov ds:dword ptr[16+edi],eax cmp eax,edx jle LInsertAndExit ;rj ; bt ds:dword ptr[24+esi],7 ; surf->flags & SURF_TRANSLUCENT ; jc LInsertAndExit sub eax,edx mov ds:dword ptr[0+ebp],edx mov ds:dword ptr[8+ebp],eax mov eax,ds:dword ptr[_current_iv] mov ds:dword ptr[4+ebp],eax mov eax,ds:dword ptr[8+esi] mov ds:dword ptr[12+ebp],eax mov ds:dword ptr[8+esi],ebp add ebp,16 LInsertAndExit: mov ds:dword ptr[0+edi],esi mov eax,ds:dword ptr[4+esi] mov ds:dword ptr[4+edi],eax mov ds:dword ptr[4+esi],edi mov ds:dword ptr[0+eax],edi Lgs_nextedge: mov ebx,ds:dword ptr[12+ebx] cmp ebx,offset _edge_tail jnz Lgs_edgeloop Lgs_lastspan: mov esi,ds:dword ptr[12345678h] LPatch3: mov eax,ds:dword ptr[_edge_tail_u_shift20] xor ecx,ecx mov edx,ds:dword ptr[16+esi] sub eax,edx jle Lgs_resetspanstate mov ds:dword ptr[0+ebp],edx mov ds:dword ptr[8+ebp],eax mov eax,ds:dword ptr[_current_iv] mov ds:dword ptr[4+ebp],eax mov eax,ds:dword ptr[8+esi] mov ds:dword ptr[12+ebp],eax mov ds:dword ptr[8+esi],ebp add ebp,16 Lgs_resetspanstate: mov ds:dword ptr[20+esi],ecx mov esi,ds:dword ptr[0+esi] cmp esi,012345678h LPatch4: jnz Lgs_resetspanstate mov ds:dword ptr[_span_p],ebp ;cmp ds:dword ptr[_FoundTrans],1 ;jne Done ;call near ptr _R_GenerateTSpans Done: pop ebx pop esi pop edi pop ebp ret align 4 Lxl_done: inc edx mov ds:dword ptr[20+edi],edx jmp Lgs_nextedge align 4 Lzcheck_for_newtop: mov eax,ds:dword ptr[0+ebx] sub eax,0FFFFFh mov ds:dword ptr[Ltemp],eax fild ds:dword ptr[Ltemp] fmul ds:dword ptr[float_1_div_0100000h] fld st(0) fmul ds:dword ptr[48+edi] fld ds:dword ptr[_fv] fmul ds:dword ptr[52+edi] fxch st(1) fadd ds:dword ptr[44+edi] fld ds:dword ptr[48+esi] fmul st(0),st(3) fxch st(1) faddp st(2),st(0) fld ds:dword ptr[_fv] fmul ds:dword ptr[52+esi] fld st(2) fmul ds:dword ptr[float_point_999] fxch st(2) fadd ds:dword ptr[44+esi] faddp st(1),st(0) fxch st(1) fcomp st(1) fxch st(1) fmul ds:dword ptr[float_1_point_001] fxch st(1) fnstsw ax test ah,001h jz Lnewtop_fpop3 fcomp st(1) fnstsw ax test ah,045h jz Lsortloop_fpop2 fld ds:dword ptr[48+edi] fcomp ds:dword ptr[48+esi] fnstsw ax test ah,001h jz Lnewtop_fpop2 Lsortloop_fpop2: fstp st(0) fstp st(0) mov eax,ds:dword ptr[12+edi] jmp Lsortloop public _R_EdgeCodeEnd _R_EdgeCodeEnd: align 4 public _R_SurfacePatch _R_SurfacePatch: mov eax,ds:dword ptr[_surfaces] add eax,64 mov ds:dword ptr[LPatch4-4],eax add eax,0 mov ds:dword ptr[LPatch0-4],eax mov ds:dword ptr[LPatch2-4],eax mov ds:dword ptr[LPatch3-4],eax call near ptr _R_SurfacePatchT ret _TEXT ENDS END engine/h2shared/masm/r_edgeb.asm000066400000000000000000000203141444734033100170540ustar00rootroot00000000000000; ; r_edgeb.asm -- for MASM ; x86 assembly-language edge-processing code. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _r_bmodelactive:dword externdef _surfaces:dword externdef _edge_tail:dword externdef _edge_aftertail:dword externdef _edge_head:dword externdef _edge_head_u_shift20:dword externdef _edge_tail_u_shift20:dword externdef _current_iv:dword externdef _span_p:dword externdef _fv:dword ; externs from ASM-only code _DATA SEGMENT Ltemp dd 0 float_1_div_0100000h dd 035800000h float_point_999 dd 0.999 float_1_point_001 dd 1.001 _DATA ENDS _TEXT SEGMENT align 4 public _R_EdgeCodeStartT _R_EdgeCodeStartT: TrailingEdge: mov eax,ds:dword ptr[20+esi] dec eax jnz LInverted mov ds:dword ptr[20+esi],eax mov ecx,ds:dword ptr[40+esi] mov edx,ds:dword ptr[12345678h] LPatch0: mov eax,ds:dword ptr[_r_bmodelactive] sub eax,ecx cmp edx,esi mov ds:dword ptr[_r_bmodelactive],eax jnz LNoEmit mov eax,ds:dword ptr[0+ebx] shr eax,20 mov edx,ds:dword ptr[16+esi] mov ecx,ds:dword ptr[0+esi] cmp eax,edx jle LNoEmit2 ;rj bt ds:dword ptr[24+esi],7 ; surf->flags & SURF_TRANSLUCENT jnc LNoEmit2 mov ds:dword ptr[16+ecx],eax sub eax,edx mov ds:dword ptr[0+ebp],edx mov ds:dword ptr[8+ebp],eax mov eax,ds:dword ptr[_current_iv] mov ds:dword ptr[4+ebp],eax mov eax,ds:dword ptr[8+esi] mov ds:dword ptr[12+ebp],eax mov ds:dword ptr[8+esi],ebp add ebp,16 mov edx,ds:dword ptr[0+esi] mov esi,ds:dword ptr[4+esi] mov ds:dword ptr[0+esi],edx mov ds:dword ptr[4+edx],esi ret LNoEmit2: mov ds:dword ptr[16+ecx],eax mov edx,ds:dword ptr[0+esi] mov esi,ds:dword ptr[4+esi] mov ds:dword ptr[0+esi],edx mov ds:dword ptr[4+edx],esi ret LNoEmit: mov edx,ds:dword ptr[0+esi] mov esi,ds:dword ptr[4+esi] mov ds:dword ptr[0+esi],edx mov ds:dword ptr[4+edx],esi ret LInverted: mov ds:dword ptr[20+esi],eax ret Lgs_trailing: push offset Lgs_nextedge jmp TrailingEdge public _R_GenerateTSpans _R_GenerateTSpans: push ebp push edi push esi push ebx mov eax,ds:dword ptr[_surfaces] mov edx,ds:dword ptr[_edge_head_u_shift20] add eax,64 mov ebp,ds:dword ptr[_span_p] mov ds:dword ptr[_r_bmodelactive],0 mov ds:dword ptr[0+eax],eax mov ds:dword ptr[4+eax],eax mov ds:dword ptr[16+eax],edx mov ebx,ds:dword ptr[_edge_head+12] cmp ebx,offset _edge_tail jz Lgs_lastspan Lgs_edgeloop: mov edi,ds:dword ptr[16+ebx] mov eax,ds:dword ptr[_surfaces] mov esi,edi and edi,0FFFF0000h and esi,0FFFFh jz Lgs_leading shl esi,6 add esi,eax test edi,edi jz Lgs_trailing call near ptr TrailingEdge mov eax,ds:dword ptr[_surfaces] Lgs_leading: shr edi,16-6 mov eax,ds:dword ptr[_surfaces] add edi,eax mov esi,ds:dword ptr[12345678h] LPatch2: mov edx,ds:dword ptr[20+edi] mov eax,ds:dword ptr[40+edi] test eax,eax jnz Lbmodel_leading test edx,edx jnz Lxl_done inc edx mov eax,ds:dword ptr[12+edi] mov ds:dword ptr[20+edi],edx mov ecx,ds:dword ptr[12+esi] cmp eax,ecx jl Lnewtop Lsortloopnb: mov esi,ds:dword ptr[0+esi] mov ecx,ds:dword ptr[12+esi] cmp eax,ecx jge Lsortloopnb jmp LInsertAndExit align 4 Lbmodel_leading: test edx,edx jnz Lxl_done mov ecx,ds:dword ptr[_r_bmodelactive] inc edx inc ecx mov ds:dword ptr[20+edi],edx mov ds:dword ptr[_r_bmodelactive],ecx mov eax,ds:dword ptr[12+edi] mov ecx,ds:dword ptr[12+esi] cmp eax,ecx jl Lnewtop jz Lzcheck_for_newtop Lsortloop: mov esi,ds:dword ptr[0+esi] mov ecx,ds:dword ptr[12+esi] cmp eax,ecx jg Lsortloop jne LInsertAndExit mov eax,ds:dword ptr[0+ebx] sub eax,0FFFFFh mov ds:dword ptr[Ltemp],eax fild ds:dword ptr[Ltemp] fmul ds:dword ptr[float_1_div_0100000h] fld st(0) fmul ds:dword ptr[48+edi] fld ds:dword ptr[_fv] fmul ds:dword ptr[52+edi] fxch st(1) fadd ds:dword ptr[44+edi] fld ds:dword ptr[48+esi] fmul st(0),st(3) fxch st(1) faddp st(2),st(0) fld ds:dword ptr[_fv] fmul ds:dword ptr[52+esi] fld st(2) fmul ds:dword ptr[float_point_999] fxch st(2) fadd ds:dword ptr[44+esi] faddp st(1),st(0) fxch st(1) fcomp st(1) fxch st(1) fmul ds:dword ptr[float_1_point_001] fxch st(1) fnstsw ax test ah,001h jz Lgotposition_fpop3 fcomp st(1) fnstsw ax test ah,045h jz Lsortloop_fpop2 fld ds:dword ptr[48+edi] fcomp ds:dword ptr[48+esi] fnstsw ax test ah,001h jz Lgotposition_fpop2 fstp st(0) fstp st(0) mov eax,ds:dword ptr[12+edi] jmp Lsortloop Lgotposition_fpop3: fstp st(0) Lgotposition_fpop2: fstp st(0) fstp st(0) jmp LInsertAndExit Lnewtop_fpop3: fstp st(0) Lnewtop_fpop2: fstp st(0) fstp st(0) mov eax,ds:dword ptr[12+edi] Lnewtop: mov eax,ds:dword ptr[0+ebx] mov edx,ds:dword ptr[16+esi] shr eax,20 mov ds:dword ptr[16+edi],eax cmp eax,edx jle LInsertAndExit ;rj bt ds:dword ptr[24+esi],7 ; surf->flags & SURF_TRANSLUCENT jnc LInsertAndExit sub eax,edx mov ds:dword ptr[0+ebp],edx mov ds:dword ptr[8+ebp],eax mov eax,ds:dword ptr[_current_iv] mov ds:dword ptr[4+ebp],eax mov eax,ds:dword ptr[8+esi] mov ds:dword ptr[12+ebp],eax mov ds:dword ptr[8+esi],ebp add ebp,16 LInsertAndExit: mov ds:dword ptr[0+edi],esi mov eax,ds:dword ptr[4+esi] mov ds:dword ptr[4+edi],eax mov ds:dword ptr[4+esi],edi mov ds:dword ptr[0+eax],edi Lgs_nextedge: mov ebx,ds:dword ptr[12+ebx] cmp ebx,offset _edge_tail jnz Lgs_edgeloop Lgs_lastspan: mov esi,ds:dword ptr[12345678h] LPatch3: mov eax,ds:dword ptr[_edge_tail_u_shift20] xor ecx,ecx mov edx,ds:dword ptr[16+esi] sub eax,edx jle Lgs_resetspanstate ;rj bt ds:dword ptr[24+esi],7 ; surf->flags & SURF_TRANSLUCENT jnc Lgs_resetspanstate mov ds:dword ptr[0+ebp],edx mov ds:dword ptr[8+ebp],eax mov eax,ds:dword ptr[_current_iv] mov ds:dword ptr[4+ebp],eax mov eax,ds:dword ptr[8+esi] mov ds:dword ptr[12+ebp],eax mov ds:dword ptr[8+esi],ebp add ebp,16 Lgs_resetspanstate: mov ds:dword ptr[20+esi],ecx mov esi,ds:dword ptr[0+esi] cmp esi,012345678h LPatch4: jnz Lgs_resetspanstate mov ds:dword ptr[_span_p],ebp pop ebx pop esi pop edi pop ebp ret align 4 Lxl_done: inc edx mov ds:dword ptr[20+edi],edx jmp Lgs_nextedge align 4 Lzcheck_for_newtop: mov eax,ds:dword ptr[0+ebx] sub eax,0FFFFFh mov ds:dword ptr[Ltemp],eax fild ds:dword ptr[Ltemp] fmul ds:dword ptr[float_1_div_0100000h] fld st(0) fmul ds:dword ptr[48+edi] fld ds:dword ptr[_fv] fmul ds:dword ptr[52+edi] fxch st(1) fadd ds:dword ptr[44+edi] fld ds:dword ptr[48+esi] fmul st(0),st(3) fxch st(1) faddp st(2),st(0) fld ds:dword ptr[_fv] fmul ds:dword ptr[52+esi] fld st(2) fmul ds:dword ptr[float_point_999] fxch st(2) fadd ds:dword ptr[44+esi] faddp st(1),st(0) fxch st(1) fcomp st(1) fxch st(1) fmul ds:dword ptr[float_1_point_001] fxch st(1) fnstsw ax test ah,001h jz Lnewtop_fpop3 fcomp st(1) fnstsw ax test ah,045h jz Lsortloop_fpop2 fld ds:dword ptr[48+edi] fcomp ds:dword ptr[48+esi] fnstsw ax test ah,001h jz Lnewtop_fpop2 Lsortloop_fpop2: fstp st(0) fstp st(0) mov eax,ds:dword ptr[12+edi] jmp Lsortloop public _R_EdgeCodeEndT _R_EdgeCodeEndT: align 4 public _R_SurfacePatchT _R_SurfacePatchT: mov eax,ds:dword ptr[_surfaces] add eax,64 mov ds:dword ptr[LPatch4-4],eax add eax,0 mov ds:dword ptr[LPatch0-4],eax mov ds:dword ptr[LPatch2-4],eax mov ds:dword ptr[LPatch3-4],eax ret _TEXT ENDS END engine/h2shared/masm/r_varsa.asm000066400000000000000000000030241444734033100171210ustar00rootroot00000000000000; r_varsa.asm -- for MASM ; global refresh variables ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT _DATA SEGMENT public float_1, float_particle_z_clip, float_point5 public float_minus_1, float_0 float_0 dd 0.0 float_1 dd 1.0 float_minus_1 dd -1.0 float_particle_z_clip dd 8.0 float_point5 dd 0.5 public fp_16, fp_64k, fp_1m, fp_64kx64k public fp_1m_minus_1 public fp_8 fp_1m dd 1048576.0 fp_1m_minus_1 dd 1048575.0 fp_64k dd 65536.0 fp_8 dd 8.0 fp_16 dd 16.0 fp_64kx64k dd 04f000000h public FloatZero, Float2ToThe31nd, FloatMinus2ToThe31nd FloatZero dd 0 Float2ToThe31nd dd 04f000000h FloatMinus2ToThe31nd dd 0cf000000h public _r_bmodelactive _r_bmodelactive dd 0 public _FoundTrans _FoundTrans dd 0 _DATA ENDS END engine/h2shared/masm/snd_mixa.asm000066400000000000000000000070701444734033100172730ustar00rootroot00000000000000; snd_mixa.asm -- for MASM. ; x86 assembly-language sound code ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _snd_scaletable:dword externdef _paintbuffer:dword externdef _snd_linear_count:dword externdef _snd_p:dword ;externdef _snd_vol:dword externdef _snd_out:dword ; externs from ASM-only code _TEXT SEGMENT public _SND_PaintChannelFrom8 _SND_PaintChannelFrom8: push esi push edi push ebx push ebp mov ebx,ds:dword ptr[4+16+esp] mov esi,ds:dword ptr[8+16+esp] mov eax,ds:dword ptr[4+ebx] mov edx,ds:dword ptr[8+ebx] cmp eax,255 jna LLeftSet mov eax,255 LLeftSet: cmp edx,255 jna LRightSet mov edx,255 LRightSet: and eax,0F8h add esi,20 and edx,0F8h mov edi,ds:dword ptr[16+ebx] mov ecx,ds:dword ptr[12+16+esp] add esi,edi shl eax,7 add edi,ecx shl edx,7 mov ds:dword ptr[16+ebx],edi add eax,offset _snd_scaletable add edx,offset _snd_scaletable sub ebx,ebx mov bl,ds:byte ptr[-1+esi+ecx*1] test ecx,1 jz LMix8Loop mov edi,ds:dword ptr[eax+ebx*4] mov ebp,ds:dword ptr[edx+ebx*4] add edi,ds:dword ptr[_paintbuffer+0-8+ecx*8] add ebp,ds:dword ptr[_paintbuffer+4-8+ecx*8] mov ds:dword ptr[_paintbuffer+0-8+ecx*8],edi mov ds:dword ptr[_paintbuffer+4-8+ecx*8],ebp mov bl,ds:byte ptr[-2+esi+ecx*1] dec ecx jz LDone LMix8Loop: mov edi,ds:dword ptr[eax+ebx*4] mov ebp,ds:dword ptr[edx+ebx*4] add edi,ds:dword ptr[_paintbuffer+0-8+ecx*8] add ebp,ds:dword ptr[_paintbuffer+4-8+ecx*8] mov bl,ds:byte ptr[-2+esi+ecx*1] mov ds:dword ptr[_paintbuffer+0-8+ecx*8],edi mov ds:dword ptr[_paintbuffer+4-8+ecx*8],ebp mov edi,ds:dword ptr[eax+ebx*4] mov ebp,ds:dword ptr[edx+ebx*4] mov bl,ds:byte ptr[-3+esi+ecx*1] add edi,ds:dword ptr[_paintbuffer+0-8*2+ecx*8] add ebp,ds:dword ptr[_paintbuffer+4-8*2+ecx*8] mov ds:dword ptr[_paintbuffer+0-8*2+ecx*8],edi mov ds:dword ptr[_paintbuffer+4-8*2+ecx*8],ebp sub ecx,2 jnz LMix8Loop LDone: pop ebp pop ebx pop edi pop esi ret public _Snd_WriteLinearBlastStereo16 _Snd_WriteLinearBlastStereo16: ;push esi push edi push ebx mov ecx,ds:dword ptr[_snd_linear_count] mov ebx,ds:dword ptr[_snd_p] ;mov esi,ds:dword ptr[_snd_vol] mov edi,ds:dword ptr[_snd_out] LWLBLoopTop: mov eax,ds:dword ptr[-8+ebx+ecx*4] ;imul eax,esi sar eax,8 cmp eax,07FFFh jg LClampHigh cmp eax,0FFFF8000h jnl LClampDone mov eax,0FFFF8000h jmp LClampDone LClampHigh: mov eax,07FFFh LClampDone: mov edx,ds:dword ptr[-4+ebx+ecx*4] ;imul edx,esi sar edx,8 cmp edx,07FFFh jg LClampHigh2 cmp edx,0FFFF8000h jnl LClampDone2 mov edx,0FFFF8000h jmp LClampDone2 LClampHigh2: mov edx,07FFFh LClampDone2: shl edx,16 and eax,0FFFFh or edx,eax mov ds:dword ptr[-4+edi+ecx*2],edx sub ecx,2 jnz LWLBLoopTop pop ebx pop edi ;pop esi ret _TEXT ENDS END engine/h2shared/masm/surf16.asm000066400000000000000000000141771444734033100166250ustar00rootroot00000000000000; ; surf16.asm -- for MASM ; x86 assembly-language 16 bpp surface block drawing code. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _prowdestbase:dword externdef _pbasesource:dword externdef _lightright:dword externdef _lightrightstep:dword externdef _lightleft:dword externdef _lightleftstep:dword externdef _lightdeltastep:dword externdef _lightdelta:dword externdef _sourcetstep:dword externdef _surfrowbytes:dword externdef _colormap:dword externdef _blocksize:dword externdef _sourcesstep:dword externdef _blockdivshift:dword externdef _blockdivmask:dword externdef _r_lightptr:dword externdef _r_lightwidth:dword externdef _r_numvblocks:dword externdef _r_sourcemax:dword externdef _r_stepback:dword ; externs from ASM-only code _DATA SEGMENT k dd 0 loopentry dd 0 align 4 blockjumptable16: dd LEnter2_16 dd LEnter4_16 dd 0, LEnter8_16 dd 0, 0, 0, LEnter16_16 _DATA ENDS _TEXT SEGMENT align 4 public _R_Surf16Start _R_Surf16Start: align 4 public _R_DrawSurfaceBlock16 _R_DrawSurfaceBlock16: push ebp push edi push esi push ebx mov eax,ds:dword ptr[_blocksize] mov edi,ds:dword ptr[_prowdestbase] mov esi,ds:dword ptr[_pbasesource] mov ebx,ds:dword ptr[_sourcesstep] mov ecx,ds:dword ptr[blockjumptable16-4+eax*2] mov ds:dword ptr[k],eax mov ds:dword ptr[loopentry],ecx mov edx,ds:dword ptr[_lightleft] mov ebp,ds:dword ptr[_lightright] Lblockloop16: sub ebp,edx mov cl,ds:byte ptr[_blockdivshift] sar ebp,cl jns Lp1_16 test ebp,ds:dword ptr[_blockdivmask] jz Lp1_16 inc ebp Lp1_16: sub eax,eax sub ecx,ecx jmp dword ptr[loopentry] align 4 LEnter16_16: mov al,ds:byte ptr[esi] mov cl,ds:byte ptr[esi+ebx] mov ah,dh add edx,ebp mov ch,dh lea esi,ds:dword ptr[esi+ebx*2] mov ax,ds:word ptr[12345678h+eax*2] LBPatch0: add edx,ebp mov ds:word ptr[edi],ax mov cx,ds:word ptr[12345678h+ecx*2] LBPatch1: mov ds:word ptr[2+edi],cx add edi,04h mov al,ds:byte ptr[esi] mov cl,ds:byte ptr[esi+ebx] mov ah,dh add edx,ebp mov ch,dh lea esi,ds:dword ptr[esi+ebx*2] mov ax,ds:word ptr[12345678h+eax*2] LBPatch2: add edx,ebp mov ds:word ptr[edi],ax mov cx,ds:word ptr[12345678h+ecx*2] LBPatch3: mov ds:word ptr[2+edi],cx add edi,04h mov al,ds:byte ptr[esi] mov cl,ds:byte ptr[esi+ebx] mov ah,dh add edx,ebp mov ch,dh lea esi,ds:dword ptr[esi+ebx*2] mov ax,ds:word ptr[12345678h+eax*2] LBPatch4: add edx,ebp mov ds:word ptr[edi],ax mov cx,ds:word ptr[12345678h+ecx*2] LBPatch5: mov ds:word ptr[2+edi],cx add edi,04h mov al,ds:byte ptr[esi] mov cl,ds:byte ptr[esi+ebx] mov ah,dh add edx,ebp mov ch,dh lea esi,ds:dword ptr[esi+ebx*2] mov ax,ds:word ptr[12345678h+eax*2] LBPatch6: add edx,ebp mov ds:word ptr[edi],ax mov cx,ds:word ptr[12345678h+ecx*2] LBPatch7: mov ds:word ptr[2+edi],cx add edi,04h LEnter8_16: mov al,ds:byte ptr[esi] mov cl,ds:byte ptr[esi+ebx] mov ah,dh add edx,ebp mov ch,dh lea esi,ds:dword ptr[esi+ebx*2] mov ax,ds:word ptr[12345678h+eax*2] LBPatch8: add edx,ebp mov ds:word ptr[edi],ax mov cx,ds:word ptr[12345678h+ecx*2] LBPatch9: mov ds:word ptr[2+edi],cx add edi,04h mov al,ds:byte ptr[esi] mov cl,ds:byte ptr[esi+ebx] mov ah,dh add edx,ebp mov ch,dh lea esi,ds:dword ptr[esi+ebx*2] mov ax,ds:word ptr[12345678h+eax*2] LBPatch10: add edx,ebp mov ds:word ptr[edi],ax mov cx,ds:word ptr[12345678h+ecx*2] LBPatch11: mov ds:word ptr[2+edi],cx add edi,04h LEnter4_16: mov al,ds:byte ptr[esi] mov cl,ds:byte ptr[esi+ebx] mov ah,dh add edx,ebp mov ch,dh lea esi,ds:dword ptr[esi+ebx*2] mov ax,ds:word ptr[12345678h+eax*2] LBPatch12: add edx,ebp mov ds:word ptr[edi],ax mov cx,ds:word ptr[12345678h+ecx*2] LBPatch13: mov ds:word ptr[2+edi],cx add edi,04h LEnter2_16: mov al,ds:byte ptr[esi] mov cl,ds:byte ptr[esi+ebx] mov ah,dh add edx,ebp mov ch,dh lea esi,ds:dword ptr[esi+ebx*2] mov ax,ds:word ptr[12345678h+eax*2] LBPatch14: add edx,ebp mov ds:word ptr[edi],ax mov cx,ds:word ptr[12345678h+ecx*2] LBPatch15: mov ds:word ptr[2+edi],cx add edi,04h mov esi,ds:dword ptr[_pbasesource] mov edx,ds:dword ptr[_lightleft] mov ebp,ds:dword ptr[_lightright] mov eax,ds:dword ptr[_sourcetstep] mov ecx,ds:dword ptr[_lightrightstep] mov edi,ds:dword ptr[_prowdestbase] add esi,eax add ebp,ecx mov eax,ds:dword ptr[_lightleftstep] mov ecx,ds:dword ptr[_surfrowbytes] add edx,eax add edi,ecx mov ds:dword ptr[_pbasesource],esi mov ds:dword ptr[_lightright],ebp mov eax,ds:dword ptr[k] mov ds:dword ptr[_lightleft],edx dec eax mov ds:dword ptr[_prowdestbase],edi mov ds:dword ptr[k],eax jnz Lblockloop16 pop ebx pop esi pop edi pop ebp ret public _R_Surf16End _R_Surf16End: _TEXT ENDS _DATA SEGMENT align 4 LPatchTable16: dd LBPatch0-4 dd LBPatch1-4 dd LBPatch2-4 dd LBPatch3-4 dd LBPatch4-4 dd LBPatch5-4 dd LBPatch6-4 dd LBPatch7-4 dd LBPatch8-4 dd LBPatch9-4 dd LBPatch10-4 dd LBPatch11-4 dd LBPatch12-4 dd LBPatch13-4 dd LBPatch14-4 dd LBPatch15-4 _DATA ENDS _TEXT SEGMENT align 4 public _R_Surf16Patch _R_Surf16Patch: push ebx mov eax,ds:dword ptr[_colormap] mov ebx,offset LPatchTable16 mov ecx,16 LPatchLoop16: mov edx,ds:dword ptr[ebx] add ebx,4 mov ds:dword ptr[edx],eax dec ecx jnz LPatchLoop16 pop ebx ret _TEXT ENDS END engine/h2shared/masm/surf8.asm000066400000000000000000000274571444734033100165530ustar00rootroot00000000000000; ; surf8.asm -- for MASM ; x86 assembly-language 8 bpp surface block drawing code. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT ; externs from C code externdef _prowdestbase:dword externdef _pbasesource:dword externdef _lightright:dword externdef _lightrightstep:dword externdef _lightleft:dword externdef _lightleftstep:dword externdef _lightdeltastep:dword externdef _lightdelta:dword externdef _sourcetstep:dword externdef _surfrowbytes:dword externdef _colormap:dword externdef _blocksize:dword externdef _sourcesstep:dword externdef _blockdivshift:dword externdef _blockdivmask:dword externdef _r_lightptr:dword externdef _r_lightwidth:dword externdef _r_numvblocks:dword externdef _r_sourcemax:dword externdef _r_stepback:dword ; externs from ASM-only code _DATA SEGMENT sb_v dd 0 _DATA ENDS _TEXT SEGMENT align 4 public _R_Surf8Start _R_Surf8Start: align 4 public _R_DrawSurfaceBlock8_mip0 _R_DrawSurfaceBlock8_mip0: push ebp push edi push esi push ebx mov ebx,ds:dword ptr[_r_lightptr] mov eax,ds:dword ptr[_r_numvblocks] mov ds:dword ptr[sb_v],eax mov edi,ds:dword ptr[_prowdestbase] mov esi,ds:dword ptr[_pbasesource] Lv_loop_mip0: mov eax,ds:dword ptr[ebx] mov edx,ds:dword ptr[4+ebx] mov ebp,eax mov ecx,ds:dword ptr[_r_lightwidth] mov ds:dword ptr[_lightright],edx sub ebp,edx and ebp,0FFFFFh lea ebx,ds:dword ptr[ebx+ecx*4] mov ds:dword ptr[_r_lightptr],ebx mov ecx,ds:dword ptr[4+ebx] mov ebx,ds:dword ptr[ebx] sub ebx,eax sub ecx,edx sar ecx,4 or ebp,0F0000000h sar ebx,4 mov ds:dword ptr[_lightrightstep],ecx sub ebx,ecx and ebx,0FFFFFh or ebx,0F0000000h sub ecx,ecx mov ds:dword ptr[_lightdeltastep],ebx sub ebx,ebx Lblockloop8_mip0: mov ds:dword ptr[_lightdelta],ebp mov cl,ds:byte ptr[14+esi] sar ebp,4 mov bh,dh mov bl,ds:byte ptr[15+esi] add edx,ebp mov ch,dh add edx,ebp mov ah,ds:byte ptr[12345678h+ebx] LBPatch0: mov bl,ds:byte ptr[13+esi] mov al,ds:byte ptr[12345678h+ecx] LBPatch1: mov cl,ds:byte ptr[12+esi] mov bh,dh add edx,ebp ror eax,16 mov ch,dh add edx,ebp mov ah,ds:byte ptr[12345678h+ebx] LBPatch2: mov bl,ds:byte ptr[11+esi] mov al,ds:byte ptr[12345678h+ecx] LBPatch3: mov cl,ds:byte ptr[10+esi] mov ds:dword ptr[12+edi],eax mov bh,dh add edx,ebp mov ch,dh add edx,ebp mov ah,ds:byte ptr[12345678h+ebx] LBPatch4: mov bl,ds:byte ptr[9+esi] mov al,ds:byte ptr[12345678h+ecx] LBPatch5: mov cl,ds:byte ptr[8+esi] mov bh,dh add edx,ebp ror eax,16 mov ch,dh add edx,ebp mov ah,ds:byte ptr[12345678h+ebx] LBPatch6: mov bl,ds:byte ptr[7+esi] mov al,ds:byte ptr[12345678h+ecx] LBPatch7: mov cl,ds:byte ptr[6+esi] mov ds:dword ptr[8+edi],eax mov bh,dh add edx,ebp mov ch,dh add edx,ebp mov ah,ds:byte ptr[12345678h+ebx] LBPatch8: mov bl,ds:byte ptr[5+esi] mov al,ds:byte ptr[12345678h+ecx] LBPatch9: mov cl,ds:byte ptr[4+esi] mov bh,dh add edx,ebp ror eax,16 mov ch,dh add edx,ebp mov ah,ds:byte ptr[12345678h+ebx] LBPatch10: mov bl,ds:byte ptr[3+esi] mov al,ds:byte ptr[12345678h+ecx] LBPatch11: mov cl,ds:byte ptr[2+esi] mov ds:dword ptr[4+edi],eax mov bh,dh add edx,ebp mov ch,dh add edx,ebp mov ah,ds:byte ptr[12345678h+ebx] LBPatch12: mov bl,ds:byte ptr[1+esi] mov al,ds:byte ptr[12345678h+ecx] LBPatch13: mov cl,ds:byte ptr[esi] mov bh,dh add edx,ebp ror eax,16 mov ch,dh mov ah,ds:byte ptr[12345678h+ebx] LBPatch14: mov edx,ds:dword ptr[_lightright] mov al,ds:byte ptr[12345678h+ecx] LBPatch15: mov ebp,ds:dword ptr[_lightdelta] mov ds:dword ptr[edi],eax add esi,ds:dword ptr[_sourcetstep] add edi,ds:dword ptr[_surfrowbytes] add edx,ds:dword ptr[_lightrightstep] add ebp,ds:dword ptr[_lightdeltastep] mov ds:dword ptr[_lightright],edx jc Lblockloop8_mip0 cmp esi,ds:dword ptr[_r_sourcemax] jb LSkip_mip0 sub esi,ds:dword ptr[_r_stepback] LSkip_mip0: mov ebx,ds:dword ptr[_r_lightptr] dec ds:dword ptr[sb_v] jnz Lv_loop_mip0 pop ebx pop esi pop edi pop ebp ret align 4 public _R_DrawSurfaceBlock8_mip1 _R_DrawSurfaceBlock8_mip1: push ebp push edi push esi push ebx mov ebx,ds:dword ptr[_r_lightptr] mov eax,ds:dword ptr[_r_numvblocks] mov ds:dword ptr[sb_v],eax mov edi,ds:dword ptr[_prowdestbase] mov esi,ds:dword ptr[_pbasesource] Lv_loop_mip1: mov eax,ds:dword ptr[ebx] mov edx,ds:dword ptr[4+ebx] mov ebp,eax mov ecx,ds:dword ptr[_r_lightwidth] mov ds:dword ptr[_lightright],edx sub ebp,edx and ebp,0FFFFFh lea ebx,ds:dword ptr[ebx+ecx*4] mov ds:dword ptr[_r_lightptr],ebx mov ecx,ds:dword ptr[4+ebx] mov ebx,ds:dword ptr[ebx] sub ebx,eax sub ecx,edx sar ecx,3 or ebp,070000000h sar ebx,3 mov ds:dword ptr[_lightrightstep],ecx sub ebx,ecx and ebx,0FFFFFh or ebx,0F0000000h sub ecx,ecx mov ds:dword ptr[_lightdeltastep],ebx sub ebx,ebx Lblockloop8_mip1: mov ds:dword ptr[_lightdelta],ebp mov cl,ds:byte ptr[6+esi] sar ebp,3 mov bh,dh mov bl,ds:byte ptr[7+esi] add edx,ebp mov ch,dh add edx,ebp mov ah,ds:byte ptr[12345678h+ebx] LBPatch22: mov bl,ds:byte ptr[5+esi] mov al,ds:byte ptr[12345678h+ecx] LBPatch23: mov cl,ds:byte ptr[4+esi] mov bh,dh add edx,ebp ror eax,16 mov ch,dh add edx,ebp mov ah,ds:byte ptr[12345678h+ebx] LBPatch24: mov bl,ds:byte ptr[3+esi] mov al,ds:byte ptr[12345678h+ecx] LBPatch25: mov cl,ds:byte ptr[2+esi] mov ds:dword ptr[4+edi],eax mov bh,dh add edx,ebp mov ch,dh add edx,ebp mov ah,ds:byte ptr[12345678h+ebx] LBPatch26: mov bl,ds:byte ptr[1+esi] mov al,ds:byte ptr[12345678h+ecx] LBPatch27: mov cl,ds:byte ptr[esi] mov bh,dh add edx,ebp ror eax,16 mov ch,dh mov ah,ds:byte ptr[12345678h+ebx] LBPatch28: mov edx,ds:dword ptr[_lightright] mov al,ds:byte ptr[12345678h+ecx] LBPatch29: mov ebp,ds:dword ptr[_lightdelta] mov ds:dword ptr[edi],eax mov eax,ds:dword ptr[_sourcetstep] add esi,eax mov eax,ds:dword ptr[_surfrowbytes] add edi,eax mov eax,ds:dword ptr[_lightrightstep] add edx,eax mov eax,ds:dword ptr[_lightdeltastep] add ebp,eax mov ds:dword ptr[_lightright],edx jc Lblockloop8_mip1 cmp esi,ds:dword ptr[_r_sourcemax] jb LSkip_mip1 sub esi,ds:dword ptr[_r_stepback] LSkip_mip1: mov ebx,ds:dword ptr[_r_lightptr] dec ds:dword ptr[sb_v] jnz Lv_loop_mip1 pop ebx pop esi pop edi pop ebp ret align 4 public _R_DrawSurfaceBlock8_mip2 _R_DrawSurfaceBlock8_mip2: push ebp push edi push esi push ebx mov ebx,ds:dword ptr[_r_lightptr] mov eax,ds:dword ptr[_r_numvblocks] mov ds:dword ptr[sb_v],eax mov edi,ds:dword ptr[_prowdestbase] mov esi,ds:dword ptr[_pbasesource] Lv_loop_mip2: mov eax,ds:dword ptr[ebx] mov edx,ds:dword ptr[4+ebx] mov ebp,eax mov ecx,ds:dword ptr[_r_lightwidth] mov ds:dword ptr[_lightright],edx sub ebp,edx and ebp,0FFFFFh lea ebx,ds:dword ptr[ebx+ecx*4] mov ds:dword ptr[_r_lightptr],ebx mov ecx,ds:dword ptr[4+ebx] mov ebx,ds:dword ptr[ebx] sub ebx,eax sub ecx,edx sar ecx,2 or ebp,030000000h sar ebx,2 mov ds:dword ptr[_lightrightstep],ecx sub ebx,ecx and ebx,0FFFFFh or ebx,0F0000000h sub ecx,ecx mov ds:dword ptr[_lightdeltastep],ebx sub ebx,ebx Lblockloop8_mip2: mov ds:dword ptr[_lightdelta],ebp mov cl,ds:byte ptr[2+esi] sar ebp,2 mov bh,dh mov bl,ds:byte ptr[3+esi] add edx,ebp mov ch,dh add edx,ebp mov ah,ds:byte ptr[12345678h+ebx] LBPatch18: mov bl,ds:byte ptr[1+esi] mov al,ds:byte ptr[12345678h+ecx] LBPatch19: mov cl,ds:byte ptr[esi] mov bh,dh add edx,ebp ror eax,16 mov ch,dh mov ah,ds:byte ptr[12345678h+ebx] LBPatch20: mov edx,ds:dword ptr[_lightright] mov al,ds:byte ptr[12345678h+ecx] LBPatch21: mov ebp,ds:dword ptr[_lightdelta] mov ds:dword ptr[edi],eax mov eax,ds:dword ptr[_sourcetstep] add esi,eax mov eax,ds:dword ptr[_surfrowbytes] add edi,eax mov eax,ds:dword ptr[_lightrightstep] add edx,eax mov eax,ds:dword ptr[_lightdeltastep] add ebp,eax mov ds:dword ptr[_lightright],edx jc Lblockloop8_mip2 cmp esi,ds:dword ptr[_r_sourcemax] jb LSkip_mip2 sub esi,ds:dword ptr[_r_stepback] LSkip_mip2: mov ebx,ds:dword ptr[_r_lightptr] dec ds:dword ptr[sb_v] jnz Lv_loop_mip2 pop ebx pop esi pop edi pop ebp ret align 4 public _R_DrawSurfaceBlock8_mip3 _R_DrawSurfaceBlock8_mip3: push ebp push edi push esi push ebx mov ebx,ds:dword ptr[_r_lightptr] mov eax,ds:dword ptr[_r_numvblocks] mov ds:dword ptr[sb_v],eax mov edi,ds:dword ptr[_prowdestbase] mov esi,ds:dword ptr[_pbasesource] Lv_loop_mip3: mov eax,ds:dword ptr[ebx] mov edx,ds:dword ptr[4+ebx] mov ebp,eax mov ecx,ds:dword ptr[_r_lightwidth] mov ds:dword ptr[_lightright],edx sub ebp,edx and ebp,0FFFFFh lea ebx,ds:dword ptr[ebx+ecx*4] mov ds:dword ptr[_lightdelta],ebp mov ds:dword ptr[_r_lightptr],ebx mov ecx,ds:dword ptr[4+ebx] mov ebx,ds:dword ptr[ebx] sub ebx,eax sub ecx,edx sar ecx,1 sar ebx,1 mov ds:dword ptr[_lightrightstep],ecx sub ebx,ecx and ebx,0FFFFFh sar ebp,1 or ebx,0F0000000h mov ds:dword ptr[_lightdeltastep],ebx sub ebx,ebx mov bl,ds:byte ptr[1+esi] sub ecx,ecx mov bh,dh mov cl,ds:byte ptr[esi] add edx,ebp mov ch,dh mov al,ds:byte ptr[12345678h+ebx] LBPatch16: mov edx,ds:dword ptr[_lightright] mov ds:byte ptr[1+edi],al mov al,ds:byte ptr[12345678h+ecx] LBPatch17: mov ds:byte ptr[edi],al mov eax,ds:dword ptr[_sourcetstep] add esi,eax mov eax,ds:dword ptr[_surfrowbytes] add edi,eax mov eax,ds:dword ptr[_lightdeltastep] mov ebp,ds:dword ptr[_lightdelta] mov cl,ds:byte ptr[esi] add ebp,eax mov eax,ds:dword ptr[_lightrightstep] sar ebp,1 add edx,eax mov bh,dh mov bl,ds:byte ptr[1+esi] add edx,ebp mov ch,dh mov al,ds:byte ptr[12345678h+ebx] LBPatch30: mov edx,ds:dword ptr[_sourcetstep] mov ds:byte ptr[1+edi],al mov al,ds:byte ptr[12345678h+ecx] LBPatch31: mov ds:byte ptr[edi],al mov ebp,ds:dword ptr[_surfrowbytes] add esi,edx add edi,ebp cmp esi,ds:dword ptr[_r_sourcemax] jb LSkip_mip3 sub esi,ds:dword ptr[_r_stepback] LSkip_mip3: mov ebx,ds:dword ptr[_r_lightptr] dec ds:dword ptr[sb_v] jnz Lv_loop_mip3 pop ebx pop esi pop edi pop ebp ret public _R_Surf8End _R_Surf8End: _TEXT ENDS _DATA SEGMENT align 4 LPatchTable8: dd LBPatch0-4 dd LBPatch1-4 dd LBPatch2-4 dd LBPatch3-4 dd LBPatch4-4 dd LBPatch5-4 dd LBPatch6-4 dd LBPatch7-4 dd LBPatch8-4 dd LBPatch9-4 dd LBPatch10-4 dd LBPatch11-4 dd LBPatch12-4 dd LBPatch13-4 dd LBPatch14-4 dd LBPatch15-4 dd LBPatch16-4 dd LBPatch17-4 dd LBPatch18-4 dd LBPatch19-4 dd LBPatch20-4 dd LBPatch21-4 dd LBPatch22-4 dd LBPatch23-4 dd LBPatch24-4 dd LBPatch25-4 dd LBPatch26-4 dd LBPatch27-4 dd LBPatch28-4 dd LBPatch29-4 dd LBPatch30-4 dd LBPatch31-4 _DATA ENDS _TEXT SEGMENT align 4 public _R_Surf8Patch _R_Surf8Patch: push ebx mov eax,ds:dword ptr[_colormap] mov ebx,offset LPatchTable8 mov ecx,32 LPatchLoop8: mov edx,ds:dword ptr[ebx] add ebx,4 mov ds:dword ptr[edx],eax dec ecx jnz LPatchLoop8 pop ebx ret _TEXT ENDS END engine/h2shared/masm/sys_ia32.asm000066400000000000000000000036431444734033100171270ustar00rootroot00000000000000; ; sys_ia32.asm -- for MASM. ; x86 assembly-language misc system routines. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2 of the License, or (at ; your option) any later version. ; ; This program is distributed in the hope that it will be useful, but ; WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License along ; with this program; if not, write to the Free Software Foundation, Inc., ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; .386P .model FLAT _DATA SEGMENT align 4 fpenv: dd 0, 0, 0, 0, 0, 0, 0, 0 _DATA ENDS _TEXT SEGMENT public _MaskExceptions _MaskExceptions: fnstenv ds:dword ptr[fpenv] or ds:dword ptr[fpenv],03Fh fldenv ds:dword ptr[fpenv] ret _TEXT ENDS _DATA SEGMENT align 4 public ceil_cw, single_cw, full_cw, cw, pushed_cw ceil_cw dd 0 single_cw dd 0 full_cw dd 0 cw dd 0 pushed_cw dd 0 _DATA ENDS _TEXT SEGMENT public _Sys_LowFPPrecision _Sys_LowFPPrecision: fldcw ds:word ptr[single_cw] ret public _Sys_HighFPPrecision _Sys_HighFPPrecision: fldcw ds:word ptr[full_cw] ret public _Sys_PushFPCW_SetHigh _Sys_PushFPCW_SetHigh: fnstcw ds:word ptr[pushed_cw] fldcw ds:word ptr[full_cw] ret public _Sys_PopFPCW _Sys_PopFPCW: fldcw ds:word ptr[pushed_cw] ret public _Sys_SetFPCW _Sys_SetFPCW: fnstcw ds:word ptr[cw] mov eax,ds:dword ptr[cw] and ah,0F0h or ah,003h mov ds:dword ptr[full_cw],eax and ah,0F0h or ah,00Ch mov ds:dword ptr[single_cw],eax and ah,0F0h or ah,008h mov ds:dword ptr[ceil_cw],eax ret _TEXT ENDS END engine/h2shared/masm/worlda.asm000066400000000000000000000047551444734033100167700ustar00rootroot00000000000000; worlda.asm -- for MASM ; x86 assembly-language server testing stuff ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; include worlda.inc .386P .model FLAT _DATA SEGMENT Ltemp dd 0 _DATA ENDS _TEXT SEGMENT align 4 public _SV_HullPointContents _SV_HullPointContents: push edi mov eax,ds:dword ptr[8+4+esp] test eax,eax js Lhquickout push ebx mov ebx,ds:dword ptr[4+8+esp] push ebp mov edx,ds:dword ptr[12+12+esp] mov edi,ds:dword ptr[0+ebx] mov ebp,ds:dword ptr[4+ebx] sub ebx,ebx push esi Lhloop: IFDEF ENABLE_BSP2 lea eax,ds:dword ptr[eax+eax*2] ;eax*=3 mov ecx,ds:dword ptr[0+edi+eax*4] ELSE mov ecx,ds:dword ptr[0+edi+eax*8] mov eax,ds:dword ptr[4+edi+eax*8] mov esi,eax ror eax,16 ENDIF lea ecx,ds:dword ptr[ecx+ecx*4] mov bl,ds:byte ptr[16+ebp+ecx*4] cmp bl,3 jb Lnodot fld ds:dword ptr[0+ebp+ecx*4] fmul ds:dword ptr[0+edx] fld ds:dword ptr[0+4+ebp+ecx*4] fmul ds:dword ptr[4+edx] fld ds:dword ptr[0+8+ebp+ecx*4] fmul ds:dword ptr[8+edx] fxch st(1) faddp st(2),st(0) faddp st(1),st(0) fsub ds:dword ptr[12+ebp+ecx*4] jmp Lsub Lnodot: fld ds:dword ptr[12+ebp+ecx*4] fsubr ds:dword ptr[edx+ebx*4] Lsub: IFDEF ENABLE_BSP2 ; if dist is negative(float's sign bit is set), copy child[1] into eax fstp ds:dword ptr[Ltemp] test ds:dword ptr[Ltemp],080000000h jns Lpos mov eax,ds:dword ptr[8+edi+eax*4] test eax,eax jns Lhloop jmp Lhdone Lpos: ; otherwise copy child[0] into eax mov eax,ds:dword ptr[4+edi+eax*4] test eax,eax ELSE sar eax,16 sar esi,16 fstp ds:dword ptr[Ltemp] mov ecx,ds:dword ptr[Ltemp] sar ecx,31 and esi,ecx xor ecx,0FFFFFFFFh and eax,ecx or eax,esi ENDIF jns Lhloop Lhdone: pop esi pop ebp pop ebx Lhquickout: pop edi ret _TEXT ENDS END engine/h2shared/masm/worlda.inc000066400000000000000000000001751444734033100167510ustar00rootroot00000000000000;comment-out the following to disable BSP2 support ;!! remember to do the same thing in h2config.h !! ENABLE_BSP2 equ 1 engine/h2shared/math.asm000066400000000000000000000122241444734033100154620ustar00rootroot00000000000000; ; math.asm ; x86 assembly-language math routines. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix vright _sym_prefix vup _sym_prefix vpn _sym_prefix BOPS_Error ; C-shared globals: _sym_prefix Invert24To16 _sym_prefix TransformVector _sym_prefix BoxOnPlaneSide %endif ;_sym_prefix ; externs from C code extern vright extern vup extern vpn extern BOPS_Error ; externs from ASM-only code SEGMENT .data ALIGN 4 Ljmptab dd Lcase0, Lcase1, Lcase2, Lcase3 dd Lcase4, Lcase5, Lcase6, Lcase7 SEGMENT .text global Invert24To16 Invert24To16: mov ecx, dword [4+esp] mov edx,0100h cmp ecx,edx jle LOutOfRange sub eax,eax div ecx ret LOutOfRange: mov eax,0FFFFFFFFh ret ALIGN 2 global TransformVector TransformVector: mov eax, dword [4+esp] mov edx, dword [8+esp] fld dword [eax] fmul dword [vright] fld dword [eax] fmul dword [vup] fld dword [eax] fmul dword [vpn] fld dword [4+eax] fmul dword [vright+4] fld dword [4+eax] fmul dword [vup+4] fld dword [4+eax] fmul dword [vpn+4] fxch st2 faddp st5,st0 faddp st3,st0 faddp st1,st0 fld dword [8+eax] fmul dword [vright+8] fld dword [8+eax] fmul dword [vup+8] fld dword [8+eax] fmul dword [vpn+8] fxch st2 faddp st5,st0 faddp st3,st0 faddp st1,st0 fstp dword [8+edx] fstp dword [4+edx] fstp dword [edx] ret ALIGN 2 global BoxOnPlaneSide BoxOnPlaneSide: push ebx mov edx, dword [4+12+esp] mov ecx, dword [4+4+esp] xor eax,eax mov ebx, dword [4+8+esp] mov al, byte [17+edx] cmp al,8 jge near Lerror fld dword [0+edx] fld st0 jmp dword[Ljmptab+eax*4] Lcase0: fmul dword [ebx] fld dword [0+4+edx] fxch st2 fmul dword [ecx] fxch st2 fld st0 fmul dword [4+ebx] fld dword [0+8+edx] fxch st2 fmul dword [4+ecx] fxch st2 fld st0 fmul dword [8+ebx] fxch st5 faddp st3,st0 fmul dword [8+ecx] fxch st1 faddp st3,st0 fxch st3 faddp st2,st0 jmp LSetSides Lcase1: fmul dword [ecx] fld dword [0+4+edx] fxch st2 fmul dword [ebx] fxch st2 fld st0 fmul dword [4+ebx] fld dword [0+8+edx] fxch st2 fmul dword [4+ecx] fxch st2 fld st0 fmul dword [8+ebx] fxch st5 faddp st3,st0 fmul dword [8+ecx] fxch st1 faddp st3,st0 fxch st3 faddp st2,st0 jmp LSetSides Lcase2: fmul dword [ebx] fld dword [0+4+edx] fxch st2 fmul dword [ecx] fxch st2 fld st0 fmul dword [4+ecx] fld dword [0+8+edx] fxch st2 fmul dword [4+ebx] fxch st2 fld st0 fmul dword [8+ebx] fxch st5 faddp st3,st0 fmul dword [8+ecx] fxch st1 faddp st3,st0 fxch st3 faddp st2,st0 jmp LSetSides Lcase3: fmul dword [ecx] fld dword [0+4+edx] fxch st2 fmul dword [ebx] fxch st2 fld st0 fmul dword [4+ecx] fld dword [0+8+edx] fxch st2 fmul dword [4+ebx] fxch st2 fld st0 fmul dword [8+ebx] fxch st5 faddp st3,st0 fmul dword [8+ecx] fxch st1 faddp st3,st0 fxch st3 faddp st2,st0 jmp LSetSides Lcase4: fmul dword [ebx] fld dword [0+4+edx] fxch st2 fmul dword [ecx] fxch st2 fld st0 fmul dword [4+ebx] fld dword [0+8+edx] fxch st2 fmul dword [4+ecx] fxch st2 fld st0 fmul dword [8+ecx] fxch st5 faddp st3,st0 fmul dword [8+ebx] fxch st1 faddp st3,st0 fxch st3 faddp st2,st0 jmp LSetSides Lcase5: fmul dword [ecx] fld dword [0+4+edx] fxch st2 fmul dword [ebx] fxch st2 fld st0 fmul dword [4+ebx] fld dword [0+8+edx] fxch st2 fmul dword [4+ecx] fxch st2 fld st0 fmul dword [8+ecx] fxch st5 faddp st3,st0 fmul dword [8+ebx] fxch st1 faddp st3,st0 fxch st3 faddp st2,st0 jmp LSetSides Lcase6: fmul dword [ebx] fld dword [0+4+edx] fxch st2 fmul dword [ecx] fxch st2 fld st0 fmul dword [4+ecx] fld dword [0+8+edx] fxch st2 fmul dword [4+ebx] fxch st2 fld st0 fmul dword [8+ecx] fxch st5 faddp st3,st0 fmul dword [8+ebx] fxch st1 faddp st3,st0 fxch st3 faddp st2,st0 jmp LSetSides Lcase7: fmul dword [ecx] fld dword [0+4+edx] fxch st2 fmul dword [ebx] fxch st2 fld st0 fmul dword [4+ecx] fld dword [0+8+edx] fxch st2 fmul dword [4+ebx] fxch st2 fld st0 fmul dword [8+ecx] fxch st5 faddp st3,st0 fmul dword [8+ebx] fxch st1 faddp st3,st0 fxch st3 faddp st2,st0 LSetSides: faddp st2,st0 fcomp dword [12+edx] xor ecx,ecx fnstsw ax fcomp dword [12+edx] and ah,1 xor ah,1 add cl,ah fnstsw ax and ah,1 add ah,ah add cl,ah pop ebx mov eax,ecx ret Lerror: call BOPS_Error ; call near BOPS_Error engine/h2shared/mathlib.c000066400000000000000000000213171444734033100156160ustar00rootroot00000000000000/* * mathlib.c -- math primitives * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" vec3_t vec3_origin = { 0, 0, 0 }; #if 0 void Math_InitSinCos (void) { int i; for (i = 0; i < SINCOS_ANGLES; i++) { printf("%f, %f,\n", sin(i * M_PI*2 / SINCOS_ANGLES), cos(i * M_PI*2 / SINCOS_ANGLES)); } } #endif #ifdef USE_SINCOS_TABLE const float sincos_tab[SINCOS_SIZE] = { #include "sincos.h" }; #endif /* ================ Q_isnan For NaN tests with -ffast-math For 32 bit floats only. ================ */ int Q_isnan (float x) { union { float f; unsigned int i; } t; t.f = x; t.i &= 0x7FFFFFFF; t.i = 0x7F800000 - t.i; return (int)( (unsigned int)t.i >> 31 ); } float anglemod (float a) { a = (360.0/65536) * ((int)(a*(65536/360.0)) & 65535); return a; } /* ================== BOPS_Error Split out like this for ASM to call. ================== */ ASM_LINKAGE_BEGIN /* called from asm. */ FUNC_NORETURN void BOPS_Error (void); #if defined(__WATCOMC__) #pragma aux BOPS_Error aborts; #endif ASM_LINKAGE_END void BOPS_Error (void) { Sys_Error ("BoxOnPlaneSide: Bad signbits"); } #if !id386 /* ================== BoxOnPlaneSide Returns 1, 2, or 1 + 2 ================== */ int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, mplane_t *p) { float dist1, dist2; int sides; /* fast axial cases */ #if 0 /* this is done by the BOX_ON_PLANE_SIDE macro before calling this function */ if (p->type < 3) { if (p->dist <= emins[p->type]) return 1; if (p->dist >= emaxs[p->type]) return 2; return 3; } #endif /* general case */ switch (p->signbits) { case 0: dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; break; case 1: dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; break; case 2: dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; break; case 3: dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; break; case 4: dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; break; case 5: dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; break; case 6: dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; break; case 7: dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; break; default: BOPS_Error (); dist1 = dist2 = 0; /* shut up compiler */ break; } #if 0 int i; vec3_t corners[2]; for (i = 0; i < 3; i++) { if (plane->normal[i] < 0) { corners[0][i] = emins[i]; corners[1][i] = emaxs[i]; } else { corners[1][i] = emins[i]; corners[0][i] = emaxs[i]; } } dist = DotProduct (plane->normal, corners[0]) - plane->dist; dist2 = DotProduct (plane->normal, corners[1]) - plane->dist; sides = 0; if (dist1 >= 0) sides = 1; if (dist2 < 0) sides |= 2; #endif sides = 0; if (dist1 >= p->dist) sides = 1; if (dist2 < p->dist) sides |= 2; #ifdef PARANOID if (sides == 0) Sys_Error ("%s: sides == 0", __thisfunc__); #endif return sides; } #endif /* id386 */ void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) { float angle; float sr, sp, sy, cr, cp, cy; angle = angles[YAW] * (M_PI*2 / 360); sy = sin(angle); cy = cos(angle); angle = angles[PITCH] * (M_PI*2 / 360); sp = sin(angle); cp = cos(angle); angle = angles[ROLL] * (M_PI*2 / 360); sr = sin(angle); cr = cos(angle); forward[0] = cp*cy; forward[1] = cp*sy; forward[2] = -sp; right[0] = -1 * (sr * sp * cy + cr * -sy); right[1] = -1 * (sr * sp * sy + cr * cy); right[2] = -1 * (sr * cp); up[0] = (cr * sp * cy + -sr * -sy); up[1] = (cr * sp * sy + -sr * cy); up[2] = cr * cp; } /* ================ R_ConcatRotations ================ */ void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]) { out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1]; out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2]; out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0]; out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1]; out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2]; out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0]; out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1]; out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2]; } /* ================ R_ConcatTransforms ================ */ void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]) { out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1]; out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2]; out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3]; out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0]; out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1]; out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2]; out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3]; out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0]; out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1]; out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2]; out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3]; } /* =================== FloorDivMod Returns mathematically correct (floor-based) quotient and remainder for numer and denom, both of which should contain no fractional part. The quotient must fit in 32 bits. ==================== */ void FloorDivMod (double numer, double denom, int *quotient, int *rem) { int q, r; double x; #ifndef PARANOID if (denom <= 0.0) Sys_Error ("%s: bad denominator %f", __thisfunc__, denom); /* if ((floor(numer) != numer) || (floor(denom) != denom)) Sys_Error ("%s: non-integer numer or denom %f %f", __thisfunc__, numer, denom); */ #endif if (numer >= 0.0) { x = floor(numer / denom); q = (int)x; r = (int)floor(numer - (x * denom)); } else { /* perform operations with positive values, * and fix mod to make floor-based */ x = floor(-numer / denom); q = -(int)x; r = (int)floor(-numer - (x * denom)); if (r != 0) { q--; r = (int)denom - r; } } *quotient = q; *rem = r; } /* =================== GreatestCommonDivisor ==================== */ int GreatestCommonDivisor (int i1, int i2) { if (i1 > i2) { if (i2 == 0) return (i1); return GreatestCommonDivisor (i2, i1 % i2); } else { if (i1 == 0) return (i2); return GreatestCommonDivisor (i1, i2 % i1); } } int Q_log2 (int val) { int answer = 0; while (val >>= 1) answer++; return answer; } #if !id386 /* =================== Invert24To16 Inverts an 8.24 value to a 16.16 value ==================== */ fixed16_t Invert24To16(fixed16_t val) { if (val < 256) return (0xFFFFFFFF); return (fixed16_t) (((double)0x10000 * (double)0x1000000 / (double)val) + 0.5); } #endif engine/h2shared/mathlib.h000066400000000000000000000166421444734033100156300ustar00rootroot00000000000000/* * mathlib.h -- math primitives * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __MATHLIB_H #define __MATHLIB_H #include #ifndef M_PI #define M_PI 3.14159265358979323846 /* matches value in gcc v2 math.h */ #endif #define nanmask (255 << 23) /* 7F800000 */ #if 0 /* macro is violating strict aliasing rules */ #define IS_NAN(x) (((*(int *) (char *) &x) & nanmask) == nanmask) #else static inline int IS_NAN (float x) { union { float f; int i; } num; num.f = x; return ((num.i & nanmask) == nanmask); } #endif int Q_isnan (float x); /* For 32 bit floats only. */ // square root approximation for single precision floats // https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Approximations_that_depend_on_the_floating_point_representation static inline float Q_sqrt (float x) { union { float f; unsigned int i; } t; t.f = x; t.i -= 1 << 23; t.i >>= 1; t.i += 1 << 29; return t.f; } // fast inverse square root // https://en.wikipedia.org/wiki/Fast_inverse_square_root static inline float Q_rsqrt (float x) { union { float f; unsigned int i; } t; t.f = x; t.i = 0x5f3759df - (t.i >> 1); t.f *= 1.5F - (x * 0.5F * t.f * t.f); return t.f; } extern vec3_t vec3_origin; #define VectorCompare(v1,v2) (((v1)[0] == (v2)[0]) && ((v1)[1] == (v2)[1]) && ((v1)[2] == (v2)[2])) #define DotProduct(x,y) ((x)[0] * (y)[0] + (x)[1] * (y)[1] + (x)[2] * (y)[2]) #define VectorLength(a) sqrt(DotProduct((a),(a))) #define DotProductDBL(x,y) ((double)(x)[0] * (double)(y)[0] + \ (double)(x)[1] * (double)(y)[1] + \ (double)(x)[2] * (double)(y)[2]) #define VectorLengthDBL(a) sqrt(DotProductDBL((a), (a))) #define VectorLengthFast(a) Q_sqrt(DotProduct((a),(a))) #define CrossProduct(v1,v2,cross) \ do { \ (cross)[0] = (v1)[1] * (v2)[2] - (v1)[2] * (v2)[1]; \ (cross)[1] = (v1)[2] * (v2)[0] - (v1)[0] * (v2)[2]; \ (cross)[2] = (v1)[0] * (v2)[1] - (v1)[1] * (v2)[0]; \ } while (0) #define VectorAdd(a,b,c) \ do { \ (c)[0] = (a)[0] + (b)[0]; \ (c)[1] = (a)[1] + (b)[1]; \ (c)[2] = (a)[2] + (b)[2]; \ } while (0) #define VectorSubtract(a,b,c) \ do { \ (c)[0] = (a)[0] - (b)[0]; \ (c)[1] = (a)[1] - (b)[1]; \ (c)[2] = (a)[2] - (b)[2]; \ } while (0) #define VectorInverse(v) \ do { \ (v)[0] = -(v)[0]; \ (v)[1] = -(v)[1]; \ (v)[2] = -(v)[2]; \ } while (0) #define VectorClear(a) ((a)[2] = (a)[1] = (a)[0] = 0) #if 0 #define VectorCopy(a,b) memcpy((b),(a),sizeof(vec3_t)) #else #define VectorCopy(a,b) \ do { \ (b)[0] = (a)[0]; \ (b)[1] = (a)[1]; \ (b)[2] = (a)[2]; \ } while (0) #endif #define VectorSet(v,a,b,c) \ do { \ (v)[0] = (a); \ (v)[1] = (b); \ (v)[2] = (c); \ } while (0) #if 0 #define VectorNegate(a,b) VectorSubtract(vec3_origin,(a),(b)) #else #define VectorNegate(a,b) \ do { \ (b)[0] = -(a)[0]; \ (b)[1] = -(a)[1]; \ (b)[2] = -(a)[2]; \ } while (0) #endif #define VectorScale(a,b,c) \ do { \ (c)[0] = (a)[0] * (b); \ (c)[1] = (a)[1] * (b); \ (c)[2] = (a)[2] * (b); \ } while (0) #define VectorMA(a,s,b,c) \ do { \ (c)[0] = (a)[0] + (s) * (b)[0]; \ (c)[1] = (a)[1] + (s) * (b)[1]; \ (c)[2] = (a)[2] + (s) * (b)[2]; \ } while (0) static inline float _inl_VectorNormalize (vec3_t v); /* returns vector length */ static inline float _inl_VectorNormalize (vec3_t v) { float length = VectorLength(v); if (length) { float ilength = 1.0 / length; v[0] *= ilength; v[1] *= ilength; v[2] *= ilength; } return length; } #define VectorNormalize(a) _inl_VectorNormalize((a)) static inline float _inl_VectorNormalizeFast (vec3_t v) { float d = DotProduct(v, v); float ilength = Q_rsqrt(d); v[0] *= ilength; v[1] *= ilength; v[2] *= ilength; return Q_sqrt(d); } #define VectorNormalizeFast(a) _inl_VectorNormalizeFast((a)) int Q_log2(int val); void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]); void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]); void FloorDivMod (double numer, double denom, int *quotient, int *rem); int GreatestCommonDivisor (int i1, int i2); void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up); float anglemod (float a); struct mplane_s; ASM_LINKAGE_BEGIN int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct mplane_s *plane); ASM_LINKAGE_END #define BOX_ON_PLANE_SIDE(emins, emaxs, p) \ (((p)->type < 3) ? \ ( ((p)->dist <= (emins)[(p)->type]) ? 1 : \ ( ((p)->dist >= (emaxs)[(p)->type]) ? 2 : 3 ) ) \ : \ BoxOnPlaneSide((emins), (emaxs), (p))) ASM_LINKAGE_BEGIN fixed16_t Invert24To16 (fixed16_t val); ASM_LINKAGE_END #define SINCOS_ANGLES 2048 #define SINCOS_SIZE (SINCOS_ANGLES*2) #define SINCOS_SINE 0 #define SINCOS_COSINE 1 #define SINCOS_DEG(angle) (((int)((angle) * (1.0f/360.0f) * SINCOS_ANGLES) & (SINCOS_ANGLES-1)) * 2) #define SINCOS_RAD(angle) (((int)((angle) * (1.0f/M_PI/2.0f) * SINCOS_ANGLES) & (SINCOS_ANGLES-1)) * 2) /* FIXME: make this into a makefile option? */ #if !defined(GLQUAKE) && !defined(SERVERONLY) #define USE_SINCOS_TABLE 1 #endif #ifdef USE_SINCOS_TABLE extern const float sincos_tab[SINCOS_SIZE]; static inline float q_sindeg(float ang) { const int val = SINCOS_DEG(ang); return sincos_tab[val]; } static inline float q_sinrad(float ang) { const int val = SINCOS_RAD(ang); return sincos_tab[val]; } static inline float q_cosdeg(float ang) { const int val = SINCOS_DEG(ang); return sincos_tab[val + SINCOS_COSINE]; } static inline float q_cosrad(float ang) { const int rad = SINCOS_RAD(ang); return sincos_tab[rad + SINCOS_COSINE]; } static inline void q_sincosdeg(float ang, float *sinval, float *cosval) { const int val = SINCOS_DEG(ang); const float *psincos = &sincos_tab[val]; *sinval = *psincos++; *cosval = *psincos; } static inline void q_sincosrad(float ang, float *sinval, float *cosval) { const int val = SINCOS_RAD(ang); const float *psincos = &sincos_tab[val]; *sinval = *psincos++; *cosval = *psincos; } #else static inline float q_sindeg(float ang) { const float val = ang*M_PI*2 / 360; return sin(val); } static inline float q_sinrad(float ang) { return sin(ang); } static inline float q_cosdeg(float ang) { const float val = ang*M_PI*2 / 360; return cos(val); } static inline float q_cosrad(float ang) { return cos(ang); } static inline void q_sincosdeg(float ang, float *sinval, float *cosval) { const float val = ang*M_PI*2 / 360; *sinval = sin(val); *cosval = cos(val); } static inline void q_sincosrad(float ang, float *sinval, float *cosval) { *sinval = sin(ang); *cosval = cos(ang); } #endif #endif /* __MATHLIB_H */ engine/h2shared/mid2strm.c000066400000000000000000000633031444734033100157400ustar00rootroot00000000000000/* * mid2strm.c -- Convert MIDI data to a MIDI stream for playback using * the Windows midiStream API. * * Originally from Hexen II source (C) Raven Software Corp. * based on an old DirectX5 sample code. * Few bits from Doom Legacy: Copyright (C) 1998-2000 by DooM Legacy Team. * Multiple fixes and cleanups and adaptation into new Hammer of Thyrion * (uHexen2) code by O.Sezer: * Copyright (C) 2006-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include "mid2strm.h" #include "midifile.h" #include "quakedef.h" #include "bgmusic.h" midi_filestate_t mfs; static DWORD currenttime; static fshandle_t midi_fh; static DWORD buffer_tick_len; static const char err_bad_midi_file[] = "Read error on input file or file is corrupt."; #ifdef DEBUG_BUILD static const char err_add_event[] = "Unable to add event to stream buffer."; static const char err_bad_runstat[] = "Reference to missing running status."; static const char err_trunc_runstat[] = "Running status message truncated"; static const char err_trunc_chan_msg[] = "Channel message truncated"; static const char err_trunc_sysex[] = "SysEx event truncated"; static const char err_trunc_sysex_len[] = "SysEx event truncated (length)"; static const char err_meta_noclass[] = "Meta event truncated (no class byte)"; static const char err_trunc_meta[] = "Meta event truncated"; static const char err_trunc_meta_len[] = "Meta event truncated (length)"; #endif static int AddEventToStreamBuffer (temp_event_t *te, convert_buf_t *); static int GetInFileData (void *dest, DWORD bytes_wanted); static int GetTrackByte (track_state_t *ts, BYTE *b); static int GetTrackEvent (track_state_t *ts, temp_event_t *te); static int GetTrackVDWord (track_state_t *ts, DWORD *v); static int RefillTrackBuffer (track_state_t *ts); static int RewindConverter (void); #ifndef DEBUG_BUILD #define TRACKERR(p,msg) #else #define TRACKERR(p,msg) ShowTrackError(p,msg) static void ShowTrackError (track_state_t *ts, const char *msg) { Con_Printf ("MIDI: %s\n", msg); Con_Printf ("Track buffer offset: %lu, total: %lu, left: %lu\n", (unsigned long)(ts->current_ptr - ts->start_ptr), ts->length, ts->left_in_buffer); } #endif static int MID2STREAM_fileopen(const char *filename) { FILE *handle; qboolean pak; long length; length = FS_OpenFile(filename, &handle, NULL); pak = file_from_pak; if (length < 0) return -1; midi_fh.file = handle; midi_fh.start = ftell(handle); midi_fh.pos = 0; midi_fh.length = length; midi_fh.pak = pak; return 0; } static void MID2STREAM_fileclose(void) { FS_fclose(&midi_fh); midi_fh.file = NULL; } static long MID2STREAM_seek (long offset, int whence) { if (FS_fseek(&midi_fh, offset, whence) != 0) return -1; return FS_ftell(&midi_fh); } static void MID2STREAM_readfile (void *buffer, DWORD bytes_wanted, DWORD *bytes_read) { size_t nmemb = FS_fread (buffer, 1, bytes_wanted, &midi_fh); *bytes_read = nmemb; } /* ConverterInit * * Open the input file * Allocate and read the entire input file into memory * Validate the input file structure * Allocate the input track structures and initialize them * Initialize the output track structures * * Return zero on success */ int ConverterInit (const char *filename) { int err = 1; DWORD bytes_wanted, bytes_read, magic, bytes; UINT idx; midihdr_t header; track_state_t *ts; currenttime = 0; memset (&mfs, 0, sizeof(midi_filestate_t)); memset (&midi_fh, 0, sizeof(fshandle_t)); if (MID2STREAM_fileopen(filename) != 0) return 1; mfs.length = midi_fh.length; /* Read and validate MThd header, size of file header chunk * and the file header itself. */ if (GetInFileData(&magic, sizeof(DWORD))) goto Init_Cleanup; magic = (DWORD)BigLong(magic); if (magic == MIDI_MAGIC_RIFF) /* RMID ?? */ { if (GetInFileData(&bytes, sizeof(DWORD)) != 0 || /* size */ GetInFileData(&magic, sizeof(DWORD)) != 0 || MIDI_MAGIC_RMID != (DWORD)BigLong(magic) || GetInFileData(&magic, sizeof(DWORD)) != 0 || /* "data" */0x64617461 != (DWORD)BigLong(magic) || GetInFileData(&bytes, sizeof(DWORD)) != 0 || /* size */ /* SMF must begin from here onwards: */ GetInFileData(&magic, sizeof(DWORD)) != 0) { goto Init_Cleanup; } magic = (DWORD)BigLong(magic); } if (magic != MIDI_MAGIC_MTHD) goto Init_Cleanup; if (GetInFileData(&bytes, sizeof(DWORD))) goto Init_Cleanup; if ((bytes = (DWORD)BigLong(bytes)) < sizeof(midihdr_t)) goto Init_Cleanup; if (GetInFileData(&header, bytes)) goto Init_Cleanup; /* File header is stored in big endian (hi-lo) order. */ mfs.format = (DWORD) BigShort(header.format); mfs.numtracks = (DWORD) BigShort(header.numtracks); mfs.timediv = (DWORD) BigShort(header.timediv); if (mfs.format != 0 && mfs.format != 1) /* Type-2 not supported */ goto Init_Cleanup; if (mfs.numtracks == 0) goto Init_Cleanup; if (mfs.format == 0 && mfs.numtracks != 1) goto Init_Cleanup; /* We know how many tracks there are; allocate structures for them * and parse them. The parse merely looks at the MTrk signature and * track chunk length in order to skip to the next track header. */ mfs.tracks = (track_state_t *) Z_Malloc(mfs.numtracks * sizeof(track_state_t), Z_MAINZONE); for (idx = 0, ts = mfs.tracks; idx < mfs.numtracks; ++idx, ++ts) { ts->start_ptr = (BYTE *) Z_Malloc(TRACK_BUFFER_SIZE, Z_MAINZONE); if (GetInFileData(&magic, sizeof(magic))) goto Init_Cleanup; if ((magic = (DWORD)BigLong(magic)) != MIDI_MAGIC_MTRK) goto Init_Cleanup; if (GetInFileData(&bytes, sizeof(bytes))) goto Init_Cleanup; bytes = (DWORD)BigLong(bytes); ts->length = bytes; /* Total track length */ /* Determine whether all track data will fit into a single one of our track * buffers. If not, we need to read in a buffer full and come back for more * later, saving the file offset to continue from and the amount left to read * in the track structure. */ /* Save the file offset of the beginning of this track */ ts->start_ofs = MID2STREAM_seek(0, SEEK_CUR); if (ts->length > TRACK_BUFFER_SIZE) bytes_wanted = TRACK_BUFFER_SIZE; else bytes_wanted = ts->length; MID2STREAM_readfile(ts->start_ptr, bytes_wanted, &bytes_read); if (bytes_read != bytes_wanted) goto Init_Cleanup; /* Save the number of bytes that didn't make it into the buffer */ ts->left_on_disk = ts->length - bytes_read; ts->left_in_buffer = bytes_read; /* Save the current file offset so we can seek to it later */ ts->nextread_ofs = MID2STREAM_seek(0, SEEK_CUR); /* Setup pointer to the current position in the track */ ts->current_ptr = ts->start_ptr; ts->status = 0; ts->running_status = 0; ts->next_event_time = 0; /* Handle bozo MIDI files which contain empty track chunks */ if (!ts->left_in_buffer && !ts->left_on_disk) { ts->status |= ITS_F_ENDOFTRK; continue; } /* always preread the time from each track so the mixer code can * determine which track has the next event with minimum work. */ if (GetTrackVDWord(ts, &ts->next_event_time)) goto Init_Cleanup; /* Step over any unread data, advancing to the beginning of the next * track's data */ MID2STREAM_seek(ts->start_ofs + ts->length, SEEK_SET); } /* End of track initialization code */ err = 0; Init_Cleanup: if (err) { Con_Printf("MIDI: %s\n", err_bad_midi_file); ConverterCleanup(); } return err; } /* GetInFileData * * Gets the requested number of bytes of data from the input file. * Returns 0 on success, or 1 upon short reads. */ static int GetInFileData (void *dest, DWORD bytes_wanted) { DWORD bytes_read; MID2STREAM_readfile(dest, bytes_wanted, &bytes_read); return (bytes_read != bytes_wanted); } /* ConverterCleanup * Free anything we ever allocated */ void ConverterCleanup (void) { DWORD idx; MID2STREAM_fileclose(); if (mfs.tracks) { for (idx = 0; idx < mfs.numtracks; idx++) { if (mfs.tracks[idx].start_ptr) Z_Free(mfs.tracks[idx].start_ptr); } Z_Free(mfs.tracks); mfs.tracks = NULL; } } /* RewindConverter * * An adaptation of the ConverterInit() code which resets the * tracks without closing and opening the file. */ static int RewindConverter (void) { DWORD bytes_wanted, bytes_read, idx; int err = 1; track_state_t *ts; currenttime = 0; for (idx = 0, ts = mfs.tracks; idx < mfs.numtracks; ++idx, ++ts) { /* Determine whether all track data will fit into a single one of our track * buffers. If not, we need to read in a buffer full and come back for more * later, saving the file offset to continue from and the amount left to read * in the track structure. */ MID2STREAM_seek(ts->start_ofs, SEEK_SET); if (ts->length > TRACK_BUFFER_SIZE) bytes_wanted = TRACK_BUFFER_SIZE; else bytes_wanted = ts->length; MID2STREAM_readfile(ts->start_ptr, bytes_wanted, &bytes_read); if (bytes_read != bytes_wanted) goto Rewind_Cleanup; /* Save the number of bytes that didn't make it into the buffer */ ts->left_on_disk = ts->length - bytes_read; ts->left_in_buffer = bytes_read; /* Save the current file offset so we can seek to it later */ ts->nextread_ofs = MID2STREAM_seek(0, SEEK_CUR); /* Setup pointer to the current position in the track */ ts->current_ptr = ts->start_ptr; ts->status = 0; ts->running_status = 0; ts->next_event_time = 0; /* Handle bozo MIDI files which contain empty track chunks */ if (!ts->left_in_buffer && !ts->left_on_disk) { ts->status |= ITS_F_ENDOFTRK; continue; } /* always preread the time from each track so the mixer code can * determine which track has the next event with minimum work. */ if (GetTrackVDWord(ts, &ts->next_event_time)) goto Rewind_Cleanup; /* Step over any unread data, advancing to the beginning of the next * track's data */ MID2STREAM_seek(ts->start_ofs + ts->length, SEEK_SET); } /* End of track initialization code */ err = 0; Rewind_Cleanup: if (err) Con_Printf("MIDI: %s\n", err_bad_midi_file); return err; } /* ConvertToBuffer * * Converts MIDI data from the track buffers setup by a previous call * to ConverterInit(). Converts data until an error is encountered * or the output buffer has been filled with as much event data as possible, * not to exceed maxlen. * * Success/failure and the number of output bytes actually converted will * be returned in the convert_buf structure. */ int ConvertToBuffer (unsigned int flags, convert_buf_t *buf) { static track_state_t *ts, *found; static DWORD status; static DWORD next_time; static temp_event_t tevent; int err; DWORD idx; buf->bytes_in = 0; if (flags & CONVERTF_RESET) { status = 0; memset(&tevent, 0, sizeof(struct _temp_event_s)); ts = found = NULL; } /* If we were already done, then return with a warning */ if (status & CONVERTF_STATUS_DONE) { if (!bgmloop) return CONVERTERR_DONE; RewindConverter(); status = 0; } /* The caller is asking us to continue, but we're already hosed because * we previously identified something as corrupt, so complain louder this * time. */ else if (status & CONVERTF_STATUS_STUCK) { return CONVERTERR_STUCK; } else if (status & CONVERTF_STATUS_GOTEVENT) { /* Turn off this bit flag */ status ^= CONVERTF_STATUS_GOTEVENT; /* The following code for this case is duplicated from below, * and is designed to handle a "straggler" event, should we * have one left over from previous processing the last time * this function was called. */ /* Don't add end of track event until we are done */ if (tevent.shortdata[0] == MIDI_META_EVENT && tevent.shortdata[1] == MIDI_META_EOT) { if (tevent.longdata) { Z_Free(tevent.longdata); tevent.longdata = NULL; } } else if ((err = AddEventToStreamBuffer(&tevent, buf)) != CONVERTERR_NOERROR) { if (err == CONVERTERR_BUFFERFULL) { /* Do some processing and tell caller that this buffer is full */ status |= CONVERTF_STATUS_GOTEVENT; return CONVERTERR_NOERROR; } else if (err == CONVERTERR_METASKIP) { /* We skip by all meta events that aren't tempo changes */ } else { DEBUG_Printf("MIDI: %s\n", err_add_event); if (tevent.longdata) { Z_Free(tevent.longdata); tevent.longdata = NULL; } return err; } } } for ( ; ; ) { found = NULL; next_time = ~(DWORD)0; /* 0xFFFFFFFFL */ /* Find nearest event due */ for (idx = 0, ts = mfs.tracks; idx < mfs.numtracks; ++idx, ++ts) { if (!(ts->status & ITS_F_ENDOFTRK) && ts->next_event_time < next_time) { next_time = ts->next_event_time; found = ts; } } /* None found? We must be done, so return to the caller with a smile. */ if (!found) { status |= CONVERTF_STATUS_DONE; return CONVERTERR_NOERROR; } /* Ok, get the event header from that track */ if (GetTrackEvent(found, &tevent)) { /* Warn future calls that this converter is stuck * at a corrupt spot and can't continue */ status |= CONVERTF_STATUS_STUCK; return CONVERTERR_CORRUPT; } /* Don't add end of track event 'til we're done */ if (tevent.shortdata[0] == MIDI_META_EVENT && tevent.shortdata[1] == MIDI_META_EOT) { if (tevent.longdata) { Z_Free(tevent.longdata); tevent.longdata = NULL; } continue; } if ((err = AddEventToStreamBuffer(&tevent, buf)) != CONVERTERR_NOERROR) { if (err == CONVERTERR_BUFFERFULL) { /* Do some processing and tell caller that this buffer is full */ status |= CONVERTF_STATUS_GOTEVENT; return CONVERTERR_NOERROR; } else if (err == CONVERTERR_METASKIP) { /* We skip by all meta events that aren't tempo changes */ } else { DEBUG_Printf("MIDI: %s\n", err_add_event); if (tevent.longdata) { Z_Free(tevent.longdata); tevent.longdata = NULL; } return err; } } } return CONVERTERR_NOERROR; /* not reached. */ } /* GetTrackVDWord * * Parse a variable length DWORD from the given track. A VDWord in * a MIDI file is in lo-hi format and has the high bit set on every * byte except the last. * * Returns the DWORD in *v and zero on success; else non-zero if we * hit end of track. Sets ITS_F_ENDOFTRK if we hit end of track. */ static int GetTrackVDWord (track_state_t *ts, DWORD *v) { BYTE b; DWORD val = 0; if (ts->status & ITS_F_ENDOFTRK) return 1; do { if (!ts->left_in_buffer && !ts->left_on_disk) { ts->status |= ITS_F_ENDOFTRK; return 1; } if (GetTrackByte(ts, &b)) return 1; val = (val << 7) | (b & 0x7F); } while (b & 0x80); *v = val; return 0; } /* GetTrackEvent * * Fills in the event struct with the next event from the track * * te->event_time will contain the absolute tick time of the event. * * te->shortdata[0] will contain: * - MIDI_META_EVENT if the event is a meta event; in this case * te->shortdata[1] will contain the meta class. * - MIDICMD_SYSEX or MIDICMD_SYSEX_END if the event is a SysEx event * - Otherwise, event is a channel message and te->shortdata[1] and * te->shortdata[2] will contain the rest of the event. * * te->event_len will contain: * - The total length of the channel message in te->shortdata if the * event is a channel message, * - The total length of the paramter data pointed to by te->longdata, * otherwise. * * te->longdata will point at any additional paramters if the event is * a SysEx or meta event with non-zero length; else it will be NULL. * * Returns zero on success or non-zero on any kind of parse error * Maintains the state of the input track (i.e. ts->left_in_buffer, * ts->pTrackPointers, and ts->running_status). */ static int GetTrackEvent (track_state_t *ts, temp_event_t *te) { DWORD idx; BYTE b; UINT event_len; /* Clear out the temporary event structure to get rid of old data */ memset(te, 0, sizeof(struct _temp_event_s)); /* Already at end of track? There's nothing to read. */ if (ts->status & ITS_F_ENDOFTRK) return 1; if (!ts->left_in_buffer && !ts->left_on_disk) return 1; /* Get the first byte, which determines the type of event. */ if (GetTrackByte(ts, &b)) return 1; /* If the high bit is not set, then this is a channel message * which uses the status byte from the last channel message * we saw. NOTE: We do not clear running status across SysEx or * meta events even though the spec says to because there are * actually files out there which contain that sequence of data. */ if ( !(b & 0x80)) { /* No previous status byte? We're hosed. */ if (!ts->running_status) { TRACKERR(ts, err_bad_runstat); return 1; } te->shortdata[0] = ts->running_status; te->shortdata[1] = b; b = te->shortdata[0] & 0xF0; te->event_len = 2; /* Only program change and channel pressure events are * 2 bytes long. the rest are 3 and need another byte. */ if (b != MIDICMD_PGM_CHANGE && b != MIDICMD_CHANNEL_PRESSURE) { if (!ts->left_in_buffer && !ts->left_on_disk) { TRACKERR(ts, err_trunc_runstat); ts->status |= ITS_F_ENDOFTRK; return 1; } if (GetTrackByte(ts, &te->shortdata[2])) return 1; ++te->event_len; } } else if ((b & 0xF0) != MIDICMD_SYSEX) { /* Not running status, not in SysEx range - must be * normal channel message (0x80-0xEF) */ te->shortdata[0] = b; ts->running_status = b; /* Strip off channel and just keep message type */ b &= 0xF0; event_len = (b == MIDICMD_PGM_CHANGE || b == MIDICMD_CHANNEL_PRESSURE) ? 1 : 2; te->event_len = event_len + 1; if ((ts->left_in_buffer + ts->left_on_disk) < event_len) { TRACKERR(ts, err_trunc_chan_msg); ts->status |= ITS_F_ENDOFTRK; return 1; } if (GetTrackByte(ts, &te->shortdata[1])) return 1; if (event_len == 2) { if (GetTrackByte(ts, &te->shortdata[2])) return 1; } } else if (b == MIDICMD_SYSEX || b == MIDICMD_SYSEX_END) { /* One of the SysEx types. (They are the same as far as we're * concerned; there is only a semantic difference in how the * data would actually get sent when the file is played. * We must take care to put the proper event type back on the * output track, however.) * * Parse the general format of: * BYTE event (MIDICMD_SYSEX or MIDICMD_SYSEX_END) * VDWORD num_parms * BYTE ab_parms[num_parms] */ te->shortdata[0] = b; if (GetTrackVDWord(ts, &te->event_len)) { TRACKERR(ts, err_trunc_sysex_len); return 1; } if ((ts->left_in_buffer + ts->left_on_disk) < te->event_len) { TRACKERR(ts, err_trunc_sysex); ts->status |= ITS_F_ENDOFTRK; return 1; } te->longdata = (BYTE *) Z_Malloc(te->event_len, Z_MAINZONE); for (idx = 0; idx < te->event_len; idx++) { if (GetTrackByte(ts, te->longdata + idx)) { Z_Free(te->longdata); te->longdata = NULL; TRACKERR(ts, err_trunc_sysex); return 1; } } } else if (b == MIDI_META_EVENT) { /* It's a meta event. Parse the general form: * BYTE event (MIDI_META_EVENT) * BYTE class * VDWORD num_parms * BYTE ab_parms[num_parms] */ te->shortdata[0] = b; if (!ts->left_in_buffer && !ts->left_on_disk) { TRACKERR(ts, err_meta_noclass); ts->status |= ITS_F_ENDOFTRK; return 1; } if (GetTrackByte(ts, &te->shortdata[1])) return 1; if (GetTrackVDWord(ts, &te->event_len)) { TRACKERR(ts, err_trunc_meta_len); return 1; } /* NOTE: It's perfectly valid to have a meta with no data * In this case, event_len == 0 and longdata == NULL */ if (te->event_len) { if ((ts->left_in_buffer + ts->left_on_disk) < te->event_len) { TRACKERR(ts, err_trunc_meta); ts->status |= ITS_F_ENDOFTRK; return 1; } te->longdata = (BYTE *) Z_Malloc(te->event_len, Z_MAINZONE); for (idx = 0; idx < te->event_len; idx++) { if (GetTrackByte(ts, te->longdata + idx)) { Z_Free(te->longdata); te->longdata = NULL; TRACKERR(ts, err_trunc_meta); return 1; } } } if (te->shortdata[1] == MIDI_META_EOT) ts->status |= ITS_F_ENDOFTRK; } else { /* Messages in this range are system messages and aren't * supposed to be in a normal MIDI file. If they are, we * have either misparsed or the authoring software is stupid. */ return 1; } /* Event time was already stored as the current track time */ te->event_time = ts->next_event_time; /* Now update to the next event time. The code above MUST properly * maintain the end of track flag in case the end of track meta is * missing. NOTE: This code is a continuation of the track event * time pre-read which is done at the end of track initialization. */ if ( !(ts->status & ITS_F_ENDOFTRK)) { DWORD delta_time; if (GetTrackVDWord(ts, &delta_time)) return 1; ts->next_event_time += delta_time; } return 0; } /* GetTrackByte * * Retrieve the next byte from the track buffer, refilling the buffer * from disk if necessary. */ static int GetTrackByte (track_state_t *ts, BYTE *b) { if (!ts->left_in_buffer) { if (RefillTrackBuffer(ts)) return 1; } *b = *ts->current_ptr++; ts->left_in_buffer--; return 0; } /* RefillTrackBuffer * * Attempts to read in a buffer-full of data for a MIDI track. */ static int RefillTrackBuffer (track_state_t *ts) { long ofs; DWORD bytes_wanted, bytes_read; if (!ts->left_on_disk) return 1; ts->current_ptr = ts->start_ptr; /* Seek to the proper place in the file, indicated by * ts->nextread_ofs and read in the remaining data, up * to a maximum of the buffer size. */ ofs = MID2STREAM_seek((long)ts->nextread_ofs, SEEK_SET); if (ofs == -1) { Con_Printf("MIDI: Unable to seek to track buffer location!\n"); return 1; } if (ts->left_on_disk > TRACK_BUFFER_SIZE) bytes_wanted = TRACK_BUFFER_SIZE; else bytes_wanted = ts->left_on_disk; MID2STREAM_readfile(ts->start_ptr, bytes_wanted, &bytes_read); ts->left_on_disk -= bytes_read; ts->nextread_ofs = (DWORD)ofs + bytes_read; ts->left_in_buffer = bytes_read; if (!bytes_read || bytes_read != bytes_wanted) { Con_Printf("MIDI: Read failed prematurely!\n"); return 1; } return 0; } /* AddEventToStreamBuffer * * Put the given event into the given stream buffer at the given location * te must point to an event filled out in accordance with the description * given in GetTrackEvent() * * Returns zero on sucess, non-zero on error. */ static int AddEventToStreamBuffer (temp_event_t *te, convert_buf_t *buf) { DWORD delta_time; MIDIEVENT *me; me = (MIDIEVENT *)(buf->mh.lpData + buf->start_ofs + buf->bytes_in); /* When we see a new, empty buffer, set the start time on it */ if (!buf->bytes_in) buf->starttime = currenttime; /* Use the above set start time to figure out how much longer * we should fill this buffer before declaring it as "full" */ if (currenttime - buf->starttime > buffer_tick_len) { if (buf->times_up) { buf->times_up = FALSE; return CONVERTERR_BUFFERFULL; } buf->times_up = TRUE; } /* Delta time is absolute event time minus absolute time * already gone by on this track */ delta_time = te->event_time - currenttime; /* Event time is now current time on this track */ currenttime = te->event_time; if (te->shortdata[0] < MIDICMD_SYSEX) { /* Channel message. Need 3 DWORD's: delta-t, stream-ID, event */ if (buf->maxlen - buf->bytes_in < 3*sizeof(DWORD)) return CONVERTERR_BUFFERFULL; me->dwDeltaTime = delta_time; me->dwStreamID = 0; me->dwEvent = (te->shortdata[0]) | (((DWORD)te->shortdata[1]) << 8) | (((DWORD)te->shortdata[2]) << 16) | MEVT_F_SHORT; if ((te->shortdata[0] & 0xF0) == MIDICMD_CONTROL && te->shortdata[1] == MIDICTL_MSB_MAIN_VOLUME) { /* If this is a volume change, generate a callback * so we can grab the new volume for our cache. */ me->dwEvent |= MEVT_F_CALLBACK; } buf->bytes_in += 3 *sizeof(DWORD); } else if (te->shortdata[0] == MIDICMD_SYSEX || te->shortdata[0] == MIDICMD_SYSEX_END) { DEBUG_Printf("%s: Ignoring SysEx event.\n", __thisfunc__); if (te->longdata) { Z_Free(te->longdata); te->longdata = NULL; } } else { /* Better be a meta event. * BYTE event * BYTE type * VDWORD len * BYTE longdata[len] */ assert(te->shortdata[0] == MIDI_META_EVENT); /* The only meta-event we care about is change tempo */ if (te->shortdata[1] != MIDI_META_TEMPO) { if (te->longdata) { Z_Free(te->longdata); te->longdata = NULL; } return CONVERTERR_METASKIP; } /* We should have three bytes of parameter data */ assert(te->event_len == 3); /* Need 3 DWORD's: delta-t, stream-ID, event data */ if (buf->maxlen - buf->bytes_in < 3 *sizeof(DWORD)) { if (te->longdata) { Z_Free(te->longdata); te->longdata = NULL; } return CONVERTERR_BUFFERFULL; } me->dwDeltaTime = delta_time; me->dwStreamID = 0; /* Note: this is backwards from above because we're converting * a single data value from hi-lo to lo-hi format... */ me->dwEvent = (te->longdata[2]) | (((DWORD)te->longdata[1]) << 8 ) | (((DWORD)te->longdata[0]) << 16); buffer_tick_len = (mfs.timediv * 1000 * BUFFER_TIME_LENGTH) / me->dwEvent /* == current tempo */; DEBUG_Printf("%s: buffer tick length: %lu\n", __thisfunc__, buffer_tick_len); me->dwEvent |= (((DWORD)MEVT_TEMPO) << 24) | MEVT_F_SHORT; if (te->longdata) { Z_Free(te->longdata); te->longdata = NULL; } buf->bytes_in += 3 *sizeof(DWORD); } return 0; } engine/h2shared/mid2strm.h000066400000000000000000000107371444734033100157500ustar00rootroot00000000000000/* * mid2strm.h -- Convert MIDI data to a MIDI stream for playback using * the Windows midiStream API. * * Originally from Hexen II source (C) Raven Software Corp. * based on an old DirectX5 sample code. * Few bits from Doom Legacy: Copyright (C) 1998-2000 by DooM Legacy Team. * Multiple fixes and cleanups and adaptation into new Hammer of Thyrion * (uHexen2) code by O.Sezer: * Copyright (C) 2006-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef MID2STRM_H #define MID2STRM_H #define TRACK_BUFFER_SIZE 2048 #define OUT_BUFFER_SIZE 2048 /* max stream buffer size in bytes */ #define BUFFER_TIME_LENGTH 2000 /* amount to fill, in milliseconds */ #define NUM_STREAM_BUFFERS 5 #define DEBUG_CALLBACK_TIMEOUT 2000 /* wait 2 seconds for callback */ #define CONVERTF_RESET 0x00000001 #define CONVERTF_STATUS_DONE 0x00000001 #define CONVERTF_STATUS_STUCK 0x00000002 #define CONVERTF_STATUS_GOTEVENT 0x00000004 #define CONVERTERR_NOERROR 0 /* no errors */ #define CONVERTERR_CORRUPT -101 /* input file is corrupt */ #define CONVERTERR_STUCK -102 /* converter has already encountered a corrupt */ /* file and can not convert any more * of it. you must reset the converter. */ #define CONVERTERR_DONE -103 /* converter is done */ #define CONVERTERR_BUFFERFULL -104 /* the buffer is full */ #define CONVERTERR_METASKIP -105 /* skipping unknown meta event */ #define STATUS_KILLCALLBACK 100 /* signals that the callback should die */ #define STATUS_CALLBACKDEAD 200 /* signals callback is done processing */ #define STATUS_WAITINGFOREND 300 /* callback is waiting for buffers to play */ #define VOL_CACHE_INIT 100 /* Temporary event structure which stores event data * until we're ready to dump it into a stream buffer */ typedef struct _temp_event_s { DWORD event_time; /* absolute tick time of event */ BYTE shortdata[4]; /* event type and parameters if channel msg */ DWORD event_len; /* length of data which follows if meta or sysex */ BYTE *longdata; /* -> event data if applicable */ } temp_event_t; /* state of a track open for read */ typedef struct _track_state_s { DWORD status; /* track status */ DWORD length; /* total bytes in track */ DWORD left_in_buffer; /* bytes left unread in track buffer */ BYTE *start_ptr; /* -> start of track data buffer */ BYTE *current_ptr; /* -> next byte to read in buffer */ DWORD next_event_time;/* absolute time of next event in track */ BYTE running_status; /* running status from last channel msg */ DWORD start_ofs; /* start of track. for walking the file */ DWORD nextread_ofs; /* file offset of next read from disk */ DWORD left_on_disk; /* bytes left unread on disk */ } track_state_t; #define ITS_F_ENDOFTRK 0x00000001 /* state of the input MIDI file */ typedef struct _midi_filestate_s { DWORD length; /* total bytes in file */ DWORD timediv; /* original time division */ DWORD format; /* original format */ DWORD numtracks; /* tracks count */ track_state_t *tracks; /* -> array of tracks in this file */ } midi_filestate_t; extern midi_filestate_t mfs; /* convert_buf_t is used to pass information to the ConvertToBuffer () * and then internally by that function to send information about the * target stream buffer and current state of the conversion process to * lower level conversion routines. */ typedef struct _convert_buf_s { MIDIHDR mh; /* windows stream buffer header */ BOOL prepared; /* midiOutPrepareHeader() done? */ DWORD start_ofs; /* start offset from mhBuffer.lpData */ DWORD maxlen; /* max length to convert on this pass */ DWORD bytes_in; /* number of bytes put into this buf. */ DWORD starttime; BOOL times_up; } convert_buf_t; int ConverterInit (const char *filename); void ConverterCleanup (void); int ConvertToBuffer (unsigned int flags, convert_buf_t *buf); #endif /* MID2STRM_H */ engine/h2shared/midi_camd.c000066400000000000000000000532551444734033100161120ustar00rootroot00000000000000/* midi_camd.c -- MIDI module for Amiga using CAMD * Based on PlayMF by Dan Baker, Christian Buchner * * Copyright (C) 2017 Szilard Biro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "bgmusic.h" #include "midi_drv.h" #include #include #include #include #include #include /* MAKE_ID */ #include #ifdef __MORPHOS__ #define USE_INLINE_STDARG #endif #include #include #include #include /* IPTR */ /* prototypes of functions exported to BGM: */ static void *MIDI_Play (const char *filename); static void MIDI_Update (void **handle); static void MIDI_Rewind (void **handle); static void MIDI_Stop (void **handle); static void MIDI_Pause (void **handle); static void MIDI_Resume (void **handle); static void MIDI_SetVolume (void **handle, float value); static midi_driver_t midi_amiga_camd = { false, /* init success */ "CAMD MIDI for Amiga", MIDI_Init, MIDI_Cleanup, MIDI_Play, MIDI_Update, MIDI_Rewind, MIDI_Stop, MIDI_Pause, MIDI_Resume, MIDI_SetVolume, NULL }; #define ID_MTHD MAKE_ID('M','T','h','d') #define ID_MTRK MAKE_ID('M','T','r','k') struct SMFHeader { LONG ChunkID; /* 4 ASCII characters */ LONG VarLeng; WORD Format; UWORD Ntrks; WORD Division; }; typedef int _SMFHeader_check[(offsetof(struct SMFHeader,Division) == 12)*2 - 1]; /* (sizeof(struct SMFHeader) == 14) */ typedef enum { Event_ignore, Event_playable, Event_sysex, Event_tempo, Event_trackend } Eventtype; struct DecTrack { UWORD tracknum; /* number of this track */ BOOL trackend; /* end of track flag */ ULONG absdelta; /* 32-bit delta */ ULONG nexclock; /* storage */ UBYTE status; /* status from file */ UBYTE rstatus; /* running status from track */ UBYTE comsize; /* size of current command */ UBYTE d1; /* data byte 1 */ UBYTE d2; /* data byte 2 */ UBYTE *endmarker; Eventtype eventtype; UBYTE *eventptr; }; struct SysEx { struct MinNode se_node; /* node for linking */ //ULONG se_size; /* sysex size */ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) UBYTE se_data[]; /* sysex data */ #else UBYTE se_data[0]; /* sysex data */ #endif }; #define MAXTRAX 40 #define MIDIBUFSIZE 256 struct Global { UBYTE trackct; UBYTE donecount; UBYTE masterswitch; ULONG division; ULONG tempo; ULONG lastclock; //ULONG abstimeHI, abstimeLO; //uint64_t abstime; double abstime; //struct DecTrack *pDTrack[MAXTRAX]; struct DecTrack dtrack[MAXTRAX]; UBYTE *ptrack[MAXTRAX]; UBYTE *ptrackstart[MAXTRAX]; UBYTE *ptrackend[MAXTRAX]; ULONG fillclock[2]; ULONG fillstate[2]; ULONG buftempo[2]; UBYTE lastRSchan; /* These buffers hold the notes translated */ /* from the midifile file for playback */ UBYTE pfillbuf[2][MIDIBUFSIZE]; struct MinList SysExList[2]; }; static UBYTE CommandSize[7]= { 2, /* 80-8f */ 2, /* 90-9f */ 2, /* a0-af */ 2, /* b0-bf */ 1, /* c0-cf */ 1, /* d0-df */ 2, /* e0-ef */ }; static UBYTE CommonSize[8]= { 0, /* f0 */ 1, /* f1 */ 2, /* f2 */ 1, /* f3 */ 0, /* f4 */ 0, /* f5 */ 0, /* f6 */ 0, /* f7 */ }; static UBYTE ProgramReset[] = { MS_Prog | 0, 0, MS_Prog | 1, 0, MS_Prog | 2, 0, MS_Prog | 3, 0, MS_Prog | 4, 0, MS_Prog | 5, 0, MS_Prog | 6, 0, MS_Prog | 7, 0, MS_Prog | 8, 0, MS_Prog | 9, 0, MS_Prog | 10, 0, MS_Prog | 11, 0, MS_Prog | 12, 0, MS_Prog | 13, 0, MS_Prog | 14, 0, MS_Prog | 15, 0 }; static UBYTE ResetAllControllers[] = { MS_Ctrl | 0, MM_ResetCtrl, 0, MS_Ctrl | 1, MM_ResetCtrl, 0, MS_Ctrl | 2, MM_ResetCtrl, 0, MS_Ctrl | 3, MM_ResetCtrl, 0, MS_Ctrl | 4, MM_ResetCtrl, 0, MS_Ctrl | 5, MM_ResetCtrl, 0, MS_Ctrl | 6, MM_ResetCtrl, 0, MS_Ctrl | 7, MM_ResetCtrl, 0, MS_Ctrl | 8, MM_ResetCtrl, 0, MS_Ctrl | 9, MM_ResetCtrl, 0, MS_Ctrl | 10, MM_ResetCtrl, 0, MS_Ctrl | 11, MM_ResetCtrl, 0, MS_Ctrl | 12, MM_ResetCtrl, 0, MS_Ctrl | 13, MM_ResetCtrl, 0, MS_Ctrl | 14, MM_ResetCtrl, 0, MS_Ctrl | 15, MM_ResetCtrl, 0 }; static UBYTE AllNotesOff[] = { MS_Ctrl | 0, MM_AllOff, 0, MS_Ctrl | 1, MM_AllOff, 0, MS_Ctrl | 2, MM_AllOff, 0, MS_Ctrl | 3, MM_AllOff, 0, MS_Ctrl | 4, MM_AllOff, 0, MS_Ctrl | 5, MM_AllOff, 0, MS_Ctrl | 6, MM_AllOff, 0, MS_Ctrl | 7, MM_AllOff, 0, MS_Ctrl | 8, MM_AllOff, 0, MS_Ctrl | 9, MM_AllOff, 0, MS_Ctrl | 10, MM_AllOff, 0, MS_Ctrl | 11, MM_AllOff, 0, MS_Ctrl | 12, MM_AllOff, 0, MS_Ctrl | 13, MM_AllOff, 0, MS_Ctrl | 14, MM_AllOff, 0, MS_Ctrl | 15, MM_AllOff, 0 }; static UBYTE AllSoundsOff[] = { MS_Ctrl | 0, 0x78, 0, MS_Ctrl | 1, 0x78, 0, MS_Ctrl | 2, 0x78, 0, MS_Ctrl | 3, 0x78, 0, MS_Ctrl | 4, 0x78, 0, MS_Ctrl | 5, 0x78, 0, MS_Ctrl | 6, 0x78, 0, MS_Ctrl | 7, 0x78, 0, MS_Ctrl | 8, 0x78, 0, MS_Ctrl | 9, 0x78, 0, MS_Ctrl | 10, 0x78, 0, MS_Ctrl | 11, 0x78, 0, MS_Ctrl | 12, 0x78, 0, MS_Ctrl | 13, 0x78, 0, MS_Ctrl | 14, 0x78, 0, MS_Ctrl | 15, 0x78, 0 }; struct Library *CamdBase; #ifdef PLATFORM_AMIGAOS3 struct RealTimeBase *RealTimeBase; #else struct Library *RealTimeBase; #endif static qboolean midi_playing, midi_paused; static UBYTE *smfdata; static struct Global *glob; static struct MidiNode *pMidiNode; static struct MidiLink *pMidiLink; static struct Task *playerTask; static struct Task *parentTask; static struct Player *pPlayer; #define CHECK_MIDI_ALIVE() \ do { \ if (!midi_playing) \ { \ if (handle) \ *handle = NULL; \ return; \ } \ } while (0) static void MIDI_SetVolume (void **handle, float value) { CHECK_MIDI_ALIVE(); } static void MIDI_Rewind (void **handle) { CHECK_MIDI_ALIVE(); // mididrv_rewind unused } static void MIDI_Update (void **handle) { CHECK_MIDI_ALIVE(); // mididrv_advance nothing to do here } static char *MIDI_GetDeviceName(char *dst, size_t dstsize) { APTR key; struct MidiCluster *cluster; char *retname = NULL; key = LockCAMD(CD_Linkages); if (key) { cluster = NextCluster(NULL); while (cluster) { //Con_Printf("%s CAMD device: %s\n", __thisfunc__, cluster->mcl_Node.ln_Name); if (strstr(cluster->mcl_Node.ln_Name, "out")) { q_strlcpy(dst, cluster->mcl_Node.ln_Name, dstsize); retname = dst; break; } cluster = NextCluster(cluster); } UnlockCAMD(key); } return retname; } qboolean MIDI_Init(void) { static const char midi_name[] = "Hexen II Player"; static const char mlink_comment[] = "Hexen II Player Link"; static const char player_name[] = "Hexen II player"; char linkName[32]; if (midi_amiga_camd.available) return true; BGM_RegisterMidiDRV(&midi_amiga_camd); if (safemode || COM_CheckParm("-nomidi")) return false; if (!(CamdBase = OpenLibrary("camd.library", 37))) { Con_Printf ("Can't open camd.library\n"); return false; } #ifdef PLATFORM_AMIGAOS3 RealTimeBase = (struct RealTimeBase *) OpenLibrary("realtime.library", 37); #else RealTimeBase = OpenLibrary("realtime.library", 37); #endif if (!RealTimeBase) { Con_Printf ("Can't open realtime.library\n"); MIDI_Cleanup(); return false; } if (!MIDI_GetDeviceName(linkName, sizeof(linkName))) { Con_Printf ("No output MIDI device found\n"); MIDI_Cleanup(); return false; } if (!(pMidiNode = CreateMidi( MIDI_Name, (IPTR)midi_name, MIDI_MsgQueue, 0, MIDI_SysExSize, 4096, TAG_END))) { Con_Printf("Can't create MIDI Node\n"); MIDI_Cleanup(); return false; } if (!(pMidiLink = AddMidiLink( pMidiNode, MLTYPE_Sender, MLINK_Comment, (IPTR)mlink_comment, MLINK_Parse, TRUE, MLINK_Location, (IPTR)linkName, TAG_END))) { Con_Printf("Can't create MIDI Link\n"); MIDI_Cleanup(); return false; } ParseMidi(pMidiLink, ProgramReset, sizeof(ProgramReset)); Delay(5); ParseMidi(pMidiLink, ResetAllControllers, sizeof(ResetAllControllers)); Delay(5); if (!(pPlayer = CreatePlayer( PLAYER_Name, (IPTR)player_name, PLAYER_Conductor, (IPTR)-1, TAG_END))) { Con_Printf("Can't create the RealTime player\n"); MIDI_Cleanup(); return false; } parentTask = FindTask(NULL); Con_Printf("%s initialized.\nMIDI link: %s\n", midi_amiga_camd.desc, linkName); midi_paused = false; midi_amiga_camd.available = true; return true; } static ULONG GetDelta(UBYTE **value) { ULONG newval = 0; UWORD i; UBYTE dat; for (i = 0; i < 4; i++) { dat = *((*value)++); newval = newval<<7; newval |= dat & 0x7f; if (dat < 0x80) break; } return newval; } static UBYTE *DecodeEvent(UBYTE *ptdata, struct DecTrack *pDT) { ULONG length; BOOL skipit; UBYTE status; UBYTE data; UBYTE comsize; pDT->absdelta = 0; /* is this track all used up? */ if (pDT->trackend) return NULL; if (ptdata >= pDT->endmarker) { Con_DPrintf("Warning: missing proper track end marker in track %d.\n", pDT->tracknum); pDT->eventptr = ptdata; pDT->eventtype = Event_trackend; pDT->trackend = TRUE; return ptdata; } skipit = FALSE; do { /* Decode delta */ ULONG delta = GetDelta(&ptdata); pDT->absdelta += delta; pDT->nexclock += delta; pDT->eventtype = Event_ignore; data = *ptdata; if (data & 0x80) /* Event with status ($80-$FF): decode new status */ { ptdata++; if (data < 0xf0) /* "Normal" events? */ { status = data; comsize = CommandSize[(data&0x7f)>>4]; pDT->status = status; pDT->comsize = comsize; pDT->rstatus = 0; /* No running status was used */ pDT->eventtype = Event_playable; skipit = FALSE; } else { if (data < 0xf8) /* System Common Event? */ { status = data; comsize = CommonSize[status-0xf0]; skipit = TRUE; if (status==0xf0 || status==0xf7) /* It's a sysex event */ { pDT->eventptr = ptdata-1; pDT->eventtype = Event_sysex; skipit = FALSE; length = GetDelta(&ptdata); ptdata += length; } } else { status = data; comsize = 0; skipit = TRUE; if (data == 0xff) /* It's a meta event ($ff) */ { UBYTE metatype; metatype = *(ptdata++); if (metatype == 0x2F) /* track end marker */ { pDT->eventptr = ptdata; pDT->eventtype = Event_trackend; pDT->trackend = TRUE; skipit = FALSE; } else { if (metatype == 0x51) /* Tempo change */ { pDT->eventptr = ptdata; pDT->eventtype = Event_tempo; skipit = FALSE; } length = GetDelta(&ptdata); ptdata += length; } } } } } else /* Event without status ($00-$7F): use running status */ { status = pDT->status; if (status == 0) { Con_DPrintf("Warning: Data bytes without initial status in track %d.\n", pDT->tracknum); comsize = 1; skipit = TRUE; } else { skipit = FALSE; comsize = pDT->comsize; pDT->rstatus = status; pDT->eventtype = Event_playable; } } if (comsize > 0) pDT->d1 = *ptdata++; else pDT->d1 = 0; if (comsize > 1) pDT->d2 = *ptdata++; else pDT->d2 = 0; } while (skipit); return ptdata; } static void CollectEvents(struct Global *glob) { UWORD track; ULONG lowclock; UBYTE mswitch = glob->masterswitch; ULONG nexttempo = glob->tempo; ULONG pos = 0; ULONG delta = 0; if (glob->donecount < glob->trackct) { lowclock = 0xffffffff; for (track = 0; track < glob->trackct; track++) { if (glob->dtrack[track].nexclock < lowclock && glob->ptrack[track]) lowclock = glob->dtrack[track].nexclock; } delta = lowclock - glob->lastclock; glob->lastclock = lowclock; for (track = 0; track < glob->trackct; track++) { struct DecTrack *pDT = &glob->dtrack[track]; if ((pDT->nexclock == lowclock) && glob->ptrack[track]) { do { /* Transfer event to parse buffer and handle successor */ switch (pDT->eventtype) { case Event_playable: { if (pos >= MIDIBUFSIZE-3) { Con_DPrintf("MIDI buffer overflow (>=%d), dropped event\n", MIDIBUFSIZE); break; } if (pDT->rstatus == glob->lastRSchan) { /* Running status */ if (pDT->comsize>0) *(glob->pfillbuf[mswitch] + pos++) = pDT->d1; if (pDT->comsize>1) *(glob->pfillbuf[mswitch] + pos++) = pDT->d2; } else { /* New status so store status and data bytes */ *(glob->pfillbuf[mswitch] + pos++) = pDT->status; glob->lastRSchan = pDT->status; if (pDT->comsize>0) *(glob->pfillbuf[mswitch] + pos++) = pDT->d1; if (pDT->comsize>1) *(glob->pfillbuf[mswitch] + pos++) = pDT->d2; } } break; case Event_sysex: { /* Link SysEx into Queue */ UBYTE *src = pDT->eventptr; UBYTE hdr = *src++; struct SysEx *se = NULL; if (hdr == 0xf0) { ULONG length = GetDelta(&src); if ((se = (struct SysEx *) calloc(1, sizeof(struct SysEx)+length+1)) != NULL) { UBYTE *dst = se->se_data; //se->se_size = length+1; *dst++ = 0xf0; while (length--) *dst++ = *src++; } } if (hdr == 0xf7) { ULONG length = GetDelta(&src); if ((se = (struct SysEx *) calloc(1, sizeof(struct SysEx)+length+1)) != NULL) { UBYTE *dst = se->se_data; //se->se_size = length; while (length--) *dst++ = *src++; } } if (se) { AddTail((struct List *)&glob->SysExList[mswitch], (struct Node *)se); } } break; case Event_tempo: { UBYTE *meta = pDT->eventptr; LONG metalen = GetDelta(&meta); nexttempo = 0; while (metalen-- > 0) nexttempo = (nexttempo << 8) | *(meta++); //Message("Tempo meta-event (%ld BPM) at nexclock %ld!", NULL, 60*1000000/nexttempo, pDT->nexclock); } break; case Event_trackend: { glob->donecount++; } break; default: { Con_DPrintf("Unknown MIDI event $%02x\n", pDT->eventtype); } break; } glob->ptrack[track] = DecodeEvent(glob->ptrack[track], pDT); } while ((pDT->absdelta == 0) && glob->ptrack[track]); } } } glob->fillstate[mswitch] = pos; glob->buftempo[mswitch] = nexttempo; //AddAbsTime(&glob->abstimeLO, &glob->abstimeHI, glob->tempo, delta, glob->division); //glob->abstime += ((uint64_t)glob->tempo*delta)/glob->division; glob->abstime += ((double)glob->tempo*delta)/glob->division; //glob->fillclock[mswitch] = CalcFillClock(glob->abstimeLO, glob->abstimeHI, 833); glob->fillclock[mswitch] = (ULONG)(glob->abstime / 833); glob->tempo = nexttempo; } static void PlayerFunc(void) { ULONG sigs; struct SysEx *se, *nse; SetTaskPri(FindTask(NULL), 25); /* Transfer the events the fist buffer */ glob->masterswitch = 0; CollectEvents(glob); while (TRUE) { /* Set the alarm */ if (!SetPlayerAttrs(pPlayer, PLAYER_AlarmTime, glob->fillclock[glob->masterswitch], PLAYER_Ready, TRUE, TAG_END)) { break; } /* Fill the other buffer */ glob->masterswitch ^= 1; CollectEvents(glob); sigs = Wait(SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_C); if (sigs & SIGBREAKF_CTRL_C) { break; } /* Send off one buffer... */ for (se = (struct SysEx *)glob->SysExList[glob->masterswitch^1].mlh_Head; (nse = (struct SysEx *)se->se_node.mln_Succ) != NULL; se = nse) { PutSysEx(pMidiLink, se->se_data); Remove((struct Node *)se); free(se); } if (glob->fillstate[glob->masterswitch^1] != 0) { ParseMidi(pMidiLink, glob->pfillbuf[glob->masterswitch^1], glob->fillstate[glob->masterswitch^1]); } /* Loop the music */ if (glob->donecount >= glob->trackct) { //break; UWORD track; //Con_Printf("Music loop\n"); glob->donecount = 0; glob->lastclock = 0; for (track = 0; track < glob->trackct; track++) { glob->dtrack[track].nexclock = 0; glob->dtrack[track].trackend = FALSE; glob->ptrack[track] = DecodeEvent(glob->ptrackstart[track], &glob->dtrack[track]); } CollectEvents(glob); } } // let the parent thread know that we are done Forbid(); midi_playing = false; Signal(parentTask, SIGBREAKF_CTRL_F); } static void *MIDI_Play (const char *filename) { static const char task_name[] = "Hexen II CAMD player task"; struct SMFHeader *hdr; UBYTE *pbyte; long smfdatasize; UWORD track; LONG error; ULONG id; if (!midi_amiga_camd.available) return NULL; if (!filename || !*filename) { Con_DPrintf("null music file name\n"); return NULL; } MIDI_Stop (NULL); smfdata = (UBYTE *) FS_LoadMallocFile (filename, NULL); if (!smfdata) { Con_DPrintf("Couldn't open %s\n", filename); return NULL; } smfdatasize = fs_filesize; if (smfdatasize < 34) { Con_Printf("MIDI file too short.\n"); MIDI_Stop (NULL); return false; } pbyte = smfdata; if (memcmp(pbyte,"RIFF",4) == 0 && memcmp(pbyte +8,"RMID",4) == 0 && memcmp(pbyte+12,"data",4) == 0) pbyte += 20; /* Microsoft RMID */ hdr = (struct SMFHeader *)pbyte; hdr->ChunkID = BigLong(hdr->ChunkID); hdr->VarLeng = BigLong(hdr->VarLeng); hdr->Format = BigShort(hdr->Format); hdr->Ntrks = (UWORD) BigShort(hdr->Ntrks); hdr->Division = BigShort(hdr->Division); if (hdr->ChunkID != ID_MTHD || hdr->VarLeng != 6 || (hdr->Format != 0 && hdr->Format != 1) || !hdr->Ntrks || hdr->Ntrks > MAXTRAX || hdr->Division <= 0) { Con_Printf("Can't recognize the MIDI format.\n"); MIDI_Stop (NULL); return false; } if (!(glob = (struct Global *) calloc(1, sizeof(struct Global)))) { Con_DPrintf("No memory for global MIDI variables\n"); MIDI_Stop (NULL); return false; } glob->division = hdr->Division; glob->lastRSchan = 0xf1; glob->tempo = 500000; NewList((struct List *)&glob->SysExList[0]); NewList((struct List *)&glob->SysExList[1]); glob->trackct = 0; pbyte += 14; /* sizeof(struct SMFHeader) */ while ((pbyte-smfdata < smfdatasize) && (glob->trackct < MAXTRAX)) { id = (ULONG) BigLong(*(ULONG *)pbyte); if (id == ID_MTRK) { if (glob->trackct > 0) glob->ptrackend[glob->trackct-1] = pbyte; if (glob->trackct == hdr->Ntrks) break; glob->ptrackstart[glob->trackct] = pbyte+8; glob->trackct++; pbyte += 4; } else { pbyte++; } } if (glob->trackct > 0) glob->ptrackend[glob->trackct-1] = pbyte; if (glob->trackct != hdr->Ntrks) { Con_Printf("Missing tracks. Only %d tracks found (%d expected).\n", glob->trackct, hdr->Ntrks); MIDI_Stop (NULL); return false; } for (track = 0; track < glob->trackct; track++) { glob->dtrack[track].tracknum = track+1; glob->dtrack[track].endmarker = glob->ptrackend[track]; glob->ptrack[track] = DecodeEvent(glob->ptrackstart[track], &glob->dtrack[track]); } /* start thread */ #ifdef __MORPHOS__ playerTask = (struct Task *)CreateNewProcTags( NP_Entry, (IPTR)PlayerFunc, NP_CodeType, CODETYPE_PPC, NP_Name, (IPTR)task_name, TAG_DONE); #else playerTask = (struct Task *)CreateNewProcTags( NP_Entry, (IPTR)PlayerFunc, NP_Name, (IPTR)task_name, TAG_DONE); #endif if (!playerTask) { Con_Printf("Can't create the MIDI player task\n"); MIDI_Stop (NULL); return false; } midi_playing = true; if (!SetPlayerAttrs(pPlayer, PLAYER_AlarmSigTask, (IPTR)playerTask, PLAYER_AlarmSigBit, SIGBREAKB_CTRL_E, //PLAYER_Ready, TRUE, PLAYER_ErrorCode, (IPTR)&error, TAG_END)) { Con_Printf("Can't set the RealTime player attrs, error %ld\n", (long)error); MIDI_Stop (NULL); return false; } SetConductorState(pPlayer, CONDSTATE_RUNNING, 0); Con_Printf ("Started midi music %s\n", filename); return glob; } static void MIDI_Pause (void **handle) { CHECK_MIDI_ALIVE(); SetConductorState(pPlayer, CONDSTATE_PAUSED, 0); ParseMidi(pMidiLink, AllNotesOff, sizeof(AllNotesOff)); ParseMidi(pMidiLink, AllSoundsOff, sizeof(AllSoundsOff)); } static void MIDI_Resume (void **handle) { CHECK_MIDI_ALIVE(); SetConductorState(pPlayer, CONDSTATE_RUNNING, 0); } static void MIDI_Stop (void **handle) { //CHECK_MIDI_ALIVE(); if (pPlayer) { SetConductorState(pPlayer, CONDSTATE_STOPPED, 0); SetPlayerAttrs(pPlayer, PLAYER_Ready, FALSE, TAG_END); } if (pMidiLink) { ParseMidi(pMidiLink, AllNotesOff, sizeof(AllNotesOff)); ParseMidi(pMidiLink, AllSoundsOff, sizeof(AllSoundsOff)); Delay(10); } if (playerTask) { if (midi_playing) { SetSignal(SIGBREAKF_CTRL_F, 0); Signal(playerTask, SIGBREAKF_CTRL_C); Wait(SIGBREAKF_CTRL_F); } playerTask = NULL; } if (glob) { struct MinNode *se, *nse; for (se = glob->SysExList[0].mlh_Head; (nse = se->mln_Succ) != NULL; se = nse) { Remove((struct Node *)se); free(se); } for (se = glob->SysExList[1].mlh_Head; (nse = se->mln_Succ) != NULL; se = nse) { Remove((struct Node *)se); free(se); } free(glob); glob = NULL; } if (smfdata) { free(smfdata); smfdata = NULL; } } void MIDI_Cleanup(void) { if (midi_amiga_camd.available) { midi_amiga_camd.available = false; MIDI_Stop (NULL); } if (pMidiLink) { RemoveMidiLink(pMidiLink); pMidiLink = NULL; } if (pMidiNode) { DeleteMidi(pMidiNode); pMidiNode = NULL; } if (pPlayer) { DeletePlayer(pPlayer); pPlayer = NULL; } if (RealTimeBase) { CloseLibrary((struct Library *)RealTimeBase); RealTimeBase = NULL; } if (CamdBase) { CloseLibrary(CamdBase); CamdBase = NULL; } } engine/h2shared/midi_drv.h000066400000000000000000000032451444734033100160000ustar00rootroot00000000000000/* * midi_drv.h - MIDI driver exports for BGM interface * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2010-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _MIDI_DRV_H_ #define _MIDI_DRV_H_ typedef qboolean (*MIDI_INIT)(void); typedef void (*MIDI_SHUTDOWN)(void); typedef void *(*MIDI_OPEN)(const char *); typedef void (*MIDI_ADVANCE)(void **); typedef void (*MIDI_REWIND)(void **); typedef void (*MIDI_CLOSE)(void **); typedef void (*MIDI_PAUSE)(void **); typedef void (*MIDI_RESUME)(void **); typedef void (*MIDI_SETVOL)(void **, float); typedef struct midi_driver_s { qboolean available; const char *desc; MIDI_INIT mididrv_initialize; MIDI_SHUTDOWN mididrv_shutdown; MIDI_OPEN mididrv_open; MIDI_ADVANCE mididrv_advance; MIDI_REWIND mididrv_rewind; MIDI_CLOSE mididrv_close; MIDI_PAUSE mididrv_pause; MIDI_RESUME mididrv_resume; MIDI_SETVOL mididrv_setvol; struct midi_driver_s *next; } midi_driver_t; #endif /* _MIDI_DRV_H_ */ engine/h2shared/midi_mac.c000066400000000000000000000134401444734033100157360ustar00rootroot00000000000000/* midi_mac.c -- MIDI module for Mac OS using QuickTime. * Based on the macglquake project with adjustments to make * it work with Mac OS X and Hexen II: Hammer of Thyrion. * (FIXME: Maybe update this to use QuickTime TunePlayer, * as in native_midi_mac of SDL_mixer, sometime?) * * Copyright (C) 2002 contributors of the macglquake project * Copyright (C) 2006 Levent Yavas * Copyright (C) 2006-2011 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef __LP64__ #error QuickTime midi is not for 64 bit #endif #include "quakedef.h" #include "bgmusic.h" #include "midi_drv.h" #include /* */ #include static Movie midiTrack = NULL; static qboolean midi_paused; /* prototypes of functions exported to BGM: */ static void *MIDI_Play (const char *filename); static void MIDI_Update (void **handle); static void MIDI_Rewind (void **handle); static void MIDI_Stop (void **handle); static void MIDI_Pause (void **handle); static void MIDI_Resume (void **handle); static void MIDI_SetVolume (void **handle, float value); static midi_driver_t midi_mac_qt = { false, /* init success */ "QuickTime midi for Mac", MIDI_Init, MIDI_Cleanup, MIDI_Play, MIDI_Update, MIDI_Rewind, MIDI_Stop, MIDI_Pause, MIDI_Resume, MIDI_SetVolume, NULL }; #define CHECK_MIDI_ALIVE() \ do { \ if (!midiTrack) \ { \ if (handle) \ *handle = NULL; \ return; \ } \ } while (0) static void MIDI_SetVolume (void **handle, float value) { CHECK_MIDI_ALIVE(); SetMovieVolume (midiTrack, (short)(value * 256.0f)); } static void MIDI_Rewind (void **handle) { CHECK_MIDI_ALIVE(); GoToBeginningOfMovie (midiTrack); StartMovie (midiTrack); } static void MIDI_Update (void **handle) { CHECK_MIDI_ALIVE(); /* let QuickTime get some time */ MoviesTask (midiTrack, 0); if (IsMovieDone (midiTrack)) { if (bgmloop) { GoToBeginningOfMovie (midiTrack); StartMovie (midiTrack); } else { DisposeMovie (midiTrack); midiTrack = NULL; if (handle) *handle = NULL; } } } qboolean MIDI_Init(void) { OSErr theErr; if (midi_mac_qt.available) return true; BGM_RegisterMidiDRV(&midi_mac_qt); if (safemode || COM_CheckParm("-nomidi")) return false; theErr = EnterMovies (); if (theErr != noErr) { Con_Printf ("Unable to initialize QuickTime.\n"); return false; } Con_Printf("%s initialized.\n", midi_mac_qt.desc); midi_paused = false; midi_mac_qt.available = true; return true; } static void *MIDI_Play (const char *filename) { #define TEMP_MIDINAME "tmpmusic.mid" char midipath[MAX_OSPATH]; byte *buf; size_t len; int err = 0; FSSpec midiSpec; FSRef midiRef; short midiRefNum; if (!midi_mac_qt.available) return NULL; if (!filename || !*filename) { Con_DPrintf("null music file name\n"); return NULL; } /* midi files are small: safe to load onto hunk */ buf = FS_LoadTempFile (filename, NULL); if (!buf) { Con_DPrintf("Couldn't open %s\n", filename); return NULL; } len = (size_t) fs_filesize; /* Using NewMovieFromDataRef() to import midi data to QT * movies would return -2048 if done wrong; using a Data * Reference Extension is advised (TechNote 1195). Lazy * and ugly workaround is simply extracting the file. */ Con_DPrintf("Extracting %s from pakfile\n", filename); FS_MakePath_BUF(FS_USERBASE, &err, midipath, sizeof(midipath), TEMP_MIDINAME); if (err == 0) { FILE *f = fopen (midipath, "wb"); if (!f) err = 1; else { err = (fwrite(buf, 1, len, f) != len); fclose (f); } } if (err != 0) { Con_Printf("Error extracting %s from pak\n", filename); return NULL; } err = FSPathMakeRef ((UInt8 *)midipath, &midiRef, NULL); if (err != noErr) { Con_Printf ("Error getting FSRef for %s\n", midipath); return NULL; } err = FSGetCatalogInfo (&midiRef, kFSCatInfoNone, NULL, NULL, &midiSpec, NULL); if (err != noErr) { Con_Printf ("Error getting FSSpec for %s\n", midipath); return NULL; } err = OpenMovieFile (&midiSpec, &midiRefNum, fsRdPerm); if (err != noErr) { Con_Printf ("OpenMovieStream error opening midi file\n"); return NULL; } err = NewMovieFromFile (&midiTrack, midiRefNum, NULL, NULL, newMovieActive, NULL); if (err != noErr || !midiTrack) { Con_Printf ("QuickTime error in creating midi stream\n"); return NULL; } CloseMovieFile (midiRefNum); /* data now in memory */ GoToBeginningOfMovie (midiTrack); PrerollMovie (midiTrack, 0, 0); SetMovieVolume (midiTrack, (short)(bgmvolume.value * 256.0f)); StartMovie (midiTrack); Con_Printf ("Started midi music %s\n", filename); return midiTrack; } static void MIDI_Pause (void **handle) { CHECK_MIDI_ALIVE(); if (!midi_paused) { StopMovie (midiTrack); midi_paused = true; } } static void MIDI_Resume (void **handle) { CHECK_MIDI_ALIVE(); if (midi_paused) { StartMovie (midiTrack); midi_paused = false; } } static void MIDI_Stop (void **handle) { CHECK_MIDI_ALIVE(); StopMovie (midiTrack); DisposeMovie (midiTrack); midiTrack = NULL; midi_paused = false; } void MIDI_Cleanup(void) { if (midi_mac_qt.available) { midi_mac_qt.available = false; Con_Printf("%s: closing QuickTime.\n", __thisfunc__); ExitMovies (); } } engine/h2shared/midi_nul.c000066400000000000000000000020611444734033100157710ustar00rootroot00000000000000/* midi_nul.c -- NULL midi driver * * Copyright (C) 2006-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "bgmusic.h" #include "midi_drv.h" qboolean MIDI_Init (void) { /* don't bother doing BGM_RegisterMidiDRV() */ Con_Printf("MIDI_DRV: disabled at compile time.\n"); return false; } void MIDI_Cleanup(void) { } engine/h2shared/midi_osx.c000066400000000000000000000247061444734033100160160ustar00rootroot00000000000000/* based on an SDL_mixer code: * native_midi_macosx: Native Midi support on Mac OS X for the SDL_mixer library * Copyright (C) 2009 Ryan C. Gordon * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. * * Adaptation to uHexen2: * Copyright (C) 2012 O. Sezer * * Only for Mac OS X using Core MIDI and requiring version * 10.2 or later. Use QuickTime with midi_mac.c, otherwise. */ #include "quakedef.h" #include "bgmusic.h" #include "midi_drv.h" #include /* ComponentDescription */ #include #include #include #if (MAC_OS_X_VERSION_MIN_REQUIRED < 1030 && (defined(__ppc__)||defined(__POWERPC__))) #define OLD_TRACK_DURATION #endif #if (MAC_OS_X_VERSION_MIN_REQUIRED < 1050) #define AUGraphNodeInfo_FN(_graph,_node,_desc,_unit) \ AUGraphGetNodeInfo((_graph),(_node),(_desc),NULL,NULL,(_unit)) typedef struct ComponentDescription AudioComponentDesc_t; /* MusicSequenceLoadSMFData() (avail. in 10.2, no 64 bit) is * equivalent to calling MusicSequenceLoadSMFDataWithFlags() * with a flags value of 0 (avail. in 10.3, avail. 64 bit). * So, we use MusicSequenceLoadSMFData() for powerpc versions * but the *WithFlags() on intel which require 10.4 anyway. */ #if defined(__ppc__) || defined(__POWERPC__) #define MusicSequenceFileLoadData_FN(_seq,_data,_ftid,_flag) \ MusicSequenceLoadSMFData((_seq),(_data)) #else /* 10.4 - intel: */ #define MusicSequenceFileLoadData_FN(_seq,_data,_ftid,_flag) \ MusicSequenceLoadSMFDataWithFlags((_seq),(_data),(_flag)) #endif #else /* 10.5 OR NEWER: */ #define AUGraphNodeInfo_FN(_graph,_node,_desc,_unit) \ AUGraphNodeInfo((_graph),(_node),(_desc),(_unit)) /* AUGraphNodeInfo() is changed to take an AudioComponentDescription* * desc parameter instead of a ComponentDescription* in the 10.6 SDK. * AudioComponentDescription is in 10.6 or newer SDKs, but is actually * the same as ComponentDescription with 20 bytes of size and same * offsets and names of all members, therefore is binary compatible. */ #if !defined(AUDIO_UNIT_VERSION) || ((AUDIO_UNIT_VERSION + 0) < 1060) typedef struct ComponentDescription AudioComponentDesc_t; #else /* SDK 10.6 or newer : */ typedef AudioComponentDescription AudioComponentDesc_t; #endif #define MusicSequenceFileLoadData_FN(_seq,_data,_ftid,_flag) \ MusicSequenceFileLoadData((_seq),(_data),(_ftid),(_flag)) #endif /* -- compat macros */ /* prototypes of functions exported to BGM: */ static void *MIDI_Play (const char *filename); static void MIDI_Update (void **handle); static void MIDI_Rewind (void **handle); static void MIDI_Stop (void **handle); static void MIDI_Pause (void **handle); static void MIDI_Resume (void **handle); static void MIDI_SetVolume (void **handle, float value); static midi_driver_t midi_mac_core = { false, /* init success */ "Core MIDI for Mac OS X", MIDI_Init, MIDI_Cleanup, MIDI_Play, MIDI_Update, MIDI_Rewind, MIDI_Stop, MIDI_Pause, MIDI_Resume, MIDI_SetVolume, NULL }; typedef struct _CoreMidiSong { MusicPlayer player; MusicSequence sequence; MusicTimeStamp endTime; MusicTimeStamp pauseTime; AudioUnit audiounit; } CoreMidiSong; static CoreMidiSong *currentsong = NULL; static qboolean midi_paused; #define CHECK_MIDI_ALIVE() \ do { \ if (!currentsong) \ { \ if (handle) \ *handle = NULL; \ return; \ } \ } while (0) static void MIDI_SetVolume (void **handle, float value) { CHECK_MIDI_ALIVE(); if (currentsong->audiounit) { AudioUnitSetParameter(currentsong->audiounit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, value, 0); } } static void MIDI_Rewind (void **handle) { CHECK_MIDI_ALIVE(); MusicPlayerSetTime(currentsong->player, 0); } static void MIDI_Update (void **handle) { MusicTimeStamp currentTime; CHECK_MIDI_ALIVE(); currentTime = 0; MusicPlayerGetTime(currentsong->player, ¤tTime); if (currentTime < currentsong->endTime || currentTime >= kMusicTimeStamp_EndOfTrack) { return; } if (bgmloop) { MusicPlayerSetTime(currentsong->player, 0); } else { MIDI_Stop(NULL); if (handle) *handle = NULL; } } qboolean MIDI_Init(void) { if (midi_mac_core.available) return true; BGM_RegisterMidiDRV(&midi_mac_core); if (safemode || COM_CheckParm("-nomidi")) return false; midi_mac_core.available = true; /* always available. */ return true; } /* https://lists.apple.com/archives/Coreaudio-api/2003/Jul/msg00370.html * figure out sequence length. */ static OSStatus GetSequenceLength(MusicSequence sequence, MusicTimeStamp *_sequenceLength) { #ifdef OLD_TRACK_DURATION static qboolean old_osx = false; #endif UInt32 ntracks, i; MusicTimeStamp sequenceLength = 0; OSStatus err; err = MusicSequenceGetTrackCount(sequence, &ntracks); if (err != noErr) return err; for (i = 0; i < ntracks; ++i) { MusicTrack track; #ifdef OLD_TRACK_DURATION MusicEventIterator iter = NULL; #endif MusicTimeStamp tracklen = 0; UInt32 tracklenlen = sizeof (tracklen); err = MusicSequenceGetIndTrack(sequence, i, &track); if (err != noErr) return err; #ifndef OLD_TRACK_DURATION err = MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, &tracklen, &tracklenlen); if (err != noErr) return err; if (sequenceLength < tracklen) sequenceLength = tracklen; #else if (!old_osx) { /* kSequenceTrackProperty_TrackLength (5) needs 10.3 and newer. * so, the following returns error when run on 10.2 or older. */ err = MusicTrackGetProperty(track, 5, &tracklen, &tracklenlen); if (err != noErr) { if (i != 0) return err; old_osx = true; goto altern; } if (sequenceLength < tracklen) sequenceLength = tracklen; } else { altern: if ((err = NewMusicEventIterator(track, &iter)) != noErr) goto fail; if ((err = MusicEventIteratorSeek(iter, kMusicTimeStamp_EndOfTrack)) != noErr) goto fail; if (!MusicEventIteratorPreviousEvent(iter)) { if (!MusicEventIteratorGetEventInfo(iter, &tracklen, NULL, NULL, NULL)) { tracklen += 4; /* add 4 beats - nice and arbitrary!!! */ if (tracklen > sequenceLength) sequenceLength = tracklen; } } fail: if (iter) DisposeMusicEventIterator (iter); if (err != noErr) return err; } #endif } *_sequenceLength = sequenceLength; Con_DPrintf("MIDI sequence len: %f\n", sequenceLength); return noErr; } /* we're looking for the sequence output audiounit. */ static OSStatus GetSequenceAudioUnit(MusicSequence sequence, AudioUnit *aunit) { AUGraph graph; UInt32 nodecount, i; OSStatus err; err = MusicSequenceGetAUGraph(sequence, &graph); if (err != noErr) return err; err = AUGraphGetNodeCount(graph, &nodecount); if (err != noErr) return err; for (i = 0; i < nodecount; i++) { AUNode node; AudioComponentDesc_t desc; if (AUGraphGetIndNode(graph, i, &node) != noErr) continue; if (AUGraphNodeInfo_FN(graph, node, &desc, aunit) != noErr) continue; if (desc.componentType != kAudioUnitType_Output) continue; if (desc.componentSubType != kAudioUnitSubType_DefaultOutput) continue; return noErr; /* found it! */ } return kAUGraphErr_NodeNotFound; } static void *MIDI_Play (const char *filename) { byte *buf; long len; CoreMidiSong *song = NULL; CFDataRef data = NULL; if (!midi_mac_core.available) return NULL; if (!filename || !*filename) { Con_DPrintf("null music file name\n"); return NULL; } /* midi files are small: safe to load onto hunk */ buf = FS_LoadTempFile (filename, NULL); if (!buf) { Con_DPrintf("Couldn't open %s\n", filename); return NULL; } len = fs_filesize; song = (CoreMidiSong *) Z_Malloc(sizeof(CoreMidiSong), Z_MAINZONE); if (NewMusicPlayer(&song->player) != noErr) goto fail; if (NewMusicSequence(&song->sequence) != noErr) goto fail; data = CFDataCreate(NULL, (const UInt8 *) buf, len); if (data == NULL) goto fail; if (MusicSequenceFileLoadData_FN(song->sequence, data, 0, 0) != noErr) goto fail; CFRelease(data); data = NULL; if (GetSequenceLength(song->sequence, &song->endTime) != noErr) goto fail; if (MusicPlayerSetSequence(song->player, song->sequence) != noErr) goto fail; MusicPlayerPreroll(song->player); GetSequenceAudioUnit(song->sequence, &song->audiounit); if (song->audiounit) { AudioUnitSetParameter(song->audiounit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, bgmvolume.value, 0); } MusicPlayerSetTime(song->player, 0); MusicPlayerStart(song->player); currentsong = song; midi_paused = false; return song; fail: currentsong = NULL; if (song) { if (song->sequence) DisposeMusicSequence(song->sequence); if (song->player) DisposeMusicPlayer(song->player); Z_Free(song); } if (data) CFRelease(data); return NULL; } static void MIDI_Pause (void **handle) { CHECK_MIDI_ALIVE(); if (!midi_paused) { MusicPlayerGetTime(currentsong->player, ¤tsong->pauseTime); MusicPlayerStop(currentsong->player); midi_paused = true; } } static void MIDI_Resume (void **handle) { CHECK_MIDI_ALIVE(); if (midi_paused) { MusicPlayerSetTime(currentsong->player, currentsong->pauseTime); MusicPlayerStart(currentsong->player); midi_paused = false; } } static void MIDI_Stop (void **handle) { CoreMidiSong *song = currentsong; CHECK_MIDI_ALIVE(); currentsong = NULL; MusicPlayerStop(song->player); // MusicPlayerSetSequence(song->player, NULL); /* see: https://bugzilla.libsdl.org/show_bug.cgi?id=4573 */ DisposeMusicSequence(song->sequence); DisposeMusicPlayer(song->player); Z_Free(song); } void MIDI_Cleanup(void) { if (midi_mac_core.available) { Con_Printf("%s: closing Core MIDI.\n", __thisfunc__); MIDI_Stop (NULL); midi_mac_core.available = false; } } engine/h2shared/midi_win.c000066400000000000000000000344441444734033100160020ustar00rootroot00000000000000/* * midi_win.c -- MIDI module for Windows using midiStream API * * Originally from Hexen II source (C) Raven Software Corp. * based on an old DirectX5 sample code. * Few bits from Doom Legacy: Copyright (C) 1998-2000 by DooM Legacy Team. * Multiple fixes and cleanups and adaptation into new Hammer of Thyrion * (uHexen2) code by O.Sezer: * Copyright (C) 2006-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include /*#include */ #include #include "midifile.h" #include "mid2strm.h" #include "quakedef.h" #include "winquake.h" #include "bgmusic.h" #include "midi_drv.h" /* prototypes of functions exported to BGM: */ static void *MIDI_Play (const char *filename); static void MIDI_Update (void **handle); static void MIDI_Rewind (void **handle); static void MIDI_Stop (void **handle); static void MIDI_Pause (void **handle); static void MIDI_Resume (void **handle); static void MIDI_SetVolume (void **handle, float value); static midi_driver_t midi_win_ms = { false, /* init success */ "midiStream for Windows", MIDI_Init, MIDI_Cleanup, MIDI_Play, MIDI_Update, MIDI_Rewind, MIDI_Stop, MIDI_Pause, MIDI_Resume, MIDI_SetVolume, NULL }; /* macros to be used with windows MIDIEVENT structure -> dwEvent */ #define MIDIEVENT_CHANNEL(x) (x & 0x0000000F) #define MIDIEVENT_TYPE(x) (x & 0x000000F0) #define MIDIEVENT_DATA1(x) ((x & 0x0000FF00) >> 8) #define MIDIEVENT_VOLUME(x) ((x & 0x007F0000) >> 16) static qboolean midi_file_open, midi_playing, midi_paused; static UINT device_id = MIDI_MAPPER, callback_status; static int buf_num, num_empty_bufs; static DWORD volume_cache[MIDI_CHANNELS]; static qboolean hw_vol_capable = false; static HMIDISTRM hStream; static convert_buf_t stream_bufs[NUM_STREAM_BUFFERS]; static HANDLE hBufferReturnEvent; static void FreeBuffers (void); static int StreamBufferSetup (const char *filename); static void CALLBACK MidiProc (HMIDIIN, UINT, DWORD_PTR, DWORD_PTR, DWORD_PTR); void MIDI_SetAllChannelVolumes (DWORD percent); void MIDI_SetChannelVolume (DWORD chn, DWORD percent); #define CHECK_MIDI_ALIVE() \ do { \ if (!midi_playing) \ { \ if (handle) \ *handle = NULL; \ return; \ } \ } while (0) static void MidiErrorMessageBox (MMRESULT mmr) { char temp[1024]; midiOutGetErrorText(mmr, temp, sizeof(temp)); Con_Printf("MIDI_DRV: %s\n", temp); } static void MIDI_SetVolume (void **handle, float value) { CHECK_MIDI_ALIVE(); if (hw_vol_capable) { DWORD val = (DWORD)(value * 65535.0f); midiOutSetVolume((HMIDIOUT)hStream, (val << 16) + val); } else { // MIDI_SetAllChannelVolumes((DWORD) (value * 1000.0f)); PostMessage(mainwindow, WM_MSTREAM_UPDATEVOLUMES, (DWORD) (value * 1000.0f), 0); } } static void MIDI_Rewind (void **handle) { CHECK_MIDI_ALIVE(); /* handled by converter module */ } static void MIDI_Update (void **handle) { CHECK_MIDI_ALIVE(); /* handled by callback */ } qboolean MIDI_Init(void) { MMRESULT mmr; MIDIOUTCAPS midi_caps; if (midi_win_ms.available) return true; BGM_RegisterMidiDRV(&midi_win_ms); if (safemode || COM_CheckParm("-nomidi")) return false; hBufferReturnEvent = CreateEvent(NULL, FALSE, FALSE, "uHexen2 Midi: Wait For Buffer Return"); mmr = midiStreamOpen(&hStream, &device_id, (DWORD)1, (DWORD_PTR)MidiProc, (DWORD_PTR)0, CALLBACK_FUNCTION); if (mmr != MMSYSERR_NOERROR) { MidiErrorMessageBox(mmr); return false; } midi_file_open = false; midi_playing = false; midi_paused = false; callback_status = 0; midi_win_ms.available = true; Con_Printf("%s initialized.\n", midi_win_ms.desc); /* try to see if the MIDI device supports midiOutSetVolume */ if (midiOutGetDevCaps(device_id, &midi_caps, sizeof(midi_caps)) == MMSYSERR_NOERROR) { if (midi_caps.dwSupport & MIDICAPS_VOLUME) { if (COM_CheckParm("-nohwmidivol")) Con_Printf("Hardware MIDI volume disabled by user\n"); else if (WinVista) /* http://msdn.microsoft.com/en-us/library/dd798480(VS.85).aspx#1 "This [midiOutSetVolume] function does not set the MIDI device volume when using a software synthesizer under Windows Vista or Windows 7, but instead alters the application-specific volume level in the system mixer. This means that if your application also outputs digital audio, the volume level of that audio will be reduced or increased by the same amount." */ Con_Printf("Hardware MIDI volume ignored (Vista/7)\n"); else { hw_vol_capable = true; Con_Printf("Using hardware MIDI volume adjustment\n"); } } } return true; } static void *MIDI_Play (const char *filename) { MMRESULT mmr; if (!midi_win_ms.available) return NULL; if (!filename || !*filename) { Con_DPrintf("null music file name\n"); return NULL; } if (StreamBufferSetup(filename)) { Con_DPrintf("Couldn't open %s\n", filename); return NULL; } Con_Printf("Started midi music %s\n", filename); midi_file_open = true; callback_status = 0; mmr = midiStreamRestart(hStream); if (mmr != MMSYSERR_NOERROR) { MidiErrorMessageBox(mmr); return NULL; } midi_playing = true; midi_paused = false; MIDI_SetVolume ((void **) &hStream, bgmvolume.value); return hStream; } static void MIDI_Pause (void **handle) { CHECK_MIDI_ALIVE(); if (!midi_paused) { midi_paused = true; midiStreamPause(hStream); } } static void MIDI_Resume (void **handle) { CHECK_MIDI_ALIVE(); if (midi_paused) { midi_paused = false; midiStreamRestart(hStream); } } static void MIDI_Stop (void **handle) { MMRESULT mmr; /*CHECK_MIDI_ALIVE();*/ if (handle) *handle = NULL; if (midi_file_open || midi_playing)/* || callback_status != STATUS_CALLBACKDEAD)*/ { midi_playing = midi_paused = false; if (callback_status != STATUS_CALLBACKDEAD && callback_status != STATUS_WAITINGFOREND) callback_status = STATUS_KILLCALLBACK; mmr = midiStreamStop(hStream); if (mmr != MMSYSERR_NOERROR) { MidiErrorMessageBox(mmr); return; } mmr = midiOutReset((HMIDIOUT)hStream); if (mmr != MMSYSERR_NOERROR) { MidiErrorMessageBox(mmr); return; } if (WaitForSingleObject(hBufferReturnEvent,DEBUG_CALLBACK_TIMEOUT) == WAIT_TIMEOUT) { Con_DPrintf("Timed out waiting for MIDI callback\n"); callback_status = STATUS_CALLBACKDEAD; } } if (callback_status == STATUS_CALLBACKDEAD) { callback_status = 0; if (midi_file_open) { ConverterCleanup(); FreeBuffers(); if (hStream) { mmr = midiStreamClose(hStream); if (mmr != MMSYSERR_NOERROR) MidiErrorMessageBox(mmr); hStream = NULL; } midi_file_open = false; } } } void MIDI_Cleanup(void) { MMRESULT mmr; if (!midi_win_ms.available) return; midi_win_ms.available = false; CloseHandle(hBufferReturnEvent); if (hStream) { mmr = midiStreamClose(hStream); if (mmr != MMSYSERR_NOERROR) MidiErrorMessageBox(mmr); hStream = NULL; } } /* FreeBuffers * * unprepares and frees all our buffers -- something we must do to * work around a bug in MMYSYSTEM that prevents a device from playing * back properly unless it is closed and reopened after each stop. */ static void FreeBuffers(void) { int i; MMRESULT mmr; for (i = 0; i < NUM_STREAM_BUFFERS; i++) { if (stream_bufs[i].prepared) { stream_bufs[i].prepared = FALSE; mmr = midiOutUnprepareHeader((HMIDIOUT)hStream, &stream_bufs[i].mh, sizeof(MIDIHDR)); if (mmr != MMSYSERR_NOERROR) MidiErrorMessageBox(mmr); } if (stream_bufs[i].mh.lpData) { Z_Free(stream_bufs[i].mh.lpData); stream_bufs[i].mh.lpData = NULL; } } } /* StreamBufferSetup * * Uses the filename to open a MIDI file. Then goes * about converting at least the first part of * that file into a midiStream buffer for playback. */ static int StreamBufferSetup(const char *filename) { int err, i; qboolean found_end = false; unsigned int flags; MMRESULT mmr; MIDIPROPTIMEDIV mptd; if (!hStream) { mmr = midiStreamOpen(&hStream, &device_id, (DWORD)1, (DWORD_PTR)MidiProc, (DWORD_PTR)0, CALLBACK_FUNCTION); if (mmr != MMSYSERR_NOERROR) { MidiErrorMessageBox(mmr); return 1; } } for (i = 0; i < NUM_STREAM_BUFFERS; i++) { stream_bufs[i].mh.dwBufferLength = OUT_BUFFER_SIZE; stream_bufs[i].mh.lpData = (LPSTR) Z_Malloc(OUT_BUFFER_SIZE, Z_MAINZONE); } if (ConverterInit(filename)) return 1; for (i = 0; i < MIDI_CHANNELS; i++) volume_cache[i] = VOL_CACHE_INIT; mptd.cbStruct = sizeof(mptd); mptd.dwTimeDiv = mfs.timediv; mmr = midiStreamProperty(hStream, (LPBYTE)&mptd, MIDIPROP_SET | MIDIPROP_TIMEDIV); if (mmr != MMSYSERR_NOERROR) { MidiErrorMessageBox(mmr); ConverterCleanup(); return 1; } num_empty_bufs = 0; flags = CONVERTF_RESET; for (buf_num = 0; buf_num < NUM_STREAM_BUFFERS; buf_num++) { /* Tell the converter to convert up to one entire buffer's length of output * data. Also, set a flag so it knows to reset any saved state variables it * may keep from call to call. */ stream_bufs[buf_num].start_ofs = 0; stream_bufs[buf_num].maxlen = OUT_BUFFER_SIZE; stream_bufs[buf_num].starttime = 0; stream_bufs[buf_num].times_up = FALSE; err = ConvertToBuffer(flags, &stream_bufs[buf_num]); if (err != CONVERTERR_NOERROR) { if (err == CONVERTERR_DONE) { found_end = true; } else { DEBUG_Printf("%s: Initial conversion pass failed\n", __thisfunc__); ConverterCleanup(); return 1; } } stream_bufs[buf_num].mh.dwBytesRecorded = stream_bufs[buf_num].bytes_in; if (!stream_bufs[buf_num].prepared) { mmr = midiOutPrepareHeader((HMIDIOUT)hStream, &stream_bufs[buf_num].mh, sizeof(MIDIHDR)); if (mmr != MMSYSERR_NOERROR) { MidiErrorMessageBox(mmr); ConverterCleanup(); return 1; } stream_bufs[buf_num].prepared = TRUE; } mmr = midiStreamOut(hStream, &stream_bufs[buf_num].mh, sizeof(MIDIHDR)); if (mmr != MMSYSERR_NOERROR) { MidiErrorMessageBox(mmr); break; } flags = 0; if (found_end) break; } buf_num = 0; return 0; } /* MidiProc * * the callback handler which continually refills MIDI data buffers * as they're returned to us from the audio subsystem. */ static void CALLBACK MidiProc(HMIDIIN hMidi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { MIDIEVENT *me; MIDIHDR *mh; MMRESULT mmr; int err; switch (uMsg) { case MOM_DONE: if (callback_status == STATUS_CALLBACKDEAD) return; num_empty_bufs++; if (callback_status == STATUS_WAITINGFOREND) { if (num_empty_bufs < NUM_STREAM_BUFFERS) { return; } else { callback_status = STATUS_CALLBACKDEAD; MIDI_Stop((void **)NULL); SetEvent(hBufferReturnEvent); return; } } /* this flag is set whenever the callback is waiting for all buffers to * come back. */ if (callback_status == STATUS_KILLCALLBACK) { /* count NUM_STREAM_BUFFERS-1 being returned for the last time */ if (num_empty_bufs < NUM_STREAM_BUFFERS) { return; } /* .. then send a stop message when we get the last buffer back */ else { callback_status = STATUS_CALLBACKDEAD; SetEvent(hBufferReturnEvent); return; } } /* fill an available buffer with audio data again */ if (midi_playing && num_empty_bufs) { stream_bufs[buf_num].start_ofs = 0; stream_bufs[buf_num].maxlen = OUT_BUFFER_SIZE; stream_bufs[buf_num].starttime = 0; stream_bufs[buf_num].bytes_in = 0; stream_bufs[buf_num].times_up = FALSE; err = ConvertToBuffer(0, &stream_bufs[buf_num]); if (err != CONVERTERR_NOERROR) { if (err == CONVERTERR_DONE) { callback_status = STATUS_WAITINGFOREND; return; } else { Con_Printf("MidiProc() conversion pass failed!"); ConverterCleanup(); return; } } stream_bufs[buf_num].mh.dwBytesRecorded = stream_bufs[buf_num].bytes_in; mmr = midiStreamOut(hStream, &stream_bufs[buf_num].mh, sizeof(MIDIHDR)); if (mmr != MMSYSERR_NOERROR) { MidiErrorMessageBox(mmr); ConverterCleanup(); return; } buf_num = (buf_num + 1) % NUM_STREAM_BUFFERS; num_empty_bufs--; } break; case MOM_POSITIONCB: mh = (MIDIHDR *)dwParam1; me = (MIDIEVENT *)(mh->lpData + mh->dwOffset); if (MIDIEVENT_TYPE(me->dwEvent) == MIDICMD_CONTROL) { if (MIDIEVENT_DATA1(me->dwEvent) != MIDICTL_MSB_MAIN_VOLUME) break; /* mask off the channel number and cache the volume data byte */ volume_cache[MIDIEVENT_CHANNEL(me->dwEvent)] = MIDIEVENT_VOLUME(me->dwEvent); if (hw_vol_capable) break; PostMessage(mainwindow, WM_MSTREAM_UPDATEVOLUME, MIDIEVENT_CHANNEL(me->dwEvent), (DWORD) (bgmvolume.value * 1000.0f)); } break; default: break; } } /* SetAllChannelVolumes * * Given a percent in tenths of a percent, sets volume * on all channels to reflect the new value. */ void MIDI_SetAllChannelVolumes(DWORD volume_percent) { DWORD i; DWORD event, status, vol; MMRESULT mmr; if (!midi_playing) return; for (i = 0, status = MIDICMD_CONTROL; i < MIDI_CHANNELS; i++, status++) { vol = (volume_cache[i] * volume_percent) / 1000; event = status | ((DWORD)MIDICTL_MSB_MAIN_VOLUME << 8) | ((DWORD)vol << 16); mmr = midiOutShortMsg((HMIDIOUT)hStream, event); if (mmr != MMSYSERR_NOERROR) { MidiErrorMessageBox(mmr); return; } } } /* SetChannelVolume * * Given a percent in tenths of a percent, sets volume * on a specified channel to reflect the new value. */ void MIDI_SetChannelVolume(DWORD channel_num, DWORD volume_percent) { DWORD event, vol; MMRESULT mmr; if (!midi_playing) return; vol = (volume_cache[channel_num] * volume_percent) / 1000; event = MIDICMD_CONTROL | channel_num | ((DWORD)MIDICTL_MSB_MAIN_VOLUME << 8) | ((DWORD)vol << 16); mmr = midiOutShortMsg((HMIDIOUT)hStream, event); if (mmr != MMSYSERR_NOERROR) MidiErrorMessageBox(mmr); } engine/h2shared/model.c000066400000000000000000001767521444734033100153140ustar00rootroot00000000000000/* * model.c -- model loading and caching * models are the only shared resource between a client and server * running on the same machine. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "hashindex.h" #include "hwal.h" #include "r_local.h" static qmodel_t* loadmodel; static char loadname[MAX_QPATH]; /* for hunk tags */ static qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash); static void Mod_LoadSpriteModel (qmodel_t *mod, void *buffer); static void Mod_LoadBrushModel (qmodel_t *mod, void *buffer); static void Mod_LoadAliasModel (qmodel_t *mod, void *buffer); static void Mod_LoadAliasModelNew (qmodel_t *mod, void *buffer); static void Mod_Print (void); static cvar_t external_ents = {"external_ents", "1", CVAR_ARCHIVE}; static byte mod_novis[MAX_MAP_LEAFS/8]; #define MAX_MOD_KNOWN 2048 static qmodel_t mod_known[MAX_MOD_KNOWN]; static int mod_numknown; static hashindex_t hash_mod; static vec3_t aliasmins, aliasmaxs; #ifndef H2W int entity_file_size; #endif /* =============== Mod_Init =============== */ void Mod_Init (void) { Cvar_RegisterVariable (&external_ents); Cmd_AddCommand ("mcache", Mod_Print); memset (mod_novis, 0xff, sizeof(mod_novis)); Hash_Allocate (&hash_mod, MAX_MOD_KNOWN); } /* =============== Mod_Extradata Caches the data if needed =============== */ void *Mod_Extradata (qmodel_t *mod) { void *r; r = Cache_Check (&mod->cache); if (r) return r; Mod_LoadModel (mod, true); if (!mod->cache.data) Sys_Error ("%s: caching failed", __thisfunc__); return mod->cache.data; } /* =============== Mod_PointInLeaf =============== */ mleaf_t *Mod_PointInLeaf (vec3_t p, qmodel_t *model) { mnode_t *node; float d; mplane_t *plane; if (!model) Sys_Error ("%s: NULL model", __thisfunc__); if (!model->nodes) Sys_Error ("%s: model w/o nodes : %s", __thisfunc__, model->name); node = model->nodes; while (1) { if (node->contents < 0) return (mleaf_t *)node; plane = node->plane; d = DotProduct (p,plane->normal) - plane->dist; if (d > 0) node = node->children[0]; else node = node->children[1]; } return NULL; // never reached } /* =================== Mod_DecompressVis =================== */ static byte *Mod_DecompressVis (byte *in, qmodel_t *model) { static byte decompressed[MAX_MAP_LEAFS/8]; int c; byte *out; int row; row = (model->numleafs+7)>>3; out = decompressed; if (!in) { // no vis info, so make all visible while (row) { *out++ = 0xff; row--; } return decompressed; } do { if (*in) { *out++ = *in++; continue; } c = in[1]; in += 2; while (c) { *out++ = 0; c--; } } while (out - decompressed < row); return decompressed; } byte *Mod_LeafPVS (mleaf_t *leaf, qmodel_t *model) { if (leaf == model->leafs) return mod_novis; return Mod_DecompressVis (leaf->compressed_vis, model); } /* =================== Mod_ClearAll =================== */ void Mod_ClearAll (void) { int i; qmodel_t *mod; for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { mod->needload = NL_UNREFERENCED; } } /* ================== Mod_FindName ================== */ qmodel_t *Mod_FindName (const char *name) { int i, key; qmodel_t *mod; if (!name[0]) Sys_Error ("%s: NULL name", __thisfunc__); // // search the currently loaded models // key = Hash_GenerateKeyString (&hash_mod, name, true); for (i = Hash_First(&hash_mod, key); i != -1; i = Hash_Next(&hash_mod, i)) { mod = &mod_known[i]; if (!strcmp (mod->name, name) ) break; } if (i == -1) { if (mod_numknown == MAX_MOD_KNOWN) { for (i = 0; i < mod_numknown; i++) { mod = &mod_known[i]; if (mod->needload == NL_UNREFERENCED && mod->type != mod_alias) break; } if (i < mod_numknown) { Hash_Add (&hash_mod, key, i); mod = &mod_known[i]; if (mod->type == mod_alias) { if (Cache_Check (&mod->cache)) Cache_Free (&mod->cache); } else if (mod->type == mod_sprite) mod->cache.data = NULL; } else Sys_Error ("mod_numknown == MAX_MOD_KNOWN"); } else { Hash_Add (&hash_mod, key, mod_numknown); mod = &mod_known[mod_numknown]; mod_numknown++; } q_strlcpy (mod->name, name, MAX_QPATH); mod->needload = NL_NEEDS_LOADED; } return mod; } /* ================== Mod_TouchModel ================== */ void Mod_TouchModel (const char *name) { qmodel_t *mod; mod = Mod_FindName (name); if (mod->needload == NL_PRESENT) { if (mod->type == mod_alias) Cache_Check (&mod->cache); } } /* ================== Mod_LoadModel Loads a model into the cache ================== */ static qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash) { byte *buf; byte stackbuf[1024]; // avoid dirtying the cache heap int mod_type; if (mod->type == mod_alias) { if (Cache_Check(&mod->cache)) { mod->needload = NL_PRESENT; return mod; } } else if (mod->needload == NL_PRESENT) { return mod; } // // load the file // buf = FS_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf), & mod->path_id); if (!buf) { if (crash) Sys_Error ("%s: %s not found", __thisfunc__, mod->name); return NULL; } // // allocate a new model // COM_FileBase (mod->name, loadname, sizeof(loadname)); loadmodel = mod; // // fill it in // // call the apropriate loader mod->needload = NL_PRESENT; mod_type = (buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24)); switch (mod_type) { case RAPOLYHEADER: Mod_LoadAliasModelNew (mod, buf); break; case IDPOLYHEADER: Mod_LoadAliasModel (mod, buf); break; case IDSPRITEHEADER: Mod_LoadSpriteModel (mod, buf); break; default: Mod_LoadBrushModel (mod, buf); break; } return mod; } /* ================== Mod_ForName Loads in a model for the given name ================== */ qmodel_t *Mod_ForName (const char *name, qboolean crash) { qmodel_t *mod; mod = Mod_FindName (name); return Mod_LoadModel (mod, crash); } /* =============================================================================== BRUSHMODEL LOADING =============================================================================== */ static byte *mod_base; /* ================= Mod_LoadTextures ================= */ static void Mod_LoadTextures (lump_t *l) { int i, j, pixels, num, maxanim, altmax; miptex_t *mt; texture_t *tx, *tx2; texture_t *anims[10]; texture_t *altanims[10]; dmiptexlump_t *m; #ifdef WAL_TEXTURES // external WAL texture loading char texname[MAX_QPATH]; int mark; miptex_wal_t *mt_wal; #endif if (!l->filelen) { loadmodel->textures = NULL; return; } m = (dmiptexlump_t *)(mod_base + l->fileofs); m->nummiptex = LittleLong (m->nummiptex); loadmodel->numtextures = m->nummiptex; loadmodel->textures = (texture_t **) Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures), "texture"); for (i = 0; i < m->nummiptex; i++) { m->dataofs[i] = LittleLong(m->dataofs[i]); if (m->dataofs[i] == -1) continue; mt = (miptex_t *)((byte *)m + m->dataofs[i]); mt->width = LittleLong (mt->width); mt->height = LittleLong (mt->height); for (j = 0; j < MIPLEVELS; j++) mt->offsets[j] = LittleLong (mt->offsets[j]); #ifdef WAL_TEXTURES if (!r_texture_external.integer) goto bsp_tex_internal; // try an external wal texture file first q_snprintf (texname, sizeof(texname), "textures/%s.wal", mt->name); if (texname[sizeof(WAL_EXT_DIRNAME)] == '*') texname[sizeof(WAL_EXT_DIRNAME)] = WAL_REPLACE_ASTERIX; mark = Hunk_LowMark (); mt_wal = (miptex_wal_t *)FS_LoadHunkFile(texname, NULL); if (mt_wal != NULL) { mt_wal->ident = LittleLong (mt_wal->ident); mt_wal->version = LittleLong (mt_wal->version); if (mt_wal->ident == IDWALHEADER && mt_wal->version == WALVERSION) { mt_wal->width = LittleLong (mt_wal->width); mt_wal->height = LittleLong (mt_wal->height); for (j = 0; j < MIPLEVELS; j++) mt_wal->offsets[j] = LittleLong (mt_wal->offsets[j]); if ( (mt_wal->width & 15) || (mt_wal->height & 15) ) { Hunk_FreeToLowMark (mark); Sys_Printf ("Texture %s is not 16 aligned", texname); goto bsp_tex_internal; } pixels = mt_wal->width*mt_wal->height/64*85; tx = (texture_t *) Hunk_AllocName (sizeof(texture_t) +pixels, "texture"); loadmodel->textures[i] = tx; memcpy (tx->name, mt_wal->name, sizeof(tx->name)); tx->width = mt_wal->width; tx->height = mt_wal->height; for (j = 0; j < MIPLEVELS; j++) tx->offsets[j] = mt_wal->offsets[j] + sizeof(texture_t) - sizeof(miptex_wal_t); // the pixels immediately follow the structures memcpy (tx+1, mt_wal+1, pixels); } else { if (mt_wal->ident != IDWALHEADER) Sys_Printf ("%s: %s is not a valid WAL file\n", __thisfunc__, texname); if (mt_wal->version != WALVERSION) Sys_Printf ("%s: WAL file %s has unsupported version (%d)\n", __thisfunc__, texname, mt_wal->version); Hunk_FreeToLowMark (mark); goto bsp_tex_internal; } } else { // load internal bsp pixel data bsp_tex_internal: #endif /* WAL_TEXTURES */ if ( (mt->width & 15) || (mt->height & 15) ) Sys_Error ("Texture %s is not 16 aligned", mt->name); pixels = mt->width*mt->height/64*85; tx = (texture_t *) Hunk_AllocName (sizeof(texture_t) +pixels, "texture" ); loadmodel->textures[i] = tx; memcpy (tx->name, mt->name, sizeof(tx->name)); tx->width = mt->width; tx->height = mt->height; for (j = 0; j < MIPLEVELS; j++) tx->offsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); // the pixels immediately follow the structures memcpy ( tx+1, mt+1, pixels); #ifdef WAL_TEXTURES } #endif if (!strncmp(mt->name,"sky",3)) R_InitSky (tx); } // // sequence the animations // for (i = 0; i < m->nummiptex; i++) { tx = loadmodel->textures[i]; if (!tx || tx->name[0] != '+') continue; if (tx->anim_next) continue; // already sequenced // find the number of frames in the animation memset (anims, 0, sizeof(anims)); memset (altanims, 0, sizeof(altanims)); maxanim = tx->name[1]; altmax = 0; if (maxanim >= 'a' && maxanim <= 'z') maxanim -= 'a' - 'A'; if (maxanim >= '0' && maxanim <= '9') { maxanim -= '0'; altmax = 0; anims[maxanim] = tx; maxanim++; } else if (maxanim >= 'A' && maxanim <= 'J') { altmax = maxanim - 'A'; maxanim = 0; altanims[altmax] = tx; altmax++; } else Sys_Error ("Bad animating texture %s", tx->name); for (j = i+1; j < m->nummiptex; j++) { tx2 = loadmodel->textures[j]; if (!tx2 || tx2->name[0] != '+') continue; if (strcmp (tx2->name+2, tx->name+2)) continue; num = tx2->name[1]; if (num >= 'a' && num <= 'z') num -= 'a' - 'A'; if (num >= '0' && num <= '9') { num -= '0'; anims[num] = tx2; if (num+1 > maxanim) maxanim = num + 1; } else if (num >= 'A' && num <= 'J') { num = num - 'A'; altanims[num] = tx2; if (num+1 > altmax) altmax = num+1; } else Sys_Error ("Bad animating texture %s", tx->name); } #define ANIM_CYCLE 2 // link them all together for (j = 0; j < maxanim; j++) { tx2 = anims[j]; if (!tx2) Sys_Error ("Missing frame %i of %s",j, tx->name); tx2->anim_total = maxanim * ANIM_CYCLE; tx2->anim_min = j * ANIM_CYCLE; tx2->anim_max = (j+1) * ANIM_CYCLE; tx2->anim_next = anims[ (j+1)%maxanim ]; if (altmax) tx2->alternate_anims = altanims[0]; } for (j = 0; j < altmax; j++) { tx2 = altanims[j]; if (!tx2) Sys_Error ("Missing frame %i of %s",j, tx->name); tx2->anim_total = altmax * ANIM_CYCLE; tx2->anim_min = j * ANIM_CYCLE; tx2->anim_max = (j+1) * ANIM_CYCLE; tx2->anim_next = altanims[ (j+1)%altmax ]; if (maxanim) tx2->alternate_anims = anims[0]; } } } /* ================= Mod_LoadLighting ================= */ static void Mod_LoadLighting (lump_t *l) { if (!l->filelen) { loadmodel->lightdata = NULL; return; } loadmodel->lightdata = (byte *) Hunk_AllocName ( l->filelen, "light"); memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); } /* ================= Mod_LoadVisibility ================= */ static void Mod_LoadVisibility (lump_t *l) { if (!l->filelen) { loadmodel->visdata = NULL; return; } loadmodel->visdata = (byte *) Hunk_AllocName ( l->filelen, "vis"); memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); } /* ================= Mod_LoadEntities ================= */ static void Mod_LoadEntities (lump_t *l) { char entfilename[MAX_QPATH]; char *ents; int mark; unsigned int path_id; if (! external_ents.integer) goto _load_embedded; q_strlcpy(entfilename, loadmodel->name, sizeof(entfilename)); COM_StripExtension(entfilename, entfilename, sizeof(entfilename)); q_strlcat(entfilename, ".ent", sizeof(entfilename)); Con_DPrintf("trying to load %s\n", entfilename); mark = Hunk_LowMark(); ents = (char *) FS_LoadHunkFile (entfilename, &path_id); if (ents) { // use ent file only from the same gamedir as the map // itself or from a searchpath with higher priority. if (path_id < loadmodel->path_id) { Hunk_FreeToLowMark(mark); Con_DPrintf("ignored %s from a gamedir with lower priority\n", entfilename); } else { #ifndef H2W entity_file_size = fs_filesize; #endif loadmodel->entities = ents; Con_DPrintf("Loaded external entity file %s\n", entfilename); return; } } _load_embedded: if (!l->filelen) { loadmodel->entities = NULL; return; } loadmodel->entities = (char *) Hunk_AllocName ( l->filelen, "entities"); memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); #ifndef H2W entity_file_size = l->filelen; #endif } /* ================= Mod_LoadVertexes ================= */ static void Mod_LoadVertexes (lump_t *l) { dvertex_t *in; mvertex_t *out; int i, count; in = (dvertex_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mvertex_t *) Hunk_AllocName (count * sizeof(*out), "vertexes"); loadmodel->vertexes = out; loadmodel->numvertexes = count; for (i = 0; i < count; i++, in++, out++) { out->position[0] = LittleFloat (in->point[0]); out->position[1] = LittleFloat (in->point[1]); out->position[2] = LittleFloat (in->point[2]); } } /* ================= Mod_LoadSubmodels ================= */ static void Mod_LoadSubmodels (lump_t *l) { dmodel_t *in; dmodel_t *out; int i, j, count; in = (dmodel_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (dmodel_t *) Hunk_AllocName (count * sizeof(*out), "submodels"); loadmodel->submodels = out; loadmodel->numsubmodels = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { // spread the mins / maxs by a pixel out->mins[j] = LittleFloat (in->mins[j]) - 1; out->maxs[j] = LittleFloat (in->maxs[j]) + 1; out->origin[j] = LittleFloat (in->origin[j]); } for (j = 0; j < MAX_MAP_HULLS; j++) out->headnode[j] = LittleLong (in->headnode[j]); out->visleafs = LittleLong (in->visleafs); out->firstface = LittleLong (in->firstface); out->numfaces = LittleLong (in->numfaces); } } /* ================= Mod_LoadEdges ================= */ #ifndef ENABLE_BSP2 #define Mod_LoadEdges(_l, _v) Mod_LoadEdges_V29((_l)) #else static void Mod_LoadEdges_V29 (lump_t *l); static void Mod_LoadEdges_BSP2(lump_t *l); static void Mod_LoadEdges (lump_t *l, qboolean bsp2) { if (bsp2) Mod_LoadEdges_BSP2(l); else Mod_LoadEdges_V29 (l); } static void Mod_LoadEdges_BSP2(lump_t *l) { dedge2_t *in; medge_t *out; int i, count; in = (dedge2_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (medge_t *) Hunk_AllocName ((count + 1) * sizeof(*out), "edges"); loadmodel->edges = out; loadmodel->numedges = count; for (i = 0; i < count; i++, in++, out++) { out->v[0] = (unsigned int)LittleLong(in->v[0]); out->v[1] = (unsigned int)LittleLong(in->v[1]); } } #endif static void Mod_LoadEdges_V29 (lump_t *l) { dedge_t *in; medge_t *out; int i, count; in = (dedge_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (medge_t *) Hunk_AllocName ((count + 1) * sizeof(*out), "edges"); loadmodel->edges = out; loadmodel->numedges = count; for (i = 0; i < count; i++, in++, out++) { out->v[0] = (unsigned short)LittleShort(in->v[0]); out->v[1] = (unsigned short)LittleShort(in->v[1]); } } /* ================= Mod_LoadTexinfo ================= */ static void Mod_LoadTexinfo (lump_t *l) { texinfo_t *in; mtexinfo_t *out; int i, j, count; int miptex; float len1, len2; in = (texinfo_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mtexinfo_t *) Hunk_AllocName (count * sizeof(*out), "texture"); loadmodel->texinfo = out; loadmodel->numtexinfo = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 4; j++) { out->vecs[0][j] = LittleFloat (in->vecs[0][j]); out->vecs[1][j] = LittleFloat (in->vecs[1][j]); } len1 = VectorLength (out->vecs[0]); len2 = VectorLength (out->vecs[1]); len1 = (len1 + len2)/2; if (len1 < 0.32) out->mipadjust = 4; else if (len1 < 0.49) out->mipadjust = 3; else if (len1 < 0.99) out->mipadjust = 2; else out->mipadjust = 1; #if 0 if (len1 + len2 < 0.001) out->mipadjust = 1; // don't crash else out->mipadjust = 1 / floor( (len1+len2)/2 + 0.1 ); #endif miptex = LittleLong (in->miptex); out->flags = LittleLong (in->flags); if (!loadmodel->textures) { out->texture = r_notexture_mip; // checkerboard texture out->flags = 0; } else { if (miptex >= loadmodel->numtextures) Sys_Error ("miptex >= loadmodel->numtextures"); out->texture = loadmodel->textures[miptex]; if (!out->texture) { out->texture = r_notexture_mip; // texture not found out->flags = 0; } } } } /* ================ CalcSurfaceExtents Fills in s->texturemins[] and s->extents[] ================ */ static void CalcSurfaceExtents (msurface_t *s) { float mins[2], maxs[2], val; int i, j, e; mvertex_t *v; mtexinfo_t *tex; int bmins[2], bmaxs[2]; mins[0] = mins[1] = 9999999; /* FIXME: change these two to FLT_MAX/-FLT_MAX */ maxs[0] = maxs[1] = -9999999; tex = s->texinfo; for (i = 0; i < s->numedges; i++) { e = loadmodel->surfedges[s->firstedge+i]; if (e >= 0) v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; else v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; for (j = 0; j < 2; j++) { /* added double casts so that 64 bit/sse2 builds' precision * matches that of x87 floating point. took from QuakeSpasm, * patch by Eric Wasylishen. */ val = ((double)v->position[0] * (double)tex->vecs[j][0]) + ((double)v->position[1] * (double)tex->vecs[j][1]) + ((double)v->position[2] * (double)tex->vecs[j][2]) + (double)tex->vecs[j][3]; if (val < mins[j]) mins[j] = val; if (val > maxs[j]) maxs[j] = val; } } for (i = 0; i < 2; i++) { bmins[i] = (int) floor(mins[i]/16); bmaxs[i] = (int) ceil(maxs[i]/16); s->texturemins[i] = bmins[i] * 16; s->extents[i] = (bmaxs[i] - bmins[i]) * 16; if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 256) Sys_Error ("Bad surface extents"); } } /* ================= Mod_LoadFaces ================= */ static void Mod_SetDrawingFlags(msurface_t *out); #ifndef ENABLE_BSP2 #define Mod_LoadFaces(_l, _v) Mod_LoadFaces_V29((_l)) #else static void Mod_LoadFaces_BSP2(lump_t *l); static void Mod_LoadFaces_V29 (lump_t *l); static void Mod_LoadFaces (lump_t *l, qboolean bsp2) { if (bsp2) Mod_LoadFaces_BSP2(l); else Mod_LoadFaces_V29 (l); } static void Mod_LoadFaces_BSP2(lump_t *l) { dface2_t *in; msurface_t *out; int i, count, surfnum; int planenum, side; in = (dface2_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (msurface_t *) Hunk_AllocName (count * sizeof(*out), "faces"); loadmodel->surfaces = out; loadmodel->numsurfaces = count; for (surfnum = 0; surfnum < count; surfnum++, in++, out++) { out->firstedge = LittleLong(in->firstedge); out->numedges = LittleLong(in->numedges); out->flags = 0; planenum = LittleLong(in->planenum); side = LittleLong(in->side); if (side) out->flags |= SURF_PLANEBACK; out->plane = loadmodel->planes + planenum; out->texinfo = loadmodel->texinfo + LittleLong (in->texinfo); CalcSurfaceExtents (out); // lighting info for (i = 0; i < MAXLIGHTMAPS; i++) out->styles[i] = in->styles[i]; i = LittleLong(in->lightofs); if (i == -1) { out->samples = NULL; } else { out->samples = loadmodel->lightdata + i; } // set the drawing flags flag Mod_SetDrawingFlags(out); } } #endif static void Mod_LoadFaces_V29 (lump_t *l) { dface_t *in; msurface_t *out; int i, count, surfnum; int planenum, side; in = (dface_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (msurface_t *) Hunk_AllocName (count * sizeof(*out), "faces"); loadmodel->surfaces = out; loadmodel->numsurfaces = count; for (surfnum = 0; surfnum < count; surfnum++, in++, out++) { out->firstedge = LittleLong(in->firstedge); out->numedges = LittleShort(in->numedges); out->flags = 0; planenum = LittleShort(in->planenum); side = LittleShort(in->side); if (side) out->flags |= SURF_PLANEBACK; out->plane = loadmodel->planes + planenum; out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo); CalcSurfaceExtents (out); // lighting info for (i = 0; i < MAXLIGHTMAPS; i++) out->styles[i] = in->styles[i]; i = LittleLong(in->lightofs); if (i == -1) { out->samples = NULL; } else { out->samples = loadmodel->lightdata + i; } // set the drawing flags flag Mod_SetDrawingFlags(out); } } static void Mod_SetDrawingFlags(msurface_t *out) { int i; if (!strncmp(out->texinfo->texture->name,"sky",3)) // sky { out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); return; } if (out->texinfo->texture->name[0] == '*') // turbulent { if (!strncmp(out->texinfo->texture->name, "*BLACK", 6)) { out->flags |= (SURF_DRAWTILED | SURF_DRAWBLACK); return; } out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); for (i = 0; i < 2; i++) { out->extents[i] = 16384; out->texturemins[i] = -8192; } if (!q_strncasecmp(out->texinfo->texture->name,"*rtex078",8) || !q_strncasecmp(out->texinfo->texture->name,"*lowlight",9) ) out->flags |= SURF_TRANSLUCENT; return; } if (out->texinfo->texture->anim_total) return; // BSzili: for D_DrawSolidSurface optimization: if (!strncmp(out->texinfo->texture->name, "rtex", 4)) // solid color { const texture_t *texture = out->texinfo->texture; const byte *pixels = (byte *)texture + texture->offsets[0]; const int size = texture->width * texture->height; for (i = 1; i < size; i++) { if (pixels[i] != pixels[0]) return; } out->flags |= SURF_DRAWSOLID; } } /* ================= Mod_SetParent ================= */ static void Mod_SetParent (mnode_t *node, mnode_t *parent) { node->parent = parent; if (node->contents < 0) return; Mod_SetParent (node->children[0], node); Mod_SetParent (node->children[1], node); } /* ================= Mod_LoadNodes ================= */ #ifndef ENABLE_BSP2 #define Mod_LoadNodes(_l, _v) Mod_LoadNodes_V29((_l)) #else static void Mod_LoadNodes_BSP2(lump_t *l); static void Mod_LoadNodes_V29 (lump_t *l); static void Mod_LoadNodes (lump_t *l, qboolean bsp2) { if (bsp2) Mod_LoadNodes_BSP2(l); else Mod_LoadNodes_V29 (l); } static void Mod_LoadNodes_BSP2(lump_t *l) { int i, j, count, p; dnode2_t *in; mnode_t *out; in = (dnode2_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mnode_t *) Hunk_AllocName (count * sizeof(*out), "nodes"); loadmodel->nodes = out; loadmodel->numnodes = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { out->minmaxs[j] = LittleFloat (in->mins[j]); out->minmaxs[3+j] = LittleFloat (in->maxs[j]); } p = LittleLong(in->planenum); out->plane = loadmodel->planes + p; out->firstsurface = LittleLong (in->firstface); out->numsurfaces = LittleLong (in->numfaces); for (j = 0; j < 2; j++) { p = LittleLong (in->children[j]); if (p >= 0) out->children[j] = loadmodel->nodes + p; else out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); } } Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs } #endif static void Mod_LoadNodes_V29 (lump_t *l) { int i, j, count, p; dnode_t *in; mnode_t *out; in = (dnode_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mnode_t *) Hunk_AllocName (count * sizeof(*out), "nodes"); loadmodel->nodes = out; loadmodel->numnodes = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { out->minmaxs[j] = LittleShort (in->mins[j]); out->minmaxs[3+j] = LittleShort (in->maxs[j]); } p = LittleLong(in->planenum); out->plane = loadmodel->planes + p; out->firstsurface = LittleShort (in->firstface); out->numsurfaces = LittleShort (in->numfaces); for (j = 0; j < 2; j++) { p = LittleShort (in->children[j]); if (p >= 0) out->children[j] = loadmodel->nodes + p; else out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); } } Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs } /* ================= Mod_LoadLeafs ================= */ #ifndef ENABLE_BSP2 #define Mod_LoadLeafs(_l, _v) Mod_LoadLeafs_V29((_l)) #else static void Mod_LoadLeafs_BSP2(lump_t *l); static void Mod_LoadLeafs_V29 (lump_t *l); static void Mod_LoadLeafs (lump_t *l, qboolean bsp2) { if (bsp2) Mod_LoadLeafs_BSP2(l); else Mod_LoadLeafs_V29 (l); } static void Mod_LoadLeafs_BSP2(lump_t *l) { dleaf2_t *in; mleaf_t *out; int i, j, count, p; in = (dleaf2_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mleaf_t *) Hunk_AllocName (count * sizeof(*out), "leafs"); loadmodel->leafs = out; loadmodel->numleafs = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { out->minmaxs[j] = LittleFloat (in->mins[j]); out->minmaxs[3+j] = LittleFloat (in->maxs[j]); } p = LittleLong(in->contents); out->contents = p; out->firstmarksurface = loadmodel->marksurfaces + LittleLong(in->firstmarksurface); out->nummarksurfaces = LittleLong(in->nummarksurfaces); p = LittleLong(in->visofs); if (p == -1) out->compressed_vis = NULL; else out->compressed_vis = loadmodel->visdata + p; out->efrags = NULL; for (j = 0; j < 4; j++) out->ambient_sound_level[j] = in->ambient_level[j]; } } #endif static void Mod_LoadLeafs_V29 (lump_t *l) { dleaf_t *in; mleaf_t *out; int i, j, count, p; in = (dleaf_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mleaf_t *) Hunk_AllocName (count * sizeof(*out), "leafs"); loadmodel->leafs = out; loadmodel->numleafs = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { out->minmaxs[j] = LittleShort (in->mins[j]); out->minmaxs[3+j] = LittleShort (in->maxs[j]); } p = LittleLong(in->contents); out->contents = p; out->firstmarksurface = loadmodel->marksurfaces + LittleShort(in->firstmarksurface); out->nummarksurfaces = LittleShort(in->nummarksurfaces); p = LittleLong(in->visofs); if (p == -1) out->compressed_vis = NULL; else out->compressed_vis = loadmodel->visdata + p; out->efrags = NULL; for (j = 0; j < 4; j++) out->ambient_sound_level[j] = in->ambient_level[j]; } } /* ================= Mod_LoadClipnodes ================= */ static void Mod_MakeHulls (int count); #ifndef ENABLE_BSP2 #define Mod_LoadClipnodes(_l, _v) Mod_LoadClipnodes_V29((_l)) #else static void Mod_LoadClipnodes_BSP2(lump_t *l); static void Mod_LoadClipnodes_V29 (lump_t *l); static void Mod_LoadClipnodes (lump_t *l, qboolean bsp2) { if (bsp2) Mod_LoadClipnodes_BSP2(l); else Mod_LoadClipnodes_V29 (l); } static void Mod_LoadClipnodes_BSP2(lump_t *l) { dclipnode2_t *in; mclipnode_t *out; int i, count; in = (dclipnode2_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mclipnode_t *) Hunk_AllocName (count * sizeof(*out), "clipnodes"); loadmodel->clipnodes = out; loadmodel->numclipnodes = count; for (i = 0; i < count; i++, out++, in++) { out->planenum = LittleLong(in->planenum); out->children[0] = LittleLong(in->children[0]); out->children[1] = LittleLong(in->children[1]); } Mod_MakeHulls(count); } #endif static void Mod_LoadClipnodes_V29 (lump_t *l) { dclipnode_t *in; mclipnode_t *out; int i, count; in = (dclipnode_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mclipnode_t *) Hunk_AllocName (count * sizeof(*out), "clipnodes"); loadmodel->clipnodes = out; loadmodel->numclipnodes = count; for (i = 0; i < count; i++, out++, in++) { out->planenum = LittleLong(in->planenum); out->children[0] = LittleShort(in->children[0]); out->children[1] = LittleShort(in->children[1]); } Mod_MakeHulls(count); } static void Mod_MakeHulls (int count) { hull_t *hull; //player hull = &loadmodel->hulls[1]; hull->clipnodes = loadmodel->clipnodes; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; hull->clip_mins[0] = -16; hull->clip_mins[1] = -16; hull->clip_mins[2] = -24; hull->clip_maxs[0] = 16; hull->clip_maxs[1] = 16; hull->clip_maxs[2] = 32; //scorpion hull = &loadmodel->hulls[2]; hull->clipnodes = loadmodel->clipnodes; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; hull->clip_mins[0] = -24; hull->clip_mins[1] = -24; hull->clip_mins[2] = -20; hull->clip_maxs[0] = 24; hull->clip_maxs[1] = 24; hull->clip_maxs[2] = 20; //crouch hull = &loadmodel->hulls[3]; hull->clipnodes = loadmodel->clipnodes; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; hull->clip_mins[0] = -16; hull->clip_mins[1] = -16; hull->clip_mins[2] = -12; hull->clip_maxs[0] = 16; hull->clip_maxs[1] = 16; hull->clip_maxs[2] = 16; //hydra -changing in MP to '-8 -8 -8', '8 8 8' for pentacles hull = &loadmodel->hulls[4]; hull->clipnodes = loadmodel->clipnodes; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; #ifdef H2W hull->clip_mins[0] = -40; hull->clip_mins[1] = -40; hull->clip_mins[2] = -42; hull->clip_maxs[0] = 40; hull->clip_maxs[1] = 40; hull->clip_maxs[2] = 42; #else hull->clip_mins[0] = -8; hull->clip_mins[1] = -8; hull->clip_mins[2] = -8; hull->clip_maxs[0] = 8; hull->clip_maxs[1] = 8; hull->clip_maxs[2] = 8; #endif //golem - maybe change to '-28 -28 -40', '28 28 40' for Yakman hull = &loadmodel->hulls[5]; hull->clipnodes = loadmodel->clipnodes; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; #if 0 //use yak sizes hull->clip_mins[0] = -28; hull->clip_mins[1] = -28; hull->clip_mins[2] = -40; hull->clip_maxs[0] = 28; hull->clip_maxs[1] = 28; hull->clip_maxs[2] = 40; #else hull->clip_mins[0] = -48; hull->clip_mins[1] = -48; hull->clip_mins[2] = -50; hull->clip_maxs[0] = 48; hull->clip_maxs[1] = 48; hull->clip_maxs[2] = 50; #endif } /* ================= Mod_MakeHull0 Duplicate the drawing hull structure as a clipping hull ================= */ static void Mod_MakeHull0 (void) { mnode_t *in, *child; mclipnode_t *out; int i, j, count; hull_t *hull; hull = &loadmodel->hulls[0]; in = loadmodel->nodes; count = loadmodel->numnodes; out = (mclipnode_t *) Hunk_AllocName (count * sizeof(*out), "hull0"); hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; for (i = 0; i < count; i++, out++, in++) { out->planenum = in->plane - loadmodel->planes; for (j = 0; j < 2; j++) { child = in->children[j]; if (child->contents < 0) out->children[j] = child->contents; else out->children[j] = child - loadmodel->nodes; } } } /* ================= Mod_LoadMarksurfaces ================= */ #ifndef ENABLE_BSP2 #define Mod_LoadMarksurfaces(_l, _v) Mod_LoadMarksurfaces_V29((_l)) #else static void Mod_LoadMarksurfaces_BSP2(lump_t *l); static void Mod_LoadMarksurfaces_V29 (lump_t *l); static void Mod_LoadMarksurfaces (lump_t *l, qboolean bsp2) { if (bsp2) Mod_LoadMarksurfaces_BSP2(l); else Mod_LoadMarksurfaces_V29 (l); } static void Mod_LoadMarksurfaces_BSP2(lump_t *l) { int i, j, count; int *in; msurface_t **out; in = (int *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (msurface_t **) Hunk_AllocName (count * sizeof(*out), "marksurfaces"); loadmodel->marksurfaces = out; loadmodel->nummarksurfaces = count; for (i = 0; i < count; i++) { j = LittleLong(in[i]); if (j >= loadmodel->numsurfaces) Sys_Error ("%s: bad surface number", __thisfunc__); out[i] = loadmodel->surfaces + j; } } #endif static void Mod_LoadMarksurfaces_V29 (lump_t *l) { int i, j, count; unsigned short *in; msurface_t **out; in = (unsigned short *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (msurface_t **) Hunk_AllocName (count * sizeof(*out), "marksurfaces"); loadmodel->marksurfaces = out; loadmodel->nummarksurfaces = count; for (i = 0; i < count; i++) { j = (unsigned short)LittleShort(in[i]); if (j >= loadmodel->numsurfaces) Sys_Error ("%s: bad surface number", __thisfunc__); out[i] = loadmodel->surfaces + j; } } /* ================= Mod_LoadSurfedges ================= */ static void Mod_LoadSurfedges (lump_t *l) { int i, count; int *in, *out; in = (int *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (int *) Hunk_AllocName (count * sizeof(*out), "surfedges"); loadmodel->surfedges = out; loadmodel->numsurfedges = count; for (i = 0; i < count; i++) out[i] = LittleLong (in[i]); } /* ================= Mod_LoadPlanes ================= */ static void Mod_LoadPlanes (lump_t *l) { int i, j; mplane_t *out; dplane_t *in; int count; int bits; in = (dplane_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mplane_t *) Hunk_AllocName (count * 2 * sizeof(*out), "planes"); loadmodel->planes = out; loadmodel->numplanes = count; for (i = 0; i < count; i++, in++, out++) { bits = 0; for (j = 0; j < 3; j++) { out->normal[j] = LittleFloat (in->normal[j]); if (out->normal[j] < 0) bits |= 1<dist = LittleFloat (in->dist); out->type = LittleLong (in->type); out->signbits = bits; } } /* ================= RadiusFromBounds ================= */ static float RadiusFromBounds (const vec3_t mins, const vec3_t maxs) { int i; vec3_t corner; vec_t a, b; for (i = 0; i < 3; i++) { b = fabs(maxs[i]); a = fabs(mins[i]); corner[i] = (a > b)? a : b; } return VectorLength (corner); } /* ================= Mod_LoadBrushModel ================= */ static void Mod_LoadBrushModel (qmodel_t *mod, void *buffer) { int i, j; dheader_t *header; dmodel_t *bm; qboolean bsp2 = false; loadmodel->type = mod_brush; header = (dheader_t *)buffer; i = LittleLong (header->version); #ifndef ENABLE_BSP2 (void) bsp2; #else if (i == BSP2VERSION) bsp2 = true; else #endif if (i != BSPVERSION) Sys_Error ("%s: %s has unsupported version %i", __thisfunc__, mod->name, i); // swap all the lumps mod_base = (byte *)header; for (i = 0; i < (int) sizeof(dheader_t) / 4; i++) ((int *)header)[i] = LittleLong ( ((int *)header)[i]); // load into heap Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); Mod_LoadEdges (&header->lumps[LUMP_EDGES], bsp2); Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]); Mod_LoadPlanes (&header->lumps[LUMP_PLANES]); Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]); Mod_LoadFaces (&header->lumps[LUMP_FACES], bsp2); Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES], bsp2); Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); Mod_LoadLeafs (&header->lumps[LUMP_LEAFS], bsp2); Mod_LoadNodes (&header->lumps[LUMP_NODES], bsp2); Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES], bsp2); Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); Mod_MakeHull0 (); mod->numframes = 2; // regular and alternate animation mod->flags = 0; // // set up the submodels (FIXME: this is confusing) // for (i = 0; i < mod->numsubmodels; i++) { bm = &mod->submodels[i]; mod->hulls[0].firstclipnode = bm->headnode[0]; for (j = 1; j < MAX_MAP_HULLS; j++) { mod->hulls[j].firstclipnode = bm->headnode[j]; mod->hulls[j].lastclipnode = mod->numclipnodes-1; } mod->firstmodelsurface = bm->firstface; mod->nummodelsurfaces = bm->numfaces; VectorCopy (bm->maxs, mod->maxs); VectorCopy (bm->mins, mod->mins); mod->radius = RadiusFromBounds (mod->mins, mod->maxs); mod->numleafs = bm->visleafs; if (i < mod->numsubmodels-1) { // duplicate the basic information char name[10]; q_snprintf (name, sizeof(name), "*%i", i+1); loadmodel = Mod_FindName (name); *loadmodel = *mod; strcpy (loadmodel->name, name); mod = loadmodel; } } } /* ============================================================================== ALIAS MODELS ============================================================================== */ static float aliastransform2[3][4]; /* ================ R_AliasTransformVector ================ */ static void R_AliasTransformVector2 (vec3_t in, vec3_t out) { out[0] = DotProduct(in, aliastransform2[0]) + aliastransform2[0][3]; out[1] = DotProduct(in, aliastransform2[1]) + aliastransform2[1][3]; out[2] = DotProduct(in, aliastransform2[2]) + aliastransform2[2][3]; } /* ================= Mod_LoadAliasFrame ================= */ static void *Mod_LoadAliasFrame (void *pin, int *pframeindex, int numv, trivertx_t *pbboxmin, trivertx_t *pbboxmax, aliashdr_t *pheader, char *name, newmdl_t *pmodel) { trivertx_t *pframe, *pinframe; int i, j; daliasframe_t *pdaliasframe; vec3_t in, out; pdaliasframe = (daliasframe_t *)pin; strcpy (name, pdaliasframe->name); for (i = 0; i < 3; i++) { // these are byte values, so we don't have to worry about // endianness pbboxmin->v[i] = pdaliasframe->bboxmin.v[i]; pbboxmax->v[i] = pdaliasframe->bboxmax.v[i]; } pinframe = (trivertx_t *)(pdaliasframe + 1); pframe = (trivertx_t *) Hunk_AllocName (numv * sizeof(*pframe), loadname); *pframeindex = (byte *)pframe - (byte *)pheader; aliastransform2[0][0] = pmodel->scale[0]; aliastransform2[1][1] = pmodel->scale[1]; aliastransform2[2][2] = pmodel->scale[2]; aliastransform2[0][3] = pmodel->scale_origin[0]; aliastransform2[1][3] = pmodel->scale_origin[1]; aliastransform2[2][3] = pmodel->scale_origin[2]; for (j = 0; j < numv; j++) { int k; in[0] = pinframe[j].v[0]; in[1] = pinframe[j].v[1]; in[2] = pinframe[j].v[2]; R_AliasTransformVector2(in, out); // these are all byte values, so no need to deal with endianness pframe[j].lightnormalindex = pinframe[j].lightnormalindex; for (k = 0; k < 3; k++) { pframe[j].v[k] = pinframe[j].v[k]; if (aliasmins[k] > out[k]) aliasmins[k] = out[k]; if (aliasmaxs[k] < out[k]) aliasmaxs[k] = out[k]; } } pinframe += numv; return (void *)pinframe; } /* ================= Mod_LoadAliasGroup ================= */ static void *Mod_LoadAliasGroup (void *pin, int *pframeindex, int numv, trivertx_t *pbboxmin, trivertx_t *pbboxmax, aliashdr_t *pheader, char *name, newmdl_t *pmodel) { daliasgroup_t *pingroup; maliasgroup_t *paliasgroup; int i, numframes; daliasinterval_t *pin_intervals; float *poutintervals; void *ptemp; pingroup = (daliasgroup_t *)pin; numframes = LittleLong (pingroup->numframes); paliasgroup = (maliasgroup_t *) Hunk_AllocName (sizeof(maliasgroup_t) + (numframes - 1) * sizeof(paliasgroup->frames[0]), loadname); paliasgroup->numframes = numframes; for (i = 0; i < 3; i++) { // these are byte values, so we don't have to worry about endianness pbboxmin->v[i] = pingroup->bboxmin.v[i]; pbboxmax->v[i] = pingroup->bboxmax.v[i]; } *pframeindex = (byte *)paliasgroup - (byte *)pheader; pin_intervals = (daliasinterval_t *)(pingroup + 1); poutintervals = (float *) Hunk_AllocName (numframes * sizeof(float), loadname); paliasgroup->intervals = (byte *)poutintervals - (byte *)pheader; for (i = 0; i < numframes; i++) { *poutintervals = LittleFloat (pin_intervals->interval); if (*poutintervals <= 0.0) Sys_Error ("%s: interval <= 0", __thisfunc__); poutintervals++; pin_intervals++; } ptemp = (void *)pin_intervals; for (i = 0; i < numframes; i++) { ptemp = Mod_LoadAliasFrame (ptemp, &paliasgroup->frames[i].frame, numv, &paliasgroup->frames[i].bboxmin, &paliasgroup->frames[i].bboxmax, pheader, name, pmodel); } return ptemp; } /* ================= Mod_LoadAliasSkin ================= */ static void *Mod_LoadAliasSkin (void *pin, int *pskinindex, int skinsize, aliashdr_t *pheader) { int i; byte *pskin, *pinskin; unsigned short *pusskin; pskin = (byte *) Hunk_AllocName (skinsize * r_pixbytes, loadname); pinskin = (byte *)pin; *pskinindex = (byte *)pskin - (byte *)pheader; if (r_pixbytes == 1) { memcpy (pskin, pinskin, skinsize); } else if (r_pixbytes == 2) { pusskin = (unsigned short *)pskin; for (i = 0; i < skinsize; i++) pusskin[i] = d_8to16table[pinskin[i]]; } else { Sys_Error ("%s: driver set invalid r_pixbytes: %d", __thisfunc__, r_pixbytes); } pinskin += skinsize; return ((void *)pinskin); } /* ================= Mod_LoadAliasSkinGroup ================= */ static void *Mod_LoadAliasSkinGroup (void *pin, int *pskinindex, int skinsize, aliashdr_t *pheader) { daliasskingroup_t *pinskingroup; maliasskingroup_t *paliasskingroup; int i, numskins; daliasskininterval_t *pinskinintervals; float *poutskinintervals; void *ptemp; pinskingroup = (daliasskingroup_t *)pin; numskins = LittleLong (pinskingroup->numskins); paliasskingroup = (maliasskingroup_t *) Hunk_AllocName (sizeof(maliasskingroup_t) + (numskins - 1) * sizeof(paliasskingroup->skindescs[0]), loadname); paliasskingroup->numskins = numskins; *pskinindex = (byte *)paliasskingroup - (byte *)pheader; pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1); poutskinintervals = (float *) Hunk_AllocName (numskins * sizeof(float),loadname); paliasskingroup->intervals = (byte *)poutskinintervals - (byte *)pheader; for (i = 0; i < numskins; i++) { *poutskinintervals = LittleFloat (pinskinintervals->interval); if (*poutskinintervals <= 0) Sys_Error ("%s: interval <= 0", __thisfunc__); poutskinintervals++; pinskinintervals++; } ptemp = (void *)pinskinintervals; for (i = 0; i < numskins; i++) { ptemp = Mod_LoadAliasSkin (ptemp, &paliasskingroup->skindescs[i].skin, skinsize, pheader); } return ptemp; } /* ================= Mod_LoadAliasModelNew loads new expanded header format needed to do vert opts ================= */ static void Mod_LoadAliasModelNew (qmodel_t *mod, void *buffer) { int i, j; newmdl_t *pinmodel; newmdl_t *pmodel; stvert_t *pstverts, *pinstverts; aliashdr_t *pheader; mtriangle_t *ptri; dnewtriangle_t *pintriangles; int version, numframes, numskins; int size; daliasframetype_t *pframetype; daliasskintype_t *pskintype; maliasskindesc_t *pskindesc; int skinsize; int start, end, total; start = Hunk_LowMark (); pinmodel = (newmdl_t *)buffer; version = LittleLong (pinmodel->version); if (version != ALIAS_NEWVERSION) Sys_Error ("%s has wrong version number (%i should be %i)", mod->name, version, ALIAS_NEWVERSION); // // allocate space for a working header, plus all the data except the frames, // skin and group info // size = sizeof (aliashdr_t) + (LittleLong (pinmodel->numframes) - 1) * sizeof (pheader->frames[0]) + sizeof (newmdl_t) + LittleLong (pinmodel->num_st_verts) * sizeof (stvert_t) + LittleLong (pinmodel->numtris) * sizeof (mtriangle_t); pheader = (aliashdr_t *) Hunk_AllocName (size, loadname); pmodel = (newmdl_t *) ((byte *)&pheader[1] + (LittleLong (pinmodel->numframes) - 1) * sizeof (pheader->frames[0])); // mod->cache.data = pheader; mod->flags = LittleLong (pinmodel->flags); // // endian-adjust and copy the data, starting with the alias model header // pmodel->boundingradius = LittleFloat (pinmodel->boundingradius); pmodel->numskins = LittleLong (pinmodel->numskins); pmodel->skinwidth = LittleLong (pinmodel->skinwidth); pmodel->skinheight = LittleLong (pinmodel->skinheight); if (pmodel->skinheight > MAX_SKIN_HEIGHT) Sys_Error ("model %s has a skin taller than %d", mod->name, MAX_SKIN_HEIGHT); pmodel->numverts = LittleLong (pinmodel->numverts); //use the new num pmodel->num_st_verts = LittleLong (pinmodel->num_st_verts); if (pmodel->numverts <= 0) Sys_Error ("model %s has no vertices", mod->name); if (pmodel->num_st_verts <= 0) Sys_Error ("model %s has no ST vertices", mod->name); if (pmodel->numverts > MAXALIASVERTS) Sys_Error ("model %s has too many vertices", mod->name); pmodel->numtris = LittleLong (pinmodel->numtris); if (pmodel->numtris <= 0) Sys_Error ("model %s has no triangles", mod->name); pmodel->numframes = LittleLong (pinmodel->numframes); pmodel->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO; mod->synctype = (synctype_t) LittleLong (pinmodel->synctype); mod->numframes = pmodel->numframes; for (i = 0; i < 3; i++) { pmodel->scale[i] = LittleFloat (pinmodel->scale[i]); pmodel->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]); pmodel->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]); } numskins = pmodel->numskins; numframes = pmodel->numframes; if (pmodel->skinwidth & 0x03) Sys_Error ("%s: skinwidth not multiple of 4", __thisfunc__); pheader->model = (byte *)pmodel - (byte *)pheader; // // load the skins // skinsize = pmodel->skinheight * pmodel->skinwidth; if (numskins < 1) Sys_Error ("%s: Invalid # of skins: %d", __thisfunc__, numskins); pskintype = (daliasskintype_t *)&pinmodel[1]; pskindesc = (maliasskindesc_t *) Hunk_AllocName (numskins * sizeof(maliasskindesc_t), loadname); pheader->skindesc = (byte *)pskindesc - (byte *)pheader; for (i = 0; i < numskins; i++) { aliasskintype_t skintype; skintype = (aliasskintype_t) LittleLong (pskintype->type); pskindesc[i].type = skintype; if (skintype == ALIAS_SKIN_SINGLE) { pskintype = (daliasskintype_t *) Mod_LoadAliasSkin (pskintype + 1, &pskindesc[i].skin, skinsize, pheader); } else { pskintype = (daliasskintype_t *) Mod_LoadAliasSkinGroup (pskintype + 1, &pskindesc[i].skin, skinsize, pheader); } } // // set base s and t vertices // pstverts = (stvert_t *)&pmodel[1]; pinstverts = (stvert_t *)pskintype; pheader->stverts = (byte *)pstverts - (byte *)pheader; //jfm: change for new models (use num_st_verts) for (i = 0; i < pmodel->num_st_verts; i++) { pstverts[i].onseam = LittleLong (pinstverts[i].onseam); // put s and t in 16.16 format pstverts[i].s = LittleLong (pinstverts[i].s) << 16; pstverts[i].t = LittleLong (pinstverts[i].t) << 16; } // // set up the triangles // //jfm: change for new models (use num_st_verts) ptri = (mtriangle_t *)&pstverts[pmodel->num_st_verts]; pintriangles = (dnewtriangle_t *)&pinstverts[pmodel->num_st_verts]; pheader->triangles = (byte *)ptri - (byte *)pheader; for (i = 0; i < pmodel->numtris; i++) { ptri[i].facesfront = LittleLong (pintriangles[i].facesfront); for (j = 0; j < 3; j++) { ptri[i].vertindex[j] = LittleShort (pintriangles[i].vertindex[j]); ptri[i].stindex[j] = LittleShort (pintriangles[i].stindex[j]); } } // // load the frames // if (numframes < 1) Sys_Error ("%s: Invalid # of frames: %d", __thisfunc__, numframes); pframetype = (daliasframetype_t *)&pintriangles[pmodel->numtris]; aliasmins[0] = aliasmins[1] = aliasmins[2] = 32768; aliasmaxs[0] = aliasmaxs[1] = aliasmaxs[2] = -32768; for (i = 0; i < numframes; i++) { aliasframetype_t frametype; frametype = (aliasframetype_t) LittleLong (pframetype->type); pheader->frames[i].type = frametype; if (frametype == ALIAS_SINGLE) { pframetype = (daliasframetype_t *) Mod_LoadAliasFrame (pframetype + 1, &pheader->frames[i].frame, pmodel->numverts, &pheader->frames[i].bboxmin, &pheader->frames[i].bboxmax, pheader, pheader->frames[i].name, pmodel); } else { pframetype = (daliasframetype_t *) Mod_LoadAliasGroup (pframetype + 1, &pheader->frames[i].frame, pmodel->numverts, &pheader->frames[i].bboxmin, &pheader->frames[i].bboxmax, pheader, pheader->frames[i].name, pmodel); } } mod->type = mod_alias; // FIXME: do this right // mod->mins[0] = mod->mins[1] = mod->mins[2] = -16; // mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16; mod->mins[0] = aliasmins[0]; mod->mins[1] = aliasmins[1]; mod->mins[2] = aliasmins[2]; mod->maxs[0] = aliasmaxs[0]; mod->maxs[1] = aliasmaxs[1]; mod->maxs[2] = aliasmaxs[2]; // // move the complete, relocatable alias model to the cache // end = Hunk_LowMark (); total = end - start; Cache_Alloc (&mod->cache, total, mod->name); if (!mod->cache.data) return; memcpy (mod->cache.data, pheader, total); Hunk_FreeToLowMark (start); } /* ================= Mod_LoadAliasModel loads old model fmt, converts to new ================= */ static void Mod_LoadAliasModel (qmodel_t *mod, void *buffer) { int i, j; mdl_t *pinmodel; newmdl_t *pmodel; stvert_t *pstverts, *pinstverts; aliashdr_t *pheader; mtriangle_t *ptri; dtriangle_t *pintriangles; int version, numframes, numskins; int size; daliasframetype_t *pframetype; daliasskintype_t *pskintype; maliasskindesc_t *pskindesc; int skinsize; int start, end, total; #if defined(H2W) /* // rjr FIXME if (!strcmp(loadmodel->name, "models/paladin.mdl")) { unsigned short crc; byte *p; int len; char st[40]; CRC_Init(&crc); for (len = fs_filesize, p = (byte *)buffer; len; len--, p++) CRC_ProcessByte(&crc, *p); q_snprintf (st, sizeof(st), "%d", (int) crc); // rjr FIXME Info_SetValueForKey (cls.userinfo, "pmodel", st, MAX_INFO_STRING);//"emodel" // rjr FIXME if (cls.state >= ca_connected) { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); q_snprintf (st, sizeof(st), "setinfo %s %d", "pmodel", (int)crc);//"emodel" SZ_Print (&cls.netchan.message, st); } } */ #endif start = Hunk_LowMark (); pinmodel = (mdl_t *)buffer; version = LittleLong (pinmodel->version); if (version != ALIAS_VERSION) Sys_Error ("%s has wrong version number (%i should be %i)", mod->name, version, ALIAS_VERSION); // // allocate space for a working header, plus all the data except the frames, // skin and group info // size = sizeof (aliashdr_t) + (LittleLong (pinmodel->numframes) - 1) * sizeof (pheader->frames[0]) + sizeof (newmdl_t) + LittleLong (pinmodel->numverts) * sizeof (stvert_t) + LittleLong (pinmodel->numtris) * sizeof (mtriangle_t); pheader = (aliashdr_t *) Hunk_AllocName (size, loadname); pmodel = (newmdl_t *) ((byte *)&pheader[1] + (LittleLong (pinmodel->numframes) - 1) * sizeof (pheader->frames[0])); // mod->cache.data = pheader; mod->flags = LittleLong (pinmodel->flags); // // endian-adjust and copy the data, starting with the alias model header // pmodel->boundingradius = LittleFloat (pinmodel->boundingradius); pmodel->numskins = LittleLong (pinmodel->numskins); pmodel->skinwidth = LittleLong (pinmodel->skinwidth); pmodel->skinheight = LittleLong (pinmodel->skinheight); if (pmodel->skinheight > MAX_SKIN_HEIGHT) Sys_Error ("model %s has a skin taller than %d", mod->name, MAX_SKIN_HEIGHT); pmodel->numverts = LittleLong (pinmodel->numverts); pmodel->num_st_verts = LittleLong (pinmodel->numverts); if (pmodel->numverts <= 0) Sys_Error ("model %s has no vertices", mod->name); if (pmodel->numverts > MAXALIASVERTS) Sys_Error ("model %s has too many vertices", mod->name); pmodel->numtris = LittleLong (pinmodel->numtris); if (pmodel->numtris <= 0) Sys_Error ("model %s has no triangles", mod->name); pmodel->numframes = LittleLong (pinmodel->numframes); pmodel->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO; mod->synctype = (synctype_t) LittleLong (pinmodel->synctype); mod->numframes = pmodel->numframes; for (i = 0; i < 3; i++) { pmodel->scale[i] = LittleFloat (pinmodel->scale[i]); pmodel->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]); pmodel->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]); } numskins = pmodel->numskins; numframes = pmodel->numframes; if (pmodel->skinwidth & 0x03) Sys_Error ("%s: skinwidth not multiple of 4", __thisfunc__); pheader->model = (byte *)pmodel - (byte *)pheader; // // load the skins // skinsize = pmodel->skinheight * pmodel->skinwidth; if (numskins < 1) Sys_Error ("%s: Invalid # of skins: %d", __thisfunc__, numskins); pskintype = (daliasskintype_t *)&pinmodel[1]; pskindesc = (maliasskindesc_t *) Hunk_AllocName (numskins * sizeof(maliasskindesc_t), loadname); pheader->skindesc = (byte *)pskindesc - (byte *)pheader; for (i = 0; i < numskins; i++) { aliasskintype_t skintype; skintype = (aliasskintype_t) LittleLong (pskintype->type); pskindesc[i].type = skintype; if (skintype == ALIAS_SKIN_SINGLE) { pskintype = (daliasskintype_t *) Mod_LoadAliasSkin (pskintype + 1, &pskindesc[i].skin, skinsize, pheader); } else { pskintype = (daliasskintype_t *) Mod_LoadAliasSkinGroup (pskintype + 1, &pskindesc[i].skin, skinsize, pheader); } } // // set base s and t vertices // pstverts = (stvert_t *)&pmodel[1]; pinstverts = (stvert_t *)pskintype; pheader->stverts = (byte *)pstverts - (byte *)pheader; //jfm: change for new models (use num_st_verts) for (i = 0; i < pmodel->num_st_verts; i++) { pstverts[i].onseam = LittleLong (pinstverts[i].onseam); // put s and t in 16.16 format pstverts[i].s = LittleLong (pinstverts[i].s) << 16; pstverts[i].t = LittleLong (pinstverts[i].t) << 16; } // // set up the triangles // //jfm: change for new models (use num_st_verts) ptri = (mtriangle_t *)&pstverts[pmodel->num_st_verts]; pintriangles = (dtriangle_t *)&pinstverts[pmodel->num_st_verts]; pheader->triangles = (byte *)ptri - (byte *)pheader; for (i = 0; i < pmodel->numtris; i++) { ptri[i].facesfront = LittleLong (pintriangles[i].facesfront); for (j = 0; j < 3; j++) { #if 0 if (pintriangles[i].vertindex[j] > Q_MAXSHORT) Sys_Error ("%s: ind too big!", __thisfunc__); #endif ptri[i].vertindex[j] =(short) LittleLong (pintriangles[i].vertindex[j]); ptri[i].stindex[j] = ptri[i].vertindex[j]; //MAKE A COPY } } // // load the frames // if (numframes < 1) Sys_Error ("%s: Invalid # of frames: %d", __thisfunc__, numframes); pframetype = (daliasframetype_t *)&pintriangles[pmodel->numtris]; aliasmins[0] = aliasmins[1] = aliasmins[2] = 32768; aliasmaxs[0] = aliasmaxs[1] = aliasmaxs[2] = -32768; for (i = 0; i < numframes; i++) { aliasframetype_t frametype; frametype = (aliasframetype_t) LittleLong (pframetype->type); pheader->frames[i].type = frametype; if (frametype == ALIAS_SINGLE) { pframetype = (daliasframetype_t *) Mod_LoadAliasFrame (pframetype + 1, &pheader->frames[i].frame, pmodel->numverts, &pheader->frames[i].bboxmin, &pheader->frames[i].bboxmax, pheader, pheader->frames[i].name, pmodel); } else { pframetype = (daliasframetype_t *) Mod_LoadAliasGroup (pframetype + 1, &pheader->frames[i].frame, pmodel->numverts, &pheader->frames[i].bboxmin, &pheader->frames[i].bboxmax, pheader, pheader->frames[i].name, pmodel); } } mod->type = mod_alias; // FIXME: do this right // mod->mins[0] = mod->mins[1] = mod->mins[2] = -16; // mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16; mod->mins[0] = aliasmins[0]; mod->mins[1] = aliasmins[1]; mod->mins[2] = aliasmins[2]; mod->maxs[0] = aliasmaxs[0]; mod->maxs[1] = aliasmaxs[1]; mod->maxs[2] = aliasmaxs[2]; // // move the complete, relocatable alias model to the cache // end = Hunk_LowMark (); total = end - start; Cache_Alloc (&mod->cache, total, mod->name); if (!mod->cache.data) return; memcpy (mod->cache.data, pheader, total); Hunk_FreeToLowMark (start); } //============================================================================= /* ================= Mod_LoadSpriteFrame ================= */ static void *Mod_LoadSpriteFrame (void *pin, mspriteframe_t **ppframe) { dspriteframe_t *pinframe; mspriteframe_t *pspriteframe; int i, width, height, size, origin[2]; unsigned short *ppixout; byte *ppixin; pinframe = (dspriteframe_t *)pin; width = LittleLong (pinframe->width); height = LittleLong (pinframe->height); size = width * height; pspriteframe = (mspriteframe_t *) Hunk_AllocName (sizeof(mspriteframe_t) + size*r_pixbytes, loadname); *ppframe = pspriteframe; pspriteframe->width = width; pspriteframe->height = height; origin[0] = LittleLong (pinframe->origin[0]); origin[1] = LittleLong (pinframe->origin[1]); pspriteframe->up = origin[1]; pspriteframe->down = origin[1] - height; pspriteframe->left = origin[0]; pspriteframe->right = width + origin[0]; if (r_pixbytes == 1) { memcpy (&pspriteframe->pixels[0], (byte *)(pinframe + 1), size); } else if (r_pixbytes == 2) { ppixin = (byte *)(pinframe + 1); ppixout = (unsigned short *)&pspriteframe->pixels[0]; for (i = 0; i < size; i++) ppixout[i] = d_8to16table[ppixin[i]]; } else { Sys_Error ("%s: driver set invalid r_pixbytes: %d", __thisfunc__, r_pixbytes); } return (void *)((byte *)pinframe + sizeof (dspriteframe_t) + size); } /* ================= Mod_LoadSpriteGroup ================= */ static void *Mod_LoadSpriteGroup (void *pin, mspriteframe_t **ppframe) { dspritegroup_t *pingroup; mspritegroup_t *pspritegroup; int i, numframes; dspriteinterval_t *pin_intervals; float *poutintervals; void *ptemp; pingroup = (dspritegroup_t *)pin; numframes = LittleLong (pingroup->numframes); pspritegroup = (mspritegroup_t *) Hunk_AllocName (sizeof(mspritegroup_t) + (numframes - 1) * sizeof(pspritegroup->frames[0]), loadname); pspritegroup->numframes = numframes; *ppframe = (mspriteframe_t *)pspritegroup; pin_intervals = (dspriteinterval_t *)(pingroup + 1); poutintervals = (float *) Hunk_AllocName (numframes * sizeof(float), loadname); pspritegroup->intervals = poutintervals; for (i = 0; i < numframes; i++) { *poutintervals = LittleFloat (pin_intervals->interval); if (*poutintervals <= 0.0) Sys_Error ("%s: interval <= 0", __thisfunc__); poutintervals++; pin_intervals++; } ptemp = (void *)pin_intervals; for (i = 0; i < numframes; i++) { ptemp = Mod_LoadSpriteFrame (ptemp, &pspritegroup->frames[i]); } return ptemp; } /* ================= Mod_LoadSpriteModel ================= */ static void Mod_LoadSpriteModel (qmodel_t *mod, void *buffer) { int i; int version; dsprite_t *pin; msprite_t *psprite; int numframes; int size; dspriteframetype_t *pframetype; pin = (dsprite_t *)buffer; version = LittleLong (pin->version); if (version != SPRITE_VERSION) Sys_Error ("%s has wrong version number " "(%i should be %i)", mod->name, version, SPRITE_VERSION); numframes = LittleLong (pin->numframes); size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames); psprite = (msprite_t *) Hunk_AllocName (size, mod->name); mod->cache.data = psprite; psprite->type = LittleLong (pin->type); psprite->maxwidth = LittleLong (pin->width); psprite->maxheight = LittleLong (pin->height); psprite->beamlength = LittleFloat (pin->beamlength); mod->synctype = (synctype_t) LittleLong (pin->synctype); psprite->numframes = numframes; mod->mins[0] = mod->mins[1] = -psprite->maxwidth/2; mod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2; mod->mins[2] = -psprite->maxheight/2; mod->maxs[2] = psprite->maxheight/2; // // load the frames // if (numframes < 1) Sys_Error ("%s: Invalid # of frames: %d", __thisfunc__, numframes); mod->numframes = numframes; mod->flags = 0; pframetype = (dspriteframetype_t *)(pin + 1); for (i = 0; i < numframes; i++) { spriteframetype_t frametype; frametype = (spriteframetype_t) LittleLong (pframetype->type); psprite->frames[i].type = frametype; if (frametype == SPR_SINGLE) { pframetype = (dspriteframetype_t *) Mod_LoadSpriteFrame (pframetype + 1, &psprite->frames[i].frameptr); } else { pframetype = (dspriteframetype_t *) Mod_LoadSpriteGroup (pframetype + 1, &psprite->frames[i].frameptr); } } mod->type = mod_sprite; } //============================================================================= /* ================ Mod_Print ================ */ #if defined(__GNUC__) && !(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) #define MOD_Printf(FH, fmt, args...) \ do { \ if ((FH)) fprintf((FH), fmt, ##args); \ else Con_Printf(fmt, ##args); \ } while (0) #else #define MOD_Printf(FH, ...) \ do { \ if ((FH)) fprintf((FH), __VA_ARGS__); \ else Con_Printf(__VA_ARGS__); \ } while (0) #endif static void Mod_Print (void) { int i, counter; FILE *FH = NULL; qmodel_t *mod; i = Cmd_Argc(); for (counter = 1; counter < i; counter++) { if (q_strcasecmp(Cmd_Argv(counter),"save") == 0) { FH = fopen(FS_MakePath(FS_USERDIR,NULL,"mcache.txt"), "w"); break; } } MOD_Printf (FH, "Cached models:\n"); for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { MOD_Printf (FH, "%4i (%8p): %s", i, mod->cache.data, mod->name); if (mod->needload & NL_UNREFERENCED) MOD_Printf (FH, " (!R)"); if (mod->needload & NL_NEEDS_LOADED) MOD_Printf (FH, " (!P)"); MOD_Printf (FH, "\n"); } if (FH) { fclose(FH); Con_Printf ("Wrote to mcache.txt\n"); } } engine/h2shared/model.h000066400000000000000000000272001444734033100153000ustar00rootroot00000000000000/* * model.h -- header for model loading and caching * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef H2_MODEL_H #define H2_MODEL_H #include "genmodel.h" #include "spritegn.h" /* d*_t structures are on-disk representations m*_t structures are in-memory */ /* ============================================================================== BRUSH MODELS ============================================================================== */ // // in memory representation // // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct { vec3_t position; } mvertex_t; #define SIDE_FRONT 0 #define SIDE_BACK 1 #define SIDE_ON 2 // plane_t structure // !!! if this is changed, it must be changed in asm_i386.h too !!! typedef struct mplane_s { vec3_t normal; float dist; byte type; // for texture axis selection and fast side tests byte signbits; // signx + signy<<1 + signz<<1 byte pad[2]; } mplane_t; typedef struct texture_s { char name[16]; unsigned int width, height; int anim_total; // total tenths in sequence ( 0 = no) int anim_min, anim_max; // time for this frame min <=time< max struct texture_s *anim_next; // in the animation sequence struct texture_s *alternate_anims; // bmodels in frmae 1 use these unsigned int offsets[MIPLEVELS]; // four mip maps stored } texture_t; #define SURF_PLANEBACK 2 #define SURF_DRAWSKY 4 #define SURF_DRAWSPRITE 8 #define SURF_DRAWTURB 0x10 #define SURF_DRAWTILED 0x20 #define SURF_DRAWBACKGROUND 0x40 #define SURF_TRANSLUCENT 0x80 /* r_edge.asm checks this */ #define SURF_DRAWBLACK 0x100 #define SURF_DRAWSOLID 0x200 // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct { unsigned int v[2]; unsigned int cachededgeoffset; } medge_t; typedef struct { float vecs[2][4]; float mipadjust; texture_t *texture; int flags; } mtexinfo_t; typedef struct msurface_s { int visframe; // should be drawn when node is crossed int dlightframe; int dlightbits; mplane_t *plane; int flags; int firstedge; // look up in model->surfedges[], negative numbers int numedges; // are backwards edges // surface generation data struct surfcache_s *cachespots[MIPLEVELS]; int texturemins[2]; int extents[2]; mtexinfo_t *texinfo; // lighting info byte styles[MAXLIGHTMAPS]; byte *samples; // [numstyles*surfsize] } msurface_t; typedef struct mnode_s { // common with leaf int contents; // 0, to differentiate from leafs int visframe; // node needs to be traversed if current float minmaxs[6]; // for bounding box culling struct mnode_s *parent; // node specific mplane_t *plane; struct mnode_s *children[2]; unsigned int firstsurface; unsigned int numsurfaces; } mnode_t; typedef struct mleaf_s { // common with node int contents; // wil be a negative contents number int visframe; // node needs to be traversed if current float minmaxs[6]; // for bounding box culling struct mnode_s *parent; // leaf specific byte *compressed_vis; struct efrag_s *efrags; msurface_t **firstmarksurface; int nummarksurfaces; int key; // BSP sequence number for leaf's contents byte ambient_sound_level[NUM_AMBIENTS]; } mleaf_t; #ifdef ENABLE_BSP2 typedef dclipnode2_t mclipnode_t; #else typedef dclipnode_t mclipnode_t; #endif // !!! if this is changed, it must be changed in asm_i386.h too !!! typedef struct { mclipnode_t *clipnodes; mplane_t *planes; int firstclipnode; int lastclipnode; vec3_t clip_mins; vec3_t clip_maxs; } hull_t; #define HULL_IMPLICIT 0 // Choose the hull based on bounding box- like in Quake #define HULL_POINT 1 // 0 0 0, 0 0 0 #define HULL_PLAYER 2 // '-16 -16 0', '16 16 56' #define HULL_SCORPION 3 // '-24 -24 -20', '24 24 20' #define HULL_CROUCH 4 // '-16 -16 0', '16 16 28' #define HULL_HYDRA 5 // '-28 -28 -24', '28 28 24' #define HULL_GOLEM 6 // ???,??? /* ============================================================================== SPRITE MODELS ============================================================================== */ // FIXME: shorten these? typedef struct mspriteframe_s { short width; short height; //void *pcachespot; // remove? float up, down, left, right; byte pixels[4]; } mspriteframe_t; typedef struct { short numframes; float *intervals; mspriteframe_t *frames[1]; } mspritegroup_t; typedef struct { spriteframetype_t type; mspriteframe_t *frameptr; } mspriteframedesc_t; typedef struct { short type; short maxwidth; short maxheight; short numframes; float beamlength; // remove? //void *cachespot; // remove? mspriteframedesc_t frames[1]; } msprite_t; /* ============================================================================== ALIAS MODELS Alias models are position independent, so the cache manager can move them. ============================================================================== */ typedef struct { aliasframetype_t type; trivertx_t bboxmin; trivertx_t bboxmax; int frame; char name[16]; } maliasframedesc_t; typedef struct { aliasskintype_t type; void *pcachespot; int skin; } maliasskindesc_t; typedef struct { trivertx_t bboxmin; trivertx_t bboxmax; int frame; } maliasgroupframedesc_t; typedef struct { int numframes; int intervals; maliasgroupframedesc_t frames[1]; } maliasgroup_t; typedef struct { int numskins; int intervals; maliasskindesc_t skindescs[1]; } maliasskingroup_t; // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct mtriangle_s { int facesfront; unsigned short vertindex[3]; unsigned short stindex[3]; } mtriangle_t; typedef struct { int model; int stverts; int skindesc; int triangles; maliasframedesc_t frames[1]; } aliashdr_t; //=================================================================== // // entity effects // #ifndef H2W /* see below for hexenworld */ #define EF_BRIGHTFIELD 0x00000001 #endif #define EF_MUZZLEFLASH 0x00000002 #define EF_BRIGHTLIGHT 0x00000004 #define EF_DIMLIGHT 0x00000008 #define EF_DARKLIGHT 0x00000010 #define EF_DARKFIELD 0x00000020 #define EF_LIGHT 0x00000040 #define EF_NODRAW 0x00000080 #ifdef H2W /* The only difference between Raven's hw-0.15 binary release and the * later HexenC source release is the EF_BRIGHTFIELD and EF_ONFIRE values: * the original binary releases had them as 1 and 1024 respectively, but * the later hcode src releases have them flipped: EF_BRIGHTFIELD = 1024 * and EF_ONFIRE = 1, which is a BIG BOO BOO. (On the other hand, Siege * binary and source releases have EF_BRIGHTFIELD and EF_ONFIRE values as * 1 and 1024, which makes the mess even messier.. Sigh..) * The hexenworld engine src release also have EF_BRIGHTFIELD as 1024 and * EF_ONFIRE as 1, therefore uHexen2 sticks to those values. */ #define EF_ONFIRE 0x00000001 #define EF_BRIGHTFIELD 0x00000400 #define EF_POWERFLAMEBURN 0x00000800 #define EF_UPDATESOUND 0x00002000 #define EF_POISON_GAS 0x00200000 #define EF_ACIDBLOB 0x00400000 //#define EF_PURIFY2_EFFECT 0x00200000 //#define EF_AXE_EFFECT 0x00400000 //#define EF_SWORD_EFFECT 0x00800000 //#define EF_TORNADO_EFFECT 0x01000000 #define EF_ICESTORM_EFFECT 0x02000000 //#define EF_ICEBALL_EFFECT 0x04000000 //#define EF_METEOR_EFFECT 0x08000000 #define EF_HAMMER_EFFECTS 0x10000000 #define EF_BEETLE_EFFECTS 0x20000000 #endif /* H2W */ //=================================================================== // // Whole model // typedef enum {mod_brush, mod_sprite, mod_alias} modtype_t; /* EF_ changes must also be made in both model.h and gl_model.h and you MUST check with the constants in gamecode, as well. */ #define EF_ROCKET (1 << 0 ) /* leave a trail */ #define EF_GRENADE (1 << 1 ) /* leave a trail */ #define EF_GIB (1 << 2 ) /* leave a trail */ #define EF_ROTATE (1 << 3 ) /* rotate (bonus items) */ #define EF_TRACER (1 << 4 ) /* green split trail */ #define EF_ZOMGIB (1 << 5 ) /* small blood trail */ #define EF_TRACER2 (1 << 6 ) /* orange split trail + rotate */ #define EF_TRACER3 (1 << 7 ) /* purple trail */ #define EF_FIREBALL (1 << 8 ) /* Yellow transparent trail in all directions */ #define EF_ICE (1 << 9 ) /* Blue-white transparent trail, with gravity */ #define EF_MIP_MAP (1 << 10) /* This model has mip-maps */ #define EF_SPIT (1 << 11) /* Black transparent trail with negative light */ #define EF_TRANSPARENT (1 << 12) /* Transparent sprite */ #define EF_SPELL (1 << 13) /* Vertical spray of particles */ #define EF_HOLEY (1 << 14) /* Solid model with color 0 */ #define EF_SPECIAL_TRANS (1 << 15) /* Translucency through the particle table */ #define EF_FACE_VIEW (1 << 16) /* Poly Model always faces you */ #define EF_VORP_MISSILE (1 << 17) /* leave a trail at top and bottom of model */ #define EF_SET_STAFF (1 << 18) /* slowly move up and left/right */ #define EF_MAGICMISSILE (1 << 19) /* a trickle of blue/white particles with gravity */ #define EF_BONESHARD (1 << 20) /* a trickle of brown particles with gravity */ #define EF_SCARAB (1 << 21) /* white transparent particles with little gravity */ #define EF_ACIDBALL (1 << 22) /* Green drippy acid shit */ #define EF_BLOODSHOT (1 << 23) /* Blood rain shot trail */ #define EF_MIP_MAP_FAR (1 << 24) /* Set per frame, this model will use the far mip map */ typedef struct qmodel_s { char name[MAX_QPATH]; unsigned int path_id; // path id of the game directory // that this model came from int needload; // bmodels and sprites don't cache normally modtype_t type; int numframes; synctype_t synctype; int flags; // // volume occupied by the model graphics // vec3_t mins, maxs; float radius; // // brush model // int firstmodelsurface, nummodelsurfaces; int numsubmodels; dmodel_t *submodels; int numplanes; mplane_t *planes; int numleafs; // number of visible leafs, not counting 0 mleaf_t *leafs; int numvertexes; mvertex_t *vertexes; int numedges; medge_t *edges; int numnodes; mnode_t *nodes; int numtexinfo; mtexinfo_t *texinfo; int numsurfaces; msurface_t *surfaces; int numsurfedges; int *surfedges; int numclipnodes; mclipnode_t *clipnodes; int nummarksurfaces; msurface_t **marksurfaces; hull_t hulls[MAX_MAP_HULLS]; int numtextures; texture_t **textures; byte *visdata; byte *lightdata; char *entities; // // additional model data // cache_user_t cache; // only access through Mod_Extradata } qmodel_t; // values for qmodel_t->needload #define NL_PRESENT 0 #define NL_NEEDS_LOADED 1 #define NL_UNREFERENCED 2 //============================================================================ void Mod_Init (void); void Mod_ClearAll (void); qmodel_t *Mod_ForName (const char *name, qboolean crash); qmodel_t *Mod_FindName (const char *name); void *Mod_Extradata (qmodel_t *mod); // handles caching void Mod_TouchModel (const char *name); mleaf_t *Mod_PointInLeaf (vec3_t p, qmodel_t *model); byte *Mod_LeafPVS (mleaf_t *leaf, qmodel_t *model); #endif /* H2_MODEL_H */ engine/h2shared/msg_io.c000066400000000000000000000166361444734033100154630ustar00rootroot00000000000000/* * msg_io.c -- Message IO functions * Handles byte ordering and avoids alignment errors * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include "h2config.h" #include "compiler.h" #include "q_endian.h" #include "sys.h" #include "sizebuf.h" #include "msg_io.h" #if defined(H2W) #include "protocol.h" #endif /* H2W */ extern sizebuf_t net_message; // // writing functions // void MSG_WriteChar (sizebuf_t *sb, int c) { byte *buf; #ifdef PARANOID if (c < -128 || c > 127) Sys_Error ("%s: range error", __thisfunc__); #endif buf = (byte *) SZ_GetSpace (sb, 1); buf[0] = c; } void MSG_WriteByte (sizebuf_t *sb, int c) { byte *buf; #ifdef PARANOID if (c < 0 || c > 255) Sys_Error ("%s: range error", __thisfunc__); #endif buf = (byte *) SZ_GetSpace (sb, 1); buf[0] = c; } void MSG_WriteShort (sizebuf_t *sb, int c) { byte *buf; #ifdef PARANOID if (c < ((short)0x8000) || c > (short)0x7fff) Sys_Error ("%s: range error", __thisfunc__); #endif buf = (byte *) SZ_GetSpace (sb, 2); buf[0] = c&0xff; buf[1] = c>>8; } void MSG_WriteLong (sizebuf_t *sb, int c) { byte *buf; buf = (byte *) SZ_GetSpace (sb, 4); buf[0] = c&0xff; buf[1] = (c>>8)&0xff; buf[2] = (c>>16)&0xff; buf[3] = c>>24; } void MSG_WriteFloat (sizebuf_t *sb, float f) { union { float f; int l; } dat; dat.f = f; dat.l = LittleLong (dat.l); SZ_Write (sb, &dat.l, 4); } void MSG_WriteString (sizebuf_t *sb, const char *s) { if (!s) SZ_Write (sb, "", 1); else SZ_Write (sb, s, (int)strlen(s) + 1); } void MSG_WriteCoord (sizebuf_t *sb, float f) { // MSG_WriteShort (sb, (int)(f*8)); if (f >= 0) MSG_WriteShort (sb, (int)(f * 8.0f + 0.5f)); else MSG_WriteShort (sb, (int)(f * 8.0f - 0.5f)); } void MSG_WriteAngle (sizebuf_t *sb, float f) { // MSG_WriteByte (sb, (int)(f*256/360) & 255); // LordHavoc: round to nearest value, rather than rounding toward zero if (f >= 0) MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) + 0.5f) & 255); else MSG_WriteByte (sb, (int)(f*(256.0f/360.0f) - 0.5f) & 255); } #if defined(H2W) void MSG_WriteAngle16 (sizebuf_t *sb, float f) { // MSG_WriteShort (sb, (int)(f*65536/360) & 65535); if (f >= 0) MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) + 0.5f) & 65535); else MSG_WriteShort (sb, (int)(f*(65536.0f/360.0f) - 0.5f) & 65535); } void MSG_WriteUsercmd (sizebuf_t *buf, const usercmd_t *cmd, qboolean long_msg) { int bits; // // send the movement message // bits = 0; if (cmd->angles[0]) bits |= CM_ANGLE1; if (cmd->angles[2]) bits |= CM_ANGLE3; if (cmd->forwardmove) bits |= CM_FORWARD; if (cmd->sidemove) bits |= CM_SIDE; if (cmd->upmove) bits |= CM_UP; if (cmd->buttons) bits |= CM_BUTTONS; if (cmd->impulse) bits |= CM_IMPULSE; if (cmd->msec) bits |= CM_MSEC; MSG_WriteByte (buf, bits); if (long_msg) { MSG_WriteByte (buf, cmd->light_level); } if (bits & CM_ANGLE1) MSG_WriteAngle16 (buf, cmd->angles[0]); MSG_WriteAngle16 (buf, cmd->angles[1]); if (bits & CM_ANGLE3) MSG_WriteAngle16 (buf, cmd->angles[2]); if (bits & CM_FORWARD) MSG_WriteChar (buf, (int)(cmd->forwardmove*0.25f)); if (bits & CM_SIDE) MSG_WriteChar (buf, (int)(cmd->sidemove*0.25f)); if (bits & CM_UP) MSG_WriteChar (buf, (int)(cmd->upmove*0.25f)); if (bits & CM_BUTTONS) MSG_WriteByte (buf, cmd->buttons); if (bits & CM_IMPULSE) MSG_WriteByte (buf, cmd->impulse); if (bits & CM_MSEC) MSG_WriteByte (buf, cmd->msec); } #endif /* H2W */ // // reading functions // int msg_readcount; qboolean msg_badread; void MSG_BeginReading (void) { msg_readcount = 0; msg_badread = false; } // returns -1 and sets msg_badread if no more characters are available int MSG_ReadChar (void) { int c; if (msg_readcount+1 > net_message.cursize) { msg_badread = true; return -1; } c = (signed char)net_message.data[msg_readcount]; msg_readcount++; return c; } int MSG_ReadByte (void) { int c; if (msg_readcount+1 > net_message.cursize) { msg_badread = true; return -1; } c = (unsigned char)net_message.data[msg_readcount]; msg_readcount++; return c; } int MSG_ReadShort (void) { int c; if (msg_readcount+2 > net_message.cursize) { msg_badread = true; return -1; } c = (short)(net_message.data[msg_readcount] + (net_message.data[msg_readcount+1]<<8)); msg_readcount += 2; return c; } int MSG_ReadLong (void) { int c; if (msg_readcount+4 > net_message.cursize) { msg_badread = true; return -1; } c = net_message.data[msg_readcount] + (net_message.data[msg_readcount+1]<<8) + (net_message.data[msg_readcount+2]<<16) + (net_message.data[msg_readcount+3]<<24); msg_readcount += 4; return c; } float MSG_ReadFloat (void) { union { byte b[4]; float f; int l; } dat; dat.b[0] = net_message.data[msg_readcount]; dat.b[1] = net_message.data[msg_readcount+1]; dat.b[2] = net_message.data[msg_readcount+2]; dat.b[3] = net_message.data[msg_readcount+3]; msg_readcount += 4; dat.l = LittleLong (dat.l); return dat.f; } const char *MSG_ReadString (void) { static char string[2048]; int c; size_t l; l = 0; do { c = MSG_ReadByte (); if (c == -1 || c == 0) break; string[l] = c; l++; } while (l < sizeof(string) - 1); string[l] = 0; return string; } #if defined(H2W) const char *MSG_ReadStringLine (void) { static char string[2048]; int c; size_t l; l = 0; do { c = MSG_ReadByte (); if (c == -1 || c == 0 || c == '\n') break; string[l] = c; l++; } while (l < sizeof(string) - 1); string[l] = 0; return string; } #endif /* H2W */ float MSG_ReadCoord (void) { return MSG_ReadShort() * (1.0f/8.0f); } float MSG_ReadAngle (void) { return MSG_ReadChar() * (360.0f/256.0f); } #if defined(H2W) float MSG_ReadAngle16 (void) { return MSG_ReadShort() * (360.0f/65536.0f); } void MSG_ReadUsercmd (usercmd_t *move, qboolean long_msg) { int bits; memset (move, 0, sizeof(*move)); bits = MSG_ReadByte (); if (long_msg) { move->light_level = MSG_ReadByte(); } else { move->light_level = 0; } // read current angles if (bits & CM_ANGLE1) move->angles[0] = MSG_ReadAngle16 (); else move->angles[0] = 0; move->angles[1] = MSG_ReadAngle16 (); if (bits & CM_ANGLE3) move->angles[2] = MSG_ReadAngle16 (); else move->angles[2] = 0; // read movement if (bits & CM_FORWARD) move->forwardmove = MSG_ReadChar () * 4; if (bits & CM_SIDE) move->sidemove = MSG_ReadChar () * 4; if (bits & CM_UP) move->upmove = MSG_ReadChar () * 4; // read buttons if (bits & CM_BUTTONS) move->buttons = MSG_ReadByte (); else move->buttons = 0; if (bits & CM_IMPULSE) move->impulse = MSG_ReadByte (); else move->impulse = 0; // read time to run command if (bits & CM_MSEC) move->msec = MSG_ReadByte (); else move->msec = 0; } #endif /* H2W */ engine/h2shared/msg_io.h000066400000000000000000000037631444734033100154650ustar00rootroot00000000000000/* * msg_io.h -- Message IO functions * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __MSGIO_H #define __MSGIO_H void MSG_WriteChar (sizebuf_t *sb, int c); void MSG_WriteByte (sizebuf_t *sb, int c); void MSG_WriteShort (sizebuf_t *sb, int c); void MSG_WriteLong (sizebuf_t *sb, int c); void MSG_WriteFloat (sizebuf_t *sb, float f); void MSG_WriteString (sizebuf_t *sb, const char *s); void MSG_WriteCoord (sizebuf_t *sb, float f); void MSG_WriteAngle (sizebuf_t *sb, float f); #if defined(H2W) struct usercmd_s; void MSG_WriteAngle16 (sizebuf_t *sb, float f); void MSG_WriteUsercmd (sizebuf_t *sb, const struct usercmd_s *cmd, qboolean long_msg); #endif /* H2W */ void MSG_BeginReading (void); int MSG_ReadChar (void); int MSG_ReadByte (void); int MSG_ReadShort (void); int MSG_ReadLong (void); float MSG_ReadFloat (void); const char *MSG_ReadString (void); #if defined(H2W) const char *MSG_ReadStringLine (void); #endif /* H2W */ float MSG_ReadCoord (void); float MSG_ReadAngle (void); #if defined(H2W) float MSG_ReadAngle16 (void); void MSG_ReadUsercmd (struct usercmd_s *cmd, qboolean long_msg); #endif /* H2W*/ extern int msg_readcount; extern qboolean msg_badread; /* set if a read goes beyond end of message */ #endif /* __MSGIO_H */ engine/h2shared/no_dxe.c000066400000000000000000000000341444734033100154430ustar00rootroot00000000000000void Sys_InitDXE3(void) { } engine/h2shared/pr_cmds.c000066400000000000000000002406451444734033100156340ustar00rootroot00000000000000/* pr_cmds.c -- prog commands * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #ifdef PLATFORM_WINDOWS #include #endif #ifdef PLATFORM_UNIX #include #include #endif #if defined(H2W) && !defined(SERVERONLY) #error SERVERONLY not defined for HW server #endif #if !defined(H2W) #define SV_MAXCLIENTS svs.maxclients #define SV_NETMSG(_c) (_c)->message #define SV_CHECKCLTIME 0.1 #define SV_CL_OK(_c) (_c)->active || (_c)->spawned #else #define SV_MAXCLIENTS MAX_CLIENTS #define SV_NETMSG(_c) (_c)->netchan.message #define SV_MAXSOUNDS MAX_SOUNDS #define SV_CHECKCLTIME HX_FRAME_TIME #define SV_CL_OK(_c) (_c)->state == cs_spawned #endif #define STRINGTEMP_BUFFERS 16 #define STRINGTEMP_LENGTH 1024 static char pr_string_temp[STRINGTEMP_BUFFERS][STRINGTEMP_LENGTH]; static byte pr_string_tempindex = 0; static char *PR_GetTempString (void) { return pr_string_temp[(STRINGTEMP_BUFFERS-1) & ++pr_string_tempindex]; } #define RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e)) #define RETURN_STRING(s) (((int *)pr_globals)[OFS_RETURN] = PR_SetEngineString(s)) #define MSG_BROADCAST 0 // unreliable to all #define MSG_ONE 1 // reliable to one (msg_entity) #define MSG_ALL 2 // reliable to all #define MSG_INIT 3 // write to the init string #if defined(H2W) #define MSG_MULTICAST 4 // for multicast() #endif #if defined(SERVERONLY) static int d_lightstylevalue[256]; #else extern int d_lightstylevalue[256]; #endif static sizebuf_t *WriteDest (void); /* =============================================================================== BUILT-IN FUNCTIONS =============================================================================== */ static char *PF_VarString (int first) { int i; static char out[256]; out[0] = 0; for (i = first; i < pr_argc; i++) { if ( q_strlcat(out, G_STRING((OFS_PARM0+i*3)), sizeof(out)) >= sizeof(out) ) { Con_Printf("%s: overflow (string truncated)\n", __thisfunc__); break; } } return out; } /* ================= PF_error This is a TERMINAL error, which will kill off the entire server. Dumps self. error(value) ================= */ static void PF_error (void) { char *s; edict_t *ed; s = PF_VarString(0); Con_Printf ("======SERVER ERROR in %s:\n%s\n", PR_GetString(pr_xfunction->s_name), s); ed = PROG_TO_EDICT(*sv_globals.self); ED_Print (ed); Host_Error ("Program error"); } /* ================= PF_objerror Dumps out self, then an error message. The program is aborted and self is removed, but the level can continue. objerror(value) ================= */ static void PF_objerror (void) { char *s; edict_t *ed; s = PF_VarString(0); Con_Printf ("======OBJECT ERROR in %s:\n%s\n", PR_GetString(pr_xfunction->s_name), s); ed = PROG_TO_EDICT(*sv_globals.self); ED_Print (ed); ED_Free (ed); Host_Error ("Program error"); } /* ============== PF_makevectors Writes new values for v_forward, v_up, and v_right based on angles makevectors(vector) ============== */ static void PF_makevectors (void) { AngleVectors (G_VECTOR(OFS_PARM0), *sv_globals.v_forward, *sv_globals.v_right, *sv_globals.v_up); } /* ================= PF_setorigin This is the only valid way to move an object without using the physics of the world (setting velocity and waiting). Directly changing origin will not set internal links correctly, so clipping would be messed up. This should be called when an object is spawned, and then only if it is teleported. setorigin (entity, origin) ================= */ static void PF_setorigin (void) { edict_t *e; float *org; e = G_EDICT(OFS_PARM0); org = G_VECTOR(OFS_PARM1); VectorCopy (org, e->v.origin); SV_LinkEdict (e, false); } static void SetMinMaxSize (edict_t *e, float *minvec, float *maxvec, qboolean rotate) { float *angles; vec3_t rmin, rmax; float bounds[2][3]; float xvector[2], yvector[2]; float a; vec3_t base, transformed; int i, j, k, l; for (i = 0; i < 3; i++) { if (minvec[i] > maxvec[i]) PR_RunError ("backwards mins/maxs"); } rotate = false; // FIXME: implement rotation properly again if (!rotate) { VectorCopy (minvec, rmin); VectorCopy (maxvec, rmax); } else { // find min / max for rotations angles = e->v.angles; a = angles[1]/180 * M_PI; xvector[0] = cos(a); xvector[1] = sin(a); yvector[0] = -sin(a); yvector[1] = cos(a); VectorCopy (minvec, bounds[0]); VectorCopy (maxvec, bounds[1]); rmin[0] = rmin[1] = rmin[2] = 9999999; /* FIXME: change these two to FLT_MAX/-FLT_MAX */ rmax[0] = rmax[1] = rmax[2] = -9999999; for (i = 0; i <= 1; i++) { base[0] = bounds[i][0]; for (j = 0; j <= 1; j++) { base[1] = bounds[j][1]; for (k = 0; k <= 1; k++) { base[2] = bounds[k][2]; // transform the point transformed[0] = xvector[0]*base[0] + yvector[0]*base[1]; transformed[1] = xvector[1]*base[0] + yvector[1]*base[1]; transformed[2] = base[2]; for (l = 0; l < 3; l++) { if (transformed[l] < rmin[l]) rmin[l] = transformed[l]; if (transformed[l] > rmax[l]) rmax[l] = transformed[l]; } } } } } // set derived values VectorCopy (rmin, e->v.mins); VectorCopy (rmax, e->v.maxs); VectorSubtract (maxvec, minvec, e->v.size); SV_LinkEdict (e, false); } /* ================= PF_setsize the size box is rotated by the current angle setsize (entity, minvector, maxvector) ================= */ static void PF_setsize (void) { edict_t *e; float *minvec, *maxvec; e = G_EDICT(OFS_PARM0); minvec = G_VECTOR(OFS_PARM1); maxvec = G_VECTOR(OFS_PARM2); SetMinMaxSize (e, minvec, maxvec, false); } /* ================= PF_setmodel setmodel(entity, model) ================= */ static void PF_setmodel (void) { int i; const char *m, **check; qmodel_t *mod; edict_t *e; e = G_EDICT(OFS_PARM0); m = G_STRING(OFS_PARM1); // check to see if model was properly precached for (i = 0, check = sv.model_precache; i < MAX_MODELS && *check; i++, check++) { if (!strcmp(*check, m)) break; } if (i >= MAX_MODELS || !*check) { PR_RunError ("no precache: %s", m); } else { e->v.model = PR_SetEngineString(*check); e->v.modelindex = i; //SV_ModelIndex (m); mod = sv.models[ (int)e->v.modelindex]; // Mod_ForName (m, true); if (mod) SetMinMaxSize (e, mod->mins, mod->maxs, true); else SetMinMaxSize (e, vec3_origin, vec3_origin, true); } } static void PF_setpuzzlemodel (void) { int i; const char *m, **check; qmodel_t *mod; edict_t *e; e = G_EDICT(OFS_PARM0); m = G_STRING(OFS_PARM1); m = va ("models/puzzle/%s.mdl", m); // check to see if model was properly precached for (i = 0, check = sv.model_precache; i < MAX_MODELS && *check; i++, check++) { if (!strcmp(*check, m)) break; } if (i >= MAX_MODELS) { PR_RunError ("%s: overflow", __thisfunc__); } else { if (!*check) { Con_Printf("NO PRECACHE FOR PUZZLE PIECE: %s\n", m); m = (const char *)Hunk_Strdup(m, "puzzlemodel"); sv.model_precache[i] = m; e->v.model = PR_SetEngineString(m); #ifndef SERVERONLY sv.models[i] = Mod_ForName (m, true); #endif } else { e->v.model = PR_SetEngineString(*check); } e->v.modelindex = i; //SV_ModelIndex (m); mod = sv.models[ (int)e->v.modelindex]; // Mod_ForName (m, true); if (mod) SetMinMaxSize (e, mod->mins, mod->maxs, true); else SetMinMaxSize (e, vec3_origin, vec3_origin, true); } } /* ================= PF_bprint broadcast print to everyone on server bprint(value) ================= */ #ifndef H2W static void PF_bprint (void) { char *s; s = PF_VarString(0); SV_BroadcastPrintf ("%s", s); } #else static void PF_bprint (void) { char *s; int level; level = G_FLOAT(OFS_PARM0); s = PF_VarString(1); if (spartanPrint.integer && level < 2) return; SV_BroadcastPrintf (level, "%s", s); } #endif /* ================= PF_sprint single print to a specific client sprint(clientent, value) ================= */ #ifndef H2W static void PF_sprint (void) { char *s; client_t *client; int entnum; entnum = G_EDICTNUM(OFS_PARM0); s = PF_VarString(1); if (entnum < 1 || entnum > SV_MAXCLIENTS) { Con_Printf ("tried to sprint to a non-client\n"); return; } client = &svs.clients[entnum-1]; MSG_WriteChar (&SV_NETMSG(client),svc_print); MSG_WriteString (&SV_NETMSG(client), s); } #else static void PF_sprint (void) { char *s; client_t *client; int entnum; int level; entnum = G_EDICTNUM(OFS_PARM0); level = G_FLOAT(OFS_PARM1); s = PF_VarString(2); if (entnum < 1 || entnum > SV_MAXCLIENTS) { Con_Printf ("tried to sprint to a non-client\n"); return; } client = &svs.clients[entnum-1]; if (spartanPrint.integer && level < 2) return; SV_ClientPrintf (client, level, "%s", s); } /* ================= PF_name_print print player's name name_print(to, level, who) ================= */ static void PF_name_print (void) { int idx, style; idx = (int) G_EDICTNUM(OFS_PARM2); style = (int) G_FLOAT(OFS_PARM1); if (spartanPrint.integer && style < 2) return; if (idx < 1 || idx > SV_MAXCLIENTS) PR_RunError ("%s: unexpected index %d", __thisfunc__, idx); if ((int)G_FLOAT(OFS_PARM0) == MSG_BROADCAST) {/* broadcast message--send like bprint, print it out on server too. */ client_t *cl; int i; Sys_Printf("%s", svs.clients[idx - 1].name); for (i = 0, cl = svs.clients; i < SV_MAXCLIENTS; i++, cl++) { if (style < cl->messagelevel) continue; if (cl->state != cs_spawned) {//should i be checking cs_connected too? if (cl->state) {//not fully in so won't know name yet, explicitly say the name MSG_WriteByte (&SV_NETMSG(cl), svc_print); MSG_WriteByte (&SV_NETMSG(cl), style); MSG_WriteString (&SV_NETMSG(cl), svs.clients[idx - 1].name); } continue; } MSG_WriteByte (&SV_NETMSG(cl), svc_name_print); MSG_WriteByte (&SV_NETMSG(cl), style); //knows the name, send the index. MSG_WriteByte (&SV_NETMSG(cl), idx - 1); } return; } MSG_WriteByte (WriteDest(), svc_name_print); MSG_WriteByte (WriteDest(), style); MSG_WriteByte (WriteDest(), idx - 1);//heh, don't need a short here. } /* ================= PF_print_indexed print string from strings.txt print_indexed(to, level, index) ================= */ static void PF_print_indexed (void) { int idx, style; idx = (int) G_FLOAT(OFS_PARM2); style = (int) G_FLOAT(OFS_PARM1); if (spartanPrint.integer && style < 2) return; if (idx < 1 || idx > host_string_count) { PR_RunError ("%s: unexpected index %d (host_string_count: %d)", __thisfunc__, idx, host_string_count); } if ((int)G_FLOAT(OFS_PARM0) == MSG_BROADCAST) {/* broadcast message--send like bprint, print it out on server too. */ client_t *cl; int i; Sys_Printf("%s", Host_GetString(idx - 1)); for (i = 0, cl = svs.clients; i < SV_MAXCLIENTS; i++, cl++) { if (style < cl->messagelevel) continue; if (!cl->state) continue; MSG_WriteByte (&SV_NETMSG(cl), svc_indexed_print); MSG_WriteByte (&SV_NETMSG(cl), style); MSG_WriteShort (&SV_NETMSG(cl), idx); } return; } MSG_WriteByte (WriteDest(), svc_indexed_print); MSG_WriteByte (WriteDest(), style); MSG_WriteShort (WriteDest(), idx); } #endif /* H2W */ /* ================= PF_centerprint single print to a specific client centerprint(clientent, value) ================= */ static void PF_centerprint (void) { char *s; client_t *client; int entnum; entnum = G_EDICTNUM(OFS_PARM0); s = PF_VarString(1); if (entnum < 1 || entnum > SV_MAXCLIENTS) { Con_Printf ("tried to sprint to a non-client\n"); return; } client = &svs.clients[entnum-1]; MSG_WriteChar (&SV_NETMSG(client),svc_centerprint); MSG_WriteString (&SV_NETMSG(client), s); } #if defined(H2W) /* ================= PF_bcenterprint2 single print to all bcenterprint2(value, value) ================= */ static void PF_bcenterprint2 (void) { char *s; client_t *cl; int i; s = PF_VarString(0); for (i = 0, cl = svs.clients; i < SV_MAXCLIENTS; i++, cl++) { if (!cl->state) continue; MSG_WriteByte (&SV_NETMSG(cl), svc_centerprint); MSG_WriteString (&SV_NETMSG(cl), s); } } /* ================= PF_centerprint2 single print to a specific client centerprint(clientent, value, value) ================= */ static void PF_centerprint2 (void) { char *s; client_t *client; int entnum; entnum = G_EDICTNUM(OFS_PARM0); s = PF_VarString(1); if (entnum < 1 || entnum > SV_MAXCLIENTS) { Con_Printf ("tried to sprint to a non-client\n"); return; } client = &svs.clients[entnum-1]; MSG_WriteChar (&SV_NETMSG(client),svc_centerprint); MSG_WriteString (&SV_NETMSG(client), s); } #endif /* H2W */ /* ================= PF_normalize vector normalize(vector) ================= */ static void PF_normalize (void) { float *value1; vec3_t newvalue; double new_temp; value1 = G_VECTOR(OFS_PARM0); /* using double here so that 64 bit/sse2 builds' precision matches that * of x87 floating point. from QuakeSpasm, patch by Eric Wasylishen. */ new_temp = VectorLengthDBL(value1); if (new_temp == 0) newvalue[0] = newvalue[1] = newvalue[2] = 0; else { new_temp = 1 / new_temp; newvalue[0] = value1[0] * new_temp; newvalue[1] = value1[1] * new_temp; newvalue[2] = value1[2] * new_temp; } VectorCopy (newvalue, G_VECTOR(OFS_RETURN)); } /* ================= PF_vlen scalar vlen(vector) ================= */ static void PF_vlen (void) { float *value1; double new_temp; value1 = G_VECTOR(OFS_PARM0); /* using double here so that 64 bit/sse2 builds' precision matches that * of x87 floating point. from QuakeSpasm, patch by Eric Wasylishen. */ new_temp = VectorLengthDBL(value1); G_FLOAT(OFS_RETURN) = new_temp; } /* ================= PF_vhlen scalar vhlen(vector) ================= */ static void PF_vhlen (void) { float *value1; double new_temp; value1 = G_VECTOR(OFS_PARM0); /* using double here so that 64 bit/sse2 builds' precision matches that * of x87 floating point. from QuakeSpasm, patch by Eric Wasylishen. */ new_temp = (double)value1[0] * (double)value1[0] + (double)value1[1] * (double)value1[1]; new_temp = sqrt(new_temp); G_FLOAT(OFS_RETURN) = new_temp; } /* ================= PF_vectoyaw float vectoyaw(vector) ================= */ static void PF_vectoyaw (void) { float *value1; float yaw; value1 = G_VECTOR(OFS_PARM0); if (value1[1] == 0 && value1[0] == 0) yaw = 0; else { yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; } G_FLOAT(OFS_RETURN) = yaw; } /* ================= PF_vectoangles vector vectoangles(vector) ================= */ static void PF_vectoangles (void) { float *value1; float forward; float yaw, pitch; value1 = G_VECTOR(OFS_PARM0); if (value1[1] == 0 && value1[0] == 0) { yaw = 0; if (value1[2] > 0) pitch = 90; else pitch = 270; } else { yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]); pitch = (int) (atan2(value1[2], forward) * 180 / M_PI); if (pitch < 0) pitch += 360; } G_FLOAT(OFS_RETURN+0) = pitch; G_FLOAT(OFS_RETURN+1) = yaw; G_FLOAT(OFS_RETURN+2) = 0; } /* ================= PF_Random Returns a number from 0 <= num < 1 random() ================= */ static void PF_random (void) { float num; num = rand() * (1.0 / RAND_MAX); G_FLOAT(OFS_RETURN) = num; } /* ================= PF_particle particle(origin, color, count) ================= */ static void PF_particle (void) { float *org, *dir; float color; float count; org = G_VECTOR(OFS_PARM0); dir = G_VECTOR(OFS_PARM1); color = G_FLOAT(OFS_PARM2); count = G_FLOAT(OFS_PARM3); SV_StartParticle (org, dir, color, count); } /* ================= PF_particle2 particle(origin, dmin, dmax, color, effect, count) ================= */ static void PF_particle2 (void) { float *org, *dmin, *dmax; float color; float count; float effect; org = G_VECTOR(OFS_PARM0); dmin = G_VECTOR(OFS_PARM1); dmax = G_VECTOR(OFS_PARM2); color = G_FLOAT(OFS_PARM3); effect = G_FLOAT(OFS_PARM4); count = G_FLOAT(OFS_PARM5); SV_StartParticle2 (org, dmin, dmax, color, effect, count); } /* ================= PF_particle3 particle(origin, box, color, effect, count) ================= */ static void PF_particle3 (void) { float *org, *box; float color; float count; float effect; org = G_VECTOR(OFS_PARM0); box = G_VECTOR(OFS_PARM1); color = G_FLOAT(OFS_PARM2); effect = G_FLOAT(OFS_PARM3); count = G_FLOAT(OFS_PARM4); SV_StartParticle3 (org, box, color, effect, count); } /* ================= PF_particle4 particle(origin, radius, color, effect, count) ================= */ static void PF_particle4 (void) { float *org; float radius; float color; float count; float effect; org = G_VECTOR(OFS_PARM0); radius = G_FLOAT(OFS_PARM1); color = G_FLOAT(OFS_PARM2); effect = G_FLOAT(OFS_PARM3); count = G_FLOAT(OFS_PARM4); SV_StartParticle4 (org, radius, color, effect, count); } /* ================= PF_ambientsound ================= */ static void PF_ambientsound (void) { const char *samp, **check; float *pos; float vol, attenuation; int i, soundnum; #ifndef H2W /* just to be on the safe side */ const int SV_MAXSOUNDS = (sv_protocol == PROTOCOL_RAVEN_111) ? MAX_SOUNDS_OLD : MAX_SOUNDS; #endif pos = G_VECTOR (OFS_PARM0); samp = G_STRING(OFS_PARM1); vol = G_FLOAT(OFS_PARM2); attenuation = G_FLOAT(OFS_PARM3); // check to see if samp was properly precached for (soundnum = 0, check = sv.sound_precache; soundnum < SV_MAXSOUNDS && *check; soundnum++, check++) { if (!strcmp(*check, samp)) break; } if (soundnum == SV_MAXSOUNDS || !*check) { Con_Printf ("no precache: %s\n", samp); return; } // add an svc_spawnambient command to the level signon packet MSG_WriteByte (&sv.signon,svc_spawnstaticsound); for (i = 0; i < 3; i++) MSG_WriteCoord(&sv.signon, pos[i]); #ifndef H2W if (sv_protocol == PROTOCOL_RAVEN_111) MSG_WriteByte (&sv.signon, soundnum); else MSG_WriteShort(&sv.signon, soundnum); #else MSG_WriteByte (&sv.signon, soundnum); #endif MSG_WriteByte (&sv.signon, vol*255); MSG_WriteByte (&sv.signon, attenuation*64); } /* ================= PF_StopSound stop ent's sound on this chan ================= */ static void PF_StopSound(void) { int channel; edict_t *entity; entity = G_EDICT(OFS_PARM0); channel = G_FLOAT(OFS_PARM1); if (channel < 0 || channel > 7) Host_Error ("%s: channel = %i", __thisfunc__, channel); SV_StopSound (entity, channel); } /* ================= PF_UpdateSoundPos sends cur pos to client to update this ent/chan pair ================= */ static void PF_UpdateSoundPos(void) { int channel; edict_t *entity; entity = G_EDICT(OFS_PARM0); channel = G_FLOAT(OFS_PARM1); if (channel < 0 || channel > 7) Host_Error ("%s: channel = %i", __thisfunc__, channel); SV_UpdateSoundPos (entity, channel); } /* ================= PF_sound Each entity can have eight independant sound sources, like voice, weapon, feet, etc. Channel 0 is an auto-allocate channel, the others override anything already running on that entity/channel pair. An attenuation of 0 will play full volume everywhere in the level. Larger attenuations will drop off. ================= */ static void PF_sound (void) { const char *sample; int channel; edict_t *entity; int volume; float attenuation; entity = G_EDICT(OFS_PARM0); channel = G_FLOAT(OFS_PARM1); sample = G_STRING(OFS_PARM2); volume = G_FLOAT(OFS_PARM3) * 255; attenuation = G_FLOAT(OFS_PARM4); SV_StartSound (entity, channel, sample, volume, attenuation); } /* ================= PF_break break() ================= */ static void PF_break (void) { static qboolean done = false; if (!done) { done = true; Con_Printf ("break statement\n"); #if defined(PLATFORM_WINDOWS) DebugBreak(); #elif defined(PLATFORM_UNIX) kill(getpid(), SIGKILL); #else *(int *)-4 = 0; // dump to debugger #endif } // PR_RunError ("break statement"); } static void PR_SetTrace (trace_t *trace) { *sv_globals.trace_allsolid = trace->allsolid; *sv_globals.trace_startsolid = trace->startsolid; *sv_globals.trace_fraction = trace->fraction; *sv_globals.trace_inwater = trace->inwater; *sv_globals.trace_inopen = trace->inopen; VectorCopy (trace->endpos, *sv_globals.trace_endpos); VectorCopy (trace->plane.normal, *sv_globals.trace_plane_normal); *sv_globals.trace_plane_dist = trace->plane.dist; if (trace->ent) *sv_globals.trace_ent = EDICT_TO_PROG(trace->ent); else *sv_globals.trace_ent = EDICT_TO_PROG(sv.edicts); } /* ================= PF_traceline Used for use tracing and shot targeting Traces are blocked by bbox and exact bsp entityes, and also slide box entities if the tryents flag is set. traceline (vector1, vector2, tryents) ================= */ static void PF_traceline (void) { float *v1, *v2; trace_t trace; int nomonsters; edict_t *ent; float save_hull; v1 = G_VECTOR(OFS_PARM0); v2 = G_VECTOR(OFS_PARM1); nomonsters = G_FLOAT(OFS_PARM2); ent = G_EDICT(OFS_PARM3); save_hull = ent->v.hull; ent->v.hull = 0; trace = SV_Move (v1, vec3_origin, vec3_origin, v2, nomonsters, ent); ent->v.hull = save_hull; PR_SetTrace (&trace); } #ifdef QUAKE2 extern trace_t SV_Trace_Toss (edict_t *ent, edict_t *ignore); static void PF_TraceToss (void) { trace_t trace; edict_t *ent; edict_t *ignore; ent = G_EDICT(OFS_PARM0); ignore = G_EDICT(OFS_PARM1); trace = SV_Trace_Toss (ent, ignore); PR_SetTrace (&trace); } #endif /* QUAKE2 */ /* ================= PF_tracearea Used for use tracing and shot targeting Traces are blocked by bbox and exact bsp entityes, and also slide box entities if the tryents flag is set. tracearea (vector1, vector2, mins, maxs, tryents) ================= */ static void PF_tracearea (void) { float *v1, *v2, *mins, *maxs; trace_t trace; int nomonsters; edict_t *ent; float save_hull; v1 = G_VECTOR(OFS_PARM0); v2 = G_VECTOR(OFS_PARM1); mins = G_VECTOR(OFS_PARM2); maxs = G_VECTOR(OFS_PARM3); nomonsters = G_FLOAT(OFS_PARM4); ent = G_EDICT(OFS_PARM5); save_hull = ent->v.hull; ent->v.hull = 0; trace = SV_Move (v1, mins, maxs, v2, nomonsters, ent); ent->v.hull = save_hull; PR_SetTrace (&trace); } #if 0 /* not used */ struct PointInfo_t { char Found, NumFound, MarkedWhen; struct PointInfo_t *FromPos, *Next; }; #define MAX_POINT_X 21 #define MAX_POINT_Y 21 #define MAX_POINT_Z 11 #define MAX_POINT (MAX_POINT_X * MAX_POINT_Y * MAX_POINT_Z) struct PointInfo_t PI[MAX_POINT]; extern particle_t *active_particles, *free_particles; static int ZOffset, YOffset; #define POINT_POS(x,y,z) ((z*ZOffset)+(y*YOffset)+(x)) #define POINT_X_SIZE 160 #define POINT_Y_SIZE 160 #define POINT_Z_SIZE 50 #define POINT_MAX_DEPTH 5 static void AddParticle (float *Org, float color) { particle_t *p; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->die = 99999; p->color = color; p->type = pt_static; VectorClear (p->vel); VectorCopy (Org, p->org); } static void FindPath (float *StartV, float *EndV, float *Mins, float *Maxs, int NoMonsters, edict_t *Ent) { vec3_t NewStartV, NewEndV; int XSize, YSize, ZSize; int x, y, z, c, c2, xs, ys, zs, nx, ny, nz, XPos, YPos, ZPos; int StartX, StartY, StartZ, DiffX, DiffY, DiffZ, Diff, OrigDiff; int NumTraces, NumTracesEach, NumMarks; float TracePercent, PercentEach; struct PointInfo_t *Pos,*EndPos;//,*ToDo[POINT_MAX_DEPTH]; int test; trace_t trace; XSize = 11; YSize = 11; ZSize = 11; NumTraces = 0; NumMarks = 0; ZOffset = XSize*YSize; YOffset = XSize; // for (c = 0; c < POINT_MAX_DEPTH; c++) // ToDo[c] = NULL; for (c = 0, Pos = PI; c < MAX_POINT; c++, Pos++) { Pos->Found = Pos->NumFound = Pos->MarkedWhen = 0; Pos->FromPos = Pos->Next = NULL; } StartX = (XSize) / 2; StartY = (YSize) / 2; StartZ = (ZSize) / 2; // ToDo[0] = &PI[POINT_POS(StartX,StartY,StartZ)]; // ToDo[0]->Found = 1; PI[POINT_POS(StartX,StartY,StartZ)].Found = 1; test = 0; for (c = 1; c <= 5; c++) { NumMarks = NumTracesEach = 0; for (z = 0, Pos = PI; z < ZSize; z++) { for (y = 0; y < YSize; y++) { for (x = 0; x < XSize; x++, Pos++) { if (Pos->Found == c) { for (zs = -1; zs <= 1; zs++) { switch (zs) { case -1: ZPos = 0; if (z == ZPos) continue; break; case 0: ZPos = z; break; case 1: ZPos = ZSize-1; if (z == ZPos) continue; break; } for (ys = -1; ys <= 1; ys++) { switch (ys) { case -1: YPos = 0; if (y == YPos) continue; break; case 0: YPos = y; break; case 1: YPos = YSize-1; if (y == YPos) continue; break; } for (xs = -1; xs <= 1; xs++) { if (zs || ys || xs) { switch (xs) { case -1: XPos = 0; if (x == XPos) continue; break; case 0: XPos = x; break; case 1: XPos = XSize-1; if (x == XPos) continue; break; } if (XPos == x && YPos == y && ZPos == z) continue; test++; DiffX = abs(x - XPos); DiffY = abs(y - YPos); DiffZ = abs(z - ZPos); Diff = 999; if (DiffX && DiffX < Diff) Diff = DiffX; if (DiffY && DiffY < Diff) Diff = DiffY; if (DiffZ && DiffZ < Diff) Diff = DiffZ; if (Diff == 999) continue; OrigDiff = Diff; nx = x; ny = y; nz = z; Diff = 0; do { Diff++; nx += xs; ny += ys; nz += zs; EndPos = &PI[POINT_POS(nx,ny,nz)]; // if (EndPos < PI || EndPos >= &PI[MAX_POINT]) { // Diff = 0; // Con_Printf("ERROR2\n"); // break; // } } while (!EndPos->Found && Diff != OrigDiff); if (Diff != OrigDiff || EndPos->Found) { nx -= xs; ny -= ys; nz -= zs; Diff--; } if (!Diff) { continue; } DiffX = x - StartX; DiffY = y - StartY; DiffZ = z - StartZ; NewStartV[0] = StartV[0] + (DiffX * POINT_X_SIZE); NewStartV[1] = StartV[1] + (DiffY * POINT_Y_SIZE); NewStartV[2] = StartV[2] + (DiffZ * POINT_Z_SIZE); DiffX = nx - x; DiffY = ny - y; DiffZ = nz - z; NewEndV[0] = NewStartV[0] + (DiffX * POINT_X_SIZE); NewEndV[1] = NewStartV[1] + (DiffY * POINT_Y_SIZE); NewEndV[2] = NewStartV[2] + (DiffZ * POINT_Z_SIZE); NumTraces++; NumTracesEach++; trace = SV_Move (NewStartV, Mins, Maxs, NewEndV, NoMonsters, Ent); // trace = SV_Move (NewStartV,vec3_origin, vec3_origin, NewEndV, NoMonsters, Ent); TracePercent = trace.fraction; PercentEach = 1 / (float)Diff; // OrigStartV[0] = NewStartV[0]; // OrigStartV[1] = NewStartV[1]; // OrigStartV[2] = NewStartV[2]; nz = z; ny = y; nx = x; c2 = Pos->Found; while (TracePercent >= PercentEach) { nz += zs; ny += ys; nx += xs; // NewStartV[0] += xs*POINT_X_SIZE; // NewStartV[1] += ys*POINT_Y_SIZE; // NewStartV[2] += zs*POINT_Z_SIZE; // AddParticle(NewStartV,(test == 2062 ? 255 : 252)); c2++; EndPos = &PI[POINT_POS(nx,ny,nz)]; // if (EndPos < PI || EndPos >= &PI[MAX_POINT]) // { // Con_Printf("ERROR %d %d\n",OrigDiff,test); // break; // } if (EndPos->Found && EndPos->Found <= c2+1) break; EndPos->Found = c2; EndPos->MarkedWhen = Pos->Found; EndPos->FromPos = Pos; NumMarks++; TracePercent -= PercentEach; } } } } } } } } } // Con_Printf("NumMarks: %d NumTraces: %d\n",NumMarks,NumTracesEach); if (!NumMarks) break; } // Con_Printf("\n\n%d traces\n",NumTraces); } static void PF_FindPath(void) { float *v1, *v2, *mins, *maxs; int nomonsters; edict_t *ent; double b; v1 = G_VECTOR(OFS_PARM0); v2 = G_VECTOR(OFS_PARM1); mins = G_VECTOR(OFS_PARM2); maxs = G_VECTOR(OFS_PARM3); nomonsters = G_FLOAT(OFS_PARM4); ent = G_EDICT(OFS_PARM5); b = Sys_DoubleTime (); FindPath(v1, v2, mins, maxs, nomonsters,ent); Con_Printf("Time is %10.4f\n", Sys_DoubleTime() - b); } /* ================= PF_checkpos Returns true if the given entity can move to the given position from it's current position by walking or rolling. FIXME: make work... scalar checkpos (entity, vector) ================= */ static void PF_checkpos (void) { } #endif /* not used */ //============================================================================ static byte checkpvs[MAX_MAP_LEAFS/8]; static int PF_newcheckclient (int check) { int i; byte *pvs; edict_t *ent; mleaf_t *leaf; vec3_t org; // cycle to the next one if (check < 1) check = 1; if (check > SV_MAXCLIENTS) check = SV_MAXCLIENTS; if (check == SV_MAXCLIENTS) i = 1; else i = check + 1; for ( ; ; i++) { if (i == SV_MAXCLIENTS+1) i = 1; ent = EDICT_NUM(i); if (i == check) break; // didn't find anything else if (ent->free) continue; if (ent->v.health <= 0) continue; if ((int)ent->v.flags & FL_NOTARGET) continue; // anything that is a client, or has a client as an enemy break; } // get the PVS for the entity VectorAdd (ent->v.origin, ent->v.view_ofs, org); leaf = Mod_PointInLeaf (org, sv.worldmodel); pvs = Mod_LeafPVS (leaf, sv.worldmodel); memcpy (checkpvs, pvs, (sv.worldmodel->numleafs+7)>>3 ); return i; } /* ================= PF_checkclient Returns a client (or object that has a client enemy) that would be a valid target. If there are more than one valid options, they are cycled each frame If (self.origin + self.viewofs) is not in the PVS of the current target, it is not returned at all. name checkclient () ================= */ #define MAX_CHECK 16 static int c_invis, c_notvis; static void PF_checkclient (void) { edict_t *ent, *self; mleaf_t *leaf; int l; vec3_t view; // find a new check if on a new frame if (sv.time - sv.lastchecktime >= SV_CHECKCLTIME) { sv.lastcheck = PF_newcheckclient (sv.lastcheck); sv.lastchecktime = sv.time; } // return check if it might be visible ent = EDICT_NUM(sv.lastcheck); if (ent->free || ent->v.health <= 0) { RETURN_EDICT(sv.edicts); return; } // if current entity can't possibly see the check entity, return 0 self = PROG_TO_EDICT(*sv_globals.self); VectorAdd (self->v.origin, self->v.view_ofs, view); leaf = Mod_PointInLeaf (view, sv.worldmodel); l = (leaf - sv.worldmodel->leafs) - 1; if ( (l < 0) || !(checkpvs[l>>3] & (1 << (l & 7))) ) { c_notvis++; RETURN_EDICT(sv.edicts); return; } // might be able to see it c_invis++; RETURN_EDICT(ent); } //============================================================================ /* ================= PF_stuffcmd Sends text over to the client's execution buffer stuffcmd (clientent, value) ================= */ static void PF_stuffcmd (void) { int entnum; const char *str; client_t *old; entnum = G_EDICTNUM(OFS_PARM0); if (entnum < 1 || entnum > SV_MAXCLIENTS) PR_RunError ("Parm 0 not a client"); str = G_STRING(OFS_PARM1); old = host_client; host_client = &svs.clients[entnum-1]; #ifndef H2W Host_ClientCommands ("%s", str); #else MSG_WriteByte (&SV_NETMSG(host_client), svc_stufftext); MSG_WriteString (&SV_NETMSG(host_client), str); #endif host_client = old; } /* ================= PF_localcmd Sends text over to the client's execution buffer localcmd (string) ================= */ static void PF_localcmd (void) { const char *str; str = G_STRING(OFS_PARM0); Cbuf_AddText (str); } /* ================= PF_cvar float cvar (string) ================= */ static void PF_cvar (void) { const char *str; str = G_STRING(OFS_PARM0); G_FLOAT(OFS_RETURN) = Cvar_VariableValue (str); } /* ================= PF_cvar_set float cvar (string) ================= */ static void PF_cvar_set (void) { const char *var, *val; var = G_STRING(OFS_PARM0); val = G_STRING(OFS_PARM1); Cvar_Set (var, val); } /* ================= PF_findradius Returns a chain of entities that have origins within a spherical area findradius (origin, radius) ================= */ static void PF_findradius (void) { edict_t *ent, *chain; float rad; float *org; int i; chain = (edict_t *)sv.edicts; org = G_VECTOR(OFS_PARM0); rad = G_FLOAT(OFS_PARM1); rad *= rad; ent = NEXT_EDICT(sv.edicts); for (i = 1; i < sv.num_edicts; i++, ent = NEXT_EDICT(ent)) { float d, lensq; if (ent->free) continue; if (ent->v.solid == SOLID_NOT) continue; d = org[0] - (ent->v.origin[0] + (ent->v.mins[0] + ent->v.maxs[0]) * 0.5); lensq = d * d; if (lensq > rad) continue; d = org[1] - (ent->v.origin[1] + (ent->v.mins[1] + ent->v.maxs[1]) * 0.5); lensq += d * d; if (lensq > rad) continue; d = org[2] - (ent->v.origin[2] + (ent->v.mins[2] + ent->v.maxs[2]) * 0.5); lensq += d * d; if (lensq > rad) continue; ent->v.chain = EDICT_TO_PROG(chain); chain = ent; } RETURN_EDICT(chain); } /* ========= PF_dprint ========= */ static void PF_dprint (void) { Con_DPrintf ("%s",PF_VarString(0)); } static void PF_dprintf (void) { char temp[256]; float v; v = G_FLOAT(OFS_PARM1); if (v == (int)v) sprintf (temp, "%d",(int)v); else sprintf (temp, "%5.1f",v); Con_DPrintf (G_STRING(OFS_PARM0),temp); } static void PF_dprintv (void) { char temp[256]; sprintf (temp, "'%5.1f %5.1f %5.1f'", G_VECTOR(OFS_PARM1)[0], G_VECTOR(OFS_PARM1)[1], G_VECTOR(OFS_PARM1)[2]); Con_DPrintf (G_STRING(OFS_PARM0),temp); } static void PF_ftos (void) { float v; char *s; v = G_FLOAT(OFS_PARM0); s = PR_GetTempString(); if (v == (int)v) sprintf (s, "%d",(int)v); else sprintf (s, "%5.1f",v); G_INT(OFS_RETURN) = PR_SetEngineString(s); } static void PF_fabs (void) { float v; v = G_FLOAT(OFS_PARM0); G_FLOAT(OFS_RETURN) = fabs(v); } static void PF_vtos (void) { char *s; s = PR_GetTempString(); sprintf (s, "'%5.1f %5.1f %5.1f'", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]); G_INT(OFS_RETURN) = PR_SetEngineString(s); } #ifdef QUAKE2 static void PF_etos (void) { char *s; s = PR_GetTempString(); sprintf (s, "entity %i", G_EDICTNUM(OFS_PARM0)); G_INT(OFS_RETURN) = PR_SetEngineString(s); } #endif static void PF_Spawn (void) { edict_t *ed; ed = ED_Alloc(); RETURN_EDICT(ed); } static void PF_SpawnTemp (void) { edict_t *ed; ed = ED_Alloc_Temp(); RETURN_EDICT(ed); } static void PF_Remove (void) { edict_t *ed; int i; ed = G_EDICT(OFS_PARM0); if (ed == sv.edicts) { Con_DPrintf("Tried to remove the world at %s in %s!\n", PR_GetString(pr_xfunction->s_name), PR_GetString(pr_xfunction->s_file)); return; } i = NUM_FOR_EDICT(ed); if (i <= SV_MAXCLIENTS) { Con_DPrintf("Tried to remove a client at %s in %s!\n", PR_GetString(pr_xfunction->s_name), PR_GetString(pr_xfunction->s_file)); return; } ED_Free (ed); } // entity (entity start, .string field, string match) find = #5; static void PF_Find (void) #ifdef QUAKE2 { int e; int f; const char *s, *t; edict_t *ed; edict_t *first; edict_t *second; edict_t *last; first = second = last = (edict_t *)sv.edicts; e = G_EDICTNUM(OFS_PARM0); f = G_INT(OFS_PARM1); s = G_STRING(OFS_PARM2); if (!s) PR_RunError ("%s: bad search string", __thisfunc__); for (e++ ; e < sv.num_edicts ; e++) { ed = EDICT_NUM(e); if (ed->free) continue; t = E_STRING(ed,f); if (!t) continue; if (!strcmp(t,s)) { if (first == (edict_t *)sv.edicts) first = ed; else if (second == (edict_t *)sv.edicts) second = ed; ed->v.chain = EDICT_TO_PROG(last); last = ed; } } if (first != last) { if (last != second) first->v.chain = last->v.chain; else first->v.chain = EDICT_TO_PROG(last); last->v.chain = EDICT_TO_PROG((edict_t *)sv.edicts); if (second && second != last) second->v.chain = EDICT_TO_PROG(last); } RETURN_EDICT(first); } #else { int e; int f; const char *s, *t; edict_t *ed; e = G_EDICTNUM(OFS_PARM0); f = G_INT(OFS_PARM1); s = G_STRING(OFS_PARM2); if (!s) PR_RunError ("%s: bad search string", __thisfunc__); for (e++ ; e < sv.num_edicts ; e++) { ed = EDICT_NUM(e); if (ed->free) continue; t = E_STRING(ed,f); if (!t) continue; if (!strcmp(t,s)) { RETURN_EDICT(ed); return; } } RETURN_EDICT(sv.edicts); } #endif /* QUAKE2 */ #if 0 static void PF_FindFloat (void) { int e; int f; float s, t; edict_t *ed; e = G_EDICTNUM(OFS_PARM0); f = G_INT(OFS_PARM1); s = G_FLOAT(OFS_PARM2); if (!s) PR_RunError ("%s: bad search string", __thisfunc__); for (e++ ; e < sv.num_edicts ; e++) { ed = EDICT_NUM(e); if (ed->free) continue; t = E_FLOAT(ed,f); if (t == s) { RETURN_EDICT(ed); return; } } RETURN_EDICT(sv.edicts); } #endif /* #if 0 */ static void PR_CheckEmptyString (const char *s) { if (s[0] <= ' ') PR_RunError ("Bad string"); } static void PF_precache_file (void) { // precache_file is only used to copy files with qcc, it does nothing G_INT(OFS_RETURN) = G_INT(OFS_PARM0); } static void PF_precache_sound (void) { const char *s; int i; #ifndef H2W /* just to be on the safe side */ const int SV_MAXSOUNDS = (sv_protocol == PROTOCOL_RAVEN_111) ? MAX_SOUNDS_OLD : MAX_SOUNDS; #endif if (sv.state != ss_loading && !ignore_precache) PR_RunError ("%s: Precache can only be done in spawn functions", __thisfunc__); s = G_STRING(OFS_PARM0); G_INT(OFS_RETURN) = G_INT(OFS_PARM0); PR_CheckEmptyString (s); for (i = 0; i < SV_MAXSOUNDS; i++) { if (!sv.sound_precache[i]) { sv.sound_precache[i] = s; return; } if (!strcmp(sv.sound_precache[i], s)) return; } PR_RunError ("%s: overflow", __thisfunc__); } static void PF_precache_sound2 (void) { if (!registered.integer) return; PF_precache_sound(); } static void PF_precache_sound3 (void) { if (!registered.integer && !oem.integer) return; PF_precache_sound(); } #ifndef H2W static void PF_precache_sound4 (void) {//mission pack only if (!registered.integer) return; PF_precache_sound(); } #endif static void PF_precache_model (void) { const char *s; int i; if (sv.state != ss_loading && !ignore_precache) PR_RunError ("%s: Precache can only be done in spawn functions", __thisfunc__); s = G_STRING(OFS_PARM0); G_INT(OFS_RETURN) = G_INT(OFS_PARM0); PR_CheckEmptyString (s); for (i = 0; i < MAX_MODELS; i++) { if (!sv.model_precache[i]) { sv.model_precache[i] = s; #ifndef SERVERONLY sv.models[i] = Mod_ForName (s, true); #endif return; } if (!strcmp(sv.model_precache[i], s)) return; /* duplicate precache */ } PR_RunError ("%s: overflow", __thisfunc__); } static void PF_precache_model2 (void) { if (!registered.integer) return; PF_precache_model(); } static void PF_precache_model3 (void) { if (!registered.integer && !oem.integer) return; PF_precache_model(); } #ifndef H2W static void PF_precache_model4 (void) { if (!registered.integer) return; PF_precache_model(); } #endif static void PF_precache_puzzle_model (void) { int i; const char *s, *temp; if (sv.state != ss_loading && !ignore_precache) PR_RunError ("%s: Precache can only be done in spawn functions", __thisfunc__); s = G_STRING(OFS_PARM0); G_INT(OFS_RETURN) = G_INT(OFS_PARM0); PR_CheckEmptyString (s); temp = va ("models/puzzle/%s.mdl", s); for (i = 0; i < MAX_MODELS; i++) { if (!sv.model_precache[i]) { s = (const char *)Hunk_Strdup(temp, "puzzlemodel"); sv.model_precache[i] = s; #ifndef SERVERONLY sv.models[i] = Mod_ForName (s, true); #endif return; } if (!strcmp(sv.model_precache[i], temp)) return; } PR_RunError ("%s: overflow", __thisfunc__); } static void PF_coredump (void) { ED_PrintEdicts (); } static void PF_traceon (void) { pr_trace = true; } static void PF_traceoff (void) { pr_trace = false; } static void PF_eprint (void) { ED_PrintNum (G_EDICTNUM(OFS_PARM0)); } /* =============== PF_walkmove float(float yaw, float dist) walkmove =============== */ static void PF_walkmove (void) { edict_t *ent; float yaw, dist; vec3_t move; dfunction_t *oldf; int oldself; qboolean set_trace; ent = PROG_TO_EDICT(*sv_globals.self); yaw = G_FLOAT(OFS_PARM0); dist = G_FLOAT(OFS_PARM1); set_trace = G_FLOAT(OFS_PARM2); if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) { G_FLOAT(OFS_RETURN) = 0; return; } yaw = yaw * M_PI * 2 / 360; move[0] = cos(yaw) * dist; move[1] = sin(yaw) * dist; move[2] = 0; // save program state, because SV_movestep may call other progs oldf = pr_xfunction; oldself = *sv_globals.self; G_FLOAT(OFS_RETURN) = SV_movestep(ent, move, true, true, set_trace); // restore program state pr_xfunction = oldf; *sv_globals.self = oldself; } /* =============== PF_droptofloor void() droptofloor =============== */ static void PF_droptofloor (void) { edict_t *ent; vec3_t end; trace_t trace; ent = PROG_TO_EDICT(*sv_globals.self); VectorCopy (ent->v.origin, end); end[2] -= 256; trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent); if (trace.fraction == 1 || trace.allsolid) G_FLOAT(OFS_RETURN) = 0; else { VectorCopy (trace.endpos, ent->v.origin); SV_LinkEdict (ent, false); ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = EDICT_TO_PROG(trace.ent); G_FLOAT(OFS_RETURN) = 1; } } /* =============== PF_lightstyle void(float style, string value) lightstyle =============== */ static void PF_lightstyle (void) { int style; const char *val; client_t *client; int j; style = G_FLOAT(OFS_PARM0); val = G_STRING(OFS_PARM1); // bounds check to avoid clobbering sv struct if (style < 0 || style >= MAX_LIGHTSTYLES) Host_Error("%s: style = %d", __thisfunc__, style); // change the string in sv sv.lightstyles[style] = val; // send message to all clients on this server if (sv.state != ss_active) return; for (j = 0, client = svs.clients; j < SV_MAXCLIENTS; j++, client++) { if (SV_CL_OK(client)) { MSG_WriteChar (&SV_NETMSG(client), svc_lightstyle); MSG_WriteChar (&SV_NETMSG(client), style); MSG_WriteString (&SV_NETMSG(client), val); } } } //========================================================================== // // PF_lightstylevalue // // void lightstylevalue(float style); // //========================================================================== static void PF_lightstylevalue(void) { int style; style = G_FLOAT(OFS_PARM0); if (style < 0 || style >= MAX_LIGHTSTYLES) { G_FLOAT(OFS_RETURN) = 0; return; } #ifndef H2W G_FLOAT(OFS_RETURN) = (int)((float)d_lightstylevalue[style]/22.0); #else // G_FLOAT(OFS_RETURN) = 0; G_FLOAT(OFS_RETURN) = (int)d_lightstylevalue[style]; #endif } //========================================================================== // // PF_lightstylestatic // // void lightstylestatic(float style, float value); // //========================================================================== static const char *styleDefs[] = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" }; static void PF_lightstylestatic(void) { int i, value, styleNumber; const char *styleString; client_t *client; styleNumber = G_FLOAT(OFS_PARM0); value = G_FLOAT(OFS_PARM1); if (value < 0) { value = 0; } else if (value > 'z'-'a') { value = 'z'-'a'; } styleString = styleDefs[value]; // Change the string in sv sv.lightstyles[styleNumber] = styleString; #ifdef SERVERONLY d_lightstylevalue[styleNumber] = value; #endif if (sv.state != ss_active) return; // Send message to all clients on this server for (i = 0, client = svs.clients; i < SV_MAXCLIENTS; i++, client++) { if (SV_CL_OK(client)) { MSG_WriteChar(&SV_NETMSG(client), svc_lightstyle); MSG_WriteChar(&SV_NETMSG(client), styleNumber); MSG_WriteString(&SV_NETMSG(client), styleString); } } } static void PF_rint (void) { float f; f = G_FLOAT(OFS_PARM0); #ifndef H2W /* H2 version differs from H2W (and) Quake versions. * Maybe a change done with mission pack? (because * H2W seem to have derived from H2 v1.11 code...) */ G_FLOAT(OFS_RETURN) = (f > 0) ? (int)(f + 0.1) : (int)(f - 0.1); #else G_FLOAT(OFS_RETURN) = (f > 0) ? (int)(f + 0.5) : (int)(f - 0.5); #endif } static void PF_floor (void) { G_FLOAT(OFS_RETURN) = floor(G_FLOAT(OFS_PARM0)); } static void PF_ceil (void) { G_FLOAT(OFS_RETURN) = ceil(G_FLOAT(OFS_PARM0)); } /* ============= PF_checkbottom ============= */ static void PF_checkbottom (void) { edict_t *ent; ent = G_EDICT(OFS_PARM0); G_FLOAT(OFS_RETURN) = SV_CheckBottom (ent); } /* ============= PF_pointcontents ============= */ static void PF_pointcontents (void) { float *v; v = G_VECTOR(OFS_PARM0); G_FLOAT(OFS_RETURN) = SV_PointContents (v); } /* ============= PF_nextent entity nextent(entity) ============= */ static void PF_nextent (void) { int i; edict_t *ent; i = G_EDICTNUM(OFS_PARM0); while (1) { i++; if (i == sv.num_edicts) { RETURN_EDICT(sv.edicts); return; } ent = EDICT_NUM(i); if (!ent->free) { RETURN_EDICT(ent); return; } } } /* ============= PF_aim Pick a vector for the player to shoot along vector aim(entity, missilespeed) ============= */ cvar_t sv_aim = {"sv_aim", "0.93", CVAR_NONE}; static void PF_aim (void) { edict_t *ent, *check, *bestent; vec3_t start, dir, end, bestdir,hold_org; int i, j; trace_t tr; float dist, bestdist; float speed; float *shot_org; float save_hull; ent = G_EDICT(OFS_PARM0); shot_org = G_VECTOR(OFS_PARM1); speed = G_FLOAT(OFS_PARM2); (void) speed; /* variable set but not used */ // VectorCopy (ent->v.origin, start); VectorCopy (shot_org, start); start[2] += 20; // try sending a trace straight VectorCopy (*sv_globals.v_forward, dir); VectorMA (start, 2048, dir, end); save_hull = ent->v.hull; ent->v.hull = 0; tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent); ent->v.hull = save_hull; if (tr.ent && tr.ent->v.takedamage == DAMAGE_YES && (!teamplay.integer || ent->v.team <= 0 || ent->v.team != tr.ent->v.team) ) { VectorCopy (*sv_globals.v_forward, G_VECTOR(OFS_RETURN)); return; } // try all possible entities VectorCopy (dir, bestdir); bestdist = sv_aim.value; bestent = NULL; check = NEXT_EDICT(sv.edicts); for (i = 1; i < sv.num_edicts; i++, check = NEXT_EDICT(check) ) { if (check->v.takedamage != DAMAGE_YES) continue; if (check == ent) continue; if (teamplay.integer && ent->v.team > 0 && ent->v.team == check->v.team) continue; // don't aim at teammate for (j = 0; j < 3; j++) end[j] = check->v.origin[j] + 0.5 * (check->v.mins[j] + check->v.maxs[j]); VectorSubtract (end, start, dir); VectorNormalize (dir); dist = DotProduct (dir, *sv_globals.v_forward); if (dist < bestdist) continue; // to far to turn save_hull = ent->v.hull; ent->v.hull = 0; tr = SV_Move (start, vec3_origin, vec3_origin, end, false, ent); ent->v.hull = save_hull; if (tr.ent == check) { // can shoot at this one bestdist = dist; bestent = check; } } if (bestent) { // Since all origins are at the base, move the point to the middle of the victim model hold_org[0] =bestent->v.origin[0]; hold_org[1] =bestent->v.origin[1]; hold_org[2] =bestent->v.origin[2] + (0.5 * bestent->v.maxs[2]); VectorSubtract (hold_org,shot_org,dir); dist = DotProduct (dir, *sv_globals.v_forward); VectorScale (*sv_globals.v_forward, dist, end); end[2] = dir[2]; VectorNormalize (end); VectorCopy (end, G_VECTOR(OFS_RETURN)); } else { VectorCopy (bestdir, G_VECTOR(OFS_RETURN)); } } /* ============== PF_changeyaw This was a major timewaster in progs, so it was converted to C ============== */ void PF_changeyaw (void) { edict_t *ent; float ideal, current, move, speed; ent = PROG_TO_EDICT(*sv_globals.self); current = anglemod( ent->v.angles[1] ); ideal = ent->v.ideal_yaw; speed = ent->v.yaw_speed; if (current == ideal) { G_FLOAT(OFS_RETURN) = 0; return; } move = ideal - current; if (ideal > current) { if (move >= 180) move = move - 360; } else { if (move <= -180) move = move + 360; } G_FLOAT(OFS_RETURN) = move; if (move > 0) { if (move > speed) move = speed; } else { if (move < -speed) move = -speed; } ent->v.angles[1] = anglemod (current + move); } #ifdef QUAKE2 /* ============== PF_changepitch ============== */ static void PF_changepitch (void) { edict_t *ent; float ideal, current, move, speed; ent = G_EDICT(OFS_PARM0); current = anglemod( ent->v.angles[0] ); ideal = ent->v.idealpitch; speed = ent->v.pitch_speed; if (current == ideal) return; move = ideal - current; if (ideal > current) { if (move >= 180) move = move - 360; } else { if (move <= -180) move = move + 360; } if (move > 0) { if (move > speed) move = speed; } else { if (move < -speed) move = -speed; } ent->v.angles[0] = anglemod (current + move); } #endif /* QUAKE2 */ /* =============================================================================== MESSAGE WRITING =============================================================================== */ static sizebuf_t *WriteDest (void) { int entnum; int dest; edict_t *ent; dest = G_FLOAT(OFS_PARM0); switch (dest) { case MSG_BROADCAST: return &sv.datagram; case MSG_ONE: ent = PROG_TO_EDICT(*sv_globals.msg_entity); entnum = NUM_FOR_EDICT(ent); if (entnum < 1 || entnum > SV_MAXCLIENTS) PR_RunError ("%s: not a client", __thisfunc__); return &SV_NETMSG(svs.clients + (entnum - 1)); case MSG_ALL: return &sv.reliable_datagram; case MSG_INIT: #ifdef H2W if (sv.state != ss_loading) PR_RunError ("%s: MSG_INIT can only be written in spawn functions", __thisfunc__); #endif return &sv.signon; #ifdef H2W case MSG_MULTICAST: return &sv.multicast; #endif default: PR_RunError ("%s: bad destination", __thisfunc__); break; } return NULL; } static void PF_WriteByte (void) { MSG_WriteByte (WriteDest(), G_FLOAT(OFS_PARM1)); } static void PF_WriteChar (void) { MSG_WriteChar (WriteDest(), G_FLOAT(OFS_PARM1)); } static void PF_WriteShort (void) { MSG_WriteShort (WriteDest(), G_FLOAT(OFS_PARM1)); } static void PF_WriteLong (void) { MSG_WriteLong (WriteDest(), G_FLOAT(OFS_PARM1)); } static void PF_WriteAngle (void) { MSG_WriteAngle (WriteDest(), G_FLOAT(OFS_PARM1)); } static void PF_WriteCoord (void) { MSG_WriteCoord (WriteDest(), G_FLOAT(OFS_PARM1)); } static void PF_WriteString (void) { MSG_WriteString (WriteDest(), G_STRING(OFS_PARM1)); } static void PF_WriteEntity (void) { MSG_WriteShort (WriteDest(), G_EDICTNUM(OFS_PARM1)); } //============================================================================= static void PF_makestatic (void) { edict_t *ent; int i; ent = G_EDICT(OFS_PARM0); MSG_WriteByte (&sv.signon,svc_spawnstatic); MSG_WriteShort(&sv.signon, SV_ModelIndex(PR_GetString(ent->v.model))); MSG_WriteByte (&sv.signon, ent->v.frame); MSG_WriteByte (&sv.signon, ent->v.colormap); MSG_WriteByte (&sv.signon, ent->v.skin); MSG_WriteByte (&sv.signon, (int)(ent->v.scale*100.0)&255); MSG_WriteByte (&sv.signon, ent->v.drawflags); MSG_WriteByte (&sv.signon, (int)(ent->v.abslight*255.0)&255); for (i = 0; i < 3; i++) { MSG_WriteCoord(&sv.signon, ent->v.origin[i]); MSG_WriteAngle(&sv.signon, ent->v.angles[i]); } // throw the entity away now ED_Free (ent); } //============================================================================= /* ============== PF_setspawnparms ============== */ static void PF_setspawnparms (void) { edict_t *ent; int i; client_t *client; ent = G_EDICT(OFS_PARM0); i = NUM_FOR_EDICT(ent); if (i < 1 || i > SV_MAXCLIENTS) PR_RunError ("Entity is not a client"); // copy spawn parms out of the client_t client = svs.clients + (i-1); for (i = 0; i < NUM_SPAWN_PARMS; i++) sv_globals.parm[i] = client->spawn_parms[i]; } /* ============== PF_changelevel ============== */ static void PF_changelevel (void) { const char *s1, *s2; if (svs.changelevel_issued) return; svs.changelevel_issued = true; s1 = G_STRING(OFS_PARM0); s2 = G_STRING(OFS_PARM1); if ((int)*sv_globals.serverflags & (SFL_NEW_UNIT | SFL_NEW_EPISODE)) #ifndef H2W Cbuf_AddText (va("changelevel %s %s\n",s1, s2)); #else Cbuf_AddText (va("map %s %s\n",s1, s2)); #endif else Cbuf_AddText (va("changelevel2 %s %s\n",s1, s2)); } #ifdef QUAKE2 #define CONTENT_WATER -3 #define CONTENT_SLIME -4 #define CONTENT_LAVA -5 #define FL_IMMUNE_WATER 131072 #define FL_IMMUNE_SLIME 262144 #define FL_IMMUNE_LAVA 524288 #define CHAN_VOICE 2 #define CHAN_BODY 4 #define ATTN_NORM 1 static void PF_WaterMove (void) { edict_t *self; int flags; int waterlevel; int watertype; float drownlevel; float damage = 0.0; self = PROG_TO_EDICT(*sv_globals.self); if (self->v.movetype == MOVETYPE_NOCLIP) { self->v.air_finished = sv.time + 12; G_FLOAT(OFS_RETURN) = damage; return; } if (self->v.health < 0) { G_FLOAT(OFS_RETURN) = damage; return; } if (self->v.deadflag == DEAD_NO) drownlevel = 3; else drownlevel = 1; flags = (int)self->v.flags; waterlevel = (int)self->v.waterlevel; watertype = (int)self->v.watertype; if (!(flags & (FL_IMMUNE_WATER + FL_GODMODE))) { if (((flags & FL_SWIM) && (waterlevel < drownlevel)) || (waterlevel >= drownlevel)) { if (self->v.air_finished < sv.time) { if (self->v.pain_finished < sv.time) { self->v.dmg = self->v.dmg + 2; if (self->v.dmg > 15) self->v.dmg = 10; // T_Damage (self, world, world, self.dmg, 0, FALSE); damage = self->v.dmg; self->v.pain_finished = sv.time + 1.0; } } } else { if (self->v.air_finished < sv.time) SV_StartSound (self, CHAN_VOICE, "raven/gasp2.wav", 255, ATTN_NORM); else if (self->v.air_finished < sv.time + 9) SV_StartSound (self, CHAN_VOICE, "raven/gasp1.wav", 255, ATTN_NORM); self->v.air_finished = sv.time + 12.0; self->v.dmg = 2; } } if (!waterlevel) { if (flags & FL_INWATER) { // play leave water sound SV_StartSound (self, CHAN_BODY, "raven/outwater.wav", 255, ATTN_NORM); self->v.flags = (float)(flags &~FL_INWATER); } self->v.air_finished = sv.time + 12.0; G_FLOAT(OFS_RETURN) = damage; return; } if (watertype == CONTENT_LAVA) { // do damage if (!(flags & (FL_IMMUNE_LAVA + FL_GODMODE))) { if (self->v.dmgtime < sv.time) { if (self->v.radsuit_finished < sv.time) self->v.dmgtime = sv.time + 0.2; else self->v.dmgtime = sv.time + 1.0; // T_Damage (self, world, world, 10*self.waterlevel, 0, TRUE); damage = (float)(10*waterlevel); } } } else if (watertype == CONTENT_SLIME) { // do damage if (!(flags & (FL_IMMUNE_SLIME + FL_GODMODE))) { if (self->v.dmgtime < sv.time && self->v.radsuit_finished < sv.time) { self->v.dmgtime = sv.time + 1.0; // T_Damage (self, world, world, 4*self.waterlevel, 0, TRUE); damage = (float)(4*waterlevel); } } } if ( !(flags & FL_INWATER) ) { // player enter water sound if (watertype == CONTENT_LAVA) SV_StartSound (self, CHAN_BODY, "raven/inlava.wav", 255, ATTN_NORM); else if (watertype == CONTENT_WATER) SV_StartSound (self, CHAN_BODY, "raven/inh2o.wav", 255, ATTN_NORM); else if (watertype == CONTENT_SLIME) SV_StartSound (self, CHAN_BODY, "player/slimbrn1.wav", 255, ATTN_NORM); self->v.flags = (float)(flags | FL_INWATER); self->v.dmgtime = 0; } if (! (flags & FL_WATERJUMP) ) { // self.velocity = self.velocity - 0.8*self.waterlevel*frametime*self.velocity; VectorMA (self->v.velocity, -0.8 * self->v.waterlevel * host_frametime, self->v.velocity, self->v.velocity); } G_FLOAT(OFS_RETURN) = damage; } static void PF_sin (void) { G_FLOAT(OFS_RETURN) = sin(G_FLOAT(OFS_PARM0)); } static void PF_cos (void) { G_FLOAT(OFS_RETURN) = cos(G_FLOAT(OFS_PARM0)); } #endif /* QUAKE2 */ #ifdef H2W /* ============== PF_logfrag logfrag (killer, killee) ============== */ static void PF_logfrag (void) { edict_t *ent1, *ent2; int e1, e2; char *s; ent1 = G_EDICT(OFS_PARM0); ent2 = G_EDICT(OFS_PARM1); e1 = NUM_FOR_EDICT(ent1); e2 = NUM_FOR_EDICT(ent2); if (e1 < 1 || e1 > SV_MAXCLIENTS || e2 < 1 || e2 > SV_MAXCLIENTS) return; s = va("\\%s\\%s\\\n",svs.clients[e1-1].name, svs.clients[e2-1].name); SZ_Print (&svs.log[svs.logsequence&1], s); if (sv_fraglogfile) { fprintf (sv_fraglogfile, "%s", s); fflush (sv_fraglogfile); } } /* ============== PF_infokey string(entity e, string key) infokey ============== */ static void PF_infokey (void) { edict_t *e; int e1; const char *value; const char *key; e = G_EDICT(OFS_PARM0); e1 = NUM_FOR_EDICT(e); key = G_STRING(OFS_PARM1); if (e1 == 0) { value = Info_ValueForKey (svs.info, key); if (!*value) value = Info_ValueForKey(localinfo, key); } else if (e1 <= SV_MAXCLIENTS) value = Info_ValueForKey (svs.clients[e1-1].userinfo, key); else value = ""; RETURN_STRING(value); } /* ============== PF_stof float(string s) stof ============== */ static void PF_stof (void) { const char *s; s = G_STRING(OFS_PARM0); G_FLOAT(OFS_RETURN) = atof(s); } /* ============== PF_multicast void(vector where, float set) multicast ============== */ static void PF_multicast (void) { float *o; int to; o = G_VECTOR(OFS_PARM0); to = G_FLOAT(OFS_PARM1); SV_Multicast (o, to); } #endif /* H2W */ static void PF_sqrt (void) { G_FLOAT(OFS_RETURN) = sqrt(G_FLOAT(OFS_PARM0)); } static void PF_Fixme (void) { PR_RunError ("unimplemented builtin"); } static void PF_plaque_draw (void) { int idx = (int) G_FLOAT(OFS_PARM1); /* 0 means "clear the plaquemessage", hence * the check for idx < 0 and NOT for idx < 1 */ if (idx < 0 || idx > host_string_count) { PR_RunError ("%s: unexpected index %d (host_string_count: %d)", __thisfunc__, idx, host_string_count); } MSG_WriteByte (WriteDest(), svc_plaque); MSG_WriteShort (WriteDest(), idx); } static void PF_rain_go (void) { float *min_org, *max_org, *e_size; float *dir; vec3_t org; #ifdef H2W vec3_t org2; #endif int color, count, x_dir, y_dir; min_org = G_VECTOR (OFS_PARM0); max_org = G_VECTOR (OFS_PARM1); e_size = G_VECTOR (OFS_PARM2); dir = G_VECTOR (OFS_PARM3); color = G_FLOAT (OFS_PARM4); count = G_FLOAT (OFS_PARM5); org[0] = min_org[0]; org[1] = min_org[1]; org[2] = max_org[2]; #ifdef H2W org2[0] = e_size[0]; org2[1] = e_size[1]; org2[2] = e_size[2]; #endif x_dir = dir[0]; y_dir = dir[1]; #ifndef H2W MSG_WriteByte (&sv.datagram, svc_raineffect); MSG_WriteCoord (&sv.datagram, org[0]); MSG_WriteCoord (&sv.datagram, org[1]); MSG_WriteCoord (&sv.datagram, org[2]); MSG_WriteCoord (&sv.datagram, e_size[0]); MSG_WriteCoord (&sv.datagram, e_size[1]); MSG_WriteCoord (&sv.datagram, e_size[2]); MSG_WriteAngle (&sv.datagram, x_dir); MSG_WriteAngle (&sv.datagram, y_dir); MSG_WriteShort (&sv.datagram, color); MSG_WriteShort (&sv.datagram, count); #else SV_StartRainEffect (org,org2,x_dir,y_dir,color,count); #endif } static void PF_particleexplosion (void) { float *org; int color, radius, counter; org = G_VECTOR(OFS_PARM0); color = G_FLOAT(OFS_PARM1); radius = G_FLOAT(OFS_PARM2); counter = G_FLOAT(OFS_PARM3); MSG_WriteByte(&sv.datagram, svc_particle_explosion); MSG_WriteCoord(&sv.datagram, org[0]); MSG_WriteCoord(&sv.datagram, org[1]); MSG_WriteCoord(&sv.datagram, org[2]); MSG_WriteShort(&sv.datagram, color); MSG_WriteShort(&sv.datagram, radius); MSG_WriteShort(&sv.datagram, counter); } static void PF_movestep (void) { vec3_t v; edict_t *ent; dfunction_t *oldf; int oldself; qboolean set_trace; ent = PROG_TO_EDICT(*sv_globals.self); v[0] = G_FLOAT(OFS_PARM0); v[1] = G_FLOAT(OFS_PARM1); v[2] = G_FLOAT(OFS_PARM2); set_trace = G_FLOAT(OFS_PARM3); // save program state, because SV_movestep may call other progs oldf = pr_xfunction; oldself = *sv_globals.self; G_INT(OFS_RETURN) = SV_movestep (ent, v, false, true, set_trace); // restore program state pr_xfunction = oldf; *sv_globals.self = oldself; } static void PF_Cos (void) { float angle; angle = G_FLOAT(OFS_PARM0); angle = angle * M_PI * 2 / 360; G_FLOAT(OFS_RETURN) = cos(angle); } static void PF_Sin (void) { float angle; angle = G_FLOAT(OFS_PARM0); angle = angle * M_PI * 2 / 360; G_FLOAT(OFS_RETURN) = sin(angle); } static void PF_AdvanceFrame (void) { edict_t *ent; float start, end, result; ent = PROG_TO_EDICT(*sv_globals.self); start = G_FLOAT(OFS_PARM0); end = G_FLOAT(OFS_PARM1); #ifndef H2W /* H2 and H2W versions differ from each other: Maybe * a change done with mission pack? (Because the H2W * code seem to have derived from H2 v1.11 code...) */ if (ent->v.frame < start || ent->v.frame > end) #else if ( (start < end && (ent->v.frame < start || ent->v.frame > end)) || (start > end && (ent->v.frame > start || ent->v.frame < end)) ) #endif { // Didn't start in the range ent->v.frame = start; result = 0; } else if (ent->v.frame == end) { // Wrapping ent->v.frame = start; result = 1; } else #ifdef H2W /* see comment above */ if (end > start) #endif { // Regular Advance ent->v.frame++; if (ent->v.frame == end) result = 2; else result = 0; } #ifdef H2W /* see comment above */ else if (end < start) { // Reverse Advance ent->v.frame--; if (ent->v.frame == end) result = 2; else result = 0; } else { ent->v.frame = end; result = 1; } #endif /**/ G_FLOAT(OFS_RETURN) = result; } static void PF_RewindFrame (void) { edict_t *ent; float start, end, result; ent = PROG_TO_EDICT(*sv_globals.self); start = G_FLOAT(OFS_PARM0); end = G_FLOAT(OFS_PARM1); if (ent->v.frame > start || ent->v.frame < end) { // Didn't start in the range ent->v.frame = start; result = 0; } else if (ent->v.frame == end) { // Wrapping ent->v.frame = start; result = 1; } else { // Regular Advance ent->v.frame--; if (ent->v.frame == end) result = 2; else result = 0; } G_FLOAT(OFS_RETURN) = result; } #define WF_NORMAL_ADVANCE 0 #define WF_CYCLE_STARTED 1 #define WF_CYCLE_WRAPPED 2 #define WF_LAST_FRAME 3 static void PF_advanceweaponframe (void) { edict_t *ent; float startframe, endframe; float state; ent = PROG_TO_EDICT(*sv_globals.self); startframe = G_FLOAT(OFS_PARM0); endframe = G_FLOAT(OFS_PARM1); if ( (endframe > startframe && (ent->v.weaponframe > endframe || ent->v.weaponframe < startframe)) || (endframe < startframe && (ent->v.weaponframe < endframe || ent->v.weaponframe > startframe)) ) { ent->v.weaponframe=startframe; state = WF_CYCLE_STARTED; } else if (ent->v.weaponframe==endframe) { ent->v.weaponframe=startframe; state = WF_CYCLE_WRAPPED; } else { if (startframe > endframe) ent->v.weaponframe = ent->v.weaponframe - 1; else if (startframe < endframe) ent->v.weaponframe = ent->v.weaponframe + 1; if (ent->v.weaponframe==endframe) state = WF_LAST_FRAME; else state = WF_NORMAL_ADVANCE; } G_FLOAT(OFS_RETURN) = state; } static void PF_setclass (void) { float newclass; int entnum; edict_t *e; client_t *client, *old; #ifdef H2W char temp[1024]; #endif entnum = G_EDICTNUM(OFS_PARM0); e = G_EDICT(OFS_PARM0); newclass = G_FLOAT(OFS_PARM1); if (entnum < 1 || entnum > SV_MAXCLIENTS) { Con_Printf ("tried to change class of a non-client\n"); return; } client = &svs.clients[entnum-1]; old = host_client; host_client = client; #ifndef H2W Host_ClientCommands ("playerclass %i\n", (int)newclass); host_client = old; // These will get set again after the message has filtered its way // but it wouldn't take affect right away e->v.playerclass = newclass; client->playerclass = newclass; #else if (newclass > CLASS_DEMON && (dmMode.integer != DM_SIEGE || !SV_PROGS_HAVE_SIEGE)) { newclass = CLASS_PALADIN; } e->v.playerclass = newclass; host_client->playerclass = newclass; sprintf(temp,"%d",(int)newclass); Info_SetValueForKey (host_client->userinfo, "playerclass", temp, MAX_INFO_STRING); q_strlcpy (host_client->name, Info_ValueForKey(host_client->userinfo, "name"), sizeof(host_client->name)); host_client->sendinfo = true; // process any changed values //SV_ExtractFromUserinfo (host_client); //update everyone else about playerclass change MSG_WriteByte (&sv.reliable_datagram, svc_updatepclass); MSG_WriteByte (&sv.reliable_datagram, entnum - 1); MSG_WriteByte (&sv.reliable_datagram, ((host_client->playerclass<<5)|((int)e->v.level&31))); host_client = old; #endif /* H2W */ } #if defined(H2W) static void PF_setsiegeteam (void) { float newteam; int entnum; edict_t *e; client_t *client,*old; // char temp[1024]; if (! SV_PROGS_HAVE_SIEGE) /* paranoia? */ return; entnum = G_EDICTNUM(OFS_PARM0); e = G_EDICT(OFS_PARM0); newteam = G_FLOAT(OFS_PARM1); if (entnum < 1 || entnum > SV_MAXCLIENTS) { Con_Printf ("tried to change siege_team of a non-client\n"); return; } client = &svs.clients[entnum-1]; old = host_client; host_client = client; e->v.siege_team = newteam; host_client->siege_team = newteam; //??? // sprintf(temp,"%d",(int)newteam); // Info_SetValueForKey (host_client->userinfo, "playerclass", temp, MAX_INFO_STRING); // q_strlcpy (host_client->name, Info_ValueForKey(host_client->userinfo, "name"), sizeof(host_client->name)); // host_client->sendinfo = true; //update everyone else about playerclass change MSG_WriteByte (&sv.reliable_datagram, svc_updatesiegeteam); MSG_WriteByte (&sv.reliable_datagram, entnum - 1); MSG_WriteByte (&sv.reliable_datagram, host_client->siege_team); host_client = old; } static void PF_updateSiegeInfo (void) { int j; client_t *client; for (j = 0, client = svs.clients; j < SV_MAXCLIENTS; j++, client++) { if (client->state < cs_connected) continue; MSG_WriteByte (&SV_NETMSG(client), svc_updatesiegeinfo); MSG_WriteByte (&SV_NETMSG(client), (int)ceil(timelimit.value)); MSG_WriteByte (&SV_NETMSG(client), (int)ceil(fraglimit.value)); } } #endif /* H2W */ static void PF_starteffect (void) { #ifndef H2W SV_ParseEffect(&sv.reliable_datagram); #else SV_ParseEffect(NULL); #endif } static void PF_endeffect (void) { int idx; idx = G_FLOAT(OFS_PARM0); idx = G_FLOAT(OFS_PARM1); if (!sv.Effects[idx].type) return; sv.Effects[idx].type = 0; #ifndef H2W MSG_WriteByte (&sv.reliable_datagram, svc_end_effect); MSG_WriteByte (&sv.reliable_datagram, idx); #else MSG_WriteByte (&sv.multicast, svc_end_effect); MSG_WriteByte (&sv.multicast, idx); SV_Multicast (vec3_origin, MULTICAST_ALL_R); #endif } #if defined(H2W) static void PF_turneffect (void) { float *dir, *pos; int idx; idx = G_FLOAT(OFS_PARM0); pos = G_VECTOR(OFS_PARM1); dir = G_VECTOR(OFS_PARM2); if (!sv.Effects[idx].type) return; VectorCopy(pos, sv.Effects[idx].ef.Missile.origin); VectorCopy(dir, sv.Effects[idx].ef.Missile.velocity); MSG_WriteByte (&sv.multicast, svc_turn_effect); MSG_WriteByte (&sv.multicast, idx); MSG_WriteFloat(&sv.multicast, sv.time); MSG_WriteCoord(&sv.multicast, pos[0]); MSG_WriteCoord(&sv.multicast, pos[1]); MSG_WriteCoord(&sv.multicast, pos[2]); MSG_WriteCoord(&sv.multicast, dir[0]); MSG_WriteCoord(&sv.multicast, dir[1]); MSG_WriteCoord(&sv.multicast, dir[2]); SV_MulticastSpecific (sv.Effects[idx].client_list, true); } static void PF_updateeffect (void) //type-specific what this will send { int idx, type, cmd; vec3_t tvec; // the effect we're lookin to change is parm 0 idx = G_FLOAT(OFS_PARM0); // the type of effect that it had better be is parm 1 type = G_FLOAT(OFS_PARM1); if (!sv.Effects[idx].type) return; if (sv.Effects[idx].type != type) return; //common writing--PLEASE use sent type when determining // how much and what to read, so it's safe MSG_WriteByte (&sv.multicast, svc_update_effect); MSG_WriteByte (&sv.multicast, idx); //paranoia alert--make sure client reads the correct number of bytes MSG_WriteByte (&sv.multicast, type); switch (type) { case CE_SCARABCHAIN: //new ent to be attached to--pass in 0 for chain retract sv.Effects[idx].ef.Chain.owner = G_INT(OFS_PARM2) & 0x0fff; sv.Effects[idx].ef.Chain.material = G_INT(OFS_PARM2) >> 12; if (sv.Effects[idx].ef.Chain.owner) sv.Effects[idx].ef.Chain.state = 1; else sv.Effects[idx].ef.Chain.state = 2; MSG_WriteShort (&sv.multicast, G_EDICTNUM(OFS_PARM2)); break; case CE_HWSHEEPINATOR: case CE_HWXBOWSHOOT: cmd = G_FLOAT(OFS_PARM2); MSG_WriteByte (&sv.multicast, cmd); if (cmd & 1) { sv.Effects[idx].ef.Xbow.activebolts &= ~(1 << ((cmd >> 4) & 7)); MSG_WriteCoord (&sv.multicast, G_FLOAT(OFS_PARM3)); } else { sv.Effects[idx].ef.Xbow.vel[(cmd >> 4) & 7][0] = G_FLOAT(OFS_PARM3); sv.Effects[idx].ef.Xbow.vel[(cmd >> 4) & 7][1] = G_FLOAT(OFS_PARM4); sv.Effects[idx].ef.Xbow.vel[(cmd >> 4) & 7][2] = 0; MSG_WriteAngle (&sv.multicast, G_FLOAT(OFS_PARM3)); MSG_WriteAngle (&sv.multicast, G_FLOAT(OFS_PARM4)); if (cmd & 128)//send origin too { sv.Effects[idx].ef.Xbow.turnedbolts |= 1 << ((cmd >> 4) & 7); VectorCopy(G_VECTOR(OFS_PARM5), sv.Effects[idx].ef.Xbow.origin[(cmd >> 4) & 7]); MSG_WriteCoord (&sv.multicast, sv.Effects[idx].ef.Xbow.origin[(cmd >> 4) & 7][0]); MSG_WriteCoord (&sv.multicast, sv.Effects[idx].ef.Xbow.origin[(cmd >> 4) & 7][1]); MSG_WriteCoord (&sv.multicast, sv.Effects[idx].ef.Xbow.origin[(cmd >> 4) & 7][2]); } } break; case CE_HWDRILLA: cmd = G_FLOAT(OFS_PARM2); MSG_WriteByte(&sv.multicast, cmd); if (cmd == 0) { VectorCopy(G_VECTOR(OFS_PARM3), tvec); MSG_WriteCoord (&sv.multicast, tvec[0]); MSG_WriteCoord (&sv.multicast, tvec[1]); MSG_WriteCoord (&sv.multicast, tvec[2]); MSG_WriteByte (&sv.multicast, G_FLOAT(OFS_PARM4)); } else { sv.Effects[idx].ef.Missile.angle[0] = G_FLOAT(OFS_PARM3); MSG_WriteAngle (&sv.multicast, G_FLOAT(OFS_PARM3)); sv.Effects[idx].ef.Missile.angle[1] = G_FLOAT(OFS_PARM4); MSG_WriteAngle (&sv.multicast, G_FLOAT(OFS_PARM4)); VectorCopy(G_VECTOR(OFS_PARM5), sv.Effects[idx].ef.Missile.origin); MSG_WriteCoord (&sv.multicast, sv.Effects[idx].ef.Missile.origin[0]); MSG_WriteCoord (&sv.multicast, sv.Effects[idx].ef.Missile.origin[1]); MSG_WriteCoord (&sv.multicast, sv.Effects[idx].ef.Missile.origin[2]); } break; } SV_MulticastSpecific (sv.Effects[idx].client_list, true); } #endif /* H2W */ #if 0 /* not used */ static void PF_randomrange(void) { float num, minv, maxv; minv = G_FLOAT(OFS_PARM0); maxv = G_FLOAT(OFS_PARM1); num = rand() * (1.0 / RAND_MAX); G_FLOAT(OFS_RETURN) = ((maxv-minv) * num) + minv; } static void PF_randomvalue(void) { float num,range; range = G_FLOAT(OFS_PARM0); num = rand() * (1.0 / RAND_MAX); G_FLOAT(OFS_RETURN) = range * num; } static void PF_randomvrange(void) { float num, *minv, *maxv; vec3_t result; minv = G_VECTOR(OFS_PARM0); maxv = G_VECTOR(OFS_PARM1); num = rand() * (1.0 / RAND_MAX); result[0] = ((maxv[0] - minv[0]) * num) + minv[0]; num = rand() * (1.0 / RAND_MAX); result[1] = ((maxv[1] - minv[1]) * num) + minv[1]; num = rand() * (1.0 / RAND_MAX); result[2] = ((maxv[2] - minv[2]) * num) + minv[2]; VectorCopy (result, G_VECTOR(OFS_RETURN)); } static void PF_randomvvalue(void) { float num, *range; vec3_t result; range = G_VECTOR(OFS_PARM0); num = rand() * (1.0 / RAND_MAX); result[0] = range[0] * num; num = rand() * (1.0 / RAND_MAX); result[1] = range[1] * num; num = rand() * (1.0 / RAND_MAX); result[2] = range[2] * num; VectorCopy (result, G_VECTOR(OFS_RETURN)); } #endif /* not used */ static void PF_concatv(void) { float *in,*range; vec3_t result; in = G_VECTOR(OFS_PARM0); range = G_VECTOR(OFS_PARM1); VectorCopy (in, result); if (result[0] < -range[0]) result[0] = -range[0]; if (result[0] > range[0]) result[0] = range[0]; if (result[1] < -range[1]) result[1] = -range[1]; if (result[1] > range[1]) result[1] = range[1]; if (result[2] < -range[2]) result[2] = -range[2]; if (result[2] > range[2]) result[2] = range[2]; VectorCopy (result, G_VECTOR(OFS_RETURN)); } static void PF_GetString(void) { int idx = (int) G_FLOAT(OFS_PARM0); if (idx < 1 || idx > host_string_count) { PR_RunError ("%s: unexpected index %d (host_string_count: %d)", __thisfunc__, idx, host_string_count); } G_INT(OFS_RETURN) = PR_SetEngineString(Host_GetString(idx - 1)); } static void PF_v_factor(void) // returns (v_right * factor_x) + (v_forward * factor_y) + (v_up * factor_z) { float *range; vec3_t result; range = G_VECTOR(OFS_PARM0); result[0] = ((*sv_globals.v_right)[0] * range[0]) + ((*sv_globals.v_forward)[0] * range[1]) + ((*sv_globals.v_up)[0] * range[2]); result[1] = ((*sv_globals.v_right)[1] * range[0]) + ((*sv_globals.v_forward)[1] * range[1]) + ((*sv_globals.v_up)[1] * range[2]); result[2] = ((*sv_globals.v_right)[2] * range[0]) + ((*sv_globals.v_forward)[2] * range[1]) + ((*sv_globals.v_up)[2] * range[2]); VectorCopy (result, G_VECTOR(OFS_RETURN)); } static void PF_v_factorrange(void) // returns (v_right * factor_x) + (v_forward * factor_y) + (v_up * factor_z) { float num, *minv, *maxv; vec3_t result, r2; minv = G_VECTOR(OFS_PARM0); maxv = G_VECTOR(OFS_PARM1); num = rand() * (1.0 / RAND_MAX); result[0] = ((maxv[0] - minv[0]) * num) + minv[0]; num = rand() * (1.0 / RAND_MAX); result[1] = ((maxv[1] - minv[1]) * num) + minv[1]; num = rand() * (1.0 / RAND_MAX); result[2] = ((maxv[2] - minv[2]) * num) + minv[2]; r2[0] = ((*sv_globals.v_right)[0] * result[0]) + ((*sv_globals.v_forward)[0] * result[1]) + ((*sv_globals.v_up)[0] * result[2]); r2[1] = ((*sv_globals.v_right)[1] * result[0]) + ((*sv_globals.v_forward)[1] * result[1]) + ((*sv_globals.v_up)[1] * result[2]); r2[2] = ((*sv_globals.v_right)[2] * result[0]) + ((*sv_globals.v_forward)[2] * result[1]) + ((*sv_globals.v_up)[2] * result[2]); VectorCopy (r2, G_VECTOR(OFS_RETURN)); } #if !defined(H2W) static void PF_matchAngleToSlope(void) { edict_t *actor; vec3_t v_forward, old_forward, old_right, new_angles2 = { 0, 0, 0 }; float pitch, mod, dot; // OFS_PARM0 is used by PF_vectoangles below actor = G_EDICT(OFS_PARM1); AngleVectors(actor->v.angles, old_forward, old_right, *sv_globals.v_up); PF_vectoangles(); pitch = G_FLOAT(OFS_RETURN) - 90; new_angles2[1] = G_FLOAT(OFS_RETURN+1); AngleVectors(new_angles2, v_forward, *sv_globals.v_right, *sv_globals.v_up); mod = DotProduct(v_forward, old_right); if (mod < 0) mod = 1; else mod = -1; dot = DotProduct(v_forward, old_forward); actor->v.angles[0] = dot * pitch; actor->v.angles[2] = (1-fabs(dot)) * pitch * mod; } static void PF_updateInfoPlaque (void) { /* update the objectives in the mission pack : * see trigger_objective() and objective_use() */ unsigned int check; unsigned int idx, mode; unsigned int *use; idx = G_FLOAT(OFS_PARM0); mode = G_FLOAT(OFS_PARM1); /* index to infolist.txt must be >= 0 && < 64 */ if (idx < 32) { use = &info_mask; check = 1U << idx; } else if (idx < 64) { use = &info_mask2; idx -= 32U; check = 1U << idx; } else { PR_RunError ("%s: bad objective index %u", __thisfunc__, idx); } if ( ((mode & 1 /* FORCE_ON */) && (*use & check)) || ((mode & 2 /* FORCE_OFF */) && !(*use & check)) ) ; else { *use ^= check; } } static void PF_doWhiteFlash(void) { /* buddha.hc::buddha_die() */ MSG_WriteByte (&sv.reliable_datagram, svc_stufftext); MSG_WriteString (&sv.reliable_datagram, "wf\n"); } #else /* H2W: */ extern void SV_setseed(int seed); extern float SV_seedrand(void); extern float SV_GetMultiEffectId(void); extern void SV_ParseMultiEffect(sizebuf_t *sb); static void PF_setseed(void) { SV_setseed(G_FLOAT(OFS_PARM0)); } static void PF_seedrand(void) { G_FLOAT(OFS_RETURN) = SV_seedrand(); } static void PF_multieffect(void) { SV_ParseMultiEffect(&sv.reliable_datagram); } static void PF_getmeid(void) { G_FLOAT(OFS_RETURN) = SV_GetMultiEffectId(); } static void PF_weapon_sound(void) { edict_t *entity; int sound_num; const char *sample; entity = G_EDICT(OFS_PARM0); sample = G_STRING(OFS_PARM1); for (sound_num = 1; sound_num < MAX_SOUNDS && sv.sound_precache[sound_num]; sound_num++) { if (!strcmp(sample, sv.sound_precache[sound_num])) break; } if (sound_num == MAX_SOUNDS || !sv.sound_precache[sound_num]) { Con_Printf ("%s: %s not precached\n", __thisfunc__, sample); return; } entity->v.wpn_sound = sound_num; } #endif /* H2W */ static builtin_t pr_builtin[] = { PF_Fixme, PF_makevectors, // void(entity e) makevectors = #1 PF_setorigin, // void(entity e, vector o) setorigin = #2 PF_setmodel, // void(entity e, string m) setmodel = #3 PF_setsize, // void(entity e, vector min, vector max) setsize = #4 PF_lightstylestatic, // 5 PF_break, // void() break = #6 PF_random, // float() random = #7 PF_sound, // void(entity e, float chan, string samp) sound = #8 PF_normalize, // vector(vector v) normalize = #9 PF_error, // void(string e) error = #10 PF_objerror, // void(string e) objerror = #11 PF_vlen, // float(vector v) vlen = #12 PF_vectoyaw, // float(vector v) vectoyaw = #13 PF_Spawn, // entity() spawn = #14 PF_Remove, // void(entity e) remove = #15 PF_traceline, // float(vector v1, vector v2, float tryents) traceline = #16 PF_checkclient, // entity() clientlist = #17 PF_Find, // entity(entity start, .string fld, string match) find = #18 PF_precache_sound, // void(string s) precache_sound = #19 PF_precache_model, // void(string s) precache_model = #20 PF_stuffcmd, // void(entity client, string s)stuffcmd = #21 PF_findradius, // entity(vector org, float rad) findradius = #22 PF_bprint, // void(string s) bprint = #23 PF_sprint, // void(entity client, string s) sprint = #24 PF_dprint, // void(string s) dprint = #25 PF_ftos, // void(string s) ftos = #26 PF_vtos, // void(string s) vtos = #27 PF_coredump, // PF_coredump = #28 PF_traceon, // PF_traceon = #29 PF_traceoff, // PF_traceoff = #30 PF_eprint, // void(entity e) debug print an entire entity = #31 PF_walkmove, // float(float yaw, float dist) walkmove = #32 PF_tracearea, // float(vector v1, vector v2, vector mins, vector maxs, // float tryents) traceline = #33 PF_droptofloor, // PF_droptofloor = #34 PF_lightstyle, // 35 PF_rint, // 36 PF_floor, // 37 PF_ceil, // 38 PF_Fixme, PF_checkbottom, // 40 PF_pointcontents, // 41 PF_particle2, PF_fabs, // 43 PF_aim, // 44 PF_cvar, // 45 PF_localcmd, // 46 PF_nextent, // 47 PF_particle, // 48 PF_changeyaw, // 49 PF_vhlen, // float(vector v) vhlen = #50 PF_vectoangles, // 51 PF_WriteByte, // 52 PF_WriteChar, // 53 PF_WriteShort, // 54 PF_WriteLong, // 55 PF_WriteCoord, // 56 PF_WriteAngle, // 57 PF_WriteString, // 58 PF_WriteEntity, // 59 #ifndef H2W // PF_FindPath, // 60 #endif PF_dprintf, // void(string s1, string s2) dprint = #60 PF_Cos, // 61 PF_Sin, // 62 PF_AdvanceFrame, // 63 PF_dprintv, // void(string s1, string s2) dprint = #64 PF_RewindFrame, // 65 PF_setclass, SV_MoveToGoal, PF_precache_file, PF_makestatic, PF_changelevel, PF_lightstylevalue, // 71 PF_cvar_set, PF_centerprint, PF_ambientsound, PF_precache_model2, PF_precache_sound2, // precache_sound2 is different only for qcc PF_precache_file, PF_setspawnparms, PF_plaque_draw, PF_rain_go, // 80 PF_particleexplosion, // 81 PF_movestep, PF_advanceweaponframe, PF_sqrt, PF_particle3, // 85 PF_particle4, // 86 PF_setpuzzlemodel, // 87 PF_starteffect, // 88 PF_endeffect, // 89 PF_precache_puzzle_model, // 90 PF_concatv, // 91 PF_GetString, // 92 PF_SpawnTemp, // 93 PF_v_factor, // 94 PF_v_factorrange, // 95 PF_precache_sound3, // 96 PF_precache_model3, // 97 PF_precache_file, // 98 #if !defined(H2W) PF_matchAngleToSlope, // 99 PF_updateInfoPlaque, // 100 PF_precache_sound4, // 101 PF_precache_model4, // 102 PF_precache_file, // 103 PF_doWhiteFlash, // 104 PF_UpdateSoundPos, // 105 PF_StopSound, // 106 #ifdef QUAKE2 PF_sin, PF_cos, PF_sqrt, PF_changepitch, PF_TraceToss, PF_etos, PF_WaterMove, #else PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, PF_Fixme, #endif #else /* H2W: */ PF_logfrag, // 99 PF_infokey, // 100 PF_stof, // 101 PF_multicast, // 102 PF_turneffect, // 103 PF_updateeffect, // 104 PF_setseed, // 105 PF_seedrand, // 106 PF_multieffect, // 107 PF_getmeid, // 108 PF_weapon_sound, // 109 PF_bcenterprint2, // 110 PF_print_indexed, // 111 PF_centerprint2, // 112 PF_name_print, // 113 PF_StopSound, // 114 PF_UpdateSoundPos, // 115 PF_precache_sound, // 116 PF_precache_model, // 117 PF_precache_file, // 118 PF_setsiegeteam, // 119 PF_updateSiegeInfo, // 120 #endif /* H2W */ }; const builtin_t *pr_builtins = pr_builtin; const int pr_numbuiltins = sizeof(pr_builtin) / sizeof(pr_builtin[0]); engine/h2shared/pr_edict.c000066400000000000000000002001161444734033100157630ustar00rootroot00000000000000/* sv_edict.c -- entity dictionary * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(H2W) && !defined(SERVERONLY) #error SERVERONLY not defined for HW server #endif #if !defined(H2W) #define SV_MAXCLIENTS svs.maxclients #define SV_ACTIVE sv.active #define SV_CURSKILL current_skill #else #define SV_MAXCLIENTS MAX_CLIENTS #define SV_ACTIVE (sv.state == ss_active) #define SV_CURSKILL sv.current_skill #endif dprograms_t *progs; dfunction_t *pr_functions; static char pr_null_string[] = ""; static char *pr_strings; static int pr_stringssize; static const char **pr_knownstrings; static int pr_maxknownstrings; static int pr_numknownstrings; static ddef_t *pr_fielddefs; static ddef_t *pr_globaldefs; dstatement_t *pr_statements; float *pr_globals; sv_globals_t sv_globals; int pr_edict_size; /* in bytes */ qboolean is_progs_v6; qboolean ignore_precache = false; unsigned short pr_crc; static int type_size[8] = { 1, /* ev_void */ 1, /* ev_string */ 1, /* ev_float */ 3, /* ev_vector */ 1, /* ev_entity */ 1, /* ev_field */ 1, /* ev_function */ 1 /* ev_pointer */ }; typedef struct sv_def_s { etype_t type; int offset; void *field; } sv_def_t; #if !defined(H2W) /* HEXEN2 PROGS: */ #define OFS_V103(m) (int)offsetof(globalvars_v103_t,m)/4 #define OFS_V111(m) (int)offsetof(globalvars_v111_t,m)/4 #define OFS_V112(m) (int)offsetof(globalvars_v112_t,m)/4 COMPILE_TIME_ASSERT(v103_gofs, offsetof(globalvars_v103_t,ClassChangeWeapon) == 412); static sv_def_t globals_v103[] = { {ev_entity, OFS_V103(self), &sv_globals.self}, {ev_entity, OFS_V103(other), &sv_globals.other}, {ev_entity, OFS_V103(world), &sv_globals.world}, {ev_float, OFS_V103(time), &sv_globals.time}, {ev_float, OFS_V103(frametime), &sv_globals.frametime}, {ev_float, OFS_V103(force_retouch), &sv_globals.force_retouch}, {ev_string, OFS_V103(mapname), &sv_globals.mapname}, {ev_string, OFS_V103(startspot), &sv_globals.startspot}, {ev_float, OFS_V103(deathmatch), &sv_globals.deathmatch}, {ev_float, OFS_V103(coop), &sv_globals.coop}, {ev_float, OFS_V103(teamplay), &sv_globals.teamplay}, {ev_float, OFS_V103(serverflags), &sv_globals.serverflags}, {ev_float, OFS_V103(total_secrets), &sv_globals.total_secrets}, {ev_float, OFS_V103(total_monsters), &sv_globals.total_monsters}, {ev_float, OFS_V103(found_secrets), &sv_globals.found_secrets}, {ev_float, OFS_V103(killed_monsters), &sv_globals.killed_monsters}, {ev_float, OFS_V103(chunk_cnt), &sv_globals.chunk_cnt}, {ev_float, OFS_V103(done_precache), &sv_globals.done_precache}, {ev_float, OFS_V103(parm1), &sv_globals.parm}, {ev_vector, OFS_V103(v_forward), &sv_globals.v_forward}, {ev_vector, OFS_V103(v_up), &sv_globals.v_up}, {ev_vector, OFS_V103(v_right), &sv_globals.v_right}, {ev_float, OFS_V103(trace_allsolid), &sv_globals.trace_allsolid}, {ev_float, OFS_V103(trace_startsolid), &sv_globals.trace_startsolid}, {ev_float, OFS_V103(trace_fraction), &sv_globals.trace_fraction}, {ev_vector, OFS_V103(trace_endpos), &sv_globals.trace_endpos}, {ev_vector, OFS_V103(trace_plane_normal), &sv_globals.trace_plane_normal}, {ev_float, OFS_V103(trace_plane_dist), &sv_globals.trace_plane_dist}, {ev_entity, OFS_V103(trace_ent), &sv_globals.trace_ent}, {ev_float, OFS_V103(trace_inopen), &sv_globals.trace_inopen}, {ev_float, OFS_V103(trace_inwater), &sv_globals.trace_inwater}, {ev_entity, OFS_V103(msg_entity), &sv_globals.msg_entity}, {ev_float, OFS_V103(cycle_wrapped), &sv_globals.cycle_wrapped}, {ev_float, OFS_V103(crouch_cnt), &sv_globals.crouch_cnt}, {ev_float, OFS_V103(modelindex_assassin), &sv_globals.modelindex_assassin}, {ev_float, OFS_V103(modelindex_crusader), &sv_globals.modelindex_crusader}, {ev_float, OFS_V103(modelindex_paladin), &sv_globals.modelindex_paladin}, {ev_float, OFS_V103(modelindex_necromancer),&sv_globals.modelindex_necromancer}, {ev_float, OFS_V103(modelindex_sheep), &sv_globals.modelindex_sheep}, {ev_float, OFS_V103(num_players), &sv_globals.num_players}, {ev_float, OFS_V103(exp_mult), &sv_globals.exp_mult}, {ev_function, OFS_V103(main), &sv_globals.main}, {ev_function, OFS_V103(StartFrame), &sv_globals.StartFrame}, {ev_function, OFS_V103(PlayerPreThink), &sv_globals.PlayerPreThink}, {ev_function, OFS_V103(PlayerPostThink), &sv_globals.PlayerPostThink}, {ev_function, OFS_V103(ClientKill), &sv_globals.ClientKill}, {ev_function, OFS_V103(ClientConnect), &sv_globals.ClientConnect}, {ev_function, OFS_V103(PutClientInServer), &sv_globals.PutClientInServer}, {ev_function, OFS_V103(ClientReEnter), &sv_globals.ClientReEnter}, {ev_function, OFS_V103(ClientDisconnect), &sv_globals.ClientDisconnect}, {ev_function, OFS_V103(ClassChangeWeapon), &sv_globals.ClassChangeWeapon}, {ev_void, 0, NULL } }; COMPILE_TIME_ASSERT(v111_gofs, offsetof(globalvars_v111_t,ClassChangeWeapon) == 416); static sv_def_t globals_v111[] = { {ev_entity, OFS_V111(self), &sv_globals.self}, {ev_entity, OFS_V111(other), &sv_globals.other}, {ev_entity, OFS_V111(world), &sv_globals.world}, {ev_float, OFS_V111(time), &sv_globals.time}, {ev_float, OFS_V111(frametime), &sv_globals.frametime}, {ev_float, OFS_V111(force_retouch), &sv_globals.force_retouch}, {ev_string, OFS_V111(mapname), &sv_globals.mapname}, {ev_string, OFS_V111(startspot), &sv_globals.startspot}, {ev_float, OFS_V111(deathmatch), &sv_globals.deathmatch}, {ev_float, OFS_V111(randomclass), &sv_globals.randomclass}, {ev_float, OFS_V111(coop), &sv_globals.coop}, {ev_float, OFS_V111(teamplay), &sv_globals.teamplay}, {ev_float, OFS_V111(serverflags), &sv_globals.serverflags}, {ev_float, OFS_V111(total_secrets), &sv_globals.total_secrets}, {ev_float, OFS_V111(total_monsters), &sv_globals.total_monsters}, {ev_float, OFS_V111(found_secrets), &sv_globals.found_secrets}, {ev_float, OFS_V111(killed_monsters), &sv_globals.killed_monsters}, {ev_float, OFS_V111(chunk_cnt), &sv_globals.chunk_cnt}, {ev_float, OFS_V111(done_precache), &sv_globals.done_precache}, {ev_float, OFS_V111(parm1), &sv_globals.parm}, {ev_vector, OFS_V111(v_forward), &sv_globals.v_forward}, {ev_vector, OFS_V111(v_up), &sv_globals.v_up}, {ev_vector, OFS_V111(v_right), &sv_globals.v_right}, {ev_float, OFS_V111(trace_allsolid), &sv_globals.trace_allsolid}, {ev_float, OFS_V111(trace_startsolid), &sv_globals.trace_startsolid}, {ev_float, OFS_V111(trace_fraction), &sv_globals.trace_fraction}, {ev_vector, OFS_V111(trace_endpos), &sv_globals.trace_endpos}, {ev_vector, OFS_V111(trace_plane_normal), &sv_globals.trace_plane_normal}, {ev_float, OFS_V111(trace_plane_dist), &sv_globals.trace_plane_dist}, {ev_entity, OFS_V111(trace_ent), &sv_globals.trace_ent}, {ev_float, OFS_V111(trace_inopen), &sv_globals.trace_inopen}, {ev_float, OFS_V111(trace_inwater), &sv_globals.trace_inwater}, {ev_entity, OFS_V111(msg_entity), &sv_globals.msg_entity}, {ev_float, OFS_V111(cycle_wrapped), &sv_globals.cycle_wrapped}, {ev_float, OFS_V111(crouch_cnt), &sv_globals.crouch_cnt}, {ev_float, OFS_V111(modelindex_assassin), &sv_globals.modelindex_assassin}, {ev_float, OFS_V111(modelindex_crusader), &sv_globals.modelindex_crusader}, {ev_float, OFS_V111(modelindex_paladin), &sv_globals.modelindex_paladin}, {ev_float, OFS_V111(modelindex_necromancer),&sv_globals.modelindex_necromancer}, {ev_float, OFS_V111(modelindex_sheep), &sv_globals.modelindex_sheep}, {ev_float, OFS_V111(num_players), &sv_globals.num_players}, {ev_float, OFS_V111(exp_mult), &sv_globals.exp_mult}, {ev_function, OFS_V111(main), &sv_globals.main}, {ev_function, OFS_V111(StartFrame), &sv_globals.StartFrame}, {ev_function, OFS_V111(PlayerPreThink), &sv_globals.PlayerPreThink}, {ev_function, OFS_V111(PlayerPostThink), &sv_globals.PlayerPostThink}, {ev_function, OFS_V111(ClientKill), &sv_globals.ClientKill}, {ev_function, OFS_V111(ClientConnect), &sv_globals.ClientConnect}, {ev_function, OFS_V111(PutClientInServer), &sv_globals.PutClientInServer}, {ev_function, OFS_V111(ClientReEnter), &sv_globals.ClientReEnter}, {ev_function, OFS_V111(ClientDisconnect), &sv_globals.ClientDisconnect}, {ev_function, OFS_V111(ClassChangeWeapon), &sv_globals.ClassChangeWeapon}, {ev_void, 0, NULL } }; COMPILE_TIME_ASSERT(v112_gofs, offsetof(globalvars_v112_t,ClassChangeWeapon) == 404); static sv_def_t globals_v112[] = { {ev_entity, OFS_V112(self), &sv_globals.self}, {ev_entity, OFS_V112(other), &sv_globals.other}, {ev_entity, OFS_V112(world), &sv_globals.world}, {ev_float, OFS_V112(time), &sv_globals.time}, {ev_float, OFS_V112(frametime), &sv_globals.frametime}, {ev_float, OFS_V112(force_retouch), &sv_globals.force_retouch}, {ev_string, OFS_V112(mapname), &sv_globals.mapname}, {ev_string, OFS_V112(startspot), &sv_globals.startspot}, {ev_float, OFS_V112(deathmatch), &sv_globals.deathmatch}, {ev_float, OFS_V112(randomclass), &sv_globals.randomclass}, {ev_float, OFS_V112(coop), &sv_globals.coop}, {ev_float, OFS_V112(teamplay), &sv_globals.teamplay}, {ev_float, OFS_V112(cl_playerclass), &sv_globals.cl_playerclass}, {ev_float, OFS_V112(serverflags), &sv_globals.serverflags}, {ev_float, OFS_V112(total_secrets), &sv_globals.total_secrets}, {ev_float, OFS_V112(total_monsters), &sv_globals.total_monsters}, {ev_float, OFS_V112(found_secrets), &sv_globals.found_secrets}, {ev_float, OFS_V112(killed_monsters), &sv_globals.killed_monsters}, {ev_float, OFS_V112(chunk_cnt), &sv_globals.chunk_cnt}, {ev_float, OFS_V112(done_precache), &sv_globals.done_precache}, {ev_float, OFS_V112(parm1), &sv_globals.parm}, {ev_vector, OFS_V112(v_forward), &sv_globals.v_forward}, {ev_vector, OFS_V112(v_up), &sv_globals.v_up}, {ev_vector, OFS_V112(v_right), &sv_globals.v_right}, {ev_float, OFS_V112(trace_allsolid), &sv_globals.trace_allsolid}, {ev_float, OFS_V112(trace_startsolid), &sv_globals.trace_startsolid}, {ev_float, OFS_V112(trace_fraction), &sv_globals.trace_fraction}, {ev_vector, OFS_V112(trace_endpos), &sv_globals.trace_endpos}, {ev_vector, OFS_V112(trace_plane_normal), &sv_globals.trace_plane_normal}, {ev_float, OFS_V112(trace_plane_dist), &sv_globals.trace_plane_dist}, {ev_entity, OFS_V112(trace_ent), &sv_globals.trace_ent}, {ev_float, OFS_V112(trace_inopen), &sv_globals.trace_inopen}, {ev_float, OFS_V112(trace_inwater), &sv_globals.trace_inwater}, {ev_entity, OFS_V112(msg_entity), &sv_globals.msg_entity}, {ev_float, OFS_V112(cycle_wrapped), &sv_globals.cycle_wrapped}, {ev_float, OFS_V112(crouch_cnt), &sv_globals.crouch_cnt}, {ev_float, OFS_V112(modelindex_sheep), &sv_globals.modelindex_sheep}, {ev_float, OFS_V112(num_players), &sv_globals.num_players}, {ev_float, OFS_V112(exp_mult), &sv_globals.exp_mult}, {ev_function, OFS_V112(main), &sv_globals.main}, {ev_function, OFS_V112(StartFrame), &sv_globals.StartFrame}, {ev_function, OFS_V112(PlayerPreThink), &sv_globals.PlayerPreThink}, {ev_function, OFS_V112(PlayerPostThink), &sv_globals.PlayerPostThink}, {ev_function, OFS_V112(ClientKill), &sv_globals.ClientKill}, {ev_function, OFS_V112(ClientConnect), &sv_globals.ClientConnect}, {ev_function, OFS_V112(PutClientInServer), &sv_globals.PutClientInServer}, {ev_function, OFS_V112(ClientReEnter), &sv_globals.ClientReEnter}, {ev_function, OFS_V112(ClientDisconnect), &sv_globals.ClientDisconnect}, {ev_function, OFS_V112(ClassChangeWeapon), &sv_globals.ClassChangeWeapon}, {ev_void, 0, NULL } }; #else /* HEXENWORLD PROGS: */ #define OFS_V009(m) (int)offsetof(globalvars_v009_t,m)/4 #define OFS_V011(m) (int)offsetof(globalvars_v011_t,m)/4 #define OFS_V014(m) (int)offsetof(globalvars_v014_t,m)/4 #define OFS_V015(m) (int)offsetof(globalvars_v015_t,m)/4 #if 0 COMPILE_TIME_ASSERT(v009_gofs, offsetof(globalvars_v009_t,SetChangeParms) == 464); static sv_def_t globals_v009[] = { {ev_entity, OFS_V009(self), &sv_globals.self}, {ev_entity, OFS_V009(other), &sv_globals.other}, {ev_entity, OFS_V009(world), &sv_globals.world}, {ev_float, OFS_V009(time), &sv_globals.time}, {ev_float, OFS_V009(frametime), &sv_globals.frametime}, {ev_entity, OFS_V009(newmis), &sv_globals.newmis}, {ev_float, OFS_V009(force_retouch), &sv_globals.force_retouch}, {ev_string, OFS_V009(mapname), &sv_globals.mapname}, {ev_string, OFS_V009(startspot), &sv_globals.startspot}, {ev_float, OFS_V009(deathmatch), &sv_globals.deathmatch}, {ev_float, OFS_V009(randomclass), &sv_globals.randomclass}, {ev_float, OFS_V009(damageScale), &sv_globals.damageScale}, {ev_float, OFS_V009(manaScale), &sv_globals.manaScale}, {ev_float, OFS_V009(tomeMode), &sv_globals.tomeMode}, {ev_float, OFS_V009(tomeRespawn), &sv_globals.tomeRespawn}, {ev_float, OFS_V009(w2Respawn), &sv_globals.w2Respawn}, {ev_float, OFS_V009(altRespawn), &sv_globals.altRespawn}, {ev_float, OFS_V009(fixedLevel), &sv_globals.fixedLevel}, {ev_float, OFS_V009(autoItems), &sv_globals.autoItems}, {ev_float, OFS_V009(dmMode), &sv_globals.dmMode}, {ev_float, OFS_V009(coop), &sv_globals.coop}, {ev_float, OFS_V009(teamplay), &sv_globals.teamplay}, {ev_float, OFS_V009(serverflags), &sv_globals.serverflags}, {ev_float, OFS_V009(total_secrets), &sv_globals.total_secrets}, {ev_float, OFS_V009(total_monsters), &sv_globals.total_monsters}, {ev_float, OFS_V009(found_secrets), &sv_globals.found_secrets}, {ev_float, OFS_V009(killed_monsters), &sv_globals.killed_monsters}, {ev_float, OFS_V009(chunk_cnt), &sv_globals.chunk_cnt}, {ev_float, OFS_V009(done_precache), &sv_globals.done_precache}, {ev_float, OFS_V009(parm1), &sv_globals.parm}, {ev_vector, OFS_V009(v_forward), &sv_globals.v_forward}, {ev_vector, OFS_V009(v_up), &sv_globals.v_up}, {ev_vector, OFS_V009(v_right), &sv_globals.v_right}, {ev_float, OFS_V009(trace_allsolid), &sv_globals.trace_allsolid}, {ev_float, OFS_V009(trace_startsolid), &sv_globals.trace_startsolid}, {ev_float, OFS_V009(trace_fraction), &sv_globals.trace_fraction}, {ev_vector, OFS_V009(trace_endpos), &sv_globals.trace_endpos}, {ev_vector, OFS_V009(trace_plane_normal), &sv_globals.trace_plane_normal}, {ev_float, OFS_V009(trace_plane_dist), &sv_globals.trace_plane_dist}, {ev_entity, OFS_V009(trace_ent), &sv_globals.trace_ent}, {ev_float, OFS_V009(trace_inopen), &sv_globals.trace_inopen}, {ev_float, OFS_V009(trace_inwater), &sv_globals.trace_inwater}, {ev_entity, OFS_V009(msg_entity), &sv_globals.msg_entity}, {ev_float, OFS_V009(cycle_wrapped), &sv_globals.cycle_wrapped}, {ev_float, OFS_V009(crouch_cnt), &sv_globals.crouch_cnt}, {ev_float, OFS_V009(modelindex_assassin), &sv_globals.modelindex_assassin}, {ev_float, OFS_V009(modelindex_crusader), &sv_globals.modelindex_crusader}, {ev_float, OFS_V009(modelindex_paladin), &sv_globals.modelindex_paladin}, {ev_float, OFS_V009(modelindex_necromancer),&sv_globals.modelindex_necromancer}, {ev_float, OFS_V009(modelindex_sheep), &sv_globals.modelindex_sheep}, {ev_float, OFS_V009(num_players), &sv_globals.num_players}, {ev_float, OFS_V009(exp_mult), &sv_globals.exp_mult}, {ev_function, OFS_V009(main), &sv_globals.main}, {ev_function, OFS_V009(StartFrame), &sv_globals.StartFrame}, {ev_function, OFS_V009(PlayerPreThink), &sv_globals.PlayerPreThink}, {ev_function, OFS_V009(PlayerPostThink), &sv_globals.PlayerPostThink}, {ev_function, OFS_V009(ClientKill), &sv_globals.ClientKill}, {ev_function, OFS_V009(ClientConnect), &sv_globals.ClientConnect}, {ev_function, OFS_V009(PutClientInServer), &sv_globals.PutClientInServer}, {ev_function, OFS_V009(ClientReEnter), &sv_globals.ClientReEnter}, {ev_function, OFS_V009(ClientDisconnect), &sv_globals.ClientDisconnect}, {ev_function, OFS_V009(ClassChangeWeapon), &sv_globals.ClassChangeWeapon}, {ev_function, OFS_V009(SetNewParms), &sv_globals.SetNewParms}, {ev_function, OFS_V009(SetChangeParms), &sv_globals.SetChangeParms}, {ev_void, 0, NULL } }; #endif COMPILE_TIME_ASSERT(v011_gofs, offsetof(globalvars_v011_t,SetChangeParms) == 480); static sv_def_t globals_v011[] = { {ev_entity, OFS_V011(self), &sv_globals.self}, {ev_entity, OFS_V011(other), &sv_globals.other}, {ev_entity, OFS_V011(world), &sv_globals.world}, {ev_float, OFS_V011(time), &sv_globals.time}, {ev_float, OFS_V011(frametime), &sv_globals.frametime}, {ev_entity, OFS_V011(newmis), &sv_globals.newmis}, {ev_float, OFS_V011(force_retouch), &sv_globals.force_retouch}, {ev_string, OFS_V011(mapname), &sv_globals.mapname}, {ev_string, OFS_V011(startspot), &sv_globals.startspot}, {ev_float, OFS_V011(deathmatch), &sv_globals.deathmatch}, {ev_float, OFS_V011(randomclass), &sv_globals.randomclass}, {ev_float, OFS_V011(damageScale), &sv_globals.damageScale}, {ev_float, OFS_V011(meleeDamScale), &sv_globals.meleeDamScale}, {ev_float, OFS_V011(shyRespawn), &sv_globals.shyRespawn}, {ev_float, OFS_V011(manaScale), &sv_globals.manaScale}, {ev_float, OFS_V011(tomeMode), &sv_globals.tomeMode}, {ev_float, OFS_V011(tomeRespawn), &sv_globals.tomeRespawn}, {ev_float, OFS_V011(w2Respawn), &sv_globals.w2Respawn}, {ev_float, OFS_V011(altRespawn), &sv_globals.altRespawn}, {ev_float, OFS_V011(fixedLevel), &sv_globals.fixedLevel}, {ev_float, OFS_V011(autoItems), &sv_globals.autoItems}, {ev_float, OFS_V011(dmMode), &sv_globals.dmMode}, {ev_float, OFS_V011(easyFourth), &sv_globals.easyFourth}, {ev_float, OFS_V011(patternRunner), &sv_globals.patternRunner}, {ev_float, OFS_V011(coop), &sv_globals.coop}, {ev_float, OFS_V011(teamplay), &sv_globals.teamplay}, {ev_float, OFS_V011(serverflags), &sv_globals.serverflags}, {ev_float, OFS_V011(total_secrets), &sv_globals.total_secrets}, {ev_float, OFS_V011(total_monsters), &sv_globals.total_monsters}, {ev_float, OFS_V011(found_secrets), &sv_globals.found_secrets}, {ev_float, OFS_V011(killed_monsters), &sv_globals.killed_monsters}, {ev_float, OFS_V011(chunk_cnt), &sv_globals.chunk_cnt}, {ev_float, OFS_V011(done_precache), &sv_globals.done_precache}, {ev_float, OFS_V011(parm1), &sv_globals.parm}, {ev_vector, OFS_V011(v_forward), &sv_globals.v_forward}, {ev_vector, OFS_V011(v_up), &sv_globals.v_up}, {ev_vector, OFS_V011(v_right), &sv_globals.v_right}, {ev_float, OFS_V011(trace_allsolid), &sv_globals.trace_allsolid}, {ev_float, OFS_V011(trace_startsolid), &sv_globals.trace_startsolid}, {ev_float, OFS_V011(trace_fraction), &sv_globals.trace_fraction}, {ev_vector, OFS_V011(trace_endpos), &sv_globals.trace_endpos}, {ev_vector, OFS_V011(trace_plane_normal), &sv_globals.trace_plane_normal}, {ev_float, OFS_V011(trace_plane_dist), &sv_globals.trace_plane_dist}, {ev_entity, OFS_V011(trace_ent), &sv_globals.trace_ent}, {ev_float, OFS_V011(trace_inopen), &sv_globals.trace_inopen}, {ev_float, OFS_V011(trace_inwater), &sv_globals.trace_inwater}, {ev_entity, OFS_V011(msg_entity), &sv_globals.msg_entity}, {ev_float, OFS_V011(cycle_wrapped), &sv_globals.cycle_wrapped}, {ev_float, OFS_V011(crouch_cnt), &sv_globals.crouch_cnt}, {ev_float, OFS_V011(modelindex_assassin), &sv_globals.modelindex_assassin}, {ev_float, OFS_V011(modelindex_crusader), &sv_globals.modelindex_crusader}, {ev_float, OFS_V011(modelindex_paladin), &sv_globals.modelindex_paladin}, {ev_float, OFS_V011(modelindex_necromancer),&sv_globals.modelindex_necromancer}, {ev_float, OFS_V011(modelindex_sheep), &sv_globals.modelindex_sheep}, {ev_float, OFS_V011(num_players), &sv_globals.num_players}, {ev_float, OFS_V011(exp_mult), &sv_globals.exp_mult}, {ev_function, OFS_V011(main), &sv_globals.main}, {ev_function, OFS_V011(StartFrame), &sv_globals.StartFrame}, {ev_function, OFS_V011(PlayerPreThink), &sv_globals.PlayerPreThink}, {ev_function, OFS_V011(PlayerPostThink), &sv_globals.PlayerPostThink}, {ev_function, OFS_V011(ClientKill), &sv_globals.ClientKill}, {ev_function, OFS_V011(ClientConnect), &sv_globals.ClientConnect}, {ev_function, OFS_V011(PutClientInServer), &sv_globals.PutClientInServer}, {ev_function, OFS_V011(ClientReEnter), &sv_globals.ClientReEnter}, {ev_function, OFS_V011(ClientDisconnect), &sv_globals.ClientDisconnect}, {ev_function, OFS_V011(ClassChangeWeapon), &sv_globals.ClassChangeWeapon}, {ev_function, OFS_V011(SetNewParms), &sv_globals.SetNewParms}, {ev_function, OFS_V011(SetChangeParms), &sv_globals.SetChangeParms}, {ev_void, 0, NULL } }; COMPILE_TIME_ASSERT(v014_gofs, offsetof(globalvars_v014_t,SetChangeParms) == 496); static sv_def_t globals_v014[] = { {ev_entity, OFS_V014(self), &sv_globals.self}, {ev_entity, OFS_V014(other), &sv_globals.other}, {ev_entity, OFS_V014(world), &sv_globals.world}, {ev_float, OFS_V014(time), &sv_globals.time}, {ev_float, OFS_V014(frametime), &sv_globals.frametime}, {ev_entity, OFS_V014(newmis), &sv_globals.newmis}, {ev_float, OFS_V014(force_retouch), &sv_globals.force_retouch}, {ev_string, OFS_V014(mapname), &sv_globals.mapname}, {ev_string, OFS_V014(startspot), &sv_globals.startspot}, {ev_float, OFS_V014(deathmatch), &sv_globals.deathmatch}, {ev_float, OFS_V014(randomclass), &sv_globals.randomclass}, {ev_float, OFS_V014(damageScale), &sv_globals.damageScale}, {ev_float, OFS_V014(meleeDamScale), &sv_globals.meleeDamScale}, {ev_float, OFS_V014(shyRespawn), &sv_globals.shyRespawn}, {ev_float, OFS_V014(spartanPrint), &sv_globals.spartanPrint}, {ev_float, OFS_V014(manaScale), &sv_globals.manaScale}, {ev_float, OFS_V014(tomeMode), &sv_globals.tomeMode}, {ev_float, OFS_V014(tomeRespawn), &sv_globals.tomeRespawn}, {ev_float, OFS_V014(w2Respawn), &sv_globals.w2Respawn}, {ev_float, OFS_V014(altRespawn), &sv_globals.altRespawn}, {ev_float, OFS_V014(fixedLevel), &sv_globals.fixedLevel}, {ev_float, OFS_V014(autoItems), &sv_globals.autoItems}, {ev_float, OFS_V014(dmMode), &sv_globals.dmMode}, {ev_float, OFS_V014(easyFourth), &sv_globals.easyFourth}, {ev_float, OFS_V014(patternRunner), &sv_globals.patternRunner}, {ev_float, OFS_V014(coop), &sv_globals.coop}, {ev_float, OFS_V014(teamplay), &sv_globals.teamplay}, {ev_float, OFS_V014(serverflags), &sv_globals.serverflags}, {ev_float, OFS_V014(total_secrets), &sv_globals.total_secrets}, {ev_float, OFS_V014(total_monsters), &sv_globals.total_monsters}, {ev_float, OFS_V014(found_secrets), &sv_globals.found_secrets}, {ev_float, OFS_V014(killed_monsters), &sv_globals.killed_monsters}, {ev_float, OFS_V014(chunk_cnt), &sv_globals.chunk_cnt}, {ev_float, OFS_V014(done_precache), &sv_globals.done_precache}, {ev_float, OFS_V014(parm1), &sv_globals.parm}, {ev_vector, OFS_V014(v_forward), &sv_globals.v_forward}, {ev_vector, OFS_V014(v_up), &sv_globals.v_up}, {ev_vector, OFS_V014(v_right), &sv_globals.v_right}, {ev_float, OFS_V014(trace_allsolid), &sv_globals.trace_allsolid}, {ev_float, OFS_V014(trace_startsolid), &sv_globals.trace_startsolid}, {ev_float, OFS_V014(trace_fraction), &sv_globals.trace_fraction}, {ev_vector, OFS_V014(trace_endpos), &sv_globals.trace_endpos}, {ev_vector, OFS_V014(trace_plane_normal), &sv_globals.trace_plane_normal}, {ev_float, OFS_V014(trace_plane_dist), &sv_globals.trace_plane_dist}, {ev_entity, OFS_V014(trace_ent), &sv_globals.trace_ent}, {ev_float, OFS_V014(trace_inopen), &sv_globals.trace_inopen}, {ev_float, OFS_V014(trace_inwater), &sv_globals.trace_inwater}, {ev_entity, OFS_V014(msg_entity), &sv_globals.msg_entity}, {ev_float, OFS_V014(cycle_wrapped), &sv_globals.cycle_wrapped}, {ev_float, OFS_V014(crouch_cnt), &sv_globals.crouch_cnt}, {ev_float, OFS_V014(modelindex_assassin), &sv_globals.modelindex_assassin}, {ev_float, OFS_V014(modelindex_crusader), &sv_globals.modelindex_crusader}, {ev_float, OFS_V014(modelindex_paladin), &sv_globals.modelindex_paladin}, {ev_float, OFS_V014(modelindex_necromancer),&sv_globals.modelindex_necromancer}, {ev_float, OFS_V014(modelindex_sheep), &sv_globals.modelindex_sheep}, {ev_float, OFS_V014(num_players), &sv_globals.num_players}, {ev_float, OFS_V014(exp_mult), &sv_globals.exp_mult}, {ev_float, OFS_V014(max_players), &sv_globals.max_players}, {ev_float, OFS_V014(defLosses), &sv_globals.defLosses}, {ev_float, OFS_V014(attLosses), &sv_globals.attLosses}, {ev_function, OFS_V014(main), &sv_globals.main}, {ev_function, OFS_V014(StartFrame), &sv_globals.StartFrame}, {ev_function, OFS_V014(PlayerPreThink), &sv_globals.PlayerPreThink}, {ev_function, OFS_V014(PlayerPostThink), &sv_globals.PlayerPostThink}, {ev_function, OFS_V014(ClientKill), &sv_globals.ClientKill}, {ev_function, OFS_V014(ClientConnect), &sv_globals.ClientConnect}, {ev_function, OFS_V014(PutClientInServer), &sv_globals.PutClientInServer}, {ev_function, OFS_V014(ClientReEnter), &sv_globals.ClientReEnter}, {ev_function, OFS_V014(ClientDisconnect), &sv_globals.ClientDisconnect}, {ev_function, OFS_V014(ClassChangeWeapon), &sv_globals.ClassChangeWeapon}, {ev_function, OFS_V014(SetNewParms), &sv_globals.SetNewParms}, {ev_function, OFS_V014(SetChangeParms), &sv_globals.SetChangeParms}, {ev_void, 0, NULL } }; COMPILE_TIME_ASSERT(v015_gofs, offsetof(globalvars_v015_t,SmitePlayer) == 500); static sv_def_t globals_v015[] = { {ev_entity, OFS_V015(self), &sv_globals.self}, {ev_entity, OFS_V015(other), &sv_globals.other}, {ev_entity, OFS_V015(world), &sv_globals.world}, {ev_float, OFS_V015(time), &sv_globals.time}, {ev_float, OFS_V015(frametime), &sv_globals.frametime}, {ev_entity, OFS_V015(newmis), &sv_globals.newmis}, {ev_float, OFS_V015(force_retouch), &sv_globals.force_retouch}, {ev_string, OFS_V015(mapname), &sv_globals.mapname}, {ev_string, OFS_V015(startspot), &sv_globals.startspot}, {ev_float, OFS_V015(deathmatch), &sv_globals.deathmatch}, {ev_float, OFS_V015(randomclass), &sv_globals.randomclass}, {ev_float, OFS_V015(damageScale), &sv_globals.damageScale}, {ev_float, OFS_V015(meleeDamScale), &sv_globals.meleeDamScale}, {ev_float, OFS_V015(shyRespawn), &sv_globals.shyRespawn}, {ev_float, OFS_V015(spartanPrint), &sv_globals.spartanPrint}, {ev_float, OFS_V015(manaScale), &sv_globals.manaScale}, {ev_float, OFS_V015(tomeMode), &sv_globals.tomeMode}, {ev_float, OFS_V015(tomeRespawn), &sv_globals.tomeRespawn}, {ev_float, OFS_V015(w2Respawn), &sv_globals.w2Respawn}, {ev_float, OFS_V015(altRespawn), &sv_globals.altRespawn}, {ev_float, OFS_V015(fixedLevel), &sv_globals.fixedLevel}, {ev_float, OFS_V015(autoItems), &sv_globals.autoItems}, {ev_float, OFS_V015(dmMode), &sv_globals.dmMode}, {ev_float, OFS_V015(easyFourth), &sv_globals.easyFourth}, {ev_float, OFS_V015(patternRunner), &sv_globals.patternRunner}, {ev_float, OFS_V015(coop), &sv_globals.coop}, {ev_float, OFS_V015(teamplay), &sv_globals.teamplay}, {ev_float, OFS_V015(serverflags), &sv_globals.serverflags}, {ev_float, OFS_V015(total_secrets), &sv_globals.total_secrets}, {ev_float, OFS_V015(total_monsters), &sv_globals.total_monsters}, {ev_float, OFS_V015(found_secrets), &sv_globals.found_secrets}, {ev_float, OFS_V015(killed_monsters), &sv_globals.killed_monsters}, {ev_float, OFS_V015(chunk_cnt), &sv_globals.chunk_cnt}, {ev_float, OFS_V015(done_precache), &sv_globals.done_precache}, {ev_float, OFS_V015(parm1), &sv_globals.parm}, {ev_vector, OFS_V015(v_forward), &sv_globals.v_forward}, {ev_vector, OFS_V015(v_up), &sv_globals.v_up}, {ev_vector, OFS_V015(v_right), &sv_globals.v_right}, {ev_float, OFS_V015(trace_allsolid), &sv_globals.trace_allsolid}, {ev_float, OFS_V015(trace_startsolid), &sv_globals.trace_startsolid}, {ev_float, OFS_V015(trace_fraction), &sv_globals.trace_fraction}, {ev_vector, OFS_V015(trace_endpos), &sv_globals.trace_endpos}, {ev_vector, OFS_V015(trace_plane_normal), &sv_globals.trace_plane_normal}, {ev_float, OFS_V015(trace_plane_dist), &sv_globals.trace_plane_dist}, {ev_entity, OFS_V015(trace_ent), &sv_globals.trace_ent}, {ev_float, OFS_V015(trace_inopen), &sv_globals.trace_inopen}, {ev_float, OFS_V015(trace_inwater), &sv_globals.trace_inwater}, {ev_entity, OFS_V015(msg_entity), &sv_globals.msg_entity}, {ev_float, OFS_V015(cycle_wrapped), &sv_globals.cycle_wrapped}, {ev_float, OFS_V015(crouch_cnt), &sv_globals.crouch_cnt}, {ev_float, OFS_V015(modelindex_assassin), &sv_globals.modelindex_assassin}, {ev_float, OFS_V015(modelindex_crusader), &sv_globals.modelindex_crusader}, {ev_float, OFS_V015(modelindex_paladin), &sv_globals.modelindex_paladin}, {ev_float, OFS_V015(modelindex_necromancer),&sv_globals.modelindex_necromancer}, {ev_float, OFS_V015(modelindex_sheep), &sv_globals.modelindex_sheep}, {ev_float, OFS_V015(num_players), &sv_globals.num_players}, {ev_float, OFS_V015(exp_mult), &sv_globals.exp_mult}, {ev_float, OFS_V015(max_players), &sv_globals.max_players}, {ev_float, OFS_V015(defLosses), &sv_globals.defLosses}, {ev_float, OFS_V015(attLosses), &sv_globals.attLosses}, {ev_function, OFS_V015(main), &sv_globals.main}, {ev_function, OFS_V015(StartFrame), &sv_globals.StartFrame}, {ev_function, OFS_V015(PlayerPreThink), &sv_globals.PlayerPreThink}, {ev_function, OFS_V015(PlayerPostThink), &sv_globals.PlayerPostThink}, {ev_function, OFS_V015(ClientKill), &sv_globals.ClientKill}, {ev_function, OFS_V015(ClientConnect), &sv_globals.ClientConnect}, {ev_function, OFS_V015(PutClientInServer), &sv_globals.PutClientInServer}, {ev_function, OFS_V015(ClientReEnter), &sv_globals.ClientReEnter}, {ev_function, OFS_V015(ClientDisconnect), &sv_globals.ClientDisconnect}, {ev_function, OFS_V015(ClassChangeWeapon), &sv_globals.ClassChangeWeapon}, {ev_function, OFS_V015(SetNewParms), &sv_globals.SetNewParms}, {ev_function, OFS_V015(SetChangeParms), &sv_globals.SetChangeParms}, {ev_function, OFS_V015(SmitePlayer), &sv_globals.SmitePlayer}, {ev_void, 0, NULL } }; #endif /* H2 / H2W PROGS */ static ddef_t *ED_FieldAtOfs (int ofs); static qboolean ED_ParseEpair (void *base, ddef_t *key, const char *s); static char field_name[256], class_name[256]; static qboolean RemoveBadReferences; #define MAX_FIELD_LEN 64 #define GEFV_CACHESIZE 2 typedef struct { ddef_t *pcache; char field[MAX_FIELD_LEN]; } gefv_cache; static gefv_cache gefvCache[GEFV_CACHESIZE] = { { NULL, "" }, { NULL, "" } }; cvar_t max_temp_edicts = {"max_temp_edicts", "30", CVAR_ARCHIVE}; #if !defined(H2W) // these actually are not used in hexen2, but mods may use them. cvar_t nomonsters = {"nomonsters", "0", CVAR_NONE}; cvar_t gamecfg = {"gamecfg", "0", CVAR_NONE}; cvar_t savedgamecfg = {"savedgamecfg", "0", CVAR_ARCHIVE}; cvar_t saved1 = {"saved1", "0", CVAR_ARCHIVE}; cvar_t saved2 = {"saved2", "0", CVAR_ARCHIVE}; cvar_t saved3 = {"saved3", "0", CVAR_ARCHIVE}; cvar_t saved4 = {"saved4", "0", CVAR_ARCHIVE}; cvar_t scratch1 = {"scratch1", "0", CVAR_NONE}; cvar_t scratch2 = {"scratch2", "0", CVAR_NONE}; cvar_t scratch3 = {"scratch3", "0", CVAR_NONE}; cvar_t scratch4 = {"scratch4", "0", CVAR_NONE}; #else /* HexenWorld: */ func_t SpectatorConnect; func_t SpectatorThink; func_t SpectatorDisconnect; #endif //=========================================================================== /* ================= ED_ClearEdict Sets everything to NULL ================= */ void ED_ClearEdict (edict_t *e) { memset (&e->v, 0, progs->entityfields * 4); #ifndef H2W memset (&e->baseline, 0, sizeof(e->baseline)); #endif e->free = false; } /* ================= ED_Alloc Either finds a free edict, or allocates a new one. Try to avoid reusing an entity that was recently freed, because it can cause the client to think the entity morphed into something else instead of being removed and recreated, which can cause interpolated angles and bad trails. ================= */ edict_t *ED_Alloc (void) { int i; edict_t *e; for (i = SV_MAXCLIENTS + 1 + max_temp_edicts.integer; i < sv.num_edicts; i++) { e = EDICT_NUM(i); // the first couple seconds of server time can involve a lot of // freeing and allocating, so relax the replacement policy if (e->free && (e->freetime < 2 || sv.time - e->freetime > 0.5)) { ED_ClearEdict (e); return e; } } #if !defined(H2W) if (i == MAX_EDICTS) { SV_Edicts("edicts.txt"); Host_Error ("%s: no free edicts", __thisfunc__); } #else if (i == MAX_EDICTS) { Con_Printf ("WARNING: %s: no free edicts\n", __thisfunc__); i--; // step on whatever is the last edict e = EDICT_NUM(i); SV_UnlinkEdict(e); } else #endif sv.num_edicts++; e = EDICT_NUM(i); ED_ClearEdict (e); return e; } edict_t *ED_Alloc_Temp (void) { int i, j; edict_t *e, *Least; Least = NULL; for (i = SV_MAXCLIENTS + 1, j = 0; j < max_temp_edicts.integer; i++, j++) { e = EDICT_NUM(i); // the first couple seconds of server time can involve a lot of // freeing and allocating, so relax the replacement policy if (e->free && (e->freetime < 2 || sv.time - e->freetime > 0.5)) { ED_ClearEdict (e); e->alloctime = sv.time; return e; } if (Least == NULL || e->alloctime < Least->alloctime) { Least = e; } } ED_Free(Least); ED_ClearEdict (Least); Least->alloctime = sv.time; return Least; } /* ================= ED_Free Marks the edict as free FIXME: walk all entities and NULL out references to this entity ================= */ void ED_Free (edict_t *ed) { SV_UnlinkEdict (ed); // unlink from world bsp ed->free = true; ed->v.model = 0; ed->v.takedamage = 0; ed->v.modelindex = 0; ed->v.colormap = 0; ed->v.skin = 0; ed->v.frame = 0; VectorClear (ed->v.origin); VectorClear (ed->v.angles); ed->v.nextthink = -1; ed->v.solid = 0; ed->freetime = sv.time; ed->alloctime = -1; } //=========================================================================== /* ============ ED_GlobalAtOfs ============ */ static ddef_t *ED_GlobalAtOfs (int ofs) { ddef_t *def; int i; for (i = 0; i < progs->numglobaldefs; i++) { def = &pr_globaldefs[i]; if (def->ofs == ofs) return def; } return NULL; } /* ============ ED_FieldAtOfs ============ */ static ddef_t *ED_FieldAtOfs (int ofs) { ddef_t *def; int i; for (i = 0; i < progs->numfielddefs; i++) { def = &pr_fielddefs[i]; if (def->ofs == ofs) return def; } return NULL; } /* ============ ED_FindField ============ */ static ddef_t *ED_FindField (const char *name) { ddef_t *def; int i; for (i = 0; i < progs->numfielddefs; i++) { def = &pr_fielddefs[i]; if ( !strcmp(PR_GetString(def->s_name), name) ) return def; } return NULL; } /* ============ ED_FindGlobal ============ */ static ddef_t *ED_FindGlobal (const char *name) { ddef_t *def; int i; for (i = 0; i < progs->numglobaldefs; i++) { def = &pr_globaldefs[i]; if ( !strcmp(PR_GetString(def->s_name), name) ) return def; } return NULL; } /* ============ ED_FindFunction ============ */ static dfunction_t *ED_FindFunction (const char *fn_name) { dfunction_t *func; int i; for (i = 0; i < progs->numfunctions; i++) { func = &pr_functions[i]; if ( !strcmp(PR_GetString(func->s_name), fn_name) ) return func; } return NULL; } dfunction_t *ED_FindFunctioni (const char *fn_name) { dfunction_t *func; int i; for (i = 0; i < progs->numfunctions; i++) { func = &pr_functions[i]; if ( !q_strcasecmp(PR_GetString(func->s_name), fn_name) ) return func; } return NULL; } eval_t *GetEdictFieldValue(edict_t *ed, const char *field) { ddef_t *def = NULL; int i; static int rep = 0; for (i = 0; i < GEFV_CACHESIZE; i++) { if (!strcmp(field, gefvCache[i].field)) { def = gefvCache[i].pcache; goto Done; } } def = ED_FindField (field); if (strlen(field) < MAX_FIELD_LEN) { gefvCache[rep].pcache = def; strcpy (gefvCache[rep].field, field); rep ^= 1; } Done: if (!def) return NULL; return (eval_t *)((char *)&ed->v + def->ofs*4); } /* ============ PR_ValueString (etype_t type, eval_t *val) Returns a string describing *data in a type specific manner ============= */ static const char *PR_ValueString (int type, eval_t *val) { static char line[512]; ddef_t *def; dfunction_t *f; type &= ~DEF_SAVEGLOBAL; switch (type) { case ev_string: q_snprintf (line, sizeof(line), "%s", PR_GetString(val->string)); break; case ev_entity: q_snprintf (line, sizeof(line), "entity %i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)) ); break; case ev_function: f = pr_functions + val->function; q_snprintf (line, sizeof(line), "%s()", PR_GetString(f->s_name)); break; case ev_field: def = ED_FieldAtOfs ( val->_int ); q_snprintf (line, sizeof(line), ".%s", PR_GetString(def->s_name)); break; case ev_void: q_snprintf (line, sizeof(line), "void"); break; case ev_float: q_snprintf (line, sizeof(line), "%5.1f", val->_float); break; case ev_vector: q_snprintf (line, sizeof(line), "'%5.1f %5.1f %5.1f'", val->vector[0], val->vector[1], val->vector[2]); break; case ev_pointer: q_snprintf (line, sizeof(line), "pointer"); break; default: q_snprintf (line, sizeof(line), "bad type %i", type); break; } return line; } /* ============ PR_UglyValueString (etype_t type, eval_t *val) Returns a string describing *data in a type specific manner Easier to parse than PR_ValueString ============= */ static const char *PR_UglyValueString (int type, eval_t *val) { static char line[512]; ddef_t *def; dfunction_t *f; type &= ~DEF_SAVEGLOBAL; switch (type) { case ev_string: q_snprintf (line, sizeof(line), "%s", PR_GetString(val->string)); break; case ev_entity: q_snprintf (line, sizeof(line), "%i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict))); break; case ev_function: f = pr_functions + val->function; q_snprintf (line, sizeof(line), "%s", PR_GetString(f->s_name)); break; case ev_field: def = ED_FieldAtOfs ( val->_int ); q_snprintf (line, sizeof(line), "%s", PR_GetString(def->s_name)); break; case ev_void: q_snprintf (line, sizeof(line), "void"); break; case ev_float: q_snprintf (line, sizeof(line), "%f", val->_float); break; case ev_vector: q_snprintf (line, sizeof(line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]); break; default: q_snprintf (line, sizeof(line), "bad type %i", type); break; } return line; } /* ============ PR_GlobalString Returns a string with a description and the contents of a global, padded to 20 field width ============ */ const char *PR_GlobalString (int ofs) { static char line[512]; const char *s; int i; ddef_t *def; void *val; val = (void *)&pr_globals[ofs]; def = ED_GlobalAtOfs(ofs); if (!def) q_snprintf (line, sizeof(line), "%i(?)", ofs); else { s = PR_ValueString (def->type, (eval_t *)val); q_snprintf (line, sizeof(line), "%i(%s)%s", ofs, PR_GetString(def->s_name), s); } i = strlen(line); for ( ; i < 20; i++) strcat (line, " "); strcat (line, " "); return line; } const char *PR_GlobalStringNoContents (int ofs) { static char line[512]; int i; ddef_t *def; def = ED_GlobalAtOfs(ofs); if (!def) q_snprintf (line, sizeof(line), "%i(?)", ofs); else q_snprintf (line, sizeof(line), "%i(%s)", ofs, PR_GetString(def->s_name)); i = strlen(line); for ( ; i < 20; i++) strcat (line, " "); strcat (line, " "); return line; } /* ============= ED_Print For debugging ============= */ void ED_Print (edict_t *ed) { ddef_t *d; int *v; int i, j, l; const char *name; int type; if (ed->free) { Con_Printf ("FREE\n"); return; } Con_Printf("\nEDICT %i:\n", NUM_FOR_EDICT(ed)); for (i = 1; i < progs->numfielddefs; i++) { d = &pr_fielddefs[i]; name = PR_GetString(d->s_name); l = strlen (name); j = l - 1; if (j > 0 && name[j-1] == '_' && name[j] >= 'x' && name[j] <= 'z') continue; // skip _x, _y, _z vars v = (int *)((char *)&ed->v + d->ofs*4); // if the value is still all 0, skip the field type = d->type & ~DEF_SAVEGLOBAL; for (j = 0; j < type_size[type]; j++) { if (v[j]) break; } if (j == type_size[type]) continue; Con_Printf ("%s", name); while (l++ < 15) Con_Printf (" "); Con_Printf ("%s\n", PR_ValueString(d->type, (eval_t *)v)); } } /* ============= ED_Write For savegames ============= */ void ED_Write (FILE *f, edict_t *ed) { ddef_t *d; int *v; int i, j; const char *name; int type; fprintf (f, "{\n"); if (ed->free) { fprintf (f, "}\n"); return; } RemoveBadReferences = true; if (ed->v.classname) q_strlcpy (class_name, PR_GetString(ed->v.classname), sizeof(class_name)); else class_name[0] = 0; for (i = 1; i < progs->numfielddefs; i++) { d = &pr_fielddefs[i]; name = PR_GetString(d->s_name); j = strlen(name) - 1; if (j > 0 && name[j-1] == '_' && name[j] >= 'x' && name[j] <= 'z') continue; // skip _x, _y, _z vars v = (int *)((char *)&ed->v + d->ofs*4); // if the value is still all 0, skip the field type = d->type & ~DEF_SAVEGLOBAL; for (j = 0; j < type_size[type]; j++) { if (v[j]) break; } if (j == type_size[type]) continue; q_strlcpy(field_name, name, sizeof(field_name)); fprintf (f, "\"%s\" ", name); fprintf (f, "\"%s\"\n", PR_UglyValueString(d->type, (eval_t *)v)); } field_name[0] = 0; class_name[0] = 0; fprintf (f, "}\n"); RemoveBadReferences = false; } void ED_PrintNum (int ent) { ED_Print (EDICT_NUM(ent)); } /* ============= ED_PrintEdicts For debugging, prints all the entities in the current server ============= */ void ED_PrintEdicts (void) { int i; if (!SV_ACTIVE) return; Con_Printf ("%i entities\n", sv.num_edicts); for (i = 0; i < sv.num_edicts; i++) ED_PrintNum (i); } /* ============= ED_PrintEdict_f For debugging, prints a single edicy ============= */ static void ED_PrintEdict_f (void) { int i; if (!SV_ACTIVE) return; i = atoi (Cmd_Argv(1)); if (i < 0 || i >= sv.num_edicts) { Con_Printf("Bad edict number\n"); return; } ED_PrintNum (i); } /* ============= ED_Count For debugging ============= */ static void ED_Count (void) { edict_t *ent; int i, active, models, solid, step; if (!SV_ACTIVE) return; active = models = solid = step = 0; for (i = 0; i < sv.num_edicts; i++) { ent = EDICT_NUM(i); if (ent->free) continue; active++; if (ent->v.solid) solid++; if (ent->v.model) models++; if (ent->v.movetype == MOVETYPE_STEP) step++; } Con_Printf ("num_edicts:%3i\n", sv.num_edicts); Con_Printf ("active :%3i\n", active); Con_Printf ("view :%3i\n", models); Con_Printf ("touch :%3i\n", solid); Con_Printf ("step :%3i\n", step); } /* ============================================================================== ARCHIVING GLOBALS FIXME: need to tag constants, doesn't really work ============================================================================== */ /* ============= ED_WriteGlobals ============= */ void ED_WriteGlobals (FILE *f) { ddef_t *def; int i; const char *name; int type; fprintf (f, "{\n"); for (i = 0; i < progs->numglobaldefs; i++) { def = &pr_globaldefs[i]; type = def->type; if ( !(def->type & DEF_SAVEGLOBAL) ) continue; type &= ~DEF_SAVEGLOBAL; if (type != ev_string && type != ev_float && type != ev_entity) continue; name = PR_GetString(def->s_name); fprintf (f, "\"%s\" ", name); fprintf (f, "\"%s\"\n", PR_UglyValueString(type, (eval_t *)&pr_globals[def->ofs])); } fprintf (f, "}\n"); } /* ============= ED_ParseGlobals ============= */ void ED_ParseGlobals (const char *data) { char keyname[64]; ddef_t *key; while (1) { // parse key data = COM_Parse (data); if (com_token[0] == '}') break; if (!data) Host_Error ("%s: EOF without closing brace", __thisfunc__); q_strlcpy (keyname, com_token, sizeof(keyname)); // parse value data = COM_Parse (data); if (!data) Host_Error ("%s: EOF without closing brace", __thisfunc__); if (com_token[0] == '}') Host_Error ("%s: closing brace without data", __thisfunc__); key = ED_FindGlobal (keyname); if (!key) { Con_Printf ("'%s' is not a global\n", keyname); continue; } if (!ED_ParseEpair ((void *)pr_globals, key, com_token)) Host_Error ("%s: parse error", __thisfunc__); } } //============================================================================ /* ============= ED_NewString ============= */ static string_t ED_NewString (const char *string) { char *new_p; int i, l; string_t num; l = strlen(string) + 1; num = PR_AllocString (l, &new_p); for (i = 0; i < l; i++) { if (string[i] == '\\' && i < l-1) { i++; if (string[i] == 'n') *new_p++ = '\n'; else *new_p++ = '\\'; } else *new_p++ = string[i]; } return num; } /* ============= ED_ParseEval Can parse either fields or globals returns false if error ============= */ static qboolean ED_ParseEpair (void *base, ddef_t *key, const char *s) { int i; char string[128]; ddef_t *def; char *v, *w; char *end; void *d; dfunction_t *func; d = (void *)((int *)base + key->ofs); switch (key->type & ~DEF_SAVEGLOBAL) { case ev_string: *(string_t *)d = ED_NewString(s); break; case ev_float: *(float *)d = atof (s); break; case ev_vector: q_strlcpy (string, s, sizeof(string)); end = (char *)string + strlen(string); v = string; w = string; for (i = 0; i < 3 && (w <= end); i++) // ericw -- added (w <= end) check { // set v to the next space (or 0 byte), and change that char to a 0 byte while (*v && *v != ' ') v++; *v = 0; ((float *)d)[i] = atof (w); w = v = v+1; } // ericw -- fill remaining elements to 0 in case we hit the end of string // before reading 3 floats. if (i < 3) { Con_DPrintf ("Avoided reading garbage for \"%s\" \"%s\"\n", PR_GetString(key->s_name), s); for (; i < 3; i++) ((float *)d)[i] = 0.0f; } break; case ev_entity: *(int *)d = EDICT_TO_PROG(EDICT_NUM(atoi (s))); break; case ev_field: def = ED_FindField (s); if (!def) { Con_Printf ("Can't find field %s\n", s); return false; } *(int *)d = G_INT(def->ofs); break; case ev_function: func = ED_FindFunction (s); if (!func) { Con_Printf ("Can't find function %s\n", s); return false; } *(func_t *)d = func - pr_functions; break; default: break; } return true; } /* ==================== ED_ParseEdict Parses an edict out of the given string, returning the new position ed should be a properly initialized empty edict. Used for initial level load and for savegames. ==================== */ const char *ED_ParseEdict (const char *data, edict_t *ent) { ddef_t *key; char keyname[256]; qboolean anglehack, init; int n; init = false; // clear it if (ent != sv.edicts) // hack // we rely on this.. memset (&ent->v, 0, progs->entityfields * 4); // go through all the dictionary pairs while (1) { // parse key data = COM_Parse (data); if (com_token[0] == '}') break; if (!data) Host_Error ("%s: EOF without closing brace", __thisfunc__); // anglehack is to allow QuakeEd to write single scalar angles // and allow them to be turned into vectors. (FIXME...) if (!strcmp(com_token, "angle")) { strcpy (com_token, "angles"); anglehack = true; } else anglehack = false; // FIXME: change light to _light to get rid of this hack if (!strcmp(com_token, "light")) strcpy (com_token, "light_lev"); // hack for single light def q_strlcpy (keyname, com_token, sizeof(keyname)); // another hack to fix keynames with trailing spaces n = strlen(keyname); while (n && keyname[n-1] == ' ') { keyname[n-1] = 0; n--; } // parse value data = COM_Parse (data); if (!data) Host_Error ("%s: EOF without closing brace", __thisfunc__); if (com_token[0] == '}') Host_Error ("%s: closing brace without data", __thisfunc__); init = true; // keynames with a leading underscore are used for utility comments, // and are immediately discarded by quake if (keyname[0] == '_') continue; if (q_strcasecmp(keyname,"MIDI") == 0) { q_strlcpy(sv.midi_name, com_token, sizeof(sv.midi_name)); continue; } else if (q_strcasecmp(keyname,"CD") == 0) { sv.cd_track = (byte)atoi(com_token); continue; } key = ED_FindField (keyname); if (!key) { Con_Printf ("'%s' is not a field\n", keyname); continue; } if (anglehack) { char temp[32]; strcpy (temp, com_token); sprintf (com_token, "0 %s 0", temp); } if (!ED_ParseEpair ((void *)&ent->v, key, com_token)) Host_Error ("%s: parse error", __thisfunc__); } if (!init) ent->free = true; return data; } extern int entity_file_size; /* ================ ED_LoadFromFile The entities are directly placed in the array, rather than allocated with ED_Alloc, because otherwise an error loading the map would have entity number references out of order. Creates a server's entity / program execution context by parsing textual entity definitions out of an ent file. Used for both fresh maps and savegame loads. A fresh map would also need to call ED_CallSpawnFunctions () to let the objects initialize themselves. ================ */ void ED_LoadFromFile (const char *data) { dfunction_t *func; edict_t *ent = NULL; int inhibit = 0; #ifndef SERVERONLY int start_amount = current_loading_size; const char *orig = data; #endif *sv_globals.time = sv.time; // parse ents while (1) { // parse the opening brace data = COM_Parse (data); if (!data) break; #ifndef SERVERONLY if (entity_file_size) { current_loading_size = start_amount + ((data - orig) * 80 / entity_file_size); D_ShowLoadingSize(); } #endif if (com_token[0] != '{') Host_Error ("%s: found %s when expecting {", __thisfunc__, com_token); if (!ent) ent = EDICT_NUM(0); else ent = ED_Alloc (); data = ED_ParseEdict (data, ent); #if 0 //jfm fuckup test //remove for final release if (ent->v.spawnflags > 1 && !strcmp("worldspawn", PR_GetString(ent->v.classname))) { Host_Error ("invalid SpawnFlags on World!!!\n"); } #endif // remove things from different skill levels or deathmatch if (deathmatch.integer) { if (((int)ent->v.spawnflags & SPAWNFLAG_NOT_DEATHMATCH)) { ED_Free (ent); inhibit++; continue; } } else if (coop.integer) { if (((int)ent->v.spawnflags & SPAWNFLAG_NOT_COOP)) { ED_Free (ent); inhibit++; continue; } } else { // Gotta be single player int skip; if (((int)ent->v.spawnflags & SPAWNFLAG_NOT_SINGLE)) { ED_Free (ent); inhibit++; continue; } skip = 0; #ifndef SERVERONLY switch (cl_playerclass.integer) { case CLASS_PALADIN: if ((int)ent->v.spawnflags & SPAWNFLAG_NOT_PALADIN) { skip = 1; } break; case CLASS_CLERIC: if ((int)ent->v.spawnflags & SPAWNFLAG_NOT_CLERIC) { skip = 1; } break; case CLASS_DEMON: case CLASS_NECROMANCER: if ((int)ent->v.spawnflags & SPAWNFLAG_NOT_NECROMANCER) { skip = 1; } break; case CLASS_THEIF: if ((int)ent->v.spawnflags & SPAWNFLAG_NOT_THEIF) { skip = 1; } break; } #endif /* !SERVERONLY */ if (skip) { ED_Free (ent); inhibit++; continue; } } if ((SV_CURSKILL == 0 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_EASY)) || (SV_CURSKILL == 1 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_MEDIUM)) || (SV_CURSKILL >= 2 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_HARD)) ) { ED_Free (ent); inhibit++; continue; } // // immediately call spawn function // if (!ent->v.classname) { Con_Printf ("No classname for:\n"); ED_Print (ent); ED_Free (ent); continue; } // look for the spawn function func = ED_FindFunction ( PR_GetString(ent->v.classname) ); if (!func) { Con_Printf ("No spawn function for:\n"); ED_Print (ent); ED_Free (ent); continue; } *sv_globals.self = EDICT_TO_PROG(ent); PR_ExecuteProgram (func - pr_functions); #ifdef H2W SV_FlushSignon(); #endif } Con_DPrintf ("%i entities inhibited\n", inhibit); } /* =============== PR_GetProgFilename return the correct progs filename based on map name by parsing maplist.txt =============== */ #if !defined(H2W) static const char def_progname[] = "progs.dat"; #else static const char def_progname[] = "hwprogs.dat"; #endif static const char *PR_GetProgFilename (void) { #if !USE_MULTIPLE_PROGS return def_progname; #else static const char maplist_name[] = "maplist.txt"; /* original format: * line #1 : * line #2+: */ static char finalprogname[MAX_QPATH]; unsigned int id0, id1; fshandle_t FH; FH.length = FS_OpenFile (maplist_name, & FH.file, &id1); FH.pak = file_from_pak; if (FH.file == NULL) return def_progname; else if (FS_FileExists(def_progname, &id0) && id1 < id0) { Con_DPrintf("ignored %s from a gamedir with lower priority\n", maplist_name); goto _fail; } else { char build[256], *test; int entries; FH.pos = 0; FH.start = ftell(FH.file); if (!FS_fgets(build, sizeof(build), &FH)) goto _fail; entries = atoi(build); if (entries <= 0) goto _fail; while (--entries >= 0) { if (!(test = FS_fgets (build, sizeof(build), &FH))) goto _fail; /* unexpected EOF */ while (*test) { if (*test == '\r' || *test == '\n') { *test = '\0'; break; } if (*test == '\t') *test = ' '; ++test; } while (--test > &build[0]) { if (*test == ' ') *test = '\0'; else break; } if (!(test = strchr(build, ' '))) continue; *test = 0; if (q_strcasecmp(build, sv.name) == 0) { FS_fclose (&FH); while (*(++test) == ' ') ; q_strlcpy(finalprogname, test, sizeof(finalprogname)); return finalprogname; } } } _fail: FS_fclose (&FH); return def_progname; #endif /* end of USE_MULTIPLE_PROGS */ } static void set_address (sv_def_t *def, void *address) { switch (def->type) { case ev_void: case ev_bad: break; case ev_float: case ev_vector: *(float **)def->field = (float *) address; break; case ev_string: case ev_entity: case ev_field: case ev_function: case ev_pointer: *(int **)def->field = (int *) address; break; } } /* =============== PR_ConvertV6Defs, PR_ConvertV6Stmts -- Pa3PyX Convert ddef_v6_t and dstatement_v6_t arrays into _v7 format with byte swapping. See PR_ExecuteProgram() for more info. =============== */ static ddef_v7_t *PR_ConvertV6Defs (ddef_v6_t *v6defs, int numdefs) { int i; ddef_v7_t *v7defs, *v7ptr; ddef_v6_t *v6ptr; v7defs = (ddef_v7_t *) Hunk_AllocName(sizeof(ddef_v7_t) * numdefs, "prog7defs"); for (i = 0, v6ptr = v6defs, v7ptr = v7defs; i < numdefs; i++, v6ptr++, v7ptr++) { v7ptr->type = LittleShort(v6ptr->type); v7ptr->ofs = (unsigned short)LittleShort(v6ptr->ofs); v7ptr->s_name = LittleLong(v6ptr->s_name); } return v7defs; } static dstatement_v7_t *PR_ConvertV6Stmts (dstatement_v6_t *v6stmts, int numstmts) { int i; dstatement_v7_t *v7stmts, *v7ptr; dstatement_v6_t *v6ptr; v7stmts = (dstatement_v7_t *) Hunk_AllocName(sizeof(dstatement_v7_t) * numstmts, "prog7stmt"); for (i = 0, v6ptr = v6stmts, v7ptr = v7stmts; i < numstmts; i++, v6ptr++, v7ptr++) { v7ptr->op = LittleShort(v6ptr->op); v7ptr->a = (unsigned short)LittleShort(v6ptr->a); v7ptr->b = (unsigned short)LittleShort(v6ptr->b); v7ptr->c = (unsigned short)LittleShort(v6ptr->c); } return v7stmts; } /* =============== PR_LoadProgs =============== */ void PR_LoadProgs (void) { int i; const char *progname; const char *progvstr; sv_def_t *def; #if defined(H2W) char num[32]; dfunction_t *f; #endif // flush the non-C variable lookup cache for (i = 0; i < GEFV_CACHESIZE; i++) gefvCache[i].field[0] = 0; progname = PR_GetProgFilename(); progs = (dprograms_t *)FS_LoadHunkFile (progname, NULL); if (!progs) Host_Error ("%s: couldn't load %s", __thisfunc__, progname); Con_DPrintf ("Programs occupy %ldK.\n", fs_filesize / 1024); pr_crc = CRC_Block ((byte *)progs, fs_filesize); #if defined(H2W) /* add prog crc to the serverinfo */ sprintf (num, "%u", pr_crc); Info_SetValueForStarKey (svs.info, "*progs", num, MAX_SERVERINFO_STRING); #endif // byte swap the header for (i = 0; i < (int) sizeof(*progs) / 4; i++) ((int *)progs)[i] = LittleLong ( ((int *)progs)[i] ); switch (progs->version) { case PROG_VERSION_V6: is_progs_v6 = true; break; case PROG_VERSION_V7: is_progs_v6 = false; break; default: Host_Error ("%s is of unsupported version (%d, should be %d or %d)", progname, progs->version, PROG_VERSION_V6, PROG_VERSION_V7); return; /* silence compiler */ } switch (progs->crc) { #if !defined(H2W) /* HEXEN2 PROGS: */ case PROGS_V103_CRC: def = globals_v103; progvstr = "H2/v1.03"; break; case PROGS_V111_CRC: def = globals_v111; progvstr = "H2/v1.11"; break; case PROGS_V112_CRC: def = globals_v112; progvstr = "H2MP/v1.12"; break; #else /* HEXENWORLD PROGS: */ #if 0 case PROGS_V009_CRC: def = globals_v009; progvstr = "HW/v0.09"; break; #endif case PROGS_V011_CRC: def = globals_v011; progvstr = "HW/v0.11"; break; case PROGS_V014_CRC: def = globals_v014; progvstr = "HW/v0.14"; break; case PROGS_V015_CRC: def = globals_v015; progvstr = "HW/v0.15"; break; #endif default: Host_Error ("Unexpected crc ( %d ) for %s", progs->crc, progname); return; /* silence compiler */ } pr_functions = (dfunction_t *)((byte *)progs + progs->ofs_functions); pr_strings = (char *)progs + progs->ofs_strings; if (progs->ofs_strings + progs->numstrings >= fs_filesize) Host_Error ("%s: strings go past end of file\n", progname); // initialize the strings pr_numknownstrings = 0; pr_maxknownstrings = 0; pr_stringssize = progs->numstrings; if (pr_knownstrings) Z_Free ((void *)pr_knownstrings); pr_knownstrings = NULL; PR_SetEngineString(pr_null_string); if (progs->version == PROG_VERSION_V6) { pr_globaldefs = PR_ConvertV6Defs ((ddef_v6_t *)((byte *)progs + progs->ofs_globaldefs), progs->numglobaldefs); pr_fielddefs = PR_ConvertV6Defs ((ddef_v6_t *)((byte *)progs + progs->ofs_fielddefs), progs->numfielddefs); pr_statements = PR_ConvertV6Stmts((dstatement_v6_t *)((byte *)progs + progs->ofs_statements), progs->numstatements); } else { pr_globaldefs = (ddef_t *)((byte *)progs + progs->ofs_globaldefs); pr_fielddefs = (ddef_t *)((byte *)progs + progs->ofs_fielddefs); pr_statements = (dstatement_t *)((byte *)progs + progs->ofs_statements); } Con_DPrintf ("Loaded %s, v%d, %d crc, %s structures\n", progname, progs->version, progs->crc, progvstr); memset (&sv_globals, 0, sizeof(sv_globals)); pr_globals = (float *)((byte *)progs + progs->ofs_globals); for (; def->field; def++) set_address (def, &G_FLOAT(def->offset)); // byte swap the lumps for (i = 0; i < progs->numfunctions; i++) { pr_functions[i].first_statement = LittleLong (pr_functions[i].first_statement); pr_functions[i].parm_start = LittleLong (pr_functions[i].parm_start); pr_functions[i].s_name = LittleLong (pr_functions[i].s_name); pr_functions[i].s_file = LittleLong (pr_functions[i].s_file); pr_functions[i].numparms = LittleLong (pr_functions[i].numparms); pr_functions[i].locals = LittleLong (pr_functions[i].locals); } if (progs->version == PROG_VERSION_V7) { for (i = 0; i < progs->numstatements; i++) { pr_statements[i].op = LittleShort(pr_statements[i].op); pr_statements[i].a = LittleLong(pr_statements[i].a); pr_statements[i].b = LittleLong(pr_statements[i].b); pr_statements[i].c = LittleLong(pr_statements[i].c); } for (i = 0; i < progs->numglobaldefs; i++) { pr_globaldefs[i].type = LittleShort (pr_globaldefs[i].type); pr_globaldefs[i].ofs = LittleLong (pr_globaldefs[i].ofs); pr_globaldefs[i].s_name = LittleLong (pr_globaldefs[i].s_name); } for (i = 0; i < progs->numfielddefs; i++) { pr_fielddefs[i].type = LittleShort (pr_fielddefs[i].type); pr_fielddefs[i].ofs = LittleLong (pr_fielddefs[i].ofs); pr_fielddefs[i].s_name = LittleLong (pr_fielddefs[i].s_name); } } for (i = 0; i < progs->numfielddefs; i++) { if (pr_fielddefs[i].type & DEF_SAVEGLOBAL) Host_Error ("%s: pr_fielddefs[i].type & DEF_SAVEGLOBAL", __thisfunc__); } for (i = 0; i < progs->numglobals; i++) ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]); pr_edict_size = progs->entityfields * 4 + sizeof(edict_t) - sizeof(entvars_t); // round off to next highest whole word address (esp for Alpha) // this ensures that pointers in the engine data area are always // properly aligned pr_edict_size += sizeof(void *) - 1; pr_edict_size &= ~(sizeof(void *) - 1); #if !defined(SERVERONLY) // set the cl_playerclass value after sv_globals has been created if (sv_globals.cl_playerclass) *sv_globals.cl_playerclass = cl_playerclass.value; #endif #if defined(H2W) // Zoid, find the spectator functions SpectatorConnect = SpectatorThink = SpectatorDisconnect = 0; if ((f = ED_FindFunction ("SpectatorConnect")) != NULL) SpectatorConnect = (func_t)(f - pr_functions); if ((f = ED_FindFunction ("SpectatorThink")) != NULL) SpectatorThink = (func_t)(f - pr_functions); if ((f = ED_FindFunction ("SpectatorDisconnect")) != NULL) SpectatorDisconnect = (func_t)(f - pr_functions); #endif } /* =============== PR_Init =============== */ void PR_Init (void) { Cmd_AddCommand ("edict", ED_PrintEdict_f); Cmd_AddCommand ("edicts", ED_PrintEdicts); Cmd_AddCommand ("edictcount", ED_Count); Cmd_AddCommand ("profile", PR_Profile_f); Cvar_RegisterVariable (&max_temp_edicts); #if !defined(H2W) Cvar_RegisterVariable (&nomonsters); Cvar_RegisterVariable (&gamecfg); Cvar_RegisterVariable (&savedgamecfg); Cvar_RegisterVariable (&scratch1); Cvar_RegisterVariable (&scratch2); Cvar_RegisterVariable (&scratch3); Cvar_RegisterVariable (&scratch4); Cvar_RegisterVariable (&saved1); Cvar_RegisterVariable (&saved2); Cvar_RegisterVariable (&saved3); Cvar_RegisterVariable (&saved4); #endif } //=========================================================================== edict_t *EDICT_NUM(int n) { if (n < 0 || n >= MAX_EDICTS) Host_Error ("%s: bad number %i", __thisfunc__, n); return (edict_t *)((byte *)sv.edicts + (n)*pr_edict_size); } int NUM_FOR_EDICT(edict_t *e) { int b; b = (byte *)e - (byte *)sv.edicts; b = b / pr_edict_size; if (b < 0 || b >= sv.num_edicts) { if (!RemoveBadReferences) { Con_DPrintf ("%s: bad pointer, Class: %s Field: %s, Index %d, Total %d\n", __thisfunc__, class_name, field_name, b, sv.num_edicts); } return 0; } if (e->free && RemoveBadReferences) { // Con_DPrintf ("%s: freed edict, Class: %s Field: %s, Index %d, Total %d\n", // __thisfunc__, class_name, field_name, b, sv.num_edicts); return 0; } return b; } //=========================================================================== #define PR_STRING_ALLOCSLOTS 256 static void PR_AllocStringSlots (void) { pr_maxknownstrings += PR_STRING_ALLOCSLOTS; Sys_DPrintf("%s: realloc'ing for %d slots\n", __thisfunc__, pr_maxknownstrings); pr_knownstrings = (const char **) Z_Realloc ((void *)pr_knownstrings, pr_maxknownstrings * sizeof(char *), Z_MAINZONE); } const char *PR_GetString (int num) { if (num >= 0 && num < pr_stringssize) return pr_strings + num; else if (num < 0 && num >= -pr_numknownstrings) { if (!pr_knownstrings[-1 - num]) { Host_Error ("%s: attempt to get a non-existant string %d\n", __thisfunc__, num); return ""; } return pr_knownstrings[-1 - num]; } else { Host_Error ("%s: invalid string offset %d\n", __thisfunc__, num); return ""; } } int PR_SetEngineString (const char *s) { int i; if (!s) return 0; #if 0 /* can't: sv.model_precache & sv.sound_precache points to pr_strings */ if (s >= pr_strings && s <= pr_strings + pr_stringssize) Host_Error ("%s: \"%s\" is in pr_strings area\n", __thisfunc__, s); #else if (s >= pr_strings && s <= pr_strings + pr_stringssize - 2) return (int)(s - pr_strings); #endif for (i = 0; i < pr_numknownstrings; i++) { if (pr_knownstrings[i] == s) return -1 - i; } // new unknown engine string DEBUG_Printf ("%s: new engine string %p\n", __thisfunc__, s); #if 0 for (i = 0; i < pr_numknownstrings; i++) { if (!pr_knownstrings[i]) break; } #endif // if (i >= pr_numknownstrings) // { if (i >= pr_maxknownstrings) PR_AllocStringSlots(); pr_numknownstrings++; // } pr_knownstrings[i] = s; return -1 - i; } int PR_AllocString (int size, char **ptr) { int i; if (!size) return 0; for (i = 0; i < pr_numknownstrings; i++) { if (!pr_knownstrings[i]) break; } // if (i >= pr_numknownstrings) // { if (i >= pr_maxknownstrings) PR_AllocStringSlots(); pr_numknownstrings++; // } pr_knownstrings[i] = (char *)Hunk_AllocName(size, "string"); if (ptr) *ptr = (char *) pr_knownstrings[i]; return -1 - i; } engine/h2shared/pr_exec.c000066400000000000000000000670751444734033100156360ustar00rootroot00000000000000/* pr_exec.c -- PROGS execution * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // HEADER FILES ------------------------------------------------------------ #include "quakedef.h" #include "q_ctype.h" // MACROS ------------------------------------------------------------------ #define MAX_STACK_DEPTH 64 /* was 32 */ #define LOCALSTACK_SIZE 2048 // TYPES ------------------------------------------------------------------- typedef struct { int s; dfunction_t *f; } prstack_t; /* switch types */ enum { SWITCH_F, SWITCH_V, SWITCH_S, SWITCH_E, SWITCH_FNC }; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- const char *PR_GlobalString(int ofs); const char *PR_GlobalStringNoContents(int ofs); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static int EnterFunction(dfunction_t *f); static int LeaveFunction(void); static void PrintStatement(dstatement_t *s); static void PrintCallHistory(void); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- qboolean pr_trace; dfunction_t *pr_xfunction; int pr_xstatement; int pr_argc; // PRIVATE DATA DEFINITIONS ------------------------------------------------ static prstack_t pr_stack[MAX_STACK_DEPTH]; static int pr_depth; static int localstack[LOCALSTACK_SIZE]; static int localstack_used; static const char *pr_opnames[] = { "DONE", "MUL_F", "MUL_V", "MUL_FV", "MUL_VF", "DIV", "ADD_F", "ADD_V", "SUB_F", "SUB_V", "EQ_F", "EQ_V", "EQ_S", "EQ_E", "EQ_FNC", "NE_F", "NE_V", "NE_S", "NE_E", "NE_FNC", "LE", "GE", "LT", "GT", "INDIRECT", "INDIRECT", "INDIRECT", "INDIRECT", "INDIRECT", "INDIRECT", "ADDRESS", "STORE_F", "STORE_V", "STORE_S", "STORE_ENT", "STORE_FLD", "STORE_FNC", "STOREP_F", "STOREP_V", "STOREP_S", "STOREP_ENT", "STOREP_FLD", "STOREP_FNC", "RETURN", "NOT_F", "NOT_V", "NOT_S", "NOT_ENT", "NOT_FNC", "IF", "IFNOT", "CALL0", "CALL1", "CALL2", "CALL3", "CALL4", "CALL5", "CALL6", "CALL7", "CALL8", "STATE", "GOTO", "AND", "OR", "BITAND", "BITOR", "OP_MULSTORE_F", "OP_MULSTORE_V", "OP_MULSTOREP_F", "OP_MULSTOREP_V", "OP_DIVSTORE_F", "OP_DIVSTOREP_F", "OP_ADDSTORE_F", "OP_ADDSTORE_V", "OP_ADDSTOREP_F", "OP_ADDSTOREP_V", "OP_SUBSTORE_F", "OP_SUBSTORE_V", "OP_SUBSTOREP_F", "OP_SUBSTOREP_V", "OP_FETCH_GBL_F", "OP_FETCH_GBL_V", "OP_FETCH_GBL_S", "OP_FETCH_GBL_E", "OP_FETCH_GBL_FNC", "OP_CSTATE", "OP_CWSTATE", "OP_THINKTIME", "OP_BITSET", "OP_BITSETP", "OP_BITCLR", "OP_BITCLRP", "OP_RAND0", "OP_RAND1", "OP_RAND2", "OP_RANDV0", "OP_RANDV1", "OP_RANDV2", "OP_SWITCH_F", "OP_SWITCH_V", "OP_SWITCH_S", "OP_SWITCH_E", "OP_SWITCH_FNC", "OP_CASE", "OP_CASERANGE" }; // CODE -------------------------------------------------------------------- //========================================================================== // // PR_ExecuteProgram // //========================================================================== #if 0 #define OPA ((eval_t *)&pr_globals[(unsigned short)st->a]) #define OPB ((eval_t *)&pr_globals[(unsigned short)st->b]) #define OPC ((eval_t *)&pr_globals[(unsigned short)st->c]) #else /* unsigned short casts were needed with progs version 6. * we are now processing progs internally as version 7. */ #define OPA ((eval_t *)&pr_globals[st->a]) #define OPB ((eval_t *)&pr_globals[st->b]) #define OPC ((eval_t *)&pr_globals[st->c]) #endif void PR_ExecuteProgram (func_t fnum) { eval_t *ptr, *a, *b, *c; float *vecptr; dstatement_t *st; dfunction_t *f, *newf; edict_t *ed; int jump_ofs; int exitdepth; int profile, startprofile; /* switch/case support: */ int case_type = -1; float switch_float = 0; if (!fnum || fnum >= progs->numfunctions) { if (*sv_globals.self) { ED_Print(PROG_TO_EDICT(*sv_globals.self)); } Host_Error("%s: NULL function", __thisfunc__); } f = &pr_functions[fnum]; pr_trace = false; exitdepth = pr_depth; st = &pr_statements[EnterFunction(f)]; startprofile = profile = 0; while (1) { st++; /* next statement */ a = OPA; b = OPB; c = OPC; if (++profile > 100000) { pr_xstatement = st - pr_statements; PR_RunError("runaway loop error"); } if (pr_trace) { PrintStatement(st); } switch (st->op) { case OP_ADD_F: c->_float = a->_float + b->_float; break; case OP_ADD_V: c->vector[0] = a->vector[0] + b->vector[0]; c->vector[1] = a->vector[1] + b->vector[1]; c->vector[2] = a->vector[2] + b->vector[2]; break; case OP_SUB_F: c->_float = a->_float - b->_float; break; case OP_SUB_V: c->vector[0] = a->vector[0] - b->vector[0]; c->vector[1] = a->vector[1] - b->vector[1]; c->vector[2] = a->vector[2] - b->vector[2]; break; case OP_MUL_F: c->_float = a->_float * b->_float; break; case OP_MUL_V: c->_float = a->vector[0] * b->vector[0] + a->vector[1] * b->vector[1] + a->vector[2] * b->vector[2]; break; case OP_MUL_FV: c->vector[0] = a->_float * b->vector[0]; c->vector[1] = a->_float * b->vector[1]; c->vector[2] = a->_float * b->vector[2]; break; case OP_MUL_VF: c->vector[0] = b->_float * a->vector[0]; c->vector[1] = b->_float * a->vector[1]; c->vector[2] = b->_float * a->vector[2]; break; case OP_DIV_F: c->_float = a->_float / b->_float; break; case OP_BITAND: c->_float = (int)a->_float & (int)b->_float; break; case OP_BITOR: c->_float = (int)a->_float | (int)b->_float; break; case OP_GE: c->_float = a->_float >= b->_float; break; case OP_LE: c->_float = a->_float <= b->_float; break; case OP_GT: c->_float = a->_float > b->_float; break; case OP_LT: c->_float = a->_float < b->_float; break; case OP_AND: c->_float = a->_float && b->_float; break; case OP_OR: c->_float = a->_float || b->_float; break; case OP_NOT_F: c->_float = !a->_float; break; case OP_NOT_V: c->_float = !a->vector[0] && !a->vector[1] && !a->vector[2]; break; case OP_NOT_S: c->_float = !a->string || !*PR_GetString(a->string); break; case OP_NOT_FNC: c->_float = !a->function; break; case OP_NOT_ENT: c->_float = (PROG_TO_EDICT(a->edict) == sv.edicts); break; case OP_EQ_F: c->_float = a->_float == b->_float; break; case OP_EQ_V: c->_float = (a->vector[0] == b->vector[0]) && (a->vector[1] == b->vector[1]) && (a->vector[2] == b->vector[2]); break; case OP_EQ_S: c->_float = !strcmp(PR_GetString(a->string), PR_GetString(b->string)); break; case OP_EQ_E: c->_float = a->_int == b->_int; break; case OP_EQ_FNC: c->_float = a->function == b->function; break; case OP_NE_F: c->_float = a->_float != b->_float; break; case OP_NE_V: c->_float = (a->vector[0] != b->vector[0]) || (a->vector[1] != b->vector[1]) || (a->vector[2] != b->vector[2]); break; case OP_NE_S: c->_float = strcmp(PR_GetString(a->string), PR_GetString(b->string)); break; case OP_NE_E: c->_float = a->_int != b->_int; break; case OP_NE_FNC: c->_float = a->function != b->function; break; case OP_STORE_F: case OP_STORE_ENT: case OP_STORE_FLD: // integers case OP_STORE_S: case OP_STORE_FNC: // pointers b->_int = a->_int; break; case OP_STORE_V: b->vector[0] = a->vector[0]; b->vector[1] = a->vector[1]; b->vector[2] = a->vector[2]; break; case OP_STOREP_F: case OP_STOREP_ENT: case OP_STOREP_FLD: // integers case OP_STOREP_S: case OP_STOREP_FNC: // pointers ptr = (eval_t *)((byte *)sv.edicts + b->_int); ptr->_int = a->_int; break; case OP_STOREP_V: ptr = (eval_t *)((byte *)sv.edicts + b->_int); ptr->vector[0] = a->vector[0]; ptr->vector[1] = a->vector[1]; ptr->vector[2] = a->vector[2]; break; case OP_MULSTORE_F: // f *= f b->_float *= a->_float; break; case OP_MULSTORE_V: // v *= f b->vector[0] *= a->_float; b->vector[1] *= a->_float; b->vector[2] *= a->_float; break; case OP_MULSTOREP_F: // e.f *= f ptr = (eval_t *)((byte *)sv.edicts + b->_int); c->_float = (ptr->_float *= a->_float); break; case OP_MULSTOREP_V: // e.v *= f ptr = (eval_t *)((byte *)sv.edicts + b->_int); c->vector[0] = (ptr->vector[0] *= a->_float); c->vector[0] = (ptr->vector[1] *= a->_float); c->vector[0] = (ptr->vector[2] *= a->_float); break; case OP_DIVSTORE_F: // f /= f b->_float /= a->_float; break; case OP_DIVSTOREP_F: // e.f /= f ptr = (eval_t *)((byte *)sv.edicts + b->_int); c->_float = (ptr->_float /= a->_float); break; case OP_ADDSTORE_F: // f += f b->_float += a->_float; break; case OP_ADDSTORE_V: // v += v b->vector[0] += a->vector[0]; b->vector[1] += a->vector[1]; b->vector[2] += a->vector[2]; break; case OP_ADDSTOREP_F: // e.f += f ptr = (eval_t *)((byte *)sv.edicts + b->_int); c->_float = (ptr->_float += a->_float); break; case OP_ADDSTOREP_V: // e.v += v ptr = (eval_t *)((byte *)sv.edicts + b->_int); c->vector[0] = (ptr->vector[0] += a->vector[0]); c->vector[1] = (ptr->vector[1] += a->vector[1]); c->vector[2] = (ptr->vector[2] += a->vector[2]); break; case OP_SUBSTORE_F: // f -= f b->_float -= a->_float; break; case OP_SUBSTORE_V: // v -= v b->vector[0] -= a->vector[0]; b->vector[1] -= a->vector[1]; b->vector[2] -= a->vector[2]; break; case OP_SUBSTOREP_F: // e.f -= f ptr = (eval_t *)((byte *)sv.edicts + b->_int); c->_float = (ptr->_float -= a->_float); break; case OP_SUBSTOREP_V: // e.v -= v ptr = (eval_t *)((byte *)sv.edicts + b->_int); c->vector[0] = (ptr->vector[0] -= a->vector[0]); c->vector[1] = (ptr->vector[1] -= a->vector[1]); c->vector[2] = (ptr->vector[2] -= a->vector[2]); break; case OP_ADDRESS: ed = PROG_TO_EDICT(a->edict); #ifdef PARANOID NUM_FOR_EDICT(ed); // Make sure it's in range #endif if (ed == (edict_t *)sv.edicts && sv.state == ss_active) { pr_xstatement = st - pr_statements; PR_RunError("assignment to world entity"); } c->_int = (byte *)((int *)&ed->v + b->_int) - (byte *)sv.edicts; break; case OP_LOAD_F: case OP_LOAD_FLD: case OP_LOAD_ENT: case OP_LOAD_S: case OP_LOAD_FNC: ed = PROG_TO_EDICT(a->edict); #ifdef PARANOID NUM_FOR_EDICT(ed); // Make sure it's in range #endif ptr = (eval_t *)((int *)&ed->v + b->_int); c->_int = ptr->_int; break; case OP_LOAD_V: ed = PROG_TO_EDICT(a->edict); #ifdef PARANOID NUM_FOR_EDICT(ed); // Make sure it's in range #endif ptr = (eval_t *)((int *)&ed->v + b->_int); c->vector[0] = ptr->vector[0]; c->vector[1] = ptr->vector[1]; c->vector[2] = ptr->vector[2]; break; case OP_FETCH_GBL_F: case OP_FETCH_GBL_S: case OP_FETCH_GBL_E: case OP_FETCH_GBL_FNC: { int i = (int)b->_float; if (i < 0 || i > G_INT(st->a - 1)) { pr_xstatement = st - pr_statements; PR_RunError("array index out of bounds: %d", i); } ptr = (eval_t *)&pr_globals[st->a + i]; c->_int = ptr->_int; } break; case OP_FETCH_GBL_V: { int i = (int)b->_float; if (i < 0 || i > G_INT(st->a - 1)) { pr_xstatement = st - pr_statements; PR_RunError("array index out of bounds: %d", i); } ptr = (eval_t *)&pr_globals[st->a + (i * 3)]; c->vector[0] = ptr->vector[0]; c->vector[1] = ptr->vector[1]; c->vector[2] = ptr->vector[2]; } break; case OP_IFNOT: if (!a->_int) { /* Pa3PyX: a, b, and c used to be signed shorts for progs v6, * now they are signed ints. The problem is, they were used * as signed sometimes and as unsigned other times - most of * the time they were used as unsigned with an explicit cast * in PR_ExecuteProgram(). When we convert the old progs to * to the new format in PR_ConvertOldStmts(), we zero-extend * them instead of sign-extending them for that reason: if we * sign-extend them, most of the code will not work - we will * have negative array offsets in PR_ExecuteProgram(), among * other things. Note that they are cast to unsigned short * in PR_ConvertOldStmts() prior to assigning them to what is * now int. There are a few instances where these shorts are * used as signed as in the case below where negative offsets * are needed. Since we now have a zero-extended number in a, * b, and c, we must change it back to signed short, so that * when it is added with and assigned to an int, the result * ends up sign-extended and we get a proper negative offset, * if there is one. */ jump_ofs = st->b; if (is_progs_v6) jump_ofs = (signed short)jump_ofs; st += jump_ofs - 1; /* -1 to offset the st++ */ } break; case OP_IF: if (a->_int) { jump_ofs = st->b; if (is_progs_v6) jump_ofs = (signed short)jump_ofs; st += jump_ofs - 1; /* -1 to offset the st++ */ } break; case OP_GOTO: jump_ofs = st->a; if (is_progs_v6) jump_ofs = (signed short)jump_ofs; st += jump_ofs - 1; /* -1 to offset the st++ */ break; case OP_CALL8: case OP_CALL7: case OP_CALL6: case OP_CALL5: case OP_CALL4: case OP_CALL3: case OP_CALL2: // Copy second arg to shared space vecptr = G_VECTOR(OFS_PARM1); VectorCopy(c->vector, vecptr); case OP_CALL1: // Copy first arg to shared space vecptr = G_VECTOR(OFS_PARM0); VectorCopy(b->vector, vecptr); case OP_CALL0: pr_xfunction->profile += profile - startprofile; startprofile = profile; pr_xstatement = st - pr_statements; pr_argc = st->op - OP_CALL0; if (!a->function) { PR_RunError("NULL function"); } newf = &pr_functions[a->function]; if (newf->first_statement < 0) { // Built-in function int i = -newf->first_statement; if (i >= pr_numbuiltins) { PR_RunError("Bad builtin call number %d", i); } pr_builtins[i](); break; } // Normal function st = &pr_statements[EnterFunction(newf)]; break; case OP_DONE: case OP_RETURN: { float *retptr = &pr_globals[OFS_RETURN]; float *valptr = &pr_globals[st->a]; pr_xfunction->profile += profile - startprofile; startprofile = profile; pr_xstatement = st - pr_statements; *retptr++ = *valptr++; *retptr++ = *valptr++; *retptr = *valptr; st = &pr_statements[LeaveFunction()]; if (pr_depth == exitdepth) { // Done return; } } break; case OP_STATE: ed = PROG_TO_EDICT(*sv_globals.self); /* Id 1.07 changes #ifdef FPS_20 ed->v.nextthink = *sv_globals.time + 0.05; #else ed->v.nextthink = *sv_globals.time + 0.1; #endif */ ed->v.nextthink = *sv_globals.time + HX_FRAME_TIME; ed->v.frame = a->_float; ed->v.think = b->function; break; case OP_CSTATE: // Cycle state { int startFrame, endFrame; ed = PROG_TO_EDICT(*sv_globals.self); ed->v.nextthink = *sv_globals.time + HX_FRAME_TIME; ed->v.think = pr_xfunction - pr_functions; *sv_globals.cycle_wrapped = false; startFrame = (int)a->_float; endFrame = (int)b->_float; if (startFrame <= endFrame) { // Increment if (ed->v.frame < startFrame || ed->v.frame > endFrame) { ed->v.frame = startFrame; } else { ed->v.frame++; if (ed->v.frame > endFrame) { *sv_globals.cycle_wrapped = true; ed->v.frame = startFrame; } } } else { // Decrement if (ed->v.frame > startFrame || ed->v.frame < endFrame) { ed->v.frame = startFrame; } else { ed->v.frame--; if (ed->v.frame < endFrame) { *sv_globals.cycle_wrapped = true; ed->v.frame = startFrame; } } } } break; case OP_CWSTATE: // Cycle weapon state { int startFrame, endFrame; ed = PROG_TO_EDICT(*sv_globals.self); ed->v.nextthink = *sv_globals.time + HX_FRAME_TIME; ed->v.think = pr_xfunction - pr_functions; *sv_globals.cycle_wrapped = false; startFrame = (int)a->_float; endFrame = (int)b->_float; if (startFrame <= endFrame) { // Increment if (ed->v.weaponframe < startFrame || ed->v.weaponframe > endFrame) { ed->v.weaponframe = startFrame; } else { ed->v.weaponframe++; if (ed->v.weaponframe > endFrame) { *sv_globals.cycle_wrapped = true; ed->v.weaponframe = startFrame; } } } else { // Decrement if (ed->v.weaponframe > startFrame || ed->v.weaponframe < endFrame) { ed->v.weaponframe = startFrame; } else { ed->v.weaponframe--; if (ed->v.weaponframe < endFrame) { *sv_globals.cycle_wrapped = true; ed->v.weaponframe = startFrame; } } } } break; case OP_THINKTIME: ed = PROG_TO_EDICT(a->edict); #ifdef PARANOID NUM_FOR_EDICT(ed); // Make sure it's in range #endif if (ed == (edict_t *)sv.edicts && sv.state == ss_active) { pr_xstatement = st - pr_statements; PR_RunError("assignment to world entity"); } ed->v.nextthink = *sv_globals.time + b->_float; break; case OP_BITSET: // f (+) f b->_float = (int)b->_float | (int)a->_float; break; case OP_BITSETP: // e.f (+) f ptr = (eval_t *)((byte *)sv.edicts + b->_int); ptr->_float = (int)ptr->_float | (int)a->_float; break; case OP_BITCLR: // f (-) f b->_float = (int)b->_float & ~((int)a->_float); break; case OP_BITCLRP: // e.f (-) f ptr = (eval_t *)((byte *)sv.edicts + b->_int); ptr->_float = (int)ptr->_float & ~((int)a->_float); break; case OP_RAND0: { float val; val = rand() * (1.0 / RAND_MAX); G_FLOAT(OFS_RETURN) = val; } break; case OP_RAND1: { float val; val = rand() * (1.0 / RAND_MAX) * a->_float; G_FLOAT(OFS_RETURN) = val; } break; case OP_RAND2: { float val; if (a->_float < b->_float) { val = a->_float + (rand() * (1.0 / RAND_MAX) * (b->_float - a->_float)); } else { val = b->_float + (rand() * (1.0 / RAND_MAX) * (a->_float - b->_float)); } G_FLOAT(OFS_RETURN) = val; } break; case OP_RANDV0: { float val; float *retptr = &G_FLOAT(OFS_RETURN); val = rand() * (1.0 / RAND_MAX); *retptr++ = val; val = rand() * (1.0 / RAND_MAX); *retptr++ = val; val = rand() * (1.0 / RAND_MAX); *retptr = val; } break; case OP_RANDV1: { float val; float *retptr = &G_FLOAT(OFS_RETURN); val = rand() * (1.0 / RAND_MAX) * a->vector[0]; *retptr++ = val; val = rand() * (1.0 / RAND_MAX) * a->vector[1]; *retptr++ = val; val = rand() * (1.0 / RAND_MAX) * a->vector[2]; *retptr = val; } break; case OP_RANDV2: { float val; int i; float *retptr = &G_FLOAT(OFS_RETURN); for (i = 0; i < 3; i++) { if (a->vector[i] < b->vector[i]) { val = a->vector[i] + (rand() * (1.0 / RAND_MAX) * (b->vector[i] - a->vector[i])); } else { val = b->vector[i] + (rand() * (1.0 / RAND_MAX) * (a->vector[i] - b->vector[i])); } *retptr++ = val; } } break; case OP_SWITCH_F: case_type = SWITCH_F; switch_float = a->_float; jump_ofs = st->b; if (is_progs_v6) jump_ofs = (signed short)jump_ofs; st += jump_ofs - 1; /* -1 to offset the st++ */ break; case OP_SWITCH_V: case OP_SWITCH_S: case OP_SWITCH_E: case OP_SWITCH_FNC: pr_xstatement = st - pr_statements; PR_RunError("%s not done yet!", pr_opnames[st->op]); break; case OP_CASERANGE: if (case_type != SWITCH_F) { pr_xstatement = st - pr_statements; PR_RunError("caserange fucked!"); } if ((switch_float >= a->_float) && (switch_float <= b->_float)) { jump_ofs = st->c; if (is_progs_v6) jump_ofs = (signed short)jump_ofs; st += jump_ofs - 1; /* -1 to offset the st++ */ } break; case OP_CASE: switch (case_type) { case SWITCH_F: if (switch_float == a->_float) { jump_ofs = st->b; if (is_progs_v6) jump_ofs = (signed short)jump_ofs; st += jump_ofs - 1; /* -1 to offset the st++ */ } break; case SWITCH_V: case SWITCH_S: case SWITCH_E: case SWITCH_FNC: pr_xstatement = st - pr_statements; PR_RunError("OP_CASE for %s not done yet!", pr_opnames[case_type + OP_SWITCH_F - SWITCH_F]); break; default: pr_xstatement = st - pr_statements; PR_RunError("fucked case!"); } break; default: pr_xstatement = st - pr_statements; PR_RunError("Bad opcode %i", st->op); } } /* end of while(1) loop */ } #undef OPA #undef OPB #undef OPC //========================================================================== // // EnterFunction // //========================================================================== static int EnterFunction (dfunction_t *f) { int i, j, c, o; pr_stack[pr_depth].s = pr_xstatement; pr_stack[pr_depth].f = pr_xfunction; pr_depth++; if (pr_depth >= MAX_STACK_DEPTH) { PR_RunError("stack overflow"); } // save off any locals that the new function steps on c = f->locals; if (localstack_used + c > LOCALSTACK_SIZE) { PR_RunError ("%s: locals stack overflow", __thisfunc__); } for (i = 0; i < c ; i++) { localstack[localstack_used + i] = ((int *)pr_globals)[f->parm_start + i]; } localstack_used += c; // copy parameters o = f->parm_start; for (i = 0; i < f->numparms; i++) { for (j = 0; j < f->parm_size[i]; j++) { ((int *)pr_globals)[o] = ((int *)pr_globals)[OFS_PARM0 + i*3 + j]; o++; } } pr_xfunction = f; return f->first_statement - 1; // offset the s++ } //========================================================================== // // LeaveFunction // //========================================================================== static int LeaveFunction (void) { int i, c; if (pr_depth <= 0) { Host_Error("prog stack underflow"); } // Restore locals from the stack c = pr_xfunction->locals; localstack_used -= c; if (localstack_used < 0) { PR_RunError("%s: locals stack underflow", __thisfunc__); } for (i = 0; i < c; i++) { ((int *)pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used + i]; } // up stack pr_depth--; pr_xfunction = pr_stack[pr_depth].f; return pr_stack[pr_depth].s; } //========================================================================== // // PR_RunError // //========================================================================== void PR_RunError (const char *error, ...) { va_list argptr; char string[1024]; va_start (argptr, error); q_vsnprintf (string, sizeof(string), error, argptr); va_end (argptr); PrintStatement(pr_statements + pr_xstatement); PrintCallHistory(); #ifdef H2W fprintf(stderr, "%s\n", string); #endif Con_Printf("%s\n", string); pr_depth = 0; // dump the stack so host_error can shutdown functions Host_Error("Program error"); } //========================================================================== // // PrintCallHistory // //========================================================================== static void PrintCallHistory (void) { int i; dfunction_t *f; if (pr_depth == 0) { Con_Printf("\n"); return; } pr_stack[pr_depth].f = pr_xfunction; for (i = pr_depth; i >= 0; i--) { f = pr_stack[i].f; if (!f) { Con_Printf("\n"); } else { Con_Printf("%12s : %s\n", PR_GetString(f->s_file), PR_GetString(f->s_name)); } } } //========================================================================== // // PrintStatement // //========================================================================== static void PrintStatement (dstatement_t *s) { int i; if ((unsigned int)s->op < sizeof(pr_opnames)/sizeof(pr_opnames[0])) { Con_Printf("%s ", pr_opnames[s->op]); i = strlen(pr_opnames[s->op]); for ( ; i < 10; i++) { Con_Printf(" "); } } if (s->op == OP_IF || s->op == OP_IFNOT) { Con_Printf("%sbranch %i", PR_GlobalString(s->a), s->b); } else if (s->op == OP_GOTO) { Con_Printf("branch %i", s->a); } else if ((unsigned int)(s->op-OP_STORE_F) < 6) { Con_Printf("%s", PR_GlobalString(s->a)); Con_Printf("%s", PR_GlobalStringNoContents(s->b)); } else { if (s->a) { Con_Printf("%s", PR_GlobalString(s->a)); } if (s->b) { Con_Printf("%s", PR_GlobalString(s->b)); } if (s->c) { Con_Printf("%s", PR_GlobalStringNoContents(s->c)); } } Con_Printf("\n"); } //========================================================================== // // PR_Profile_f // //========================================================================== void PR_Profile_f (void) { int i, j; int pmax; dfunction_t *f, *bestFunc; int total; int funcCount; qboolean byHC; const char *saveName = NULL; FILE *saveFile = NULL; int currentFile; int bestFile; int tally; const char *s; #ifndef H2W if (!sv.active) return; #else if (sv.state != ss_active) return; #endif byHC = false; funcCount = 10; for (i = 1; i < Cmd_Argc(); i++) { s = Cmd_Argv(i); if (*s == 'h' || *s == 'H') { // Sort by HC source file byHC = true; } else if (*s == 's' || *s == 'S') { // Save to file if (i + 1 < Cmd_Argc() && !q_isdigit(*Cmd_Argv(i + 1))) { i++; saveName = FS_MakePath(FS_USERDIR, NULL, Cmd_Argv(i)); } else { saveName = FS_MakePath(FS_USERDIR, NULL, "profile.txt"); } } else if (q_isdigit(*s)) { // Specify function count funcCount = atoi(Cmd_Argv(i)); if (funcCount < 1) { funcCount = 1; } } } total = 0; for (i = 0; i < progs->numfunctions; i++) { total += pr_functions[i].profile; } if (saveName) { // Create the output file saveFile = fopen(saveName, "w"); if (saveFile == NULL) Con_Printf("Could not open %s\n", saveName); } if (byHC == false) { j = 0; do { pmax = 0; bestFunc = NULL; for (i = 0; i < progs->numfunctions; i++) { f = &pr_functions[i]; if (f->profile > pmax) { pmax = f->profile; bestFunc = f; } } if (bestFunc) { if (j < funcCount) { if (saveFile) { fprintf(saveFile, "%05.2f %s\n", ((float)bestFunc->profile / (float)total) * 100.0, PR_GetString(bestFunc->s_name)); } else { Con_Printf("%05.2f %s\n", ((float)bestFunc->profile / (float)total) * 100.0, PR_GetString(bestFunc->s_name)); } } j++; bestFunc->profile = 0; } } while (bestFunc); if (saveFile) { fclose(saveFile); } return; } currentFile = -1; do { tally = 0; bestFile = Q_MAXINT; for (i = 0; i < progs->numfunctions; i++) { if (pr_functions[i].s_file > currentFile && pr_functions[i].s_file < bestFile) { bestFile = pr_functions[i].s_file; tally = pr_functions[i].profile; continue; } if (pr_functions[i].s_file == bestFile) { tally += pr_functions[i].profile; } } currentFile = bestFile; if (tally && currentFile != Q_MAXINT) { if (saveFile) { fprintf(saveFile, "\"%s\"\n", PR_GetString(currentFile)); } else { Con_Printf("\"%s\"\n", PR_GetString(currentFile)); } j = 0; do { pmax = 0; bestFunc = NULL; for (i = 0; i < progs->numfunctions; i++) { f = &pr_functions[i]; if (f->s_file == currentFile && f->profile > pmax) { pmax = f->profile; bestFunc = f; } } if (bestFunc) { if (j < funcCount) { if (saveFile) { fprintf(saveFile, " %05.2f %s\n", ((float)bestFunc->profile / (float)total) * 100.0, PR_GetString(bestFunc->s_name)); } else { Con_Printf(" %05.2f %s\n", ((float)bestFunc->profile / (float)total) * 100.0, PR_GetString(bestFunc->s_name)); } } j++; bestFunc->profile = 0; } } while (bestFunc); } } while (currentFile != Q_MAXINT); if (saveFile) { fclose(saveFile); } } engine/h2shared/printsys.h000066400000000000000000000061561444734033100161020ustar00rootroot00000000000000/* * printsys.h -- console printing * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __PRINTSYS_H #define __PRINTSYS_H /* * CON_Printf: Prints to the in-game console and manages other jobs, such * as echoing to the terminal and log. In case of HexenWorld this may be * redirected. In that case, the message is printed to the relevant client. * * Location: Graphical client : console.c * HexenWorld server: sv_send.c * Hexen II dedicated server: host.c */ void CON_Printf (unsigned int flags, const char *fmt, ...) FUNC_PRINTF(2,3); /* common print flags */ #define _PRINT_NORMAL 0 /* print to both terminal and to the in-game console */ #define _PRINT_TERMONLY 1 /* print to the terminal only: formerly Sys_Printf */ #define _PRINT_DEVEL 2 /* print only if the developer cvar is set */ #define _PRINT_SAFE 4 /* okay to call even when the screen can't be updated */ /* macros for compatibility with quake api */ #if defined(__GNUC__) && !(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) #define Con_Printf(fmt, args...) CON_Printf(_PRINT_NORMAL, fmt, ##args) #define Con_DPrintf(fmt, args...) CON_Printf(_PRINT_DEVEL, fmt, ##args) #define Con_SafePrintf(fmt, args...) CON_Printf(_PRINT_SAFE, fmt, ##args) #define Con_SafeDPrintf(fmt, args...) CON_Printf(_PRINT_DEVEL|_PRINT_SAFE, fmt, ##args) #else #define Con_Printf(...) CON_Printf(_PRINT_NORMAL, __VA_ARGS__) #define Con_DPrintf(...) CON_Printf(_PRINT_DEVEL, __VA_ARGS__) #define Con_SafePrintf(...) CON_Printf(_PRINT_SAFE, __VA_ARGS__) #define Con_SafeDPrintf(...) CON_Printf(_PRINT_DEVEL|_PRINT_SAFE, __VA_ARGS__) #endif /* these macros print to the terminal only */ #if defined(__GNUC__) && !(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) #define Sys_Printf(fmt, args...) CON_Printf(_PRINT_TERMONLY, fmt, ##args) #define Sys_DPrintf(fmt, args...) CON_Printf(_PRINT_TERMONLY|_PRINT_DEVEL, fmt, ##args) #else #define Sys_Printf(...) CON_Printf(_PRINT_TERMONLY, __VA_ARGS__) #define Sys_DPrintf(...) CON_Printf(_PRINT_TERMONLY|_PRINT_DEVEL, __VA_ARGS__) #endif #if defined(DEBUG_BUILD) #define DEBUG_Printf Sys_DPrintf #else /* not debug : */ #if defined (__GNUC__) && !(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) #define DEBUG_Printf(fmt, args...) do {} while (0) #else #define DEBUG_Printf(...) do {} while (0) #endif #endif #endif /* __PRINTSYS_H */ engine/h2shared/progs.h000066400000000000000000000115551444734033100153400ustar00rootroot00000000000000/* progs.h -- PROGS structures, public functions and vars * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef HX2_PROGS_H #define HX2_PROGS_H #include "pr_comp.h" /* defs shared with qcc */ #include "progdefs.h" /* generated by program cdefs */ typedef union eval_s { string_t string; float _float; float vector[3]; func_t function; int _int; int edict; } eval_t; #define MAX_ENT_LEAFS 16 typedef struct edict_s { qboolean free; link_t area; /* linked to a division node or leaf */ int num_leafs; int leafnums[MAX_ENT_LEAFS]; entity_state_t baseline; float freetime; /* sv.time when the object was freed */ float alloctime; /* sv.time when the object was allocated */ entvars_t v; /* C exported fields from progs */ /* other fields from progs come immediately after */ } edict_t; #define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area) //============================================================================ extern dprograms_t *progs; extern dfunction_t *pr_functions; extern dstatement_t *pr_statements; extern sv_globals_t sv_globals; extern float *pr_globals; /* same as sv_globals */ extern int pr_edict_size; /* in bytes */ extern qboolean is_progs_v6; /* If USE_MULTIPLE_PROGS is defined as 1, Hexen II will look for a file * named "maplist.txt" in its searchpath and load a map-specific progs * file using the information therein. The boss levels used this trick * in original Hexen II. The Portal of Praevus expansion and HexenWorld * do not do this, as they use a combined single progs.dat. * When progs.dat exists (it usually does), uHexen2 accepts maplist.txt * only if it is from the same game directory as progs.dat itself or if * it is from a searchpath with a higher priority. */ #define USE_MULTIPLE_PROGS 1 /* USE_MULTIPLE_PROGS is only for original hexen2. */ #if defined(H2W) #undef USE_MULTIPLE_PROGS #define USE_MULTIPLE_PROGS 0 #endif void PR_Init (void); void PR_ExecuteProgram (func_t fnum); void PR_LoadProgs (void); const char *PR_GetString (int num); int PR_SetEngineString (const char *s); int PR_AllocString (int bufferlength, char **ptr); void PR_Profile_f (void); edict_t *ED_Alloc (void); edict_t *ED_Alloc_Temp (void); void ED_Free (edict_t *ed); void ED_ClearEdict (edict_t *e); void ED_Print (edict_t *ed); void ED_Write (FILE *f, edict_t *ed); const char *ED_ParseEdict (const char *data, edict_t *ent); void ED_WriteGlobals (FILE *f); void ED_ParseGlobals (const char *data); void ED_LoadFromFile (const char *data); /* #define EDICT_NUM(n) ((edict_t *)(sv.edicts+ (n)*pr_edict_size)) #define NUM_FOR_EDICT(e) (((byte *)(e) - sv.edicts) / pr_edict_size) */ edict_t *EDICT_NUM(int); int NUM_FOR_EDICT(edict_t*); #define NEXT_EDICT(e) ((edict_t *)( (byte *)e + pr_edict_size)) #define EDICT_TO_PROG(e) ((byte *)e - (byte *)sv.edicts) #define PROG_TO_EDICT(e) ((edict_t *)((byte *)sv.edicts + e)) #define G_FLOAT(o) (pr_globals[o]) #define G_INT(o) (*(int *)&pr_globals[o]) #define G_EDICT(o) ((edict_t *)((byte *)sv.edicts+ *(int *)&pr_globals[o])) #define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o)) #define G_VECTOR(o) (&pr_globals[o]) #define G_STRING(o) (PR_GetString(*(string_t *)&pr_globals[o])) #define G_FUNCTION(o) (*(func_t *)&pr_globals[o]) #define E_FLOAT(e,o) (((float*)&e->v)[o]) #define E_INT(e,o) (*(int *)&((float*)&e->v)[o]) #define E_VECTOR(e,o) (&((float*)&e->v)[o]) #define E_STRING(e,o) (PR_GetString(*(string_t *)&((float*)&e->v)[o])) typedef void (*builtin_t) (void); extern const builtin_t *pr_builtins; extern const int pr_numbuiltins; extern int pr_argc; extern qboolean pr_trace; extern dfunction_t *pr_xfunction; extern int pr_xstatement; extern unsigned short pr_crc; #ifdef H2W extern func_t SpectatorConnect; extern func_t SpectatorThink; extern func_t SpectatorDisconnect; #endif FUNC_NORETURN void PR_RunError(const char *error, ...) FUNC_PRINTF(1,2); #ifdef __WATCOMC__ #pragma aux PR_RunError aborts; #endif void ED_PrintEdicts (void); void ED_PrintNum (int ent); eval_t *GetEdictFieldValue(edict_t *ed, const char *field); extern cvar_t max_temp_edicts; extern qboolean ignore_precache; #endif /* HX2_PROGS_H */ engine/h2shared/q_sound.h000066400000000000000000000117451444734033100156570ustar00rootroot00000000000000/* * q_sound.h -- client sound i/o functions. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_SOUND_H #define __HX2_SOUND_H /* !!! if this is changed, it must be changed in asm_i386.h too !!! */ typedef struct { int left; int right; } portable_samplepair_t; typedef struct sfx_s { char name[MAX_QPATH]; cache_user_t cache; } sfx_t; /* !!! if this is changed, it must be changed in asm_i386.h too !!! */ typedef struct { int length; int loopstart; int speed; int width; int stereo; byte data[1]; /* variable sized */ } sfxcache_t; typedef struct { int channels; int samples; /* mono samples in buffer */ int submission_chunk; /* don't mix less than this # */ int samplepos; /* in mono samples */ int samplebits; int signed8; /* device opened for S8 format? (e.g. Amiga AHI) */ int speed; unsigned char *buffer; } dma_t; /* !!! if this is changed, it must be changed in asm_i386.h too !!! */ typedef struct { sfx_t *sfx; /* sfx number */ int leftvol; /* 0-255 volume */ int rightvol; /* 0-255 volume */ int end; /* end time in global paintsamples */ int pos; /* sample position in sfx */ // int looping; /* where to loop, -1 = no looping */ int entnum; /* to allow overriding a specific sound */ int entchannel; vec3_t origin; /* origin of sound effect */ vec_t dist_mult; /* distance multiplier (attenuation/clipK) */ int master_vol; /* 0-255 master volume */ } channel_t; #define WAV_FORMAT_PCM 1 typedef struct { int rate; int width; int channels; int loopstart; int samples; int dataofs; /* chunk starts this many bytes from file start */ } wavinfo_t; void S_Init (void); void S_Startup (void); void S_Shutdown (void); void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation); void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation); void S_StopSound (int entnum, int entchannel); void S_UpdateSoundPos (int entnum, int entchannel, vec3_t origin); void S_StopAllSounds(qboolean clear); void S_ClearBuffer (void); void S_Update (vec3_t origin, vec3_t forward, vec3_t right, vec3_t up); void S_ExtraUpdate (void); void S_BlockSound (void); void S_UnblockSound (void); sfx_t *S_PrecacheSound (const char *sample); void S_TouchSound (const char *sample); void S_ClearPrecache (void); void S_BeginPrecaching (void); void S_EndPrecaching (void); void S_PaintChannels (int endtime); void S_InitPaintChannels (void); /* picks a channel based on priorities, empty slots, number of channels */ channel_t *SND_PickChannel (int entnum, int entchannel); /* spatializes a channel */ void SND_Spatialize (channel_t *ch); /* music stream support */ void S_RawSamples(int samples, int rate, int width, int channels, byte * data, float volume); /* Expects data in signed 16 bit, or unsigned 8 bit format. */ /* ==================================================================== * User-setable variables * ==================================================================== */ #define MAX_CHANNELS 128 #define MAX_DYNAMIC_CHANNELS 8 extern channel_t snd_channels[MAX_CHANNELS]; /* 0 to MAX_DYNAMIC_CHANNELS-1 = normal entity sounds * MAX_DYNAMIC_CHANNELS to MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS -1 = water, etc * MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS to total_channels = static sounds */ extern volatile dma_t *shm; extern const int MAX_TRYRATES; extern const int tryrates[]; extern int desired_bits, desired_speed, desired_channels; extern int total_channels; extern int soundtime; extern int paintedtime; extern int s_rawend; extern vec3_t listener_origin; extern vec3_t listener_forward; extern vec3_t listener_right; extern vec3_t listener_up; extern cvar_t sfxvolume; extern cvar_t loadas8bit; #define MAX_RAW_SAMPLES 8192 extern portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES]; extern cvar_t bgmtype; extern cvar_t bgmvolume; void S_LocalSound (const char *name); sfxcache_t *S_LoadSound (sfx_t *s); wavinfo_t GetWavinfo (const char *name, byte *wav, int wavlength); void SND_InitScaletable (void); ASM_LINKAGE_BEGIN #if id386 void Snd_WriteLinearBlastStereo16 (void); void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime); #endif #if id68k void S_TransferStereoAmiga (int endtime); #endif ASM_LINKAGE_END #endif /* __HX2_SOUND_H */ engine/h2shared/quakefs.c000066400000000000000000001237141444734033100156410ustar00rootroot00000000000000/* * quakefs.c -- Hexen II filesystem * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "pakfile.h" #include #ifdef PLATFORM_WINDOWS #include #endif #include "filenames.h" #include "hashindex.h" typedef struct { char name[MAX_QPATH]; int filepos, filelen; } pakfiles_t; typedef struct pack_s { char filename[MAX_OSPATH]; FILE *handle; int numfiles; pakfiles_t *files; hashindex_t hash; } pack_t; typedef struct searchpath_s { unsigned int path_id; /* identifier assigned to the game directory * Note that /game1 and * /game1 have the same id. */ char filename[MAX_OSPATH]; struct pack_s *pack; /* only one of filename / pack will be used */ struct searchpath_s *next; } searchpath_t; static searchpath_t *fs_searchpaths; static searchpath_t *fs_base_searchpaths; /* without gamedirs */ static const char *fs_basedir; static char fs_gamedir[MAX_OSPATH]; static char fs_userdir[MAX_OSPATH]; char fs_gamedir_nopath[MAX_QPATH]; unsigned int gameflags; cvar_t oem = {"oem", "0", CVAR_ROM}; cvar_t registered = {"registered", "0", CVAR_ROM}; typedef struct { int numfiles; unsigned int crc; long size; const char *dirname; } pakdata_t; #define MAX_PAKDATA 5 /* pak0...4 */ static pakdata_t pakdata[MAX_PAKDATA] = { { 696, 34289, 22704056, "data1" }, /* pak0.pak, registered, up-to-date, v1.11 * MD5: c9675191e75dd25a3b9ed81ee7e05eff */ { 523, 2995 , 75601170, "data1" }, /* pak1.pak, registered, up-to-date, v1.11 * MD5: c2ac5b0640773eed9ebe1cda2eca2ad0 */ { 183, 4807 , 17742721, "data1" }, /* pak2.pak, oem (Matrox m3D bundle) v1.10 * MD5: 99e0054861e94f66fc8e0e29416859c9 */ { 245, 1478 , 49089114, "portals" }, /* pak3.pak, Portal of Praevus expansion pack * MD5: 77ae298dd0dcd16ab12f4a68067ff2c3 */ { 102, 41062, 10780245, "hw" } /* pak4.pak, hexenworld, versions 0.14 - 0.15 * MD5: 88109ee385d9723ac5f1015e034a44dd */ }; static pakdata_t demo_pakdata[] = { { 797, 22780, 27750257, "data1" } /* pak0.pak, demo v1.11 from Nov. 1997 * MD5: 8e598d82bf53436ed7a0e133aa4b9f09 */ }; static pakdata_t oem0_pakdata[] = /* Continent of Blackmarsh */ { { 697, 9787 , 22720659, "data1" } /* pak0.pak, oem (Matrox m3D bundle) v1.10 * MD5: 8c9c6118117baca7b9349d477403fcc0 */ }; static pakdata_t old_pakdata[] = { { 697, 53062, 21714275, "data1" }, /* pak0.pak, original cdrom (1.03) version * MD5: b53c9391d16134cb3baddc1085f18683 */ { 525, 47762, 76958474, "data1" }, /* pak1.pak, original cdrom (1.03) version * MD5: 9a2010aafb9c0fe71c37d01292030270 */ { 701, 20870, 23537707, "data1" }, /* pak0.pak, original demo v0.42 from Aug. 1997 * (h2.exe -> console -> version says 1.07!) * MD5: 208643a09193dafbca4b851762479438 */ /* !!! FIXME: I don't have the original v1.08 of Continent of Blackmarsh. I only know the file sizes. */ { -1, 0, 22719295, "data1" }, /* pak0.pak, original oem (Matrox m3D) v1.08 * MD5: ???????????????????????????????? */ { -1, 0, 17739969, "data1" }, /* pak2.pak, original oem (Matrox m3D) v1.08 * MD5: ???????????????????????????????? */ { 98, 25864, 10678369, "hw" }, /* pak4.pak, Hexen2World v0.11 (ugh..) * MD5: c311a30ac8ee1f112019723b4fe42268 */ { 40, 48258, 3357888, "hw" }, /* pak4.pak, Hexen2World v0.09 (ugh ugh ugh!) * MD5: 7708da4323f668cf9c71f99315704baa */ }; /* this graphic needs to be in the pak file to use registered features */ static const unsigned short pop[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6600, 0x0000, 0x0000, 0x0000, 0x6600, 0x0000, 0x0000, 0x0066, 0x0000, 0x0000, 0x0000, 0x0000, 0x0067, 0x0000, 0x0000, 0x6665, 0x0000, 0x0000, 0x0000, 0x0000, 0x0065, 0x6600, 0x0063, 0x6561, 0x0000, 0x0000, 0x0000, 0x0000, 0x0061, 0x6563, 0x0064, 0x6561, 0x0000, 0x0000, 0x0000, 0x0000, 0x0061, 0x6564, 0x0064, 0x6564, 0x0000, 0x6469, 0x6969, 0x6400, 0x0064, 0x6564, 0x0063, 0x6568, 0x6200, 0x0064, 0x6864, 0x0000, 0x6268, 0x6563, 0x0000, 0x6567, 0x6963, 0x0064, 0x6764, 0x0063, 0x6967, 0x6500, 0x0000, 0x6266, 0x6769, 0x6a68, 0x6768, 0x6a69, 0x6766, 0x6200, 0x0000, 0x0062, 0x6566, 0x6666, 0x6666, 0x6666, 0x6562, 0x0000, 0x0000, 0x0000, 0x0062, 0x6364, 0x6664, 0x6362, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0062, 0x6662, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0061, 0x6661, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6500, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6400, 0x0000, 0x0000, 0x0000 }; static char *FSERR_MakePath_BUF (const char *caller, int linenum, int base, char *buf, size_t siz, const char *path); static char *FSERR_MakePath_VABUF (const char *caller, int linenum, int base, char *buf, size_t siz, const char *format, ...) FUNC_PRINTF(6,7); static char *do_MakePath_VA (int base, int *error, char *buf, size_t siz, const char *format, va_list args) FUNC_PRINTF(5,0); /* All of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources. The "base directory" is the path to the directory holding the exe and all game directories. The sys_* files pass this to host_init in quakeparms_t->basedir. This can be overridden with the "-basedir" command line parm to allow code debugging in a different directory. The base directory is only used during filesystem initialization. The "game directory" is the first tree on the search path and directory that all generated files (savegames, screenshots, demos, config files) will be saved to. This can be overridden with the "-game" command line parameter. The game directory can never be changed while quake is executing. This is a precacution against having a malicious server instruct clients to write files over areas they shouldn't. */ static unsigned int check_known_paks (int paknum, int numfiles, unsigned short crc) { if (paknum >= MAX_PAKDATA) return GAME_MODIFIED; if (strcmp(fs_gamedir_nopath, pakdata[paknum].dirname) != 0) return GAME_MODIFIED; /* Raven didn't ship like that */ if (numfiles != pakdata[paknum].numfiles) { switch (paknum) { case 0: /* demo ?? */ if (numfiles == demo_pakdata[0].numfiles && crc == demo_pakdata[0].crc) return GAME_DEMO; /* oem ?? */ if (numfiles == oem0_pakdata[0].numfiles && crc == oem0_pakdata[0].crc) return GAME_OEM0; /* old version of demo ?? */ if (numfiles == old_pakdata[2].numfiles && crc == old_pakdata[2].crc) return GAME_OLD_DEMO; /* old cdrom version ?? */ if (numfiles == old_pakdata[0].numfiles && crc == old_pakdata[0].crc) return GAME_OLD_CDROM0; /* old oem version ?? */ if (numfiles == old_pakdata[3].numfiles && crc == old_pakdata[3].crc) return GAME_OLD_OEM0; /* not original: */ return GAME_MODIFIED; case 1: /* old cdrom version ?? */ if (numfiles == old_pakdata[1].numfiles && crc == old_pakdata[1].crc) return GAME_OLD_CDROM1; /* not original: */ return GAME_MODIFIED; case 2: /* old oem version ?? */ if (numfiles == old_pakdata[4].numfiles && crc == old_pakdata[4].crc) return GAME_OLD_OEM2; /* not original: */ return GAME_MODIFIED; case 4: /* old HW version ?? */ if (numfiles == old_pakdata[5].numfiles && crc == old_pakdata[5].crc) return GAME_HEXENWORLD; /* not original: */ return GAME_MODIFIED; default:/* not original */ return GAME_MODIFIED; } } if (crc != pakdata[paknum].crc) return GAME_MODIFIED; /* not original */ /* both crc and numfiles are good, we are still original */ switch (paknum) { case 0: /* pak0 of full version 1.11 */ return GAME_REGISTERED0; case 1: /* pak1 of full version 1.11 */ return GAME_REGISTERED1; case 2: /* bundle version */ return GAME_OEM2; case 3: /* mission pack */ return GAME_PORTALS; case 4: /* hexenworld */ return GAME_HEXENWORLD; } return GAME_MODIFIED; /* we shouldn't reach here */ } /* ================= FS_LoadPackFile Takes a path to a pak file. Loads the header and directory. ================= */ static pack_t *FS_LoadPackFile (const char *packfile, int paknum, qboolean base_fs) { dpackheader_t header; int i, numpackfiles, key; pakfiles_t *newfiles; pack_t *pack; FILE *packhandle; dpackfile_t info[MAX_FILES_IN_PACK]; unsigned short crc; packhandle = fopen (packfile, "rb"); if (!packhandle) return NULL; fread (&header, 1, sizeof(header), packhandle); if (header.id[0] != 'P' || header.id[1] != 'A' || header.id[2] != 'C' || header.id[3] != 'K') { Sys_Printf ("WARNING: %s is not a packfile, ignored\n", packfile); goto pak_error; } header.dirofs = LittleLong (header.dirofs); header.dirlen = LittleLong (header.dirlen); numpackfiles = header.dirlen / sizeof(dpackfile_t); if (header.dirlen < 0 || header.dirofs < 0) { Sys_Error ("Invalid packfile %s (dirlen: %i, dirofs: %i)", packfile, header.dirlen, header.dirofs); } if (!numpackfiles) { Sys_Printf ("WARNING: %s has no files, ignored\n", packfile); goto pak_error; } if (numpackfiles > MAX_FILES_IN_PACK) { Sys_Printf ("WARNING: %s has %i files (max. allowed is %i), ignored\n", packfile, numpackfiles, MAX_FILES_IN_PACK); goto pak_error; } newfiles = (pakfiles_t *) Z_Malloc (numpackfiles * sizeof(pakfiles_t), Z_MAINZONE); fseek (packhandle, header.dirofs, SEEK_SET); fread (info, 1, header.dirlen, packhandle); /* crc the directory */ CRC_Init (&crc); for (i = 0; i < header.dirlen; i++) CRC_ProcessByte (&crc, ((byte *)info)[i]); /* check for modifications */ if (base_fs) gameflags |= check_known_paks (paknum, numpackfiles, crc); else gameflags |= GAME_MODIFIED; pack = (pack_t *) Z_Malloc (sizeof(pack_t), Z_MAINZONE); /* get the hash table size from the number of files in the pak */ for (i = 1; i < MAX_FILES_IN_PACK; i <<= 1) { if (i > numpackfiles) break; } Hash_Allocate(&pack->hash, i); /* parse the directory */ for (i = 0; i < numpackfiles; i++) { qerr_strlcpy(__thisfunc__, __LINE__, newfiles[i].name, info[i].name, MAX_QPATH); newfiles[i].filepos = LittleLong(info[i].filepos); newfiles[i].filelen = LittleLong(info[i].filelen); key = Hash_GenerateKeyString (&pack->hash, newfiles[i].name, true); Hash_Add (&pack->hash, key, i); } qerr_strlcpy(__thisfunc__, __LINE__, pack->filename, packfile, MAX_OSPATH); pack->handle = packhandle; pack->numfiles = numpackfiles; pack->files = newfiles; Sys_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles); return pack; pak_error: fclose (packhandle); return NULL; } /* ================ FS_AddGameDirectory Sets fs_gamedir, fs_userdir and fs_gamedir_nopath. adds the directory to the head of the path, then loads and adds pak1.pak pak2.pak ... ================ */ static void FS_AddGameDirectory (const char *dir, qboolean base_fs) { unsigned int path_id; searchpath_t *search; pack_t *pak; char pakfile[MAX_OSPATH]; qboolean do_userdir = false; int i; qerr_strlcpy(__thisfunc__, __LINE__, fs_gamedir_nopath, dir, sizeof(fs_gamedir_nopath)); FSERR_MakePath_BUF (__thisfunc__, __LINE__, FS_BASEDIR, fs_gamedir, sizeof(fs_gamedir), dir); FSERR_MakePath_BUF (__thisfunc__, __LINE__, FS_USERBASE, fs_userdir, sizeof(fs_userdir), dir); /* assign a path_id to this game directory */ if (fs_searchpaths) path_id = fs_searchpaths->path_id << 1; else path_id = 1U; #if DO_USERDIRS add_pakfile: #endif /* add any pak files in the format pak0.pak pak1.pak, ... * unlike Quake, Hexen II can't stop at first unavailable * pak: the mission pack has only pak3, hw has only pak4. */ for (i = 0; i < 10; i++) { FSERR_MakePath_VABUF (__thisfunc__, __LINE__, (do_userdir) ? FS_USERDIR : FS_GAMEDIR, pakfile, sizeof(pakfile), "pak%i.pak", i); pak = FS_LoadPackFile (pakfile, i, base_fs); if (!pak) continue; search = (searchpath_t *) Z_Malloc (sizeof(searchpath_t), Z_MAINZONE); search->path_id = path_id; search->pack = pak; search->next = fs_searchpaths; fs_searchpaths = search; } /* add the directory itself to the search path. unlike Quake, * Hexen II does this ~after~ adding the pakfiles in this dir, * so that the dir itself will be placed above the pakfiles in * the search order which, in turn, will allow override files. */ search = (searchpath_t *) Z_Malloc (sizeof(searchpath_t), Z_MAINZONE); if (do_userdir) qerr_strlcpy(__thisfunc__, __LINE__, search->filename, fs_userdir, MAX_OSPATH); else qerr_strlcpy(__thisfunc__, __LINE__, search->filename, fs_gamedir, MAX_OSPATH); search->path_id = path_id; search->next = fs_searchpaths; fs_searchpaths = search; if (do_userdir) return; do_userdir = true; #if DO_USERDIRS /* add user's directory to the search path and * add any pak files in the user's directory. */ if (strcmp(fs_gamedir, fs_userdir)) { Sys_mkdir (fs_userdir, true); goto add_pakfile; } #endif } /* ================ FS_Gamedir Sets the gamedir and path to a different directory. Hexen II uses this for setting the game directory from a -game command line argument. HexenWorld uses this to set the gamedir on both server and client sides while the game is running: The client calls this upon every map change from within CL_ParseServerData(), and the Server calls this upon a gamedir command from within SV_Gamedir_f(). ================ */ #ifdef H2W static inline void set_hw_dir (void) { /* helper for FS_Gamedir () */ qerr_strlcpy(__thisfunc__, __LINE__, fs_gamedir_nopath, "hw", sizeof(fs_gamedir_nopath)); FSERR_MakePath_BUF (__thisfunc__, __LINE__, FS_BASEDIR, fs_gamedir, sizeof(fs_gamedir), "hw"); FSERR_MakePath_BUF (__thisfunc__, __LINE__, FS_USERBASE, fs_userdir, sizeof(fs_userdir), "hw"); #ifdef SERVERONLY /* change the *gamedir serverinfo properly */ Info_SetValueForStarKey (svs.info, "*gamedir", "hw", MAX_SERVERINFO_STRING); #endif } #endif void FS_Gamedir (const char *dir) { searchpath_t *next; if (!*dir || !strcmp(dir, ".") || strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\") || strstr(dir, ":")) { if (!host_initialized) Sys_Error ("gamedir should be a single directory name, not a path\n"); else { Con_Printf("gamedir should be a single directory name, not a path\n"); return; } } if (!q_strcasecmp(fs_gamedir_nopath, dir)) return; /* still the same */ /* free up any current game dir info: our top searchpath dir will be hw * and any gamedirs set before by this very procedure will be removed. * since hexen2 doesn't use this during game execution there will be no * changes for it: it has portals or data1 at the top. */ while (fs_searchpaths != fs_base_searchpaths) { if (fs_searchpaths->pack) { fclose (fs_searchpaths->pack->handle); Z_Free (fs_searchpaths->pack->files); Hash_Free(&fs_searchpaths->pack->hash); Z_Free (fs_searchpaths->pack); } next = fs_searchpaths->next; Z_Free (fs_searchpaths); fs_searchpaths = next; } /* flush all data, so it will be forced to reload */ #if !defined(SERVERONLY) Cache_Flush (); #endif /* check for reserved gamedirs */ if (!q_strcasecmp(dir, "hw")) { #if defined(H2W) /* that we reached here means the hw server decided to abandon * whatever the previous mod it was running and went back to * pure hw. weird.. do as he wishes anyway and adjust our variables. */ set_hw_dir (); #else /* hexen2 case: */ /* hw is reserved for hexenworld only. hexen2 shouldn't use it */ Con_Printf ("WARNING: Gamedir not set to hw :\n" "It is reserved for HexenWorld.\n"); #endif /* H2W */ return; } if (!q_strcasecmp(dir, "portals")) { /* no hw server is supposed to set gamedir to portals * and hw must be above portals in hierarchy. this is * actually a hypothetical case. * as for hexen2, it cannot reach here. */ #ifdef H2W set_hw_dir (); #endif return; } if (!q_strcasecmp(dir, "data1")) { /* another hypothetical case: no hw mod is supposed to * do this and hw must stay above data1 in hierarchy. * as for hexen2, it can only reach here by a silly * command line argument like -game data1, ignore it. */ #ifdef H2W set_hw_dir (); #endif return; } /* a new gamedir: let's set it here. */ FS_AddGameDirectory(dir, false); #if defined(H2W) && defined(SERVERONLY) /* change the *gamedir serverinfo properly */ Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING); #endif } /* ============================================================================== FILE I/O within QFS ============================================================================== */ long fs_filesize; /* size of the last file opened through QFS */ int file_from_pak; /* ZOID: global indicating that file came from a pak */ /* =========== FS_CopyFile Copies the FROMPATH file as TOPATH file, creating any dirs needed. Used for saving the game. Returns 0 on success, non-zero on error. =========== */ int FS_CopyFile (const char *frompath, const char *topath) { char *tmp; int err; if (!frompath || !topath) { Con_Printf ("%s: null input\n", __thisfunc__); return 1; } /* create directories up to the dest file */ tmp = Z_Strdup (topath); err = FS_CreatePath(tmp); Z_Free (tmp); if (err != 0) { Con_Printf ("%s: unable to create directory\n", __thisfunc__); return err; } err = Sys_CopyFile (frompath, topath); return err; } #define COPY_READ_BUFSIZE 8192 /* BUFSIZ */ int FS_WriteFileFromHandle (FILE *fromfile, const char *topath, size_t size) { char buf[COPY_READ_BUFSIZE]; FILE *out; /* off_t remaining, count;*/ size_t remaining, count; char *tmp; int err; if (!fromfile || !topath) { Con_Printf ("%s: null input\n", __thisfunc__); return 1; } /* create directories up to the dest file */ tmp = Z_Strdup (topath); err = FS_CreatePath(tmp); Z_Free (tmp); if (err != 0) { Con_Printf ("%s: unable to create directory\n", __thisfunc__); return err; } out = fopen (topath, "wb"); if (!out) { Con_Printf ("%s: unable to create %s\n", topath, __thisfunc__); return 1; } remaining = size; while (remaining) { if (remaining < sizeof(buf)) count = remaining; else count = sizeof(buf); if (fread(buf, 1, count, fromfile) != count) break; if (fwrite(buf, 1, count, out) != count) break; remaining -= count; } fclose (out); return (remaining == 0)? 0 : 1; } /* ============ FS_WriteFile The filename will be prefixed by the current game directory Returns 0 on success, 1 on error. ============ */ int FS_WriteFile (const char *filename, const void *data, size_t len) { FILE *f; char name[MAX_OSPATH]; size_t size; int err; FS_MakePath_BUF (FS_USERDIR, &err, name, sizeof(name), filename); if (err) { Host_Error("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return 1; } f = fopen (name, "wb"); if (!f) { Con_Printf ("Error opening %s\n", filename); return 1; } Sys_Printf ("%s: %s\n", __thisfunc__, name); size = fwrite (data, 1, len, f); fclose (f); if (size != len) { Con_Printf ("Error in writing %s\n", filename); return 1; } return 0; } /* ============ FS_CreatePath Creates directory under user's path, making parent directories as needed. The path must either be a path to a file, or, if the full path is meant to be created, it must have the trailing path seperator. Returns 0 on success, non-zero on error. ============ */ int FS_CreatePath (char *path) { char *ofs, c; int err = 0; size_t offset; if (!path || !*path) { Con_Printf ("%s: no path!\n", __thisfunc__); return 1; } if (strstr(path, "..")) { Con_Printf ("Relative pathnames are not allowed.\n"); return 1; } offset = strlen(host_parms->userdir); if (offset && strstr(path, host_parms->userdir) != path) { Sys_Error ("Attempted to create a directory out of user's path"); return 1; } ofs = path + offset; if (*ofs == '\0') /* not necessarily an error. */ return 0; /* check for the path separator after the userdir. */ if (IS_DIR_SEPARATOR(*ofs)) ofs++; else if (offset) { /* if the userdir itself has no trailing DIRSEP * either, then it is a bad path: */ if (!IS_DIR_SEPARATOR(host_parms->userdir[offset - 1])) { Con_Printf ("%s: bad path\n", __thisfunc__); return 1; } } for ( ; *ofs; ofs++) { c = *ofs; if (IS_DIR_SEPARATOR(c)) { /* create the directory */ *ofs = 0; err = Sys_mkdir (path, false); *ofs = c; if (err) break; } } return err; } /* =========== FS_OpenFile Finds the file in the search path, returns fs_filesize. =========== */ long FS_OpenFile (const char *filename, FILE **file, unsigned int *path_id) { searchpath_t *search; pack_t *pak; char ospath[MAX_OSPATH]; int i, key; file_from_pak = 0; /* search through the path, one element at a time */ for (search = fs_searchpaths ; search ; search = search->next) { if (search->pack) /* look through all the pak file elements */ { pak = search->pack; key = Hash_GenerateKeyString (&pak->hash, filename, true); for (i = Hash_First(&pak->hash, key); i != -1; i = Hash_Next(&pak->hash, i)) { if (strcmp(pak->files[i].name, filename) != 0) continue; /* found it! */ fs_filesize = pak->files[i].filelen; file_from_pak = 1; if (path_id) *path_id = search->path_id; if (!file) /* for FS_FileExists() */ return fs_filesize; /* open a new file on the pakfile */ *file = fopen (pak->filename, "rb"); if (!*file) Sys_Error ("Couldn't reopen %s", pak->filename); fseek (*file, pak->files[i].filepos, SEEK_SET); return fs_filesize; } } else /* check a file in the directory tree */ { q_snprintf (ospath, sizeof(ospath), "%s/%s",search->filename, filename); fs_filesize = Sys_filesize (ospath); if (fs_filesize < 0) continue; if (path_id) *path_id = search->path_id; if (!file) /* for FS_FileExists() */ return fs_filesize; *file = fopen (ospath, "rb"); if (!*file) Sys_Error ("Couldn't reopen %s", ospath); return fs_filesize; } } Sys_DPrintf ("%s: can't find %s\n", __thisfunc__, filename); if (file) *file = NULL; fs_filesize = -1; return fs_filesize; } /* =========== FS_FileExists Returns whether the file is found in the hexen2 filesystem. =========== */ qboolean FS_FileExists (const char *filename, unsigned int *path_id) { long ret = FS_OpenFile(filename, NULL, path_id); return (ret < 0) ? false : true; } /* ============ FS_FileInGamedir Reports the existance of a file with read permissions in fs_gamedir or fs_userdir. *NOT* for files in pakfiles! ============ */ qboolean FS_FileInGamedir (const char *filename) { int ret; ret = Sys_FileType(FS_MakePath(FS_USERDIR, NULL, filename)); if (ret & FS_ENT_FILE) return true; ret = Sys_FileType(FS_MakePath(FS_GAMEDIR, NULL, filename)); if (ret & FS_ENT_FILE) return true; return false; } /* ============ FS_LoadFile Filename are reletive to the quake directory. Allways appends a 0 byte to the loaded data. ============ */ #define LOADFILE_ZONE 0 #define LOADFILE_HUNK 1 #define LOADFILE_TEMPHUNK 2 #define LOADFILE_CACHE 3 #define LOADFILE_STACK 4 #define LOADFILE_MALLOC 5 static byte *loadbuf; #if !defined(SERVERONLY) static cache_user_t *loadcache; #endif static long loadsize; static int zone_num; #if defined (SERVERONLY) #define Draw_BeginDisc() #define Draw_EndDisc() #endif static byte *FS_LoadFile (const char *path, int usehunk, unsigned int *path_id) { FILE *h; byte *buf; char base[32]; long len; /* look for it in the filesystem or pack files */ len = FS_OpenFile (path, &h, path_id); if (len < 0) return NULL; /* extract the file's base name for hunk tag */ COM_FileBase (path, base, sizeof(base)); buf = NULL; /* quiet compiler warning */ switch (usehunk) { case LOADFILE_HUNK: buf = (byte *) Hunk_AllocName (len+1, base); break; case LOADFILE_TEMPHUNK: buf = (byte *) Hunk_TempAlloc (len+1); break; case LOADFILE_ZONE: buf = (byte *) Z_Malloc (len+1, zone_num); break; #if !defined(SERVERONLY) case LOADFILE_CACHE: buf = (byte *) Cache_Alloc (loadcache, len+1, base); break; #endif case LOADFILE_STACK: if (len < loadsize) buf = loadbuf; else buf = (byte *) Hunk_TempAlloc (len+1); break; case LOADFILE_MALLOC: buf = (byte *) malloc (len+1); break; default: Sys_Error ("%s: bad usehunk", __thisfunc__); } if (!buf) Sys_Error ("%s: not enough space for %s", __thisfunc__, path); ((byte *)buf)[len] = 0; Draw_BeginDisc (); fread (buf, 1, (size_t)len, h); fclose (h); Draw_EndDisc (); return buf; } byte *FS_LoadHunkFile (const char *path, unsigned int *path_id) { return FS_LoadFile (path, LOADFILE_HUNK, path_id); } byte *FS_LoadZoneFile (const char *path, int zone_id, unsigned int *path_id) { zone_num = zone_id; return FS_LoadFile (path, LOADFILE_ZONE, path_id); } byte *FS_LoadTempFile (const char *path, unsigned int *path_id) { return FS_LoadFile (path, LOADFILE_TEMPHUNK, path_id); } #if !defined(SERVERONLY) void FS_LoadCacheFile (const char *path, struct cache_user_s *cu, unsigned int *path_id) { loadcache = cu; FS_LoadFile (path, LOADFILE_CACHE, path_id); } #endif /* uses temp hunk if larger than bufsize */ byte *FS_LoadStackFile (const char *path, void *buffer, long bufsize, unsigned int *path_id) { byte *buf; loadbuf = (byte *)buffer; loadsize = bufsize; buf = FS_LoadFile (path, LOADFILE_STACK, path_id); return buf; } /* returns malloc'd memory */ byte *FS_LoadMallocFile (const char *path, unsigned int *path_id) { return FS_LoadFile (path, LOADFILE_MALLOC, path_id); } /* ============================================================================== MISC CONSOLE COMMANDS ============================================================================== */ /* ============ FS_Path_f Prints the search path to the console ============ */ static void FS_Path_f (void) { searchpath_t *s; Con_Printf ("Current search path:\n"); for (s = fs_searchpaths ; s ; s = s->next) { if (s == fs_base_searchpaths) Con_Printf ("----------\n"); if (s->pack) Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles); else Con_Printf ("%s\n", s->filename); } } /* =========== processMapname: Callback for FS_Maplist_f. Returns 0 if a name is skipped, the current number of names added to the list if the name is added, or -1 upon failures. =========== */ #if !defined(SERVERONLY) /* dedicated servers dont need this command */ #define MAX_NUMMAPS 256 /* max number of maps to list */ static int map_count = 0; static char *maplist[MAX_NUMMAPS]; static int processMapname (const char *mapname, const char *partial, size_t len_partial) { char cur_name[MAX_QPATH]; int j, len; if (map_count >= MAX_NUMMAPS) { Con_Printf ("WARNING: reached maximum number of maps to list\n"); return -1; } if (len_partial) { if (q_strncasecmp(partial, mapname, len_partial) != 0) return 0; /* doesn't match the prefix. skip. */ } len = q_strlcpy (cur_name, mapname, sizeof(cur_name)) - 4; /* -4 to kill ".bsp" */ if (len <= 0) return 0; if (q_strcasecmp(&cur_name[len], ".bsp") != 0) return 0; cur_name[len] = 0; for (j = 0; j < map_count; j++) { if (! q_strcasecmp(maplist[j], cur_name)) return 0; /* duplicated name. skip. */ } /* add to the maplist */ maplist[map_count] = Z_Strdup (cur_name); return (++map_count); } /* =========== FS_Maplist_f Prints map filenames to the console =========== */ static void FS_Maplist_f (void) { searchpath_t *search; const char *prefix; size_t preLen; if (Cmd_Argc() > 1) { prefix = Cmd_Argv(1); preLen = strlen(prefix); } else { preLen = 0; prefix = NULL; } for (search = fs_searchpaths; search; search = search->next) { if (search->pack) { int i = 0; for (; i < search->pack->numfiles; ++i) { if (strncmp("maps/", search->pack->files[i].name, 5) != 0) continue; if (processMapname(search->pack->files[i].name + 5, prefix, preLen) < 0) goto done; } } else { const char *findname = Sys_FindFirstFile(va("%s/maps",search->filename), "*.bsp"); while (findname) { if (processMapname(findname, prefix, preLen) < 0) { Sys_FindClose (); goto done; } findname = Sys_FindNextFile (); } Sys_FindClose (); } } done: if (!map_count) { Con_Printf ("No maps found.\n\n"); return; } Con_Printf ("Found %d maps:\n\n", map_count); /* sort the list */ if (map_count > 1) qsort (maplist, map_count, sizeof(char *), COM_StrCompare); Con_ShowList (map_count, (const char**)maplist); Con_Printf ("\n"); /* free the memory and zero map_count */ while (map_count) Z_Free (maplist[--map_count]); } #endif /* SERVERONLY */ /* ============================================================================== INIT ============================================================================== */ /* ================ CheckRegistered Looks for the pop.txt file and verifies it. Sets the registered flag. ================ */ static int CheckRegistered (void) { FILE *h; unsigned short check[128]; int i; FS_OpenFile("gfx/pop.lmp", &h, NULL); if (!h) return -1; fread (check, 1, sizeof(check), h); fclose (h); for (i = 0; i < 128; i++) { if (pop[i] != (unsigned short)BigShort(check[i])) { Sys_Printf ("Corrupted data file\n"); return -1; } } return 0; } /* ================ FS_Init ================ */ void FS_Init (void) { qboolean check_portals = false; int i; Cvar_RegisterVariable (&oem); Cvar_RegisterVariable (®istered); Cmd_AddCommand ("path", FS_Path_f); #if !defined(SERVERONLY) Cmd_AddCommand ("maplist", FS_Maplist_f); #endif /* -basedir overrides the system supplied base directory */ i = COM_CheckParm ("-basedir"); if (i && i < com_argc-1) { fs_basedir = com_argv[i+1]; if (!*fs_basedir) Sys_Error("Bad argument to -basedir"); #if !DO_USERDIRS host_parms->userdir = com_argv[i+1]; #endif Sys_Printf ("%s: basedir changed to: %s\n", __thisfunc__, fs_basedir); } else { fs_basedir = host_parms->basedir; } /* step 1: start up with data1 by default */ FS_AddGameDirectory ("data1", true); if (gameflags & GAME_REGISTERED0 && gameflags & GAME_REGISTERED1) gameflags |= GAME_REGISTERED; if (gameflags & GAME_OEM0 && gameflags & GAME_OEM2) gameflags |= GAME_OEM; if (gameflags & GAME_OLD_CDROM0 && gameflags & GAME_OLD_CDROM1) gameflags |= GAME_REGISTERED_OLD; if (gameflags & GAME_OLD_OEM0 && gameflags & GAME_OLD_OEM2) gameflags |= GAME_OLD_OEM; /* check for bad installations (mix'n'match data): */ if ((gameflags & GAME_REGISTERED0 && gameflags & GAME_OLD_CDROM1) || (gameflags & GAME_REGISTERED1 && gameflags & GAME_OLD_CDROM0) || (gameflags & (GAME_OEM2|GAME_OLD_OEM2) && gameflags & (GAME_REGISTERED|GAME_REGISTERED_OLD|GAME_DEMO|GAME_OLD_DEMO)) || (gameflags & (GAME_REGISTERED1|GAME_OLD_CDROM1) && gameflags & (GAME_DEMO|GAME_OLD_DEMO|GAME_OEM0|GAME_OLD_OEM0|GAME_OEM2|GAME_OLD_OEM2))) { Sys_Error ("Bad Hexen II installation: mixed data from incompatible versions"); } #if !ENABLE_OLD_DEMO if (gameflags & GAME_OLD_DEMO) Sys_Error ("Old version of Hexen II demo isn't supported"); #endif /* OLD_DEMO */ #if !ENABLE_OLD_RETAIL /* check if we have 1.11 versions of pak0.pak and pak1.pak */ if (gameflags & (GAME_OLD_CDROM0|GAME_OLD_CDROM1|GAME_OLD_OEM0|GAME_OLD_OEM2)) Sys_Error ("You must patch your installation with Raven's 1.11 update"); #endif /* OLD_RETAIL */ /* finish the base filesystem setup */ if (gameflags & (GAME_REGISTERED|GAME_REGISTERED_OLD)) { Cvar_SetROM ("registered", "1"); Sys_Printf ("Playing the registered version.\n"); } else if (gameflags & GAME_OEM) { Cvar_SetROM ("oem", "1"); Sys_Printf ("Playing the oem (Matrox m3D bundle) version \"Continent of Blackmarsh\"\n"); } else if (gameflags & (GAME_DEMO|GAME_OLD_DEMO)) { Sys_Printf ("Playing the demo version.\n"); } else { /* no proper Raven data: it's best to error out here */ Sys_Error ("Unable to find a proper Hexen II installation"); } if (gameflags & (GAME_OLD_DEMO|GAME_REGISTERED_OLD|GAME_OLD_OEM)) Sys_Printf ("Using old/unsupported, pre-1.11 version pak files.\n"); if (gameflags & (GAME_REGISTERED|GAME_REGISTERED_OLD)) { if (CheckRegistered() != 0) Sys_Error ("Unable to verify retail version data."); } #if !(defined(H2W) && defined(SERVERONLY)) if (gameflags & GAME_MODIFIED && !(gameflags & (GAME_REGISTERED|GAME_REGISTERED_OLD))) Sys_Error ("You must have the full version of Hexen II to play modified games"); #endif /* step 2: portals directory (mission pack) */ #if defined(H2MP) if (! COM_CheckParm ("-noportals")) check_portals = true; if (check_portals && !(gameflags & (GAME_REGISTERED|GAME_REGISTERED_OLD))) Sys_Error ("Portal of Praevus requires registered version of Hexen II"); #elif defined(H2W) if (! COM_CheckParm ("-noportals") && gameflags & (GAME_REGISTERED|GAME_REGISTERED_OLD)) check_portals = true; #else /* see if the user wants mission pack support */ check_portals = (COM_CheckParm ("-portals")) || (COM_CheckParm ("-missionpack")) || (COM_CheckParm ("-h2mp")); i = COM_CheckParm ("-game"); if (i && i < com_argc-1) { if (!q_strcasecmp(com_argv[i+1], "portals")) check_portals = true; } if (check_portals && !(gameflags & (GAME_REGISTERED|GAME_REGISTERED_OLD))) Sys_Error ("Portal of Praevus requires registered version of Hexen II"); #endif #if !defined(H2W) if (sv_protocol == PROTOCOL_RAVEN_111 && check_portals) Sys_Error ("Old protocol request not compatible with the Mission Pack"); #endif if (check_portals) { #if defined(H2W) searchpath_t *mark = fs_searchpaths; #endif FS_AddGameDirectory ("portals", true); if (! (gameflags & GAME_PORTALS)) { #if !defined(H2W) Sys_Error ("Missing or invalid mission pack installation\n"); #else /* back out searchpaths from invalid mission pack * installations because the portals directory is * reserved for the mission pack */ searchpath_t *next; Sys_Printf ("Missing or invalid mission pack installation\n"); while (fs_searchpaths != mark) { if (fs_searchpaths->pack) { fclose (fs_searchpaths->pack->handle); Z_Free (fs_searchpaths->pack->files); Hash_Free(&fs_searchpaths->pack->hash); Z_Free (fs_searchpaths->pack); Sys_Printf ("Removed packfile %s\n", fs_searchpaths->pack->filename); } else { Sys_Printf ("Removed path %s\n", fs_searchpaths->filename); } next = fs_searchpaths->next; Z_Free (fs_searchpaths); fs_searchpaths = next; } fs_searchpaths = mark; /* back to data1 */ FS_MakePath_BUF (FS_BASEDIR, NULL, fs_gamedir, sizeof(fs_gamedir), "data1"); FS_MakePath_BUF (FS_USERBASE,NULL, fs_userdir, sizeof(fs_userdir), "data1"); #endif /* H2W */ } } /* step 3: hw directory (hexenworld) */ #if defined(H2W) FS_AddGameDirectory ("hw", true); /* error out if GAME_HEXENWORLD isn't set */ if (!(gameflags & GAME_HEXENWORLD)) Sys_Error ("You must have the HexenWorld data installed"); #endif /* H2W */ /* this is the end of our base searchpath: * any set gamedirs, such as those from -game commandline * arguments, from exec'ed configs or the ones dictated by * the server, will be freed up to here upon a new gamedir * command */ fs_base_searchpaths = fs_searchpaths; i = COM_CheckParm ("-game"); if (i != 0) { /* only registered versions can do -game */ if (! (gameflags & (GAME_REGISTERED|GAME_REGISTERED_OLD))) Sys_Error ("You must have the full version of Hexen II to play modified games"); /* add basedir/gamedir as an override game */ if (i < com_argc - 1) FS_Gamedir (com_argv[i+1]); } } #define FS_NUM_BUFFS 4 #define FS_BUFFERLEN 1024 static char *get_fs_buffer(void) { static char fs_buffers[FS_NUM_BUFFS][FS_BUFFERLEN]; static int buffer_idx = 0; buffer_idx = (buffer_idx + 1) & (FS_NUM_BUFFS - 1); return fs_buffers[buffer_idx]; } static int init_MakePath (int base, char *buf, size_t siz) { int len; switch (base) { case FS_USERDIR: len = q_strlcpy(buf, fs_userdir, siz); break; case FS_GAMEDIR: len = q_strlcpy(buf, fs_gamedir, siz); break; case FS_USERBASE: len = q_strlcpy(buf, host_parms->userdir, siz); break; case FS_BASEDIR: len = q_strlcpy(buf, fs_basedir, siz); break; default: Sys_Error ("%s: Bad FS_BASE", __thisfunc__); return -1; } if (len >= (int)siz - 1) return -1; if (len && !IS_DIR_SEPARATOR(buf[len - 1])) buf[len++] = DIR_SEPARATOR_CHAR; buf[len] = '\0'; return len; } static char *do_MakePath (int base, int *error, char *buf, size_t siz, const char *path) { int len; len = init_MakePath(base, buf, siz); if (len < 0) goto _bad; len = q_strlcat(buf, path, siz); if (len < (int)siz) { if (error) *error = 0; } else { _bad: if (error) *error = 1; Con_DPrintf("%s: overflow (string truncated)\n", __thisfunc__); } return buf; } static char *do_MakePath_VA (int base, int *error, char *buf, size_t siz, const char *format, va_list args) { int len, ret; len = init_MakePath(base, buf, siz); if (len < 0) goto _bad; ret = q_vsnprintf(&buf[len], siz - len, format, args); if (ret < (int)siz - len) { if (error) *error = 0; } else { _bad: if (error) *error = 1; Con_DPrintf("%s: overflow (string truncated)\n", __thisfunc__); } return buf; } static char *FSERR_MakePath_BUF (const char *caller, int linenum, int base, char *buf, size_t siz, const char *path) { int err; char *p; p = do_MakePath(base, &err, buf, siz, path); if (err) Sys_Error("%s: %d: string buffer overflow!", caller, linenum); return p; } static char *FSERR_MakePath_VABUF (const char *caller, int linenum, int base, char *buf, size_t siz, const char *format, ...) { va_list argptr; int err; char *p; va_start (argptr, format); p = do_MakePath_VA(base, &err, buf, siz, format, argptr); va_end (argptr); if (err) Sys_Error("%s: %d: string buffer overflow!", caller, linenum); return p; } char *FS_MakePath (int base, int *error, const char *path) { return do_MakePath(base, error, get_fs_buffer(), FS_BUFFERLEN, path); } char *FS_MakePath_BUF (int base, int *error, char *buf, size_t siz, const char *path) { return do_MakePath(base, error, buf, siz, path); } char *FS_MakePath_VA (int base, int *error, const char *format, ...) { va_list argptr; char *p; va_start (argptr, format); p = do_MakePath_VA(base, error, get_fs_buffer(), FS_BUFFERLEN, format, argptr); va_end (argptr); return p; } char *FS_MakePath_VABUF (int base, int *error, char *buf, size_t siz, const char *format, ...) { va_list argptr; char *p; va_start (argptr, format); p = do_MakePath_VA(base, error, buf, siz, format, argptr); va_end (argptr); return p; } const char *FS_GetBasedir (void) { return fs_basedir; } const char *FS_GetUserbase (void) { return host_parms->userdir; } const char *FS_GetGamedir (void) { return fs_gamedir; } const char *FS_GetUserdir (void) { return fs_userdir; } /* The following FS_*() stdio replacements are necessary if one is * to perform non-sequential reads on files reopened on pak files * because we need the bookkeeping about file start/end positions. * Allocating and filling in the fshandle_t structure is the users' * responsibility when the file is initially opened. */ size_t FS_fread(void *ptr, size_t size, size_t nmemb, fshandle_t *fh) { long byte_size; long bytes_read; size_t nmemb_read; if (!fh) { errno = EBADF; return 0; } if (!ptr) { errno = EFAULT; return 0; } if (!size || !nmemb) { /* no error, just zero bytes wanted */ errno = 0; return 0; } byte_size = nmemb * size; if (byte_size > fh->length - fh->pos) /* just read to end */ byte_size = fh->length - fh->pos; bytes_read = fread(ptr, 1, byte_size, fh->file); fh->pos += bytes_read; /* fread() must return the number of elements read, * not the total number of bytes. */ nmemb_read = bytes_read / size; /* even if the last member is only read partially * it is counted as a whole in the return value. */ if (bytes_read % size) nmemb_read++; return nmemb_read; } int FS_fseek(fshandle_t *fh, long offset, int whence) { /* I don't care about 64 bit off_t or fseeko() here. * the quake/hexen2 file system is 32 bits, anyway. */ int ret; if (!fh) { errno = EBADF; return -1; } /* the relative file position shouldn't be smaller * than zero or bigger than the filesize. */ switch (whence) { case SEEK_SET: break; case SEEK_CUR: offset += fh->pos; break; case SEEK_END: offset = fh->length + offset; break; default: errno = EINVAL; return -1; } if (offset < 0) { errno = EINVAL; return -1; } if (offset > fh->length) /* just seek to end */ offset = fh->length; ret = fseek(fh->file, fh->start + offset, SEEK_SET); if (ret < 0) return ret; fh->pos = offset; return 0; } int FS_fclose(fshandle_t *fh) { if (!fh) { errno = EBADF; return -1; } return fclose(fh->file); } long FS_ftell(fshandle_t *fh) { if (!fh) { errno = EBADF; return -1; } return fh->pos; } void FS_rewind(fshandle_t *fh) { if (!fh) return; clearerr(fh->file); fseek(fh->file, fh->start, SEEK_SET); fh->pos = 0; } int FS_feof(fshandle_t *fh) { if (!fh) { errno = EBADF; return -1; } if (fh->pos >= fh->length) return -1; return 0; } int FS_ferror(fshandle_t *fh) { if (!fh) { errno = EBADF; return -1; } return ferror(fh->file); } int FS_fgetc(fshandle_t *fh) { if (!fh) { errno = EBADF; return EOF; } if (fh->pos >= fh->length) return EOF; fh->pos += 1; return fgetc(fh->file); } char *FS_fgets(char *s, int size, fshandle_t *fh) { char *ret; if (FS_feof(fh)) return NULL; if (size > (fh->length - fh->pos) + 1) size = (fh->length - fh->pos) + 1; ret = fgets(s, size, fh->file); fh->pos = ftell(fh->file) - fh->start; return ret; } long FS_filelength (fshandle_t *fh) { if (!fh) { errno = EBADF; return -1; } return fh->length; } engine/h2shared/quakefs.h000066400000000000000000000151111444734033100156350ustar00rootroot00000000000000/* quakefs.h - Hexen II filesystem * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __QUAKEFS_H #define __QUAKEFS_H /* game data flags */ #define GAME_DEMO (1 << 0) #define GAME_OEM (1 << 1) #define GAME_OEM0 (1 << 2) #define GAME_OEM2 (1 << 3) #define GAME_REGISTERED (1 << 4) #define GAME_REGISTERED0 (1 << 5) #define GAME_REGISTERED1 (1 << 6) #define GAME_PORTALS (1 << 7) #define GAME_HEXENWORLD (1 << 8) /* FIXME: more detailed data needed for the oem * (Matrox m3D bundle) original 1.08 version. */ #define GAME_OLD_CDROM0 (1 << 9) #define GAME_OLD_CDROM1 (1 << 10) #define GAME_OLD_DEMO (1 << 11) #define GAME_REGISTERED_OLD (1 << 12) #define GAME_OLD_OEM (1 << 13) #define GAME_OLD_OEM0 (1 << 14) #define GAME_OLD_OEM2 (1 << 15) #define GAME_MODIFIED (1 << 16) extern char fs_gamedir_nopath[MAX_QPATH]; const char *FS_GetBasedir (void); const char *FS_GetUserbase (void); const char *FS_GetGamedir (void); const char *FS_GetUserdir (void); extern struct cvar_s registered; extern struct cvar_s oem; extern unsigned int gameflags; void FS_Init (void); void FS_Gamedir (const char *dir); /* Sets the gamedir and path to a different directory. */ /* file i/o within qfs */ extern long fs_filesize; /* size of the last file opened through QFS api */ extern int file_from_pak; /* global indicating that file came from a pak. */ #define FS_ENT_NONE (0) #define FS_ENT_FILE (1 << 0) #define FS_ENT_DIRECTORY (1 << 1) int FS_CopyFile (const char *frompath, const char *topath); /* Copies the FROMPATH file as TOPATH file, creating any dirs needed. * Used for saving the game. Returns 0 on success, non-zero on error. */ int FS_WriteFileFromHandle (FILE *fromfile, const char *topath, size_t size); /* takes an open file as its source, writes a new file copying `size' bytes */ int FS_WriteFile (const char *filename, const void *data, size_t len); /* Prefixes the filename by the current game directory and does an fwrite() * Returns 0 on success, 1 on error. */ int FS_CreatePath (char *path); /* Creates directory under user's path, making parent directories as needed. * The path must either be a path to a file, or, if the full path is meant to * be created, it must have the trailing path seperator. Returns 0 on success, * non-zero on error. */ long FS_OpenFile (const char *filename, FILE **file, unsigned int *path_id); /* Opens a file (a standalone file or a file in pak) in the hexen2 filesystem, * returns fs_filesize on success or (-1) on failure. If path_id is not NULL, * the id number of the opened file's gamedir is stored in path_id. */ qboolean FS_FileExists (const char *filename, unsigned int *path_id); /* Returns whether the file is found in the hexen2 filesystem. if path_id is * not NULL, the id number of the found file's gamedir is stored in path_id. */ qboolean FS_FileInGamedir (const char *filename); /* Reports the existance of a file with read permissions in * fs_gamedir or fs_userdir. *NOT* for files in pakfiles! */ /* these procedures open a file using FS_OpenFile and loads it into a proper * buffer. the buffer is allocated with a total size of fs_filesize + 1. the * procedures differ by their buffer allocation method. */ byte *FS_LoadZoneFile (const char *path, int zone_id, unsigned int *path_id); /* allocates the buffer on the zone. zone_id: which zone to use. */ byte *FS_LoadTempFile (const char *path, unsigned int *path_id); /* allocates the buffer on the temp hunk. */ byte *FS_LoadHunkFile (const char *path, unsigned int *path_id); /* allocates the buffer on the hunk. */ byte *FS_LoadMallocFile (const char *path, unsigned int *path_id); /* allocates the buffer on the system mem (malloc). */ byte *FS_LoadStackFile (const char *path, void *buffer, long bufsize, unsigned int *path_id); /* uses the specified stack stack buffer with the specified size * of bufsize. if bufsize is too short, uses temp hunk. the bufsize * must include the +1 */ struct cache_user_s; void FS_LoadCacheFile (const char *path, struct cache_user_s *cu, unsigned int *path_id); /* uses cache mem for allocating the buffer. */ #define FS_BASEDIR 0 /* host_parms->basedir (i.e.: fs_basedir) */ #define FS_USERBASE 1 /* host_parms->userdir */ #define FS_GAMEDIR 2 /* host_parms->basedir/gamedir (fs_gamedir) */ #define FS_USERDIR 3 /* host_parms->userdir/gamedir (fs_userdir) */ char *FS_MakePath (int base, int *error, const char *path); /* strcat to an FS path basename. returns a static buffer. */ char *FS_MakePath_VA (int base, int *error, const char *format, ...) FUNC_PRINTF(3,4); /* like FS_MakePath(), but does varargs. */ char *FS_MakePath_BUF (int base, int *error, char *buf, size_t siz, const char *path); char *FS_MakePath_VABUF (int base, int *error, char *buf, size_t siz, const char *format, ...) FUNC_PRINTF(5,6); /* like the two above, but with a user-provided buffer. */ /* The following FS_*() stdio replacements are necessary if one is * to perform non-sequential reads on files reopened on pak files * because we need the bookkeeping about file start/end positions. * Allocating and filling in the fshandle_t structure is the users' * responsibility when the file is initially opened. */ typedef struct _fshandle_t { FILE *file; qboolean pak; /* is the file read from a pak */ long start; /* file or data start position */ long length; /* file or data size */ long pos; /* current position relative to start */ } fshandle_t; size_t FS_fread(void *ptr, size_t size, size_t nmemb, fshandle_t *fh); int FS_fseek(fshandle_t *fh, long offset, int whence); long FS_ftell(fshandle_t *fh); void FS_rewind(fshandle_t *fh); int FS_feof(fshandle_t *fh); int FS_ferror(fshandle_t *fh); int FS_fclose(fshandle_t *fh); int FS_fgetc(fshandle_t *fh); char *FS_fgets(char *s, int size, fshandle_t *fh); long FS_filelength (fshandle_t *fh); #endif /* __QUAKEFS_H */ engine/h2shared/r_aclip.c000066400000000000000000000210361444734033100156050ustar00rootroot00000000000000/* * r_aclip.c -- * clip routines for drawing Alias models directly to the screen. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" #include "d_local.h" static finalvert_t fv[2][8]; static auxvert_t av[8]; #if !id386 && !id68k static void R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out); static void R_Alias_clip_bottom (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out); static void R_Alias_clip_left (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out); static void R_Alias_clip_right (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out); #endif /* ================ R_Alias_clip_z pfv0 is the unclipped vertex, pfv1 is the z-clipped vertex ================ */ static void R_Alias_clip_z (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) { float scale; auxvert_t *pav0, *pav1, avout; pav0 = &av[pfv0 - &fv[0][0]]; pav1 = &av[pfv1 - &fv[0][0]]; if (pfv0->v[1] >= pfv1->v[1]) { scale = (ALIAS_Z_CLIP_PLANE - pav0->fv[2]) / (pav1->fv[2] - pav0->fv[2]); avout.fv[0] = pav0->fv[0] + (pav1->fv[0] - pav0->fv[0]) * scale; avout.fv[1] = pav0->fv[1] + (pav1->fv[1] - pav0->fv[1]) * scale; avout.fv[2] = ALIAS_Z_CLIP_PLANE; out->v[2] = pfv0->v[2] + (pfv1->v[2] - pfv0->v[2]) * scale; out->v[3] = pfv0->v[3] + (pfv1->v[3] - pfv0->v[3]) * scale; out->v[4] = pfv0->v[4] + (pfv1->v[4] - pfv0->v[4]) * scale; } else { scale = (ALIAS_Z_CLIP_PLANE - pav1->fv[2]) / (pav0->fv[2] - pav1->fv[2]); avout.fv[0] = pav1->fv[0] + (pav0->fv[0] - pav1->fv[0]) * scale; avout.fv[1] = pav1->fv[1] + (pav0->fv[1] - pav1->fv[1]) * scale; avout.fv[2] = ALIAS_Z_CLIP_PLANE; out->v[2] = pfv1->v[2] + (pfv0->v[2] - pfv1->v[2]) * scale; out->v[3] = pfv1->v[3] + (pfv0->v[3] - pfv1->v[3]) * scale; out->v[4] = pfv1->v[4] + (pfv0->v[4] - pfv1->v[4]) * scale; } R_AliasProjectFinalVert (out, &avout); if (out->v[0] < r_refdef.aliasvrect.x) out->flags |= ALIAS_LEFT_CLIP; if (out->v[1] < r_refdef.aliasvrect.y) out->flags |= ALIAS_TOP_CLIP; if (out->v[0] > r_refdef.aliasvrectright) out->flags |= ALIAS_RIGHT_CLIP; if (out->v[1] > r_refdef.aliasvrectbottom) out->flags |= ALIAS_BOTTOM_CLIP; } #if !id386 && !id68k static void R_Alias_clip_left (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) { float scale; int i; if (pfv0->v[1] >= pfv1->v[1]) { scale = (float)(r_refdef.aliasvrect.x - pfv0->v[0]) / (pfv1->v[0] - pfv0->v[0]); for (i = 0; i < 6; i++) out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5; } else { scale = (float)(r_refdef.aliasvrect.x - pfv1->v[0]) / (pfv0->v[0] - pfv1->v[0]); for (i = 0; i < 6; i++) out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5; } } static void R_Alias_clip_right (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) { float scale; int i; if (pfv0->v[1] >= pfv1->v[1]) { scale = (float)(r_refdef.aliasvrectright - pfv0->v[0]) / (pfv1->v[0] - pfv0->v[0]); for (i = 0; i < 6; i++) out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5; } else { scale = (float)(r_refdef.aliasvrectright - pfv1->v[0]) / (pfv0->v[0] - pfv1->v[0]); for (i = 0; i < 6; i++) out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5; } } static void R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) { float scale; int i; if (pfv0->v[1] >= pfv1->v[1]) { scale = (float)(r_refdef.aliasvrect.y - pfv0->v[1]) / (pfv1->v[1] - pfv0->v[1]); for (i = 0; i < 6; i++) out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5; } else { scale = (float)(r_refdef.aliasvrect.y - pfv1->v[1]) / (pfv0->v[1] - pfv1->v[1]); for (i = 0; i < 6; i++) out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5; } } static void R_Alias_clip_bottom (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) { float scale; int i; if (pfv0->v[1] >= pfv1->v[1]) { scale = (float)(r_refdef.aliasvrectbottom - pfv0->v[1]) / (pfv1->v[1] - pfv0->v[1]); for (i = 0; i < 6; i++) out->v[i] = pfv0->v[i] + (pfv1->v[i] - pfv0->v[i])*scale + 0.5; } else { scale = (float)(r_refdef.aliasvrectbottom - pfv1->v[1]) / (pfv0->v[1] - pfv1->v[1]); for (i = 0; i < 6; i++) out->v[i] = pfv1->v[i] + (pfv0->v[i] - pfv1->v[i])*scale + 0.5; } } #endif static int R_AliasClip (finalvert_t *in, finalvert_t *out, int flag, int count, void(*clip)(finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out) ) { int i, j, k; int flags, oldflags; j = count-1; k = 0; for (i = 0; i < count; j = i, i++) { oldflags = in[j].flags & flag; flags = in[i].flags & flag; if (flags && oldflags) continue; if (oldflags ^ flags) { clip (&in[j], &in[i], &out[k]); out[k].flags = 0; if (out[k].v[0] < r_refdef.aliasvrect.x) out[k].flags |= ALIAS_LEFT_CLIP; if (out[k].v[1] < r_refdef.aliasvrect.y) out[k].flags |= ALIAS_TOP_CLIP; if (out[k].v[0] > r_refdef.aliasvrectright) out[k].flags |= ALIAS_RIGHT_CLIP; if (out[k].v[1] > r_refdef.aliasvrectbottom) out[k].flags |= ALIAS_BOTTOM_CLIP; k++; } if (!flags) { out[k] = in[i]; k++; } } return k; } /* ================ R_AliasClipTriangle ================ */ void R_AliasClipTriangle (mtriangle_t *ptri) { int i, k, pingpong; mtriangle_t mtri; unsigned int clipflags; // copy vertexes and fix seam texture coordinates if (ptri->facesfront) { fv[0][0] = pfinalverts[ptri->vertindex[0]]; fv[0][1] = pfinalverts[ptri->vertindex[1]]; fv[0][2] = pfinalverts[ptri->vertindex[2]]; } else { for (i = 0; i < 3; i++) { fv[0][i] = pfinalverts[ptri->vertindex[i]]; if (fv[0][i].flags & ALIAS_ONSEAM) fv[0][i].v[2] += r_affinetridesc.seamfixupX16; } } // clip clipflags = fv[0][0].flags | fv[0][1].flags | fv[0][2].flags; if (clipflags & ALIAS_Z_CLIP) { for (i = 0; i < 3; i++) av[i] = pauxverts[ptri->vertindex[i]]; k = R_AliasClip (fv[0], fv[1], ALIAS_Z_CLIP, 3, R_Alias_clip_z); if (k == 0) return; pingpong = 1; clipflags = fv[1][0].flags | fv[1][1].flags | fv[1][2].flags; } else { pingpong = 0; k = 3; } if (clipflags & ALIAS_LEFT_CLIP) { k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], ALIAS_LEFT_CLIP, k, R_Alias_clip_left); if (k == 0) return; pingpong ^= 1; } if (clipflags & ALIAS_RIGHT_CLIP) { k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], ALIAS_RIGHT_CLIP, k, R_Alias_clip_right); if (k == 0) return; pingpong ^= 1; } if (clipflags & ALIAS_BOTTOM_CLIP) { k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], ALIAS_BOTTOM_CLIP, k, R_Alias_clip_bottom); if (k == 0) return; pingpong ^= 1; } if (clipflags & ALIAS_TOP_CLIP) { k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], ALIAS_TOP_CLIP, k, R_Alias_clip_top); if (k == 0) return; pingpong ^= 1; } for (i = 0; i < k; i++) { if (fv[pingpong][i].v[0] < r_refdef.aliasvrect.x) fv[pingpong][i].v[0] = r_refdef.aliasvrect.x; else if (fv[pingpong][i].v[0] > r_refdef.aliasvrectright) fv[pingpong][i].v[0] = r_refdef.aliasvrectright; if (fv[pingpong][i].v[1] < r_refdef.aliasvrect.y) fv[pingpong][i].v[1] = r_refdef.aliasvrect.y; else if (fv[pingpong][i].v[1] > r_refdef.aliasvrectbottom) fv[pingpong][i].v[1] = r_refdef.aliasvrectbottom; fv[pingpong][i].flags = 0; } // draw triangles mtri.facesfront = ptri->facesfront; r_affinetridesc.ptriangles = &mtri; r_affinetridesc.pfinalverts = fv[pingpong]; // FIXME: do all at once as trifan? mtri.vertindex[0] = 0; for (i = 1; i < k - 1; i++) { mtri.vertindex[1] = i; mtri.vertindex[2] = i+1; if (currententity->model->flags & EF_SPECIAL_TRANS) D_PolysetDrawT5 (); else if (currententity->drawflags & DRF_TRANSLUCENT) D_PolysetDrawT (); else if (currententity->model->flags & EF_TRANSPARENT) D_PolysetDrawT2 (); else if (currententity->model->flags & EF_HOLEY) D_PolysetDrawT3 (); else D_PolysetDraw (); } } engine/h2shared/r_aclip68k.s000066400000000000000000000167371444734033100161720ustar00rootroot00000000000000** ** Quake for AMIGA ** r_aclip.c assembler implementations by Frank Wille ** ;INCLUDE "quakedef68k.i" REFDEF_ALIASVRECT equ 20 REFDEF_ALIASVRECTRIGHT equ 48 REFDEF_ALIASVRECTBOTTOM equ 52 VRECT_X equ 0 VRECT_Y equ 4 XREF _r_refdef XDEF _R_Alias_clip_left XDEF _R_Alias_clip_right XDEF _R_Alias_clip_top XDEF _R_Alias_clip_bottom ALIAS_LEFT_CLIP = 1 ALIAS_TOP_CLIP = 2 ALIAS_RIGHT_CLIP = 4 ALIAS_BOTTOM_CLIP = 8 fpu ****************************************************************************** * * void _R_Alias_clip_left (finalvert_t *pfv0, finalvert_t *pfv1, * finalvert_t *out) * ****************************************************************************** cnop 0,4 _R_Alias_clip_left ***** stackframe rsreset .fpuregs rs.x 2 .intregs rs.l 5 rs.l 1 .pfv0 rs.l 1 .pfv1 rs.l 1 .out rs.l 1 movem.l d2-d4/a2/a3,-(sp) fmovem.x fp2/fp3,-(sp) move.l .pfv0(sp),a0 move.l .pfv1(sp),a1 move.l .out(sp),a2 lea _r_refdef,a3 fmove.s #0.5,fp2 move.l (a0)+,d2 move.l (a0)+,d3 move.l (a1)+,d0 move.l (a1)+,d1 move.l REFDEF_ALIASVRECT+VRECT_X(a3),d4 cmp.l d1,d3 blt.b .cont sub.l d2,d4 fmove.l d4,fp0 fmove fp0,fp3 fadd fp2,fp3 move.l d0,d4 sub.l d2,d4 fmove.l d4,fp1 fdiv fp1,fp0 fmove.l fp3,d0 add.l d2,d0 move.l d0,(a2)+ moveq #5-1,d2 bra.b .entry .loop move.l (a0)+,d3 move.l (a1)+,d1 .entry sub.l d3,d1 fmove.l d1,fp1 fmul fp0,fp1 fadd fp2,fp1 fmove.l fp1,d1 add.l d3,d1 move.l d1,(a2)+ dbra d2,.loop bra.b .exit .cont sub.l d0,d4 fmove.l d4,fp0 fmove fp0,fp3 fadd fp2,fp3 move.l d2,d4 sub.l d0,d4 fmove.l d4,fp1 fdiv fp1,fp0 fmove.l fp3,d2 add.l d0,d2 move.l d2,(a2)+ moveq #5-1,d2 bra.b .entry2 .loop2 move.l (a0)+,d3 move.l (a1)+,d1 .entry2 sub.l d1,d3 fmove.l d3,fp1 fmul fp0,fp1 fadd fp2,fp1 fmove.l fp1,d3 add.l d1,d3 move.l d3,(a2)+ dbra d2,.loop2 .exit fmovem.x (sp)+,fp2/fp3 movem.l (sp)+,d2-d4/a2/a3 rts ****************************************************************************** * * void _R_Alias_clip_right (finalvert_t *pfv0, finalvert_t *pfv1, * finalvert_t *out) * ****************************************************************************** cnop 0,4 _R_Alias_clip_right ***** stackframe rsreset .fpuregs rs.x 2 .intregs rs.l 5 rs.l 1 .pfv0 rs.l 1 .pfv1 rs.l 1 .out rs.l 1 movem.l d2-d4/a2/a3,-(sp) fmovem.x fp2/fp3,-(sp) move.l .pfv0(sp),a0 move.l .pfv1(sp),a1 move.l .out(sp),a2 lea _r_refdef,a3 fmove.s #0.5,fp2 move.l (a0)+,d2 move.l (a0)+,d3 move.l (a1)+,d0 move.l (a1)+,d1 move.l REFDEF_ALIASVRECTRIGHT(a3),d4 cmp.l d1,d3 blt.b .cont sub.l d2,d4 fmove.l d4,fp0 fmove fp0,fp3 fadd fp2,fp3 move.l d0,d4 sub.l d2,d4 fmove.l d4,fp1 fdiv fp1,fp0 fmove.l fp3,d0 add.l d2,d0 move.l d0,(a2)+ moveq #5-1,d2 bra.b .entry .loop move.l (a0)+,d3 move.l (a1)+,d1 .entry sub.l d3,d1 fmove.l d1,fp1 fmul fp0,fp1 fadd fp2,fp1 fmove.l fp1,d1 add.l d3,d1 move.l d1,(a2)+ dbra d2,.loop bra.b .exit .cont sub.l d0,d4 fmove.l d4,fp0 fmove fp0,fp3 fadd fp2,fp3 move.l d2,d4 sub.l d0,d4 fmove.l d4,fp1 fdiv fp1,fp0 fmove.l fp3,d2 add.l d0,d2 move.l d2,(a2)+ moveq #5-1,d2 bra.b .entry2 .loop2 move.l (a0)+,d3 move.l (a1)+,d1 .entry2 sub.l d1,d3 fmove.l d3,fp1 fmul fp0,fp1 fadd fp2,fp1 fmove.l fp1,d3 add.l d1,d3 move.l d3,(a2)+ dbra d2,.loop2 .exit fmovem.x (sp)+,fp2/fp3 movem.l (sp)+,d2-d4/a2/a3 rts ****************************************************************************** * * void _R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1, * finalvert_t *out) * ****************************************************************************** cnop 0,4 _R_Alias_clip_top ***** stackframe rsreset .fpuregs rs.x 2 .intregs rs.l 5 rs.l 1 .pfv0 rs.l 1 .pfv1 rs.l 1 .out rs.l 1 movem.l d2-d4/a2/a3,-(sp) fmovem.x fp2/fp3,-(sp) move.l .pfv0(sp),a0 move.l .pfv1(sp),a1 move.l .out(sp),a2 lea _r_refdef,a3 fmove.s #0.5,fp2 move.l (a0)+,d2 move.l (a0)+,d3 move.l (a1)+,d0 move.l (a1)+,d1 move.l REFDEF_ALIASVRECT+VRECT_Y(a3),d4 cmp.l d1,d3 blt.b .cont sub.l d3,d4 fmove.l d4,fp0 fmove fp0,fp3 fadd fp2,fp3 move.l d1,d4 sub.l d3,d4 fmove.l d4,fp1 fdiv fp1,fp0 fmove.l fp3,d1 add.l d3,d1 move.l d1,(a2)+ moveq #5-1,d3 bra.b .entry .loop move.l (a0)+,d2 move.l (a1)+,d0 .entry sub.l d2,d0 fmove.l d0,fp1 fmul fp0,fp1 fadd fp2,fp1 fmove.l fp1,d0 add.l d2,d0 move.l d0,(a2)+ dbra d3,.loop bra.b .exit .cont sub.l d1,d4 fmove.l d4,fp0 fmove fp0,fp3 fadd fp2,fp3 move.l d3,d4 sub.l d1,d4 fmove.l d4,fp1 fdiv fp1,fp0 fmove.l fp3,d3 add.l d1,d3 move.l d3,(a2)+ moveq #5-1,d3 bra.b .entry2 .loop2 move.l (a0)+,d2 move.l (a1)+,d0 .entry2 sub.l d0,d2 fmove.l d2,fp1 fmul fp0,fp1 fadd fp2,fp1 fmove.l fp1,d2 add.l d0,d2 move.l d2,(a2)+ dbra d3,.loop2 .exit move.l -24(a2),d0 move.l -20(a2),-24(a2) move.l d0,-20(a2) fmovem.x (sp)+,fp2/fp3 movem.l (sp)+,d2-d4/a2/a3 rts ****************************************************************************** * * void _R_Alias_clip_bottom (finalvert_t *pfv0, finalvert_t *pfv1, * finalvert_t *out) * ****************************************************************************** cnop 0,4 _R_Alias_clip_bottom ***** stackframe rsreset .fpuregs rs.x 2 .intregs rs.l 5 rs.l 1 .pfv0 rs.l 1 .pfv1 rs.l 1 .out rs.l 1 movem.l d2-d4/a2/a3,-(sp) fmovem.x fp2/fp3,-(sp) move.l .pfv0(sp),a0 move.l .pfv1(sp),a1 move.l .out(sp),a2 lea _r_refdef,a3 fmove.s #0.5,fp2 move.l (a0)+,d2 move.l (a0)+,d3 move.l (a1)+,d0 move.l (a1)+,d1 move.l REFDEF_ALIASVRECTBOTTOM(a3),d4 cmp.l d1,d3 blt.b .cont sub.l d3,d4 fmove.l d4,fp0 fmove fp0,fp3 fadd fp2,fp3 move.l d1,d4 sub.l d3,d4 fmove.l d4,fp1 fdiv fp1,fp0 fmove.l fp3,d1 add.l d3,d1 move.l d1,(a2)+ moveq #5-1,d3 bra.b .entry .loop move.l (a0)+,d2 move.l (a1)+,d0 .entry sub.l d2,d0 fmove.l d0,fp1 fmul fp0,fp1 fadd fp2,fp1 fmove.l fp1,d0 add.l d2,d0 move.l d0,(a2)+ dbra d3,.loop bra.b .exit .cont sub.l d1,d4 fmove.l d4,fp0 fmove fp0,fp3 fadd fp2,fp3 move.l d3,d4 sub.l d1,d4 fmove.l d4,fp1 fdiv fp1,fp0 fmove.l fp3,d3 add.l d1,d3 move.l d3,(a2)+ moveq #5-1,d3 bra.b .entry2 .loop2 move.l (a0)+,d2 move.l (a1)+,d0 .entry2 sub.l d0,d2 fmove.l d2,fp1 fmul fp0,fp1 fadd fp2,fp1 fmove.l fp1,d2 add.l d0,d2 move.l d2,(a2)+ dbra d3,.loop2 .exit move.l -24(a2),d0 move.l -20(a2),-24(a2) move.l d0,-20(a2) fmovem.x (sp)+,fp2/fp3 movem.l (sp)+,d2-d4/a2/a3 rts engine/h2shared/r_aclipa.asm000066400000000000000000000070141444734033100163040ustar00rootroot00000000000000; ; r_aclipa.asm ; x86 assembly-language Alias model transform and project code. ; ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix r_refdef ; C-shared globals: _sym_prefix R_Alias_clip_bottom _sym_prefix R_Alias_clip_top _sym_prefix R_Alias_clip_right _sym_prefix R_Alias_clip_left %endif ; _sym_prefix ; externs from C code extern r_refdef ; externs from ASM-only code extern float_point5 SEGMENT .data Ltemp0 dd 0 Ltemp1 dd 0 SEGMENT .text ;;;;;;;;;;;;;;;;;;;;;;;; ; R_Alias_clip_bottom ;;;;;;;;;;;;;;;;;;;;;;;; global R_Alias_clip_bottom R_Alias_clip_bottom: push esi push edi mov esi, dword [8+4+esp] mov edi, dword [8+8+esp] mov eax, dword [r_refdef+52] LDoForwardOrBackward: mov edx, dword [0+4+esi] mov ecx, dword [0+4+edi] cmp edx,ecx jl LDoForward mov ecx, dword [0+4+esi] mov edx, dword [0+4+edi] mov edi, dword [8+4+esp] mov esi, dword [8+8+esp] LDoForward: sub ecx,edx sub eax,edx mov dword [Ltemp1],ecx mov dword [Ltemp0],eax fild dword [Ltemp1] fild dword [Ltemp0] mov edx, dword [8+12+esp] mov eax,2 fdivrp st1,st0 LDo3Forward: fild dword [0+0+esi] fild dword [0+0+edi] fild dword [0+4+esi] fild dword [0+4+edi] fild dword [0+8+esi] fild dword [0+8+edi] fxch st5 fsub st4,st0 fxch st3 fsub st2,st0 fxch st1 fsub st5,st0 fxch st6 fmul st4,st0 add edi,12 fmul st2,st0 add esi,12 add edx,12 fmul st5,st0 fxch st3 faddp st4,st0 faddp st1,st0 fxch st4 faddp st3,st0 fxch st1 fadd dword [float_point5] fxch st3 fadd dword [float_point5] fxch st2 fadd dword [float_point5] fxch st3 fistp dword [0+0-12+edx] fxch st1 fistp dword [0+4-12+edx] fxch st1 fistp dword [0+8-12+edx] dec eax jnz LDo3Forward fstp st0 pop edi pop esi ret ;;;;;;;;;;;;;;;;;;;;;;;; ; R_Alias_clip_top ;;;;;;;;;;;;;;;;;;;;;;;; global R_Alias_clip_top R_Alias_clip_top: push esi push edi mov esi, dword [8+4+esp] mov edi, dword [8+8+esp] mov eax, dword [r_refdef+20+4] jmp LDoForwardOrBackward ;;;;;;;;;;;;;;;;;;;;;;;; ; R_Alias_clip_right ;;;;;;;;;;;;;;;;;;;;;;;; global R_Alias_clip_right R_Alias_clip_right: push esi push edi mov esi, dword [8+4+esp] mov edi, dword [8+8+esp] mov eax, dword [r_refdef+48] LRightLeftEntry: mov edx, dword [0+4+esi] mov ecx, dword [0+4+edi] cmp edx,ecx mov edx, dword [0+0+esi] mov ecx, dword [0+0+edi] jl LDoForward2 mov ecx, dword [0+0+esi] mov edx, dword [0+0+edi] mov edi, dword [8+4+esp] mov esi, dword [8+8+esp] LDoForward2: jmp LDoForward ;;;;;;;;;;;;;;;;;;;;;;;; ; R_Alias_clip_left ;;;;;;;;;;;;;;;;;;;;;;;; global R_Alias_clip_left R_Alias_clip_left: push esi push edi mov esi, dword [8+4+esp] mov edi, dword [8+8+esp] mov eax, dword [r_refdef+20+0] jmp LRightLeftEntry engine/h2shared/r_alias68k.s000066400000000000000000000110601444734033100161530ustar00rootroot00000000000000** ** Quake for AMIGA ** r_alias.c assembler implementations by Frank Wille ** Adapted for Hexen II by Szilard Biro ** ;INCLUDE "quakedef68k.i" ;FV_FLAGS equ 24 TV_LIGHTNORMALINDEX equ 3 ;SV_ONSEAM equ 0 ;SV_S equ 4 ;SV_T equ 8 XREF _aliastransform XREF _r_avertexnormals XREF _r_plightvec XREF _r_ambientlight XREF _r_shadelight ;XREF _ziscale ;XREF _aliasxscale ;XREF _aliasyscale ;XREF _aliasxcenter ;XREF _aliasycenter XDEF _R_AliasTransformVector XDEF _R_AliasTransformFinalVert fpu ****************************************************************************** * * void _R_AliasTransformVector (vec3_t in, vec3_t out) * ****************************************************************************** cnop 0,4 _R_AliasTransformVector ***** stackframe rsreset .fpuregs rs.x 4 rs.l 1 .in rs.l 1 .out rs.l 1 fmovem.x fp2-fp5,-(sp) move.l .in(sp),a0 move.l .out(sp),a1 fmove.s (a0)+,fp0 fmove.s (a0)+,fp1 fmove.s (a0)+,fp2 lea _aliastransform,a0 fmove.s (a0)+,fp3 fmul fp0,fp3 fmove.s (a0)+,fp4 fmul fp1,fp4 fadd fp4,fp3 fmove.s (a0)+,fp4 fmul fp2,fp4 fadd fp4,fp3 fadd.s (a0)+,fp3 fmove.s fp3,(a1)+ fmove.s (a0)+,fp3 fmul fp0,fp3 fmove.s (a0)+,fp4 fmul fp1,fp4 fadd fp4,fp3 fmove.s (a0)+,fp4 fmul fp2,fp4 fadd fp4,fp3 fadd.s (a0)+,fp3 fmove.s fp3,(a1)+ fmul.s (a0)+,fp0 fmul.s (a0)+,fp1 fadd fp1,fp0 fmul.s (a0)+,fp2 fadd fp2,fp0 fadd.s (a0)+,fp0 fmove.s fp0,(a1)+ fmovem.x (sp)+,fp2-fp5 rts ****************************************************************************** * * void _R_AliasTransformFinalVert (finalvert_t *fv, auxvert_t *av, * trivertx_t *pverts) * ****************************************************************************** cnop 0,4 _R_AliasTransformFinalVert ***** stackframe rsreset .fpuregs rs.x 3 .intregs rs.l 1 rs.l 1 .fv rs.l 1 .av rs.l 1 .tv rs.l 1 ;.sv rs.l 1 move.l a2,-(sp) fmovem.x fp2-fp4,-(sp) move.l .av(sp),a1 move.l .tv(sp),a2 * av->fv[0] = DotProduct(pverts->v, aliastransform[0]) + * aliastransform[0][3]; * av->fv[1] = DotProduct(pverts->v, aliastransform[1]) + * aliastransform[1][3]; * av->fv[2] = DotProduct(pverts->v, aliastransform[2]) + * aliastransform[2][3]; lea _aliastransform,a0 moveq #0,d0 move.b (a2),d0 fmove.l d0,fp0 move.b 1(a2),d0 fmove.l d0,fp1 move.b 2(a2),d0 fmove.l d0,fp2 fmove.s (a0)+,fp3 fmul fp0,fp3 fmove.s (a0)+,fp4 fmul fp1,fp4 fadd fp4,fp3 fmove.s (a0)+,fp4 fmul fp2,fp4 fadd fp4,fp3 fadd.s (a0)+,fp3 fmove.s fp3,(a1)+ fmove.s (a0)+,fp3 fmul fp0,fp3 fmove.s (a0)+,fp4 fmul fp1,fp4 fadd fp4,fp3 fmove.s (a0)+,fp4 fmul fp2,fp4 fadd fp4,fp3 fadd.s (a0)+,fp3 fmove.s fp3,(a1)+ fmove.s (a0)+,fp3 fmul fp0,fp3 fmove.s (a0)+,fp4 fmul fp1,fp4 fadd fp4,fp3 fmove.s (a0)+,fp4 fmul fp2,fp4 fadd fp4,fp3 fadd.s (a0)+,fp3 fmove.s fp3,(a1)+ * fv->v[2] = pstverts->s; * fv->v[3] = pstverts->t; * * fv->flags = pstverts->onseam; move.l .fv(sp),a0 ;move.l .sv(sp),a1 ;move.l SV_S(a1),8(a0) ;move.l SV_T(a1),12(a0) ;move.l SV_ONSEAM(a1),FV_FLAGS(a0) * plightnormal = r_avertexnormals[pverts->lightnormalindex]; * lightcos = DotProduct (plightnormal, r_plightvec); * temp = r_ambientlight; lea _r_avertexnormals,a1 moveq #0,d0 move.b TV_LIGHTNORMALINDEX(a2),d0 muls #12,d0 add.l d0,a1 lea _r_plightvec,a2 fmove.s (a2)+,fp0 fmove.s (a2)+,fp1 fmove.s (a2)+,fp2 fmul.s (a1)+,fp0 fmul.s (a1)+,fp1 fadd fp1,fp0 fmul.s (a1)+,fp2 fadd fp2,fp0 move.l _r_ambientlight,d0 * if (lightcos < 0) * { * temp += (int)(r_shadelight * lightcos); * * // clamp; because we limited the minimum ambient and shading light, we * // don't have to clamp low light, just bright * if (temp < 0) * temp = 0; * } * * fv->v[4] = temp; ftst fp0 fboge.w .cont fmul.s _r_shadelight,fp0 fmove.l fp0,d1 add.l d1,d0 bge.b .cont moveq #0,d0 .cont move.l d0,16(a0) fmovem.x (sp)+,fp2-fp4 move.l (sp)+,a2 rts engine/h2shared/r_aliasa.asm000066400000000000000000000075331444734033100163130ustar00rootroot00000000000000; ; r_aliasa.asm ; x86 assembly-language Alias model transform and project code. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix r_apverts _sym_prefix r_anumverts _sym_prefix aliastransform _sym_prefix r_avertexnormals _sym_prefix r_plightvec _sym_prefix r_ambientlight _sym_prefix r_shadelight _sym_prefix aliasxcenter _sym_prefix aliasycenter ; C-shared globals: _sym_prefix R_AliasTransformAndProjectFinalVerts %endif ; _sym_prefix ; externs from C code extern r_apverts extern r_anumverts extern aliastransform extern r_avertexnormals extern r_plightvec extern r_ambientlight extern r_shadelight extern aliasxcenter extern aliasycenter ; externs from ASM-only code SEGMENT .data Lfloat_1 dd 1.0 Ltemp dd 0 Lcoords dd 0, 0, 0 SEGMENT .text ;;;;;;;;;;;;;;;;;;;;;;;; ; R_AliasTransformAndProjectFinalVerts ;;;;;;;;;;;;;;;;;;;;;;;; global R_AliasTransformAndProjectFinalVerts R_AliasTransformAndProjectFinalVerts: push ebp push edi push esi mov esi,dword[r_apverts] mov ebp,dword[12+8+esp] mov edi,dword[12+4+esp] mov ecx,dword[r_anumverts] sub edx,edx Lloop: mov dl,byte[esi] mov byte[Lcoords],dl fild dword[Lcoords] mov dl,byte[1+esi] mov byte[Lcoords+4],dl fild dword[Lcoords+4] mov dl,byte[2+esi] mov byte[Lcoords+8],dl fild dword[Lcoords+8] fld st2 fmul dword[aliastransform+32] fld st2 fmul dword[aliastransform+36] fxch st1 fadd dword[aliastransform+44] fld st2 fmul dword[aliastransform+40] fxch st1 faddp st2,st0 mov dl,byte[3+esi] ; mov eax,dword[4+ebp] ;load .s ; mov dword[0+8+edi],eax ;store .s faddp st1,st0 ; mov eax,dword[8+ebp] ;.t ; mov dword[0+12+edi],eax ;.t fdivr dword[Lfloat_1] ; mov eax,dword[0+ebp] ; .onseam ; mov dword[24+edi],eax mov eax,dword[32+edi] mov eax,dword[12+ebp] mov eax,dword[4+esi] lea eax,[edx+edx*2] fxch st3 fld dword[r_avertexnormals+eax*4] fmul dword[r_plightvec] fld dword[r_avertexnormals+4+eax*4] fmul dword[r_plightvec+4] fld dword[r_avertexnormals+8+eax*4] fmul dword[r_plightvec+8] fxch st1 faddp st2,st0 fld st2 fmul dword[aliastransform+0] fxch st2 faddp st1,st0 fst dword[Ltemp] mov eax,dword[r_ambientlight] mov dl,byte[Ltemp+3] test dl,080h jz near Lsavelight fmul dword[r_shadelight] fistp dword[Ltemp] add eax,dword[Ltemp] jns Lp1 sub eax,eax Lp1: fxch st1 fmul dword[aliastransform+16] fxch st3 fld st0 fmul dword[aliastransform+4] fxch st1 mov dword[0+16+edi],eax fmul dword[aliastransform+20] fxch st2 fadd dword[aliastransform+12] fxch st4 fadd dword[aliastransform+28] fxch st3 fld st0 fmul dword[aliastransform+8] fxch st1 fmul dword[aliastransform+24] fxch st5 faddp st2,st0 fxch st3 faddp st2,st0 add esi,4 faddp st2,st0 faddp st2,st0 add ebp,12 fmul st0,st2 fxch st1 fmul st0,st2 fxch st1 fadd dword[aliasxcenter] fxch st1 fadd dword[aliasycenter] fxch st2 fistp dword[0+20+edi] fistp dword[0+0+edi] fistp dword[0+4+edi] add edi,32 dec ecx jnz near Lloop pop esi pop edi pop ebp ret Lsavelight: fstp st0 jmp Lp1 engine/h2shared/r_bsp.c000066400000000000000000000347511444734033100153110ustar00rootroot00000000000000/* * r_bsp.c * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" // // current entity info // qboolean insubmodel; entity_t *currententity; vec3_t modelorg, base_modelorg; // modelorg is the viewpoint reletive to // the currently rendering entity vec3_t r_entorigin; // the currently rendering entity in world // coordinates float entity_rotation[3][3]; int r_currentbkey; typedef enum { touchessolid, drawnode, nodrawnode } solidstate_t; #define MAX_BMODEL_VERTS 500 // 6K #define MAX_BMODEL_EDGES 1000 // 12K static mvertex_t *pbverts; static bedge_t *pbedges; static int numbverts, numbedges; static mvertex_t *pfrontenter, *pfrontexit; static qboolean makeclippededge; //=========================================================================== /* ================ R_EntityRotate ================ */ static void R_EntityRotate (vec3_t vec) { vec3_t tvec; VectorCopy (vec, tvec); vec[0] = DotProduct (entity_rotation[0], tvec); vec[1] = DotProduct (entity_rotation[1], tvec); vec[2] = DotProduct (entity_rotation[2], tvec); } /* ================ R_RotateBmodel ================ */ void R_RotateBmodel (void) { float s, c, temp1[3][3], temp2[3][3], temp3[3][3]; // TODO: should use a look-up table // TODO: should really be stored with the entity instead of being reconstructed // TODO: could cache lazily, stored in the entity // TODO: share work with R_SetUpAliasTransform // yaw q_sincosdeg(currententity->angles[YAW], &s, &c); temp1[0][0] = c; temp1[0][1] = s; temp1[0][2] = 0; temp1[1][0] = -s; temp1[1][1] = c; temp1[1][2] = 0; temp1[2][0] = 0; temp1[2][1] = 0; temp1[2][2] = 1; // pitch q_sincosdeg(currententity->angles[PITCH], &s, &c); temp2[0][0] = c; temp2[0][1] = 0; temp2[0][2] = -s; temp2[1][0] = 0; temp2[1][1] = 1; temp2[1][2] = 0; temp2[2][0] = s; temp2[2][1] = 0; temp2[2][2] = c; R_ConcatRotations (temp2, temp1, temp3); // roll q_sincosdeg(currententity->angles[ROLL], &s, &c); temp1[0][0] = 1; temp1[0][1] = 0; temp1[0][2] = 0; temp1[1][0] = 0; temp1[1][1] = c; temp1[1][2] = s; temp1[2][0] = 0; temp1[2][1] = -s; temp1[2][2] = c; R_ConcatRotations (temp1, temp3, entity_rotation); // // rotate modelorg and the transformation matrix // R_EntityRotate (modelorg); R_EntityRotate (vpn); R_EntityRotate (vright); R_EntityRotate (vup); R_TransformFrustum (); } /* ================ R_RecursiveClipBPoly ================ */ static void R_RecursiveClipBPoly (bedge_t *pedges, mnode_t *pnode, msurface_t *psurf) { bedge_t *psideedges[2], *pnextedge, *ptedge; int i, side, lastside; float dist, frac, lastdist; mplane_t *splitplane, tplane; mvertex_t *pvert, *plastvert, *ptvert; mnode_t *pn; psideedges[0] = psideedges[1] = NULL; makeclippededge = false; // transform the BSP plane into model space // FIXME: cache these? splitplane = pnode->plane; tplane.dist = splitplane->dist - DotProduct(r_entorigin, splitplane->normal); tplane.normal[0] = DotProduct (entity_rotation[0], splitplane->normal); tplane.normal[1] = DotProduct (entity_rotation[1], splitplane->normal); tplane.normal[2] = DotProduct (entity_rotation[2], splitplane->normal); // clip edges to BSP plane for ( ; pedges ; pedges = pnextedge) { pnextedge = pedges->pnext; // set the status for the last point as the previous point // FIXME: cache this stuff somehow? plastvert = pedges->v[0]; lastdist = DotProduct (plastvert->position, tplane.normal) - tplane.dist; if (lastdist > 0) lastside = 0; else lastside = 1; pvert = pedges->v[1]; dist = DotProduct (pvert->position, tplane.normal) - tplane.dist; if (dist > 0) side = 0; else side = 1; if (side != lastside) { // clipped if (numbverts >= MAX_BMODEL_VERTS) return; // generate the clipped vertex frac = lastdist / (lastdist - dist); ptvert = &pbverts[numbverts++]; ptvert->position[0] = plastvert->position[0] + frac * (pvert->position[0] - plastvert->position[0]); ptvert->position[1] = plastvert->position[1] + frac * (pvert->position[1] - plastvert->position[1]); ptvert->position[2] = plastvert->position[2] + frac * (pvert->position[2] - plastvert->position[2]); // split into two edges, one on each side, and remember entering // and exiting points // FIXME: share the clip edge by having a winding direction flag? if (numbedges >= (MAX_BMODEL_EDGES - 1)) { Con_Printf ("Out of edges for bmodel\n"); return; } ptedge = &pbedges[numbedges]; ptedge->pnext = psideedges[lastside]; psideedges[lastside] = ptedge; ptedge->v[0] = plastvert; ptedge->v[1] = ptvert; ptedge = &pbedges[numbedges + 1]; ptedge->pnext = psideedges[side]; psideedges[side] = ptedge; ptedge->v[0] = ptvert; ptedge->v[1] = pvert; numbedges += 2; if (side == 0) { // entering for front, exiting for back pfrontenter = ptvert; makeclippededge = true; } else { pfrontexit = ptvert; makeclippededge = true; } } else { // add the edge to the appropriate side pedges->pnext = psideedges[side]; psideedges[side] = pedges; } } // if anything was clipped, reconstitute and add the edges along the clip // plane to both sides (but in opposite directions) if (makeclippededge) { if (numbedges >= (MAX_BMODEL_EDGES - 2)) { Con_Printf ("Out of edges for bmodel\n"); return; } ptedge = &pbedges[numbedges]; ptedge->pnext = psideedges[0]; psideedges[0] = ptedge; ptedge->v[0] = pfrontexit; ptedge->v[1] = pfrontenter; ptedge = &pbedges[numbedges + 1]; ptedge->pnext = psideedges[1]; psideedges[1] = ptedge; ptedge->v[0] = pfrontenter; ptedge->v[1] = pfrontexit; numbedges += 2; } // draw or recurse further for (i = 0 ; i < 2 ; i++) { if (psideedges[i]) { // draw if we've reached a non-solid leaf, done if all // that's left is a solid leaf, and continue down the // tree if it's not a leaf pn = pnode->children[i]; // we're done with this branch if the node or leaf isn't // in the PVS if (pn->visframe == r_visframecount) { if (pn->contents < 0) { if (pn->contents != CONTENTS_SOLID) { r_currentbkey = ((mleaf_t *)pn)->key; R_RenderBmodelFace (psideedges[i], psurf); } } else { R_RecursiveClipBPoly (psideedges[i], pnode->children[i], psurf); } } } } } /* ================ R_DrawSolidClippedSubmodelPolygons ================ */ void R_DrawSolidClippedSubmodelPolygons (qmodel_t *pmodel) { int i, j, lindex; vec_t dot; msurface_t *psurf; int numsurfaces; mplane_t *pplane; mvertex_t bverts[MAX_BMODEL_VERTS]; bedge_t bedges[MAX_BMODEL_EDGES], *pbedge; medge_t *pedge, *pedges; // FIXME: use bounding-box-based frustum clipping info? psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; numsurfaces = pmodel->nummodelsurfaces; pedges = pmodel->edges; for (i = 0 ; i < numsurfaces ; i++, psurf++) { // find which side of the node we are on pplane = psurf->plane; dot = DotProduct (modelorg, pplane->normal) - pplane->dist; // draw the polygon if ( ((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)) ) { // FIXME: use bounding-box-based frustum clipping info? // copy the edges to bedges, flipping if necessary // so always clockwise winding // FIXME: if edges and vertices get caches, these // assignments must move outside the loop, // and overflow checking must be done here pbverts = bverts; pbedges = bedges; numbverts = numbedges = 0; if (psurf->numedges > 0) { pbedge = &bedges[numbedges]; numbedges += psurf->numedges; for (j = 0 ; j < psurf->numedges ; j++) { lindex = pmodel->surfedges[psurf->firstedge+j]; if (lindex > 0) { pedge = &pedges[lindex]; pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[0]]; pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[1]]; } else { lindex = -lindex; pedge = &pedges[lindex]; pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[1]]; pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[0]]; } pbedge[j].pnext = &pbedge[j+1]; } pbedge[j-1].pnext = NULL; // mark end of edges R_RecursiveClipBPoly (pbedge, currententity->topnode, psurf); } else { Sys_Error ("no edges in bmodel"); } } } } /* ================ R_DrawSubmodelPolygons ================ */ void R_DrawSubmodelPolygons (qmodel_t *pmodel, int clipflags) { int i; vec_t dot; msurface_t *psurf; int numsurfaces; mplane_t *pplane; // FIXME: use bounding-box-based frustum clipping info? psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; numsurfaces = pmodel->nummodelsurfaces; for (i = 0 ; i < numsurfaces ; i++, psurf++) { // find which side of the node we are on pplane = psurf->plane; dot = DotProduct (modelorg, pplane->normal) - pplane->dist; // draw the polygon if ( ((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON)) ) { r_currentkey = ((mleaf_t *)currententity->topnode)->key; // FIXME: use bounding-box-based frustum clipping info? R_RenderFace (psurf, clipflags); } } } /* ================ R_RecursiveWorldNode ================ */ static void R_RecursiveWorldNode (mnode_t *node, int clipflags) { int i, c, side, *pindex; vec3_t acceptpt, rejectpt; mplane_t *plane; msurface_t *surf, **mark; mleaf_t *pleaf; double d, dot; if (node->contents == CONTENTS_SOLID) return; // solid if (node->visframe != r_visframecount) return; // cull the clipping planes if not trivial accept // FIXME: the compiler is doing a lousy job of optimizing // here; it could be twice as fast in ASM if (clipflags) { for (i = 0 ; i < 4 ; i++) { if (! (clipflags & (1<minmaxs[pindex[0]]; rejectpt[1] = (float)node->minmaxs[pindex[1]]; rejectpt[2] = (float)node->minmaxs[pindex[2]]; d = DotProduct (rejectpt, view_clipplanes[i].normal); d -= view_clipplanes[i].dist; if (d <= 0) return; acceptpt[0] = (float)node->minmaxs[pindex[3+0]]; acceptpt[1] = (float)node->minmaxs[pindex[3+1]]; acceptpt[2] = (float)node->minmaxs[pindex[3+2]]; d = DotProduct (acceptpt, view_clipplanes[i].normal); d -= view_clipplanes[i].dist; if (d >= 0) clipflags &= ~(1<contents < 0) { pleaf = (mleaf_t *)node; mark = pleaf->firstmarksurface; c = pleaf->nummarksurfaces; if (c) { do { (*mark)->visframe = r_framecount; mark++; } while (--c); } // deal with model fragments in this leaf if (pleaf->efrags) { R_StoreEfrags (&pleaf->efrags); } pleaf->key = r_currentkey; r_currentkey++; // all bmodels in a leaf share the same key } else { // node is just a decision point, so go down the apropriate sides // find which side of the node we are on plane = node->plane; switch (plane->type) { case PLANE_X: dot = modelorg[0] - plane->dist; break; case PLANE_Y: dot = modelorg[1] - plane->dist; break; case PLANE_Z: dot = modelorg[2] - plane->dist; break; default: dot = DotProduct (modelorg, plane->normal) - plane->dist; break; } if (dot >= 0) side = 0; else side = 1; // recurse down the children, front side first R_RecursiveWorldNode (node->children[side], clipflags); // draw stuff c = node->numsurfaces; if (c) { surf = cl.worldmodel->surfaces + node->firstsurface; if (dot < -BACKFACE_EPSILON) { do { if ((surf->flags & SURF_PLANEBACK) && (surf->visframe == r_framecount)) { #if 0 if (r_drawpolys) { if (r_worldpolysbacktofront) { if (numbtofpolys < MAX_BTOFPOLYS) { pbtofpolys[numbtofpolys].clipflags = clipflags; pbtofpolys[numbtofpolys].psurf = surf; numbtofpolys++; } } else { R_RenderPoly (surf, clipflags); } } else #endif { R_RenderFace (surf, clipflags); } } surf++; } while (--c); } else if (dot > BACKFACE_EPSILON) { do { if (!(surf->flags & SURF_PLANEBACK) && (surf->visframe == r_framecount)) { #if 0 if (r_drawpolys) { if (r_worldpolysbacktofront) { if (numbtofpolys < MAX_BTOFPOLYS) { pbtofpolys[numbtofpolys].clipflags = clipflags; pbtofpolys[numbtofpolys].psurf = surf; numbtofpolys++; } } else { R_RenderPoly (surf, clipflags); } } else #endif { R_RenderFace (surf, clipflags); } } surf++; } while (--c); } // all surfaces on the same node share the same sequence number r_currentkey++; } // recurse down the back side R_RecursiveWorldNode (node->children[!side], clipflags); } } /* ================ R_RenderWorld ================ */ void R_RenderWorld (void) { qmodel_t *clmodel; btofpoly_t btofpolys[MAX_BTOFPOLYS]; pbtofpolys = btofpolys; currententity = &r_worldentity; VectorCopy (r_origin, modelorg); clmodel = currententity->model; r_pcurrentvertbase = clmodel->vertexes; R_RecursiveWorldNode (clmodel->nodes, 15); #if 0 // if the driver wants the polygons back to front, // play the visible ones back in that order if (r_worldpolysbacktofront) { int i; for (i = numbtofpolys-1 ; i >= 0 ; i--) { R_RenderPoly (btofpolys[i].psurf, btofpolys[i].clipflags); } } #endif } engine/h2shared/r_bsp68k.s000066400000000000000000000033451444734033100156550ustar00rootroot00000000000000** ** Quake for AMIGA ** r_bsp.c assembler implementations by Frank Wille ** Adapted for Hexen II by Szilard Biro ** ;INCLUDE "quakedef68k.i" CLIP_SIZEOF equ 24 MPLANE_SIZEOF equ 20 XREF _modelorg XREF _vpn XREF _vright XREF _vup XREF _view_clipplanes XREF _screenedge XDEF _R_TransformFrustum ****************************************************************************** * * void R_TransformFrustum (void) * ****************************************************************************** cnop 0,4 _R_TransformFrustum movem.l a2-a5,-(sp) fmovem.x fp2-fp7,-(sp) moveq #4-1,d0 ;lea _screenedge,a4 ;lea _view_clipplanes,a5 lea _screenedge,a0 lea _view_clipplanes,a1 lea _modelorg,a3 fmove.s (a3)+,fp7 .loop ; move.l a4,a0 ; move.l a5,a1 fmove.s (a0)+,fp1 fneg fp1 fmove.s (a0)+,fp2 fmove.s (a0)+,fp0 lea _vright,a2 fmove.s (a2)+,fp3 fmove.s (a2)+,fp4 fmove.s (a2)+,fp5 fmul fp1,fp3 fmul fp1,fp4 fmul fp1,fp5 lea _vup,a2 fmove.s (a2)+,fp6 fmul fp2,fp6 fadd fp6,fp3 fmove.s (a2)+,fp6 fmul fp2,fp6 fadd fp6,fp4 fmove.s (a2)+,fp6 fmul fp2,fp6 fadd fp6,fp5 lea _vpn,a2 fmove.s (a2)+,fp6 fmul fp0,fp6 fadd fp6,fp3 fmove.s fp3,(a1)+ fmove.s (a2)+,fp6 fmul fp0,fp6 fadd fp6,fp4 fmove.s fp4,(a1)+ fmove.s (a2)+,fp6 fmul fp0,fp6 fadd fp6,fp5 fmove.s fp5,(a1)+ fmul fp7,fp3 fmul.s (a3),fp4 fadd fp4,fp3 fmul.s 4(a3),fp5 fadd fp5,fp3 fmove.s fp3,(a1)+ lea CLIP_SIZEOF-16(a1),a1 lea MPLANE_SIZEOF-12(a0),a0 dbra d0,.loop fmovem.x (sp)+,fp2-fp7 movem.l (sp)+,a2-a5 rts engine/h2shared/r_draw.c000066400000000000000000000477571444734033100154740ustar00rootroot00000000000000/* * r_draw.c * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" #define MAXLEFTCLIPEDGES 100 // !!! if these are changed, they must be changed in asm_draw.h too !!! #define FULLY_CLIPPED_CACHED 0x80000000 #define FRAMECOUNT_MASK 0x7FFFFFFF int c_faceclip; // number of faces clipped zpointdesc_t r_zpointdesc; polydesc_t r_polydesc; clipplane_t *entity_clipplanes; clipplane_t view_clipplanes[4]; clipplane_t world_clipplanes[16]; int sintable[SIN_BUFFER_SIZE]; int intsintable[SIN_BUFFER_SIZE]; ASM_LINKAGE_BEGIN unsigned int cacheoffset; medge_t *r_pedge; qboolean r_leftclipped, r_rightclipped; qboolean r_nearzionly; mvertex_t r_leftenter, r_leftexit; mvertex_t r_rightenter, r_rightexit; typedef struct { float u, v; int ceilv; } evert_t; int r_emitted; float r_nearzi; float r_u1, r_v1, r_lzi1; int r_ceilv1; qboolean r_lastvertvalid; ASM_LINKAGE_END #if !id386 && !id68k static void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip); #endif //=========================================================================== #if !id386 && !id68k /* ================ R_EmitEdge ================ */ static void R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1) { edge_t *edge, *pcheck; int u_check; float u, u_step; vec3_t local, transformed; float *world; int v, v2, ceilv0; float scale, lzi0, u0, v0; int side; if (r_lastvertvalid) { u0 = r_u1; v0 = r_v1; lzi0 = r_lzi1; ceilv0 = r_ceilv1; } else { world = &pv0->position[0]; // transform and project VectorSubtract (world, modelorg, local); TransformVector (local, transformed); if (transformed[2] < NEAR_CLIP) transformed[2] = NEAR_CLIP; lzi0 = 1.0 / transformed[2]; // FIXME: build x/yscale into transform? scale = xscale * lzi0; u0 = (xcenter + scale*transformed[0]); if (u0 < r_refdef.fvrectx_adj) u0 = r_refdef.fvrectx_adj; if (u0 > r_refdef.fvrectright_adj) u0 = r_refdef.fvrectright_adj; scale = yscale * lzi0; v0 = (ycenter - scale*transformed[1]); if (v0 < r_refdef.fvrecty_adj) v0 = r_refdef.fvrecty_adj; if (v0 > r_refdef.fvrectbottom_adj) v0 = r_refdef.fvrectbottom_adj; ceilv0 = (int) ceil(v0); } world = &pv1->position[0]; // transform and project VectorSubtract (world, modelorg, local); TransformVector (local, transformed); if (transformed[2] < NEAR_CLIP) transformed[2] = NEAR_CLIP; r_lzi1 = 1.0 / transformed[2]; scale = xscale * r_lzi1; r_u1 = (xcenter + scale*transformed[0]); if (r_u1 < r_refdef.fvrectx_adj) r_u1 = r_refdef.fvrectx_adj; if (r_u1 > r_refdef.fvrectright_adj) r_u1 = r_refdef.fvrectright_adj; scale = yscale * r_lzi1; r_v1 = (ycenter - scale*transformed[1]); if (r_v1 < r_refdef.fvrecty_adj) r_v1 = r_refdef.fvrecty_adj; if (r_v1 > r_refdef.fvrectbottom_adj) r_v1 = r_refdef.fvrectbottom_adj; if (r_lzi1 > lzi0) lzi0 = r_lzi1; if (lzi0 > r_nearzi) // for mipmap finding r_nearzi = lzi0; // for right edges, all we want is the effect on 1/z if (r_nearzionly) return; r_emitted = 1; r_ceilv1 = (int) ceil(r_v1); // create the edge if (ceilv0 == r_ceilv1) { // we cache unclipped horizontal edges as fully clipped if (cacheoffset != 0x7FFFFFFF) { cacheoffset = FULLY_CLIPPED_CACHED | (r_framecount & FRAMECOUNT_MASK); } return; // horizontal edge } side = ceilv0 > r_ceilv1; edge = edge_p++; edge->owner = r_pedge; edge->nearzi = lzi0; if (side == 0) { // trailing edge (go from p1 to p2) v = ceilv0; v2 = r_ceilv1 - 1; edge->surfs[0] = surface_p - surfaces; edge->surfs[1] = 0; u_step = ((r_u1 - u0) / (r_v1 - v0)); u = u0 + ((float)v - v0) * u_step; } else { // leading edge (go from p2 to p1) v2 = ceilv0 - 1; v = r_ceilv1; edge->surfs[0] = 0; edge->surfs[1] = surface_p - surfaces; u_step = ((u0 - r_u1) / (v0 - r_v1)); u = r_u1 + ((float)v - r_v1) * u_step; } edge->u_step = u_step*0x100000; edge->u = u*0x100000 + 0xFFFFF; // we need to do this to avoid stepping off the edges if a very nearly // horizontal edge is less than epsilon above a scan, and numeric error causes // it to incorrectly extend to the scan, and the extension of the line goes off // the edge of the screen // FIXME: is this actually needed? if (edge->u < r_refdef.vrect_x_adj_shift20) edge->u = r_refdef.vrect_x_adj_shift20; if (edge->u > r_refdef.vrectright_adj_shift20) edge->u = r_refdef.vrectright_adj_shift20; // // sort the edge in normally // u_check = edge->u; if (edge->surfs[0]) u_check++; // sort trailers after leaders if (!newedges[v] || newedges[v]->u >= u_check) { edge->next = newedges[v]; newedges[v] = edge; } else { pcheck = newedges[v]; while (pcheck->next && pcheck->next->u < u_check) pcheck = pcheck->next; edge->next = pcheck->next; pcheck->next = edge; } edge->nextremove = removeedges[v2]; removeedges[v2] = edge; } /* ================ R_ClipEdge ================ */ static void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip) { float d0, d1, f; mvertex_t clipvert; if (clip) { do { d0 = DotProduct (pv0->position, clip->normal) - clip->dist; d1 = DotProduct (pv1->position, clip->normal) - clip->dist; if (d0 >= 0) { // point 0 is unclipped if (d1 >= 0) { // both points are unclipped continue; } // only point 1 is clipped // we don't cache clipped edges cacheoffset = 0x7FFFFFFF; f = d0 / (d0 - d1); clipvert.position[0] = pv0->position[0] + f * (pv1->position[0] - pv0->position[0]); clipvert.position[1] = pv0->position[1] + f * (pv1->position[1] - pv0->position[1]); clipvert.position[2] = pv0->position[2] + f * (pv1->position[2] - pv0->position[2]); if (clip->leftedge) { r_leftclipped = true; r_leftexit = clipvert; } else if (clip->rightedge) { r_rightclipped = true; r_rightexit = clipvert; } R_ClipEdge (pv0, &clipvert, clip->next); return; } else { // point 0 is clipped if (d1 < 0) { // both points are clipped // we do cache fully clipped edges if (!r_leftclipped) cacheoffset = FULLY_CLIPPED_CACHED | (r_framecount & FRAMECOUNT_MASK); return; } // only point 0 is clipped r_lastvertvalid = false; // we don't cache partially clipped edges cacheoffset = 0x7FFFFFFF; f = d0 / (d0 - d1); clipvert.position[0] = pv0->position[0] + f * (pv1->position[0] - pv0->position[0]); clipvert.position[1] = pv0->position[1] + f * (pv1->position[1] - pv0->position[1]); clipvert.position[2] = pv0->position[2] + f * (pv1->position[2] - pv0->position[2]); if (clip->leftedge) { r_leftclipped = true; r_leftenter = clipvert; } else if (clip->rightedge) { r_rightclipped = true; r_rightenter = clipvert; } R_ClipEdge (&clipvert, pv1, clip->next); return; } } while ((clip = clip->next) != NULL); } // add the edge R_EmitEdge (pv0, pv1); } #endif /* !id386 */ /* ================ R_EmitCachedEdge ================ */ static void R_EmitCachedEdge (void) { edge_t *pedge_t; pedge_t = (edge_t *)((intptr_t)r_edges + r_pedge->cachededgeoffset); if (!pedge_t->surfs[0]) pedge_t->surfs[0] = surface_p - surfaces; else pedge_t->surfs[1] = surface_p - surfaces; if (pedge_t->nearzi > r_nearzi) // for mipmap finding r_nearzi = pedge_t->nearzi; r_emitted = 1; } static qboolean makeleftedge, makerightedge; /* ================ R_RenderFace ================ */ void R_RenderFace (msurface_t *fa, int clipflags) { int i, lindex; unsigned int mask; mplane_t *pplane; float distinv; vec3_t p_normal; medge_t *pedges, tedge; clipplane_t *pclip; // skip out if no more surfs if ((surface_p) >= surf_max) { r_outofsurfaces++; return; } // ditto if not enough edges left, or switch to auxedges if possible if ((edge_p + fa->numedges + 4) >= edge_max) { r_outofedges += fa->numedges; return; } c_faceclip++; // set up clip planes pclip = NULL; for (i = 3, mask = 0x08 ; i >= 0 ; i--, mask >>= 1) { if (clipflags & mask) { view_clipplanes[i].next = pclip; pclip = &view_clipplanes[i]; } } // push the edges through r_emitted = 0; r_nearzi = 0; r_nearzionly = false; makeleftedge = makerightedge = false; pedges = currententity->model->edges; r_lastvertvalid = false; for (i = 0 ; i < fa->numedges ; i++) { lindex = currententity->model->surfedges[fa->firstedge + i]; if (lindex > 0) { r_pedge = &pedges[lindex]; // if the edge is cached, we can just reuse the edge if (!insubmodel) { if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) { if ((int)(r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == r_framecount) { r_lastvertvalid = false; continue; } } else { if ( (((uintptr_t)edge_p - (uintptr_t)r_edges) > r_pedge->cachededgeoffset) && (((edge_t *)((intptr_t)r_edges + r_pedge->cachededgeoffset))->owner == r_pedge)) { R_EmitCachedEdge (); r_lastvertvalid = false; continue; } } } // assume it's cacheable cacheoffset = (byte *)edge_p - (byte *)r_edges; r_leftclipped = r_rightclipped = false; R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[0]], &r_pcurrentvertbase[r_pedge->v[1]], pclip); r_pedge->cachededgeoffset = cacheoffset; if (r_leftclipped) makeleftedge = true; if (r_rightclipped) makerightedge = true; r_lastvertvalid = true; } else { lindex = -lindex; r_pedge = &pedges[lindex]; // if the edge is cached, we can just reuse the edge if (!insubmodel) { if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) { if ((int)(r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == r_framecount) { r_lastvertvalid = false; continue; } } else { // it's cached if the cached edge is valid and is owned // by this medge_t if ( (((uintptr_t)edge_p - (uintptr_t)r_edges) > r_pedge->cachededgeoffset) && (((edge_t *)((intptr_t)r_edges + r_pedge->cachededgeoffset))->owner == r_pedge)) { R_EmitCachedEdge (); r_lastvertvalid = false; continue; } } } // assume it's cacheable cacheoffset = (byte *)edge_p - (byte *)r_edges; r_leftclipped = r_rightclipped = false; R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[1]], &r_pcurrentvertbase[r_pedge->v[0]], pclip); r_pedge->cachededgeoffset = cacheoffset; if (r_leftclipped) makeleftedge = true; if (r_rightclipped) makerightedge = true; r_lastvertvalid = true; } } // if there was a clip off the left edge, add that edge too // FIXME: faster to do in screen space? // FIXME: share clipped edges? if (makeleftedge) { r_pedge = &tedge; r_lastvertvalid = false; R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next); } // if there was a clip off the right edge, get the right r_nearzi if (makerightedge) { r_pedge = &tedge; r_lastvertvalid = false; r_nearzionly = true; R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next); } // if no edges made it out, return without posting the surface if (!r_emitted) return; r_polycount++; surface_p->data = (void *)fa; surface_p->nearzi = r_nearzi; if (currententity->drawflags & DRF_TRANSLUCENT) surface_p->flags = fa->flags | SURF_TRANSLUCENT; else surface_p->flags = fa->flags; surface_p->insubmodel = insubmodel; surface_p->spanstate = 0; surface_p->entity = currententity; surface_p->key = r_currentkey++; surface_p->spans = NULL; pplane = fa->plane; // FIXME: cache this? TransformVector (pplane->normal, p_normal); // FIXME: cache this? distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal)); surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv; surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv; surface_p->d_ziorigin = p_normal[2] * distinv - xcenter * surface_p->d_zistepu - ycenter * surface_p->d_zistepv; surface_p++; } /* ================ R_RenderBmodelFace ================ */ void R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf) { int i; unsigned int mask; mplane_t *pplane; float distinv; vec3_t p_normal; medge_t tedge; clipplane_t *pclip; // skip out if no more surfs if (surface_p >= surf_max) { r_outofsurfaces++; return; } // ditto if not enough edges left, or switch to auxedges if possible if ((edge_p + psurf->numedges + 4) >= edge_max) { r_outofedges += psurf->numedges; return; } c_faceclip++; // this is a dummy to give the caching mechanism someplace to write to r_pedge = &tedge; // set up clip planes pclip = NULL; for (i = 3, mask = 0x08 ; i >= 0 ; i--, mask >>= 1) { if (r_clipflags & mask) { view_clipplanes[i].next = pclip; pclip = &view_clipplanes[i]; } } // push the edges through r_emitted = 0; r_nearzi = 0; r_nearzionly = false; makeleftedge = makerightedge = false; // FIXME: keep clipped bmodel edges in clockwise order so last vertex caching // can be used? r_lastvertvalid = false; for ( ; pedges ; pedges = pedges->pnext) { r_leftclipped = r_rightclipped = false; R_ClipEdge (pedges->v[0], pedges->v[1], pclip); if (r_leftclipped) makeleftedge = true; if (r_rightclipped) makerightedge = true; } // if there was a clip off the left edge, add that edge too // FIXME: faster to do in screen space? // FIXME: share clipped edges? if (makeleftedge) { r_pedge = &tedge; R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next); } // if there was a clip off the right edge, get the right r_nearzi if (makerightedge) { r_pedge = &tedge; r_nearzionly = true; R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next); } // if no edges made it out, return without posting the surface if (!r_emitted) return; r_polycount++; surface_p->data = (void *)psurf; surface_p->nearzi = r_nearzi; if (currententity->drawflags & DRF_TRANSLUCENT) surface_p->flags = psurf->flags | SURF_TRANSLUCENT; else surface_p->flags = psurf->flags; surface_p->insubmodel = true; surface_p->spanstate = 0; surface_p->entity = currententity; surface_p->key = r_currentbkey; surface_p->spans = NULL; pplane = psurf->plane; // FIXME: cache this? TransformVector (pplane->normal, p_normal); // FIXME: cache this? distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal)); surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv; surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv; surface_p->d_ziorigin = p_normal[2] * distinv - xcenter * surface_p->d_zistepu - ycenter * surface_p->d_zistepv; surface_p++; } #if 0 /* ================ R_RenderPoly ================ */ void R_RenderPoly (msurface_t *fa, int clipflags) { int i, lindex, lnumverts, s_axis, t_axis; float dist, lastdist, lzi, scale, u, v, frac; unsigned int mask; vec3_t local, transformed; clipplane_t *pclip; medge_t *pedges; mplane_t *pplane; mvertex_t verts[2][100]; //FIXME: do real number polyvert_t pverts[100]; //FIXME: do real number, safely int vertpage, newverts, newpage, lastvert; qboolean visible; // FIXME: clean this up and make it faster // FIXME: guard against running out of vertices s_axis = t_axis = 0; // keep compiler happy // set up clip planes pclip = NULL; for (i = 3, mask = 0x08 ; i >= 0 ; i--, mask >>= 1) { if (clipflags & mask) { view_clipplanes[i].next = pclip; pclip = &view_clipplanes[i]; } } // reconstruct the polygon // FIXME: these should be precalculated and loaded off disk pedges = currententity->model->edges; lnumverts = fa->numedges; vertpage = 0; for (i = 0 ; i < lnumverts ; i++) { lindex = currententity->model->surfedges[fa->firstedge + i]; if (lindex > 0) { r_pedge = &pedges[lindex]; verts[0][i] = r_pcurrentvertbase[r_pedge->v[0]]; } else { r_pedge = &pedges[-lindex]; verts[0][i] = r_pcurrentvertbase[r_pedge->v[1]]; } } // clip the polygon, done if not visible while (pclip) { lastvert = lnumverts - 1; lastdist = DotProduct (verts[vertpage][lastvert].position, pclip->normal) - pclip->dist; visible = false; newverts = 0; newpage = vertpage ^ 1; for (i = 0 ; i < lnumverts ; i++) { dist = DotProduct (verts[vertpage][i].position, pclip->normal) - pclip->dist; if ((lastdist > 0) != (dist > 0)) { frac = dist / (dist - lastdist); verts[newpage][newverts].position[0] = verts[vertpage][i].position[0] + ((verts[vertpage][lastvert].position[0] - verts[vertpage][i].position[0]) * frac); verts[newpage][newverts].position[1] = verts[vertpage][i].position[1] + ((verts[vertpage][lastvert].position[1] - verts[vertpage][i].position[1]) * frac); verts[newpage][newverts].position[2] = verts[vertpage][i].position[2] + ((verts[vertpage][lastvert].position[2] - verts[vertpage][i].position[2]) * frac); newverts++; } if (dist >= 0) { verts[newpage][newverts] = verts[vertpage][i]; newverts++; visible = true; } lastvert = i; lastdist = dist; } if (!visible || (newverts < 3)) return; lnumverts = newverts; vertpage ^= 1; pclip = pclip->next; } // transform and project, remembering the z values at the vertices and // r_nearzi, and extract the s and t coordinates at the vertices pplane = fa->plane; switch (pplane->type) { case PLANE_X: case PLANE_ANYX: s_axis = 1; t_axis = 2; break; case PLANE_Y: case PLANE_ANYY: s_axis = 0; t_axis = 2; break; case PLANE_Z: case PLANE_ANYZ: s_axis = 0; t_axis = 1; break; } r_nearzi = 0; for (i = 0 ; i < lnumverts ; i++) { // transform and project VectorSubtract (verts[vertpage][i].position, modelorg, local); TransformVector (local, transformed); if (transformed[2] < NEAR_CLIP) transformed[2] = NEAR_CLIP; lzi = 1.0 / transformed[2]; if (lzi > r_nearzi) // for mipmap finding r_nearzi = lzi; // FIXME: build x/yscale into transform? scale = xscale * lzi; u = (xcenter + scale*transformed[0]); if (u < r_refdef.fvrectx_adj) u = r_refdef.fvrectx_adj; if (u > r_refdef.fvrectright_adj) u = r_refdef.fvrectright_adj; scale = yscale * lzi; v = (ycenter - scale*transformed[1]); if (v < r_refdef.fvrecty_adj) v = r_refdef.fvrecty_adj; if (v > r_refdef.fvrectbottom_adj) v = r_refdef.fvrectbottom_adj; pverts[i].u = u; pverts[i].v = v; pverts[i].zi = lzi; pverts[i].s = verts[vertpage][i].position[s_axis]; pverts[i].t = verts[vertpage][i].position[t_axis]; } // build the polygon descriptor, including fa, r_nearzi, and u, v, s, t, and z // for each vertex r_polydesc.numverts = lnumverts; r_polydesc.nearzi = r_nearzi; r_polydesc.pcurrentface = fa; r_polydesc.pverts = pverts; // draw the polygon D_DrawPoly (); } /* ================ R_ZDrawSubmodelPolys ================ */ void R_ZDrawSubmodelPolys (qmodel_t *pmodel) { int i, numsurfaces; msurface_t *psurf; float dot; mplane_t *pplane; psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; numsurfaces = pmodel->nummodelsurfaces; for (i = 0 ; i < numsurfaces ; i++, psurf++) { // find which side of the node we are on pplane = psurf->plane; dot = DotProduct (modelorg, pplane->normal) - pplane->dist; // draw the polygon if ( ((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { // FIXME: use bounding-box-based frustum clipping info? R_RenderPoly (psurf, 15); } } } #endif engine/h2shared/r_drawa.asm000066400000000000000000000243601444734033100161540ustar00rootroot00000000000000; ; r_drawa.asm ; x86 assembly-language edge clipping and emission code ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix r_refdef _sym_prefix ycenter _sym_prefix xcenter _sym_prefix r_leftclipped _sym_prefix r_leftenter _sym_prefix r_rightclipped _sym_prefix r_rightenter _sym_prefix modelorg _sym_prefix xscale _sym_prefix yscale _sym_prefix r_leftexit _sym_prefix r_rightexit _sym_prefix r_lastvertvalid _sym_prefix cacheoffset _sym_prefix newedges _sym_prefix removeedges _sym_prefix r_pedge _sym_prefix r_framecount _sym_prefix r_u1 _sym_prefix r_emitted _sym_prefix edge_p _sym_prefix surface_p _sym_prefix surfaces _sym_prefix r_lzi1 _sym_prefix r_v1 _sym_prefix r_ceilv1 _sym_prefix r_nearzi _sym_prefix r_nearzionly _sym_prefix vright _sym_prefix vup _sym_prefix vpn ; C-shared globals: _sym_prefix R_ClipEdge %endif ; _sym_prefix ; externs from C code extern r_refdef extern ycenter extern xcenter extern r_leftclipped extern r_leftenter extern r_rightclipped extern r_rightenter extern modelorg extern xscale extern yscale extern r_leftexit extern r_rightexit extern r_lastvertvalid extern cacheoffset extern newedges extern removeedges extern r_pedge extern r_framecount extern r_u1 extern r_emitted extern edge_p extern surface_p extern surfaces extern r_lzi1 extern r_v1 extern r_ceilv1 extern r_nearzi extern r_nearzionly extern vright extern vup extern vpn ; externs from ASM-only code extern float_point5 extern float_1 extern float_minus_1 extern float_0 extern fp_1m extern fp_1m_minus_1 extern fp_8 extern fp_16 extern fp_64k extern fp_64kx64k extern ceil_cw extern single_cw SEGMENT .data Ld0 dd 0.0 Ld1 dd 0.0 Lstack dd 0 Lfp_near_clip dd 0.01 Lceilv0 dd 0 Lv dd 0 Lu0 dd 0 Lv0 dd 0 Lzi0 dd 0 SEGMENT .text ALIGN 4 ;;;;;;;;;;;;;;;;;;;;;;;; ; R_ClipEdge ;;;;;;;;;;;;;;;;;;;;;;;; global R_ClipEdge R_ClipEdge: push esi push edi push ebx mov dword [Lstack],esp mov ebx, dword [12+12+esp] mov esi, dword [4+12+esp] mov edx, dword [8+12+esp] test ebx,ebx jz Lemit Lcliploop: fld dword [0+0+esi] fmul dword [0+0+ebx] fld dword [0+4+esi] fmul dword [0+4+ebx] fld dword [0+8+esi] fmul dword [0+8+ebx] fxch st1 faddp st2,st0 fld dword [0+0+edx] fmul dword [0+0+ebx] fld dword [0+4+edx] fmul dword [0+4+ebx] fld dword [0+8+edx] fmul dword [0+8+ebx] fxch st1 faddp st2,st0 fxch st3 faddp st2,st0 faddp st2,st0 fsub dword [12+ebx] fxch st1 fsub dword [12+ebx] fxch st1 fstp dword [Ld0] fstp dword [Ld1] mov eax, dword [Ld0] mov ecx, dword [Ld1] or ecx,eax js near Lp2 Lcontinue: mov ebx, dword [16+ebx] test ebx,ebx jnz Lcliploop Lemit: fldcw word [ceil_cw] cmp dword [r_lastvertvalid],0 jz LCalcFirst mov eax, dword [r_lzi1] mov ecx, dword [r_u1] mov dword [Lzi0],eax mov dword [Lu0],ecx mov ecx, dword [r_v1] mov eax, dword [r_ceilv1] mov dword [Lv0],ecx mov dword [Lceilv0],eax jmp LCalcSecond LCalcFirst: call LTransformAndProject ; call near LTransformAndProject fst dword [Lv0] fxch st2 fstp dword [Lu0] fstp dword [Lzi0] fistp dword [Lceilv0] LCalcSecond: mov esi,edx call LTransformAndProject ; call near LTransformAndProject fld dword [Lu0] fxch st3 fld dword [Lzi0] fxch st3 fld dword [Lv0] fxch st3 fist dword [r_ceilv1] fldcw word [single_cw] fst dword [r_v1] fxch st4 fcom st1 fnstsw ax test ah,1 jz LP0 fstp st0 fld st0 LP0: fxch st1 fstp dword [r_lzi1] fxch st1 fst dword [r_u1] fxch st1 fcom dword [r_nearzi] fnstsw ax test ah,045h jnz LP1 fst dword [r_nearzi] LP1: mov eax, dword [r_nearzionly] test eax,eax jz LP2 LPop5AndDone: mov eax, dword [cacheoffset] mov edx, dword [r_framecount] cmp eax,07FFFFFFFh jz LDoPop and edx,07FFFFFFFh or edx,080000000h mov dword [cacheoffset],edx LDoPop: fstp st0 fstp st0 fstp st0 fstp st0 fstp st0 jmp Ldone LP2: mov ebx, dword [Lceilv0] mov edi, dword [edge_p] mov ecx, dword [r_ceilv1] mov edx,edi mov esi, dword [r_pedge] add edx,32 cmp ebx,ecx jz LPop5AndDone mov eax, dword [r_pedge] mov dword [28+edi],eax fstp dword [24+edi] jc LSide0 LSide1: fsubp st3,st0 fsub st0,st1 fdivp st2,st0 mov dword [r_emitted],1 mov dword [edge_p],edx mov eax, dword [edx] mov eax,ecx lea ecx, [-1+ebx] mov ebx,eax mov eax, dword [surface_p] mov esi, dword [surfaces] sub edx,edx sub eax,esi shr eax,6 mov dword [16+edi],edx mov dword [16+2+edi],eax sub esi,esi mov dword [Lv],ebx fild dword [Lv] fsubrp st1,st0 fmul st0,st1 fadd dword [r_u1] jmp LSideDone LSide0: fsub st0,st3 fxch st2 fsub st0,st1 fdivp st2,st0 mov dword [r_emitted],1 mov dword [edge_p],edx mov eax, dword [edx] dec ecx mov eax, dword [surface_p] mov esi, dword [surfaces] sub edx,edx sub eax,esi shr eax,6 mov dword [16+2+edi],edx mov dword [16+edi],eax mov esi,1 mov dword [Lv],ebx fild dword [Lv] fsubrp st1,st0 fmul st0,st1 faddp st2,st0 fxch st1 LSideDone: fmul dword [fp_1m] fxch st1 fmul dword [fp_1m] fxch st1 fadd dword [fp_1m_minus_1] fxch st1 fistp dword [4+edi] fistp dword [0+edi] mov eax, dword [0+edi] mov edx, dword [r_refdef+76] cmp eax,edx jl LP4 mov edx, dword [r_refdef+80] cmp eax,edx jng LP5 LP4: mov dword [0+edi],edx mov eax,edx LP5: add eax,esi mov esi, dword [newedges+ebx*4] test esi,esi jz LDoFirst cmp dword [0+esi],eax jl LNotFirst LDoFirst: mov dword [12+edi],esi mov dword [newedges+ebx*4],edi jmp LSetRemove LNotFirst: LFindInsertLoop: mov edx,esi mov esi, dword [12+esi] test esi,esi jz LInsertFound cmp dword [0+esi],eax jl LFindInsertLoop LInsertFound: mov dword [12+edi],esi mov dword [12+edx],edi LSetRemove: mov eax, dword [removeedges+ecx*4] mov dword [removeedges+ecx*4],edi mov dword [20+edi],eax Ldone: mov esp, dword [Lstack] pop ebx pop edi pop esi ret Lp2: test eax,eax jns Lp1 mov eax, dword [Ld1] test eax,eax jns near Lp3 mov eax, dword [r_leftclipped] mov ecx, dword [r_pedge] test eax,eax jnz Ldone mov eax, dword [r_framecount] and eax,07FFFFFFFh or eax,080000000h mov dword [cacheoffset],eax jmp Ldone Lp1: fld dword [Ld0] fld dword [Ld1] fsubr st0,st1 mov dword [cacheoffset],07FFFFFFFh fdivp st1,st0 sub esp,12 fld dword [0+8+edx] fsub dword [0+8+esi] fld dword [0+4+edx] fsub dword [0+4+esi] fld dword [0+0+edx] fsub dword [0+0+esi] mov edx,esp mov eax, dword [20+ebx] test al,al fmul st0,st3 fxch st1 fmul st0,st3 fxch st2 fmulp st3,st0 fadd dword [0+0+esi] fxch st1 fadd dword [0+4+esi] fxch st2 fadd dword [0+8+esi] fxch st1 fstp dword [0+0+esp] fstp dword [0+8+esp] fstp dword [0+4+esp] jz Ltestright mov dword [r_leftclipped],1 mov eax, dword [0+0+esp] mov dword [r_leftexit+0+0],eax mov eax, dword [0+4+esp] mov dword [r_leftexit+0+4],eax mov eax, dword [0+8+esp] mov dword [r_leftexit+0+8],eax jmp Lcontinue Ltestright: test ah,ah jz near Lcontinue mov dword [r_rightclipped],1 mov eax, dword [0+0+esp] mov dword [r_rightexit+0+0],eax mov eax, dword [0+4+esp] mov dword [r_rightexit+0+4],eax mov eax, dword [0+8+esp] mov dword [r_rightexit+0+8],eax jmp Lcontinue Lp3: mov dword [r_lastvertvalid],0 fld dword [Ld0] fld dword [Ld1] fsubr st0,st1 mov dword [cacheoffset],07FFFFFFFh fdivp st1,st0 sub esp,12 fld dword [0+8+edx] fsub dword [0+8+esi] fld dword [0+4+edx] fsub dword [0+4+esi] fld dword [0+0+edx] fsub dword [0+0+esi] mov eax, dword [20+ebx] test al,al fmul st0,st3 fxch st1 fmul st0,st3 fxch st2 fmulp st3,st0 fadd dword [0+0+esi] fxch st1 fadd dword [0+4+esi] fxch st2 fadd dword [0+8+esi] fxch st1 fstp dword [0+0+esp] fstp dword [0+8+esp] fstp dword [0+4+esp] mov esi,esp jz Ltestright2 mov dword [r_leftclipped],1 mov eax, dword [0+0+esp] mov dword [r_leftenter+0+0],eax mov eax, dword [0+4+esp] mov dword [r_leftenter+0+4],eax mov eax, dword [0+8+esp] mov dword [r_leftenter+0+8],eax jmp Lcontinue Ltestright2: test ah,ah jz near Lcontinue mov dword [r_rightclipped],1 mov eax, dword [0+0+esp] mov dword [r_rightenter+0+0],eax mov eax, dword [0+4+esp] mov dword [r_rightenter+0+4],eax mov eax, dword [0+8+esp] mov dword [r_rightenter+0+8],eax jmp Lcontinue LTransformAndProject: fld dword [0+0+esi] fsub dword [modelorg+0] fld dword [0+4+esi] fsub dword [modelorg+4] fld dword [0+8+esi] fsub dword [modelorg+8] fxch st2 fld st0 fmul dword [vpn+0] fld st1 fmul dword [vright+0] fxch st2 fmul dword [vup+0] fld st3 fmul dword [vpn+4] fld st4 fmul dword [vright+4] fxch st5 fmul dword [vup+4] fxch st1 faddp st3,st0 fxch st3 faddp st4,st0 faddp st2,st0 fld st3 fmul dword [vpn+8] fld st4 fmul dword [vright+8] fxch st5 fmul dword [vup+8] fxch st1 faddp st2,st0 fxch st4 faddp st3,st0 fxch st1 faddp st3,st0 fcom dword [Lfp_near_clip] fnstsw ax test ah,1 jz LNoClip fstp st0 fld dword [Lfp_near_clip] LNoClip: fdivr dword [float_1] fxch st1 fld dword [xscale] fmul st0,st2 fmulp st1,st0 fadd dword [xcenter] fcom dword [r_refdef+68] fnstsw ax test ah,1 jz LClampP0 fstp st0 fld dword [r_refdef+68] LClampP0: fcom dword [r_refdef+84] fnstsw ax test ah,045h jnz LClampP1 fstp st0 fld dword [r_refdef+84] LClampP1: fld st1 fmul dword [yscale] fmulp st3,st0 fxch st2 fsubr dword [ycenter] fcom dword [r_refdef+72] fnstsw ax test ah,1 jz LClampP2 fstp st0 fld dword [r_refdef+72] LClampP2: fcom dword [r_refdef+88] fnstsw ax test ah,045h jnz LClampP3 fstp st0 fld dword [r_refdef+88] LClampP3: ret engine/h2shared/r_edge.c000066400000000000000000000543041444734033100154250ustar00rootroot00000000000000/* r_edge.c * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" #include "d_local.h" /* FIXME the complex cases add new polys on most lines, so dont optimize for keeping them the same have multiple free span lists to try to get better coherence? low depth complexity -- 1 to 3 or so this breaks spans at every edge, even hidden ones (bad) have a sentinal at both ends? */ edge_t *auxedges; edge_t *r_edges, *edge_p, *edge_max; surf_t *surfaces, *surface_p, *surf_max; // surfaces are generated in back to front order by the bsp, so if a surf // pointer is greater than another one, it should be drawn in front // surfaces[1] is the background, and is used as the active surface stack edge_t *newedges[MAXHEIGHT]; edge_t *removeedges[MAXHEIGHT]; ASM_LINKAGE_BEGIN espan_t *span_p; float fv; int current_iv; int edge_head_u_shift20, edge_tail_u_shift20; ASM_LINKAGE_END edge_t edge_head; edge_t edge_tail; edge_t edge_aftertail; edge_t edge_sentinel; int TransCount; int r_currentkey; static void (*pdrawfunc)(void); static void (*pdrawTfunc)(void); #if !id386 && !id68k static void R_GenerateSpans (void); static void R_GenerateTSpans (void); #endif static void R_GenerateSpansBackward (void); static void R_TrailingEdge (surf_t *surf, edge_t *edge); static void R_LeadingEdgeBackwards (edge_t *edge); //============================================================================= #if 0 /* ============== R_DrawCulledPolys ============== */ static void R_DrawCulledPolys (void) { surf_t *s; msurface_t *pface; currententity = &r_worldentity; if (r_worldpolysbacktofront) { for (s = surface_p-1 ; s > &surfaces[1] ; s--) { if (!s->spans) continue; if (!(s->flags & SURF_DRAWBACKGROUND)) { pface = (msurface_t *)s->data; R_RenderPoly (pface, 15); } } } else { for (s = &surfaces[1] ; s < surface_p ; s++) { if (!s->spans) continue; if (!(s->flags & SURF_DRAWBACKGROUND)) { pface = (msurface_t *)s->data; R_RenderPoly (pface, 15); } } } } #endif /* ============== R_BeginEdgeFrame ============== */ void R_BeginEdgeFrame (void) { int v; edge_p = r_edges; edge_max = &r_edges[r_numallocatededges]; surface_p = &surfaces[2]; // background is surface 1, // surface 0 is a dummy surfaces[1].spans = NULL; // no background spans yet surfaces[1].flags = SURF_DRAWBACKGROUND; // put the background behind everything in the world if (r_draworder.integer) { pdrawfunc = R_GenerateSpansBackward; pdrawTfunc = R_GenerateTSpans; surfaces[1].key = 0; r_currentkey = 1; } else { pdrawfunc = R_GenerateSpans; pdrawTfunc = R_GenerateTSpans; surfaces[1].key = 0x7FFFFFFF; r_currentkey = 0; } // FIXME: set with memset for (v = r_refdef.vrect.y ; v < r_refdef.vrectbottom ; v++) { newedges[v] = removeedges[v] = NULL; } } #if !id386 && !id68k /* ============== R_InsertNewEdges Adds the edges in the linked list edgestoadd, adding them to the edges in the linked list edgelist. edgestoadd is assumed to be sorted on u, and non-empty (this is actually newedges[v]). edgelist is assumed to be sorted on u, with a sentinel at the end (actually, this is the active edge table starting at edge_head.next). ============== */ static void R_InsertNewEdges (edge_t *edgestoadd, edge_t *edgelist) { edge_t *next_edge; do { next_edge = edgestoadd->next; edgesearch: if (edgelist->u >= edgestoadd->u) goto addedge; edgelist=edgelist->next; if (edgelist->u >= edgestoadd->u) goto addedge; edgelist=edgelist->next; if (edgelist->u >= edgestoadd->u) goto addedge; edgelist=edgelist->next; if (edgelist->u >= edgestoadd->u) goto addedge; edgelist=edgelist->next; goto edgesearch; // insert edgestoadd before edgelist addedge: edgestoadd->next = edgelist; edgestoadd->prev = edgelist->prev; edgelist->prev->next = edgestoadd; edgelist->prev = edgestoadd; } while ((edgestoadd = next_edge) != NULL); } //static qboolean errorstate; /* ============== R_RemoveEdges ============== */ static void R_RemoveEdges (edge_t *pedge) { do { if (!pedge->next || !pedge->prev) { Con_Printf("Bad!!!!"); // errorstate = true; break; } pedge->next->prev = pedge->prev; pedge->prev->next = pedge->next; } while ((pedge = pedge->nextremove) != NULL); } /* ============== R_StepActiveU ============== */ static void R_StepActiveU (edge_t *pedge) { edge_t *pnext_edge, *pwedge; while (1) { nextedge: pedge->u += pedge->u_step; if (pedge->u < pedge->prev->u) goto pushback; pedge = pedge->next; pedge->u += pedge->u_step; if (pedge->u < pedge->prev->u) goto pushback; pedge = pedge->next; pedge->u += pedge->u_step; if (pedge->u < pedge->prev->u) goto pushback; pedge = pedge->next; pedge->u += pedge->u_step; if (pedge->u < pedge->prev->u) goto pushback; pedge = pedge->next; goto nextedge; pushback: if (pedge == &edge_aftertail) return; // push it back to keep it sorted pnext_edge = pedge->next; // pull the edge out of the edge list pedge->next->prev = pedge->prev; pedge->prev->next = pedge->next; // find out where the edge goes in the edge list pwedge = pedge->prev->prev; while (pwedge->u > pedge->u) { pwedge = pwedge->prev; } // put the edge back into the edge list pedge->next = pwedge->next; pedge->prev = pwedge; pedge->next->prev = pedge; pwedge->next = pedge; pedge = pnext_edge; if (pedge == &edge_tail) return; } } #endif /* !id386 && !id68k */ /* ============== R_CleanupSpan ============== */ static void R_CleanupSpan (void) { surf_t *surf; int iu; espan_t *span; // now that we've reached the right edge of the screen, we're done with any // unfinished surfaces, so emit a span for whatever's on top surf = surfaces[1].next; iu = edge_tail_u_shift20; if (iu > surf->last_u && !(surf->flags & SURF_TRANSLUCENT)) { span = span_p++; span->u = surf->last_u; span->count = iu - span->u; span->v = current_iv; span->pnext = surf->spans; surf->spans = span; } // reset spanstate for all surfaces in the surface stack do { surf->spanstate = 0; surf = surf->next; } while (surf != &surfaces[1]); } #if !id386 && !id68k static void R_CleanupSpanT (void) { surf_t *surf; int iu; espan_t *span; // now that we've reached the right edge of the screen, we're done with any // unfinished surfaces, so emit a span for whatever's on top surf = surfaces[1].next; iu = edge_tail_u_shift20; if (iu > surf->last_u && (surf->flags & SURF_TRANSLUCENT)) { span = span_p++; span->u = surf->last_u; span->count = iu - span->u; span->v = current_iv; span->pnext = surf->spans; surf->spans = span; } // reset spanstate for all surfaces in the surface stack do { surf->spanstate = 0; surf = surf->next; } while (surf != &surfaces[1]); } #endif /* !id386 && !id68k */ /* ============== R_LeadingEdgeBackwards ============== */ static void R_LeadingEdgeBackwards (edge_t *edge) { espan_t *span; surf_t *surf, *surf2; int iu; // it's adding a new surface in, so find the correct place surf = &surfaces[edge->surfs[1]]; // don't start a span if this is an inverted span, with the end // edge preceding the start edge (that is, we've already seen the // end edge) if (++surf->spanstate == 1) { surf2 = surfaces[1].next; if (surf->key > surf2->key) goto newtop; // if it's two surfaces on the same plane, the one that's already // active is in front, so keep going unless it's a bmodel if (surf->insubmodel && (surf->key == surf2->key)) { // must be two bmodels in the same leaf; don't care, // because they'll never be farthest anyway goto newtop; } continue_search: do { surf2 = surf2->next; } while (surf->key < surf2->key); if (surf->key == surf2->key) { // if it's two surfaces on the same plane, the one // that's already active is in front, so keep going // unless it's a bmodel if (!surf->insubmodel) goto continue_search; // must be two bmodels in the same leaf; don't care // which is really in front, because they'll never be // farthest anyway } goto gotposition; newtop: // emit a span (obscures current top) iu = edge->u >> 20; if (iu > surf2->last_u) { span = span_p++; span->u = surf2->last_u; span->count = iu - span->u; span->v = current_iv; span->pnext = surf2->spans; surf2->spans = span; } // set last_u on the new span surf->last_u = iu; gotposition: // insert before surf2 surf->next = surf2; surf->prev = surf2->prev; surf2->prev->next = surf; surf2->prev = surf; } } /* ============== R_TrailingEdge ============== */ static void R_TrailingEdge (surf_t *surf, edge_t *edge) { espan_t *span; int iu; // don't generate a span if this is an inverted span, with the end // edge preceding the start edge (that is, we haven't seen the // start edge yet) if (surf->flags & SURF_TRANSLUCENT) { FoundTrans = 1; return; } if (--surf->spanstate == 0) { if (surf->insubmodel) r_bmodelactive--; if (surf == surfaces[1].next) { // emit a span (current top going away) iu = edge->u >> 20; if (iu > surf->last_u) { span = span_p++; span->u = surf->last_u; span->count = iu - span->u; span->v = current_iv; span->pnext = surf->spans; surf->spans = span; } // set last_u on the surface below surf->next->last_u = iu; } surf->prev->next = surf->next; surf->next->prev = surf->prev; } } #if !id386 && !id68k static void R_TrailingEdgeT (surf_t *surf, edge_t *edge) { espan_t *span; int iu; // don't generate a span if this is an inverted span, with the end // edge preceding the start edge (that is, we haven't seen the // start edge yet) if (--surf->spanstate == 0) { if (surf->insubmodel) r_bmodelactive--; if (surf == surfaces[1].next) { // emit a span (current top going away) iu = edge->u >> 20; if (iu > surf->last_u && (surf->flags & SURF_TRANSLUCENT)) { span = span_p++; span->u = surf->last_u; span->count = iu - span->u; span->v = current_iv; span->pnext = surf->spans; surf->spans = span; } // set last_u on the surface below surf->next->last_u = iu; } surf->prev->next = surf->next; surf->next->prev = surf->prev; } } /* ============== R_LeadingEdge ============== */ static void R_LeadingEdge (edge_t *edge) { espan_t *span; surf_t *surf, *surf2; int iu; double fu, newzi, testzi, newzitop, newzibottom; if (edge->surfs[1]) { // it's adding a new surface in, so find the correct place surf = &surfaces[edge->surfs[1]]; if (surf->flags & SURF_TRANSLUCENT) { FoundTrans = 1; return; } // don't start a span if this is an inverted span, with the end // edge preceding the start edge (that is, we've already seen the // end edge) if (++surf->spanstate == 1) { if (surf->insubmodel) r_bmodelactive++; surf2 = surfaces[1].next; if (surf->key < surf2->key) goto newtop; // if it's two surfaces on the same plane, the one // that's already active is in front, so keep going // unless it's a bmodel if (surf->insubmodel && (surf->key == surf2->key)) { // must be two bmodels in the same leaf; sort on 1/z fu = (float)(edge->u - 0xFFFFF) * (1.0 / 0x100000); newzi = surf->d_ziorigin + fv*surf->d_zistepv + fu*surf->d_zistepu; newzibottom = newzi * 0.999; testzi = surf2->d_ziorigin + fv*surf2->d_zistepv + fu*surf2->d_zistepu; if (newzibottom >= testzi) { goto newtop; } newzitop = newzi * 1.001; if (newzitop >= testzi) { if (surf->d_zistepu >= surf2->d_zistepu) { goto newtop; } } } continue_search: do { surf2 = surf2->next; } while (surf->key > surf2->key); if (surf->key == surf2->key) { // if it's two surfaces on the same plane, the one // that's already active is in front, so keep going // unless it's a bmodel if (!surf->insubmodel) goto continue_search; // must be two bmodels in the same leaf; sort on 1/z fu = (float)(edge->u - 0xFFFFF) * (1.0 / 0x100000); newzi = surf->d_ziorigin + fv*surf->d_zistepv + fu*surf->d_zistepu; newzibottom = newzi * 0.999; testzi = surf2->d_ziorigin + fv*surf2->d_zistepv + fu*surf2->d_zistepu; if (newzibottom >= testzi) { goto gotposition; } newzitop = newzi * 1.001; if (newzitop >= testzi) { if (surf->d_zistepu >= surf2->d_zistepu) { goto gotposition; } } goto continue_search; } goto gotposition; newtop: // emit a span (obscures current top) iu = edge->u >> 20; if (iu > surf2->last_u && !(surf2->flags & SURF_TRANSLUCENT)) { span = span_p++; span->u = surf2->last_u; span->count = iu - span->u; span->v = current_iv; span->pnext = surf2->spans; surf2->spans = span; } // set last_u on the new span surf->last_u = iu; gotposition: // insert before surf2 surf->next = surf2; surf->prev = surf2->prev; surf2->prev->next = surf; surf2->prev = surf; } } } /* ============== R_LeadingEdgeT ============== */ static void R_LeadingEdgeT (edge_t *edge) { espan_t *span; surf_t *surf, *surf2; int iu; double fu, newzi, testzi, newzitop, newzibottom; if (edge->surfs[1]) { // it's adding a new surface in, so find the correct place surf = &surfaces[edge->surfs[1]]; // don't start a span if this is an inverted span, with the end // edge preceding the start edge (that is, we've already seen the // end edge) if (++surf->spanstate == 1) { if (surf->insubmodel) r_bmodelactive++; surf2 = surfaces[1].next; if (surf->key < surf2->key) goto newtop; // if it's two surfaces on the same plane, the one // that's already active is in front, so keep going // unless it's a bmodel if (surf->insubmodel && (surf->key == surf2->key)) { // must be two bmodels in the same leaf; sort on 1/z fu = (float)(edge->u - 0xFFFFF) * (1.0 / 0x100000); newzi = surf->d_ziorigin + fv*surf->d_zistepv + fu*surf->d_zistepu; newzibottom = newzi * 0.999; testzi = surf2->d_ziorigin + fv*surf2->d_zistepv + fu*surf2->d_zistepu; if (newzibottom >= testzi) { goto newtop; } newzitop = newzi * 1.001; if (newzitop >= testzi) { if (surf->d_zistepu >= surf2->d_zistepu) { goto newtop; } } } continue_search: do { surf2 = surf2->next; } while (surf->key > surf2->key); if (surf->key == surf2->key) { // if it's two surfaces on the same plane, the one // that's already active is in front, so keep going // unless it's a bmodel if (!surf->insubmodel) goto continue_search; // must be two bmodels in the same leaf; sort on 1/z fu = (float)(edge->u - 0xFFFFF) * (1.0 / 0x100000); newzi = surf->d_ziorigin + fv*surf->d_zistepv + fu*surf->d_zistepu; newzibottom = newzi * 0.999; testzi = surf2->d_ziorigin + fv*surf2->d_zistepv + fu*surf2->d_zistepu; if (newzibottom >= testzi) { goto gotposition; } newzitop = newzi * 1.001; if (newzitop >= testzi) { if (surf->d_zistepu >= surf2->d_zistepu) { goto gotposition; } } goto continue_search; } goto gotposition; newtop: // emit a span (obscures current top) iu = edge->u >> 20; if (iu > surf2->last_u && (surf2->flags & SURF_TRANSLUCENT)) { span = span_p++; span->u = surf2->last_u; span->count = iu - span->u; span->v = current_iv; span->pnext = surf2->spans; surf2->spans = span; } // set last_u on the new span surf->last_u = iu; gotposition: // insert before surf2 surf->next = surf2; surf->prev = surf2->prev; surf2->prev->next = surf; surf2->prev = surf; } } } /* ============== R_GenerateSpans ============== */ static void R_GenerateSpans (void) { edge_t *edge; surf_t *surf; r_bmodelactive = 0; FoundTrans = 0; // clear active surfaces to just the background surface surfaces[1].next = surfaces[1].prev = &surfaces[1]; surfaces[1].last_u = edge_head_u_shift20; // generate spans // note: edge->surfs[0] is trailing // edge->surfs[1] is leading for (edge = edge_head.next ; edge != &edge_tail; edge = edge->next) { if (edge->surfs[0]) { // it has a left surface, so a surface is going // away for this span surf = &surfaces[edge->surfs[0]]; R_TrailingEdge (surf, edge); if (!edge->surfs[1]) continue; } R_LeadingEdge (edge); } R_CleanupSpan (); #if 0 /* also disabled in asm code: see the commented * out cmp / jne (under LPatch4) in r_edgea.asm */ if (!FoundTrans) return; // clear active surfaces to just the background surface surfaces[1].next = surfaces[1].prev = &surfaces[1]; surfaces[1].last_u = edge_head_u_shift20; // generate spans for (edge = edge_head.next ; edge != &edge_tail; edge = edge->next) { if (edge->surfs[0]) { // it has a left surface, so a surface is going // away for this span surf = &surfaces[edge->surfs[0]]; R_TrailingEdgeT (surf, edge); if (!edge->surfs[1]) continue; } R_LeadingEdgeT (edge); } R_CleanupSpanT (); #endif } /* ============== R_GenerateTSpans ============== */ static void R_GenerateTSpans (void) { edge_t *edge; surf_t *surf; r_bmodelactive = 0; // clear active surfaces to just the background surface surfaces[1].next = surfaces[1].prev = &surfaces[1]; surfaces[1].last_u = edge_head_u_shift20; // generate spans for (edge = edge_head.next ; edge != &edge_tail; edge = edge->next) { if (edge->surfs[0]) { // it has a left surface, so a surface is going // away for this span surf = &surfaces[edge->surfs[0]]; R_TrailingEdgeT (surf, edge); if (!edge->surfs[1]) continue; } R_LeadingEdgeT (edge); } R_CleanupSpanT (); } #endif /* !id386 && !id68k */ /* ============== R_GenerateSpansBackward ============== */ static void R_GenerateSpansBackward (void) { edge_t *edge; r_bmodelactive = 0; // clear active surfaces to just the background surface surfaces[1].next = surfaces[1].prev = &surfaces[1]; surfaces[1].last_u = edge_head_u_shift20; // generate spans for (edge = edge_head.next ; edge != &edge_tail; edge = edge->next) { if (edge->surfs[0]) R_TrailingEdge (&surfaces[edge->surfs[0]], edge); if (edge->surfs[1]) R_LeadingEdgeBackwards (edge); } R_CleanupSpan (); } static qboolean TransList[SCAN_SIZE]; static espan_t *max_span_p; /* ============== R_ScanEdges Input: newedges[] array this has links to edges, which have links to surfaces Output: Each surface has a linked list of its visible spans ============== */ void R_ScanEdges (qboolean Translucent) { int iv, bottom; byte basespans[MAXSPANS*sizeof(espan_t)+CACHE_SIZE]; espan_t *basespan_p; surf_t *s; if (Translucent && r_draworder.integer) return; basespan_p = (espan_t *) ((intptr_t)(basespans + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); max_span_p = &basespan_p[MAXSPANS - r_refdef.vrect.width]; span_p = basespan_p; // clear active edges to just the background edges around the whole screen // FIXME: most of this only needs to be set up once edge_head.u = r_refdef.vrect.x << 20; edge_head_u_shift20 = edge_head.u >> 20; edge_head.u_step = 0; edge_head.prev = NULL; edge_head.next = &edge_tail; edge_head.surfs[0] = 0; edge_head.surfs[1] = 1; edge_tail.u = (r_refdef.vrectright << 20) + 0xFFFFF; edge_tail_u_shift20 = edge_tail.u >> 20; edge_tail.u_step = 0; edge_tail.prev = &edge_head; edge_tail.next = &edge_aftertail; edge_tail.surfs[0] = 1; edge_tail.surfs[1] = 0; edge_aftertail.u = -1; // force a move edge_aftertail.u_step = 0; edge_aftertail.next = &edge_sentinel; edge_aftertail.prev = &edge_tail; // FIXME: do we need this now that we clamp x in r_draw.c? edge_sentinel.u = 32767 << 16; // make sure nothing sorts past this edge_sentinel.prev = &edge_aftertail; // // process all scan lines // bottom = r_refdef.vrectbottom - 1; if (!Translucent) { // memset(TransList,0,sizeof(TransList)); TransCount = 0; } for (iv = r_refdef.vrect.y ; iv < bottom ; iv++) { current_iv = iv; fv = (float)iv; // mark that the head (background start) span is pre-included surfaces[1].spanstate = 1; if (newedges[iv]) { R_InsertNewEdges (newedges[iv], edge_head.next); } if (!Translucent) { (*pdrawfunc) (); TransList[iv] = FoundTrans; TransCount += FoundTrans; } else if (TransList[iv]) { (*pdrawTfunc) (); } // flush the span list if we can't be sure we have enough // spans left for the next scan if (span_p >= max_span_p) { VID_UnlockBuffer (); S_ExtraUpdate (); // don't let sound get messed up if going slow VID_LockBuffer (); #if 0 if (r_drawculledpolys) { R_DrawCulledPolys (); } else #endif { D_DrawSurfaces (Translucent); } // clear the surface span pointers for (s = &surfaces[1] ; s < surface_p ; s++) s->spans = NULL; span_p = basespan_p; } if (removeedges[iv]) R_RemoveEdges (removeedges[iv]); if (edge_head.next != &edge_tail) R_StepActiveU (edge_head.next); } // do the last scan (no need to step or sort or remove on the last scan) current_iv = iv; fv = (float)iv; // mark that the head (background start) span is pre-included surfaces[1].spanstate = 1; if (newedges[iv]) R_InsertNewEdges (newedges[iv], edge_head.next); if (!Translucent) { (*pdrawfunc) (); TransList[iv] = FoundTrans; TransCount += FoundTrans; } else if (TransList[iv]) (*pdrawTfunc) (); // draw whatever's left in the span list #if 0 if (r_drawculledpolys) R_DrawCulledPolys (); else #endif { D_DrawSurfaces (Translucent); } } engine/h2shared/r_edge68k.s000066400000000000000000001154651444734033100160040ustar00rootroot00000000000000** ** Quake for AMIGA ** r_edge.c assembler implementations by Frank Wille ** Adapted for Hexen II by Szilard Biro ** ; INCLUDE "quakedef68k.i" SURF_NEXT equ 0 SURF_PREV equ 4 SURF_SPANS equ 8 SURF_KEY equ 12 SURF_LAST_U equ 16 SURF_SPANSTATE equ 20 SURF_FLAGS equ 24 SURF_DATA equ 28 SURF_ENTITY equ 32 SURF_NEARZI equ 36 SURF_INSUBMODEL equ 40 SURF_D_ZIORIGIN equ 44 SURF_D_ZISTEPU equ 48 SURF_D_ZISTEPV equ 52 SURF_SIZEOF_EXP equ 6 SURF_SIZEOF equ 64 EDGE_U equ 0 EDGE_U_STEP equ 4 EDGE_PREV equ 8 EDGE_NEXT equ 12 EDGE_SURFS equ 16 EDGE_NEXTREMOVE equ 20 EDGE_NEARZI equ 24 EDGE_OWNER equ 28 EDGE_SIZEOF_EXP equ 5 EDGE_SIZEOF equ 32 ;XREF _r_bmodelactive XREF _surfaces XREF _span_p XREF _current_iv XREF _fv XREF _edge_head XREF _edge_tail XREF _edge_aftertail XREF _edge_head_u_shift20 ;XREF _R_CleanupSpan XREF _FoundTrans XREF _edge_tail_u_shift20 XDEF _R_RemoveEdges XDEF _R_InsertNewEdges XDEF _R_StepActiveU XDEF _R_GenerateSpans XDEF _R_GenerateTSpans fpu ****************************************************************************** * * void _R_InsertNewEdges (edge_t *edgestoadd, edge_t *edgelist) * ****************************************************************************** cnop 0,4 _R_InsertNewEdges ***** stackframe rsreset .intregs rs.l 1 rs.l 1 .edgestoadd rs.l 1 .edgelist rs.l 1 * do * { * next_edge = edgestoadd->next; *edgesearch: * if (edgelist->u >= edgestoadd->u) * goto addedge; * edgelist=edgelist->next; * if (edgelist->u >= edgestoadd->u) * goto addedge; * edgelist=edgelist->next; * if (edgelist->u >= edgestoadd->u) * goto addedge; * edgelist=edgelist->next; * if (edgelist->u >= edgestoadd->u) * goto addedge; * edgelist=edgelist->next; * goto edgesearch; * * // insert edgestoadd before edgelist *addedge: * edgestoadd->next = edgelist; * edgestoadd->prev = edgelist->prev; * edgelist->prev->next = edgestoadd; * edgelist->prev = edgestoadd; * } while ((edgestoadd = next_edge) != NULL); move.l a2,-(sp) move.l .edgestoadd(sp),a0 move.l .edgelist(sp),a1 move.l EDGE_U(a0),d1 .loop cmp.l EDGE_U(a1),d1 ;if (edgelist->u >= edgestoadd->u) ble.b .addedge ;goto addedge move.l EDGE_NEXT(a1),a1 ;edgelist=edgelist->next cmp.l EDGE_U(a1),d1 ble.b .addedge move.l EDGE_NEXT(a1),a1 cmp.l EDGE_U(a1),d1 ble.b .addedge move.l EDGE_NEXT(a1),a1 cmp.l EDGE_U(a1),d1 ble.b .addedge move.l EDGE_NEXT(a1),a1 bra.b .loop .addedge move.l EDGE_NEXT(a0),d0 ;next_edge = edgestoadd->next move.l a1,EDGE_NEXT(a0) ;edgestoadd->next = edgelist move.l EDGE_PREV(a1),a2 move.l a2,EDGE_PREV(a0) ;edgestoadd->prev = edgelist->prev move.l a0,EDGE_NEXT(a2) ;edgelist->prev->next = edgestoadd move.l a0,EDGE_PREV(a1) ;edgelist->prev = edgestoadd tst.l d0 ;while ((edgestoadd = next_edge) != NULL) beq.b .end move.l d0,a0 move.l EDGE_U(a0),d1 bra.b .loop .end move.l (sp)+,a2 rts ****************************************************************************** * * void _R_RemoveEdges (edge_t *pedge) * ****************************************************************************** cnop 0,4 _R_RemoveEdges ***** stackframe rsreset .intregs rs.l 1 rs.l 1 .pedge rs.l 1 * do * { * pedge->next->prev = pedge->prev; * pedge->prev->next = pedge->next; * } while ((pedge = pedge->nextremove) != NULL); move.l a2,-(sp) move.l .pedge(sp),a0 .loop move.l EDGE_NEXT(a0),a1 move.l EDGE_PREV(a0),a2 move.l a2,EDGE_PREV(a1) ;pedge->next->prev = pedge->prev move.l a1,EDGE_NEXT(a2) ;pedge->prev->next = pedge->next move.l EDGE_NEXTREMOVE(a0),a0 ;while ((pedge = pedge->nextremove) != NULL) tst.l a0 bne.b .loop move.l (sp)+,a2 rts ****************************************************************************** * * void _R_StepActiveU (edge_t *pedge) * ****************************************************************************** cnop 0,4 _R_StepActiveU ***** stackframe rsreset .intregs rs.l 5 rs.l 1 .pedge rs.l 1 *nextedge: * pedge->u += pedge->u_step; * if (pedge->u < pedge->prev->u) * goto pushback; * pedge = pedge->next; * * pedge->u += pedge->u_step; * if (pedge->u < pedge->prev->u) * goto pushback; * pedge = pedge->next; * * pedge->u += pedge->u_step; * if (pedge->u < pedge->prev->u) * goto pushback; * pedge = pedge->next; * * pedge->u += pedge->u_step; * if (pedge->u < pedge->prev->u) * goto pushback; * pedge = pedge->next; * * goto nextedge; movem.l a2-a6,-(sp) move.l .pedge(sp),a0 move.l EDGE_PREV(a0),a1 move.l EDGE_U(a1),d1 lea _edge_aftertail,a2 lea _edge_tail,a3 .loop move.l EDGE_U(a0),d0 add.l EDGE_U_STEP(a0),d0 move.l d0,EDGE_U(a0) ;pedge->u += pedge->u_step cmp.l d1,d0 ;if (pedge->u < pedge->prev->u) blt.b .pushback ;goto pushback move.l d0,d1 move.l EDGE_NEXT(a0),a0 ;pedge = pedge->next move.l EDGE_U(a0),d0 add.l EDGE_U_STEP(a0),d0 move.l d0,EDGE_U(a0) cmp.l d1,d0 blt.b .pushback move.l d0,d1 move.l EDGE_NEXT(a0),a0 move.l EDGE_U(a0),d0 add.l EDGE_U_STEP(a0),d0 move.l d0,EDGE_U(a0) cmp.l d1,d0 blt.b .pushback move.l d0,d1 move.l EDGE_NEXT(a0),a0 move.l EDGE_U(a0),d0 add.l EDGE_U_STEP(a0),d0 move.l d0,EDGE_U(a0) cmp.l d1,d0 blt.b .pushback move.l d0,d1 move.l EDGE_NEXT(a0),a0 bra.b .loop * if (pedge == &edge_aftertail) * return; * * // push it back to keep it sorted * pnext_edge = pedge->next; * * // pull the edge out of the edge list * pedge->next->prev = pedge->prev; * pedge->prev->next = pedge->next; * * // find out where the edge goes in the edge list * pwedge = pedge->prev->prev; * * while (pwedge->u > pedge->u) * { * pwedge = pwedge->prev; * } * * // put the edge back into the edge list * pedge->next = pwedge->next; * pedge->prev = pwedge; * pedge->next->prev = pedge; * pwedge->next = pedge; * * pedge = pnext_edge; * if (pedge == &edge_tail) * return; .pushback cmp.l a0,a2 ;if (pedge == &edge_aftertail) beq.w .end ;return move.l EDGE_NEXT(a0),a4 ;pnext_edge = pedge->next move.l EDGE_PREV(a0),a5 move.l a5,EDGE_PREV(a4) ;pedge->next->prev = pedge->prev move.l a4,EDGE_NEXT(a5) ;pedge->prev->next = pedge->next move.l EDGE_PREV(a5),a5 ;pwedge = pedge->prev->prev .loop2 cmp.l EDGE_U(a5),d0 ;while (pwedge->u > pedge->u) bgt.b .cont move.l EDGE_PREV(a5),a5 ;pwedge = pwedge->prev bra.b .loop2 .cont move.l EDGE_NEXT(a5),a1 move.l a1,EDGE_NEXT(a0) ;pedge->next = pwedge->next move.l a5,EDGE_PREV(a0) ;pedge->prev = pwedge move.l a0,EDGE_PREV(a1) ;pedge->next->prev = pedge move.l a0,EDGE_NEXT(a5) ;pwedge->next = pedge move.l a4,a0 ;pedge = pnext_edge cmp.l a3,a0 ;if (pedge == &edge_tail) bne.b .loop .end movem.l (sp)+,a2-a6 rts ****************************************************************************** * * void _R_GenerateSpans (void) * * R_TrailingEdge, R_LeadingEdge and R_CleanupSpan are inlined * * notes: * Increment and Decrement of _r_bmodelactive removed, because it's * obsolete here * ****************************************************************************** cnop 0,4 _R_GenerateSpans ****** prologue movem.l d2-d7/a2-a6,-(sp) fmovem.x fp2-fp7,-(sp) * r_bmodelactive = 0; * FoundTrans = 0; ;clr.l _r_bmodelactive clr.l _FoundTrans *// clear active surfaces to just the background surface * surfaces[1].next = surfaces[1].prev = &surfaces[1]; * surfaces[1].last_u = edge_head_u_shift20; move.l _current_iv,d2 ;d2 = current_iv move.l _surfaces,a3 ;a3 = surfaces lea 1*SURF_SIZEOF(a3),a4 ;a4 = &surfaces[1] move.l a4,SURF_NEXT(a4) ;surfaces[1].next = a4 move.l a4,SURF_PREV(a4) ;surfaces[1].prev = a4 move.l _edge_head_u_shift20,SURF_LAST_U(a4) ;surfaces[1].last_u = edge_head_u_shift20 * for (edge = edge_head.next ; edge != &edge_tail; edge = edge->next) move.l _edge_head+EDGE_NEXT,a5 ;a5 = edge->next lea _edge_tail,a6 ;a6 = &edge_tail move.l _span_p,a4 bra.w .try .loop move.l EDGE_U(a5),d4 move.l d4,d7 ;d7 = edge->u moveq #20,d0 asr.l d0,d4 ;d4 = edge->u >> 20 * if (edge->surfs[0]) move.l EDGE_SURFS(a5),d1 ;d1 = edge->surfs[0] move.l d1,d0 ;if (edge->surfs[0]) swap d0 ext.l d0 beq.b .cont asl.l #SURF_SIZEOF_EXP,d0 lea 0(a3,d0.l),a0 ;a0 = &surfaces[edge->surfs[0]]; ****** R_TrailingEdge (inlined) * if (surf->flags & SURF_TRANSLUCENT) * { * FoundTrans = 1; * return; * } * * if (--surf->spanstate == 0) * { * if (surf->insubmodel) * r_bmodelactive--; * * if (surf == surfaces[1].next) * { * // emit a span (current top going away) * iu = edge->u >> 20; * if (iu > surf->last_u) * { * span = span_p++; * span->u = surf->last_u; * span->count = iu - span->u; * span->v = current_iv; * span->pnext = surf->spans; * surf->spans = span; * } * * // set last_u on the surface below * surf->next->last_u = iu; * } * * surf->prev->next = surf->next; * surf->next->prev = surf->prev; * } ; translucent btst.b #7,SURF_FLAGS+3(a0) ;if (surf->flags & SURF_TRANSLUCENT) beq.b .te_notfound move.l #1,_FoundTrans bra.b .cont .te_notfound ; translucent subq.l #1,SURF_SPANSTATE(a0) ;if (--surf->spanstate) == 0 bne.b .cont move.l SURF_NEXT(a0),a1 cmp.l 1*SURF_SIZEOF+SURF_NEXT(a3),a0 ;if (surf==surfaces[1].next) bne.b .te_cont2 move.l d4,d0 ;iu = edge->u >> 20 move.l SURF_LAST_U(a0),d5 move.l d0,SURF_LAST_U(a1) ;surf->next->last_u = iu sub.l d5,d0 ;if (iu > surf->last_u) ble.b .te_cont2 move.l SURF_SPANS(a0),d3 move.l a4,SURF_SPANS(a0) ;surf->spans = span move.l d5,(a4)+ ;span->u = surf->last_u move.l d2,(a4)+ ;span->v = current_iv move.l d0,(a4)+ ;span->count = iu - span->u move.l d3,(a4)+ ;span->pnext = surf->spans .te_cont2 move.l SURF_PREV(a0),a2 move.l a1,SURF_NEXT(a2) ;surf->prev->next = surf->next move.l a2,SURF_PREV(a1) ;surf->next->prev = surf->prev ****** end of R_TrailingEdge .cont ****** R_LeadingEdge (inlined) * if (edge->surfs[1]) * { * // it's adding a new surface in, so find the correct place * surf = &surfaces[edge->surfs[1]]; * ext.l d1 ;if (edge->surfs[1]) beq.w .next asl.l #SURF_SIZEOF_EXP,d1 lea 0(a3,d1.l),a1 ;surf = &surfaces[edge->surfs[1]] * if (surf->flags & SURF_TRANSLUCENT) * { * FoundTrans = 1; * return; * } ; translucent btst.b #7,SURF_FLAGS+3(a1) ;if (surf->flags & SURF_TRANSLUCENT) beq.b .le_notfound move.l #1,_FoundTrans bra.w .next .le_notfound ; translucent * * // don't start a span if this is an inverted span, with the end * // edge preceding the start edge (that is, we've already seen the * // end edge) * if (++surf->spanstate == 1) * { * if (surf->insubmodel) * r_bmodelactive++; * * surf2 = surfaces[1].next; * * if (surf->key < surf2->key) * goto newtop; * // if it's two surfaces on the same plane, the one that's already * // active is in front, so keep going unless it's a bmodel * if (surf->insubmodel && (surf->key == surf2->key)) * { move.l SURF_SPANSTATE(a1),d0 ;if (++surf->spanstate == 1) beq.b .le_zero addq.l #1,SURF_SPANSTATE(a1) bra.w .next .le_zero addq.l #1,d0 move.l d0,SURF_SPANSTATE(a1) move.l SURF_INSUBMODEL(a1),d5 ;if (surf->insubmodel) move.l 1*SURF_SIZEOF+SURF_NEXT(a3),a0 ;surf2 = surfaces[1].next move.l SURF_KEY(a1),d6 moveq #0,d3 cmp.l SURF_KEY(a0),d6 ;if (surf->key < surf2->key) blt.b .le_newtop ;goto newtop bgt.b .le_search tst.b d5 beq.b .le_search moveq #-1,d3 move.l d7,d0 sub.l #$fffff,d0 ;edge->u - 0xFFFFF fmove.l d0,fp0 ;(float)(edge->u - 0xFFFFF) fmul.s #(1.0/(16*65536)),fp0 ;fu = fp0 * (1 / $100000) fmove.s _fv,fp1 ;fv fmove.s SURF_D_ZIORIGIN(a1),fp2 fmove.s SURF_D_ZISTEPV(a1),fp3 fmul fp1,fp3 ;fp1 = fv * surf->d_zistepv fadd fp3,fp2 fmove.s SURF_D_ZISTEPU(a1),fp3 fmove fp3,fp4 ;fp4 = surf->d_zistepu fmul fp0,fp3 ;fp3 = fu * surf->d_zistepu fadd fp3,fp2 ;newzi = d_ziorigin + fp1 + fp3 fmove fp2,fp3 fmul.s #0.999,fp2 ;newzibottom = newzi * 0.999 fmul.s #1.001,fp3 ;newzitop = newzi * 1.001 * // must be two bmodels in the same leaf; sort on 1/z * fu = (float)(edge->u - 0xFFFFF) * (1.0 / 0x100000); * newzi = surf->d_ziorigin + fv*surf->d_zistepv + * fu*surf->d_zistepu; * newzibottom = newzi * 0.999; * * testzi = surf2->d_ziorigin + fv*surf2->d_zistepv + * fu*surf2->d_zistepu; * * if (newzibottom >= testzi) * { * goto newtop; * } * * newzitop = newzi * 1.001; * if (newzitop >= testzi) * { * if (surf->d_zistepu >= surf2->d_zistepu) * { * goto newtop; * } * } fmove.s SURF_D_ZIORIGIN(a0),fp5 fmove.s SURF_D_ZISTEPV(a0),fp6 fmul fp1,fp6 ;fp1 = fv * surf2->d_zistepv fadd fp6,fp5 fmove.s SURF_D_ZISTEPU(a0),fp6 fmove fp6,fp7 fmul fp0,fp6 ;fp3 = fu * surf2->d_zistepu fadd fp6,fp5 ;testzi = d_ziorigin + fp1 + fp3 fcmp fp5,fp2 ;if (newzibottom >= testzi) fbge.w .le_newtop ;goto newtop fcmp fp5,fp3 ;if (newzitop >= testzi) fblt.w .le_search fcmp fp7,fp4 ;if (surf->d_zistepu >= surf2->d_zistepu) fbge.w .le_newtop ;goto newtop * do * { * surf2 = surf2->next; * } while (surf->key > surf2->key); * .le_search move.l SURF_NEXT(a0),a0 ;surf2 = surf2->next cmp.l SURF_KEY(a0),d6 ;while (surf->key > surf2->key) bgt.b .le_search * if (surf->key == surf2->key) * { * // if it's two surfaces on the same plane, the one that's already * // active is in front, so keep going unless it's a bmodel * if (!surf->insubmodel) * goto continue_search; bne.b .le_gotposition tst.b d5 beq.b .le_search tst d3 bne.b .le_precalc_done moveq #-1,d3 move.l d7,d0 sub.l #$fffff,d0 ;edge->u - 0xFFFFF fmove.l d0,fp0 ;(float)(edge->u - 0xFFFFF) fmul.s #(1.0/(16*65536)),fp0 ;fu = fp0 * (1 / $100000) fmove.s _fv,fp1 ;fv fmove.s SURF_D_ZIORIGIN(a1),fp2 fmove.s SURF_D_ZISTEPV(a1),fp3 fmul fp1,fp3 ;fp1 = fv * surf->d_zistepv fadd fp3,fp2 fmove.s SURF_D_ZISTEPU(a1),fp3 fmove fp3,fp4 ;fp4 = surf->d_zistepu fmul fp0,fp3 ;fp3 = fu * surf->d_zistepu fadd fp3,fp2 ;newzi = d_ziorigin + fp1 + fp3 fmove fp2,fp3 fmul.s #0.999,fp2 ;newzibottom = newzi * 0.999 fmul.s #1.001,fp3 ;newzitop = newzi * 1.001 .le_precalc_done * // must be two bmodels in the same leaf; sort on 1/z * fu = (float)(edge->u - 0xFFFFF) * (1.0 / 0x100000); * newzi = surf->d_ziorigin + fv*surf->d_zistepv + * fu*surf->d_zistepu; * newzibottom = newzi * 0.999; * * testzi = surf2->d_ziorigin + fv*surf2->d_zistepv + * fu*surf2->d_zistepu; * * if (newzibottom >= testzi) * { * goto gotposition; * } * * newzitop = newzi * 1.001; * if (newzitop >= testzi) * { * if (surf->d_zistepu >= surf2->d_zistepu) * { * goto gotposition; * } * } * * goto continue_search; * } * * goto gotposition; fmove.s SURF_D_ZIORIGIN(a0),fp5 fmove.s SURF_D_ZISTEPV(a0),fp6 fmul fp1,fp6 ;fp1 = fv * surf2->d_zistepv fadd fp6,fp5 fmove.s SURF_D_ZISTEPU(a0),fp6 fmove fp6,fp7 fmul fp0,fp6 ;fp3 = fu * surf2->d_zistepu fadd fp6,fp5 ;testzi = d_ziorigin + fp1 + fp3 fcmp fp5,fp2 ;if (newzibottom >= testzi) fbge.w .le_gotposition ;goto gotposition fcmp fp5,fp3 ;if (newzitop >= testzi) fblt.w .le_search fcmp fp7,fp4 ;if (surf->d_zistepu >= surf2->d_zistepu) fbge.w .le_gotposition ;goto gotposition bra.b .le_search * // emit a span (obscures current top) * iu = edge->u >> 20; * * if (iu > surf2->last_u && !(surf2->flags & SURF_TRANSLUCENT)) * { * span = span_p++; * span->u = surf2->last_u; * span->count = iu - span->u; * span->v = current_iv; * span->pnext = surf2->spans; * surf2->spans = span; * } * * // set last_u on the new span * surf->last_u = iu; .le_newtop move.l SURF_LAST_U(a0),d5 move.l d4,SURF_LAST_U(a1) ;surf->last_u = iu sub.l d5,d4 ;if (iu > surf2->last_u) ble.b .le_cont2 ; translucent btst.b #7,SURF_FLAGS+3(a0) ;if (!(surf->flags & SURF_TRANSLUCENT)) bne.b .le_cont2 ; translucent move.l SURF_SPANS(a0),d3 move.l a4,SURF_SPANS(a0) ;surf2->spans = span move.l d5,(a4)+ ;span->u = surf2->last_u move.l d2,(a4)+ ;span->v = current_iv move.l d4,(a4)+ ;span->count = iu - span->u move.l d3,(a4)+ ;span->pnext = surf2->spans .le_cont2 * // insert before surf2 * surf->next = surf2; * surf->prev = surf2->prev; * surf2->prev->next = surf; * surf2->prev = surf; .le_gotposition move.l SURF_PREV(a0),a2 move.l a2,SURF_PREV(a1) ;surf->prev = surf2->prev move.l a0,SURF_NEXT(a1) ;surf->next = surf2 move.l a1,SURF_NEXT(a2) ;surf2->prev->next = surf move.l a1,SURF_PREV(a0) ;surf2->prev = surf ****** end of R_LeadingEdge .next move.l EDGE_NEXT(a5),a5 .try cmp.l a5,a6 ;if (edge != &edge_tail) bne.b .loop ****** R_CleanupSpan (inlined) * // now that we've reached the right edge of the screen, we're done with any * // unfinished surfaces, so emit a span for whatever's on top * surf = surfaces[1].next; * iu = edge_tail_u_shift20; * if (iu > surf->last_u && !(surf->flags & SURF_TRANSLUCENT)) * { * span = span_p++; * span->u = surf->last_u; * span->count = iu - span->u; * span->v = current_iv; * span->pnext = surf->spans; * surf->spans = span; * } move.l 1*SURF_SIZEOF+SURF_NEXT(a3),a0 ;surf = surfaces[1].next move.l _edge_tail_u_shift20,d4 ;iu = edge_tail_u_shift20 move.l SURF_LAST_U(a0),d5 sub.l d5,d4 ;if (iu > surf2->last_u) ble.b .cs_cont ; translucent btst.b #7,SURF_FLAGS+3(a0) ;if (!(surf->flags & SURF_TRANSLUCENT)) bne.b .cs_cont ; translucent move.l SURF_SPANS(a0),d3 move.l a4,SURF_SPANS(a0) ;surf->spans = span move.l d5,(a4)+ ;span->u = surf->last_u move.l d2,(a4)+ ;span->v = current_iv move.l d4,(a4)+ ;span->count = iu - span->u move.l d3,(a4)+ ;span->pnext = surf->spans .cs_cont * // reset spanstate for all surfaces in the surface stack * do * { * surf->spanstate = 0; * surf = surf->next; * } while (surf != &surfaces[1]); lea 1*SURF_SIZEOF(a3),a1 ;a1 = &surfaces[1] .cs_loop clr.l SURF_SPANSTATE(a0) ;surf->spanstate = 0 move.l SURF_NEXT(a0),a0 ;surf = surf->next cmp.l a0,a1 ;while (surf != &surfaces[1]) bne.b .cs_loop ****** end of R_CleanupSpan move.l a4,_span_p ;jsr _R_CleanupSpan fmovem.x (sp)+,fp2-fp7 movem.l (sp)+,d2-d7/a2-a6 rts ****************************************************************************** * * void _R_GenerateTSpans (void) * * R_TrailingEdgeT, R_LeadingEdgeT and R_CleanupSpanT are inlined * * notes: * Increment and Decrement of _r_bmodelactive removed, because it's * obsolete here * ****************************************************************************** cnop 0,4 _R_GenerateTSpans ****** prologue movem.l d2-d7/a2-a6,-(sp) fmovem.x fp2-fp7,-(sp) * r_bmodelactive = 0; ;clr.l _r_bmodelactive *// clear active surfaces to just the background surface * surfaces[1].next = surfaces[1].prev = &surfaces[1]; * surfaces[1].last_u = edge_head_u_shift20; move.l _current_iv,d2 ;d2 = current_iv move.l _surfaces,a3 ;a3 = surfaces lea 1*SURF_SIZEOF(a3),a4 ;a4 = &surfaces[1] move.l a4,SURF_NEXT(a4) ;surfaces[1].next = a4 move.l a4,SURF_PREV(a4) ;surfaces[1].prev = a4 move.l _edge_head_u_shift20,SURF_LAST_U(a4) ;surfaces[1].last_u = edge_head_u_shift20 * for (edge = edge_head.next ; edge != &edge_tail; edge = edge->next) move.l _edge_head+EDGE_NEXT,a5 ;a5 = edge->next lea _edge_tail,a6 ;a6 = &edge_tail move.l _span_p,a4 bra.w .try .loop move.l EDGE_U(a5),d4 move.l d4,d7 ;d7 = edge->u moveq #20,d0 asr.l d0,d4 ;d4 = edge->u >> 20 * if (edge->surfs[0]) move.l EDGE_SURFS(a5),d1 ;d1 = edge->surfs[0] move.l d1,d0 ;if (edge->surfs[0]) swap d0 ext.l d0 beq.b .cont asl.l #SURF_SIZEOF_EXP,d0 lea 0(a3,d0.l),a0 ;a0 = &surfaces[edge->surfs[0]]; ****** R_TrailingEdgeT (inlined) * if (--surf->spanstate == 0) * { * if (surf->insubmodel) * r_bmodelactive--; * * if (surf == surfaces[1].next) * { * // emit a span (current top going away) * iu = edge->u >> 20; * if (iu > surf->last_u && (surf->flags & SURF_TRANSLUCENT)) * { * span = span_p++; * span->u = surf->last_u; * span->count = iu - span->u; * span->v = current_iv; * span->pnext = surf->spans; * surf->spans = span; * } * * // set last_u on the surface below * surf->next->last_u = iu; * } * * surf->prev->next = surf->next; * surf->next->prev = surf->prev; * } subq.l #1,SURF_SPANSTATE(a0) ;if (--surf->spanstate) == 0 bne.b .cont move.l SURF_NEXT(a0),a1 cmp.l 1*SURF_SIZEOF+SURF_NEXT(a3),a0 ;if (surf==surfaces[1].next) bne.b .te_cont2 move.l d4,d0 ;iu = edge->u >> 20 move.l SURF_LAST_U(a0),d5 move.l d0,SURF_LAST_U(a1) ;surf->next->last_u = iu sub.l d5,d0 ;if (iu > surf->last_u) ble.b .te_cont2 ; translucent btst.b #7,SURF_FLAGS+3(a0) ;if (surf->flags & SURF_TRANSLUCENT) beq.b .te_cont2 ; translucent move.l SURF_SPANS(a0),d3 move.l a4,SURF_SPANS(a0) ;surf->spans = span move.l d5,(a4)+ ;span->u = surf->last_u move.l d2,(a4)+ ;span->v = current_iv move.l d0,(a4)+ ;span->count = iu - span->u move.l d3,(a4)+ ;span->pnext = surf->spans .te_cont2 move.l SURF_PREV(a0),a2 move.l a1,SURF_NEXT(a2) ;surf->prev->next = surf->next move.l a2,SURF_PREV(a1) ;surf->next->prev = surf->prev ****** end of R_TrailingEdgeT .cont ****** R_LeadingEdgeT (inlined) * if (edge->surfs[1]) * { * // it's adding a new surface in, so find the correct place * surf = &surfaces[edge->surfs[1]]; * ext.l d1 ;if (edge->surfs[1]) beq.w .next asl.l #SURF_SIZEOF_EXP,d1 lea 0(a3,d1.l),a1 ;surf = &surfaces[edge->surfs[1]] * * // don't start a span if this is an inverted span, with the end * // edge preceding the start edge (that is, we've already seen the * // end edge) * if (++surf->spanstate == 1) * { * if (surf->insubmodel) * r_bmodelactive++; * * surf2 = surfaces[1].next; * * if (surf->key < surf2->key) * goto newtop; * // if it's two surfaces on the same plane, the one that's already * // active is in front, so keep going unless it's a bmodel * if (surf->insubmodel && (surf->key == surf2->key)) * { move.l SURF_SPANSTATE(a1),d0 ;if (++surf->spanstate == 1) beq.b .le_zero addq.l #1,SURF_SPANSTATE(a1) bra.w .next .le_zero addq.l #1,d0 move.l d0,SURF_SPANSTATE(a1) move.l SURF_INSUBMODEL(a1),d5 ;if (surf->insubmodel) move.l 1*SURF_SIZEOF+SURF_NEXT(a3),a0 ;surf2 = surfaces[1].next move.l SURF_KEY(a1),d6 moveq #0,d3 cmp.l SURF_KEY(a0),d6 ;if (surf->key < surf2->key) blt.b .le_newtop ;goto newtop bgt.b .le_search tst.b d5 beq.b .le_search moveq #-1,d3 move.l d7,d0 sub.l #$fffff,d0 ;edge->u - 0xFFFFF fmove.l d0,fp0 ;(float)(edge->u - 0xFFFFF) fmul.s #(1.0/(16*65536)),fp0 ;fu = fp0 * (1 / $100000) fmove.s _fv,fp1 ;fv fmove.s SURF_D_ZIORIGIN(a1),fp2 fmove.s SURF_D_ZISTEPV(a1),fp3 fmul fp1,fp3 ;fp1 = fv * surf->d_zistepv fadd fp3,fp2 fmove.s SURF_D_ZISTEPU(a1),fp3 fmove fp3,fp4 ;fp4 = surf->d_zistepu fmul fp0,fp3 ;fp3 = fu * surf->d_zistepu fadd fp3,fp2 ;newzi = d_ziorigin + fp1 + fp3 fmove fp2,fp3 fmul.s #0.999,fp2 ;newzibottom = newzi * 0.999 fmul.s #1.001,fp3 ;newzitop = newzi * 1.001 * // must be two bmodels in the same leaf; sort on 1/z * fu = (float)(edge->u - 0xFFFFF) * (1.0 / 0x100000); * newzi = surf->d_ziorigin + fv*surf->d_zistepv + * fu*surf->d_zistepu; * newzibottom = newzi * 0.999; * * testzi = surf2->d_ziorigin + fv*surf2->d_zistepv + * fu*surf2->d_zistepu; * * if (newzibottom >= testzi) * { * goto newtop; * } * * newzitop = newzi * 1.001; * if (newzitop >= testzi) * { * if (surf->d_zistepu >= surf2->d_zistepu) * { * goto newtop; * } * } fmove.s SURF_D_ZIORIGIN(a0),fp5 fmove.s SURF_D_ZISTEPV(a0),fp6 fmul fp1,fp6 ;fp1 = fv * surf2->d_zistepv fadd fp6,fp5 fmove.s SURF_D_ZISTEPU(a0),fp6 fmove fp6,fp7 fmul fp0,fp6 ;fp3 = fu * surf2->d_zistepu fadd fp6,fp5 ;testzi = d_ziorigin + fp1 + fp3 fcmp fp5,fp2 ;if (newzibottom >= testzi) fbge.w .le_newtop ;goto newtop fcmp fp5,fp3 ;if (newzitop >= testzi) fblt.w .le_search fcmp fp7,fp4 ;if (surf->d_zistepu >= surf2->d_zistepu) fbge.w .le_newtop ;goto newtop * do * { * surf2 = surf2->next; * } while (surf->key > surf2->key); * .le_search move.l SURF_NEXT(a0),a0 ;surf2 = surf2->next cmp.l SURF_KEY(a0),d6 ;while (surf->key > surf2->key) bgt.b .le_search * if (surf->key == surf2->key) * { * // if it's two surfaces on the same plane, the one that's already * // active is in front, so keep going unless it's a bmodel * if (!surf->insubmodel) * goto continue_search; bne.b .le_gotposition tst.b d5 beq.b .le_search tst d3 bne.b .le_precalc_done moveq #-1,d3 move.l d7,d0 sub.l #$fffff,d0 ;edge->u - 0xFFFFF fmove.l d0,fp0 ;(float)(edge->u - 0xFFFFF) fmul.s #(1.0/(16*65536)),fp0 ;fu = fp0 * (1 / $100000) fmove.s _fv,fp1 ;fv fmove.s SURF_D_ZIORIGIN(a1),fp2 fmove.s SURF_D_ZISTEPV(a1),fp3 fmul fp1,fp3 ;fp1 = fv * surf->d_zistepv fadd fp3,fp2 fmove.s SURF_D_ZISTEPU(a1),fp3 fmove fp3,fp4 ;fp4 = surf->d_zistepu fmul fp0,fp3 ;fp3 = fu * surf->d_zistepu fadd fp3,fp2 ;newzi = d_ziorigin + fp1 + fp3 fmove fp2,fp3 fmul.s #0.999,fp2 ;newzibottom = newzi * 0.999 fmul.s #1.001,fp3 ;newzitop = newzi * 1.001 .le_precalc_done * // must be two bmodels in the same leaf; sort on 1/z * fu = (float)(edge->u - 0xFFFFF) * (1.0 / 0x100000); * newzi = surf->d_ziorigin + fv*surf->d_zistepv + * fu*surf->d_zistepu; * newzibottom = newzi * 0.999; * * testzi = surf2->d_ziorigin + fv*surf2->d_zistepv + * fu*surf2->d_zistepu; * * if (newzibottom >= testzi) * { * goto gotposition; * } * * newzitop = newzi * 1.001; * if (newzitop >= testzi) * { * if (surf->d_zistepu >= surf2->d_zistepu) * { * goto gotposition; * } * } * * goto continue_search; * } * * goto gotposition; fmove.s SURF_D_ZIORIGIN(a0),fp5 fmove.s SURF_D_ZISTEPV(a0),fp6 fmul fp1,fp6 ;fp1 = fv * surf2->d_zistepv fadd fp6,fp5 fmove.s SURF_D_ZISTEPU(a0),fp6 fmove fp6,fp7 fmul fp0,fp6 ;fp3 = fu * surf2->d_zistepu fadd fp6,fp5 ;testzi = d_ziorigin + fp1 + fp3 fcmp fp5,fp2 ;if (newzibottom >= testzi) fbge.w .le_gotposition ;goto gotposition fcmp fp5,fp3 ;if (newzitop >= testzi) fblt.w .le_search fcmp fp7,fp4 ;if (surf->d_zistepu >= surf2->d_zistepu) fbge.w .le_gotposition ;goto gotposition bra.b .le_search * // emit a span (obscures current top) * iu = edge->u >> 20; * * if (iu > surf2->last_u && (surf2->flags & SURF_TRANSLUCENT)) * { * span = span_p++; * span->u = surf2->last_u; * span->count = iu - span->u; * span->v = current_iv; * span->pnext = surf2->spans; * surf2->spans = span; * } * * // set last_u on the new span * surf->last_u = iu; .le_newtop move.l SURF_LAST_U(a0),d5 move.l d4,SURF_LAST_U(a1) ;surf->last_u = iu sub.l d5,d4 ;if (iu > surf2->last_u) ble.b .le_cont2 ; translucent btst.b #7,SURF_FLAGS+3(a0) ;if (surf->flags & SURF_TRANSLUCENT) beq.b .le_cont2 ; translucent move.l SURF_SPANS(a0),d3 move.l a4,SURF_SPANS(a0) ;surf2->spans = span move.l d5,(a4)+ ;span->u = surf2->last_u move.l d2,(a4)+ ;span->v = current_iv move.l d4,(a4)+ ;span->count = iu - span->u move.l d3,(a4)+ ;span->pnext = surf2->spans .le_cont2 * // insert before surf2 * surf->next = surf2; * surf->prev = surf2->prev; * surf2->prev->next = surf; * surf2->prev = surf; .le_gotposition move.l SURF_PREV(a0),a2 move.l a2,SURF_PREV(a1) ;surf->prev = surf2->prev move.l a0,SURF_NEXT(a1) ;surf->next = surf2 move.l a1,SURF_NEXT(a2) ;surf2->prev->next = surf move.l a1,SURF_PREV(a0) ;surf2->prev = surf ****** end of R_LeadingEdgeT .next move.l EDGE_NEXT(a5),a5 .try cmp.l a5,a6 ;if (edge != &edge_tail) bne.b .loop ****** R_CleanupSpanT (inlined) * // now that we've reached the right edge of the screen, we're done with any * // unfinished surfaces, so emit a span for whatever's on top * surf = surfaces[1].next; * iu = edge_tail_u_shift20; * if (iu > surf->last_u && !(surf->flags & SURF_TRANSLUCENT)) * { * span = span_p++; * span->u = surf->last_u; * span->count = iu - span->u; * span->v = current_iv; * span->pnext = surf->spans; * surf->spans = span; * } move.l 1*SURF_SIZEOF+SURF_NEXT(a3),a0 ;surf = surfaces[1].next move.l _edge_tail_u_shift20,d4 ;iu = edge_tail_u_shift20 move.l SURF_LAST_U(a0),d5 sub.l d5,d4 ;if (iu > surf2->last_u) ble.b .cs_cont ; translucent btst.b #7,SURF_FLAGS+3(a0) ;if (surf->flags & SURF_TRANSLUCENT) beq.b .cs_cont ; translucent move.l SURF_SPANS(a0),d3 move.l a4,SURF_SPANS(a0) ;surf->spans = span move.l d5,(a4)+ ;span->u = surf->last_u move.l d2,(a4)+ ;span->v = current_iv move.l d4,(a4)+ ;span->count = iu - span->u move.l d3,(a4)+ ;span->pnext = surf->spans .cs_cont * // reset spanstate for all surfaces in the surface stack * do * { * surf->spanstate = 0; * surf = surf->next; * } while (surf != &surfaces[1]); lea 1*SURF_SIZEOF(a3),a1 ;a1 = &surfaces[1] .cs_loop clr.l SURF_SPANSTATE(a0) ;surf->spanstate = 0 move.l SURF_NEXT(a0),a0 ;surf = surf->next cmp.l a0,a1 ;while (surf != &surfaces[1]) bne.b .cs_loop ****** end of R_CleanupSpanT move.l a4,_span_p ;jsr _R_CleanupSpan fmovem.x (sp)+,fp2-fp7 movem.l (sp)+,d2-d7/a2-a6 rts engine/h2shared/r_edgea.asm000066400000000000000000000264231444734033100161250ustar00rootroot00000000000000; ; r_edgea.asm ; x86 assembly-language edge-processing code. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix FoundTrans _sym_prefix r_bmodelactive _sym_prefix surfaces _sym_prefix edge_tail _sym_prefix edge_aftertail _sym_prefix edge_head _sym_prefix edge_head_u_shift20 _sym_prefix edge_tail_u_shift20 _sym_prefix current_iv _sym_prefix span_p _sym_prefix fv ; C-shared globals: _sym_prefix R_EdgeCodeStart _sym_prefix R_InsertNewEdges _sym_prefix R_RemoveEdges _sym_prefix R_StepActiveU _sym_prefix R_GenerateSpans ;_sym_prefix R_GenerateTSpans _sym_prefix R_EdgeCodeEnd _sym_prefix R_SurfacePatch %endif ; _sym_prefix ; externs from C code extern FoundTrans extern r_bmodelactive extern surfaces extern edge_tail extern edge_aftertail extern edge_head extern edge_head_u_shift20 extern edge_tail_u_shift20 extern current_iv extern span_p extern fv ; externs from ASM-only code extern R_SurfacePatchT ;extern R_GenerateTSpans SEGMENT .data Ltemp dd 0 float_1_div_0100000h dd 035800000h float_point_999 dd 0.999 float_1_point_001 dd 1.001 SEGMENT .text ;;;;;;;;;;;;;;;;;;;;;;;; ; R_EdgeCodeStart ;;;;;;;;;;;;;;;;;;;;;;;; global R_EdgeCodeStart R_EdgeCodeStart: ;;;;;;;;;;;;;;;;;;;;;;;; ; R_InsertNewEdges ;;;;;;;;;;;;;;;;;;;;;;;; global R_InsertNewEdges R_InsertNewEdges: push edi push esi mov edx, dword [4+8+esp] push ebx mov ecx, dword [8+12+esp] LDoNextEdge: mov eax, dword [0+edx] mov edi,edx LContinueSearch: mov ebx, dword [0+ecx] mov esi, dword [12+ecx] cmp eax,ebx jle LAddedge mov ebx, dword [0+esi] mov ecx, dword [12+esi] cmp eax,ebx jle LAddedge2 mov ebx, dword [0+ecx] mov esi, dword [12+ecx] cmp eax,ebx jle LAddedge mov ebx, dword [0+esi] mov ecx, dword [12+esi] cmp eax,ebx jg LContinueSearch LAddedge2: mov edx, dword [12+edx] mov ebx, dword [8+esi] mov dword [12+edi],esi mov dword [8+edi],ebx mov dword [12+ebx],edi mov dword [8+esi],edi mov ecx,esi cmp edx,0 jnz LDoNextEdge jmp LDone ALIGN 4 LAddedge: mov edx, dword [12+edx] mov ebx, dword [8+ecx] mov dword [12+edi],ecx mov dword [8+edi],ebx mov dword [12+ebx],edi mov dword [8+ecx],edi cmp edx,0 jnz LDoNextEdge LDone: pop ebx pop esi pop edi ret ;;;;;;;;;;;;;;;;;;;;;;;; ; R_RemoveEdges ;;;;;;;;;;;;;;;;;;;;;;;; global R_RemoveEdges R_RemoveEdges: push ebx mov eax, dword [4+4+esp] Lre_loop: mov ecx, dword [12+eax] mov ebx, dword [20+eax] mov edx, dword [8+eax] test ebx,ebx mov dword [8+ecx],edx jz Lre_done mov dword [12+edx],ecx mov ecx, dword [12+ebx] mov edx, dword [8+ebx] mov eax, dword [20+ebx] mov dword [8+ecx],edx test eax,eax mov dword [12+edx],ecx jnz Lre_loop pop ebx ret Lre_done: mov dword [12+edx],ecx pop ebx ret ;;;;;;;;;;;;;;;;;;;;;;;; ; R_StepActiveU ;;;;;;;;;;;;;;;;;;;;;;;; global R_StepActiveU R_StepActiveU: push edi mov edx, dword [4+4+esp] push esi push ebx mov esi, dword [8+edx] LNewEdge: mov edi, dword [0+esi] LNextEdge: mov eax, dword [0+edx] mov ebx, dword [4+edx] add eax,ebx mov esi, dword [12+edx] mov dword [0+edx],eax cmp eax,edi jl LPushBack mov edi, dword [0+esi] mov ebx, dword [4+esi] add edi,ebx mov edx, dword [12+esi] mov dword [0+esi],edi cmp edi,eax jl LPushBack2 mov eax, dword [0+edx] mov ebx, dword [4+edx] add eax,ebx mov esi, dword [12+edx] mov dword [0+edx],eax cmp eax,edi jl LPushBack mov edi, dword [0+esi] mov ebx, dword [4+esi] add edi,ebx mov edx, dword [12+esi] mov dword [0+esi],edi cmp edi,eax jnl LNextEdge LPushBack2: mov ebx,edx mov eax,edi mov edx,esi mov esi,ebx LPushBack: mov ecx, dword [8+edx] mov ebx, dword [12+edx] cmp edx,offset edge_aftertail jz LUDone mov edi, dword [8+ecx] mov dword [8+esi],ecx mov dword [12+ecx],ebx LPushBackLoop: mov ecx, dword [8+edi] mov ebx, dword [0+edi] cmp eax,ebx jnl LPushBackFound mov edi, dword [8+ecx] mov ebx, dword [0+ecx] cmp eax,ebx jl LPushBackLoop mov edi,ecx LPushBackFound: mov ebx, dword [12+edi] mov dword [8+edx],edi mov dword [12+edx],ebx mov dword [12+edi],edx mov dword [8+ebx],edx mov edx,esi mov esi, dword [8+esi] cmp edx,offset edge_tail jnz near LNewEdge LUDone: pop ebx pop esi pop edi ret ALIGN 4 TrailingEdge: ;rj bt dword [24+esi],7 ; surf->flags & SURF_TRANSLUCENT jc near LInverted2 mov eax, dword [20+esi] dec eax jnz LInverted mov dword [20+esi],eax mov ecx, dword [40+esi] mov edx, dword [12345678h] LPatch0: mov eax, dword [r_bmodelactive] sub eax,ecx cmp edx,esi mov dword [r_bmodelactive],eax jnz LNoEmit mov eax, dword [0+ebx] shr eax,20 mov edx, dword [16+esi] mov ecx, dword [0+esi] cmp eax,edx jle LNoEmit2 mov dword [16+ecx],eax sub eax,edx mov dword [0+ebp],edx mov dword [8+ebp],eax mov eax, dword [current_iv] mov dword [4+ebp],eax mov eax, dword [8+esi] mov dword [12+ebp],eax mov dword [8+esi],ebp add ebp,16 mov edx, dword [0+esi] mov esi, dword [4+esi] mov dword [0+esi],edx mov dword [4+edx],esi ret LNoEmit3: mov dword [FoundTrans],1 ; O.S.: this isn't reached.. LNoEmit2: mov dword [16+ecx],eax mov edx, dword [0+esi] mov esi, dword [4+esi] mov dword [0+esi],edx mov dword [4+edx],esi ret LNoEmit: mov edx, dword [0+esi] mov esi, dword [4+esi] mov dword [0+esi],edx mov dword [4+edx],esi ret LInverted: mov dword [20+esi],eax ret ;rj LInverted2: mov dword [FoundTrans],1 ret Lgs_trailing: push offset dword Lgs_nextedge jmp TrailingEdge ;;;;;;;;;;;;;;;;;;;;;;;; ; R_GenerateSpans ;;;;;;;;;;;;;;;;;;;;;;;; global R_GenerateSpans R_GenerateSpans: push ebp push edi push esi push ebx mov dword [FoundTrans],0 ; rj mov eax, dword [surfaces] mov edx, dword [edge_head_u_shift20] add eax,64 mov ebp, dword [span_p] mov dword [r_bmodelactive],0 mov dword [0+eax],eax mov dword [4+eax],eax mov dword [16+eax],edx mov ebx, dword [edge_head+12] cmp ebx,offset edge_tail jz near Lgs_lastspan Lgs_edgeloop: mov edi, dword [16+ebx] mov eax, dword [surfaces] mov esi,edi and edi,0FFFF0000h and esi,0FFFFh jz Lgs_leading shl esi,6 add esi,eax test edi,edi jz Lgs_trailing call TrailingEdge ; call near TrailingEdge mov eax, dword [surfaces] Lgs_leading: shr edi,16-6 mov eax, dword [surfaces] add edi,eax mov esi, dword [12345678h] LPatch2: ;rj bt dword [24+edi],7 ; surf->flags & SURF_TRANSLUCENT jnc Skip mov dword [FoundTrans],1 jmp Lgs_nextedge Skip: mov edx, dword [20+edi] mov eax, dword [40+edi] test eax,eax jnz Lbmodel_leading test edx,edx jnz near Lxl_done inc edx mov eax, dword [12+edi] mov dword [20+edi],edx mov ecx, dword [12+esi] cmp eax,ecx jl near Lnewtop Lsortloopnb: mov esi, dword [0+esi] mov ecx, dword [12+esi] cmp eax,ecx jge Lsortloopnb jmp LInsertAndExit ALIGN 4 Lbmodel_leading: test edx,edx jnz near Lxl_done mov ecx, dword [r_bmodelactive] inc edx inc ecx mov dword [20+edi],edx mov dword [r_bmodelactive],ecx mov eax, dword [12+edi] mov ecx, dword [12+esi] cmp eax,ecx jl near Lnewtop jz near Lzcheck_for_newtop Lsortloop: mov esi, dword [0+esi] mov ecx, dword [12+esi] cmp eax,ecx jg Lsortloop jne near LInsertAndExit mov eax, dword [0+ebx] sub eax,0FFFFFh mov dword [Ltemp],eax fild dword [Ltemp] fmul dword [float_1_div_0100000h] fld st0 fmul dword [48+edi] fld dword [fv] fmul dword [52+edi] fxch st1 fadd dword [44+edi] fld dword [48+esi] fmul st0,st3 fxch st1 faddp st2,st0 fld dword [fv] fmul dword [52+esi] fld st2 fmul dword [float_point_999] fxch st2 fadd dword [44+esi] faddp st1,st0 fxch st1 fcomp st1 fxch st1 fmul dword [float_1_point_001] fxch st1 fnstsw ax test ah,001h jz Lgotposition_fpop3 fcomp st1 fnstsw ax test ah,045h jz near Lsortloop_fpop2 fld dword [48+edi] fcomp dword [48+esi] fnstsw ax test ah,001h jz Lgotposition_fpop2 fstp st0 fstp st0 mov eax, dword [12+edi] jmp Lsortloop Lgotposition_fpop3: fstp st0 Lgotposition_fpop2: fstp st0 fstp st0 jmp LInsertAndExit Lnewtop_fpop3: fstp st0 Lnewtop_fpop2: fstp st0 fstp st0 mov eax, dword [12+edi] Lnewtop: mov eax, dword [0+ebx] mov edx, dword [16+esi] shr eax,20 mov dword [16+edi],eax cmp eax,edx jle LInsertAndExit ;rj ; bt dword [24+esi],7 ; surf->flags & SURF_TRANSLUCENT ; jc LInsertAndExit sub eax,edx mov dword [0+ebp],edx mov dword [8+ebp],eax mov eax, dword [current_iv] mov dword [4+ebp],eax mov eax, dword [8+esi] mov dword [12+ebp],eax mov dword [8+esi],ebp add ebp,16 LInsertAndExit: mov dword [0+edi],esi mov eax, dword [4+esi] mov dword [4+edi],eax mov dword [4+esi],edi mov dword [0+eax],edi Lgs_nextedge: mov ebx, dword [12+ebx] cmp ebx,offset edge_tail jnz near Lgs_edgeloop Lgs_lastspan: mov esi, dword [12345678h] LPatch3: mov eax, dword [edge_tail_u_shift20] xor ecx,ecx mov edx, dword [16+esi] sub eax,edx jle Lgs_resetspanstate mov dword [0+ebp],edx mov dword [8+ebp],eax mov eax, dword [current_iv] mov dword [4+ebp],eax mov eax, dword [8+esi] mov dword [12+ebp],eax mov dword [8+esi],ebp add ebp,16 Lgs_resetspanstate: mov dword [20+esi],ecx mov esi, dword [0+esi] cmp esi,012345678h LPatch4: jnz Lgs_resetspanstate mov dword [span_p],ebp ;cmp dword [FoundTrans],1 ;jne Done ;call near R_GenerateTSpans Done: pop ebx pop esi pop edi pop ebp ret ALIGN 4 Lxl_done: inc edx mov dword [20+edi],edx jmp Lgs_nextedge ALIGN 4 Lzcheck_for_newtop: mov eax, dword [0+ebx] sub eax,0FFFFFh mov dword [Ltemp],eax fild dword [Ltemp] fmul dword [float_1_div_0100000h] fld st0 fmul dword [48+edi] fld dword [fv] fmul dword [52+edi] fxch st1 fadd dword [44+edi] fld dword [48+esi] fmul st0,st3 fxch st1 faddp st2,st0 fld dword [fv] fmul dword [52+esi] fld st2 fmul dword [float_point_999] fxch st2 fadd dword [44+esi] faddp st1,st0 fxch st1 fcomp st1 fxch st1 fmul dword [float_1_point_001] fxch st1 fnstsw ax test ah,001h jz near Lnewtop_fpop3 fcomp st1 fnstsw ax test ah,045h jz Lsortloop_fpop2 fld dword [48+edi] fcomp dword [48+esi] fnstsw ax test ah,001h jz near Lnewtop_fpop2 Lsortloop_fpop2: fstp st0 fstp st0 mov eax, dword [12+edi] jmp Lsortloop ;;;;;;;;;;;;;;;;;;;;;;;; ; R_EdgeCodeEnd ;;;;;;;;;;;;;;;;;;;;;;;; global R_EdgeCodeEnd R_EdgeCodeEnd: ;;;;;;;;;;;;;;;;;;;;;;;; ; R_SurfacePatch ;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 4 global R_SurfacePatch R_SurfacePatch: mov eax, dword [surfaces] add eax,64 mov dword [LPatch4-4],eax add eax,0 mov dword [LPatch0-4],eax mov dword [LPatch2-4],eax mov dword [LPatch3-4],eax call R_SurfacePatchT ; call near R_SurfacePatchT ret engine/h2shared/r_edgeb.asm000066400000000000000000000175311444734033100161260ustar00rootroot00000000000000; ; r_edgeb.asm ; x86 assembly-language edge-processing code. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix r_bmodelactive _sym_prefix surfaces _sym_prefix edge_tail _sym_prefix edge_aftertail _sym_prefix edge_head _sym_prefix edge_head_u_shift20 _sym_prefix edge_tail_u_shift20 _sym_prefix current_iv _sym_prefix span_p _sym_prefix fv ; C-shared globals: _sym_prefix R_EdgeCodeStartT _sym_prefix R_GenerateTSpans _sym_prefix R_EdgeCodeEndT ;_sym_prefix R_SurfacePatchT %endif ; _sym_prefix ; externs from C code extern r_bmodelactive extern surfaces extern edge_tail extern edge_aftertail extern edge_head extern edge_head_u_shift20 extern edge_tail_u_shift20 extern current_iv extern span_p extern fv ; externs from ASM-only code SEGMENT .data Ltemp dd 0 float_1_div_0100000h dd 035800000h float_point_999 dd 0.999 float_1_point_001 dd 1.001 SEGMENT .text ALIGN 4 ;;;;;;;;;;;;;;;;;;;;;;;; ; R_EdgeCodeStartT ;;;;;;;;;;;;;;;;;;;;;;;; global R_EdgeCodeStartT R_EdgeCodeStartT: TrailingEdge: mov eax, dword [20+esi] dec eax jnz LInverted mov dword [20+esi],eax mov ecx, dword [40+esi] mov edx, dword [12345678h] LPatch0: mov eax, dword [r_bmodelactive] sub eax,ecx cmp edx,esi mov dword [r_bmodelactive],eax jnz LNoEmit mov eax, dword [0+ebx] shr eax,20 mov edx, dword [16+esi] mov ecx, dword [0+esi] cmp eax,edx jle LNoEmit2 ;rj bt dword [24+esi],7 ; surf->flags & SURF_TRANSLUCENT jnc LNoEmit2 mov dword [16+ecx],eax sub eax,edx mov dword [0+ebp],edx mov dword [8+ebp],eax mov eax, dword [current_iv] mov dword [4+ebp],eax mov eax, dword [8+esi] mov dword [12+ebp],eax mov dword [8+esi],ebp add ebp,16 mov edx, dword [0+esi] mov esi, dword [4+esi] mov dword [0+esi],edx mov dword [4+edx],esi ret LNoEmit2: mov dword [16+ecx],eax mov edx, dword [0+esi] mov esi, dword [4+esi] mov dword [0+esi],edx mov dword [4+edx],esi ret LNoEmit: mov edx, dword [0+esi] mov esi, dword [4+esi] mov dword [0+esi],edx mov dword [4+edx],esi ret LInverted: mov dword [20+esi],eax ret Lgs_trailing: push offset dword Lgs_nextedge jmp TrailingEdge ;;;;;;;;;;;;;;;;;;;;;;;; ; R_GenerateTSpans ;;;;;;;;;;;;;;;;;;;;;;;; global R_GenerateTSpans R_GenerateTSpans: push ebp push edi push esi push ebx mov eax, dword [surfaces] mov edx, dword [edge_head_u_shift20] add eax,64 mov ebp, dword [span_p] mov dword [r_bmodelactive],0 mov dword [0+eax],eax mov dword [4+eax],eax mov dword [16+eax],edx mov ebx, dword [edge_head+12] cmp ebx,offset edge_tail jz near Lgs_lastspan Lgs_edgeloop: mov edi, dword [16+ebx] mov eax, dword [surfaces] mov esi,edi and edi,0FFFF0000h and esi,0FFFFh jz Lgs_leading shl esi,6 add esi,eax test edi,edi jz Lgs_trailing call TrailingEdge ; call near TrailingEdge mov eax, dword [surfaces] Lgs_leading: shr edi,16-6 mov eax, dword [surfaces] add edi,eax mov esi, dword [12345678h] LPatch2: mov edx, dword [20+edi] mov eax, dword [40+edi] test eax,eax jnz Lbmodel_leading test edx,edx jnz near Lxl_done inc edx mov eax, dword [12+edi] mov dword [20+edi],edx mov ecx, dword [12+esi] cmp eax,ecx jl near Lnewtop Lsortloopnb: mov esi, dword [0+esi] mov ecx, dword [12+esi] cmp eax,ecx jge Lsortloopnb jmp LInsertAndExit ALIGN 4 Lbmodel_leading: test edx,edx jnz near Lxl_done mov ecx, dword [r_bmodelactive] inc edx inc ecx mov dword [20+edi],edx mov dword [r_bmodelactive],ecx mov eax, dword [12+edi] mov ecx, dword [12+esi] cmp eax,ecx jl near Lnewtop jz near Lzcheck_for_newtop Lsortloop: mov esi, dword [0+esi] mov ecx, dword [12+esi] cmp eax,ecx jg Lsortloop jne near LInsertAndExit mov eax, dword [0+ebx] sub eax,0FFFFFh mov dword [Ltemp],eax fild dword [Ltemp] fmul dword [float_1_div_0100000h] fld st0 fmul dword [48+edi] fld dword [fv] fmul dword [52+edi] fxch st1 fadd dword [44+edi] fld dword [48+esi] fmul st0,st3 fxch st1 faddp st2,st0 fld dword [fv] fmul dword [52+esi] fld st2 fmul dword [float_point_999] fxch st2 fadd dword [44+esi] faddp st1,st0 fxch st1 fcomp st1 fxch st1 fmul dword [float_1_point_001] fxch st1 fnstsw ax test ah,001h jz Lgotposition_fpop3 fcomp st1 fnstsw ax test ah,045h jz near Lsortloop_fpop2 fld dword [48+edi] fcomp dword [48+esi] fnstsw ax test ah,001h jz Lgotposition_fpop2 fstp st0 fstp st0 mov eax, dword [12+edi] jmp Lsortloop Lgotposition_fpop3: fstp st0 Lgotposition_fpop2: fstp st0 fstp st0 jmp LInsertAndExit Lnewtop_fpop3: fstp st0 Lnewtop_fpop2: fstp st0 fstp st0 mov eax, dword [12+edi] Lnewtop: mov eax, dword [0+ebx] mov edx, dword [16+esi] shr eax,20 mov dword [16+edi],eax cmp eax,edx jle LInsertAndExit ;rj bt dword [24+esi],7 ; surf->flags & SURF_TRANSLUCENT jnc LInsertAndExit sub eax,edx mov dword [0+ebp],edx mov dword [8+ebp],eax mov eax, dword [current_iv] mov dword [4+ebp],eax mov eax, dword [8+esi] mov dword [12+ebp],eax mov dword [8+esi],ebp add ebp,16 LInsertAndExit: mov dword [0+edi],esi mov eax, dword [4+esi] mov dword [4+edi],eax mov dword [4+esi],edi mov dword [0+eax],edi Lgs_nextedge: mov ebx, dword [12+ebx] cmp ebx,offset edge_tail jnz near Lgs_edgeloop Lgs_lastspan: mov esi, dword [12345678h] LPatch3: mov eax, dword [edge_tail_u_shift20] xor ecx,ecx mov edx, dword [16+esi] sub eax,edx jle Lgs_resetspanstate ;rj bt dword [24+esi],7 ; surf->flags & SURF_TRANSLUCENT jnc Lgs_resetspanstate mov dword [0+ebp],edx mov dword [8+ebp],eax mov eax, dword [current_iv] mov dword [4+ebp],eax mov eax, dword [8+esi] mov dword [12+ebp],eax mov dword [8+esi],ebp add ebp,16 Lgs_resetspanstate: mov dword [20+esi],ecx mov esi, dword [0+esi] cmp esi,012345678h LPatch4: jnz Lgs_resetspanstate mov dword [span_p],ebp pop ebx pop esi pop edi pop ebp ret ALIGN 4 Lxl_done: inc edx mov dword [20+edi],edx jmp Lgs_nextedge ALIGN 4 Lzcheck_for_newtop: mov eax, dword [0+ebx] sub eax,0FFFFFh mov dword [Ltemp],eax fild dword [Ltemp] fmul dword [float_1_div_0100000h] fld st0 fmul dword [48+edi] fld dword [fv] fmul dword [52+edi] fxch st1 fadd dword [44+edi] fld dword [48+esi] fmul st0,st3 fxch st1 faddp st2,st0 fld dword [fv] fmul dword [52+esi] fld st2 fmul dword [float_point_999] fxch st2 fadd dword [44+esi] faddp st1,st0 fxch st1 fcomp st1 fxch st1 fmul dword [float_1_point_001] fxch st1 fnstsw ax test ah,001h jz near Lnewtop_fpop3 fcomp st1 fnstsw ax test ah,045h jz Lsortloop_fpop2 fld dword [48+edi] fcomp dword [48+esi] fnstsw ax test ah,001h jz near Lnewtop_fpop2 Lsortloop_fpop2: fstp st0 fstp st0 mov eax, dword [12+edi] jmp Lsortloop ;;;;;;;;;;;;;;;;;;;;;;;; ; R_EdgeCodeEndT ;;;;;;;;;;;;;;;;;;;;;;;; global R_EdgeCodeEndT R_EdgeCodeEndT: ;;;;;;;;;;;;;;;;;;;;;;;; ; R_SurfacePatchT ;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 4 global R_SurfacePatchT R_SurfacePatchT: mov eax, dword [surfaces] add eax,64 mov dword [LPatch4-4],eax add eax,0 mov dword [LPatch0-4],eax mov dword [LPatch2-4],eax mov dword [LPatch3-4],eax ret engine/h2shared/r_efrag.c000066400000000000000000000120251444734033100155770ustar00rootroot00000000000000/* * r_efrag.c -- entity fragments * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" mnode_t *r_pefragtopnode; /* =============================================================================== ENTITY FRAGMENT FUNCTIONS =============================================================================== */ static efrag_t **lastlink; static entity_t *r_addent; vec3_t r_emins, r_emaxs; /* ================ R_RemoveEfrags Call when removing an object from the world or moving it to another position ================ */ void R_RemoveEfrags (entity_t *ent) { efrag_t *ef, *old, *walk, **prev; ef = ent->efrag; while (ef) { prev = &ef->leaf->efrags; while (1) { walk = *prev; if (!walk) break; if (walk == ef) { // remove this fragment *prev = ef->leafnext; break; } else prev = &walk->leafnext; } old = ef; ef = ef->entnext; // put it on the free list old->entnext = cl.free_efrags; cl.free_efrags = old; } ent->efrag = NULL; } /* =================== R_SplitEntityOnNode =================== */ static void R_SplitEntityOnNode (mnode_t *node) { efrag_t *ef; mplane_t *splitplane; mleaf_t *leaf; int sides; if (node->contents == CONTENTS_SOLID) { return; } // add an efrag if the node is a leaf if ( node->contents < 0) { if (!r_pefragtopnode) r_pefragtopnode = node; leaf = (mleaf_t *)node; // grab an efrag off the free list ef = cl.free_efrags; if (!ef) { Con_Printf ("Too many efrags!\n"); return; // no free fragments... } cl.free_efrags = cl.free_efrags->entnext; ef->entity = r_addent; // add the entity link *lastlink = ef; lastlink = &ef->entnext; ef->entnext = NULL; // set the leaf links ef->leaf = leaf; ef->leafnext = leaf->efrags; leaf->efrags = ef; return; } // NODE_MIXED splitplane = node->plane; sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane); if (sides == 3) { // split on this plane // if this is the first splitter of this bmodel, remember it if (!r_pefragtopnode) r_pefragtopnode = node; } // recurse down the contacted sides if (sides & 1) R_SplitEntityOnNode (node->children[0]); if (sides & 2) R_SplitEntityOnNode (node->children[1]); } /* =================== R_SplitEntityOnNode2 =================== */ void R_SplitEntityOnNode2 (mnode_t *node) { mplane_t *splitplane; int sides; if (node->visframe != r_visframecount) return; if (node->contents < 0) { if (node->contents != CONTENTS_SOLID) r_pefragtopnode = node; // we've reached a non-solid leaf, so it's // visible and not BSP clipped return; } splitplane = node->plane; sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane); if (sides == 3) { // remember first splitter r_pefragtopnode = node; return; } // not split yet; recurse down the contacted side if (sides & 1) R_SplitEntityOnNode2 (node->children[0]); else R_SplitEntityOnNode2 (node->children[1]); } /* =========== R_AddEfrags =========== */ void R_AddEfrags (entity_t *ent) { qmodel_t *entmodel; int i; if (!ent->model) return; if (ent == &r_worldentity) return; // never add the world r_addent = ent; lastlink = &ent->efrag; r_pefragtopnode = NULL; entmodel = ent->model; for (i = 0; i < 3; i++) { r_emins[i] = ent->origin[i] + entmodel->mins[i]; r_emaxs[i] = ent->origin[i] + entmodel->maxs[i]; } R_SplitEntityOnNode (cl.worldmodel->nodes); ent->topnode = r_pefragtopnode; } /* ================ R_StoreEfrags // FIXME: a lot of this goes away with edge-based ================ */ void R_StoreEfrags (efrag_t **ppefrag) { entity_t *pent; qmodel_t *clmodel; efrag_t *pefrag; while ((pefrag = *ppefrag) != NULL) { pent = pefrag->entity; clmodel = pent->model; switch (clmodel->type) { case mod_alias: case mod_brush: case mod_sprite: pent = pefrag->entity; if ((pent->visframe != r_framecount) && (cl_numvisedicts < MAX_VISEDICTS)) { #if defined (H2W) cl_visedicts[cl_numvisedicts++] = *pent; #else cl_visedicts[cl_numvisedicts++] = pent; #endif // mark that we've recorded this entity for this frame pent->visframe = r_framecount; } ppefrag = &pefrag->leafnext; break; default: Sys_Error ("%s: Bad entity type %d", __thisfunc__, clmodel->type); } } } engine/h2shared/r_light.c000066400000000000000000000150131444734033100156220ustar00rootroot00000000000000/* * r_light.c * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" int r_dlightframecount; /* ================== R_AnimateLight ================== */ void R_AnimateLight (void) { int i, c, v; int defaultLocus; int locusHz[3]; defaultLocus = locusHz[0] = (int)(cl.time*10); locusHz[1] = (int)(cl.time*20); locusHz[2] = (int)(cl.time*30); for (i = 0; i < MAX_LIGHTSTYLES; i++) { if (!cl_lightstyle[i].length) { // No style def d_lightstylevalue[i] = 256; continue; } c = cl_lightstyle[i].map[0]; if (c == '1' || c == '2' || c == '3') { // Explicit anim rate if (cl_lightstyle[i].length == 1) { // Bad style def d_lightstylevalue[i] = 256; continue; } v = locusHz[c-'1'] % (cl_lightstyle[i].length-1); d_lightstylevalue[i] = (cl_lightstyle[i].map[v+1]-'a')*22; continue; } // Default anim rate (10 Hz) v = defaultLocus % cl_lightstyle[i].length; d_lightstylevalue[i] = (cl_lightstyle[i].map[v]-'a')*22; } } /* ============================================================================= DYNAMIC LIGHTS ============================================================================= */ /* ============= R_MarkLights ============= */ void R_MarkLights (dlight_t *light, int bit, mnode_t *node) { mplane_t *splitplane; float dist; msurface_t *surf; int i; if (node->contents < 0) return; splitplane = node->plane; dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; if (light->radius > 0) { if (dist > light->radius) { R_MarkLights (light, bit, node->children[0]); return; } if (dist < -light->radius) { R_MarkLights (light, bit, node->children[1]); return; } } else { if (dist > -light->radius) { R_MarkLights (light, bit, node->children[0]); return; } if (dist < light->radius) { R_MarkLights (light, bit, node->children[1]); return; } } // mark the polygons surf = cl.worldmodel->surfaces + node->firstsurface; for (i = 0; i < node->numsurfaces; i++, surf++) { if (surf->dlightframe != r_dlightframecount) { surf->dlightbits = 0; surf->dlightframe = r_dlightframecount; } surf->dlightbits |= bit; } R_MarkLights (light, bit, node->children[0]); R_MarkLights (light, bit, node->children[1]); } /* ============= R_PushDlights ============= */ void R_PushDlights (void) { int i; dlight_t *l; if (!r_dynamic.integer) return; r_dlightframecount = r_framecount + 1; // because the count hasn't // advanced yet for this frame l = cl_dlights; for (i = 0; i < MAX_DLIGHTS; i++, l++) { if (l->die < cl.time || !l->radius) continue; R_MarkLights ( l, 1<nodes ); } } /* ============================================================================= LIGHT SAMPLING ============================================================================= */ static int RecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end) { int r; float front, back, frac; int side; mplane_t *plane; vec3_t mid; msurface_t *surf; int s, t, ds, dt; int i; mtexinfo_t *tex; byte *lightmap; unsigned int scale; int maps; loc0: // surgeon: optimized recursion if (node->contents < 0) return -1; // didn't hit anything // calculate mid point // optimization borrowed from LordHavoc (darkplaces engine) plane = node->plane; if (plane->type < 3) { front = start[plane->type] - plane->dist; back = end[plane->type] - plane->dist; } else { front = DotProduct (start, plane->normal) - plane->dist; back = DotProduct (end, plane->normal) - plane->dist; } side = front < 0; /*if ( (back < 0) == side) return RecursiveLightPoint (node->children[side], start, end);*/ // optimization borrowed from LordHavoc (darkplaces engine) if ((back < 0) == side) { node = node->children[side]; goto loc0; } frac = front / (front-back); mid[0] = start[0] + (end[0] - start[0])*frac; mid[1] = start[1] + (end[1] - start[1])*frac; mid[2] = start[2] + (end[2] - start[2])*frac; // go down front side r = RecursiveLightPoint (node->children[side], start, mid); if (r >= 0) return r; // hit something if ( (back < 0) == side ) return -1; // didn't hit anuthing // check for impact on this node surf = cl.worldmodel->surfaces + node->firstsurface; for (i = 0; i < node->numsurfaces; i++, surf++) { if (surf->flags & SURF_DRAWTILED) continue; // no lightmaps tex = surf->texinfo; /* added double casts so that 64 bit/sse2 builds' precision * matches that of x87 floating point. took from QuakeSpasm, * patch by Eric Wasylishen. */ s = DotProductDBL(mid, tex->vecs[0]) + (double)tex->vecs[0][3]; t = DotProductDBL(mid, tex->vecs[1]) + (double)tex->vecs[1][3]; if (s < surf->texturemins[0] || t < surf->texturemins[1]) continue; ds = s - surf->texturemins[0]; dt = t - surf->texturemins[1]; if (ds > surf->extents[0] || dt > surf->extents[1]) continue; if (!surf->samples) return 0; ds >>= 4; dt >>= 4; lightmap = surf->samples; r = 0; if (lightmap) { lightmap += dt * ((surf->extents[0] >> 4) + 1) + ds; for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) { scale = d_lightstylevalue[surf->styles[maps]]; r += *lightmap * scale; lightmap += ((surf->extents[0] >> 4) + 1) * ((surf->extents[1] >> 4) + 1); } r >>= 8; } return r; } // go down back side //return RecursiveLightPoint (node->children[!side], mid, end); // surgeon: optimized recursion node = node->children[!side]; VectorCopy(start, mid); goto loc0; } int R_LightPoint (vec3_t p) { vec3_t end; int r; if (!cl.worldmodel->lightdata) return 255; end[0] = p[0]; end[1] = p[1]; end[2] = p[2] - 2048; r = RecursiveLightPoint (cl.worldmodel->nodes, p, end); if (r == -1) r = 0; if (r < r_refdef.ambientlight) r = r_refdef.ambientlight; return r; } engine/h2shared/r_local.h000066400000000000000000000236331444734033100156210ustar00rootroot00000000000000/* * r_local.h -- private refresh defs * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef R_LOCAL_H #define R_LOCAL_H #ifndef GLQUAKE #include "r_shared.h" #define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0) // normalizing factor so player model works out // to about 1 pixel per triangle #define BMODEL_FULLY_CLIPPED 0x10 // value returned by R_BmodelCheckBBox () // if bbox is trivially rejected //=========================================================================== // viewmodel lighting typedef struct { int ambientlight; int shadelight; float *plightvec; } alight_t; //=========================================================================== // clipped bmodel edges typedef struct bedge_s { mvertex_t *v[2]; struct bedge_s *pnext; } bedge_t; typedef struct { float fv[3]; // viewspace x, y } auxvert_t; //=========================================================================== extern cvar_t r_draworder; extern cvar_t r_speeds; extern cvar_t r_timegraph; extern cvar_t r_graphheight; extern cvar_t r_clearcolor; extern cvar_t r_waterwarp; extern cvar_t r_fullbright; extern cvar_t r_drawentities; extern cvar_t r_aliasstats; extern cvar_t r_dspeeds; extern cvar_t r_drawflat; extern cvar_t r_ambient; extern cvar_t r_reportsurfout; extern cvar_t r_maxsurfs; extern cvar_t r_numsurfs; extern cvar_t r_reportedgeout; extern cvar_t r_maxedges; extern cvar_t r_numedges; extern cvar_t r_aliasmip; extern cvar_t r_transwater; #ifdef H2W extern cvar_t r_teamcolor; #endif extern cvar_t r_texture_external; extern cvar_t r_dynamic; #define XCENTERING (1.0 / 2.0) #define YCENTERING (1.0 / 2.0) #define CLIP_EPSILON 0.001 #define BACKFACE_EPSILON 0.01 //=========================================================================== #define DIST_NOT_SET 98765 // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct clipplane_s { vec3_t normal; float dist; struct clipplane_s *next; byte leftedge; byte rightedge; byte reserved[2]; } clipplane_t; extern clipplane_t view_clipplanes[4]; //============================================================================= void R_RenderWorld (void); //============================================================================= extern mplane_t screenedge[4]; ASM_LINKAGE_BEGIN extern vec3_t r_origin; ASM_LINKAGE_END extern vec3_t r_entorigin; extern float screenAspect; extern float verticalFieldOfView; extern float xOrigin, yOrigin; extern int r_visframecount; //============================================================================= // // current entity info // extern qboolean insubmodel; void R_DrawSprite (void); void R_RenderFace (msurface_t *fa, int clipflags); void R_RenderPoly (msurface_t *fa, int clipflags); void R_DrawPolyList (void); void R_ClearPolyList (void); void R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf); void R_RotateBmodel (void); void R_TransformPlane (mplane_t *p, float *normal, float *dist); void R_SetSkyFrame (void); texture_t *R_TextureAnimation (texture_t *base); void R_GenSkyTile (void *pdest); void R_GenSkyTile16 (void *pdest); void R_DrawSubmodelPolygons (qmodel_t *pmodel, int clipflags); void R_DrawSolidClippedSubmodelPolygons (qmodel_t *pmodel); void R_AddPolygonEdges (emitpoint_t *pverts, int numverts, int miplevel); surf_t *R_GetSurf (void); void R_AliasDrawModel (alight_t *plighting); void R_BeginEdgeFrame (void); void R_ScanEdges (qboolean Translucent); ASM_LINKAGE_BEGIN #if id386 void R_Surf8Patch (void); void R_Surf16Patch (void); void R_SurfacePatch (void); void R_Surf8Start (void); void R_Surf8End (void); void R_Surf16Start (void); void R_Surf16End (void); void R_EdgeCodeStart (void); void R_EdgeCodeEnd (void); void R_EdgeCodeStartT (void); void R_EdgeCodeEndT (void); void R_DrawSurfaceBlock16 (void); void R_DrawSurfaceBlock8_mip0 (void); void R_DrawSurfaceBlock8_mip1 (void); void R_DrawSurfaceBlock8_mip2 (void); void R_DrawSurfaceBlock8_mip3 (void); void R_GenerateSpans (void); void R_GenerateTSpans (void); void R_InsertNewEdges (edge_t *edgestoadd, edge_t *edgelist); void R_RemoveEdges (edge_t *pedge); void R_StepActiveU (edge_t *pedge); void R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out); void R_Alias_clip_bottom (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out); void R_Alias_clip_left (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out); void R_Alias_clip_right (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out); void R_AliasTransformAndProjectFinalVerts (finalvert_t *fv, stvert_t *pstverts); void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip); #endif #if id68k void R_DrawSurfaceBlock16 (void); void R_DrawSurfaceBlock8_mip0 (void); void R_DrawSurfaceBlock8_mip1 (void); void R_DrawSurfaceBlock8_mip2 (void); void R_DrawSurfaceBlock8_mip3 (void); void R_GenerateSpans (void); void R_GenerateTSpans (void); void R_InsertNewEdges (edge_t *edgestoadd, edge_t *edgelist); void R_RemoveEdges (edge_t *pedge); void R_StepActiveU (edge_t *pedge); void R_Alias_clip_top (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out); void R_Alias_clip_bottom (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out); void R_Alias_clip_left (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out); void R_Alias_clip_right (finalvert_t *pfv0, finalvert_t *pfv1, finalvert_t *out); void R_AliasTransformAndProjectFinalVerts (finalvert_t *fv, stvert_t *pstverts); void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip); void R_AliasTransformVector (vec3_t in, vec3_t out); void R_AliasTransformFinalVert (finalvert_t *fv, auxvert_t *av, trivertx_t *pverts); #endif ASM_LINKAGE_END void R_AliasProjectFinalVert (finalvert_t *, auxvert_t *); extern int c_faceclip; extern int r_polycount; extern int r_wholepolycount; extern int *pfrustum_indexes[4]; // !!! if this is changed, it must be changed in asm_draw.h too !!! #define NEAR_CLIP 0.01 ASM_LINKAGE_BEGIN extern int ubasestep, errorterm, erroradjustup, erroradjustdown; extern int vstartscan; extern fixed16_t sadjust, tadjust; extern fixed16_t bbextents, bbextentt; ASM_LINKAGE_END #define MAXBVERTINDEXES 1000 // new clipped vertices when clipping bmodels // to the world BSP extern mvertex_t *r_ptverts, *r_ptvertsmax; extern vec3_t sbaseaxis[3], tbaseaxis[3]; extern float entity_rotation[3][3]; extern int r_currentkey; extern int r_currentbkey; typedef struct btofpoly_s { int clipflags; msurface_t *psurf; } btofpoly_t; #define MAX_BTOFPOLYS 5000 // FIXME: tune this extern int numbtofpolys; extern btofpoly_t *pbtofpolys; void R_ZDrawSubmodelPolys (qmodel_t *clmodel); //========================================================= // Alias models //========================================================= #define MAXALIASVERTS 2000 // TODO: tune this #define ALIAS_Z_CLIP_PLANE 5 extern int numverts; extern mtriangle_t *ptriangles; extern int numtriangles; extern aliashdr_t *paliashdr; extern newmdl_t *pmdl; extern float leftclip, topclip, rightclip, bottomclip; extern int r_acliptype; extern finalvert_t *pfinalverts; extern auxvert_t *pauxverts; qboolean R_AliasCheckBBox (void); //========================================================= // turbulence stuff #define AMP 8*0x10000 #define AMP2 3 #define SPEED 20 //========================================================= void R_ReadPointFile_f (void); extern int r_amodels_drawn; extern edge_t *auxedges; extern int r_numallocatededges; ASM_LINKAGE_BEGIN extern edge_t *r_edges, *edge_p, *edge_max; extern edge_t *newedges[MAXHEIGHT]; extern edge_t *removeedges[MAXHEIGHT]; extern int screenwidth; // FIXME: make stack vars when debugging done extern edge_t edge_head; extern edge_t edge_tail; extern edge_t edge_aftertail; extern int r_bmodelactive; ASM_LINKAGE_END extern vrect_t *pconupdate; ASM_LINKAGE_BEGIN extern float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; ASM_LINKAGE_END extern float r_aliastransition, r_resfudge; extern int r_outofsurfaces; extern int r_outofedges; extern mvertex_t *r_pcurrentvertbase; extern int r_maxvalidedgeoffset; void R_AliasClipTriangle (mtriangle_t *ptri); extern float r_time1; extern float dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2; extern float se_time1, se_time2, de_time1, de_time2, dv_time1, dv_time2; extern int r_frustum_indexes[4*6]; extern int r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs; extern qboolean r_surfsonstack; extern cshift_t cshift_water; extern qboolean r_dowarpold, r_viewchanged; extern float r_lasttime1; extern mleaf_t *r_viewleaf, *r_oldviewleaf; extern vec3_t r_emins, r_emaxs; extern mnode_t *r_pefragtopnode; extern int r_clipflags; extern int r_dlightframecount; extern qboolean r_fov_greater_than_90; ASM_LINKAGE_BEGIN extern int FoundTrans; extern int TransCount; ASM_LINKAGE_END void R_StoreEfrags (efrag_t **ppefrag); void R_TimeRefresh_f (void); void R_TimeGraph (void); void R_PrintAliasStats (void); void R_PrintTimes (void); void R_PrintDSpeeds (void); void R_AnimateLight (void); int R_LightPoint (vec3_t p); int *R_LightPointColour (vec3_t p); void R_SetupFrame (void); void R_cshift_f (void); void R_SplitEntityOnNode2 (mnode_t *node); void R_MarkLights (dlight_t *light, int bit, mnode_t *node); #endif /* !GLQUAKE */ #endif /* R_LOCAL_H */ engine/h2shared/r_shared.h000066400000000000000000000126201444734033100157670ustar00rootroot00000000000000/* * r_shared.h -- general refresh-related stuff shared between the * refresh and the driver. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef R_SHARED_H #define R_SHARED_H #ifndef GLQUAKE // FIXME: clean up and move into d_iface.h #define MAXVERTS 16 // max points in a surface polygon #define MAXWORKINGVERTS (MAXVERTS+4) // max points in an intermediate // polygon (while processing) // !!! if this is changed, it must be changed in d_ifacea.h too !!! #define MAXHEIGHT 1024 /* also in d_polysa.inc !!! */ /* be careful if you ever want to change MAXWIDTH: 12.20 fixed * point math used in R_ScanEdges() overflows at width 2048 !! */ #define MAXWIDTH 1280 #define MAXDIMENSION ((MAXHEIGHT > MAXWIDTH) ? MAXHEIGHT : MAXWIDTH) #define SIN_BUFFER_SIZE (MAXDIMENSION+CYCLE) #define INFINITE_DISTANCE 0x10000 // distance that's always guaranteed to be // farther away than anything in the scene //============================================================================= extern void R_DrawLine (polyvert_t *polyvert0, polyvert_t *polyvert1); ASM_LINKAGE_BEGIN extern int cachewidth; extern pixel_t *cacheblock; extern int screenwidth; ASM_LINKAGE_END extern float pixelAspect; extern int r_drawnpolycount; extern cvar_t r_clearcolor; extern int sintable[SIN_BUFFER_SIZE]; extern int intsintable[SIN_BUFFER_SIZE]; extern vec3_t vup, base_vup; extern vec3_t vpn, base_vpn; extern vec3_t vright, base_vright; extern entity_t *currententity; #define NUMSTACKEDGES 2400 #define MINEDGES NUMSTACKEDGES #define NUMSTACKSURFACES 1000 /* 800 */ #define MINSURFACES NUMSTACKSURFACES #define MAXSPANS 3000 // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct espan_s { int u, v, count; struct espan_s *pnext; } espan_t; // FIXME: compress, make a union if that will help // insubmodel is only 1, flags is fewer than 32, spanstate could be a byte typedef struct surf_s { struct surf_s *next; // active surface stack in r_edge.c struct surf_s *prev; // used in r_edge.c for active surf stack struct espan_s *spans; // pointer to linked list of spans to draw int key; // sorting key (BSP order) int last_u; // set during tracing int spanstate; // 0 = not in span // 1 = in span // -1 = in inverted span (end before // start) int flags; // currentface flags void *data; // associated data like msurface_t entity_t *entity; float nearzi; // nearest 1/z on surface, for mipmapping qboolean insubmodel; float d_ziorigin, d_zistepu, d_zistepv; int pad[2]; // to 64 bytes } surf_t; ASM_LINKAGE_BEGIN extern surf_t *surfaces, *surface_p, *surf_max; ASM_LINKAGE_END // surfaces are generated in back to front order by the bsp, so if a surf // pointer is greater than another one, it should be drawn in front // surfaces[1] is the background, and is used as the active surface stack. // surfaces[0] is a dummy, because index 0 is used to indicate no surface // attached to an edge_t //=================================================================== extern vec3_t sxformaxis[4]; // s axis transformed into viewspace extern vec3_t txformaxis[4]; // t axis transformed into viewspac ASM_LINKAGE_BEGIN extern vec3_t modelorg, base_modelorg; extern float xcenter, ycenter; extern float xscale, yscale; extern float xscaleinv, yscaleinv; extern float xscaleshrink, yscaleshrink; ASM_LINKAGE_END extern int d_lightstylevalue[256]; // 8.8 frac of base light value ASM_LINKAGE_BEGIN extern int ubasestep, errorterm, erroradjustup, erroradjustdown; // m68k asm references this one. extern int r_skymade; void TransformVector (vec3_t in, vec3_t out); void R_TransformFrustum (void); // this is in asm for m68k amiga void R_MakeSky (void); ASM_LINKAGE_END // flags in finalvert_t.flags #define ALIAS_LEFT_CLIP 0x0001 #define ALIAS_TOP_CLIP 0x0002 #define ALIAS_RIGHT_CLIP 0x0004 #define ALIAS_BOTTOM_CLIP 0x0008 #define ALIAS_Z_CLIP 0x0010 // !!! if this is changed, it must be changed in d_ifacea.h too !!! #define ALIAS_ONSEAM 0x0020 // also defined in genmodel.h // must be kept in sync #define ALIAS_XY_CLIP_MASK 0x000F // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct edge_s { fixed16_t u; fixed16_t u_step; struct edge_s *prev, *next; unsigned short surfs[2]; struct edge_s *nextremove; float nearzi; medge_t *owner; } edge_t; ASM_LINKAGE_BEGIN extern byte *mainTransTable; extern byte *transTable; /* the particle table */ #define NUMVERTEXNORMALS 162 extern float r_avertexnormals[NUMVERTEXNORMALS][3]; ASM_LINKAGE_END extern byte *playerTranslation; extern const int color_offsets[MAX_PLAYER_CLASS]; #endif /* !GLQUAKE */ #endif /* R_SHARED_H */ engine/h2shared/r_sky.c000066400000000000000000000124101444734033100153170ustar00rootroot00000000000000/* * r_sky.c * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" #include "d_local.h" static int iskyspeed = 4; static int iskyspeed2 = 1; float skyspeed, skyspeed2; float skytime; byte *r_skysource; int r_skymade; // TODO: clean up these routines // these are global for amiga m68k asm ASM_LINKAGE_BEGIN byte bottomsky[128*131]; byte bottommask[128*131]; byte newsky[128*256]; ASM_LINKAGE_END // newsky and topsky both pack in here, 128 bytes // of newsky on the left of each scan, 128 bytes // of topsky on the right, because the low-level // drawers need 256-byte scan widths /* ============= R_InitSky A sky texture is 256*128, with the right side being a masked overlay ============== */ void R_InitSky (texture_t *mt) { int i, j; byte *src; src = (byte *)mt + mt->offsets[0]; for (i = 0; i < 128; i++) { for (j = 0; j < 128; j++) { newsky[(i*256) + j + 128] = src[i*256 + j + 128]; } } for (i = 0; i < 128; i++) { for (j = 0; j < 131; j++) { if (src[i*256 + (j & 0x7F)]) { bottomsky[(i*131) + j] = src[i*256 + (j & 0x7F)]; bottommask[(i*131) + j] = 0; } else { bottomsky[(i*131) + j] = 0; bottommask[(i*131) + j] = 0xff; } } } r_skysource = newsky; } #if !id68k /* ================= R_MakeSky ================= */ void R_MakeSky (void) { int x, y; int ofs, baseofs; int xshift, yshift; unsigned int *pnewsky; static int xlast = -1, ylast = -1; xshift = skytime * skyspeed; yshift = skytime * skyspeed; if ((xshift == xlast) && (yshift == ylast)) return; xlast = xshift; ylast = yshift; pnewsky = (unsigned int *)&newsky[0]; for (y = 0; y < SKYSIZE; y++) { baseofs = ((y + yshift) & SKYMASK) * 131; // FIXME: clean this up #if UNALIGNED_OK for (x = 0; x < SKYSIZE; x += 4) { ofs = baseofs + ((x + xshift) & SKYMASK); // PORT: unaligned dword access to bottommask and bottomsky *pnewsky = (*(pnewsky + (128 / sizeof(unsigned int))) & *(unsigned int *)&bottommask[ofs]) | *(unsigned int *)&bottomsky[ofs]; pnewsky++; } #else for (x = 0; x < SKYSIZE; x++) { ofs = baseofs + ((x + xshift) & SKYMASK); *(byte *)pnewsky = (*((byte *)pnewsky + 128) & *(byte *)&bottommask[ofs]) | *(byte *)&bottomsky[ofs]; pnewsky = (unsigned int *)((byte *)pnewsky + 1); } #endif pnewsky += 128 / sizeof(unsigned int); } r_skymade = 1; } #endif /* !id68k */ #if 0 /* ================= R_GenSkyTile ================= */ void R_GenSkyTile (void *pdest) { int x, y; int ofs, baseofs; int xshift, yshift; unsigned int *pnewsky; unsigned int *pd; xshift = skytime * skyspeed; yshift = skytime * skyspeed; pnewsky = (unsigned int *)&newsky[0]; pd = (unsigned int *)pdest; for (y = 0; y < SKYSIZE; y++) { baseofs = ((y + yshift) & SKYMASK) * 131; // FIXME: clean this up #if UNALIGNED_OK for (x = 0; x < SKYSIZE; x += 4) { ofs = baseofs + ((x + xshift) & SKYMASK); // PORT: unaligned dword access to bottommask and bottomsky *pd = (*(pnewsky + (128 / sizeof(unsigned int))) & *(unsigned int *)&bottommask[ofs]) | *(unsigned int *)&bottomsky[ofs]; pnewsky++; pd++; } #else for (x = 0; x < SKYSIZE; x++) { ofs = baseofs + ((x + xshift) & SKYMASK); *(byte *)pd = (*((byte *)pnewsky + 128) & *(byte *)&bottommask[ofs]) | *(byte *)&bottomsky[ofs]; pnewsky = (unsigned int *)((byte *)pnewsky + 1); pd = (unsigned int *)((byte *)pd + 1); } #endif pnewsky += 128 / sizeof(unsigned int); } } /* ================= R_GenSkyTile16 ================= */ void R_GenSkyTile16 (void *pdest) { int x, y; int ofs, baseofs; int xshift, yshift; byte *pnewsky; unsigned short *pd; xshift = skytime * skyspeed; yshift = skytime * skyspeed; pnewsky = (byte *)&newsky[0]; pd = (unsigned short *)pdest; for (y = 0; y < SKYSIZE; y++) { baseofs = ((y + yshift) & SKYMASK) * 131; // FIXME: clean this up // FIXME: do faster unaligned version? for (x = 0; x < SKYSIZE; x++) { ofs = baseofs + ((x + xshift) & SKYMASK); *pd = d_8to16table[(*(pnewsky + 128) & *(byte *)&bottommask[ofs]) | *(byte *)&bottomsky[ofs]]; pnewsky++; pd++; } pnewsky += TILE_SIZE; } } #endif /* ============= R_SetSkyFrame ============== */ void R_SetSkyFrame (void) { int g, s1, s2; float temp; skyspeed = iskyspeed; skyspeed2 = iskyspeed2; g = GreatestCommonDivisor (iskyspeed, iskyspeed2); s1 = iskyspeed / g; s2 = iskyspeed2 / g; temp = SKYSIZE * s1 * s2; skytime = cl.time - ((int)(cl.time / temp) * temp); r_skymade = 0; } engine/h2shared/r_sky68k.s000066400000000000000000000061531444734033100156770ustar00rootroot00000000000000** ** Quake for AMIGA ** r_sky.c assembler implementations by Frank Wille ** XREF _r_skymade XREF _bottommask XREF _bottomsky XREF _newsky XREF _skytime XREF _skyspeed XDEF _R_MakeSky SKYSHIFT = 7 SKYSIZE = (1 << SKYSHIFT) SKYMASK = (SKYSIZE - 1) ****************************************************************************** * * void R_MakeSky (void) * * This function contains unaligned accesses to longwords (supported * by 68K) * ****************************************************************************** cnop 0,4 _R_MakeSky * xshift = skytime*skyspeed; * yshift = skytime*skyspeed; * * if ((xshift == xlast) && (yshift == ylast)) * return; * * xlast = xshift; * ylast = yshift; * * pnewsky = (unsigned *)&newsky[0]; movem.l d2-d5/a2,-(sp) lea _bottommask,a1 lea _bottomsky,a2 fmove.s _skytime,fp0 ;xshift = skytime*skyspeed fmul.s _skyspeed,fp0 fmove.l fp0,d0 cmp.l _xlast,d0 ;if (xshift==xlast)&&(yshift==ylast) bne.b .cont cmp.l _ylast,d0 beq.b .exit ;return .cont move.l d0,_xlast ;_xlast = xshift move.l d0,_ylast ;_ylast = yshift lea _newsky,a0 ;pnewsky = (unsigned *)&newsky[0] * for (y=0 ; ydist; pclipnormal = pclipplane->normal; // calc dists if (clip_current) { in = clip_verts[1][0]; outstep = clip_verts[0][0]; clip_current = 0; } else { in = clip_verts[0][0]; outstep = clip_verts[1][0]; clip_current = 1; } instep = in; for (i = 0 ; i < nump ; i++, instep += sizeof (vec5_t) / sizeof (float)) { dists[i] = DotProduct (instep, pclipnormal) - clipdist; } // handle wraparound case dists[nump] = dists[0]; memcpy (instep, in, sizeof (vec5_t)); // clip the winding instep = in; outcount = 0; for (i = 0 ; i < nump ; i++, instep += sizeof (vec5_t) / sizeof (float)) { if (dists[i] >= 0) { memcpy (outstep, instep, sizeof (vec5_t)); outstep += sizeof (vec5_t) / sizeof (float); outcount++; } if (dists[i] == 0 || dists[i+1] == 0) continue; if ( (dists[i] > 0) == (dists[i+1] > 0) ) continue; // split it into a new vertex frac = dists[i] / (dists[i] - dists[i+1]); vert2 = instep + sizeof (vec5_t) / sizeof (float); outstep[0] = instep[0] + frac*(vert2[0] - instep[0]); outstep[1] = instep[1] + frac*(vert2[1] - instep[1]); outstep[2] = instep[2] + frac*(vert2[2] - instep[2]); outstep[3] = instep[3] + frac*(vert2[3] - instep[3]); outstep[4] = instep[4] + frac*(vert2[4] - instep[4]); outstep += sizeof (vec5_t) / sizeof (float); outcount++; } return outcount; } /* ================ R_SetupAndDrawSprite ================ */ static void R_SetupAndDrawSprite (void) { int i, nump; float dot, scale, *pv; vec5_t *pverts; vec3_t left, up, right, down, transformed, local; emitpoint_t outverts[MAXWORKINGVERTS+1], *pout; dot = DotProduct (r_spritedesc.vpn, modelorg); // backface cull if (dot >= 0) return; // build the sprite poster in worldspace VectorScale (r_spritedesc.vright, r_spritedesc.pspriteframe->right, right); VectorScale (r_spritedesc.vup, r_spritedesc.pspriteframe->up, up); VectorScale (r_spritedesc.vright, r_spritedesc.pspriteframe->left, left); VectorScale (r_spritedesc.vup, r_spritedesc.pspriteframe->down, down); pverts = clip_verts[0]; pverts[0][0] = r_entorigin[0] + up[0] + left[0]; pverts[0][1] = r_entorigin[1] + up[1] + left[1]; pverts[0][2] = r_entorigin[2] + up[2] + left[2]; pverts[0][3] = 0; pverts[0][4] = 0; pverts[1][0] = r_entorigin[0] + up[0] + right[0]; pverts[1][1] = r_entorigin[1] + up[1] + right[1]; pverts[1][2] = r_entorigin[2] + up[2] + right[2]; pverts[1][3] = sprite_width; pverts[1][4] = 0; pverts[2][0] = r_entorigin[0] + down[0] + right[0]; pverts[2][1] = r_entorigin[1] + down[1] + right[1]; pverts[2][2] = r_entorigin[2] + down[2] + right[2]; pverts[2][3] = sprite_width; pverts[2][4] = sprite_height; pverts[3][0] = r_entorigin[0] + down[0] + left[0]; pverts[3][1] = r_entorigin[1] + down[1] + left[1]; pverts[3][2] = r_entorigin[2] + down[2] + left[2]; pverts[3][3] = 0; pverts[3][4] = sprite_height; // clip to the frustum in worldspace nump = 4; clip_current = 0; for (i = 0 ; i < 4 ; i++) { nump = R_ClipSpriteFace (nump, &view_clipplanes[i]); if (nump < 3) return; if (nump >= MAXWORKINGVERTS) Sys_Error("%s: too many points", __thisfunc__); } // transform vertices into viewspace and project pv = &clip_verts[clip_current][0][0]; r_spritedesc.nearzi = -999999; for (i = 0 ; i < nump ; i++) { VectorSubtract (pv, r_origin, local); TransformVector (local, transformed); if (transformed[2] < NEAR_CLIP) transformed[2] = NEAR_CLIP; pout = &outverts[i]; pout->zi = 1.0 / transformed[2]; if (pout->zi > r_spritedesc.nearzi) r_spritedesc.nearzi = pout->zi; pout->s = pv[3]; pout->t = pv[4]; scale = xscale * pout->zi; pout->u = (xcenter + scale * transformed[0]); scale = yscale * pout->zi; pout->v = (ycenter - scale * transformed[1]); pv += sizeof (vec5_t) / sizeof (*pv); } // draw it r_spritedesc.nump = nump; r_spritedesc.pverts = outverts; D_DrawSprite (); } /* ================ R_GetSpriteframe ================ */ static mspriteframe_t *R_GetSpriteframe (msprite_t *psprite) { mspritegroup_t *pspritegroup; mspriteframe_t *pspriteframe; int i, numframes, frame; float *pintervals, fullinterval, targettime, time; frame = currententity->frame; if ((frame >= psprite->numframes) || (frame < 0)) { Con_DPrintf ("%s: no such frame %d\n", __thisfunc__, frame); frame = 0; } if (psprite->frames[frame].type == SPR_SINGLE) { pspriteframe = psprite->frames[frame].frameptr; } else { pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; pintervals = pspritegroup->intervals; numframes = pspritegroup->numframes; fullinterval = pintervals[numframes-1]; time = cl.time + currententity->syncbase; // when loading in Mod_LoadSpriteGroup, we guaranteed all interval // values are positive, so we don't have to worry about division by 0 targettime = time - ((int)(time / fullinterval)) * fullinterval; for (i = 0 ; i < (numframes-1) ; i++) { if (pintervals[i] > targettime) break; } pspriteframe = pspritegroup->frames[i]; } return pspriteframe; } /* ================ R_DrawSprite ================ */ void R_DrawSprite (void) { int i; msprite_t *psprite; vec3_t tvec; float dot; psprite = (msprite_t *) currententity->model->cache.data; r_spritedesc.pspriteframe = R_GetSpriteframe (psprite); sprite_width = r_spritedesc.pspriteframe->width; sprite_height = r_spritedesc.pspriteframe->height; // TODO: make this caller-selectable if (psprite->type == SPR_FACING_UPRIGHT) { // generate the sprite's axes, with vup straight up in worldspace, and // r_spritedesc.vright perpendicular to modelorg. // This will not work if the view direction is very close to straight up or // down, because the cross product will be between two nearly parallel // vectors and starts to approach an undefined state, so we don't draw if // the two vectors are less than 1 degree apart tvec[0] = -modelorg[0]; tvec[1] = -modelorg[1]; tvec[2] = -modelorg[2]; VectorNormalizeFast (tvec); dot = tvec[2]; // same as DotProduct (tvec, r_spritedesc.vup) because // r_spritedesc.vup is 0, 0, 1 if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848 return; r_spritedesc.vup[0] = 0; r_spritedesc.vup[1] = 0; r_spritedesc.vup[2] = 1; r_spritedesc.vright[0] = tvec[1]; // CrossProduct(r_spritedesc.vup, -modelorg, r_spritedesc.vright[1] = -tvec[0]; // r_spritedesc.vright) r_spritedesc.vright[2] = 0; VectorNormalizeFast (r_spritedesc.vright); r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; r_spritedesc.vpn[1] = r_spritedesc.vright[0]; r_spritedesc.vpn[2] = 0; // CrossProduct (r_spritedesc.vright, r_spritedesc.vup, // r_spritedesc.vpn) } else if (psprite->type == SPR_VP_PARALLEL) { // generate the sprite's axes, completely parallel to the viewplane. There // are no problem situations, because the sprite is always in the same // position relative to the viewer for (i = 0 ; i < 3 ; i++) { r_spritedesc.vup[i] = vup[i]; r_spritedesc.vright[i] = vright[i]; r_spritedesc.vpn[i] = vpn[i]; } } else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) { // generate the sprite's axes, with vup straight up in worldspace, and // r_spritedesc.vright parallel to the viewplane. // This will not work if the view direction is very close to straight up or // down, because the cross product will be between two nearly parallel // vectors and starts to approach an undefined state, so we don't draw if // the two vectors are less than 1 degree apart dot = vpn[2]; // same as DotProduct (vpn, r_spritedesc.vup) because // r_spritedesc.vup is 0, 0, 1 if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848 return; r_spritedesc.vup[0] = 0; r_spritedesc.vup[1] = 0; r_spritedesc.vup[2] = 1; r_spritedesc.vright[0] = vpn[1]; // CrossProduct (r_spritedesc.vup, vpn, r_spritedesc.vright[1] = -vpn[0]; // r_spritedesc.vright) r_spritedesc.vright[2] = 0; VectorNormalizeFast (r_spritedesc.vright); r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; r_spritedesc.vpn[1] = r_spritedesc.vright[0]; r_spritedesc.vpn[2] = 0; // CrossProduct (r_spritedesc.vright, r_spritedesc.vup, // r_spritedesc.vpn) } else if (psprite->type == SPR_ORIENTED) { // generate the sprite's axes, according to the sprite's world orientation AngleVectors (currententity->angles, r_spritedesc.vpn, r_spritedesc.vright, r_spritedesc.vup); } else if (psprite->type == SPR_VP_PARALLEL_ORIENTED) { // generate the sprite's axes, parallel to the viewplane, but rotated in // that plane around the center according to the sprite entity's roll // angle. So vpn stays the same, but vright and vup rotate float sr, cr; q_sincosdeg(currententity->angles[ROLL], &sr, &cr); for (i = 0 ; i < 3 ; i++) { r_spritedesc.vpn[i] = vpn[i]; r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr; r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr; } } else { Sys_Error ("%s: Bad sprite type %d", __thisfunc__, psprite->type); } R_RotateSprite (psprite->beamlength); R_SetupAndDrawSprite (); } engine/h2shared/r_surf.c000066400000000000000000000351701444734033100155000ustar00rootroot00000000000000/* r_surf.c -- surface-related refresh code * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" drawsurf_t r_drawsurf; ASM_LINKAGE_BEGIN int lightleft, sourcesstep, blocksize, sourcetstep; int lightdelta, lightdeltastep; int lightright, lightleftstep, lightrightstep, blockdivshift; unsigned int blockdivmask; void *prowdestbase; unsigned char *pbasesource; int surfrowbytes; // used by ASM files unsigned int *r_lightptr; int r_stepback; int r_lightwidth; int r_numhblocks, r_numvblocks; unsigned char *r_source, *r_sourcemax; ASM_LINKAGE_END static unsigned int blocklights[18*18]; #if !id386 && !id68k static void R_DrawSurfaceBlock16 (void); static void R_DrawSurfaceBlock8_mip0 (void); static void R_DrawSurfaceBlock8_mip1 (void); static void R_DrawSurfaceBlock8_mip2 (void); static void R_DrawSurfaceBlock8_mip3 (void); #endif static void (*surfmiptable[4])(void) = { R_DrawSurfaceBlock8_mip0, R_DrawSurfaceBlock8_mip1, R_DrawSurfaceBlock8_mip2, R_DrawSurfaceBlock8_mip3 }; //============================================================================= /* =============== R_AddDynamicLights =============== */ static void R_AddDynamicLights (void) { int lnum; int sd, td; float dist, rad, minlight, lightval; vec3_t impact; vec_t local0, local1; int s, t; int i; int smax, tmax; unsigned int *pos; mtexinfo_t *tex; msurface_t *surf; if (!r_dynamic.integer) return; surf = r_drawsurf.surf; smax = (surf->extents[0]>>4)+1; tmax = (surf->extents[1]>>4)+1; tex = surf->texinfo; for (lnum = 0; lnum < MAX_DLIGHTS; lnum++) { if ( !(surf->dlightbits & (1<plane->normal) - surf->plane->dist; rad -= fabs(dist); minlight = cl_dlights[lnum].minlight; if (rad < minlight) continue; minlight = rad - minlight; if (cl_dlights[lnum].radius < 0) rad = -rad; for (i = 0; i < 3; i++) { impact[i] = cl_dlights[lnum].origin[i] - surf->plane->normal[i]*dist; } local0 = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3] - surf->texturemins[0]; local1 = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3] - surf->texturemins[1]; pos = blocklights; for (t = 0; t < tmax; t++) { td = local1 - t*16; if (td < 0) td = -td; for (s = 0; s < smax; s++, pos++) { sd = local0 - s*16; if (sd < 0) sd = -sd; if (sd > td) dist = sd + (td>>1); else dist = td + (sd>>1); if (dist < minlight) { unsigned int temp; temp = (rad - dist)*256; i = t*smax + s; if (!cl_dlights[lnum].dark) { if (rad >= 0) *pos += temp; else { lightval = (dist + rad)*256; if (lightval < 0 && -lightval > blocklights[t*smax + s]) *pos = 0; else *pos += lightval; } } else { if (*pos > temp) *pos -= temp; else *pos = 0; } } } } } } /* =============== R_BuildLightMap Combine and scale multiple lightmaps into the 8.8 format in blocklights =============== */ static void R_BuildLightMap (void) { int smax, tmax; int t; int i, size; byte *lightmap; unsigned int scale; int maps; msurface_t *surf; int light; surf = r_drawsurf.surf; smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; size = smax*tmax; lightmap = surf->samples; if (r_fullbright.integer || !cl.worldmodel->lightdata) { for (i = 0; i < size; i++) blocklights[i] = 0; return; } if ((currententity->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT) { light = (255-currententity->abslight)<styles[maps] != 255 ; maps++) { scale = r_drawsurf.lightadj[maps]; // 8.8 fraction for (i = 0; i < size; i++) blocklights[i] += lightmap[i] * scale; lightmap += size; // skip to next lightmap } } // add all the dynamic lights if (surf->dlightframe == r_framecount) R_AddDynamicLights (); // bound, invert, and shift for (i = 0; i < size; i++) { t = (255*256 - (int)blocklights[i]) >> (8 - VID_CBITS); if (t < (1 << 6)) t = (1 << 6); blocklights[i] = t; } } /* =============== R_TextureAnimation Returns the proper texture for a given time and base texture =============== */ texture_t *R_TextureAnimation (texture_t *base) { int reletive; int count; if (currententity->frame) { if (base->alternate_anims) base = base->alternate_anims; } if (!base->anim_total) return base; reletive = (int)(cl.time*10) % base->anim_total; count = 0; while (base->anim_min > reletive || base->anim_max <= reletive) { base = base->anim_next; if (!base) Sys_Error ("%s: broken cycle", __thisfunc__); if (++count > 100) Sys_Error ("%s: infinite cycle", __thisfunc__); } return base; } /* =============== R_DrawSurface =============== */ void R_DrawSurface (void) { unsigned char *basetptr; int smax, tmax, twidth; int u; int soffset, basetoffset, texwidth; int horzblockstep; unsigned char *pcolumndest; void (*pblockdrawer)(void); texture_t *mt; // calculate the lightings R_BuildLightMap (); surfrowbytes = r_drawsurf.rowbytes; mt = r_drawsurf.texture; r_source = (byte *)mt + mt->offsets[r_drawsurf.surfmip]; // the fractional light values should range from 0 to (VID_GRADES - 1) << 16 // from a source range of 0 - 255 texwidth = mt->width >> r_drawsurf.surfmip; blocksize = 16 >> r_drawsurf.surfmip; blockdivshift = 4 - r_drawsurf.surfmip; blockdivmask = (1 << blockdivshift) - 1; r_lightwidth = (r_drawsurf.surf->extents[0] >> 4) + 1; r_numhblocks = r_drawsurf.surfwidth >> blockdivshift; r_numvblocks = r_drawsurf.surfheight >> blockdivshift; if (r_pixbytes == 1) { pblockdrawer = surfmiptable[r_drawsurf.surfmip]; // TODO: only needs to be set when there is a display settings change horzblockstep = blocksize; } else { pblockdrawer = R_DrawSurfaceBlock16; // TODO: only needs to be set when there is a display settings change horzblockstep = blocksize << 1; } smax = mt->width >> r_drawsurf.surfmip; twidth = texwidth; tmax = mt->height >> r_drawsurf.surfmip; sourcetstep = texwidth; r_stepback = tmax * twidth; r_sourcemax = r_source + (tmax * smax); soffset = r_drawsurf.surf->texturemins[0]; basetoffset = r_drawsurf.surf->texturemins[1]; // << 16 components are to guarantee positive values for % soffset = ((soffset >> r_drawsurf.surfmip) + (smax << 16)) % smax; basetptr = &r_source[((((basetoffset >> r_drawsurf.surfmip) + (tmax << 16)) % tmax) * twidth)]; pcolumndest = r_drawsurf.surfdat; for (u = 0; u < r_numhblocks; u++) { r_lightptr = blocklights + u; prowdestbase = pcolumndest; pbasesource = basetptr + soffset; (*pblockdrawer)(); soffset = soffset + blocksize; if (soffset >= smax) soffset = 0; pcolumndest += horzblockstep; } } //============================================================================= #if !id386 && !id68k /* ================ R_DrawSurfaceBlock8_mip0 ================ */ static void R_DrawSurfaceBlock8_mip0 (void) { int v, i, b, lightstep, lighttemp, light; unsigned char pix, *psource, *prowdest; psource = pbasesource; prowdest = (unsigned char *) prowdestbase; for (v = 0; v < r_numvblocks; v++) { // FIXME: make these locals? // FIXME: use delta rather than both right and left, like ASM? lightleft = r_lightptr[0]; lightright = r_lightptr[1]; r_lightptr += r_lightwidth; lightleftstep = (r_lightptr[0] - lightleft) >> 4; lightrightstep = (r_lightptr[1] - lightright) >> 4; for (i = 0; i < 16; i++) { lighttemp = lightleft - lightright; lightstep = lighttemp >> 4; light = lightright; for (b = 15; b >= 0; b--) { pix = psource[b]; prowdest[b] = ((unsigned char *)vid.colormap)[(light & 0xFF00) + pix]; light += lightstep; } psource += sourcetstep; lightright += lightrightstep; lightleft += lightleftstep; prowdest += surfrowbytes; } if (psource >= r_sourcemax) psource -= r_stepback; } } /* ================ R_DrawSurfaceBlock8_mip1 ================ */ static void R_DrawSurfaceBlock8_mip1 (void) { int v, i, b, lightstep, lighttemp, light; unsigned char pix, *psource, *prowdest; psource = pbasesource; prowdest = (unsigned char *) prowdestbase; for (v = 0; v < r_numvblocks; v++) { // FIXME: make these locals? // FIXME: use delta rather than both right and left, like ASM? lightleft = r_lightptr[0]; lightright = r_lightptr[1]; r_lightptr += r_lightwidth; lightleftstep = (r_lightptr[0] - lightleft) >> 3; lightrightstep = (r_lightptr[1] - lightright) >> 3; for (i = 0; i < 8; i++) { lighttemp = lightleft - lightright; lightstep = lighttemp >> 3; light = lightright; for (b = 7; b >= 0; b--) { pix = psource[b]; prowdest[b] = ((unsigned char *)vid.colormap)[(light & 0xFF00) + pix]; light += lightstep; } psource += sourcetstep; lightright += lightrightstep; lightleft += lightleftstep; prowdest += surfrowbytes; } if (psource >= r_sourcemax) psource -= r_stepback; } } /* ================ R_DrawSurfaceBlock8_mip2 ================ */ static void R_DrawSurfaceBlock8_mip2 (void) { int v, i, b, lightstep, lighttemp, light; unsigned char pix, *psource, *prowdest; psource = pbasesource; prowdest = (unsigned char *) prowdestbase; for (v = 0; v < r_numvblocks; v++) { // FIXME: make these locals? // FIXME: use delta rather than both right and left, like ASM? lightleft = r_lightptr[0]; lightright = r_lightptr[1]; r_lightptr += r_lightwidth; lightleftstep = (r_lightptr[0] - lightleft) >> 2; lightrightstep = (r_lightptr[1] - lightright) >> 2; for (i = 0; i < 4; i++) { lighttemp = lightleft - lightright; lightstep = lighttemp >> 2; light = lightright; for (b = 3; b >= 0; b--) { pix = psource[b]; prowdest[b] = ((unsigned char *)vid.colormap)[(light & 0xFF00) + pix]; light += lightstep; } psource += sourcetstep; lightright += lightrightstep; lightleft += lightleftstep; prowdest += surfrowbytes; } if (psource >= r_sourcemax) psource -= r_stepback; } } /* ================ R_DrawSurfaceBlock8_mip3 ================ */ static void R_DrawSurfaceBlock8_mip3 (void) { int v, i, b, lightstep, lighttemp, light; unsigned char pix, *psource, *prowdest; psource = pbasesource; prowdest = (unsigned char *) prowdestbase; for (v = 0; v < r_numvblocks; v++) { // FIXME: make these locals? // FIXME: use delta rather than both right and left, like ASM? lightleft = r_lightptr[0]; lightright = r_lightptr[1]; r_lightptr += r_lightwidth; lightleftstep = (r_lightptr[0] - lightleft) >> 1; lightrightstep = (r_lightptr[1] - lightright) >> 1; for (i = 0; i < 2; i++) { lighttemp = lightleft - lightright; lightstep = lighttemp >> 1; light = lightright; for (b = 1; b >= 0; b--) { pix = psource[b]; prowdest[b] = ((unsigned char *)vid.colormap)[(light & 0xFF00) + pix]; light += lightstep; } psource += sourcetstep; lightright += lightrightstep; lightleft += lightleftstep; prowdest += surfrowbytes; } if (psource >= r_sourcemax) psource -= r_stepback; } } /* ================ R_DrawSurfaceBlock16 FIXME: make this work ================ */ static void R_DrawSurfaceBlock16 (void) { int k; int lighttemp, lightstep, light; unsigned char *psource; unsigned short *prowdest; prowdest = (unsigned short *)prowdestbase; for (k = 0; k < blocksize; k++) { unsigned short *pdest; unsigned char pix; int b; psource = pbasesource; lighttemp = lightright - lightleft; lightstep = lighttemp >> blockdivshift; light = lightleft; pdest = prowdest; for (b = 0; b < blocksize; b++) { pix = *psource; *pdest = vid.colormap16[(light & 0xFF00) + pix]; psource += sourcesstep; pdest++; light += lightstep; } pbasesource += sourcetstep; lightright += lightrightstep; lightleft += lightleftstep; prowdest = (unsigned short *)((intptr_t)prowdest + surfrowbytes); } prowdestbase = prowdest; } #endif //============================================================================ #if 0 /* ================ R_GenTurbTile ================ */ static void R_GenTurbTile (pixel_t *pbasetex, void *pdest) { int *turb; int i, j, s, t; byte *pd; turb = sintable + ((int)(cl.time * SPEED) & (CYCLE-1)); pd = (byte *)pdest; for (i = 0; i < TILE_SIZE; i++) { for (j = 0; j < TILE_SIZE; j++) { s = (((j << 16) + turb[i & (CYCLE-1)]) >> 16) & 63; t = (((i << 16) + turb[j & (CYCLE-1)]) >> 16) & 63; *pd++ = *(pbasetex + (t<<6) + s); } } } /* ================ R_GenTurbTile16 ================ */ static void R_GenTurbTile16 (pixel_t *pbasetex, void *pdest) { int *turb; int i, j, s, t; unsigned short *pd; turb = sintable + ((int)(cl.time * SPEED) & (CYCLE-1)); pd = (unsigned short *)pdest; for (i = 0; i < TILE_SIZE; i++) { for (j = 0; j < TILE_SIZE; j++) { s = (((j << 16) + turb[i & (CYCLE-1)]) >> 16) & 63; t = (((i << 16) + turb[j & (CYCLE-1)]) >> 16) & 63; *pd++ = d_8to16table[*(pbasetex + (t<<6) + s)]; } } } /* ================ R_GenTile ================ */ void R_GenTile (msurface_t *psurf, void *pdest) { if (psurf->flags & SURF_DRAWTURB) { if (r_pixbytes == 1) { R_GenTurbTile ((pixel_t *) ((byte *)psurf->texinfo->texture + psurf->texinfo->texture->offsets[0]), pdest); } else { R_GenTurbTile16 ((pixel_t *) ((byte *)psurf->texinfo->texture + psurf->texinfo->texture->offsets[0]), pdest); } } else if (psurf->flags & SURF_DRAWSKY) { if (r_pixbytes == 1) { R_GenSkyTile (pdest); } else { R_GenSkyTile16 (pdest); } } else { Sys_Error ("Unknown tile type"); } } #endif engine/h2shared/r_surf68k.s000066400000000000000000000164501444734033100160510ustar00rootroot00000000000000** ** Quake for AMIGA ** r_surf.c assembler implementations by Frank Wille ** ; INCLUDE "quakedef68k.i" VID_COLORMAP equ 4 code xref _pbasesource xref _prowdestbase xref _r_numvblocks xref _r_lightptr xref _r_lightwidth xref _sourcetstep xref _surfrowbytes xref _r_sourcemax xref _r_stepback xref _vid xdef _R_DrawSurfaceBlock8_mip0 cnop 0,4 _R_DrawSurfaceBlock8_mip0: movem.l d1-d7/a2-a6,-(sp) setso 0 .v so.l 1 ; saved d1-field is used as .v move.l _vid+VID_COLORMAP,a4 move.l _sourcetstep,a5 move.l _surfrowbytes,a6 ; psource = pbasesource; ; prowdest = prowdestbase; move.l _pbasesource,a2 ; a2 psource move.l _prowdestbase,a3 ; a3 prowdest ; for (v=0 ; v> 4; ; lightrightstep = (r_lightptr[1] - lightright) >> 4; move.l _r_lightwidth,d0 lea -4(a0,d0.l*4),a0 move.l a0,_r_lightptr move.l (a0)+,d1 sub.l d6,d1 lsr.l #4,d1 ; d1 lightleftstep move.l (a0),d2 sub.l d7,d2 lsr.l #4,d2 ; d2 lightrightstep ; for (i=0 ; i<16 ; i++) { moveq #15,d5 ; lighttemp = lightleft - lightright; ; lightstep = lighttemp >> 4; ; light = lightright; .1: move.l d6,d4 sub.l d7,d4 lsr.l #4,d4 ; d4 lightstep move.l d7,d3 ; d3 light ; for (b=15; b>=0; b--) { moveq #16,d0 add.w d0,a2 add.w d0,a3 ; pix = psource[b]; ; prowdest[b] = ((unsigned char *)vid.colormap)[(light & 0xFF00) + pix]; ; light += lightstep; REPT 16 move.w d3,d0 move.b -(a2),d0 move.b (a4,d0.l),-(a3) add.l d4,d3 ENDR ; psource += sourcetstep; ; lightright += lightrightstep; ; lightleft += lightleftstep; ; prowdest += surfrowbytes; add.l a5,a2 add.l a6,a3 add.l d1,d6 add.l d2,d7 dbf d5,.1 ; if (psource >= r_sourcemax) psource -= r_stepback; cmp.l _r_sourcemax,a2 blo.b .2 sub.l _r_stepback,a2 .2: subq.l #1,(sp) ; .v bne .vloop .quit: movem.l (sp)+,d1-d7/a2-a6 rts xdef _R_DrawSurfaceBlock8_mip1 cnop 0,4 _R_DrawSurfaceBlock8_mip1: movem.l d1-d7/a2-a6,-(sp) setso 0 .v so.l 1 ; saved d1-field is used as .v move.l _vid+VID_COLORMAP,a4 move.l _sourcetstep,a5 move.l _surfrowbytes,a6 ; psource = pbasesource; ; prowdest = prowdestbase; move.l _pbasesource,a2 ; a2 psource move.l _prowdestbase,a3 ; a3 prowdest ; for (v=0 ; v> 3; ; lightrightstep = (r_lightptr[1] - lightright) >> 3; move.l _r_lightwidth,d0 lea -4(a0,d0.l*4),a0 move.l a0,_r_lightptr move.l (a0)+,d1 sub.l d6,d1 lsr.l #3,d1 ; d1 lightleftstep move.l (a0),d2 sub.l d7,d2 lsr.l #3,d2 ; d2 lightrightstep ; for (i=0 ; i<8 ; i++) { moveq #7,d5 ; lighttemp = lightleft - lightright; ; lightstep = lighttemp >> 3; ; light = lightright; .1: move.l d6,d4 sub.l d7,d4 lsr.l #3,d4 ; d4 lightstep move.l d7,d3 ; d3 light ; for (b=7; b>=0; b--) { moveq #8,d0 add.w d0,a2 add.w d0,a3 ; pix = psource[b]; ; prowdest[b] = ((unsigned char *)vid.colormap)[(light & 0xFF00) + pix]; ; light += lightstep; REPT 8 move.w d3,d0 move.b -(a2),d0 move.b (a4,d0.l),-(a3) add.l d4,d3 ENDR ; psource += sourcetstep; ; lightright += lightrightstep; ; lightleft += lightleftstep; ; prowdest += surfrowbytes; add.l a5,a2 add.l a6,a3 add.l d1,d6 add.l d2,d7 dbf d5,.1 ; if (psource >= r_sourcemax) psource -= r_stepback; cmp.l _r_sourcemax,a2 blo.b .2 sub.l _r_stepback,a2 .2: subq.l #1,(sp) ; .v bne .vloop .quit: movem.l (sp)+,d1-d7/a2-a6 rts xdef _R_DrawSurfaceBlock8_mip2 cnop 0,4 _R_DrawSurfaceBlock8_mip2: movem.l d1-d7/a2-a6,-(sp) setso 0 .v so.l 1 ; saved d1-field is used as .v move.l _vid+VID_COLORMAP,a4 move.l _sourcetstep,a5 move.l _surfrowbytes,a6 ; psource = pbasesource; ; prowdest = prowdestbase; move.l _pbasesource,a2 ; a2 psource move.l _prowdestbase,a3 ; a3 prowdest ; for (v=0 ; v> 2; ; lightrightstep = (r_lightptr[1] - lightright) >> 2; move.l _r_lightwidth,d0 lea -4(a0,d0.l*4),a0 move.l a0,_r_lightptr move.l (a0)+,d1 sub.l d6,d1 lsr.l #2,d1 ; d1 lightleftstep move.l (a0),d2 sub.l d7,d2 lsr.l #2,d2 ; d2 lightrightstep ; for (i=0 ; i<4 ; i++) { moveq #3,d5 ; lighttemp = lightleft - lightright; ; lightstep = lighttemp >> 3; ; light = lightright; .1: move.l d6,d4 sub.l d7,d4 lsr.l #2,d4 ; d4 lightstep move.l d7,d3 ; d3 light ; for (b=3; b>=0; b--) { moveq #4,d0 add.w d0,a2 add.w d0,a3 ; pix = psource[b]; ; prowdest[b] = ((unsigned char *)vid.colormap)[(light & 0xFF00) + pix]; ; light += lightstep; REPT 4 move.w d3,d0 move.b -(a2),d0 move.b (a4,d0.l),-(a3) add.l d4,d3 ENDR ; psource += sourcetstep; ; lightright += lightrightstep; ; lightleft += lightleftstep; ; prowdest += surfrowbytes; add.l a5,a2 add.l a6,a3 add.l d1,d6 add.l d2,d7 dbf d5,.1 ; if (psource >= r_sourcemax) psource -= r_stepback; cmp.l _r_sourcemax,a2 blo.b .2 sub.l _r_stepback,a2 .2: subq.l #1,(sp) ; .v bne .vloop .quit: movem.l (sp)+,d1-d7/a2-a6 rts xdef _R_DrawSurfaceBlock8_mip3 cnop 0,4 _R_DrawSurfaceBlock8_mip3: movem.l d1-d7/a2-a6,-(sp) setso 0 .v so.l 1 ; saved d1-field is used as .v move.l _vid+VID_COLORMAP,a4 move.l _sourcetstep,a5 move.l _surfrowbytes,a6 ; psource = pbasesource; ; prowdest = prowdestbase; move.l _pbasesource,a2 ; a2 psource move.l _prowdestbase,a3 ; a3 prowdest ; for (v=0 ; v> 1; ; lightrightstep = (r_lightptr[1] - lightright) >> 1; move.l _r_lightwidth,d0 lea -4(a0,d0.l*4),a0 move.l a0,_r_lightptr move.l (a0)+,d1 sub.l d6,d1 lsr.l #1,d1 ; d1 lightleftstep move.l (a0),d2 sub.l d7,d2 lsr.l #1,d2 ; d2 lightrightstep ; for (i=0 ; i<2 ; i++) { moveq #1,d5 ; lighttemp = lightleft - lightright; ; lightstep = lighttemp >> 1; ; light = lightright; .1: move.l d6,d4 sub.l d7,d4 lsr.l #1,d4 ; d4 lightstep move.l d7,d3 ; d3 light ; for (b=1; b>=0; b--) { moveq #2,d0 add.w d0,a2 add.w d0,a3 ; pix = psource[b]; ; prowdest[b] = ((unsigned char *)vid.colormap)[(light & 0xFF00) + pix]; ; light += lightstep; REPT 2 move.w d3,d0 move.b -(a2),d0 move.b (a4,d0.l),-(a3) add.l d4,d3 ENDR ; psource += sourcetstep; ; lightright += lightrightstep; ; lightleft += lightleftstep; ; prowdest += surfrowbytes; add.l a5,a2 add.l a6,a3 add.l d1,d6 add.l d2,d7 dbf d5,.1 ; if (psource >= r_sourcemax) psource -= r_stepback; cmp.l _r_sourcemax,a2 blo.b .2 sub.l _r_stepback,a2 .2: subq.l #1,(sp) ; .v bne .vloop .quit: movem.l (sp)+,d1-d7/a2-a6 rts engine/h2shared/r_vars.c000066400000000000000000000024531444734033100154720ustar00rootroot00000000000000/* r_vars.c -- global refresh variables * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if !id386 // all global and static refresh variables are collected in a contiguous block // to avoid cache conflicts. //------------------------------------------------------- // global refresh variables //------------------------------------------------------- // FIXME: make into one big structure, like cl or sv // FIXME: do separately for refresh engine and driver int r_bmodelactive; int FoundTrans; #endif /* !id386 */ engine/h2shared/r_varsa.asm000066400000000000000000000033311444734033100161650ustar00rootroot00000000000000; ; r_varsa.asm ; global refresh variables ; ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; Copyright (C) 1997-1998 Raven Software Corp. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: ; C-shared globals: _sym_prefix r_bmodelactive _sym_prefix FoundTrans %endif ; _sym_prefix SEGMENT .data ; ; ASM-only variables ; global float_1, float_particle_z_clip, float_point5 global float_minus_1, float_0 float_0 dd 0.0 float_1 dd 1.0 float_minus_1 dd -1.0 float_particle_z_clip dd 8.0 float_point5 dd 0.5 global fp_16, fp_64k, fp_1m, fp_64kx64k global fp_1m_minus_1 global fp_8 fp_1m dd 1048576.0 fp_1m_minus_1 dd 1048575.0 fp_64k dd 65536.0 fp_8 dd 8.0 fp_16 dd 16.0 fp_64kx64k dd 04f000000h global FloatZero, Float2ToThe31nd, FloatMinus2ToThe31nd FloatZero dd 0 Float2ToThe31nd dd 04f000000h FloatMinus2ToThe31nd dd 0cf000000h ; ; C-shared variables ; global r_bmodelactive r_bmodelactive dd 0 global FoundTrans FoundTrans dd 0 engine/h2shared/resource.h000066400000000000000000000016511444734033100160310ustar00rootroot00000000000000// resource.h -- Windows resource header #ifndef __HX2_RESOURCE_H #define __HX2_RESOURCE_H #define IDC_STATIC (-1) #define IDS_STRING1 1 #define IDI_ICON2 1 #define IDD_DIALOG1 108 #define IDD_PROGRESS 109 #define IDB_HXBITMAP 112 #define IDC_PROGRESS 1000 // Icon file to use // #if defined(H2W) #define RES_ICONFILE "../../resource/hexenworld.ico" #elif defined(H2MP) #define RES_ICONFILE "../resource/h2mp.ico" #else #define RES_ICONFILE "../resource/hexen2.ico" #endif // String // #if defined(H2W) #define SPLASH_STR "Starting HexenWorld..." #define RES_STRING "HexenWorld" //#elif defined(H2MP) //#define SPLASH_STR "Starting Hexen II+..." //#define RES_STRING "HexenII" #else #define SPLASH_STR "Starting Hexen II..." #define RES_STRING "HexenII" #endif // Bitmap // #if defined(H2W) #define SPLASH_BMP "../../resource/hexenworld.bmp" #else #define SPLASH_BMP "../resource/hexen2.bmp" #endif #endif /* __HX2_RESOURCE_H */ engine/h2shared/sbar.h000066400000000000000000000030161444734033100151260ustar00rootroot00000000000000/* * sbar.h -- HUD / status bar * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_SBAR_H #define __HX2_SBAR_H // HEADER FILES ------------------------------------------------------------ // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- void Sbar_Init(void); void Sbar_Changed(void); void Sbar_Draw(void); void SB_InvChanged(void); void SB_InvReset(void); void SB_ViewSizeChanged(void); void Sbar_DeathmatchOverlay(void); // PUBLIC DATA DECLARATIONS ------------------------------------------------ extern int sb_lines; // scan lines to draw #endif /* __HX2_SBAR_H */ engine/h2shared/screen.c000066400000000000000000000747771444734033100154770ustar00rootroot00000000000000/* * screen.c -- master for refresh, status bar, console, chat, notify, etc * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /*============================================================================= background clear rendering turtle/net/ram icons sbar centerprint / slow centerprint notify lines intermission / finale overlay loading plaque console menu required background clears required update regions syncronous draw mode or async One off screen buffer, with updates either copied or xblited Need to double buffer? async draw will require the refresh area to be cleared, because it will be xblited, but sync draw can just ignore it. sync draw CenterPrint () SlowPrint () Screen_Update (); Con_Printf (); net turn off messages option the refresh is always rendered, unless the console is full screen console is: notify lines half full =============================================================================*/ #include "quakedef.h" //#include "r_local.h" #ifdef PLATFORM_WINDOWS #include "winquake.h" #endif static qboolean scr_initialized; // ready to draw vrect_t scr_vrect; //vrect_t *pconupdate; /* these are only functional in the software renderer */ int scr_copytop; // only the refresh window will be updated int scr_copyeverything; // unless these variables are flagged int scr_topupdate; int scr_fullupdate; static qboolean scr_needfull = false; static int clearconsole; int clearnotify; float scr_con_current; float scr_conlines; // lines of console to display int trans_level = 0; cvar_t scr_viewsize = {"viewsize", "110", CVAR_ARCHIVE}; cvar_t scr_fov = {"fov", "90", CVAR_NONE}; // 10 - 170 cvar_t scr_fov_adapt = {"fov_adapt", "1", CVAR_ARCHIVE}; // "Hor+" scaling cvar_t scr_contrans = {"contrans", "0", CVAR_ARCHIVE}; static cvar_t scr_conspeed = {"scr_conspeed", "300", CVAR_NONE}; static cvar_t scr_centertime = {"scr_centertime", "4", CVAR_NONE}; static cvar_t scr_showram = {"showram", "1", CVAR_NONE}; static cvar_t scr_showturtle = {"showturtle", "0", CVAR_NONE}; static cvar_t scr_showpause = {"showpause", "1", CVAR_NONE}; static cvar_t scr_showfps = {"showfps", "0", CVAR_NONE}; #if !defined(H2W) static qboolean scr_drawloading; static float scr_disabled_time; int total_loading_size, current_loading_size, loading_stage; #endif /* H2W */ qboolean scr_disabled_for_loading; qboolean scr_skipupdate; qboolean block_drawing; static qpic_t *scr_ram; static qpic_t *scr_net; static qpic_t *scr_turtle; static void SCR_ScreenShot_f (void); static const char *plaquemessage = ""; // pointer to current plaque message static void Plaque_Draw (const char *message, qboolean AlwaysDraw); #if !defined(H2W) /* procedures for the mission pack intro messages and objectives */ static void Info_Plaque_Draw (const char *message); static void Bottom_Plaque_Draw (const char *message); #endif /* H2W */ /* =============================================================================== CENTER PRINTING =============================================================================== */ static char scr_centerstring[1024]; float scr_centertime_off; static int scr_center_lines; static int scr_erase_lines; //static int scr_erase_center; #define MAXLINES 27 static int lines; static int StartC[MAXLINES], EndC[MAXLINES]; #if !defined(H2W) /* mission pack objectives: */ #define MAX_INFO 1024 static char infomessage[MAX_INFO]; static void UpdateInfoMessage (void) { unsigned int i, check; const char *newmessage; q_strlcpy(infomessage, "Objectives:", sizeof(infomessage)); if (!info_string_count) return; for (i = 0; i < 32; i++) { check = (1 << i); if (cl.info_mask & check) { newmessage = CL_GetInfoString(i); q_strlcat(infomessage, "@@", sizeof(infomessage)); q_strlcat(infomessage, newmessage, sizeof(infomessage)); } } for (i = 0; i < 32; i++) { check = (1 << i); if (cl.info_mask2 & check) { newmessage = CL_GetInfoString(i + 32); q_strlcat(infomessage, "@@", sizeof(infomessage)); q_strlcat(infomessage, newmessage, sizeof(infomessage)); } } } #endif /* H2W */ static void FindTextBreaks (const char *message, int Width) { int pos, start, lastspace, oldlast; lines = pos = start = 0; lastspace = -1; while (1) { if (pos-start >= Width || message[pos] == '@' || message[pos] == 0) { oldlast = lastspace; if (message[pos] == '@' || lastspace == -1 || message[pos] == 0) lastspace = pos; StartC[lines] = start; EndC[lines] = lastspace; lines++; if (lines == MAXLINES) return; if (message[pos] == '@') start = pos + 1; else if (oldlast == -1) start = lastspace; else start = lastspace + 1; lastspace = -1; } if (message[pos] == 32) lastspace = pos; else if (message[pos] == 0) break; pos++; } } /* ============== SCR_CenterPrint Called for important messages that should stay in the center of the screen for a few moments ============== */ void SCR_CenterPrint (const char *str) { strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1); scr_centertime_off = scr_centertime.value; FindTextBreaks(scr_centerstring, 38); scr_center_lines = lines; } /* static void SCR_EraseCenterString (void) { int y, height; if (scr_erase_center++ > vid.numpages) { scr_erase_lines = 0; return; } // y = (25-lines) * 8 / 2; y = (scr_center_lines <= 4) ? vid.height*0.35 : 48; scr_copytop = 1; height = q_min(8 * scr_erase_lines, vid.height - y - 1); Draw_TileClear (0, y, vid.width, height); } */ static void SCR_DrawCenterString (void) { int i, cnt; int bx, by; char temp[80]; // scr_erase_center = 0; FindTextBreaks(scr_centerstring, 38); by = (25-lines) * 8 / 2 + ((vid.height - 200)>>1); for (i = 0; i < lines; i++, by += 8) { cnt = EndC[i] - StartC[i]; strncpy (temp, &scr_centerstring[StartC[i]], cnt); temp[cnt] = 0; bx = (40-strlen(temp)) * 8 / 2; M_Print (bx, by, temp); } } static qboolean SCR_CheckDrawCenterString2 (void) { scr_copytop = 1; if (scr_center_lines > scr_erase_lines) scr_erase_lines = scr_center_lines; scr_centertime_off -= host_frametime; if (scr_centertime_off <= 0 && !cl.intermission) return false; if (Key_GetDest() != key_game) return false; return true; } static void SCR_CheckDrawCenterString (void) { if (! SCR_CheckDrawCenterString2()) return; #if !defined(H2W) if (intro_playing) { Bottom_Plaque_Draw(scr_centerstring); return; } #endif /* H2W */ SCR_DrawCenterString (); } //============================================================================= /* ==================== AdaptFovx Adapt a 4:3 horizontal FOV to the current screen size using the "Hor+" scaling: 2.0 * atan(width / height * 3.0 / 4.0 * tan(fov43 / 2.0)) ==================== */ static float AdaptFovx (float fov_x, float width, float height) { float a, x; if (fov_x < 1 || fov_x > 179) Sys_Error ("Bad fov: %f", fov_x); #if defined(PLATFORM_DOS) || defined(SVGAQUAKE) if (vid.aspect > 1.10f) return fov_x; /* no fov_adapt for weird VGA modes */ #endif #ifdef PLATFORM_AMIGAOS3 if (vid.noadapt) return fov_x; /* not for Amiga native chipset modes */ #endif if (!scr_fov_adapt.integer) return fov_x; if ((x = height / width) == 0.75) return fov_x; a = atan(0.75 / x * tan(fov_x / 360 * M_PI)); a = a * 360 / M_PI; return a; } /* ==================== CalcFovy ==================== */ static float CalcFovy (float fov_x, float width, float height) { float a, x; if (fov_x < 1 || fov_x > 179) Sys_Error ("Bad fov: %f", fov_x); x = width / tan(fov_x / 360 * M_PI); a = atan(height / x); a = a * 360 / M_PI; return a; } /* ================= SCR_CalcRefdef Must be called whenever vid changes Internal use only ================= */ static void SCR_CalcRefdef (void) { vrect_t vrect; scr_fullupdate = 0; // force a background redraw // bound viewsize if (scr_viewsize.integer < 30) Cvar_SetQuick (&scr_viewsize, "30"); else if (scr_viewsize.integer > 130) Cvar_SetQuick (&scr_viewsize, "130"); // bound field of view if (scr_fov.integer < 10) Cvar_SetQuick (&scr_fov, "10"); else if (scr_fov.integer > 110) Cvar_SetQuick (&scr_fov, "110"); vid.recalc_refdef = 0; // force the status bar to redraw SB_ViewSizeChanged (); Sbar_Changed(); // intermission is always full screen if (scr_viewsize.integer >= 110 || cl.intermission) sb_lines = 0; // no status bar else sb_lines = 46; // these calculations mirror those in R_Init() for r_refdef, but take no // account of water warping vrect.x = 0; vrect.y = 0; vrect.width = vid.width; vrect.height = vid.height; R_SetVrect (&vrect, &scr_vrect, sb_lines); r_refdef.vrect = scr_vrect; r_refdef.fov_x = AdaptFovx (scr_fov.value, r_refdef.vrect.width, r_refdef.vrect.height); r_refdef.fov_y = CalcFovy (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height); // guard against going from one mode to another that's less than half the // vertical resolution if (scr_con_current > vid.height) scr_con_current = vid.height; // notify the refresh of the change R_ViewChanged (vid.aspect); } //============================================================================= /* ================= SCR_SizeUp_f Keybinding command ================= */ static void SCR_SizeUp_f (void) { Cvar_SetValueQuick (&scr_viewsize, scr_viewsize.integer + 10); } /* ================= SCR_SizeDown_f Keybinding command ================= */ static void SCR_SizeDown_f (void) { Cvar_SetValueQuick (&scr_viewsize, scr_viewsize.integer - 10); } static void SCR_Callback_refdef (cvar_t *var) { vid.recalc_refdef = 1; } //============================================================================= /* ================== SCR_Init ================== */ void SCR_Init (void) { scr_ram = Draw_PicFromWad ("ram"); scr_net = Draw_PicFromWad ("net"); scr_turtle = Draw_PicFromWad ("turtle"); if (draw_reinit) return; Cvar_SetCallback (&scr_fov, SCR_Callback_refdef); Cvar_SetCallback (&scr_fov_adapt, SCR_Callback_refdef); Cvar_SetCallback (&scr_viewsize, SCR_Callback_refdef); Cvar_RegisterVariable (&scr_fov); Cvar_RegisterVariable (&scr_fov_adapt); Cvar_RegisterVariable (&scr_viewsize); Cvar_RegisterVariable (&scr_contrans); Cvar_RegisterVariable (&scr_conspeed); Cvar_RegisterVariable (&scr_showram); Cvar_RegisterVariable (&scr_showturtle); Cvar_RegisterVariable (&scr_showpause); Cvar_RegisterVariable (&scr_showfps); Cvar_RegisterVariable (&scr_centertime); Cmd_AddCommand ("screenshot",SCR_ScreenShot_f); Cmd_AddCommand ("sizeup",SCR_SizeUp_f); Cmd_AddCommand ("sizedown",SCR_SizeDown_f); scr_initialized = true; con_forcedup = true; // we're just initialized and not connected yet } //============================================================================= /* ============== SCR_DrawRam ============== */ static void SCR_DrawRam (void) { if (!scr_showram.integer) return; if (!r_cache_thrash) return; Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram); } /* ============== SCR_DrawTurtle ============== */ static void SCR_DrawTurtle (void) { static int count; if (!scr_showturtle.integer) return; if (host_frametime < HX_FRAME_TIME) { count = 0; return; } count++; if (count < 3) return; Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle); } /* ============== SCR_DrawNet ============== */ static void SCR_DrawNet (void) { #if !defined(H2W) if (realtime - cl.last_received_message < 0.3) return; #else if (cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged < UPDATE_BACKUP-1) return; #endif if (cls.demoplayback) return; Draw_Pic (scr_vrect.x+64, scr_vrect.y, scr_net); } static void SCR_DrawFPS (void) { static double oldtime = 0; static double lastfps = 0; static int oldframecount = 0; double elapsed_time; int frames; char st[16]; int x, y; if (!scr_showfps.integer) return; elapsed_time = realtime - oldtime; frames = r_framecount - oldframecount; if (elapsed_time < 0 || frames < 0) { oldtime = realtime; oldframecount = r_framecount; return; } // update value every 3/4 second if (elapsed_time > 0.75) { lastfps = frames / elapsed_time; oldtime = realtime; oldframecount = r_framecount; } sprintf(st, "%4.0f FPS", lastfps); x = vid.width - strlen(st) * 8 - 8; y = vid.height - sb_lines - 8; // Draw_TileClear(x, y, strlen(st) * 8, 8); Draw_String(x, y, st); } /* ============== DrawPause ============== */ static void SCR_DrawPause (void) { static qboolean newdraw = false; static float LogoPercent, LogoTargetPercent; qpic_t *pic; int finaly; float delta; if (!scr_showpause.integer) // turn off for screenshots return; if (!cl.paused) { newdraw = false; return; } if (!newdraw) { newdraw = true; LogoTargetPercent = 1; LogoPercent = 0; } pic = Draw_CachePic ("gfx/menu/paused.lmp"); // Draw_Pic ( (vid.width - pic->width)/2, (vid.height - 48 - pic->height)/2, pic); if (LogoPercent < LogoTargetPercent) { delta = ((LogoTargetPercent - LogoPercent) / .5) * host_frametime; if (delta < 0.004) delta = 0.004; LogoPercent += delta; if (LogoPercent > LogoTargetPercent) LogoPercent = LogoTargetPercent; } finaly = ((float)pic->height * LogoPercent) - pic->height; Draw_TransPicCropped ( (vid.width - pic->width)/2, finaly, pic); } #if !defined(H2W) /* ============== SCR_DrawLoading ============== */ #if !defined(DRAW_PROGRESSBARS) void SCR_DrawLoading (void) { int offset; qpic_t *pic; if (!scr_drawloading && loading_stage == 0) return; pic = Draw_CacheLoadingPic (); offset = (vid.width - pic->width) / 2; Draw_TransPic (offset, 0, pic); } #else void SCR_DrawLoading (void) { int size, count, offset; qpic_t *pic; if (!scr_drawloading && loading_stage == 0) return; pic = Draw_CachePic ("gfx/menu/loading.lmp"); offset = (vid.width - pic->width) / 2; Draw_TransPic (offset, 0, pic); if (loading_stage == 0) return; size = (total_loading_size) ? (current_loading_size * 106 / total_loading_size) : 0; offset += 42; count = (loading_stage == 1) ? size : 106; if (count) { Draw_Fill (offset, 87+0, count, 1, 136); Draw_Fill (offset, 87+1, count, 4, 138); Draw_Fill (offset, 87+5, count, 1, 136); } count = (loading_stage == 2) ? size : 0; if (count) { Draw_Fill (offset, 97+0, count, 1, 168); Draw_Fill (offset, 97+1, count, 4, 170); Draw_Fill (offset, 97+5, count, 1, 168); } } #endif /* !DRAW_PROGRESSBARS */ /* =============== SCR_BeginLoadingPlaque ================ */ void SCR_BeginLoadingPlaque (void) { S_StopAllSounds (true); if (cls.state != ca_connected) return; if (cls.signon != SIGNONS) return; // redraw with no console and the loading plaque Con_ClearNotify (); scr_centertime_off = 0; scr_con_current = 0; scr_drawloading = true; scr_fullupdate = 0; Sbar_Changed(); SCR_UpdateScreen (); scr_drawloading = false; scr_disabled_for_loading = true; scr_disabled_time = realtime; } /* =============== SCR_EndLoadingPlaque ================ */ void SCR_EndLoadingPlaque (void) { scr_disabled_for_loading = false; scr_fullupdate = 0; scr_topupdate = 0; Con_ClearNotify (); } #endif /* H2W */ //============================================================================= /* ================== SCR_SetUpToDrawConsole ================== */ static void SCR_SetUpToDrawConsole (void) { Con_CheckResize (); #if !defined(H2W) if (scr_drawloading) return; // never a console with loading plaque con_forcedup = !cl.worldmodel || cls.signon != SIGNONS; #else con_forcedup = cls.state != ca_active; #endif /* H2W */ // decide on the height of the console if (con_forcedup) { scr_conlines = vid.height; // full screen scr_con_current = scr_conlines; } else if (Key_GetDest() == key_console) scr_conlines = vid.height / 2; // half screen else scr_conlines = 0; // none visible if (scr_conlines < scr_con_current) { scr_con_current -= scr_conspeed.value * host_frametime; if (scr_conlines > scr_con_current) scr_con_current = scr_conlines; } else if (scr_conlines > scr_con_current) { scr_con_current += scr_conspeed.value * host_frametime; if (scr_conlines < scr_con_current) scr_con_current = scr_conlines; } if (clearconsole++ < vid.numpages) { scr_copytop = 1; Draw_TileClear (0,(int)scr_con_current,vid.width, vid.height - (int)scr_con_current); Sbar_Changed(); } else if (clearnotify++ < vid.numpages) { scr_copytop = 1; Draw_TileClear (0,0,vid.width, con_notifylines); } else con_notifylines = 0; } /* ================== SCR_DrawConsole ================== */ static void SCR_DrawConsole (void) { if (scr_con_current) { scr_copyeverything = 1; Con_DrawConsole (scr_con_current); clearconsole = 0; } else { keydest_t dest = Key_GetDest(); if (dest == key_game || dest == key_message) Con_DrawNotify (); // only draw notify in game } } /* ============================================================================== SCREEN SHOTS ============================================================================== */ #if !defined(H2W) /* FIXME!!! */ typedef struct { char manufacturer; char version; char encoding; char bits_per_pixel; unsigned short xmin,ymin,xmax,ymax; unsigned short hres,vres; unsigned char palette[48]; char reserved; char color_planes; unsigned short bytes_per_line; unsigned short palette_type; char filler[58]; unsigned char data; // unbounded } pcx_t; #endif /* H2W */ /* ============== WritePCXfile ============== */ static int WritePCXfile (const char *filename, byte *data, int width, int height, int rowbytes, byte *palette) { int i, j; size_t length; pcx_t *pcx; byte *pack; pcx = (pcx_t *) Hunk_TempAlloc (width*height*2+1000); if (pcx == NULL) { Con_Printf("%s: not enough memory\n", __thisfunc__); return -1; } pcx->manufacturer = 0x0a; // PCX id pcx->version = 5; // 256 color pcx->encoding = 1; // uncompressed pcx->bits_per_pixel = 8; // 256 color pcx->xmin = 0; pcx->ymin = 0; pcx->xmax = LittleShort((short)(width-1)); pcx->ymax = LittleShort((short)(height-1)); pcx->hres = LittleShort((short)width); pcx->vres = LittleShort((short)height); memset (pcx->palette, 0, sizeof(pcx->palette)); pcx->color_planes = 1; // chunky image pcx->bytes_per_line = LittleShort((short)width); pcx->palette_type = LittleShort(2); // not a grey scale memset (pcx->filler, 0, sizeof(pcx->filler)); // pack the image pack = &pcx->data; for (i = 0; i < height; i++) { for (j = 0; j < width; j++) { if ( (*data & 0xc0) != 0xc0) *pack++ = *data++; else { *pack++ = 0xc1; *pack++ = *data++; } } data += rowbytes - width; } // write the palette *pack++ = 0x0c; // palette ID byte for (i = 0; i < 768; i++) *pack++ = *palette++; // write output file length = pack - (byte *)pcx; return FS_WriteFile(filename, pcx, length); } /* ================== SCR_ScreenShot_f ================== */ #if !defined(H2W) static const char scr_shotbase[] = "shots/hexen00.pcx"; #define SHOTNUM_POS 11 #else static const char scr_shotbase[] = "shots/hw00.pcx"; #define SHOTNUM_POS 8 #endif static void SCR_ScreenShot_f (void) { char pcxname[80]; char checkname[MAX_OSPATH]; int i; FS_MakePath_BUF (FS_USERDIR, NULL, checkname, sizeof(checkname), "shots"); Sys_mkdir (checkname, false); // find a slot to save it to q_strlcpy (pcxname, scr_shotbase, sizeof(pcxname)); for (i = 0; i <= 99; i++) { pcxname[SHOTNUM_POS+0] = i/10 + '0'; pcxname[SHOTNUM_POS+1] = i%10 + '0'; FS_MakePath_BUF (FS_USERDIR, NULL, checkname, sizeof(checkname), pcxname); if (Sys_FileType(checkname) == FS_ENT_NONE) break; // file doesn't exist } if (i == 100) { Con_Printf ("%s: Couldn't create a PCX file\n", __thisfunc__); return; } // // save the pcx file // D_EnableBackBufferAccess (); // enable direct drawing of console // to back buffer i = WritePCXfile (pcxname, vid.buffer, vid.width, vid.height, vid.rowbytes, host_basepal); D_DisableBackBufferAccess (); // for adapters that can't stay mapped // in for linear writes all the time if (i == 0) Con_Printf ("Wrote %s\n", pcxname); } //============================================================================= static const char *scr_notifystring; static qboolean scr_drawdialog; static void SCR_DrawNotifyString (void) { Plaque_Draw(scr_notifystring, true); } /* ================== SCR_ModalMessage Displays a text string in the center of the screen and waits for a Y or N keypress. ================== */ int SCR_ModalMessage (const char *text) { #if !defined(H2W) if (cls.state == ca_dedicated) return true; #endif /* H2W */ scr_notifystring = text; // draw a fresh screen scr_fullupdate = 0; scr_drawdialog = true; SCR_UpdateScreen (); scr_drawdialog = false; S_ClearBuffer (); // so dma doesn't loop current sound do { key_count = -1; // wait for a key down and up Sys_SendKeyEvents (); } while (key_lastpress != 'y' && key_lastpress != 'n' && key_lastpress != K_ESCAPE); scr_fullupdate = 0; SCR_UpdateScreen (); return key_lastpress == 'y'; } //============================================================================= /* =============== SCR_BringDownConsole Brings the console down and fades the palettes back to normal ================ */ #if 0 /* all uses are commented out */ void SCR_BringDownConsole (void) { int i; scr_centertime_off = 0; for (i = 0; i < 20 && scr_conlines != scr_con_current; i++) SCR_UpdateScreen (); cl.cshifts[0].percent = 0; // no area contents palette on next frame VID_SetPalette (host_basepal); } #endif //============================================================================= void SCR_SetPlaqueMessage (const char *msg) { plaquemessage = msg; } static void Plaque_Draw (const char *message, qboolean AlwaysDraw) { int i, cnt; int bx, by; char temp[80]; if (scr_con_current == vid.height && !AlwaysDraw) return; // console is full screen if (!*message) return; scr_needfull = true; FindTextBreaks(message, PLAQUE_WIDTH); by = (25-lines) * 8 / 2 + ((vid.height - 200)>>1); M_DrawTextBox (32, by - 16, PLAQUE_WIDTH + 4, lines + 2); for (i = 0; i < lines; i++, by += 8) { cnt = EndC[i] - StartC[i]; strncpy (temp, &message[StartC[i]], cnt); temp[cnt] = 0; bx = (40-strlen(temp)) * 8 / 2; M_Print (bx, by, temp); } } #if !defined(H2W) static void Info_Plaque_Draw (const char *message) { int i, cnt; int bx, by; char temp[80]; if (scr_con_current == vid.height) return; // console is full screen if (!info_string_count || !*message) return; scr_needfull = true; FindTextBreaks(message, PLAQUE_WIDTH+4); if (lines == MAXLINES) { Con_DPrintf("%s: line overflow error\n", __thisfunc__); lines = MAXLINES-1; } by = (25-lines) * 8 / 2 + ((vid.height - 200)>>1); M_DrawTextBox (15, by - 16, PLAQUE_WIDTH + 4 + 4, lines + 2); for (i = 0; i < lines; i++, by += 8) { cnt = EndC[i] - StartC[i]; strncpy (temp, &message[StartC[i]], cnt); temp[cnt] = 0; bx = (40-strlen(temp)) * 8 / 2; M_Print (bx, by, temp); } } static void Bottom_Plaque_Draw (const char *message) { int i, cnt; int bx, by; char temp[80]; if (!*message) return; scr_needfull = true; FindTextBreaks(message, PLAQUE_WIDTH); by = (((vid.height) / 8) - lines - 2) * 8; M_DrawTextBox (32, by - 16, PLAQUE_WIDTH + 4, lines + 2); for (i = 0; i < lines; i++, by += 8) { cnt = EndC[i] - StartC[i]; strncpy (temp, &message[StartC[i]], cnt); temp[cnt] = 0; bx = (40-strlen(temp)) * 8 / 2; M_Print (bx, by, temp); } } //============================================================================= static void I_Print (int cx, int cy, const char *str, int flags) { int num, x, y; const char *s; x = cx + ((vid.width - 320)>>1); y = cy; if (!(flags & (INTERMISSION_PRINT_TOP|INTERMISSION_PRINT_TOPMOST))) y += ((vid.height - 200)>>1); s = str; while (*s) { num = (unsigned char)(*s); if (!(flags & INTERMISSION_PRINT_WHITE)) num += 256; Draw_Character (x, y, num); s++; x += 8; } } #if FULLSCREEN_INTERMISSIONS # define Load_IntermissionPic_FN(X,Y,Z) Draw_CachePicResize((X),(Y),(Z)) # define Draw_IntermissionPic_FN(X,Y,Z) Draw_Pic(0,0,(Z)) #else # define Load_IntermissionPic_FN(X,Y,Z) Draw_CachePic((X)) # define Draw_IntermissionPic_FN(X,Y,Z) Draw_Pic((X),(Y),(Z)) #endif /* =============== SB_IntermissionOverlay =============== */ static void SB_IntermissionOverlay (void) { qpic_t *pic; int elapsed, size, bx, by, i; char temp[80]; const char *message; scr_copyeverything = 1; scr_fullupdate = 0; #if !defined(H2W) if (cl.gametype == GAME_DEATHMATCH) #else if (!cl_siege) #endif { Sbar_DeathmatchOverlay (); return; } if (cl.intermission_pic == NULL) Host_Error ("%s: NULL intermission picture", __thisfunc__); else { pic = Load_IntermissionPic_FN (cl.intermission_pic, vid.width, vid.height); Draw_IntermissionPic_FN (((vid.width - 320)>>1), ((vid.height - 200)>>1), pic); } if (cl.message_index >= 0 && cl.message_index < host_string_count) message = Host_GetString (cl.message_index); else if (cl.intermission_flags & INTERMISSION_NO_MESSAGE) message = ""; else { message = ""; /* silence compilers */ Host_Error ("%s: Intermission string #%d not available (host_string_count: %d)", __thisfunc__, cl.message_index, host_string_count); } if (cl.intermission_flags & INTERMISSION_NOT_CONNECTED) elapsed = (realtime - cl.completed_time) * 20; else elapsed = (cl.time - cl.completed_time) * 20; if (cl.intermission_flags & INTERMISSION_PRINT_DELAY) { elapsed -= 50; /* delay about 2.5 seconds */ if (elapsed < 0) elapsed = 0; } FindTextBreaks(message, 38); if (cl.intermission_flags & INTERMISSION_PRINT_TOPMOST) by = 16; else by = (25-lines) * 8 / 2; for (i = 0; i < lines; i++, by += 8) { size = EndC[i] - StartC[i]; strncpy (temp, &message[StartC[i]], size); if (size > elapsed) size = elapsed; temp[size] = 0; bx = (40-strlen(temp)) * 8 / 2; I_Print (bx, by, temp, cl.intermission_flags); elapsed -= size; if (elapsed <= 0) break; } if (i == lines && cl.lasting_time && elapsed >= 20*cl.lasting_time) CL_SetupIntermission (cl.intermission_next); } #endif /* H2W */ //============================================================================= /* ================== SCR_UpdateScreen This is called every frame, and can also be called explicitly to flush text to the screen. WARNING: be very careful calling this from elsewhere, because the refresh needs almost the entire 256k of stack space! ================== */ void SCR_UpdateScreen (void) { vrect_t vrect; if (scr_skipupdate || block_drawing) return; #ifdef PLATFORM_WINDOWS // don't suck up any cpu if minimized if (Minimized) return; #endif scr_copytop = 0; scr_copyeverything = 0; #if defined(H2W) if (scr_disabled_for_loading) return; #else if (scr_disabled_for_loading) { if (realtime - scr_disabled_time > 25) { /* this can happen with clients connected to servers * older than uHexen2-1.5.6 who don't issue an error * upon changelevel failures. Or, it could happen if * loading is taking a really long time. */ scr_disabled_for_loading = false; total_loading_size = 0; loading_stage = 0; Con_Printf ("load timeout.\n"); } else { return; } } if (cls.state == ca_dedicated) return; // stdout only #endif /* H2W */ if (!scr_initialized || !con_initialized) return; // not initialized yet // // check for vid changes // if (vid.recalc_refdef) { // something changed, so reorder the screen SCR_CalcRefdef (); } // // do 3D refresh drawing, and then update the screen // D_EnableBackBufferAccess (); // of all overlay stuff if drawing directly if (scr_needfull && (!plaquemessage || !*plaquemessage || !SCR_CheckDrawCenterString2())) scr_fullupdate = 0; if (scr_fullupdate++ < vid.numpages) { // clear the entire screen scr_copyeverything = 1; Draw_TileClear (0,0,vid.width,vid.height); Sbar_Changed(); } else if (scr_topupdate++ < vid.numpages) { scr_copyeverything = 1; Draw_TileClear (0,0,vid.width,100); Sbar_Changed(); } // pconupdate = NULL; SCR_SetUpToDrawConsole (); // SCR_EraseCenterString (); D_DisableBackBufferAccess (); // for adapters that can't stay mapped // in for linear writes all the time #if FULLSCREEN_INTERMISSIONS // no need to draw view in fullscreen intermission screens if (!cl.intermission) #endif { VID_LockBuffer (); V_RenderView (); VID_UnlockBuffer (); } D_EnableBackBufferAccess (); // of all overlay stuff if drawing directly if (scr_drawdialog) { Sbar_Draw (); Draw_FadeScreen (); SCR_DrawNotifyString (); scr_copyeverything = true; } else if (cl.intermission) { #if !defined(H2W) SB_IntermissionOverlay(); if (!(cl.intermission_flags & INTERMISSION_NO_MENUS)) { SCR_DrawConsole(); M_Draw(); } if (scr_drawloading) SCR_DrawLoading(); #endif /* H2W */ } #if !defined(H2W) else if (scr_drawloading) { Draw_FadeScreen (); SCR_DrawLoading (); } #endif /* H2W */ else { if (crosshair.integer && !cls.demoplayback) Draw_Crosshair(); SCR_DrawRam(); SCR_DrawNet(); SCR_DrawTurtle(); SCR_DrawPause(); SCR_CheckDrawCenterString(); Sbar_Draw(); SCR_DrawFPS(); Plaque_Draw(plaquemessage, false); SCR_DrawConsole(); M_Draw(); #if !defined(H2W) if (info_up) { UpdateInfoMessage(); Info_Plaque_Draw(infomessage); } #endif /* H2W */ } D_DisableBackBufferAccess (); // for adapters that can't stay mapped // in for linear writes all the time //if (pconupdate) // D_UpdateRects (pconupdate); V_UpdatePalette (); // // update one of three areas // if (scr_copyeverything) { vrect.x = 0; vrect.y = 0; vrect.width = vid.width; vrect.height = vid.height; vrect.pnext = NULL; VID_Update (&vrect); } else if (scr_copytop) { vrect.x = 0; vrect.y = 0; vrect.width = vid.width; vrect.height = vid.height - sb_lines; vrect.pnext = NULL; VID_Update (&vrect); } else { vrect.x = scr_vrect.x; vrect.y = scr_vrect.y; vrect.width = scr_vrect.width; vrect.height = scr_vrect.height; vrect.pnext = NULL; VID_Update (&vrect); } } /* ================== SCR_UpdateWholeScreen ================== */ void SCR_UpdateWholeScreen (void) { scr_fullupdate = 0; SCR_UpdateScreen (); } engine/h2shared/screen.h000066400000000000000000000043201444734033100154550ustar00rootroot00000000000000/* screen.h * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_SCREEN_H #define __HX2_SCREEN_H #define PLAQUE_WIDTH 26 /* width for the info plaques */ void SCR_Init (void); void SCR_UpdateScreen (void); void SCR_UpdateWholeScreen (void); void SCR_CenterPrint (const char *str); //void SCR_BringDownConsole (void); void SCR_BeginLoadingPlaque (void); void SCR_EndLoadingPlaque (void); void SCR_DrawLoading (void); // for the Loading plaque void SCR_SetPlaqueMessage (const char *msg); // set pointer to current plaque message int SCR_ModalMessage (const char *text); #ifndef H2W extern int total_loading_size; // global vars for extern int current_loading_size; // the Loading screen extern int entity_file_size, loading_stage; #endif extern float scr_con_current; extern float scr_conlines; // lines of console to display extern int scr_fullupdate; // set to 0 to force full redraw extern int scr_topupdate; // set to 0 to force top redraw extern int sb_lines; extern int clearnotify; // set to 0 whenever notify text is drawn extern qboolean scr_disabled_for_loading; extern qboolean scr_skipupdate; extern qboolean block_drawing; extern cvar_t scr_fov; extern cvar_t scr_fov_adapt; extern cvar_t scr_viewsize; extern cvar_t scr_contrans; extern int trans_level; // only the refresh window will be updated unless these variables are flagged extern int scr_copytop; extern int scr_copyeverything; #endif /* __HX2_SCREEN_H */ engine/h2shared/sdl_inc.h000066400000000000000000000056021444734033100156150ustar00rootroot00000000000000/* * sdl_inc.h -- common SDL header. * * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_SDL_INC #define __HX2_SDL_INC #if !defined(SDLQUAKE) #error "SDLQUAKE must be defined in order to use sdl_inc.h" #endif /* SDLQUAKE */ #if defined(SDL_FRAMEWORK) || defined(NO_SDL_CONFIG) #include #else #include "SDL.h" #endif /* ================================================================= Minimum required SDL versions: =================================================================== */ #if SDL_VERSION_ATLEAST(1,3,0) #define SDL_MIN_X 2 #define SDL_MIN_Y 0 #define SDL_MIN_Z 0 #else /* SDL-1.2.x */ #define SDL_MIN_X 1 #define SDL_MIN_Y 2 #if defined(__APPLE__) && defined(__MACH__) /* Mac OS X */ # if defined (__ppc__) || defined(__POWERPC__) # define SDL_MIN_Z 9 # else # define SDL_MIN_Z 14 /* need 1.2.14 for x86/x86_64 support on OS X */ # endif #else # define SDL_MIN_Z 0 #endif #endif /* SDL-1.2.x */ #define SDL_REQUIREDVERSION (SDL_VERSIONNUM(SDL_MIN_X,SDL_MIN_Y,SDL_MIN_Z)) #if !(SDL_VERSION_ATLEAST(SDL_MIN_X,SDL_MIN_Y,SDL_MIN_Z)) #error SDL version found is too old #endif /* the defines below are actually part of SDL_GLattr enums in SDL versions supporting that relevant feature. */ /* gl stereo attribute was added to SDL beginning from v1.2.5 */ #define SDL_VER_WITH_STEREO (SDL_VERSIONNUM(1,2,5)) #if SDL_COMPILEDVERSION < SDL_VER_WITH_STEREO #define SDL_GL_STEREO (SDL_GL_ACCUM_ALPHA_SIZE+1) #endif /* SDL_VER_WITH_STEREO */ /* multisampling was added to SDL beginning from v1.2.6 */ #define SDL_VER_WITH_MULTISAMPLING (SDL_VERSIONNUM(1,2,6)) #if SDL_COMPILEDVERSION < SDL_VER_WITH_MULTISAMPLING #define SDL_GL_MULTISAMPLEBUFFERS (SDL_GL_ACCUM_ALPHA_SIZE+2) #define SDL_GL_MULTISAMPLESAMPLES (SDL_GL_ACCUM_ALPHA_SIZE+3) #endif /* SDL_VER_WITH_MULTISAMPLING */ /* swapinterval was added to SDL beginning from v1.2.10 */ #define SDL_VER_WITH_SWAPINTERVAL (SDL_VERSIONNUM(1,2,10)) #if SDL_COMPILEDVERSION < SDL_VER_WITH_SWAPINTERVAL #define SDL_GL_ACCELERATED_VISUAL (SDL_GL_MULTISAMPLESAMPLES+1) #define SDL_GL_SWAP_CONTROL (SDL_GL_MULTISAMPLESAMPLES+2) #endif /* SDL_VER_WITH_SWAPINTERVAL */ #endif /* __HX2_SDL_INC */ engine/h2shared/sincos.h000066400000000000000000001254751444734033100155130ustar00rootroot00000000000000/* sincos.h -- interleaved sin/cos table. * Copyright (C) 2022 Szilard Biro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 0.000000, 1.000000, 0.003068, 0.999995, 0.006136, 0.999981, 0.009204, 0.999958, 0.012272, 0.999925, 0.015339, 0.999882, 0.018407, 0.999831, 0.021474, 0.999769, 0.024541, 0.999699, 0.027608, 0.999619, 0.030675, 0.999529, 0.033741, 0.999431, 0.036807, 0.999322, 0.039873, 0.999205, 0.042938, 0.999078, 0.046003, 0.998941, 0.049068, 0.998795, 0.052132, 0.998640, 0.055195, 0.998476, 0.058258, 0.998302, 0.061321, 0.998118, 0.064383, 0.997925, 0.067444, 0.997723, 0.070505, 0.997511, 0.073565, 0.997290, 0.076624, 0.997060, 0.079682, 0.996820, 0.082740, 0.996571, 0.085797, 0.996313, 0.088854, 0.996045, 0.091909, 0.995767, 0.094963, 0.995481, 0.098017, 0.995185, 0.101070, 0.994879, 0.104122, 0.994565, 0.107172, 0.994240, 0.110222, 0.993907, 0.113271, 0.993564, 0.116319, 0.993212, 0.119365, 0.992850, 0.122411, 0.992480, 0.125455, 0.992099, 0.128498, 0.991710, 0.131540, 0.991311, 0.134581, 0.990903, 0.137620, 0.990485, 0.140658, 0.990058, 0.143695, 0.989622, 0.146730, 0.989177, 0.149765, 0.988722, 0.152797, 0.988258, 0.155828, 0.987784, 0.158858, 0.987301, 0.161886, 0.986809, 0.164913, 0.986308, 0.167938, 0.985798, 0.170962, 0.985278, 0.173984, 0.984749, 0.177004, 0.984210, 0.180023, 0.983662, 0.183040, 0.983105, 0.186055, 0.982539, 0.189069, 0.981964, 0.192080, 0.981379, 0.195090, 0.980785, 0.198098, 0.980182, 0.201105, 0.979570, 0.204109, 0.978948, 0.207111, 0.978317, 0.210112, 0.977677, 0.213110, 0.977028, 0.216107, 0.976370, 0.219101, 0.975702, 0.222094, 0.975025, 0.225084, 0.974339, 0.228072, 0.973644, 0.231058, 0.972940, 0.234042, 0.972226, 0.237024, 0.971504, 0.240003, 0.970772, 0.242980, 0.970031, 0.245955, 0.969281, 0.248928, 0.968522, 0.251898, 0.967754, 0.254866, 0.966976, 0.257831, 0.966190, 0.260794, 0.965394, 0.263755, 0.964590, 0.266713, 0.963776, 0.269668, 0.962953, 0.272621, 0.962121, 0.275572, 0.961280, 0.278520, 0.960431, 0.281465, 0.959572, 0.284408, 0.958703, 0.287347, 0.957826, 0.290285, 0.956940, 0.293219, 0.956045, 0.296151, 0.955141, 0.299080, 0.954228, 0.302006, 0.953306, 0.304929, 0.952375, 0.307850, 0.951435, 0.310767, 0.950486, 0.313682, 0.949528, 0.316593, 0.948561, 0.319502, 0.947586, 0.322408, 0.946601, 0.325310, 0.945607, 0.328210, 0.944605, 0.331106, 0.943593, 0.334000, 0.942573, 0.336890, 0.941544, 0.339777, 0.940506, 0.342661, 0.939459, 0.345541, 0.938404, 0.348419, 0.937339, 0.351293, 0.936266, 0.354164, 0.935184, 0.357031, 0.934093, 0.359895, 0.932993, 0.362756, 0.931884, 0.365613, 0.930767, 0.368467, 0.929641, 0.371317, 0.928506, 0.374164, 0.927363, 0.377007, 0.926210, 0.379847, 0.925049, 0.382683, 0.923880, 0.385516, 0.922701, 0.388345, 0.921514, 0.391170, 0.920318, 0.393992, 0.919114, 0.396810, 0.917901, 0.399624, 0.916679, 0.402435, 0.915449, 0.405241, 0.914210, 0.408044, 0.912962, 0.410843, 0.911706, 0.413638, 0.910441, 0.416430, 0.909168, 0.419217, 0.907886, 0.422000, 0.906596, 0.424780, 0.905297, 0.427555, 0.903989, 0.430326, 0.902673, 0.433094, 0.901349, 0.435857, 0.900016, 0.438616, 0.898674, 0.441371, 0.897325, 0.444122, 0.895966, 0.446869, 0.894599, 0.449611, 0.893224, 0.452350, 0.891841, 0.455084, 0.890449, 0.457813, 0.889048, 0.460539, 0.887640, 0.463260, 0.886223, 0.465976, 0.884797, 0.468689, 0.883363, 0.471397, 0.881921, 0.474100, 0.880471, 0.476799, 0.879012, 0.479494, 0.877545, 0.482184, 0.876070, 0.484869, 0.874587, 0.487550, 0.873095, 0.490226, 0.871595, 0.492898, 0.870087, 0.495565, 0.868571, 0.498228, 0.867046, 0.500885, 0.865514, 0.503538, 0.863973, 0.506187, 0.862424, 0.508830, 0.860867, 0.511469, 0.859302, 0.514103, 0.857729, 0.516732, 0.856147, 0.519356, 0.854558, 0.521975, 0.852961, 0.524590, 0.851355, 0.527199, 0.849742, 0.529804, 0.848120, 0.532403, 0.846491, 0.534998, 0.844854, 0.537587, 0.843208, 0.540171, 0.841555, 0.542751, 0.839894, 0.545325, 0.838225, 0.547894, 0.836548, 0.550458, 0.834863, 0.553017, 0.833170, 0.555570, 0.831470, 0.558119, 0.829761, 0.560662, 0.828045, 0.563199, 0.826321, 0.565732, 0.824589, 0.568259, 0.822850, 0.570781, 0.821103, 0.573297, 0.819348, 0.575808, 0.817585, 0.578314, 0.815814, 0.580814, 0.814036, 0.583309, 0.812251, 0.585798, 0.810457, 0.588282, 0.808656, 0.590760, 0.806848, 0.593232, 0.805031, 0.595699, 0.803208, 0.598161, 0.801376, 0.600616, 0.799537, 0.603067, 0.797691, 0.605511, 0.795837, 0.607950, 0.793975, 0.610383, 0.792107, 0.612810, 0.790230, 0.615232, 0.788346, 0.617647, 0.786455, 0.620057, 0.784557, 0.622461, 0.782651, 0.624859, 0.780737, 0.627252, 0.778817, 0.629638, 0.776888, 0.632019, 0.774953, 0.634393, 0.773010, 0.636762, 0.771061, 0.639124, 0.769103, 0.641481, 0.767139, 0.643832, 0.765167, 0.646176, 0.763188, 0.648514, 0.761202, 0.650847, 0.759209, 0.653173, 0.757209, 0.655493, 0.755201, 0.657807, 0.753187, 0.660114, 0.751165, 0.662416, 0.749136, 0.664711, 0.747101, 0.667000, 0.745058, 0.669283, 0.743008, 0.671559, 0.740951, 0.673829, 0.738887, 0.676093, 0.736817, 0.678350, 0.734739, 0.680601, 0.732654, 0.682846, 0.730563, 0.685084, 0.728464, 0.687315, 0.726359, 0.689541, 0.724247, 0.691759, 0.722128, 0.693971, 0.720003, 0.696177, 0.717870, 0.698376, 0.715731, 0.700569, 0.713585, 0.702755, 0.711432, 0.704934, 0.709273, 0.707107, 0.707107, 0.709273, 0.704934, 0.711432, 0.702755, 0.713585, 0.700569, 0.715731, 0.698376, 0.717870, 0.696177, 0.720003, 0.693971, 0.722128, 0.691759, 0.724247, 0.689541, 0.726359, 0.687315, 0.728464, 0.685084, 0.730563, 0.682846, 0.732654, 0.680601, 0.734739, 0.678350, 0.736817, 0.676093, 0.738887, 0.673829, 0.740951, 0.671559, 0.743008, 0.669283, 0.745058, 0.667000, 0.747101, 0.664711, 0.749136, 0.662416, 0.751165, 0.660114, 0.753187, 0.657807, 0.755201, 0.655493, 0.757209, 0.653173, 0.759209, 0.650847, 0.761202, 0.648514, 0.763188, 0.646176, 0.765167, 0.643832, 0.767139, 0.641481, 0.769103, 0.639124, 0.771061, 0.636762, 0.773010, 0.634393, 0.774953, 0.632019, 0.776888, 0.629638, 0.778817, 0.627252, 0.780737, 0.624859, 0.782651, 0.622461, 0.784557, 0.620057, 0.786455, 0.617647, 0.788346, 0.615232, 0.790230, 0.612810, 0.792107, 0.610383, 0.793975, 0.607950, 0.795837, 0.605511, 0.797691, 0.603067, 0.799537, 0.600616, 0.801376, 0.598161, 0.803208, 0.595699, 0.805031, 0.593232, 0.806848, 0.590760, 0.808656, 0.588282, 0.810457, 0.585798, 0.812251, 0.583309, 0.814036, 0.580814, 0.815814, 0.578314, 0.817585, 0.575808, 0.819348, 0.573297, 0.821103, 0.570781, 0.822850, 0.568259, 0.824589, 0.565732, 0.826321, 0.563199, 0.828045, 0.560662, 0.829761, 0.558119, 0.831470, 0.555570, 0.833170, 0.553017, 0.834863, 0.550458, 0.836548, 0.547894, 0.838225, 0.545325, 0.839894, 0.542751, 0.841555, 0.540171, 0.843208, 0.537587, 0.844854, 0.534998, 0.846491, 0.532403, 0.848120, 0.529804, 0.849742, 0.527199, 0.851355, 0.524590, 0.852961, 0.521975, 0.854558, 0.519356, 0.856147, 0.516732, 0.857729, 0.514103, 0.859302, 0.511469, 0.860867, 0.508830, 0.862424, 0.506187, 0.863973, 0.503538, 0.865514, 0.500885, 0.867046, 0.498228, 0.868571, 0.495565, 0.870087, 0.492898, 0.871595, 0.490226, 0.873095, 0.487550, 0.874587, 0.484869, 0.876070, 0.482184, 0.877545, 0.479494, 0.879012, 0.476799, 0.880471, 0.474100, 0.881921, 0.471397, 0.883363, 0.468689, 0.884797, 0.465976, 0.886223, 0.463260, 0.887640, 0.460539, 0.889048, 0.457813, 0.890449, 0.455084, 0.891841, 0.452350, 0.893224, 0.449611, 0.894599, 0.446869, 0.895966, 0.444122, 0.897325, 0.441371, 0.898674, 0.438616, 0.900016, 0.435857, 0.901349, 0.433094, 0.902673, 0.430326, 0.903989, 0.427555, 0.905297, 0.424780, 0.906596, 0.422000, 0.907886, 0.419217, 0.909168, 0.416430, 0.910441, 0.413638, 0.911706, 0.410843, 0.912962, 0.408044, 0.914210, 0.405241, 0.915449, 0.402435, 0.916679, 0.399624, 0.917901, 0.396810, 0.919114, 0.393992, 0.920318, 0.391170, 0.921514, 0.388345, 0.922701, 0.385516, 0.923880, 0.382683, 0.925049, 0.379847, 0.926210, 0.377007, 0.927363, 0.374164, 0.928506, 0.371317, 0.929641, 0.368467, 0.930767, 0.365613, 0.931884, 0.362756, 0.932993, 0.359895, 0.934093, 0.357031, 0.935184, 0.354164, 0.936266, 0.351293, 0.937339, 0.348419, 0.938404, 0.345541, 0.939459, 0.342661, 0.940506, 0.339777, 0.941544, 0.336890, 0.942573, 0.334000, 0.943593, 0.331106, 0.944605, 0.328210, 0.945607, 0.325310, 0.946601, 0.322408, 0.947586, 0.319502, 0.948561, 0.316593, 0.949528, 0.313682, 0.950486, 0.310767, 0.951435, 0.307850, 0.952375, 0.304929, 0.953306, 0.302006, 0.954228, 0.299080, 0.955141, 0.296151, 0.956045, 0.293219, 0.956940, 0.290285, 0.957826, 0.287347, 0.958703, 0.284408, 0.959572, 0.281465, 0.960431, 0.278520, 0.961280, 0.275572, 0.962121, 0.272621, 0.962953, 0.269668, 0.963776, 0.266713, 0.964590, 0.263755, 0.965394, 0.260794, 0.966190, 0.257831, 0.966976, 0.254866, 0.967754, 0.251898, 0.968522, 0.248928, 0.969281, 0.245955, 0.970031, 0.242980, 0.970772, 0.240003, 0.971504, 0.237024, 0.972226, 0.234042, 0.972940, 0.231058, 0.973644, 0.228072, 0.974339, 0.225084, 0.975025, 0.222094, 0.975702, 0.219101, 0.976370, 0.216107, 0.977028, 0.213110, 0.977677, 0.210112, 0.978317, 0.207111, 0.978948, 0.204109, 0.979570, 0.201105, 0.980182, 0.198098, 0.980785, 0.195090, 0.981379, 0.192080, 0.981964, 0.189069, 0.982539, 0.186055, 0.983105, 0.183040, 0.983662, 0.180023, 0.984210, 0.177004, 0.984749, 0.173984, 0.985278, 0.170962, 0.985798, 0.167938, 0.986308, 0.164913, 0.986809, 0.161886, 0.987301, 0.158858, 0.987784, 0.155828, 0.988258, 0.152797, 0.988722, 0.149765, 0.989177, 0.146730, 0.989622, 0.143695, 0.990058, 0.140658, 0.990485, 0.137620, 0.990903, 0.134581, 0.991311, 0.131540, 0.991710, 0.128498, 0.992099, 0.125455, 0.992480, 0.122411, 0.992850, 0.119365, 0.993212, 0.116319, 0.993564, 0.113271, 0.993907, 0.110222, 0.994240, 0.107172, 0.994565, 0.104122, 0.994879, 0.101070, 0.995185, 0.098017, 0.995481, 0.094963, 0.995767, 0.091909, 0.996045, 0.088854, 0.996313, 0.085797, 0.996571, 0.082740, 0.996820, 0.079682, 0.997060, 0.076624, 0.997290, 0.073565, 0.997511, 0.070505, 0.997723, 0.067444, 0.997925, 0.064383, 0.998118, 0.061321, 0.998302, 0.058258, 0.998476, 0.055195, 0.998640, 0.052132, 0.998795, 0.049068, 0.998941, 0.046003, 0.999078, 0.042938, 0.999205, 0.039873, 0.999322, 0.036807, 0.999431, 0.033741, 0.999529, 0.030675, 0.999619, 0.027608, 0.999699, 0.024541, 0.999769, 0.021474, 0.999831, 0.018407, 0.999882, 0.015339, 0.999925, 0.012272, 0.999958, 0.009204, 0.999981, 0.006136, 0.999995, 0.003068, 1.000000, 0.000000, 0.999995, -0.003068, 0.999981, -0.006136, 0.999958, -0.009204, 0.999925, -0.012272, 0.999882, -0.015339, 0.999831, -0.018407, 0.999769, -0.021474, 0.999699, -0.024541, 0.999619, -0.027608, 0.999529, -0.030675, 0.999431, -0.033741, 0.999322, -0.036807, 0.999205, -0.039873, 0.999078, -0.042938, 0.998941, -0.046003, 0.998795, -0.049068, 0.998640, -0.052132, 0.998476, -0.055195, 0.998302, -0.058258, 0.998118, -0.061321, 0.997925, -0.064383, 0.997723, -0.067444, 0.997511, -0.070505, 0.997290, -0.073565, 0.997060, -0.076624, 0.996820, -0.079682, 0.996571, -0.082740, 0.996313, -0.085797, 0.996045, -0.088854, 0.995767, -0.091909, 0.995481, -0.094963, 0.995185, -0.098017, 0.994879, -0.101070, 0.994565, -0.104122, 0.994240, -0.107172, 0.993907, -0.110222, 0.993564, -0.113271, 0.993212, -0.116319, 0.992850, -0.119365, 0.992480, -0.122411, 0.992099, -0.125455, 0.991710, -0.128498, 0.991311, -0.131540, 0.990903, -0.134581, 0.990485, -0.137620, 0.990058, -0.140658, 0.989622, -0.143695, 0.989177, -0.146730, 0.988722, -0.149765, 0.988258, -0.152797, 0.987784, -0.155828, 0.987301, -0.158858, 0.986809, -0.161886, 0.986308, -0.164913, 0.985798, -0.167938, 0.985278, -0.170962, 0.984749, -0.173984, 0.984210, -0.177004, 0.983662, -0.180023, 0.983105, -0.183040, 0.982539, -0.186055, 0.981964, -0.189069, 0.981379, -0.192080, 0.980785, -0.195090, 0.980182, -0.198098, 0.979570, -0.201105, 0.978948, -0.204109, 0.978317, -0.207111, 0.977677, -0.210112, 0.977028, -0.213110, 0.976370, -0.216107, 0.975702, -0.219101, 0.975025, -0.222094, 0.974339, -0.225084, 0.973644, -0.228072, 0.972940, -0.231058, 0.972226, -0.234042, 0.971504, -0.237024, 0.970772, -0.240003, 0.970031, -0.242980, 0.969281, -0.245955, 0.968522, -0.248928, 0.967754, -0.251898, 0.966976, -0.254866, 0.966190, -0.257831, 0.965394, -0.260794, 0.964590, -0.263755, 0.963776, -0.266713, 0.962953, -0.269668, 0.962121, -0.272621, 0.961280, -0.275572, 0.960431, -0.278520, 0.959572, -0.281465, 0.958703, -0.284408, 0.957826, -0.287347, 0.956940, -0.290285, 0.956045, -0.293219, 0.955141, -0.296151, 0.954228, -0.299080, 0.953306, -0.302006, 0.952375, -0.304929, 0.951435, -0.307850, 0.950486, -0.310767, 0.949528, -0.313682, 0.948561, -0.316593, 0.947586, -0.319502, 0.946601, -0.322408, 0.945607, -0.325310, 0.944605, -0.328210, 0.943593, -0.331106, 0.942573, -0.334000, 0.941544, -0.336890, 0.940506, -0.339777, 0.939459, -0.342661, 0.938404, -0.345541, 0.937339, -0.348419, 0.936266, -0.351293, 0.935184, -0.354164, 0.934093, -0.357031, 0.932993, -0.359895, 0.931884, -0.362756, 0.930767, -0.365613, 0.929641, -0.368467, 0.928506, -0.371317, 0.927363, -0.374164, 0.926210, -0.377007, 0.925049, -0.379847, 0.923880, -0.382683, 0.922701, -0.385516, 0.921514, -0.388345, 0.920318, -0.391170, 0.919114, -0.393992, 0.917901, -0.396810, 0.916679, -0.399624, 0.915449, -0.402435, 0.914210, -0.405241, 0.912962, -0.408044, 0.911706, -0.410843, 0.910441, -0.413638, 0.909168, -0.416430, 0.907886, -0.419217, 0.906596, -0.422000, 0.905297, -0.424780, 0.903989, -0.427555, 0.902673, -0.430326, 0.901349, -0.433094, 0.900016, -0.435857, 0.898674, -0.438616, 0.897325, -0.441371, 0.895966, -0.444122, 0.894599, -0.446869, 0.893224, -0.449611, 0.891841, -0.452350, 0.890449, -0.455084, 0.889048, -0.457813, 0.887640, -0.460539, 0.886223, -0.463260, 0.884797, -0.465976, 0.883363, -0.468689, 0.881921, -0.471397, 0.880471, -0.474100, 0.879012, -0.476799, 0.877545, -0.479494, 0.876070, -0.482184, 0.874587, -0.484869, 0.873095, -0.487550, 0.871595, -0.490226, 0.870087, -0.492898, 0.868571, -0.495565, 0.867046, -0.498228, 0.865514, -0.500885, 0.863973, -0.503538, 0.862424, -0.506187, 0.860867, -0.508830, 0.859302, -0.511469, 0.857729, -0.514103, 0.856147, -0.516732, 0.854558, -0.519356, 0.852961, -0.521975, 0.851355, -0.524590, 0.849742, -0.527199, 0.848120, -0.529804, 0.846491, -0.532403, 0.844854, -0.534998, 0.843208, -0.537587, 0.841555, -0.540171, 0.839894, -0.542751, 0.838225, -0.545325, 0.836548, -0.547894, 0.834863, -0.550458, 0.833170, -0.553017, 0.831470, -0.555570, 0.829761, -0.558119, 0.828045, -0.560662, 0.826321, -0.563199, 0.824589, -0.565732, 0.822850, -0.568259, 0.821103, -0.570781, 0.819348, -0.573297, 0.817585, -0.575808, 0.815814, -0.578314, 0.814036, -0.580814, 0.812251, -0.583309, 0.810457, -0.585798, 0.808656, -0.588282, 0.806848, -0.590760, 0.805031, -0.593232, 0.803208, -0.595699, 0.801376, -0.598161, 0.799537, -0.600616, 0.797691, -0.603067, 0.795837, -0.605511, 0.793975, -0.607950, 0.792107, -0.610383, 0.790230, -0.612810, 0.788346, -0.615232, 0.786455, -0.617647, 0.784557, -0.620057, 0.782651, -0.622461, 0.780737, -0.624859, 0.778817, -0.627252, 0.776888, -0.629638, 0.774953, -0.632019, 0.773010, -0.634393, 0.771061, -0.636762, 0.769103, -0.639124, 0.767139, -0.641481, 0.765167, -0.643832, 0.763188, -0.646176, 0.761202, -0.648514, 0.759209, -0.650847, 0.757209, -0.653173, 0.755201, -0.655493, 0.753187, -0.657807, 0.751165, -0.660114, 0.749136, -0.662416, 0.747101, -0.664711, 0.745058, -0.667000, 0.743008, -0.669283, 0.740951, -0.671559, 0.738887, -0.673829, 0.736817, -0.676093, 0.734739, -0.678350, 0.732654, -0.680601, 0.730563, -0.682846, 0.728464, -0.685084, 0.726359, -0.687315, 0.724247, -0.689541, 0.722128, -0.691759, 0.720003, -0.693971, 0.717870, -0.696177, 0.715731, -0.698376, 0.713585, -0.700569, 0.711432, -0.702755, 0.709273, -0.704934, 0.707107, -0.707107, 0.704934, -0.709273, 0.702755, -0.711432, 0.700569, -0.713585, 0.698376, -0.715731, 0.696177, -0.717870, 0.693971, -0.720003, 0.691759, -0.722128, 0.689541, -0.724247, 0.687315, -0.726359, 0.685084, -0.728464, 0.682846, -0.730563, 0.680601, -0.732654, 0.678350, -0.734739, 0.676093, -0.736817, 0.673829, -0.738887, 0.671559, -0.740951, 0.669283, -0.743008, 0.667000, -0.745058, 0.664711, -0.747101, 0.662416, -0.749136, 0.660114, -0.751165, 0.657807, -0.753187, 0.655493, -0.755201, 0.653173, -0.757209, 0.650847, -0.759209, 0.648514, -0.761202, 0.646176, -0.763188, 0.643832, -0.765167, 0.641481, -0.767139, 0.639124, -0.769103, 0.636762, -0.771061, 0.634393, -0.773010, 0.632019, -0.774953, 0.629638, -0.776888, 0.627252, -0.778817, 0.624859, -0.780737, 0.622461, -0.782651, 0.620057, -0.784557, 0.617647, -0.786455, 0.615232, -0.788346, 0.612810, -0.790230, 0.610383, -0.792107, 0.607950, -0.793975, 0.605511, -0.795837, 0.603067, -0.797691, 0.600616, -0.799537, 0.598161, -0.801376, 0.595699, -0.803208, 0.593232, -0.805031, 0.590760, -0.806848, 0.588282, -0.808656, 0.585798, -0.810457, 0.583309, -0.812251, 0.580814, -0.814036, 0.578314, -0.815814, 0.575808, -0.817585, 0.573297, -0.819348, 0.570781, -0.821103, 0.568259, -0.822850, 0.565732, -0.824589, 0.563199, -0.826321, 0.560662, -0.828045, 0.558119, -0.829761, 0.555570, -0.831470, 0.553017, -0.833170, 0.550458, -0.834863, 0.547894, -0.836548, 0.545325, -0.838225, 0.542751, -0.839894, 0.540171, -0.841555, 0.537587, -0.843208, 0.534998, -0.844854, 0.532403, -0.846491, 0.529804, -0.848120, 0.527199, -0.849742, 0.524590, -0.851355, 0.521975, -0.852961, 0.519356, -0.854558, 0.516732, -0.856147, 0.514103, -0.857729, 0.511469, -0.859302, 0.508830, -0.860867, 0.506187, -0.862424, 0.503538, -0.863973, 0.500885, -0.865514, 0.498228, -0.867046, 0.495565, -0.868571, 0.492898, -0.870087, 0.490226, -0.871595, 0.487550, -0.873095, 0.484869, -0.874587, 0.482184, -0.876070, 0.479494, -0.877545, 0.476799, -0.879012, 0.474100, -0.880471, 0.471397, -0.881921, 0.468689, -0.883363, 0.465976, -0.884797, 0.463260, -0.886223, 0.460539, -0.887640, 0.457813, -0.889048, 0.455084, -0.890449, 0.452350, -0.891841, 0.449611, -0.893224, 0.446869, -0.894599, 0.444122, -0.895966, 0.441371, -0.897325, 0.438616, -0.898674, 0.435857, -0.900016, 0.433094, -0.901349, 0.430326, -0.902673, 0.427555, -0.903989, 0.424780, -0.905297, 0.422000, -0.906596, 0.419217, -0.907886, 0.416430, -0.909168, 0.413638, -0.910441, 0.410843, -0.911706, 0.408044, -0.912962, 0.405241, -0.914210, 0.402435, -0.915449, 0.399624, -0.916679, 0.396810, -0.917901, 0.393992, -0.919114, 0.391170, -0.920318, 0.388345, -0.921514, 0.385516, -0.922701, 0.382683, -0.923880, 0.379847, -0.925049, 0.377007, -0.926210, 0.374164, -0.927363, 0.371317, -0.928506, 0.368467, -0.929641, 0.365613, -0.930767, 0.362756, -0.931884, 0.359895, -0.932993, 0.357031, -0.934093, 0.354164, -0.935184, 0.351293, -0.936266, 0.348419, -0.937339, 0.345541, -0.938404, 0.342661, -0.939459, 0.339777, -0.940506, 0.336890, -0.941544, 0.334000, -0.942573, 0.331106, -0.943593, 0.328210, -0.944605, 0.325310, -0.945607, 0.322408, -0.946601, 0.319502, -0.947586, 0.316593, -0.948561, 0.313682, -0.949528, 0.310767, -0.950486, 0.307850, -0.951435, 0.304929, -0.952375, 0.302006, -0.953306, 0.299080, -0.954228, 0.296151, -0.955141, 0.293219, -0.956045, 0.290285, -0.956940, 0.287347, -0.957826, 0.284408, -0.958703, 0.281465, -0.959572, 0.278520, -0.960431, 0.275572, -0.961280, 0.272621, -0.962121, 0.269668, -0.962953, 0.266713, -0.963776, 0.263755, -0.964590, 0.260794, -0.965394, 0.257831, -0.966190, 0.254866, -0.966976, 0.251898, -0.967754, 0.248928, -0.968522, 0.245955, -0.969281, 0.242980, -0.970031, 0.240003, -0.970772, 0.237024, -0.971504, 0.234042, -0.972226, 0.231058, -0.972940, 0.228072, -0.973644, 0.225084, -0.974339, 0.222094, -0.975025, 0.219101, -0.975702, 0.216107, -0.976370, 0.213110, -0.977028, 0.210112, -0.977677, 0.207111, -0.978317, 0.204109, -0.978948, 0.201105, -0.979570, 0.198098, -0.980182, 0.195090, -0.980785, 0.192080, -0.981379, 0.189069, -0.981964, 0.186055, -0.982539, 0.183040, -0.983105, 0.180023, -0.983662, 0.177004, -0.984210, 0.173984, -0.984749, 0.170962, -0.985278, 0.167938, -0.985798, 0.164913, -0.986308, 0.161886, -0.986809, 0.158858, -0.987301, 0.155828, -0.987784, 0.152797, -0.988258, 0.149765, -0.988722, 0.146730, -0.989177, 0.143695, -0.989622, 0.140658, -0.990058, 0.137620, -0.990485, 0.134581, -0.990903, 0.131540, -0.991311, 0.128498, -0.991710, 0.125455, -0.992099, 0.122411, -0.992480, 0.119365, -0.992850, 0.116319, -0.993212, 0.113271, -0.993564, 0.110222, -0.993907, 0.107172, -0.994240, 0.104122, -0.994565, 0.101070, -0.994879, 0.098017, -0.995185, 0.094963, -0.995481, 0.091909, -0.995767, 0.088854, -0.996045, 0.085797, -0.996313, 0.082740, -0.996571, 0.079682, -0.996820, 0.076624, -0.997060, 0.073565, -0.997290, 0.070505, -0.997511, 0.067444, -0.997723, 0.064383, -0.997925, 0.061321, -0.998118, 0.058258, -0.998302, 0.055195, -0.998476, 0.052132, -0.998640, 0.049068, -0.998795, 0.046003, -0.998941, 0.042938, -0.999078, 0.039873, -0.999205, 0.036807, -0.999322, 0.033741, -0.999431, 0.030675, -0.999529, 0.027608, -0.999619, 0.024541, -0.999699, 0.021474, -0.999769, 0.018407, -0.999831, 0.015339, -0.999882, 0.012272, -0.999925, 0.009204, -0.999958, 0.006136, -0.999981, 0.003068, -0.999995, 0.000000, -1.000000, -0.003068, -0.999995, -0.006136, -0.999981, -0.009204, -0.999958, -0.012272, -0.999925, -0.015339, -0.999882, -0.018407, -0.999831, -0.021474, -0.999769, -0.024541, -0.999699, -0.027608, -0.999619, -0.030675, -0.999529, -0.033741, -0.999431, -0.036807, -0.999322, -0.039873, -0.999205, -0.042938, -0.999078, -0.046003, -0.998941, -0.049068, -0.998795, -0.052132, -0.998640, -0.055195, -0.998476, -0.058258, -0.998302, -0.061321, -0.998118, -0.064383, -0.997925, -0.067444, -0.997723, -0.070505, -0.997511, -0.073565, -0.997290, -0.076624, -0.997060, -0.079682, -0.996820, -0.082740, -0.996571, -0.085797, -0.996313, -0.088854, -0.996045, -0.091909, -0.995767, -0.094963, -0.995481, -0.098017, -0.995185, -0.101070, -0.994879, -0.104122, -0.994565, -0.107172, -0.994240, -0.110222, -0.993907, -0.113271, -0.993564, -0.116319, -0.993212, -0.119365, -0.992850, -0.122411, -0.992480, -0.125455, -0.992099, -0.128498, -0.991710, -0.131540, -0.991311, -0.134581, -0.990903, -0.137620, -0.990485, -0.140658, -0.990058, -0.143695, -0.989622, -0.146730, -0.989177, -0.149765, -0.988722, -0.152797, -0.988258, -0.155828, -0.987784, -0.158858, -0.987301, -0.161886, -0.986809, -0.164913, -0.986308, -0.167938, -0.985798, -0.170962, -0.985278, -0.173984, -0.984749, -0.177004, -0.984210, -0.180023, -0.983662, -0.183040, -0.983105, -0.186055, -0.982539, -0.189069, -0.981964, -0.192080, -0.981379, -0.195090, -0.980785, -0.198098, -0.980182, -0.201105, -0.979570, -0.204109, -0.978948, -0.207111, -0.978317, -0.210112, -0.977677, -0.213110, -0.977028, -0.216107, -0.976370, -0.219101, -0.975702, -0.222094, -0.975025, -0.225084, -0.974339, -0.228072, -0.973644, -0.231058, -0.972940, -0.234042, -0.972226, -0.237024, -0.971504, -0.240003, -0.970772, -0.242980, -0.970031, -0.245955, -0.969281, -0.248928, -0.968522, -0.251898, -0.967754, -0.254866, -0.966976, -0.257831, -0.966190, -0.260794, -0.965394, -0.263755, -0.964590, -0.266713, -0.963776, -0.269668, -0.962953, -0.272621, -0.962121, -0.275572, -0.961280, -0.278520, -0.960431, -0.281465, -0.959572, -0.284408, -0.958703, -0.287347, -0.957826, -0.290285, -0.956940, -0.293219, -0.956045, -0.296151, -0.955141, -0.299080, -0.954228, -0.302006, -0.953306, -0.304929, -0.952375, -0.307850, -0.951435, -0.310767, -0.950486, -0.313682, -0.949528, -0.316593, -0.948561, -0.319502, -0.947586, -0.322408, -0.946601, -0.325310, -0.945607, -0.328210, -0.944605, -0.331106, -0.943593, -0.334000, -0.942573, -0.336890, -0.941544, -0.339777, -0.940506, -0.342661, -0.939459, -0.345541, -0.938404, -0.348419, -0.937339, -0.351293, -0.936266, -0.354164, -0.935184, -0.357031, -0.934093, -0.359895, -0.932993, -0.362756, -0.931884, -0.365613, -0.930767, -0.368467, -0.929641, -0.371317, -0.928506, -0.374164, -0.927363, -0.377007, -0.926210, -0.379847, -0.925049, -0.382683, -0.923880, -0.385516, -0.922701, -0.388345, -0.921514, -0.391170, -0.920318, -0.393992, -0.919114, -0.396810, -0.917901, -0.399624, -0.916679, -0.402435, -0.915449, -0.405241, -0.914210, -0.408044, -0.912962, -0.410843, -0.911706, -0.413638, -0.910441, -0.416430, -0.909168, -0.419217, -0.907886, -0.422000, -0.906596, -0.424780, -0.905297, -0.427555, -0.903989, -0.430326, -0.902673, -0.433094, -0.901349, -0.435857, -0.900016, -0.438616, -0.898674, -0.441371, -0.897325, -0.444122, -0.895966, -0.446869, -0.894599, -0.449611, -0.893224, -0.452350, -0.891841, -0.455084, -0.890449, -0.457813, -0.889048, -0.460539, -0.887640, -0.463260, -0.886223, -0.465976, -0.884797, -0.468689, -0.883363, -0.471397, -0.881921, -0.474100, -0.880471, -0.476799, -0.879012, -0.479494, -0.877545, -0.482184, -0.876070, -0.484869, -0.874587, -0.487550, -0.873095, -0.490226, -0.871595, -0.492898, -0.870087, -0.495565, -0.868571, -0.498228, -0.867046, -0.500885, -0.865514, -0.503538, -0.863973, -0.506187, -0.862424, -0.508830, -0.860867, -0.511469, -0.859302, -0.514103, -0.857729, -0.516732, -0.856147, -0.519356, -0.854558, -0.521975, -0.852961, -0.524590, -0.851355, -0.527199, -0.849742, -0.529804, -0.848120, -0.532403, -0.846491, -0.534998, -0.844854, -0.537587, -0.843208, -0.540171, -0.841555, -0.542751, -0.839894, -0.545325, -0.838225, -0.547894, -0.836548, -0.550458, -0.834863, -0.553017, -0.833170, -0.555570, -0.831470, -0.558119, -0.829761, -0.560662, -0.828045, -0.563199, -0.826321, -0.565732, -0.824589, -0.568259, -0.822850, -0.570781, -0.821103, -0.573297, -0.819348, -0.575808, -0.817585, -0.578314, -0.815814, -0.580814, -0.814036, -0.583309, -0.812251, -0.585798, -0.810457, -0.588282, -0.808656, -0.590760, -0.806848, -0.593232, -0.805031, -0.595699, -0.803208, -0.598161, -0.801376, -0.600616, -0.799537, -0.603067, -0.797691, -0.605511, -0.795837, -0.607950, -0.793975, -0.610383, -0.792107, -0.612810, -0.790230, -0.615232, -0.788346, -0.617647, -0.786455, -0.620057, -0.784557, -0.622461, -0.782651, -0.624859, -0.780737, -0.627252, -0.778817, -0.629638, -0.776888, -0.632019, -0.774953, -0.634393, -0.773010, -0.636762, -0.771061, -0.639124, -0.769103, -0.641481, -0.767139, -0.643832, -0.765167, -0.646176, -0.763188, -0.648514, -0.761202, -0.650847, -0.759209, -0.653173, -0.757209, -0.655493, -0.755201, -0.657807, -0.753187, -0.660114, -0.751165, -0.662416, -0.749136, -0.664711, -0.747101, -0.667000, -0.745058, -0.669283, -0.743008, -0.671559, -0.740951, -0.673829, -0.738887, -0.676093, -0.736817, -0.678350, -0.734739, -0.680601, -0.732654, -0.682846, -0.730563, -0.685084, -0.728464, -0.687315, -0.726359, -0.689541, -0.724247, -0.691759, -0.722128, -0.693971, -0.720003, -0.696177, -0.717870, -0.698376, -0.715731, -0.700569, -0.713585, -0.702755, -0.711432, -0.704934, -0.709273, -0.707107, -0.707107, -0.709273, -0.704934, -0.711432, -0.702755, -0.713585, -0.700569, -0.715731, -0.698376, -0.717870, -0.696177, -0.720003, -0.693971, -0.722128, -0.691759, -0.724247, -0.689541, -0.726359, -0.687315, -0.728464, -0.685084, -0.730563, -0.682846, -0.732654, -0.680601, -0.734739, -0.678350, -0.736817, -0.676093, -0.738887, -0.673829, -0.740951, -0.671559, -0.743008, -0.669283, -0.745058, -0.667000, -0.747101, -0.664711, -0.749136, -0.662416, -0.751165, -0.660114, -0.753187, -0.657807, -0.755201, -0.655493, -0.757209, -0.653173, -0.759209, -0.650847, -0.761202, -0.648514, -0.763188, -0.646176, -0.765167, -0.643832, -0.767139, -0.641481, -0.769103, -0.639124, -0.771061, -0.636762, -0.773010, -0.634393, -0.774953, -0.632019, -0.776888, -0.629638, -0.778817, -0.627252, -0.780737, -0.624859, -0.782651, -0.622461, -0.784557, -0.620057, -0.786455, -0.617647, -0.788346, -0.615232, -0.790230, -0.612810, -0.792107, -0.610383, -0.793975, -0.607950, -0.795837, -0.605511, -0.797691, -0.603067, -0.799537, -0.600616, -0.801376, -0.598161, -0.803208, -0.595699, -0.805031, -0.593232, -0.806848, -0.590760, -0.808656, -0.588282, -0.810457, -0.585798, -0.812251, -0.583309, -0.814036, -0.580814, -0.815814, -0.578314, -0.817585, -0.575808, -0.819348, -0.573297, -0.821103, -0.570781, -0.822850, -0.568259, -0.824589, -0.565732, -0.826321, -0.563199, -0.828045, -0.560662, -0.829761, -0.558119, -0.831470, -0.555570, -0.833170, -0.553017, -0.834863, -0.550458, -0.836548, -0.547894, -0.838225, -0.545325, -0.839894, -0.542751, -0.841555, -0.540171, -0.843208, -0.537587, -0.844854, -0.534998, -0.846491, -0.532403, -0.848120, -0.529804, -0.849742, -0.527199, -0.851355, -0.524590, -0.852961, -0.521975, -0.854558, -0.519356, -0.856147, -0.516732, -0.857729, -0.514103, -0.859302, -0.511469, -0.860867, -0.508830, -0.862424, -0.506187, -0.863973, -0.503538, -0.865514, -0.500885, -0.867046, -0.498228, -0.868571, -0.495565, -0.870087, -0.492898, -0.871595, -0.490226, -0.873095, -0.487550, -0.874587, -0.484869, -0.876070, -0.482184, -0.877545, -0.479494, -0.879012, -0.476799, -0.880471, -0.474100, -0.881921, -0.471397, -0.883363, -0.468689, -0.884797, -0.465976, -0.886223, -0.463260, -0.887640, -0.460539, -0.889048, -0.457813, -0.890449, -0.455084, -0.891841, -0.452350, -0.893224, -0.449611, -0.894599, -0.446869, -0.895966, -0.444122, -0.897325, -0.441371, -0.898674, -0.438616, -0.900016, -0.435857, -0.901349, -0.433094, -0.902673, -0.430326, -0.903989, -0.427555, -0.905297, -0.424780, -0.906596, -0.422000, -0.907886, -0.419217, -0.909168, -0.416430, -0.910441, -0.413638, -0.911706, -0.410843, -0.912962, -0.408044, -0.914210, -0.405241, -0.915449, -0.402435, -0.916679, -0.399624, -0.917901, -0.396810, -0.919114, -0.393992, -0.920318, -0.391170, -0.921514, -0.388345, -0.922701, -0.385516, -0.923880, -0.382683, -0.925049, -0.379847, -0.926210, -0.377007, -0.927363, -0.374164, -0.928506, -0.371317, -0.929641, -0.368467, -0.930767, -0.365613, -0.931884, -0.362756, -0.932993, -0.359895, -0.934093, -0.357031, -0.935184, -0.354164, -0.936266, -0.351293, -0.937339, -0.348419, -0.938404, -0.345541, -0.939459, -0.342661, -0.940506, -0.339777, -0.941544, -0.336890, -0.942573, -0.334000, -0.943593, -0.331106, -0.944605, -0.328210, -0.945607, -0.325310, -0.946601, -0.322408, -0.947586, -0.319502, -0.948561, -0.316593, -0.949528, -0.313682, -0.950486, -0.310767, -0.951435, -0.307850, -0.952375, -0.304929, -0.953306, -0.302006, -0.954228, -0.299080, -0.955141, -0.296151, -0.956045, -0.293219, -0.956940, -0.290285, -0.957826, -0.287347, -0.958703, -0.284408, -0.959572, -0.281465, -0.960431, -0.278520, -0.961280, -0.275572, -0.962121, -0.272621, -0.962953, -0.269668, -0.963776, -0.266713, -0.964590, -0.263755, -0.965394, -0.260794, -0.966190, -0.257831, -0.966976, -0.254866, -0.967754, -0.251898, -0.968522, -0.248928, -0.969281, -0.245955, -0.970031, -0.242980, -0.970772, -0.240003, -0.971504, -0.237024, -0.972226, -0.234042, -0.972940, -0.231058, -0.973644, -0.228072, -0.974339, -0.225084, -0.975025, -0.222094, -0.975702, -0.219101, -0.976370, -0.216107, -0.977028, -0.213110, -0.977677, -0.210112, -0.978317, -0.207111, -0.978948, -0.204109, -0.979570, -0.201105, -0.980182, -0.198098, -0.980785, -0.195090, -0.981379, -0.192080, -0.981964, -0.189069, -0.982539, -0.186055, -0.983105, -0.183040, -0.983662, -0.180023, -0.984210, -0.177004, -0.984749, -0.173984, -0.985278, -0.170962, -0.985798, -0.167938, -0.986308, -0.164913, -0.986809, -0.161886, -0.987301, -0.158858, -0.987784, -0.155828, -0.988258, -0.152797, -0.988722, -0.149765, -0.989177, -0.146730, -0.989622, -0.143695, -0.990058, -0.140658, -0.990485, -0.137620, -0.990903, -0.134581, -0.991311, -0.131540, -0.991710, -0.128498, -0.992099, -0.125455, -0.992480, -0.122411, -0.992850, -0.119365, -0.993212, -0.116319, -0.993564, -0.113271, -0.993907, -0.110222, -0.994240, -0.107172, -0.994565, -0.104122, -0.994879, -0.101070, -0.995185, -0.098017, -0.995481, -0.094963, -0.995767, -0.091909, -0.996045, -0.088854, -0.996313, -0.085797, -0.996571, -0.082740, -0.996820, -0.079682, -0.997060, -0.076624, -0.997290, -0.073565, -0.997511, -0.070505, -0.997723, -0.067444, -0.997925, -0.064383, -0.998118, -0.061321, -0.998302, -0.058258, -0.998476, -0.055195, -0.998640, -0.052132, -0.998795, -0.049068, -0.998941, -0.046003, -0.999078, -0.042938, -0.999205, -0.039873, -0.999322, -0.036807, -0.999431, -0.033741, -0.999529, -0.030675, -0.999619, -0.027608, -0.999699, -0.024541, -0.999769, -0.021474, -0.999831, -0.018407, -0.999882, -0.015339, -0.999925, -0.012272, -0.999958, -0.009204, -0.999981, -0.006136, -0.999995, -0.003068, -1.000000, -0.000000, -0.999995, 0.003068, -0.999981, 0.006136, -0.999958, 0.009204, -0.999925, 0.012272, -0.999882, 0.015339, -0.999831, 0.018407, -0.999769, 0.021474, -0.999699, 0.024541, -0.999619, 0.027608, -0.999529, 0.030675, -0.999431, 0.033741, -0.999322, 0.036807, -0.999205, 0.039873, -0.999078, 0.042938, -0.998941, 0.046003, -0.998795, 0.049068, -0.998640, 0.052132, -0.998476, 0.055195, -0.998302, 0.058258, -0.998118, 0.061321, -0.997925, 0.064383, -0.997723, 0.067444, -0.997511, 0.070505, -0.997290, 0.073565, -0.997060, 0.076624, -0.996820, 0.079682, -0.996571, 0.082740, -0.996313, 0.085797, -0.996045, 0.088854, -0.995767, 0.091909, -0.995481, 0.094963, -0.995185, 0.098017, -0.994879, 0.101070, -0.994565, 0.104122, -0.994240, 0.107172, -0.993907, 0.110222, -0.993564, 0.113271, -0.993212, 0.116319, -0.992850, 0.119365, -0.992480, 0.122411, -0.992099, 0.125455, -0.991710, 0.128498, -0.991311, 0.131540, -0.990903, 0.134581, -0.990485, 0.137620, -0.990058, 0.140658, -0.989622, 0.143695, -0.989177, 0.146730, -0.988722, 0.149765, -0.988258, 0.152797, -0.987784, 0.155828, -0.987301, 0.158858, -0.986809, 0.161886, -0.986308, 0.164913, -0.985798, 0.167938, -0.985278, 0.170962, -0.984749, 0.173984, -0.984210, 0.177004, -0.983662, 0.180023, -0.983105, 0.183040, -0.982539, 0.186055, -0.981964, 0.189069, -0.981379, 0.192080, -0.980785, 0.195090, -0.980182, 0.198098, -0.979570, 0.201105, -0.978948, 0.204109, -0.978317, 0.207111, -0.977677, 0.210112, -0.977028, 0.213110, -0.976370, 0.216107, -0.975702, 0.219101, -0.975025, 0.222094, -0.974339, 0.225084, -0.973644, 0.228072, -0.972940, 0.231058, -0.972226, 0.234042, -0.971504, 0.237024, -0.970772, 0.240003, -0.970031, 0.242980, -0.969281, 0.245955, -0.968522, 0.248928, -0.967754, 0.251898, -0.966976, 0.254866, -0.966190, 0.257831, -0.965394, 0.260794, -0.964590, 0.263755, -0.963776, 0.266713, -0.962953, 0.269668, -0.962121, 0.272621, -0.961280, 0.275572, -0.960431, 0.278520, -0.959572, 0.281465, -0.958703, 0.284408, -0.957826, 0.287347, -0.956940, 0.290285, -0.956045, 0.293219, -0.955141, 0.296151, -0.954228, 0.299080, -0.953306, 0.302006, -0.952375, 0.304929, -0.951435, 0.307850, -0.950486, 0.310767, -0.949528, 0.313682, -0.948561, 0.316593, -0.947586, 0.319502, -0.946601, 0.322408, -0.945607, 0.325310, -0.944605, 0.328210, -0.943593, 0.331106, -0.942573, 0.334000, -0.941544, 0.336890, -0.940506, 0.339777, -0.939459, 0.342661, -0.938404, 0.345541, -0.937339, 0.348419, -0.936266, 0.351293, -0.935184, 0.354164, -0.934093, 0.357031, -0.932993, 0.359895, -0.931884, 0.362756, -0.930767, 0.365613, -0.929641, 0.368467, -0.928506, 0.371317, -0.927363, 0.374164, -0.926210, 0.377007, -0.925049, 0.379847, -0.923880, 0.382683, -0.922701, 0.385516, -0.921514, 0.388345, -0.920318, 0.391170, -0.919114, 0.393992, -0.917901, 0.396810, -0.916679, 0.399624, -0.915449, 0.402435, -0.914210, 0.405241, -0.912962, 0.408044, -0.911706, 0.410843, -0.910441, 0.413638, -0.909168, 0.416430, -0.907886, 0.419217, -0.906596, 0.422000, -0.905297, 0.424780, -0.903989, 0.427555, -0.902673, 0.430326, -0.901349, 0.433094, -0.900016, 0.435857, -0.898674, 0.438616, -0.897325, 0.441371, -0.895966, 0.444122, -0.894599, 0.446869, -0.893224, 0.449611, -0.891841, 0.452350, -0.890449, 0.455084, -0.889048, 0.457813, -0.887640, 0.460539, -0.886223, 0.463260, -0.884797, 0.465976, -0.883363, 0.468689, -0.881921, 0.471397, -0.880471, 0.474100, -0.879012, 0.476799, -0.877545, 0.479494, -0.876070, 0.482184, -0.874587, 0.484869, -0.873095, 0.487550, -0.871595, 0.490226, -0.870087, 0.492898, -0.868571, 0.495565, -0.867046, 0.498228, -0.865514, 0.500885, -0.863973, 0.503538, -0.862424, 0.506187, -0.860867, 0.508830, -0.859302, 0.511469, -0.857729, 0.514103, -0.856147, 0.516732, -0.854558, 0.519356, -0.852961, 0.521975, -0.851355, 0.524590, -0.849742, 0.527199, -0.848120, 0.529804, -0.846491, 0.532403, -0.844854, 0.534998, -0.843208, 0.537587, -0.841555, 0.540171, -0.839894, 0.542751, -0.838225, 0.545325, -0.836548, 0.547894, -0.834863, 0.550458, -0.833170, 0.553017, -0.831470, 0.555570, -0.829761, 0.558119, -0.828045, 0.560662, -0.826321, 0.563199, -0.824589, 0.565732, -0.822850, 0.568259, -0.821103, 0.570781, -0.819348, 0.573297, -0.817585, 0.575808, -0.815814, 0.578314, -0.814036, 0.580814, -0.812251, 0.583309, -0.810457, 0.585798, -0.808656, 0.588282, -0.806848, 0.590760, -0.805031, 0.593232, -0.803208, 0.595699, -0.801376, 0.598161, -0.799537, 0.600616, -0.797691, 0.603067, -0.795837, 0.605511, -0.793975, 0.607950, -0.792107, 0.610383, -0.790230, 0.612810, -0.788346, 0.615232, -0.786455, 0.617647, -0.784557, 0.620057, -0.782651, 0.622461, -0.780737, 0.624859, -0.778817, 0.627252, -0.776888, 0.629638, -0.774953, 0.632019, -0.773010, 0.634393, -0.771061, 0.636762, -0.769103, 0.639124, -0.767139, 0.641481, -0.765167, 0.643832, -0.763188, 0.646176, -0.761202, 0.648514, -0.759209, 0.650847, -0.757209, 0.653173, -0.755201, 0.655493, -0.753187, 0.657807, -0.751165, 0.660114, -0.749136, 0.662416, -0.747101, 0.664711, -0.745058, 0.667000, -0.743008, 0.669283, -0.740951, 0.671559, -0.738887, 0.673829, -0.736817, 0.676093, -0.734739, 0.678350, -0.732654, 0.680601, -0.730563, 0.682846, -0.728464, 0.685084, -0.726359, 0.687315, -0.724247, 0.689541, -0.722128, 0.691759, -0.720003, 0.693971, -0.717870, 0.696177, -0.715731, 0.698376, -0.713585, 0.700569, -0.711432, 0.702755, -0.709273, 0.704934, -0.707107, 0.707107, -0.704934, 0.709273, -0.702755, 0.711432, -0.700569, 0.713585, -0.698376, 0.715731, -0.696177, 0.717870, -0.693971, 0.720003, -0.691759, 0.722128, -0.689541, 0.724247, -0.687315, 0.726359, -0.685084, 0.728464, -0.682846, 0.730563, -0.680601, 0.732654, -0.678350, 0.734739, -0.676093, 0.736817, -0.673829, 0.738887, -0.671559, 0.740951, -0.669283, 0.743008, -0.667000, 0.745058, -0.664711, 0.747101, -0.662416, 0.749136, -0.660114, 0.751165, -0.657807, 0.753187, -0.655493, 0.755201, -0.653173, 0.757209, -0.650847, 0.759209, -0.648514, 0.761202, -0.646176, 0.763188, -0.643832, 0.765167, -0.641481, 0.767139, -0.639124, 0.769103, -0.636762, 0.771061, -0.634393, 0.773010, -0.632019, 0.774953, -0.629638, 0.776888, -0.627252, 0.778817, -0.624859, 0.780737, -0.622461, 0.782651, -0.620057, 0.784557, -0.617647, 0.786455, -0.615232, 0.788346, -0.612810, 0.790230, -0.610383, 0.792107, -0.607950, 0.793975, -0.605511, 0.795837, -0.603067, 0.797691, -0.600616, 0.799537, -0.598161, 0.801376, -0.595699, 0.803208, -0.593232, 0.805031, -0.590760, 0.806848, -0.588282, 0.808656, -0.585798, 0.810457, -0.583309, 0.812251, -0.580814, 0.814036, -0.578314, 0.815814, -0.575808, 0.817585, -0.573297, 0.819348, -0.570781, 0.821103, -0.568259, 0.822850, -0.565732, 0.824589, -0.563199, 0.826321, -0.560662, 0.828045, -0.558119, 0.829761, -0.555570, 0.831470, -0.553017, 0.833170, -0.550458, 0.834863, -0.547894, 0.836548, -0.545325, 0.838225, -0.542751, 0.839894, -0.540171, 0.841555, -0.537587, 0.843208, -0.534998, 0.844854, -0.532403, 0.846491, -0.529804, 0.848120, -0.527199, 0.849742, -0.524590, 0.851355, -0.521975, 0.852961, -0.519356, 0.854558, -0.516732, 0.856147, -0.514103, 0.857729, -0.511469, 0.859302, -0.508830, 0.860867, -0.506187, 0.862424, -0.503538, 0.863973, -0.500885, 0.865514, -0.498228, 0.867046, -0.495565, 0.868571, -0.492898, 0.870087, -0.490226, 0.871595, -0.487550, 0.873095, -0.484869, 0.874587, -0.482184, 0.876070, -0.479494, 0.877545, -0.476799, 0.879012, -0.474100, 0.880471, -0.471397, 0.881921, -0.468689, 0.883363, -0.465976, 0.884797, -0.463260, 0.886223, -0.460539, 0.887640, -0.457813, 0.889048, -0.455084, 0.890449, -0.452350, 0.891841, -0.449611, 0.893224, -0.446869, 0.894599, -0.444122, 0.895966, -0.441371, 0.897325, -0.438616, 0.898674, -0.435857, 0.900016, -0.433094, 0.901349, -0.430326, 0.902673, -0.427555, 0.903989, -0.424780, 0.905297, -0.422000, 0.906596, -0.419217, 0.907886, -0.416430, 0.909168, -0.413638, 0.910441, -0.410843, 0.911706, -0.408044, 0.912962, -0.405241, 0.914210, -0.402435, 0.915449, -0.399624, 0.916679, -0.396810, 0.917901, -0.393992, 0.919114, -0.391170, 0.920318, -0.388345, 0.921514, -0.385516, 0.922701, -0.382683, 0.923880, -0.379847, 0.925049, -0.377007, 0.926210, -0.374164, 0.927363, -0.371317, 0.928506, -0.368467, 0.929641, -0.365613, 0.930767, -0.362756, 0.931884, -0.359895, 0.932993, -0.357031, 0.934093, -0.354164, 0.935184, -0.351293, 0.936266, -0.348419, 0.937339, -0.345541, 0.938404, -0.342661, 0.939459, -0.339777, 0.940506, -0.336890, 0.941544, -0.334000, 0.942573, -0.331106, 0.943593, -0.328210, 0.944605, -0.325310, 0.945607, -0.322408, 0.946601, -0.319502, 0.947586, -0.316593, 0.948561, -0.313682, 0.949528, -0.310767, 0.950486, -0.307850, 0.951435, -0.304929, 0.952375, -0.302006, 0.953306, -0.299080, 0.954228, -0.296151, 0.955141, -0.293219, 0.956045, -0.290285, 0.956940, -0.287347, 0.957826, -0.284408, 0.958703, -0.281465, 0.959572, -0.278520, 0.960431, -0.275572, 0.961280, -0.272621, 0.962121, -0.269668, 0.962953, -0.266713, 0.963776, -0.263755, 0.964590, -0.260794, 0.965394, -0.257831, 0.966190, -0.254866, 0.966976, -0.251898, 0.967754, -0.248928, 0.968522, -0.245955, 0.969281, -0.242980, 0.970031, -0.240003, 0.970772, -0.237024, 0.971504, -0.234042, 0.972226, -0.231058, 0.972940, -0.228072, 0.973644, -0.225084, 0.974339, -0.222094, 0.975025, -0.219101, 0.975702, -0.216107, 0.976370, -0.213110, 0.977028, -0.210112, 0.977677, -0.207111, 0.978317, -0.204109, 0.978948, -0.201105, 0.979570, -0.198098, 0.980182, -0.195090, 0.980785, -0.192080, 0.981379, -0.189069, 0.981964, -0.186055, 0.982539, -0.183040, 0.983105, -0.180023, 0.983662, -0.177004, 0.984210, -0.173984, 0.984749, -0.170962, 0.985278, -0.167938, 0.985798, -0.164913, 0.986308, -0.161886, 0.986809, -0.158858, 0.987301, -0.155828, 0.987784, -0.152797, 0.988258, -0.149765, 0.988722, -0.146730, 0.989177, -0.143695, 0.989622, -0.140658, 0.990058, -0.137620, 0.990485, -0.134581, 0.990903, -0.131540, 0.991311, -0.128498, 0.991710, -0.125455, 0.992099, -0.122411, 0.992480, -0.119365, 0.992850, -0.116319, 0.993212, -0.113271, 0.993564, -0.110222, 0.993907, -0.107172, 0.994240, -0.104122, 0.994565, -0.101070, 0.994879, -0.098017, 0.995185, -0.094963, 0.995481, -0.091909, 0.995767, -0.088854, 0.996045, -0.085797, 0.996313, -0.082740, 0.996571, -0.079682, 0.996820, -0.076624, 0.997060, -0.073565, 0.997290, -0.070505, 0.997511, -0.067444, 0.997723, -0.064383, 0.997925, -0.061321, 0.998118, -0.058258, 0.998302, -0.055195, 0.998476, -0.052132, 0.998640, -0.049068, 0.998795, -0.046003, 0.998941, -0.042938, 0.999078, -0.039873, 0.999205, -0.036807, 0.999322, -0.033741, 0.999431, -0.030675, 0.999529, -0.027608, 0.999619, -0.024541, 0.999699, -0.021474, 0.999769, -0.018407, 0.999831, -0.015339, 0.999882, -0.012272, 0.999925, -0.009204, 0.999958, -0.006136, 0.999981, -0.003068, 0.999995, engine/h2shared/sizebuf.c000066400000000000000000000043601444734033100156440ustar00rootroot00000000000000/* sizebuf.c -- sized buffers * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include "compiler.h" #include "sys.h" #include "printsys.h" #include "sizebuf.h" #include "zone.h" void SZ_Init (sizebuf_t *buf, byte *data, int length) { memset (buf, 0, sizeof(*buf)); if (data != NULL) { buf->data = data; buf->maxsize = length; } else { if (length < 256) length = 256; buf->data = (byte *) Hunk_AllocName (length, "sizebuf"); buf->maxsize = length; } } void SZ_Clear (sizebuf_t *buf) { buf->cursize = 0; buf->overflowed = false; } void *SZ_GetSpace (sizebuf_t *buf, int length) { void *data; if (buf->cursize + length > buf->maxsize) { if (!buf->allowoverflow) Sys_Error ("%s: overflow without allowoverflow set", __thisfunc__); if (length > buf->maxsize) Sys_Error ("%s: %i is > full buffer size", __thisfunc__, length); Sys_Printf ("%s: overflow\nCurrently %d of %d, requested %d\n", __thisfunc__, buf->cursize, buf->maxsize, length); SZ_Clear (buf); buf->overflowed = true; } data = buf->data + buf->cursize; buf->cursize += length; return data; } void SZ_Write (sizebuf_t *buf, const void *data, int length) { memcpy (SZ_GetSpace(buf,length),data,length); } void SZ_Print (sizebuf_t *buf, const char *data) { int len = (int)strlen(data) + 1; if (!buf->cursize || buf->data[buf->cursize-1]) { /* no trailing 0 */ memcpy ((byte *)SZ_GetSpace(buf, len ) , data, len); } else { /* write over trailing 0 */ memcpy ((byte *)SZ_GetSpace(buf, len-1)-1, data, len); } } engine/h2shared/sizebuf.h000066400000000000000000000026071444734033100156530ustar00rootroot00000000000000/* sizebuf.h -- sized buffer defs * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __SIZEBUF_H #define __SIZEBUF_H typedef struct sizebuf_s { qboolean allowoverflow; // if false, do a Sys_Error qboolean overflowed; // set to true if the buffer size failed byte *data; int maxsize; int cursize; } sizebuf_t; void SZ_Init (sizebuf_t *buf, byte *data, int length); // if the data buffer is NULL, a new one will be allocated. void SZ_Clear (sizebuf_t *buf); void *SZ_GetSpace (sizebuf_t *buf, int length); void SZ_Write (sizebuf_t *buf, const void *data, int length); void SZ_Print (sizebuf_t *buf, const char *data); // strcats onto the sizebuf #endif /* __SIZEBUF_H */ engine/h2shared/snd_ahi.c000066400000000000000000000174371444734033100156130ustar00rootroot00000000000000/* * Sound support using Amiga AHI, based on original work by * Mark Olsen * Adapted to uHexen2 by Szilard Biro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" #include "snd_sys.h" #if HAVE_AHI_SOUND #include "snd_ahi.h" #include #include #include #include #ifdef __AROS__ #include #elif !defined __MORPHOS__ #include #endif #include /* IPTR */ static char s_ahi_driver[] = "AHI audio system"; struct Library *AHIBase; struct AHIChannelInfo { struct AHIEffChannelInfo aeci; ULONG offset; }; struct AHIdata { struct MsgPort *msgport; struct AHIRequest *ahireq; struct AHIAudioCtrl *audioctrl; void *samplebuffer; struct Hook EffectHook; struct AHIChannelInfo aci; unsigned int readpos; }; static struct AHIdata *ad; #ifdef __MORPHOS__ IPTR EffectFuncTramp(); static struct EmulLibEntry EffectFunc = { TRAP_LIB, 0, (void (*)(void))EffectFuncTramp }; IPTR EffectFuncTramp() { struct Hook *hook = (struct Hook *)REG_A0; struct AHIEffChannelInfo *aeci = (struct AHIEffChannelInfo *)REG_A1; #else #if defined(__AROS__) && !defined(HOOKPROTO) /* ABIv1 ? */ #define HOOKPROTO(name, ret, obj, param) static SAVEDS ret \ name(struct Hook *hook, obj, param) #endif HOOKPROTO(EffectFunc, IPTR, struct AHIAudioCtrl *aac, struct AHIEffChannelInfo *aeci) { #endif struct AHIdata *adata = (struct AHIdata *) hook->h_Data; adata->readpos = aeci->ahieci_Offset[0]; return 0; } static qboolean S_AHI_Init(dma_t *dma) { IPTR channels, speed, bits; IPTR r, samples; struct AHISampleInfo sample; char modename[64]; if (ad) return true; ad = (struct AHIdata *) AllocVec(sizeof(*ad), MEMF_ANY); if (!ad) { Con_Printf("AHI: AllocVec(%u) failed.\n", (unsigned) sizeof(*ad)); return false; } speed = desired_speed; shm = dma; memset ((void *) dma, 0, sizeof(dma_t)); ad->msgport = CreateMsgPort(); if (ad->msgport) { ad->ahireq = (struct AHIRequest *)CreateIORequest(ad->msgport, sizeof(struct AHIRequest)); if (ad->ahireq) { if (!OpenDevice(AHINAME, AHI_NO_UNIT, (struct IORequest *)ad->ahireq, 0)) { AHIBase = (struct Library *)ad->ahireq->ahir_Std.io_Device; ad->audioctrl = AHI_AllocAudio(AHIA_AudioID, AHI_DEFAULT_ID, AHIA_MixFreq, speed, AHIA_Channels, 1, AHIA_Sounds, 1, TAG_END); if (ad->audioctrl) { AHI_GetAudioAttrs(AHI_INVALID_ID, ad->audioctrl, AHIDB_BufferLen, sizeof(modename), AHIDB_Name, (IPTR) modename, AHIDB_MaxChannels, (IPTR) &channels, AHIDB_Bits, (IPTR) &bits, TAG_END); Con_DPrintf("AHI_GetAudioAttrs: %u bits, %u channels\n", (unsigned) bits, (unsigned) channels); if (channels > desired_channels) channels = desired_channels; if (bits == 14) bits = 16; /* for 14-bit Paula modes */ if (bits > desired_bits) bits = desired_bits; AHI_ControlAudio(ad->audioctrl, AHIC_MixFreq_Query, (IPTR) &speed, TAG_END); if (bits == 8 || bits == 16) { unsigned buffer_size; /* pick a buffer size that is a power of 2 (by masking off low bits) */ buffer_size = r = (ULONG)(speed * 0.15f); while (buffer_size & (buffer_size-1)) buffer_size &= (buffer_size-1); /* then check if it is the nearest power of 2 and bump it up if not */ if (r - buffer_size >= buffer_size >> 1) buffer_size *= 2; buffer_size *= channels; #ifndef PLATFORM_AMIGAOS3 buffer_size *= 4; /* for no stutters with crappy drivers -- bszili. */ #endif samples = buffer_size; buffer_size *= (bits / 8); shm->speed = speed; shm->samplebits = bits; shm->signed8 = (bits == 8); /* AHI does signed 8 bit. */ shm->channels = channels; shm->samples = samples; shm->submission_chunk = 1; ad->samplebuffer = AllocVec(buffer_size, MEMF_ANY|MEMF_CLEAR); if (ad->samplebuffer) { shm->buffer = (unsigned char *) ad->samplebuffer; if (channels == 1) { if (bits == 8) sample.ahisi_Type = AHIST_M8S; else sample.ahisi_Type = AHIST_M16S; } else { if (bits == 8) sample.ahisi_Type = AHIST_S8S; else sample.ahisi_Type = AHIST_S16S; } sample.ahisi_Address = ad->samplebuffer; sample.ahisi_Length = samples /channels;/* buffer_size/AHI_SampleFrameSize(sample.ahisi_Type) */ r = AHI_LoadSound(0, AHIST_DYNAMICSAMPLE, &sample, ad->audioctrl); if (r == 0) { r = AHI_ControlAudio(ad->audioctrl, AHIC_Play, TRUE, TAG_END); if (r == 0) /* SUCCESS, FINALLY. */ { AHI_Play(ad->audioctrl, AHIP_BeginChannel, 0, AHIP_Freq, speed, AHIP_Vol, 0x10000, AHIP_Pan, 0x8000, AHIP_Sound, 0, AHIP_EndChannel, 0, TAG_END); ad->aci.aeci.ahie_Effect = AHIET_CHANNELINFO; ad->aci.aeci.ahieci_Func = &ad->EffectHook; ad->aci.aeci.ahieci_Channels = 1; ad->EffectHook.h_Entry = (IPTR (*)())&EffectFunc; ad->EffectHook.h_Data = ad; AHI_SetEffect(&ad->aci, ad->audioctrl); Con_Printf("AHI mode \"%s\", %u bytes buffer\n", modename, buffer_size); return true; } else { Con_Printf("AHI: LoadSound failed.\n"); } } } else { Con_Printf("AHI: AllocVec(%u) failed.\n", buffer_size); } FreeVec(ad->samplebuffer); } else { Con_Printf("AHI: %u bit format not supported.\n", (unsigned) bits); } AHI_FreeAudio(ad->audioctrl); } else { Con_Printf("AHI: AllocAudio failed.\n"); } CloseDevice((struct IORequest *)ad->ahireq); } else { Con_Printf("AHI: OpenDevice failed.\n"); } DeleteIORequest((struct IORequest *)ad->ahireq); } else { Con_Printf("AHI: CreateIORequest failed.\n"); } DeleteMsgPort(ad->msgport); } else { Con_Printf("AHI: CreateMsgPort failed.\n"); } shm->buffer = NULL; shm = NULL; FreeVec(ad); ad = NULL; AHIBase = NULL; return false; } static int S_AHI_GetDMAPos(void) { return ad->readpos * shm->channels; } static void S_AHI_Shutdown(void) { if (ad == NULL) return; shm->buffer = NULL; shm = NULL; ad->aci.aeci.ahie_Effect = AHIET_CHANNELINFO|AHIET_CANCEL; AHI_SetEffect(&ad->aci.aeci, ad->audioctrl); AHI_ControlAudio(ad->audioctrl, AHIC_Play, FALSE, TAG_END); AHI_FreeAudio(ad->audioctrl); FreeVec(ad->samplebuffer); CloseDevice((struct IORequest *)ad->ahireq); DeleteIORequest((struct IORequest *)ad->ahireq); DeleteMsgPort(ad->msgport); FreeVec(ad); ad = NULL; AHIBase = NULL; } static void S_AHI_LockBuffer (void) { } static void S_AHI_BlockSound (void) { } static void S_AHI_UnblockSound (void) { } static void S_AHI_Submit(void) { } snd_driver_t snddrv_ahi = { S_AHI_Init, S_AHI_Shutdown, S_AHI_GetDMAPos, S_AHI_LockBuffer, S_AHI_Submit, S_AHI_BlockSound, S_AHI_UnblockSound, s_ahi_driver, SNDDRV_ID_AHI, false, NULL }; #endif /* HAVE_AHI_SOUND */ engine/h2shared/snd_ahi.h000066400000000000000000000003071444734033100156040ustar00rootroot00000000000000/* Sound support using Amiga AHI. */ #if !defined(__HX2_SND_AHI) #define __HX2_SND_AHI #if HAVE_AHI_SOUND extern snd_driver_t snddrv_ahi; #endif /* HAVE_AHI_SOUND */ #endif /* __HX2_SND_AHI */ engine/h2shared/snd_alsa.c000066400000000000000000000243501444734033100157620ustar00rootroot00000000000000/* snd_alsa.c -- ALSA 1.0 sound driver for Hexen II: Hammer of Thyrion * * Copyright (C) 1999,2004 contributors of the QuakeForge project * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "snd_sys.h" #if HAVE_ALSA_SOUND #include "snd_alsa.h" #include #include #define NB_PERIODS 4 static char s_alsa_driver[] = "ALSA"; static void *alsa_handle = NULL; /*static const char alsa_default[] = "hw:0,0";*/ /*static const char alsa_default[] = "plughw:0";*/ static const char alsa_default[] = "default"; static const char *pcmname = alsa_default; static snd_pcm_t *pcm = NULL; static snd_pcm_uframes_t buffer_size; #define ALSA_FUNC(ret, func, params) \ static ret (*hx2##func) params; #include "alsa_funcs.h" #undef ALSA_FUNC static qboolean load_libasound (void) { alsa_handle = (void *) dlopen ("libasound.so.2", RTLD_GLOBAL | RTLD_NOW); if (!alsa_handle) { Con_Printf ("Couldn't load libasound.so.2: %s\n", dlerror()); return false; } #define ALSA_FUNC(ret, func, params) \ do { \ hx2##func = (ret (*) params) dlsym (alsa_handle, #func); \ if (hx2##func == NULL) \ { \ Con_Printf ("Couldn't load ALSA function %s\n", #func); \ dlclose (alsa_handle); \ alsa_handle = NULL; \ return false; \ } \ } while (0); #include "alsa_funcs.h" #undef ALSA_FUNC return true; } #if defined(__GNUC__) && \ !(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) #define ALSA_CHECK_ERR(check, fmt, args...) \ do { \ if (check < 0) { \ Con_Printf ("ALSA: " fmt, ##args); \ goto error; \ } \ } while (0) #else #define ALSA_CHECK_ERR(check, ...) \ do { \ if (check < 0) { \ Con_Printf ("ALSA: " __VA_ARGS__); \ goto error; \ } \ } while (0) #endif static int S_ALSA_GetDMAPos (void); static qboolean S_ALSA_Init (dma_t *dma) { int i, err; unsigned int rate; int tmp_bits, tmp_chan; snd_pcm_hw_params_t *hw = NULL; snd_pcm_sw_params_t *sw = NULL; snd_pcm_uframes_t frag_size; if (!load_libasound()) return false; i = COM_CheckParm("-alsadev"); if (i != 0 && i < com_argc - 1) pcmname = com_argv[i + 1]; err = hx2snd_pcm_open (&pcm, pcmname, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); if (err < 0) { Con_Printf ("ALSA: error opening device \"%s\": %s\n", pcmname, hx2snd_strerror(err)); return false; } Con_Printf ("ALSA: Using device: %s\n", pcmname); err = hx2snd_pcm_hw_params_malloc (&hw); ALSA_CHECK_ERR(err, "unable to allocate hardware params. %s\n", hx2snd_strerror(err)); err = hx2snd_pcm_hw_params_any (pcm, hw); ALSA_CHECK_ERR(err, "unable to init hardware params. %s\n", hx2snd_strerror(err)); err = hx2snd_pcm_hw_params_set_access (pcm, hw, SND_PCM_ACCESS_MMAP_INTERLEAVED); ALSA_CHECK_ERR(err, "unable to set interleaved access. %s\n", hx2snd_strerror(err)); tmp_bits = (desired_bits == 8) ? SND_PCM_FORMAT_U8 : SND_PCM_FORMAT_S16; err = hx2snd_pcm_hw_params_set_format (pcm, hw, (snd_pcm_format_t) tmp_bits); if (err < 0) { Con_Printf ("Problems setting %d bit format, trying alternatives..\n", desired_bits); tmp_bits = (desired_bits == 8) ? SND_PCM_FORMAT_S16 : SND_PCM_FORMAT_U8; err = hx2snd_pcm_hw_params_set_format (pcm, hw, (snd_pcm_format_t) tmp_bits); ALSA_CHECK_ERR(err, "Neither 8 nor 16 bit format supported. %s\n", hx2snd_strerror(err)); } tmp_bits = (tmp_bits == SND_PCM_FORMAT_U8) ? 8 : 16; tmp_chan = desired_channels; err = hx2snd_pcm_hw_params_set_channels (pcm, hw, tmp_chan); if (err < 0) { Con_Printf ("Problems setting channels to %s, retrying for %s\n", (desired_channels == 2) ? "stereo" : "mono", (desired_channels == 2) ? "mono" : "stereo"); tmp_chan = (desired_channels == 2) ? 1 : 2; err = hx2snd_pcm_hw_params_set_channels (pcm, hw, tmp_chan); ALSA_CHECK_ERR(err, "unable to set desired channels. %s\n", hx2snd_strerror(err)); } rate = desired_speed; err = hx2snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0); if (err < 0) { Con_Printf("Problems setting sample rate, trying alternatives..\n"); for (i = 0; i < MAX_TRYRATES; i++) { rate = tryrates[i]; err = hx2snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0); if (err < 0) { Con_DPrintf ("Unable to set sample rate %d\n", tryrates[i]); rate = 0; } else { if (rate != (unsigned int) tryrates[i]) { Con_Printf ("Warning: Rate set (%u) didn't match requested rate (%d)!\n", rate, tryrates[i]); /* goto error;*/ } break; } } if (rate == 0) { Con_Printf ("Unable to set any sample rates.\n"); goto error; } } else { if (rate != (unsigned int)desired_speed) { Con_Printf ("Warning: Rate set (%u) didn't match requested rate (%d)!\n", rate, desired_speed); /* goto error;*/ } } /* pick a buffer size that is a power of 2 (by masking off low bits) */ buffer_size = i = (int)(rate * 0.15f); while (buffer_size & (buffer_size-1)) buffer_size &= (buffer_size-1); /* then check if it is the nearest power of 2 and bump it up if not */ if (i - buffer_size >= buffer_size >> 1) buffer_size *= 2; err = hx2snd_pcm_hw_params_set_buffer_size_near (pcm, hw, &buffer_size); ALSA_CHECK_ERR(err, "unable to set buffer size near %lu (%s)\n", (unsigned long)buffer_size, hx2snd_strerror(err)); err = hx2snd_pcm_hw_params_get_buffer_size (hw, &buffer_size); ALSA_CHECK_ERR(err, "unable to get buffer size. %s\n", hx2snd_strerror(err)); if (buffer_size & (buffer_size-1)) { Con_Printf ("ALSA: WARNING: non-power of 2 buffer size. sound may be\n"); Con_Printf ("unsatisfactory. Recommend using either the plughw or hw\n"); Con_Printf ("devices or adjusting dmix to have a power of 2 buf size\n"); } /* pick a period size near the buffer_size we got from ALSA */ frag_size = buffer_size / NB_PERIODS; err = hx2snd_pcm_hw_params_set_period_size_near (pcm, hw, &frag_size, 0); ALSA_CHECK_ERR(err, "unable to set period size near %i. %s\n", (int)frag_size, hx2snd_strerror(err)); err = hx2snd_pcm_hw_params (pcm, hw); ALSA_CHECK_ERR(err, "unable to install hardware params. %s\n", hx2snd_strerror(err)); err = hx2snd_pcm_sw_params_malloc (&sw); ALSA_CHECK_ERR(err, "unable to allocate software params. %s\n", hx2snd_strerror(err)); err = hx2snd_pcm_sw_params_current (pcm, sw); ALSA_CHECK_ERR(err, "unable to determine current software params. %s\n", hx2snd_strerror(err)); err = hx2snd_pcm_sw_params_set_start_threshold (pcm, sw, ~0U); ALSA_CHECK_ERR(err, "unable to set playback threshold. %s\n", hx2snd_strerror(err)); err = hx2snd_pcm_sw_params_set_stop_threshold (pcm, sw, ~0U); ALSA_CHECK_ERR(err, "unable to set playback stop threshold. %s\n", hx2snd_strerror(err)); err = hx2snd_pcm_sw_params (pcm, sw); ALSA_CHECK_ERR(err, "unable to install software params. %s\n", hx2snd_strerror(err)); memset ((void *) dma, 0, sizeof(dma_t)); shm = dma; shm->channels = tmp_chan; shm->submission_chunk = 1; shm->samplepos = 0; shm->samplebits = tmp_bits; Con_Printf ("ALSA: %lu bytes buffer with mmap interleaved access\n", (unsigned long)buffer_size); shm->samples = buffer_size * shm->channels; /* mono samples in buffer */ shm->speed = rate; S_ALSA_GetDMAPos (); /* sets shm->buffer */ hx2snd_pcm_hw_params_free(hw); hx2snd_pcm_sw_params_free(sw); return true; error: /* full clean-up*/ if (hw) hx2snd_pcm_hw_params_free(hw); if (sw) hx2snd_pcm_sw_params_free(sw); shm = NULL; hx2snd_pcm_close (pcm); pcm = NULL; dlclose (alsa_handle); alsa_handle = NULL; return false; } static int S_ALSA_GetDMAPos (void) { snd_pcm_uframes_t offset; snd_pcm_uframes_t nframes; const snd_pcm_channel_area_t *areas; if (!shm) return 0; nframes = shm->samples/shm->channels; hx2snd_pcm_avail_update (pcm); hx2snd_pcm_mmap_begin (pcm, &areas, &offset, &nframes); /* the following commit was absent in QF, causing the * very first sound to be corrupted */ hx2snd_pcm_mmap_commit (pcm, offset, nframes); offset *= shm->channels; nframes *= shm->channels; shm->samplepos = offset; shm->buffer = (unsigned char *) areas->addr; /* FIXME! there's an area per channel */ return shm->samplepos; } static void S_ALSA_Shutdown (void) { if (shm) { /* full clean-up */ Con_Printf ("Shutting down ALSA sound\n"); hx2snd_pcm_drop (pcm); /* do I need this? */ hx2snd_pcm_close (pcm); pcm = NULL; shm->buffer = NULL; shm = NULL; dlclose (alsa_handle); alsa_handle = NULL; } } /* ============== SNDDMA_LockBuffer Makes sure dma buffer is valid ============== */ static void S_ALSA_LockBuffer (void) { /* nothing to do here */ } /* ============== SNDDMA_Submit Unlock the dma buffer / Send sound to the device ============== */ static void S_ALSA_Submit (void) { snd_pcm_uframes_t offset; snd_pcm_uframes_t nframes; const snd_pcm_channel_area_t *areas; int state; int count = paintedtime - soundtime; nframes = count / shm->channels; hx2snd_pcm_avail_update (pcm); hx2snd_pcm_mmap_begin (pcm, &areas, &offset, &nframes); state = hx2snd_pcm_state (pcm); switch (state) { case SND_PCM_STATE_PREPARED: hx2snd_pcm_mmap_commit (pcm, offset, nframes); hx2snd_pcm_start (pcm); break; case SND_PCM_STATE_RUNNING: hx2snd_pcm_mmap_commit (pcm, offset, nframes); break; default: break; } } static void S_ALSA_BlockSound (void) { hx2snd_pcm_pause (pcm, 1); } static void S_ALSA_UnblockSound (void) { hx2snd_pcm_pause (pcm, 0); } snd_driver_t snddrv_alsa = { S_ALSA_Init, S_ALSA_Shutdown, S_ALSA_GetDMAPos, S_ALSA_LockBuffer, S_ALSA_Submit, S_ALSA_BlockSound, S_ALSA_UnblockSound, s_alsa_driver, SNDDRV_ID_ALSA, false, NULL }; #endif /* HAVE_ALSA_SOUND */ engine/h2shared/snd_alsa.h000066400000000000000000000003101444734033100157550ustar00rootroot00000000000000/* Sound support using ALSA. */ #if !defined(__HX2_SND_ALSA) #define __HX2_SND_ALSA #if HAVE_ALSA_SOUND extern snd_driver_t snddrv_alsa; #endif /* HAVE_ALSA_SOUND */ #endif /* __HX2_SND_ALSA */ engine/h2shared/snd_codec.c000066400000000000000000000153761444734033100161270ustar00rootroot00000000000000/* * Audio Codecs: Adapted from ioquake3 with changes. * For now, only handles streaming music, not sound effects. * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2005 Stuart Dalton * Copyright (C) 2010-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" #include "snd_codec.h" #include "snd_codeci.h" /* headers for individual codecs */ #include "snd_timidity.h" #include "snd_wildmidi.h" #include "snd_mikmod.h" #include "snd_modplug.h" #include "snd_xmp.h" #include "snd_umx.h" #include "snd_wave.h" #include "snd_flac.h" #include "snd_mp3.h" #include "snd_vorbis.h" #include "snd_opus.h" static snd_codec_t *codecs; /* ================= S_CodecRegister ================= */ static void S_CodecRegister(snd_codec_t *codec) { codec->next = codecs; codecs = codec; } /* ================= S_CodecInit ================= */ void S_CodecInit (void) { snd_codec_t *codec; codecs = NULL; /* Register in the inverse order * of codec choice preference: */ #ifdef USE_CODEC_WILDMIDI S_CodecRegister(&wildmidi_codec); #endif #ifdef USE_CODEC_TIMIDITY S_CodecRegister(&timidity_codec); #endif #ifdef USE_CODEC_UMX S_CodecRegister(&umx_codec); #endif #ifdef USE_CODEC_MODPLUG S_CodecRegister(&modplug_codec); #endif #ifdef USE_CODEC_MIKMOD S_CodecRegister(&mikmod_codec); #endif #ifdef USE_CODEC_XMP S_CodecRegister(&xmp_codec); #endif #ifdef USE_CODEC_WAVE S_CodecRegister(&wav_codec); #endif #ifdef USE_CODEC_FLAC S_CodecRegister(&flac_codec); #endif #ifdef USE_CODEC_MP3 S_CodecRegister(&mp3_codec); #endif #ifdef USE_CODEC_VORBIS S_CodecRegister(&vorbis_codec); #endif #ifdef USE_CODEC_OPUS S_CodecRegister(&opus_codec); #endif codec = codecs; while (codec) { codec->initialize(); codec = codec->next; } } /* ================= S_CodecShutdown ================= */ void S_CodecShutdown (void) { snd_codec_t *codec = codecs; while (codec) { codec->shutdown(); codec = codec->next; } codecs = NULL; } /* ================= S_CodecOpenStream ================= */ snd_stream_t *S_CodecOpenStreamType (const char *filename, unsigned int type, qboolean loop) { snd_codec_t *codec; snd_stream_t *stream; if (type == CODECTYPE_NONE) { Con_Printf("Bad type for %s\n", filename); return NULL; } codec = codecs; while (codec) { if (type == codec->type) break; codec = codec->next; } if (!codec) { Con_Printf("Unknown type for %s\n", filename); return NULL; } stream = S_CodecUtilOpen(filename, codec, loop); if (stream) { if (codec->codec_open(stream)) stream->status = STREAM_PLAY; else S_CodecUtilClose(&stream); } return stream; } snd_stream_t *S_CodecOpenStreamExt (const char *filename, qboolean loop) { snd_codec_t *codec; snd_stream_t *stream; const char *ext; ext = COM_FileGetExtension(filename); if (! *ext) { Con_Printf("No extension for %s\n", filename); return NULL; } codec = codecs; while (codec) { if (!q_strcasecmp(ext, codec->ext)) break; codec = codec->next; } if (!codec) { Con_Printf("Unknown extension for %s\n", filename); return NULL; } stream = S_CodecUtilOpen(filename, codec, loop); if (stream) { if (codec->codec_open(stream)) stream->status = STREAM_PLAY; else S_CodecUtilClose(&stream); } return stream; } snd_stream_t *S_CodecOpenStreamAny (const char *filename, qboolean loop) { snd_codec_t *codec; snd_stream_t *stream; const char *ext; ext = COM_FileGetExtension(filename); if (! *ext) /* try all available */ { char tmp[MAX_QPATH]; codec = codecs; while (codec) { q_snprintf(tmp, sizeof(tmp), "%s.%s", filename, codec->ext); stream = S_CodecUtilOpen(tmp, codec, loop); if (stream) { if (codec->codec_open(stream)) { stream->status = STREAM_PLAY; return stream; } S_CodecUtilClose(&stream); } codec = codec->next; } return NULL; } else /* use the name as is */ { codec = codecs; while (codec) { if (!q_strcasecmp(ext, codec->ext)) break; codec = codec->next; } if (!codec) { Con_Printf("Unknown extension for %s\n", filename); return NULL; } stream = S_CodecUtilOpen(filename, codec, loop); if (stream) { if (codec->codec_open(stream)) stream->status = STREAM_PLAY; else S_CodecUtilClose(&stream); } return stream; } } qboolean S_CodecForwardStream (snd_stream_t *stream, unsigned int type) { snd_codec_t *codec = codecs; while (codec) { if (type == codec->type) break; codec = codec->next; } if (!codec) return false; stream->codec = codec; return codec->codec_open(stream); } void S_CodecCloseStream (snd_stream_t *stream) { stream->status = STREAM_NONE; stream->codec->codec_close(stream); } int S_CodecRewindStream (snd_stream_t *stream) { return stream->codec->codec_rewind(stream); } int S_CodecJumpToOrder (snd_stream_t *stream, int to) { if (stream->codec->codec_jump) { return stream->codec->codec_jump(stream, to); } return -1; } int S_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { return stream->codec->codec_read(stream, bytes, buffer); } /* Util functions (used by codecs) */ snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec, qboolean loop) { snd_stream_t *stream; FILE *handle; qboolean pak; long length; /* Try to open the file */ length = FS_OpenFile(filename, &handle, NULL); pak = file_from_pak; if (length < 0) { Con_DPrintf("Couldn't open %s\n", filename); return NULL; } /* Allocate a stream, Z_Malloc zeroes its content */ stream = (snd_stream_t *) Z_Malloc(sizeof(snd_stream_t), Z_MAINZONE); stream->codec = codec; stream->loop = loop; stream->fh.file = handle; stream->fh.start = ftell(handle); stream->fh.pos = 0; stream->fh.length = length; stream->fh.pak = stream->pak = pak; q_strlcpy(stream->name, filename, MAX_QPATH); return stream; } void S_CodecUtilClose(snd_stream_t **stream) { fclose((*stream)->fh.file); Z_Free(*stream); *stream = NULL; } int S_CodecIsAvailable (unsigned int type) { snd_codec_t *codec = codecs; while (codec) { if (type == codec->type) return codec->initialized; codec = codec->next; } return -1; } engine/h2shared/snd_codec.h000066400000000000000000000061561444734033100161300ustar00rootroot00000000000000/* * Audio Codecs: Adapted from ioquake3 with changes. * For now, only handles streaming music, not sound effects. * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2005 Stuart Dalton * Copyright (C) 2010-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _SND_CODEC_H_ #define _SND_CODEC_H_ typedef struct snd_info_s { int rate; int bits, width; int channels; int samples; int blocksize; int size; int dataofs; } snd_info_t; typedef enum { STREAM_NONE = -1, STREAM_INIT, STREAM_PAUSE, STREAM_PLAY } stream_status_t; typedef struct snd_codec_s snd_codec_t; typedef struct snd_stream_s { fshandle_t fh; qboolean pak; char name[MAX_QPATH]; /* name of the source file */ snd_info_t info; stream_status_t status; snd_codec_t *codec; /* codec handling this stream */ qboolean loop; void *priv; /* data private to the codec. */ } snd_stream_t; void S_CodecInit (void); void S_CodecShutdown (void); /* Callers of the following S_CodecOpenStream* functions * are reponsible for attaching any path to the filename */ snd_stream_t *S_CodecOpenStreamType (const char *filename, unsigned int type, qboolean loop); /* Decides according to the required type. */ snd_stream_t *S_CodecOpenStreamAny (const char *filename, qboolean loop); /* Decides according to file extension. if the * name has no extension, try all available. */ snd_stream_t *S_CodecOpenStreamExt (const char *filename, qboolean loop); /* Decides according to file extension. the name * MUST have an extension. */ void S_CodecCloseStream (snd_stream_t *stream); int S_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer); int S_CodecRewindStream (snd_stream_t *stream); int S_CodecJumpToOrder (snd_stream_t *stream, int to); snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec, qboolean loop); void S_CodecUtilClose(snd_stream_t **stream); #define CODECTYPE_NONE 0 #define CODECTYPE_MID (1U << 0) #define CODECTYPE_MOD (1U << 1) #define CODECTYPE_FLAC (1U << 2) #define CODECTYPE_WAV (1U << 3) #define CODECTYPE_MP3 (1U << 4) #define CODECTYPE_VORBIS (1U << 5) #define CODECTYPE_OPUS (1U << 6) #define CODECTYPE_UMX (1U << 7) #define CODECTYPE_WAVE CODECTYPE_WAV #define CODECTYPE_MIDI CODECTYPE_MID int S_CodecIsAvailable (unsigned int type); /* return 1 if available, 0 if codec failed init * or -1 if no such codec is present. */ #endif /* _SND_CODEC_H_ */ engine/h2shared/snd_codeci.h000066400000000000000000000036741444734033100163030ustar00rootroot00000000000000/* * Audio Codecs: Adapted from ioquake3 with changes. * For now, only handles streaming music, not sound effects. * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2005 Stuart Dalton * Copyright (C) 2010-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef _SND_CODECI_H_ #define _SND_CODECI_H_ /* Codec internals */ typedef qboolean (*CODEC_INIT)(void); typedef void (*CODEC_SHUTDOWN)(void); typedef qboolean (*CODEC_OPEN)(snd_stream_t *stream); typedef int (*CODEC_READ)(snd_stream_t *stream, int bytes, void *buffer); typedef int (*CODEC_REWIND)(snd_stream_t *stream); typedef int (*CODEC_JUMP)(snd_stream_t *stream, int order); typedef void (*CODEC_CLOSE)(snd_stream_t *stream); struct snd_codec_s { unsigned int type; /* handled data type. (1U << n) */ qboolean initialized; /* init succeedded */ const char *ext; /* expected extension */ CODEC_INIT initialize; CODEC_SHUTDOWN shutdown; CODEC_OPEN codec_open; CODEC_READ codec_read; CODEC_REWIND codec_rewind; CODEC_JUMP codec_jump; CODEC_CLOSE codec_close; snd_codec_t *next; }; qboolean S_CodecForwardStream (snd_stream_t *stream, unsigned int type); /* Forward a stream to another codec of 'type' type. */ #endif /* _SND_CODECI_H_ */ engine/h2shared/snd_dma.c000066400000000000000000000645121444734033100156070ustar00rootroot00000000000000/* * snd_dma.c -- main control for any streaming sound output device * * Copyright (C) 1996-2001 Id Software, Inc. * Copyright (C) 2010-2011 O. Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "hashindex.h" #include "cfgfile.h" #include "snd_sys.h" #include "snd_codec.h" #include "bgmusic.h" static snd_driver_t *qsnd_driver; static void S_Play (void); static void S_PlayVol (void); static void S_ToggleMute(void); static void S_VolumeDown(void); static void S_VolumeUp(void); static void S_SoundList (void); static void S_Update_ (void); void S_StopAllSounds (qboolean clear); static void S_StopAllSoundsC (void); #if defined(H2W) /* HexenWorld hack. */ #define viewentity playernum+1 #endif // ======================================================================= // Internal sound data & structures // ======================================================================= channel_t snd_channels[MAX_CHANNELS]; int total_channels; static int snd_blocked = 0; static qboolean snd_initialized = false; static dma_t sn; volatile dma_t *shm = NULL; vec3_t listener_origin; vec3_t listener_forward; vec3_t listener_right; vec3_t listener_up; #define sound_nominal_clip_dist 1000.0 int soundtime; // sample PAIRS int paintedtime; // sample PAIRS int s_rawend; portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES]; #define MAX_SFX 512 static sfx_t *known_sfx = NULL; // hunk allocated [MAX_SFX] static int num_sfx; static hashindex_t hash_sfx; static sfx_t *ambient_sfx[NUM_AMBIENTS]; static qboolean sound_started = false; int desired_speed = 22050; int desired_bits = 16; int desired_channels = 2; const int tryrates[] = { 11025, 22050, 44100, 48000, 96000, 16000, 24000, 8000 }; const int MAX_TRYRATES = sizeof(tryrates)/sizeof(tryrates[0]); cvar_t bgmvolume = {"bgmvolume", "1", CVAR_ARCHIVE}; cvar_t bgmtype = {"bgmtype", "cd", CVAR_ARCHIVE}; // cd or midi cvar_t sfxvolume = {"volume", "0.7", CVAR_ARCHIVE}; cvar_t precache = {"precache", "1", CVAR_NONE}; cvar_t loadas8bit = {"loadas8bit", "0", CVAR_NONE}; static cvar_t sfx_mutedvol = {"sfx_mutedvol", "0", CVAR_ARCHIVE}; static cvar_t bgm_mutedvol = {"bgm_mutedvol", "0", CVAR_ARCHIVE}; static cvar_t nosound = {"nosound", "0", CVAR_NONE}; static cvar_t ambient_level = {"ambient_level", "0.3", CVAR_NONE}; static cvar_t ambient_fade = {"ambient_fade", "100", CVAR_NONE}; static cvar_t snd_noextraupdate = {"snd_noextraupdate", "0", CVAR_NONE}; static cvar_t snd_show = {"snd_show", "0", CVAR_NONE}; static cvar_t _snd_mixahead = {"_snd_mixahead", "0.1", CVAR_ARCHIVE}; static void S_SoundInfo_f (void) { if (!sound_started || !shm) { Con_Printf ("sound system not started\n"); return; } Con_Printf("Driver: %s\n", qsnd_driver->snddrv_name); Con_Printf("%d bit, %s, %d Hz\n", shm->samplebits, (shm->channels == 2) ? "stereo" : "mono", shm->speed); Con_Printf("%5d samples\n", shm->samples); Con_Printf("%5d samplepos\n", shm->samplepos); Con_Printf("%5d submission_chunk\n", shm->submission_chunk); Con_Printf("%5d total_channels\n", total_channels); Con_Printf("%p dma buffer\n", shm->buffer); } static void S_ProcessCmdline (void) { int i, tmp; tmp = COM_CheckParm("-sndspeed"); if (tmp != 0 && tmp < com_argc - 1) { /* I won't rely on users' precision in typing or their needs here. If you know what you're doing, then change this. */ tmp = atoi(com_argv[tmp + 1]); for (i = 0; i < MAX_TRYRATES; i++) { if (tmp == tryrates[i]) { desired_speed = tmp; break; } } } tmp = COM_CheckParm("-sndbits"); if (tmp != 0 && tmp < com_argc - 1) { tmp = atoi(com_argv[tmp + 1]); if (tmp == 16 || tmp == 8) desired_bits = tmp; } tmp = COM_CheckParm("-sndmono"); if (tmp != 0) desired_channels = 1; } static void SND_Callback_sfxvolume (cvar_t *var) { SND_InitScaletable (); } /* ================ S_Startup ================ */ void S_Startup (void) { snd_driver_t *driver; if (!snd_initialized) return; S_GetDriverList(&qsnd_driver); driver = qsnd_driver; while (driver) { if (driver->userpreferred) { qsnd_driver = driver; /* don't try other available drivers upon failure */ break; } driver = driver->next; } driver = qsnd_driver; while (driver) { sound_started = driver->Init(&sn); if (sound_started) break; if (driver->userpreferred) break; if (driver->snddrv_id == SNDDRV_ID_NULL) /* ->next is NULL already */ break; driver = driver->next; } if (!sound_started) { if (!driver || driver->snddrv_id != SNDDRV_ID_NULL) Con_Printf("Failed initializing sound\n"); S_GetNullDriver(&qsnd_driver); /* just in case. */ } else { qsnd_driver = driver; /* set the active driver */ Con_Printf("Audio: %d bit, %s, %d Hz, using %s\n", shm->samplebits, (shm->channels == 2) ? "stereo" : "mono", shm->speed, qsnd_driver->snddrv_name); } } /* ================ S_Init ================ */ static const char *read_vars[] = { "bgmvolume", "volume" }; #define num_readvars (int)(sizeof(read_vars) / sizeof(read_vars[0])) void S_Init (void) { int i; if (snd_initialized) { Con_Printf("Sound is already initialized\n"); return; } S_DriversInit(); S_GetNullDriver(&qsnd_driver); Cvar_RegisterVariable(&precache); Cvar_RegisterVariable(&bgmtype); Cvar_RegisterVariable(&nosound); Cvar_RegisterVariable(&sfxvolume); Cvar_RegisterVariable(&sfx_mutedvol); Cvar_RegisterVariable(&loadas8bit); Cvar_RegisterVariable(&bgmvolume); Cvar_RegisterVariable(&bgm_mutedvol); Cvar_RegisterVariable(&ambient_level); Cvar_RegisterVariable(&ambient_fade); Cvar_RegisterVariable(&snd_noextraupdate); Cvar_RegisterVariable(&snd_show); Cvar_RegisterVariable(&_snd_mixahead); if (safemode || COM_CheckParm("-nosound") || COM_CheckParm("-s")) return; Con_Printf("\nSound Initialization\n"); Cmd_AddCommand("play", S_Play); Cmd_AddCommand("playvol", S_PlayVol); Cmd_AddCommand("stopsound", S_StopAllSoundsC); Cmd_AddCommand("soundlist", S_SoundList); Cmd_AddCommand("soundinfo", S_SoundInfo_f); Cmd_AddCommand("mute", S_ToggleMute); Cmd_AddCommand("volumeup", S_VolumeUp); Cmd_AddCommand("volumedown", S_VolumeDown); if (host_parms->memsize < 0x800000) { Cvar_SetQuick (&loadas8bit, "1"); Con_Printf ("loading all sounds as 8bit\n"); } // perform an early read of config.cfg CFG_ReadCvars (read_vars, num_readvars); // check for command line overrides CFG_ReadCvarOverrides (read_vars, num_readvars); if (sfxvolume.value < 0) Cvar_SetQuick(&sfxvolume, "0"); else if (sfxvolume.value > 1) Cvar_SetQuick(&sfxvolume, "1"); if (bgmvolume.value < 0) Cvar_SetQuick(&bgmvolume, "0"); else if (bgmvolume.value > 1) Cvar_SetQuick(&bgmvolume, "1"); Cvar_SetCallback(&sfxvolume, SND_Callback_sfxvolume); // lock the early-read cvars until Host_Init is finished for (i = 0; i < num_readvars; i++) Cvar_LockVar (read_vars[i]); SND_InitScaletable (); known_sfx = (sfx_t *) Hunk_AllocName (MAX_SFX*sizeof(sfx_t), "sfx_t"); num_sfx = 0; Hash_Allocate (&hash_sfx, MAX_SFX); snd_initialized = true; S_ProcessCmdline (); S_Startup (); if (sound_started == 0) return; // provides a tick sound until washed clean // if (shm->buffer) // shm->buffer[4] = shm->buffer[5] = 0x7f; // force a pop for debugging ambient_sfx[AMBIENT_WATER] = S_PrecacheSound ("ambience/water1.wav"); ambient_sfx[AMBIENT_SKY] = S_PrecacheSound ("ambience/wind2.wav"); S_CodecInit (); S_StopAllSounds (true); } // ======================================================================= // Shutdown sound engine // ======================================================================= void S_Shutdown (void) { if (!sound_started) return; sound_started = 0; snd_blocked = 0; S_CodecShutdown(); qsnd_driver->Shutdown(); shm = NULL; } // ======================================================================= // Load a sound // ======================================================================= /* ================== S_FindName ================== */ static sfx_t *S_FindName (const char *name) { int i, key; sfx_t *sfx; if (!name) Sys_Error ("%s: NULL", __thisfunc__); if (strlen(name) >= MAX_QPATH) Sys_Error ("Sound name too long: %s", name); // see if already loaded key = Hash_GenerateKeyString (&hash_sfx, name, true); for (i = Hash_First(&hash_sfx, key); i != -1; i = Hash_Next(&hash_sfx, i)) { sfx = &known_sfx[i]; if (!strcmp(name, sfx->name)) { return sfx; } } if (num_sfx == MAX_SFX) Sys_Error ("%s: out of sfx_t", __thisfunc__); Hash_Add (&hash_sfx, key, num_sfx); sfx = &known_sfx[num_sfx]; q_strlcpy (sfx->name, name, MAX_QPATH); num_sfx++; return sfx; } /* ================== S_TouchSound ================== */ void S_TouchSound (const char *name) { sfx_t *sfx; if (!sound_started) return; sfx = S_FindName (name); Cache_Check (&sfx->cache); } /* ================== S_PrecacheSound ================== */ sfx_t *S_PrecacheSound (const char *name) { sfx_t *sfx; if (!sound_started || nosound.integer) return NULL; sfx = S_FindName (name); // cache it in if (precache.integer) S_LoadSound (sfx); return sfx; } //============================================================================= /* ================= SND_PickChannel picks a channel based on priorities, empty slots, number of channels ================= */ channel_t *SND_PickChannel (int entnum, int entchannel) { int ch_idx; int first_to_die; int life_left; // Check for replacement sound, or find the best one to replace first_to_die = -1; life_left = 0x7fffffff; for (ch_idx = NUM_AMBIENTS; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS; ch_idx++) { if (entchannel != 0 // channel 0 never overrides && snd_channels[ch_idx].entnum == entnum && (snd_channels[ch_idx].entchannel == entchannel || entchannel == -1) ) { // always override sound from same entity first_to_die = ch_idx; break; } // don't let monster sounds override player sounds if (snd_channels[ch_idx].entnum == cl.viewentity && entnum != cl.viewentity && snd_channels[ch_idx].sfx) continue; if (snd_channels[ch_idx].end - paintedtime < life_left) { life_left = snd_channels[ch_idx].end - paintedtime; first_to_die = ch_idx; } } if (first_to_die == -1) return NULL; if (snd_channels[first_to_die].sfx) snd_channels[first_to_die].sfx = NULL; return &snd_channels[first_to_die]; } /* ================= SND_Spatialize spatializes a channel ================= */ void SND_Spatialize (channel_t *ch) { vec_t dot; vec_t dist; vec_t lscale, rscale, scale; vec3_t source_vec; // anything coming from the view entity will always be full volume if (ch->entnum == cl.viewentity) { ch->leftvol = ch->master_vol; ch->rightvol = ch->master_vol; return; } // calculate stereo seperation and distance attenuation VectorSubtract(ch->origin, listener_origin, source_vec); dist = VectorNormalizeFast(source_vec) * ch->dist_mult; dot = DotProduct(listener_right, source_vec); if (shm->channels == 1) { rscale = 1.0; lscale = 1.0; } else { rscale = 1.0 + dot; lscale = 1.0 - dot; } // add in distance effect scale = (1.0 - dist) * rscale; ch->rightvol = (int) (ch->master_vol * scale); if (ch->rightvol < 0) ch->rightvol = 0; scale = (1.0 - dist) * lscale; ch->leftvol = (int) (ch->master_vol * scale); if (ch->leftvol < 0) ch->leftvol = 0; } // ======================================================================= // Start a sound effect // ======================================================================= void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation) { channel_t *target_chan, *check; sfxcache_t *sc; int ch_idx; int skip; // qboolean skip_dist_check = false; if (!sound_started) return; if (!sfx) return; if (nosound.integer) return; // pick a channel to play on target_chan = SND_PickChannel(entnum, entchannel); if (!target_chan) return; if (attenuation == 4) // Looping sound- always play { // skip_dist_check = true; attenuation = 1; // was 3 - static } // spatialize memset (target_chan, 0, sizeof(*target_chan)); VectorCopy(origin, target_chan->origin); target_chan->dist_mult = attenuation / sound_nominal_clip_dist; target_chan->master_vol = (int) (fvol * 255); target_chan->entnum = entnum; target_chan->entchannel = entchannel; SND_Spatialize(target_chan); #if 0 /* Allow initially silent channels to be active */ /* because the player might teleport to them. */ if (!skip_dist_check) { if (!target_chan->leftvol && !target_chan->rightvol) return; // not audible at all } #endif // new channel sc = S_LoadSound (sfx); if (!sc) { target_chan->sfx = NULL; return; // couldn't load the sound's data } target_chan->sfx = sfx; target_chan->pos = 0.0; target_chan->end = paintedtime + sc->length; // if an identical sound has also been started this frame, offset the pos // a bit to keep it from just making the first one louder check = &snd_channels[NUM_AMBIENTS]; for (ch_idx = NUM_AMBIENTS; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS; ch_idx++, check++) { if (check == target_chan) continue; if (check->sfx == sfx && !check->pos) { /* skip = rand () % (int)(0.1 * shm->speed); if (skip >= target_chan->end) skip = target_chan->end - 1; */ /* LordHavoc: fixed skip calculations */ skip = 0.1 * shm->speed; /* 0.1 * sc->speed */ if (skip > sc->length) skip = sc->length; if (skip > 0) skip = rand() % skip; target_chan->pos += skip; target_chan->end -= skip; break; } } } void S_StopSound (int entnum, int entchannel) { int i; for (i = NUM_AMBIENTS; i < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS; i++) { if (snd_channels[i].entnum == entnum && ((!entchannel) || snd_channels[i].entchannel == entchannel)) // 0 matches any { snd_channels[i].end = 0; snd_channels[i].sfx = NULL; if (entchannel) return; //got a match, not looking for more. } } } void S_UpdateSoundPos (int entnum, int entchannel, vec3_t origin) { int i; for (i = NUM_AMBIENTS; i < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS; i++) { if (snd_channels[i].entnum == entnum && snd_channels[i].entchannel == entchannel) { VectorCopy(origin, snd_channels[i].origin); return; } } } void S_StopAllSounds (qboolean clear) { int i; if (!sound_started) return; total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; // no statics for (i = 0; i < MAX_CHANNELS; i++) { if (snd_channels[i].sfx) snd_channels[i].sfx = NULL; } memset(snd_channels, 0, MAX_CHANNELS * sizeof(channel_t)); if (clear) S_ClearBuffer (); } static void S_StopAllSoundsC (void) { S_StopAllSounds (true); } void S_ClearBuffer (void) { int clear; if (!sound_started || !shm) return; qsnd_driver->LockBuffer (); if (! shm->buffer) return; s_rawend = 0; if (shm->samplebits == 8 && !shm->signed8) clear = 0x80; else clear = 0; memset(shm->buffer, clear, shm->samples * shm->samplebits / 8); qsnd_driver->Submit (); } /* ================= S_StaticSound ================= */ void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) { channel_t *ss; sfxcache_t *sc; if (!sfx) return; if (total_channels == MAX_CHANNELS) { Con_Printf ("%s: MAX_CHANNELS reached\n", __thisfunc__); // Con_Printf (" failed at (%.2f, %.2f, %.2f)\n",origin[0],origin[1],origin[2]); return; } ss = &snd_channels[total_channels]; total_channels++; sc = S_LoadSound (sfx); if (!sc) return; if (sc->loopstart == -1) { Con_Printf ("Sound %s not looped\n", sfx->name); return; } ss->sfx = sfx; VectorCopy (origin, ss->origin); ss->master_vol = (int)vol; ss->dist_mult = (attenuation / 64) / sound_nominal_clip_dist; ss->end = paintedtime + sc->length; SND_Spatialize (ss); } //============================================================================= /* =================== S_UpdateAmbientSounds =================== */ static void S_UpdateAmbientSounds (void) { mleaf_t *l; int vol, ambient_channel; channel_t *chan; // calc ambient sound levels if (!cl.worldmodel || cls.state != ca_active) return; l = Mod_PointInLeaf (listener_origin, cl.worldmodel); if (!l || !ambient_level.value) { for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS; ambient_channel++) snd_channels[ambient_channel].sfx = NULL; return; } for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS; ambient_channel++) { chan = &snd_channels[ambient_channel]; chan->sfx = ambient_sfx[ambient_channel]; vol = (int) (ambient_level.value * l->ambient_sound_level[ambient_channel]); if (vol < 8) vol = 0; // don't adjust volume too fast if (chan->master_vol < vol) { chan->master_vol += (int) (host_frametime * ambient_fade.value); if (chan->master_vol > vol) chan->master_vol = vol; } else if (chan->master_vol > vol) { chan->master_vol -= (int) (host_frametime * ambient_fade.value); if (chan->master_vol < vol) chan->master_vol = vol; } chan->leftvol = chan->rightvol = chan->master_vol; } } /* =================== S_RawSamples (from QuakeII) Streaming music support. Byte swapping of data must be handled by the codec. Expects data in signed 16 bit, or unsigned 8 bit format. =================== */ void S_RawSamples (int samples, int rate, int width, int channels, byte *data, float volume) { int i; int src, dst; float scale; int intVolume; if (s_rawend < paintedtime) s_rawend = paintedtime; scale = (float) rate / shm->speed; intVolume = (int) (256 * volume); if (channels == 2 && width == 2) { for (i = 0; ; i++) { src = i * scale; if (src >= samples) break; dst = s_rawend & (MAX_RAW_SAMPLES - 1); s_rawend++; s_rawsamples [dst].left = ((short *) data)[src * 2] * intVolume; s_rawsamples [dst].right = ((short *) data)[src * 2 + 1] * intVolume; } } else if (channels == 1 && width == 2) { for (i = 0; ; i++) { src = i * scale; if (src >= samples) break; dst = s_rawend & (MAX_RAW_SAMPLES - 1); s_rawend++; s_rawsamples [dst].left = ((short *) data)[src] * intVolume; s_rawsamples [dst].right = ((short *) data)[src] * intVolume; } } else if (channels == 2 && width == 1) { intVolume *= 256; for (i = 0; ; i++) { src = i * scale; if (src >= samples) break; dst = s_rawend & (MAX_RAW_SAMPLES - 1); s_rawend++; // s_rawsamples [dst].left = ((signed char *) data)[src * 2] * intVolume; // s_rawsamples [dst].right = ((signed char *) data)[src * 2 + 1] * intVolume; s_rawsamples [dst].left = (((byte *) data)[src * 2] - 128) * intVolume; s_rawsamples [dst].right = (((byte *) data)[src * 2 + 1] - 128) * intVolume; } } else if (channels == 1 && width == 1) { intVolume *= 256; for (i = 0; ; i++) { src = i * scale; if (src >= samples) break; dst = s_rawend & (MAX_RAW_SAMPLES - 1); s_rawend++; // s_rawsamples [dst].left = ((signed char *) data)[src] * intVolume; // s_rawsamples [dst].right = ((signed char *) data)[src] * intVolume; s_rawsamples [dst].left = (((byte *) data)[src] - 128) * intVolume; s_rawsamples [dst].right = (((byte *) data)[src] - 128) * intVolume; } } } /* ============ S_Update Called once each time through the main loop ============ */ void S_Update (vec3_t origin, vec3_t forward, vec3_t right, vec3_t up) { int i, j; int total; channel_t *ch; channel_t *combine; if (!sound_started || (snd_blocked > 0)) return; VectorCopy(origin, listener_origin); VectorCopy(forward, listener_forward); VectorCopy(right, listener_right); VectorCopy(up, listener_up); // update general area ambient sound sources S_UpdateAmbientSounds (); combine = NULL; // update spatialization for static and dynamic sounds ch = snd_channels + NUM_AMBIENTS; for (i = NUM_AMBIENTS; i < total_channels; i++, ch++) { if (!ch->sfx) continue; SND_Spatialize(ch); // respatialize channel if (!ch->leftvol && !ch->rightvol) continue; // try to combine static sounds with a previous channel of the same // sound effect so we don't mix five torches every frame if (i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS) { // see if it can just use the last one if (combine && combine->sfx == ch->sfx) { combine->leftvol += ch->leftvol; combine->rightvol += ch->rightvol; ch->leftvol = ch->rightvol = 0; continue; } // search for one combine = snd_channels + MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; for (j = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; j < i; j++, combine++) { if (combine->sfx == ch->sfx) break; } if (j == total_channels) { combine = NULL; } else { if (combine != ch) { combine->leftvol += ch->leftvol; combine->rightvol += ch->rightvol; ch->leftvol = ch->rightvol = 0; } continue; } } } // // debugging output // if (snd_show.integer) { total = 0; ch = snd_channels; for (i = 0; i < total_channels; i++, ch++) { if (ch->sfx && (ch->leftvol || ch->rightvol) ) { // Con_Printf ("%3i %3i %s\n", ch->leftvol, ch->rightvol, ch->sfx->name); total++; } } Con_Printf ("----(%i)----\n", total); } // add raw data from streamed samples // BGM_Update(); // moved to the main loop just before S_Update () // mix some sound S_Update_(); } static void GetSoundtime (void) { int samplepos; static int buffers; static int oldsamplepos; int fullsamples; fullsamples = shm->samples / shm->channels; // it is possible to miscount buffers if it has wrapped twice between // calls to S_Update. Oh well. samplepos = qsnd_driver->GetDMAPos(); if (samplepos < oldsamplepos) { buffers++; // buffer wrapped if (paintedtime > 0x40000000) { // time to chop things off to avoid 32 bit limits buffers = 0; paintedtime = fullsamples; S_StopAllSounds (true); } } oldsamplepos = samplepos; soundtime = buffers*fullsamples + samplepos/shm->channels; } void S_ExtraUpdate (void) { IN_Accumulate (); if (snd_noextraupdate.integer) return; // don't pollute timings S_Update_(); } static void S_Update_ (void) { unsigned int endtime; int samps; if (!sound_started || (snd_blocked > 0)) return; qsnd_driver->LockBuffer (); if (! shm->buffer) return; // Updates DMA time GetSoundtime(); // check to make sure that we haven't overshot if (paintedtime < soundtime) { // Con_Printf ("%s : overflow\n", __thisfunc__); paintedtime = soundtime; } // mix ahead of current position endtime = soundtime + (unsigned int)(_snd_mixahead.value * shm->speed); samps = shm->samples >> (shm->channels - 1); endtime = q_min(endtime, (unsigned int)(soundtime + samps)); S_PaintChannels (endtime); qsnd_driver->Submit (); } void S_BlockSound (void) { /* FIXME: do we really need the blocking at the * driver level? */ if (sound_started && ++snd_blocked == 1) { S_ClearBuffer (); if (shm) qsnd_driver->BlockSound(); } } void S_UnblockSound (void) { if (!sound_started || !snd_blocked) return; if (--snd_blocked == 0) { qsnd_driver->UnblockSound(); S_ClearBuffer (); } } /* =============================================================================== console functions =============================================================================== */ // S.A. volume procs static void S_ToggleMute (void) { if (sfx_mutedvol.value || bgm_mutedvol.value) { Cvar_SetValueQuick(&sfxvolume, sfx_mutedvol.value); Cvar_SetValueQuick(&bgmvolume, bgm_mutedvol.value); Cvar_SetQuick(&sfx_mutedvol, "0"); Cvar_SetQuick(&bgm_mutedvol, "0"); if (sfxvolume.value || bgmvolume.value) Con_Printf ("Unmuted\n"); } else { Cvar_SetValueQuick(&sfx_mutedvol, sfxvolume.value); Cvar_SetValueQuick(&bgm_mutedvol, bgmvolume.value); Cvar_SetQuick(&sfxvolume, "0"); Cvar_SetQuick(&bgmvolume, "0"); if (sfx_mutedvol.value || bgm_mutedvol.value) Con_Printf ("Muted\n"); } } static void S_VolumeDown (void) { if (sfxvolume.value >= 0.1) Cvar_SetValueQuick(&sfxvolume, sfxvolume.value - 0.1); if (bgmvolume.value >= 0.1) Cvar_SetValueQuick(&bgmvolume, bgmvolume.value - 0.1); Con_Printf ("Volume is %3.1f\n", sfxvolume.value); } static void S_VolumeUp (void) { if (sfxvolume.value <= 0.9) Cvar_SetValueQuick(&sfxvolume, sfxvolume.value + 0.1); if (bgmvolume.value <= 0.9) Cvar_SetValueQuick(&bgmvolume, bgmvolume.value + 0.1); Con_Printf ("Volume is %3.1f\n", sfxvolume.value); } static void S_Play (void) { static int hash = 345; int i; char name[256]; sfx_t *sfx; i = 1; while (i < Cmd_Argc()) { q_strlcpy(name, Cmd_Argv(i), sizeof(name)); if (!strrchr(Cmd_Argv(i), '.')) { q_strlcat(name, ".wav", sizeof(name)); } sfx = S_PrecacheSound(name); S_StartSound(hash++, 0, sfx, listener_origin, 1.0, 1.0); i++; } } static void S_PlayVol (void) { static int hash = 543; int i; float vol; char name[256]; sfx_t *sfx; i = 1; while (i < Cmd_Argc()) { q_strlcpy(name, Cmd_Argv(i), sizeof(name)); if (!strrchr(Cmd_Argv(i), '.')) { q_strlcat(name, ".wav", sizeof(name)); } sfx = S_PrecacheSound(name); vol = atof(Cmd_Argv(i + 1)); S_StartSound(hash++, 0, sfx, listener_origin, vol, 1.0); i += 2; } } static void S_SoundList (void) { int i; sfx_t *sfx; sfxcache_t *sc; int size, total; total = 0; for (sfx = known_sfx, i = 0; i < num_sfx; i++, sfx++) { sc = (sfxcache_t *) Cache_Check (&sfx->cache); if (!sc) continue; size = sc->length*sc->width*(sc->stereo + 1); total += size; if (sc->loopstart >= 0) Con_Printf ("L"); else Con_Printf (" "); Con_Printf("(%2db) %6i : %s\n", sc->width*8, size, sfx->name); } Con_Printf ("Total resident: %i\n", total); } void S_LocalSound (const char *name) { sfx_t *sfx; if (nosound.integer) return; if (!sound_started) return; sfx = S_PrecacheSound (name); if (!sfx) { Con_Printf ("%s: can't cache %s\n", __thisfunc__, name); return; } S_StartSound (cl.viewentity, -1, sfx, vec3_origin, 1, 1); } void S_ClearPrecache (void) { } void S_BeginPrecaching (void) { } void S_EndPrecaching (void) { } engine/h2shared/snd_dsound.c000066400000000000000000000247161444734033100163440ustar00rootroot00000000000000/* snd_dsound.c * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "snd_sys.h" #if HAVE_WIN_DX_SOUND #include "winquake.h" #include "snd_dsound.h" #include #include static char s_ds_driver[] = "DirectSound"; /* DirectSound : */ #ifndef DSBSIZE_MIN #define DSBSIZE_MIN 4 #endif #ifndef DSBSIZE_MAX #define DSBSIZE_MAX 0x0FFFFFFF #endif static LPDIRECTSOUND pDS; static LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf; #if defined(DX_DLSYM) /* dynamic loading of dsound symbols */ static HINSTANCE hInstDS; static HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter); #else /* ! DX_DLSYM : we're linked to dsound */ #define pDirectSoundCreate DirectSoundCreate #endif /* DX_DLSYM */ static qboolean primary_format_set; static int sample16; static int ds_sbuf_size; static HPSTR lpData; static DWORD gSndBufSize; static MMTIME mmstarttime; /* ================== FreeSound ================== */ static void FreeSound (void) { if (pDSBuf) { IDirectSoundBuffer_Stop(pDSBuf); IDirectSound_Release(pDSBuf); } // only release primary buffer if it's not also the mixing buffer we just released if (pDSPBuf && (pDSBuf != pDSPBuf)) { IDirectSound_Release(pDSPBuf); } if (pDS) { IDirectSound_SetCooperativeLevel(pDS, mainwindow, DSSCL_NORMAL); IDirectSound_Release(pDS); } pDS = NULL; pDSBuf = NULL; pDSPBuf = NULL; lpData = NULL; } /* ================== SNDDMA_InitDirect Direct-Sound support ================== */ static qboolean S_DS_Init (dma_t *dma) { DSBUFFERDESC dsbuf; DSBCAPS dsbcaps; DWORD dwSize, dwWrite; DSCAPS dscaps; WAVEFORMATEX format, pformat; HRESULT hresult; int reps; memset((void *) dma, 0, sizeof(dma_t)); shm = dma; shm->channels = desired_channels; shm->samplebits = desired_bits; shm->speed = desired_speed; /* Calculate the DS buffer size to store 2 secs * of data, round up to the next power of 2. */ ds_sbuf_size = 1 << (Q_log2((desired_bits >> 3) * (desired_speed << 1)) + 1); memset (&format, 0, sizeof(format)); format.wFormatTag = WAVE_FORMAT_PCM; format.nChannels = shm->channels; format.wBitsPerSample = shm->samplebits; format.nSamplesPerSec = shm->speed; format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; format.cbSize = 0; format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; #if defined(DX_DLSYM) if (!hInstDS) { hInstDS = LoadLibrary("dsound.dll"); if (hInstDS == NULL) { Con_SafePrintf ("Couldn't load dsound.dll\n"); return false; } pDirectSoundCreate = (HRESULT (WINAPI *)(GUID FAR *, LPDIRECTSOUND FAR *, IUnknown FAR *)) GetProcAddress(hInstDS,"DirectSoundCreate"); if (!pDirectSoundCreate) { Con_SafePrintf ("Couldn't get DS proc addr\n"); return false; } } #endif /* DX_DLSYM */ hresult = pDirectSoundCreate(NULL, &pDS, NULL); if (hresult != DS_OK) { if (hresult != DSERR_ALLOCATED) { Con_SafePrintf ("DirectSound create failed\n"); return false; } Con_SafePrintf ("DirectSoundCreate failure, hardware already in use\n"); return false; } dscaps.dwSize = sizeof(dscaps); if (DS_OK != IDirectSound_GetCaps(pDS, &dscaps)) { Con_SafePrintf ("Couldn't get DS caps\n"); } if (dscaps.dwFlags & DSCAPS_EMULDRIVER) { Con_SafePrintf ("No DirectSound driver installed\n"); goto fail; } // if (DS_OK != IDirectSound_SetCooperativeLevel(pDS, mainwindow, DSSCL_EXCLUSIVE)) /* Pa3PyX: Some MIDI synthesizers are software and require access to waveOut; so if we set the coop level to exclusive, MIDI will fail to init because the device is locked. We use priority level instead. That way we don't lock out software synths and other apps, but can still set the sound buffer format. */ if (DS_OK != IDirectSound_SetCooperativeLevel(pDS, mainwindow, DSSCL_PRIORITY)) { Con_SafePrintf ("Set coop level failed\n"); goto fail; } // get access to the primary buffer, if possible, so we can set the // sound hardware format memset (&dsbuf, 0, sizeof(dsbuf)); dsbuf.dwSize = sizeof(DSBUFFERDESC); dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER; dsbuf.dwBufferBytes = 0; dsbuf.lpwfxFormat = NULL; memset(&dsbcaps, 0, sizeof(dsbcaps)); dsbcaps.dwSize = sizeof(dsbcaps); primary_format_set = false; if (!COM_CheckParm ("-snoforceformat")) { if (DS_OK == IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL)) { pformat = format; if (DS_OK != IDirectSoundBuffer_SetFormat(pDSPBuf, &pformat)) { Con_SafePrintf ("Set primary sound buffer format: no\n"); } else { Con_SafePrintf ("Set primary sound buffer format: yes\n"); primary_format_set = true; } } } if (!primary_format_set || !COM_CheckParm ("-primarysound")) { // create the secondary buffer we'll actually work with memset (&dsbuf, 0, sizeof(dsbuf)); dsbuf.dwSize = sizeof(DSBUFFERDESC); dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE; if (ds_sbuf_size < DSBSIZE_MIN) ds_sbuf_size = 1 << (Q_log2(DSBSIZE_MIN) + 1); if (ds_sbuf_size > DSBSIZE_MAX) ds_sbuf_size = 1 << Q_log2(DSBSIZE_MAX); dsbuf.dwBufferBytes = ds_sbuf_size; dsbuf.lpwfxFormat = &format; memset(&dsbcaps, 0, sizeof(dsbcaps)); dsbcaps.dwSize = sizeof(dsbcaps); if (DS_OK != IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL)) { Con_SafePrintf ("DS:CreateSoundBuffer Failed"); goto fail; } shm->channels = format.nChannels; shm->samplebits = format.wBitsPerSample; shm->speed = format.nSamplesPerSec; if (DS_OK != IDirectSound_GetCaps(pDSBuf, &dsbcaps)) { Con_SafePrintf ("DS:GetCaps failed\n"); goto fail; } Con_SafePrintf ("Using secondary sound buffer\n"); } else { if (DS_OK != IDirectSound_SetCooperativeLevel(pDS, mainwindow, DSSCL_WRITEPRIMARY)) { Con_SafePrintf ("Set coop level failed\n"); goto fail; } if (DS_OK != IDirectSound_GetCaps(pDSPBuf, &dsbcaps)) { Con_Printf ("DS:GetCaps failed\n"); return false; } pDSBuf = pDSPBuf; Con_SafePrintf ("Using primary sound buffer\n"); } // Make sure mixer is active IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); Con_SafePrintf ("%lu bytes in sound buffer\n", (unsigned long)dsbcaps.dwBufferBytes); gSndBufSize = dsbcaps.dwBufferBytes; // initialize the buffer reps = 0; while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (LPVOID *) (HPSTR) &lpData, &dwSize, NULL, NULL, 0)) != DS_OK) { if (hresult != DSERR_BUFFERLOST) { Con_SafePrintf ("%s: DS::Lock Sound Buffer Failed\n", __thisfunc__); goto fail; } if (++reps > 10000) { Con_SafePrintf ("%s: DS: couldn't restore buffer\n", __thisfunc__); goto fail; } } memset(lpData, 0, dwSize); // lpData[4] = lpData[5] = 0x7f; // force a pop for debugging IDirectSoundBuffer_Unlock(pDSBuf, lpData, dwSize, NULL, 0); /* we don't want anyone to access the buffer directly w/o locking it first. */ lpData = NULL; IDirectSoundBuffer_Stop(pDSBuf); IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite); IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); shm->samples = gSndBufSize / (shm->samplebits / 8); shm->samplepos = 0; shm->submission_chunk = 1; shm->buffer = (unsigned char *) lpData; sample16 = (shm->samplebits / 8) - 1; Con_SafePrintf ("DirectSound initialized\n"); return true; fail: FreeSound (); Con_SafePrintf ("DirectSound failed to init\n"); return false; } /* ============== SNDDMA_GetDMAPos return the current sample position (in mono samples read) inside the recirculating dma buffer, so the mixing code will know how many sample are required to fill it up. =============== */ static int S_DS_GetDMAPos (void) { MMTIME mmtime; int s; DWORD dwWrite; mmtime.wType = TIME_SAMPLES; IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite); s = mmtime.u.sample - mmstarttime.u.sample; s >>= sample16; s &= (shm->samples - 1); return s; } /* ============== SNDDMA_LockBuffer Makes sure dma buffer is valid =============== */ static DWORD locksize; static void S_DS_LockBuffer (void) { if (pDSBuf) { void *pData; int reps; HRESULT hresult; DWORD dwStatus; reps = 0; shm->buffer = NULL; if (IDirectSoundBuffer_GetStatus(pDSBuf, &dwStatus) != DS_OK) Con_Printf ("Couldn't get sound buffer status\n"); if (dwStatus & DSBSTATUS_BUFFERLOST) IDirectSoundBuffer_Restore(pDSBuf); if (!(dwStatus & DSBSTATUS_PLAYING)) IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, (void **) &pData, &locksize, NULL, NULL, 0)) != DS_OK) { if (hresult != DSERR_BUFFERLOST) { Con_Printf ("%s: DS::Lock Sound Buffer Failed\n", __thisfunc__); S_Shutdown (); return; } if (++reps > 10000) { Con_Printf ("%s: DS: couldn't restore buffer\n", __thisfunc__); S_Shutdown (); return; } } shm->buffer = (unsigned char *) pData; } } /* ============== SNDDMA_Submit Unlock the dma buffer / Send sound to the device =============== */ static void S_DS_Submit (void) { if (pDSBuf) IDirectSoundBuffer_Unlock(pDSBuf, shm->buffer, locksize, NULL, 0); } /* ================== SNDDMA_BlockSound ================== */ static void S_DS_BlockSound (void) { // DirectSound takes care of blocking itself } /* ================== SNDDMA_UnblockSound ================== */ static void S_DS_UnblockSound (void) { } /* ============== SNDDMA_Shutdown Reset the sound device for exiting =============== */ static void S_DS_Shutdown (void) { FreeSound (); #if defined(DX_DLSYM) if (hInstDS) { FreeLibrary(hInstDS); hInstDS = NULL; } #endif /* DX_DLSYM */ } snd_driver_t snddrv_dsound = { S_DS_Init, S_DS_Shutdown, S_DS_GetDMAPos, S_DS_LockBuffer, S_DS_Submit, S_DS_BlockSound, S_DS_UnblockSound, s_ds_driver, SNDDRV_ID_DSOUND, false, NULL }; #endif /* HAVE_WIN_DX_SOUND */ engine/h2shared/snd_dsound.h000066400000000000000000000003431444734033100163370ustar00rootroot00000000000000/* Sound support using Windows DirectSound. */ #if !defined(__HX2_SND_DSOUND) #define __HX2_SND_DSOUND #if HAVE_WIN_DX_SOUND extern snd_driver_t snddrv_dsound; #endif /* HAVE_WIN_DX_SOUND */ #endif /* __HX2_SND_DSOUND */ engine/h2shared/snd_flac.c000066400000000000000000000267031444734033100157530ustar00rootroot00000000000000/* * fLaC streaming music support, loosely based QuakeForge implementation * with modifications. requires libFLAC >= 1.0.4 at compile and runtime. * * Copyright (C) 2005 Bill Currie * Copyright (C) 2013 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" #if defined(USE_CODEC_FLAC) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_flac.h" #undef LEGACY_FLAC #include /* FLAC 1.1.3 has FLAC_API_VERSION_CURRENT == 8 */ #if !defined(FLAC_API_VERSION_CURRENT) || ((FLAC_API_VERSION_CURRENT+0) < 8) #define LEGACY_FLAC #include #endif #ifdef LEGACY_FLAC #define FLAC__StreamDecoder FLAC__SeekableStreamDecoder #define FLAC__StreamDecoderReadStatus FLAC__SeekableStreamDecoderReadStatus #define FLAC__StreamDecoderSeekStatus FLAC__SeekableStreamDecoderSeekStatus #define FLAC__StreamDecoderTellStatus FLAC__SeekableStreamDecoderTellStatus #define FLAC__StreamDecoderLengthStatus FLAC__SeekableStreamDecoderLengthStatus #define FLAC__stream_decoder_new FLAC__seekable_stream_decoder_new #define FLAC__stream_decoder_finish FLAC__seekable_stream_decoder_finish #define FLAC__stream_decoder_delete FLAC__seekable_stream_decoder_delete #define FLAC__stream_decoder_process_single FLAC__seekable_stream_decoder_process_single #define FLAC__stream_decoder_seek_absolute FLAC__seekable_stream_decoder_seek_absolute #define FLAC__stream_decoder_process_until_end_of_metadata FLAC__seekable_stream_decoder_process_until_end_of_metadata #define FLAC__stream_decoder_get_state FLAC__seekable_stream_decoder_get_state #define FLAC__STREAM_DECODER_INIT_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_OK #define FLAC__STREAM_DECODER_READ_STATUS_CONTINUE FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK #define FLAC__STREAM_DECODER_READ_STATUS_ABORT FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR #define FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK /* !!! */ #define FLAC__STREAM_DECODER_WRITE_STATUS_ABORT FLAC__STREAM_DECODER_WRITE_STATUS_ABORT #define FLAC__STREAM_DECODER_SEEK_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK #define FLAC__STREAM_DECODER_SEEK_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR #define FLAC__STREAM_DECODER_TELL_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK #define FLAC__STREAM_DECODER_TELL_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR #define FLAC__STREAM_DECODER_LENGTH_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK typedef unsigned FLAC_SIZE_T; #else typedef size_t FLAC_SIZE_T; #endif typedef struct { FLAC__StreamDecoder *decoder; fshandle_t *file; snd_info_t *info; byte *buffer; int size, pos, error; } flacfile_t; /* CALLBACK FUNCTIONS: */ static void flac_error_func (const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { flacfile_t *ff = (flacfile_t *) client_data; ff->error = -1; Con_Printf ("FLAC: decoder error %i\n", status); } static FLAC__StreamDecoderReadStatus flac_read_func (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], FLAC_SIZE_T *bytes, void *client_data) { flacfile_t *ff = (flacfile_t *) client_data; if (*bytes > 0) { *bytes = FS_fread(buffer, 1, *bytes, ff->file); if (FS_ferror(ff->file)) return FLAC__STREAM_DECODER_READ_STATUS_ABORT; if (*bytes == 0) return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; } return FLAC__STREAM_DECODER_READ_STATUS_ABORT; } static FLAC__StreamDecoderSeekStatus flac_seek_func (const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) { flacfile_t *ff = (flacfile_t *) client_data; if (FS_fseek(ff->file, (long)absolute_byte_offset, SEEK_SET) < 0) return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; return FLAC__STREAM_DECODER_SEEK_STATUS_OK; } static FLAC__StreamDecoderTellStatus flac_tell_func (const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) { flacfile_t *ff = (flacfile_t *) client_data; long pos = FS_ftell (ff->file); if (pos < 0) return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; *absolute_byte_offset = (FLAC__uint64) pos; return FLAC__STREAM_DECODER_TELL_STATUS_OK; } static FLAC__StreamDecoderLengthStatus flac_length_func (const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) { flacfile_t *ff = (flacfile_t *) client_data; *stream_length = (FLAC__uint64) FS_filelength (ff->file); return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; } static FLAC__bool flac_eof_func (const FLAC__StreamDecoder *decoder, void *client_data) { flacfile_t *ff = (flacfile_t *) client_data; if (FS_feof (ff->file)) return true; return false; } static FLAC__StreamDecoderWriteStatus flac_write_func (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) { flacfile_t *ff = (flacfile_t *) client_data; if (!ff->buffer) { ff->buffer = (byte *) malloc (ff->info->blocksize * ff->info->channels * ff->info->width); if (!ff->buffer) { ff->error = -1; /* needn't set this here, but... */ Con_Printf("Insufficient memory for fLaC audio\n"); return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } } if (ff->info->channels == 1) { unsigned i; const FLAC__int32 *in = buffer[0]; if (ff->info->bits == 8) { byte *out = ff->buffer; for (i = 0; i < frame->header.blocksize; i++) *out++ = *in++ + 128; } else { short *out = (short *) ff->buffer; for (i = 0; i < frame->header.blocksize; i++) *out++ = *in++; } } else { unsigned i; const FLAC__int32 *li = buffer[0]; const FLAC__int32 *ri = buffer[1]; if (ff->info->bits == 8) { char *lo = (char *) ff->buffer + 0; char *ro = (char *) ff->buffer + 1; for (i = 0; i < frame->header.blocksize; i++, lo++, ro++) { *lo++ = *li++ + 128; *ro++ = *ri++ + 128; } } else { short *lo = (short *) ff->buffer + 0; short *ro = (short *) ff->buffer + 1; for (i = 0; i < frame->header.blocksize; i++, lo++, ro++) { *lo++ = *li++; *ro++ = *ri++; } } } ff->size = frame->header.blocksize * ff->info->width * ff->info->channels; ff->pos = 0; return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } static void flac_meta_func (const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) { flacfile_t *ff = (flacfile_t *) client_data; if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { ff->info->rate = metadata->data.stream_info.sample_rate; ff->info->bits = metadata->data.stream_info.bits_per_sample; ff->info->width = ff->info->bits / 8; ff->info->channels = metadata->data.stream_info.channels; ff->info->blocksize = metadata->data.stream_info.max_blocksize; ff->info->dataofs = 0; /* got the STREAMINFO metadata */ } } static qboolean S_FLAC_CodecInitialize (void) { return true; } static void S_FLAC_CodecShutdown (void) { } static qboolean S_FLAC_CodecOpenStream (snd_stream_t *stream) { flacfile_t *ff; int rc; ff = (flacfile_t *) Z_Malloc(sizeof(flacfile_t), Z_MAINZONE); ff->decoder = FLAC__stream_decoder_new (); if (ff->decoder == NULL) { Con_Printf("Unable to create fLaC decoder\n"); goto _fail; } stream->priv = ff; ff->info = & stream->info; ff->file = & stream->fh; ff->info->dataofs = -1; /* check for STREAMINFO metadata existence */ #ifdef LEGACY_FLAC FLAC__seekable_stream_decoder_set_error_callback (ff->decoder, flac_error_func); FLAC__seekable_stream_decoder_set_read_callback (ff->decoder, flac_read_func); FLAC__seekable_stream_decoder_set_seek_callback (ff->decoder, flac_seek_func); FLAC__seekable_stream_decoder_set_tell_callback (ff->decoder, flac_tell_func); FLAC__seekable_stream_decoder_set_length_callback (ff->decoder, flac_length_func); FLAC__seekable_stream_decoder_set_eof_callback (ff->decoder, flac_eof_func); FLAC__seekable_stream_decoder_set_write_callback (ff->decoder, flac_write_func); FLAC__seekable_stream_decoder_set_metadata_callback (ff->decoder, flac_meta_func); FLAC__seekable_stream_decoder_set_client_data (ff->decoder, ff); rc = FLAC__seekable_stream_decoder_init (ff->decoder); #else rc = FLAC__stream_decoder_init_stream(ff->decoder, flac_read_func, flac_seek_func, flac_tell_func, flac_length_func, flac_eof_func, flac_write_func, flac_meta_func, flac_error_func, ff); #endif if (rc != FLAC__STREAM_DECODER_INIT_STATUS_OK) /* unlikely */ { Con_Printf ("FLAC: decoder init error %i\n", rc); goto _fail; } rc = FLAC__stream_decoder_process_until_end_of_metadata (ff->decoder); if (rc == false || ff->error) { rc = FLAC__stream_decoder_get_state(ff->decoder); Con_Printf("%s not a valid flac file? (decoder state %i)\n", stream->name, rc); goto _fail; } if (ff->info->dataofs < 0) { Con_Printf("%s has no STREAMINFO\n", stream->name); goto _fail; } if (ff->info->bits != 8 && ff->info->bits != 16) { Con_Printf("%s is not 8 or 16 bit\n", stream->name); goto _fail; } if (ff->info->channels != 1 && ff->info->channels != 2) { Con_Printf("Unsupported number of channels %d in %s\n", ff->info->channels, stream->name); goto _fail; } return true; _fail: if (ff->decoder) { FLAC__stream_decoder_finish (ff->decoder); FLAC__stream_decoder_delete (ff->decoder); } Z_Free(ff); return false; } static int S_FLAC_CodecReadStream (snd_stream_t *stream, int len, void *buffer) { flacfile_t *ff = (flacfile_t *) stream->priv; byte *buf = (byte *) buffer; int count = 0; while (len) { int res = 0; if (ff->size == ff->pos) FLAC__stream_decoder_process_single (ff->decoder); if (ff->error) return -1; res = ff->size - ff->pos; if (res > len) res = len; if (res > 0) { memcpy (buf, ff->buffer + ff->pos, res); count += res; len -= res; buf += res; ff->pos += res; } else if (res < 0) { /* error */ return -1; } else { Con_DPrintf ("FLAC: EOF\n"); break; } } return count; } static void S_FLAC_CodecCloseStream (snd_stream_t *stream) { flacfile_t *ff = (flacfile_t *) stream->priv; FLAC__stream_decoder_finish (ff->decoder); FLAC__stream_decoder_delete (ff->decoder); if (ff->buffer) free(ff->buffer); Z_Free(ff); S_CodecUtilClose(&stream); } static int S_FLAC_CodecRewindStream (snd_stream_t *stream) { flacfile_t *ff = (flacfile_t *) stream->priv; ff->pos = ff->size = 0; if (FLAC__stream_decoder_seek_absolute(ff->decoder, 0)) return 0; return -1; } snd_codec_t flac_codec = { CODECTYPE_FLAC, true, /* always available. */ "flac", S_FLAC_CodecInitialize, S_FLAC_CodecShutdown, S_FLAC_CodecOpenStream, S_FLAC_CodecReadStream, S_FLAC_CodecRewindStream, NULL, /* jump */ S_FLAC_CodecCloseStream, NULL }; #endif /* USE_CODEC_FLAC */ engine/h2shared/snd_flac.h000066400000000000000000000003171444734033100157510ustar00rootroot00000000000000/* fLaC streaming music support. */ #if !defined(_SND_FLAC_H_) #define _SND_FLAC_H_ 1 #if defined(USE_CODEC_FLAC) extern snd_codec_t flac_codec; #endif /* USE_CODEC_FLAC */ #endif /* ! _SND_FLAC_H_ */ engine/h2shared/snd_gus.c000066400000000000000000001015551444734033100156430ustar00rootroot00000000000000/* snd_gus.c -- Routines for GUS support in dosquake * Author(s) : Jayeson Lee-Steere * from quake1 source with minor adaptations for uhexen2. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "snd_sys.h" #if HAVE_DOS_GUS_SOUND #include #include "q_ctype.h" #include "snd_gus.h" #include "dosisms.h" //============================================================================= // Routines for reading from .INI files // The read routines are fairly efficient //============================================================================= #define INI_STRING_SIZE 0x100 static FILE *ini_fopen (const char *filename, const char *modes); static int ini_fclose (FILE *f); static void ini_fgets (FILE *f, const char *section, const char *field, char *s); #define MAX_SECTION_WIDTH 20 #define MAX_FIELD_WIDTH 20 #define NUM_SECTION_BUFFERS 10 #define NUM_FIELD_BUFFERS 20 struct section_buffer { long offset; char name[MAX_SECTION_WIDTH + 1]; }; struct field_buffer { long offset; int section; char name[MAX_FIELD_WIDTH + 1]; }; static FILE *current_file = NULL; static int current_section; static int current_section_buffer = 0; static int current_field_buffer = 0; static struct section_buffer section_buffers[NUM_SECTION_BUFFERS]; static struct field_buffer field_buffers[NUM_FIELD_BUFFERS]; static void reset_buffer (FILE *f) { int i; for (i = 0; i < NUM_SECTION_BUFFERS; i++) section_buffers[i].name[0] = 0; for (i = 0; i < NUM_FIELD_BUFFERS; i++) field_buffers[i].name[0] = 0; current_file = f; } // Sees if the current string is section "name" (i.e. ["name"]). // If "name"=="*", sees if the current string is any section // (i.e. [....]). Returns 1 if true else 0 if false. static int is_section (const char *s, const char *name) { int wild = 0; // See if wild search if (strcmp("*", name) == 0) wild = 1; // Skip leading spaces while (s[0] == ' ') s++; // Look for leading "[" if (s[0] != '[') return 0; s++; // Make sure name matches while (s[0] != ']' && s[0] != 13 && s[0] != 10 && s[0] != 0 && name[0] != 0) { if (!wild) { if (q_toupper(s[0]) != q_toupper(name[0])) return 0; } s++; if (!wild) name++; } if (!wild) { if (name[0] != 0) return 0; } // Skip trailing spaces while (s[0] == ' ') s++; // Make sure we have trailing "]" if (s[0] != ']') return 0; return 1; } // Sees if the current string is field "name" (i.e. "name"=...). // If "name"=="*", sees if the current string is any field // (i.e. ...=...). Returns 1 if true else 0 if false. static int is_field (const char *s, const char *name) { int wild = 0; // See if wild search if (strcmp("*", name) == 0) wild = 1; // Skip leading spaces while (s[0] == ' ') s++; // Make sure name matches while (s[0] != '=' && s[0] != 13 && s[0] != 10 && s[0] != 0 && name[0] != 0) { if (!wild) { if (q_toupper(s[0]) != q_toupper(name[0])) return 0; } s++; if (!wild) name++; } if (!wild) { if (name[0] != 0) return 0; } // Skip trailing spaces while (s[0] == ' ') s++; // Make sure we have an "=" if (s[0] != '=') return 0; return 1; } // Extracts the section name from a section heading // e.g. in="[hey man]" gives out="hey man" static void get_section_name (char *out, char *in) { int i = 0; // Skip spaces before '[' while (in[0] == ' ') in++; // Make sure there is a '[' if (in[0] != '[') { out[0] = 0; return; } // Skip past '[' in++; // Copy string if any to output string. while (in[0] != ']' && in[0] != 13 && in[0] != 10 && in[0] != 0) { if (i < MAX_SECTION_WIDTH) { out[i] = in[0]; i++; } in++; } // Make sure string was terminated with ']' if (in[0] != ']') { out[0] = 0; return; } // Remove trailing spaces while (i > 0 && out[i - 1] == ' ') i--; // Null terminate the output string. out[i] = 0; } // Extracts the field name from a field line // e.g. in="sooty=life be in it" gives out="sooty" static void get_field_name (char *out, char *in) { int i = 0; // Skip leading spaces while (in[0] == ' ') in++; // Copy name to output string while (in[0] != '=' && in[0] != 13 && in[0] != 10 && in[0] != 0) { if (i < MAX_FIELD_WIDTH) { out[i] = in[0]; i++; } in++; } // Make sure we stopped on "=" if (in[0] != '=') { out[0] = 0; return; } // Remove trailing spaces while (i > 0 && out[i - 1] == ' ') i--; // Null terminate the output string. out[i] = 0; } // Returns the field data from string s. // e.g. in="wally = golly man" gives out="golly man" static void get_field_string (char *out, char *in) { int i = 0; // Find '=' if it exists while (in[0] != '=' && in[0] != 13 && in[0] != 10 && in[0] != 0) in++; // If there is an '=', skip past it. if (in[0] == '=') in++; // Skip any spaces between the '=' and string. while (in[0] == ' ' || in[0] == '[') in++; // Copy string, if there is one, to the output string. while (in[0] != 13 && in[0] != 10 && in[0] != 0 && i < (INI_STRING_SIZE - 1)) { out[i] = in[0]; in++; i++; } // Null terminate the output string. out[i] = 0; } // Adds a section to the buffer static int add_section (char *instring, long offset) { int i; char section[MAX_SECTION_WIDTH + 1]; // Extract section name get_section_name(section, instring); // See if section already exists. for (i = 0; i < NUM_SECTION_BUFFERS; i++) { if (stricmp(section, section_buffers[i].name) == 0) return i; } // Increment current_section_buffer current_section_buffer++; if (current_section_buffer > NUM_SECTION_BUFFERS) current_section_buffer = 0; // Delete any field buffers that correspond to this section for (i = 0; i < NUM_FIELD_BUFFERS; i++) { if (field_buffers[i].section == current_section_buffer) field_buffers[i].name[0] = 0; } // Set buffer information strcpy(section_buffers[current_section_buffer].name, section); section_buffers[current_section_buffer].offset = offset; return current_section_buffer; } // Adds a field to the buffer static void add_field (char *instring, int section, long offset) { int i; char field[MAX_FIELD_WIDTH + 1]; // Extract field name get_field_name(field, instring); // See if field already exists for (i = 0; i < NUM_FIELD_BUFFERS; i++) { if (field_buffers[i].section == section) { if (stricmp(field_buffers[i].name, field) == 0) return; } } // Increment current_field_buffer current_field_buffer++; if (current_field_buffer > NUM_FIELD_BUFFERS) current_field_buffer = 0; // Set buffer information strcpy(field_buffers[current_field_buffer].name, field); field_buffers[current_field_buffer].section = section; field_buffers[current_field_buffer].offset = offset; } // Identical to fgets except the string is trucated at the first ';', // carriage return or line feed. static char *stripped_fgets (char *s, int n, FILE *f) { int i = 0; if (fgets(s, n, f) == NULL) return NULL; while (s[i] != ';' && s[i] != 13 && s[i] != 10 && s[i] != 0) i++; s[i] = 0; return s; } // Opens an .INI file. Works like fopen static FILE *ini_fopen (const char *filename, const char *modes) { return fopen(filename, modes); } // Closes a .INI file. Works like fclose static int ini_fclose (FILE *f) { if (f == current_file) reset_buffer (NULL); return fclose(f); } // Puts "field" from "section" from .ini file "f" into "s". // If "section" does not exist or "field" does not exist in // section then s=""; static void ini_fgets (FILE *f, const char *section, const char *field, char *s) { int i; long start_pos, string_start_pos; char ts[INI_STRING_SIZE * 2]; if (f != current_file) reset_buffer(f); // Default to "Not found" s[0] = 0; // See if section is in buffer for (i = 0; i < NUM_SECTION_BUFFERS; i++) { if (strnicmp(section_buffers[i].name, section, MAX_SECTION_WIDTH) == 0) break; } // If section is in buffer, seek to it if necessary if (i < NUM_SECTION_BUFFERS) { if (i != current_section) { current_section = i; fseek(f, section_buffers[i].offset, SEEK_SET); } } // else look through .ini file for it. else { // Make sure we are not at eof or this will cause trouble. if (feof(f)) rewind(f); start_pos = ftell(f); while (1) { stripped_fgets(ts, INI_STRING_SIZE * 2, f); // If it is a section, add it to the section buffer if (is_section(ts, "*")) current_section=add_section(ts, ftell(f)); // If it is the section we are looking for, break. if (is_section(ts, section)) break; // If we reach the end of the file, rewind to the start. if (feof(f)) rewind(f); if (ftell(f) == start_pos) return; } } // See if field is in buffer for (i = 0; i < NUM_FIELD_BUFFERS; i++) { if (field_buffers[i].section == current_section) { if (strnicmp(field_buffers[i].name, field, MAX_FIELD_WIDTH) == 0) break; } } // If field is in buffer, seek to it and read it if (i < NUM_FIELD_BUFFERS) { fseek(f, field_buffers[i].offset, SEEK_SET); stripped_fgets(ts, INI_STRING_SIZE * 2, f); get_field_string(s, ts); } // else search through section for field. else { // Make sure we do not start at eof or this will cause problems. if (feof(f)) fseek(f, section_buffers[current_section].offset, SEEK_SET); start_pos = ftell(f); while (1) { string_start_pos = ftell(f); stripped_fgets(ts, INI_STRING_SIZE * 2, f); // If it is a field, add it to the buffer if (is_field(ts, "*")) add_field(ts, current_section, string_start_pos); // If it is the field we are looking for, save it if (is_field(ts, field)) { get_field_string(s, ts); break; } // If we reach the end of the section, start over if (feof(f) || is_section(ts, "*")) fseek(f, section_buffers[current_section].offset, SEEK_SET); if (ftell(f) == start_pos) return; } } } //============================================================================= // GUS support //============================================================================= #define BYTE unsigned char #define WORD unsigned short #define DWORD unsigned long #define BUFFER_SIZE 4096 #define CODEC_ADC_INPUT_CONTROL_LEFT 0x00 #define CODEC_ADC_INPUT_CONTROL_RIGHT 0x01 #define CODEC_AUX1_INPUT_CONTROL_LEFT 0x02 #define CODEC_AUX1_INPUT_CONTROL_RIGHT 0x03 #define CODEC_AUX2_INPUT_CONTROL_LEFT 0x04 #define CODEC_AUX2_INPUT_CONTROL_RIGHT 0x05 #define CODEC_DAC_OUTPUT_CONTROL_LEFT 0x06 #define CODEC_DAC_OUTPUT_CONTROL_RIGHT 0x07 #define CODEC_FS_FORMAT 0x08 #define CODEC_INTERFACE_CONFIG 0x09 #define CODEC_PIN_CONTROL 0x0A #define CODEC_ERROR_STATUS_AND_INIT 0x0B #define CODEC_MODE_AND_ID 0x0C #define CODEC_LOOPBACK_CONTROL 0x0D #define CODEC_PLAYBACK_UPPER_BASE_COUNT 0x0E #define CODEC_PLAYBACK_LOWER_BASE_COUNT 0x0F #define SET_CONTROL 0x00 #define SET_FREQUENCY 0x01 #define SET_START_HIGH 0x02 #define SET_START_LOW 0x03 #define SET_END_HIGH 0x04 #define SET_END_LOW 0x05 #define SET_VOLUME_RATE 0x06 #define SET_VOLUME_START 0x07 #define SET_VOLUME_END 0x08 #define SET_CURR_VOLUME 0x09 #define SET_VOLUME 0x09 #define SET_ACC_HIGH 0x0A #define SET_ACC_LOW 0x0B #define SET_BALANCE 0x0C #define SET_VOLUME_CONTROL 0x0D #define SET_VOICES 0x0E #define DMA_CONTROL 0x41 #define SET_DMA_ADDRESS 0x42 #define SET_DRAM_LOW 0x43 #define SET_DRAM_HIGH 0x44 #define ADLIB_CONTROL 0x45 #define ADLIB_TIMER1 0x46 #define ADLIB_TIMER2 0x47 #define SET_RECORD_RATE 0x48 #define RECORD_CONTROL 0x49 #define SET_JOYSTICK 0x4B #define MASTER_RESET 0x4C #define GET_CONTROL 0x80 #define GET_FREQUENCY 0x81 #define GET_START_HIGH 0x82 #define GET_START_LOW 0x83 #define GET_END_HIGH 0x84 #define GET_END_LOW 0x85 #define GET_VOLUME_RATE 0x86 #define GET_VOLUME_START 0x87 #define GET_VOLUME_END 0x88 #define GET_VOLUME 0x89 #define GET_ACC_HIGH 0x8A #define GET_ACC_LOW 0x8B #define GET_BALANCE 0x8C #define GET_VOLUME_CONTROL 0x8D #define GET_VOICES 0x8E #define GET_IRQV 0x8F struct CodecRateStruct { WORD Rate; BYTE FSVal; }; struct Gf1RateStruct { WORD Rate; BYTE Voices; }; static void *dma_dosadr = NULL; /* as received from dos_getmemory() */ static short *dma_buffer = NULL; static char s_gus_driver[] = "GUS"; static BYTE HaveCodec = 0; static WORD CodecRegisterSelect; static WORD CodecData; static WORD CodecStatus; static WORD Gf1TimerControl; static WORD Gf1PageRegister; static WORD Gf1RegisterSelect; static WORD Gf1DataLow; static WORD Gf1DataHigh; static BYTE DmaChannel; static BYTE PageRegs[] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; static BYTE AddrRegs[] = { 0, 2, 4, 6, 0xc0, 0xc4, 0xc8, 0xcc }; static BYTE CountRegs[] = { 1, 3, 5, 7, 0xc2, 0xc6, 0xca, 0xce }; static WORD AddrReg; static WORD CountReg; static WORD ModeReg; static WORD DisableReg; static WORD ClearReg; static struct CodecRateStruct CodecRates[]= { { 5512, 0x01 }, { 6620, 0x0F }, { 8000, 0x00 }, { 9600, 0x0E }, { 11025, 0x03 }, { 16000, 0x02 }, { 18900, 0x05 }, { 22050, 0x07 }, { 27420, 0x04 }, { 32000, 0x06 }, { 33075, 0x0D }, { 37800, 0x09 }, { 44100, 0x0B }, { 48000, 0x0C }, { 0, 0x00 } // End marker }; static struct Gf1RateStruct Gf1Rates[]= { { 19293, 32 }, { 19916, 31 }, { 20580, 30 }, { 21289, 29 }, { 22050, 28 }, { 22866, 27 }, { 23746, 26 }, { 24696, 25 }, { 25725, 24 }, { 26843, 23 }, { 28063, 22 }, { 29400, 21 }, { 30870, 20 }, { 32494, 19 }, { 34300, 18 }, { 36317, 17 }, { 38587, 16 }, { 41160, 15 }, { 44100, 14 }, { 0, 0 } }; //============================================================================= // Basic GF1 functions //============================================================================= static void SetGf18 (BYTE reg, BYTE data) { dos_outportb(Gf1RegisterSelect, reg); dos_outportb(Gf1DataHigh, data); } static void SetGf116 (BYTE reg, WORD data) { dos_outportb(Gf1RegisterSelect, reg); dos_outportw(Gf1DataLow, data); } static BYTE GetGf18 (BYTE reg) { dos_outportb(Gf1RegisterSelect, reg); return (dos_inportb(Gf1DataHigh)); } static WORD GetGf116 (BYTE reg) { dos_outportb(Gf1RegisterSelect, reg); return (dos_inportw(Gf1DataLow)); } static void Gf1Delay (void) { int i; for (i = 0; i < 27; i++) dos_inportb(Gf1TimerControl); } static DWORD ConvertTo16 (DWORD Address) { return (((Address >> 1) & 0x0001FFFF) | (Address & 0x000C0000L)); } static void ClearGf1Ints (void) { int i; SetGf18(DMA_CONTROL, 0x00); SetGf18(ADLIB_CONTROL, 0x00); SetGf18(RECORD_CONTROL, 0x00); GetGf18(DMA_CONTROL); GetGf18(RECORD_CONTROL); for (i = 0; i < 32; i++) GetGf18(GET_IRQV); } //============================================================================= // Get Interwave (UltraSound PnP) configuration if any //============================================================================= static qboolean GUS_GetIWData (void) { char *Interwave, s[INI_STRING_SIZE]; FILE *IwFile; int CodecBase, CodecDma, i; Interwave = getenv("INTERWAVE"); if (Interwave == NULL) return false; // Open IW.INI IwFile = ini_fopen(Interwave, "rt"); if (IwFile == NULL) return false; // Read codec base and codec DMA ini_fgets(IwFile, "setup 0", "CodecBase", s); sscanf(s, "%X", &CodecBase); ini_fgets(IwFile, "setup 0", "DMA2", s); sscanf(s, "%i", &CodecDma); ini_fclose(IwFile); // Make sure numbers OK if (CodecBase == 0 || CodecDma == 0) return false; CodecRegisterSelect = CodecBase; CodecData = CodecBase + 1; CodecStatus = CodecBase + 2; DmaChannel = CodecDma; // Make sure there is a CODEC at the CODEC base // Clear any pending IRQs dos_inportb(CodecStatus); dos_outportb(CodecStatus, 0); // Wait for 'INIT' bit to clear for (i = 0; i < 0xFFFF; i++) { if ((dos_inportb(CodecRegisterSelect) & 0x80) == 0) break; } if (i == 0xFFFF) return false; // Get chip revision - can not be zero dos_outportb(CodecRegisterSelect, CODEC_MODE_AND_ID); if ((dos_inportb(CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID) return false; if ((dos_inportb(CodecData) & 0x0F) == 0) return false; HaveCodec = 1; Con_Printf("Sound Card is UltraSound PnP\n"); return true; } //============================================================================= // Get UltraSound MAX configuration if any //============================================================================= static qboolean GUS_GetMAXData (void) { char *Ultrasnd, *Ultra16; int i; int GusBase, Dma1, Dma2, Irq1, Irq2; int CodecBase, CodecDma, CodecIrq, CodecType; BYTE MaxVal; Ultrasnd = getenv("ULTRASND"); Ultra16 = getenv("ULTRA16"); if (Ultrasnd == NULL || Ultra16 == NULL) return false; sscanf(Ultrasnd, "%x,%i,%i,%i,%i", &GusBase, &Dma1, &Dma2, &Irq1, &Irq2); sscanf(Ultra16, "%x,%i,%i,%i", &CodecBase, &CodecDma, &CodecIrq, &CodecType); if (CodecType == 0 && CodecDma != 0) DmaChannel = CodecDma & 0x07; else DmaChannel = Dma2 & 0x07; // Make sure there is a GUS at GUS base dos_outportb(GusBase + 0x08, 0x55); if (dos_inportb(GusBase + 0x0A) != 0x55) return false; dos_outportb(GusBase + 0x08, 0xAA); if (dos_inportb(GusBase + 0x0A) != 0xAA) return false; // Program CODEC control register MaxVal = ((CodecBase & 0xF0) >> 4) | 0x40; if (Dma1 > 3) MaxVal |= 0x10; if (Dma2 > 3) MaxVal |= 0x20; dos_outportb(GusBase + 0x106, MaxVal); CodecRegisterSelect = CodecBase; CodecData = CodecBase + 1; CodecStatus = CodecBase + 2; // Make sure there is a CODEC at the CODEC base // Clear any pending IRQs dos_inportb(CodecStatus); dos_outportb(CodecStatus, 0); // Wait for 'INIT' bit to clear for (i = 0; i < 0xFFFF; i++) { if ((dos_inportb(CodecRegisterSelect) & 0x80) == 0) break; } if (i == 0xFFFF) return false; // Get chip revision - can not be zero dos_outportb(CodecRegisterSelect, CODEC_MODE_AND_ID); if ((dos_inportb(CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID) return false; if ((dos_inportb(CodecData) & 0x0F) == 0) return false; HaveCodec = 1; Con_Printf("Sound Card is UltraSound MAX\n"); return true; } //============================================================================= // Get regular UltraSound configuration if any //============================================================================= static qboolean GUS_GetGUSData (void) { char *Ultrasnd; int GusBase, Dma1, Dma2, Irq1, Irq2, i; Ultrasnd = getenv("ULTRASND"); if (Ultrasnd == NULL) return false; sscanf(Ultrasnd, "%x,%i,%i,%i,%i", &GusBase, &Dma1, &Dma2, &Irq1, &Irq2); DmaChannel = Dma1 & 0x07; // Make sure there is a GUS at GUS base dos_outportb(GusBase + 0x08, 0x55); if (dos_inportb(GusBase + 0x0A) != 0x55) return false; dos_outportb(GusBase + 0x08, 0xAA); if (dos_inportb(GusBase + 0x0A) != 0xAA) return false; Gf1TimerControl = GusBase + 0x008; Gf1PageRegister = GusBase + 0x102; Gf1RegisterSelect = GusBase + 0x103; Gf1DataLow = GusBase + 0x104; Gf1DataHigh = GusBase + 0x105; // Reset the GUS SetGf18(MASTER_RESET, 0x00); Gf1Delay(); Gf1Delay(); SetGf18(MASTER_RESET, 0x01); Gf1Delay(); Gf1Delay(); // Set to max (32) voices SetGf18(SET_VOICES, 0xDF); // Clear any pending IRQ's ClearGf1Ints(); // Set all registers to known values for (i = 0; i < 32; i++) { dos_outportb(Gf1PageRegister, i); SetGf18(SET_CONTROL, 0x03); SetGf18(SET_VOLUME_CONTROL, 0x03); Gf1Delay(); SetGf18(SET_CONTROL, 0x03); SetGf18(SET_VOLUME_CONTROL, 0x03); SetGf116(SET_START_HIGH, 0); SetGf116(SET_START_LOW, 0); SetGf116(SET_END_HIGH, 0); SetGf116(SET_END_LOW, 0); SetGf116(SET_ACC_HIGH, 0); SetGf116(SET_ACC_LOW, 0); SetGf18(SET_VOLUME_RATE, 63); SetGf18(SET_VOLUME_START, 5); SetGf18(SET_VOLUME_END, 251); SetGf116(SET_VOLUME, 5 << 8); } // Clear any pending IRQ's ClearGf1Ints(); // Enable DAC etc. SetGf18(MASTER_RESET, 0x07); // Enable line output so we can hear something dos_outportb(GusBase, 0x08); HaveCodec = 0; Con_Printf("Sound Card is UltraSound\n"); return true; } //============================================================================= // Programs the DMA controller to start DMAing in Auto-init mode //============================================================================= static void GUS_StartDMA (BYTE dmaChannel, short *dma_buff, int count) { int mode; int RealAddr; RealAddr = ptr2real(dma_buff); if (dmaChannel <= 3) { ModeReg = 0x0B; DisableReg = 0x0A; ClearReg = 0x0E; } else { ModeReg = 0xD6; DisableReg = 0xD4; ClearReg = 0xDC; } CountReg = CountRegs[dmaChannel]; AddrReg = AddrRegs[dmaChannel]; dos_outportb(DisableReg, dmaChannel | 4); // disable channel // set mode- see "undocumented pc", p.876 mode = (1 << 6) // single-cycle + (0 << 5) // address increment + (1 << 4) // auto-init dma + (2 << 2) // read + (dmaChannel & 0x03); // channel # dos_outportb(ModeReg, mode); // set page dos_outportb(PageRegs[dmaChannel], RealAddr >> 16); if (dmaChannel <= 3) { // address is in bytes dos_outportb(0x0C, 0); // prepare to send 16-bit value dos_outportb(AddrReg, RealAddr & 0xff); dos_outportb(AddrReg, (RealAddr >> 8) & 0xff); dos_outportb(0x0C, 0); // prepare to send 16-bit value dos_outportb(CountReg, (count - 1) & 0xff); dos_outportb(CountReg, (count - 1) >> 8); } else { // address is in words dos_outportb(0xD8, 0); // prepare to send 16-bit value dos_outportb(AddrReg, (RealAddr >> 1) & 0xff); dos_outportb(AddrReg, (RealAddr >> 9) & 0xff); dos_outportb(0xD8, 0); // prepare to send 16-bit value dos_outportb(CountReg, ((count >> 1) - 1) & 0xff); dos_outportb(CountReg, ((count >> 1) - 1) >> 8); } dos_outportb(ClearReg, 0); // clear write mask dos_outportb(DisableReg, dmaChannel & ~4); } //============================================================================= // Starts the CODEC playing //============================================================================= static void GUS_StartCODEC (int count, BYTE FSVal) { int i, j; // Clear any pending IRQs dos_inportb(CodecStatus); dos_outportb(CodecStatus, 0); // Set mode to 2 dos_outportb(CodecRegisterSelect, CODEC_MODE_AND_ID); dos_outportb(CodecData, 0xC0); // Stop any playback or capture which may be happening dos_outportb(CodecRegisterSelect, CODEC_INTERFACE_CONFIG); dos_outportb(CodecData, dos_inportb(CodecData) & 0xFC); // Set FS dos_outportb(CodecRegisterSelect, CODEC_FS_FORMAT | 0x40); dos_outportb(CodecData, FSVal | 0x50); // Or in stereo and 16 bit bits // Wait a bit for (i = 0; i < 10; i++) dos_inportb(CodecData); // Routine 1 to counter CODEC bug - wait for init bit to clear and then a // bit longer (i=min loop count, j=timeout for (i = 0, j = 0; i < 1000 && j < 0x7FFFF; j++) { if ((dos_inportb(CodecRegisterSelect) & 0x80) == 0) i++; } // Routine 2 to counter CODEC bug - this is from Forte's code. For me it // does not seem to cure the problem, but is added security // Waits till we can modify index register for (j = 0; j < 0x7FFFF; j++) { dos_outportb(CodecRegisterSelect, CODEC_INTERFACE_CONFIG | 0x40); if (dos_inportb(CodecRegisterSelect) == (CODEC_INTERFACE_CONFIG | 0x40)) break; } // Perform ACAL dos_outportb(CodecRegisterSelect, CODEC_INTERFACE_CONFIG | 0x40); dos_outportb(CodecData, 0x08); // Clear MCE bit - this makes ACAL happen dos_outportb(CodecRegisterSelect, CODEC_INTERFACE_CONFIG); // Wait for ACAL to finish for (j = 0; j < 0x7FFFF; j++) { if ((dos_inportb(CodecRegisterSelect) & 0x80) != 0) continue; dos_outportb(CodecRegisterSelect, CODEC_ERROR_STATUS_AND_INIT); if ((dos_inportb(CodecData) & 0x20) == 0) break; } // Clear ACAL bit dos_outportb(CodecRegisterSelect, CODEC_INTERFACE_CONFIG | 0x40); dos_outportb(CodecData, 0x00); dos_outportb(CodecRegisterSelect, CODEC_INTERFACE_CONFIG); // Set some other junk dos_outportb(CodecRegisterSelect, CODEC_LOOPBACK_CONTROL); dos_outportb(CodecData, 0x00); dos_outportb(CodecRegisterSelect, CODEC_PIN_CONTROL); dos_outportb(CodecData, 0x08); // IRQ is disabled in PIN control // Set count (it doesn't really matter what value we stuff in here dos_outportb(CodecRegisterSelect, CODEC_PLAYBACK_LOWER_BASE_COUNT); dos_outportb(CodecData, count & 0xFF); dos_outportb(CodecRegisterSelect, CODEC_PLAYBACK_UPPER_BASE_COUNT); dos_outportb(CodecData, count >> 8); // Start playback dos_outportb(CodecRegisterSelect, CODEC_INTERFACE_CONFIG); dos_outportb(CodecData, 0x01); } //============================================================================= // Starts the GF1 playing //============================================================================= static void GUS_StartGf1 (int count, BYTE Voices) { DWORD StartAddressL, EndAddressL, StartAddressR, EndAddressR; // Set number of voices to give us the sampling rate we want SetGf18(SET_VOICES,0xC0 | (Voices - 1)); // Figure out addresses StartAddressL = ConvertTo16(0); EndAddressL = ConvertTo16(count - 2 - 2); StartAddressR = ConvertTo16(2); EndAddressR = ConvertTo16(count - 2); // Set left voice addresses dos_outportb(Gf1PageRegister, 0); SetGf116(SET_START_LOW, StartAddressL << 9); SetGf116(SET_START_HIGH, StartAddressL >> 7); SetGf116(SET_ACC_LOW, StartAddressL << 9); SetGf116(SET_ACC_HIGH, StartAddressL >> 7); SetGf116(SET_END_LOW, EndAddressL << 9); SetGf116(SET_END_HIGH, EndAddressL >> 7); // Set balance to full left SetGf18(SET_BALANCE, 0); // Set volume to full SetGf116(SET_VOLUME, 0xFFF0); // Set FC to 2 (so we play every second sample) SetGf116(SET_FREQUENCY, 0x0800); // Set right voice addresses dos_outportb(Gf1PageRegister, 1); SetGf116(SET_START_LOW, StartAddressR << 9); SetGf116(SET_START_HIGH, StartAddressR >> 7); SetGf116(SET_ACC_LOW, StartAddressR << 9); SetGf116(SET_ACC_HIGH, StartAddressR >> 7); SetGf116(SET_END_LOW, EndAddressR << 9); SetGf116(SET_END_HIGH, EndAddressR >> 7); // Set balance to full right SetGf18(SET_BALANCE, 15); // Set volume to full SetGf116(SET_VOLUME, 0xFFF0); // Set FC to 2 (so we play every second sample) SetGf116(SET_FREQUENCY, 0x0800); // Start voices dos_outportb(Gf1PageRegister, 0); SetGf18(SET_CONTROL, 0x0C); dos_outportb(Gf1PageRegister, 1); SetGf18(SET_CONTROL, 0x0C); Gf1Delay(); dos_outportb(Gf1PageRegister, 0); SetGf18(SET_CONTROL, 0x0C); dos_outportb(Gf1PageRegister, 1); SetGf18(SET_CONTROL, 0x0C); } //============================================================================= // Figures out what kind of UltraSound we have, if any, and starts it playing //============================================================================= static qboolean S_GUS_Init (dma_t *dma) { int rc; int RealAddr; BYTE FSVal, Voices; struct CodecRateStruct *CodecRate; struct Gf1RateStruct *Gf1Rate; if (COM_CheckParm("-nogus")) return false; // See what kind of UltraSound we have, if any if (GUS_GetIWData() == false) { if (GUS_GetMAXData() == false) { if (GUS_GetGUSData() == false) return false; } } memset ((void *) dma, 0, sizeof(dma_t)); shm = dma; if (HaveCodec) { // do 11khz sampling rate unless command line parameter wants different shm->speed = 11025; FSVal = 0x03; rc = COM_CheckParm("-sndspeed"); if (rc && rc < com_argc - 1) { shm->speed = atoi(com_argv[rc+1]); // Make sure rate not too high if (shm->speed > 48000) shm->speed = 48000; // Adjust speed to match one of the possible CODEC rates for (CodecRate = CodecRates; CodecRate->Rate != 0; CodecRate++) { if (shm->speed <= CodecRate->Rate) { shm->speed = CodecRate->Rate; FSVal = CodecRate->FSVal; break; } } } // Always do 16 bit stereo shm->channels = 2; shm->samplebits = 16; // allocate buffer twice the size we need so we can get aligned buffer dma_dosadr = dos_getmemory(BUFFER_SIZE * 2); if (dma_dosadr == NULL) { shm = NULL; Con_Printf("Couldn't allocate sound dma buffer\n"); return false; } RealAddr = ptr2real(dma_dosadr); RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE - 1); dma_buffer = (short *) real2ptr(RealAddr); // Zero off DMA buffer memset(dma_buffer, 0, BUFFER_SIZE); shm->samplepos = 0; shm->submission_chunk = 1; shm->buffer = (unsigned char *) dma_buffer; shm->samples = BUFFER_SIZE / (shm->samplebits / 8); GUS_StartDMA(DmaChannel, dma_buffer, BUFFER_SIZE); GUS_StartCODEC(BUFFER_SIZE, FSVal); } else { // do 19khz sampling rate unless command line parameter wants different shm->speed = 19293; Voices = 32; rc = COM_CheckParm("-sndspeed"); if (rc && rc < com_argc - 1) { shm->speed = atoi(com_argv[rc+1]); // Make sure rate not too high if (shm->speed > 44100) shm->speed = 44100; // Adjust speed to match one of the possible GF1 rates for (Gf1Rate = Gf1Rates; Gf1Rate->Rate != 0; Gf1Rate++) { if (shm->speed <= Gf1Rate->Rate) { shm->speed = Gf1Rate->Rate; Voices = Gf1Rate->Voices; break; } } } // Always do 16 bit stereo shm->channels = 2; shm->samplebits = 16; // allocate buffer twice the size we need so we can get aligned buffer dma_dosadr = dos_getmemory(BUFFER_SIZE * 2); if (dma_dosadr == NULL) { shm = NULL; Con_Printf("Couldn't allocate sound dma buffer\n"); return false; } RealAddr = ptr2real(dma_dosadr); RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE - 1); dma_buffer = (short *) real2ptr(RealAddr); // Zero off DMA buffer memset(dma_buffer, 0, BUFFER_SIZE); shm->samplepos = 0; shm->submission_chunk = 1; shm->buffer = (unsigned char *) dma_buffer; shm->samples = BUFFER_SIZE / (shm->samplebits / 8); GUS_StartDMA(DmaChannel, dma_buffer, BUFFER_SIZE); SetGf116(SET_DMA_ADDRESS, 0x0000); if (DmaChannel <= 3) SetGf18(DMA_CONTROL, 0x41); else SetGf18(DMA_CONTROL, 0x45); GUS_StartGf1(BUFFER_SIZE, Voices); } return true; } //============================================================================= // Returns the current playback position //============================================================================= static int S_GUS_GetDMAPos (void) { int count; if (! dma_buffer) /* not initialized */ return 0; if (HaveCodec) { // clear 16-bit reg flip-flop // load the current dma count register if (DmaChannel < 4) { dos_outportb(0x0C, 0); count = dos_inportb(CountReg); count += dos_inportb(CountReg) << 8; if (shm->samplebits == 16) count /= 2; count = shm->samples - (count + 1); } else { dos_outportb(0xD8, 0); count = dos_inportb(CountReg); count += dos_inportb(CountReg) << 8; if (shm->samplebits == 8) count *= 2; count = shm->samples - (count + 1); } } else { // Read current position from GF1 dos_outportb(Gf1PageRegister,0); count = (GetGf116(GET_ACC_HIGH) << 7) & 0xFFFF; // See which half of buffer we are in. Note that since this is 16 bit // data we are playing, position is in 16 bit samples if (GetGf18(DMA_CONTROL) & 0x40) { GUS_StartDMA(DmaChannel, dma_buffer, BUFFER_SIZE); SetGf116(SET_DMA_ADDRESS,0x0000); if (DmaChannel <= 3) SetGf18(DMA_CONTROL, 0x41); else SetGf18(DMA_CONTROL, 0x45); } } shm->samplepos = count & (shm->samples - 1); return shm->samplepos; } //============================================================================= // Stops the UltraSound playback //============================================================================= static void S_GUS_Shutdown (void) { if (! dma_buffer) /* not initialized */ return; if (HaveCodec) { // Stop CODEC dos_outportb(CodecRegisterSelect, CODEC_INTERFACE_CONFIG); dos_outportb(CodecData, 0x01); } else { // Stop Voices dos_outportb(Gf1PageRegister, 0); SetGf18(SET_CONTROL, 0x03); dos_outportb(Gf1PageRegister, 1); SetGf18(SET_CONTROL, 0x03); Gf1Delay(); dos_outportb(Gf1PageRegister, 0); SetGf18(SET_CONTROL, 0x03); dos_outportb(Gf1PageRegister, 1); SetGf18(SET_CONTROL, 0x03); // Stop any DMA SetGf18(DMA_CONTROL, 0x00); GetGf18(DMA_CONTROL); } dos_outportb(DisableReg, DmaChannel | 4); // disable dma channel shm = NULL; dos_freememory(dma_dosadr); dma_dosadr = NULL; } /* ============== SNDDMA_LockBuffer Makes sure dma buffer is valid =============== */ static void S_GUS_LockBuffer (void) { /* nothing to do here */ } /* ============== SNDDMA_Submit Unlock the dma buffer / Send sound to the device =============== */ static void S_GUS_Submit (void) { /* nothing to do here */ } static void S_GUS_BlockSound (void) { } static void S_GUS_UnblockSound (void) { } snd_driver_t snddrv_gus = { S_GUS_Init, S_GUS_Shutdown, S_GUS_GetDMAPos, S_GUS_LockBuffer, S_GUS_Submit, S_GUS_BlockSound, S_GUS_UnblockSound, s_gus_driver, SNDDRV_ID_GUS_DOS, false, NULL }; #endif /* HAVE_DOS_GUS_SOUND */ engine/h2shared/snd_gus.h000066400000000000000000000003131444734033100156360ustar00rootroot00000000000000/* DOS sound support for GUS. */ #if !defined(__HX2_SND_GUS) #define __HX2_SND_GUS #if HAVE_DOS_GUS_SOUND extern snd_driver_t snddrv_gus; #endif /* HAVE_DOS_GUS_SOUND */ #endif /* __HX2_SND_GUS */ engine/h2shared/snd_mem.c000066400000000000000000000161441444734033100156220ustar00rootroot00000000000000/* * snd_mem.c -- wav sound caching * * Copyright (C) 1996-2001 Id Software, Inc. * Copyright (C) 2010-2011 O. Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" /* ================ ResampleSfx ================ */ static void ResampleSfx (sfx_t *sfx, int inrate, int inwidth, byte *data) { int outcount; int srcsample; float stepscale; int i; int sample, samplefrac, fracstep; sfxcache_t *sc; sc = (sfxcache_t *) Cache_Check (&sfx->cache); if (!sc) return; stepscale = (float)inrate / shm->speed; // this is usually 0.5, 1, or 2 outcount = sc->length / stepscale; sc->length = outcount; if (sc->loopstart != -1) sc->loopstart = sc->loopstart / stepscale; sc->speed = shm->speed; if (loadas8bit.integer) sc->width = 1; else sc->width = inwidth; sc->stereo = 0; // resample / decimate to the current source rate if (stepscale == 1 && inwidth == 1 && sc->width == 1) { // fast special case for (i = 0; i < outcount; i++) ((signed char *)sc->data)[i] = (int)( (unsigned char)(data[i]) - 128); } else { // general case samplefrac = 0; fracstep = stepscale*256; for (i = 0; i < outcount; i++) { srcsample = samplefrac >> 8; samplefrac += fracstep; if (inwidth == 2) sample = LittleShort ( ((short *)data)[srcsample] ); else sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8; if (sc->width == 2) ((short *)sc->data)[i] = sample; else ((signed char *)sc->data)[i] = sample >> 8; } } } //============================================================================= /* ============== S_LoadSound ============== */ sfxcache_t *S_LoadSound (sfx_t *s) { char namebuffer[256]; byte *data; wavinfo_t info; int len; float stepscale; sfxcache_t *sc; byte stackbuf[1*1024]; // avoid dirtying the cache heap // see if still in memory sc = (sfxcache_t *) Cache_Check (&s->cache); if (sc) return sc; // Con_Printf ("%s: %x\n", __thisfunc__, (int)stackbuf); // load it in q_strlcpy(namebuffer, "sound/", sizeof(namebuffer)); q_strlcat(namebuffer, s->name, sizeof(namebuffer)); // Con_Printf ("loading %s\n",namebuffer); data = FS_LoadStackFile(namebuffer, stackbuf, sizeof(stackbuf), NULL); if (!data) { Con_Printf ("Couldn't load %s\n", namebuffer); return NULL; } info = GetWavinfo (s->name, data, fs_filesize); if (info.channels != 1) { Con_Printf ("%s is a stereo sample\n",s->name); return NULL; } if (info.width != 1 && info.width != 2) { Con_Printf("%s is not 8 or 16 bit\n", s->name); return NULL; } stepscale = (float)info.rate / shm->speed; len = info.samples / stepscale; len = len * info.width * info.channels; if (info.samples == 0 || len == 0) { Con_Printf("%s has zero samples\n", s->name); return NULL; } sc = (sfxcache_t *) Cache_Alloc ( &s->cache, len + sizeof(sfxcache_t), s->name); if (!sc) return NULL; sc->length = info.samples; sc->loopstart = info.loopstart; sc->speed = info.rate; sc->width = info.width; sc->stereo = info.channels; ResampleSfx (s, sc->speed, sc->width, data + info.dataofs); return sc; } /* =============================================================================== WAV loading =============================================================================== */ static byte *data_p; static byte *iff_end; static byte *last_chunk; static byte *iff_data; static int iff_chunk_len; static short GetLittleShort (void) { short val = 0; val = *data_p; val = val + (*(data_p+1)<<8); data_p += 2; return val; } static int GetLittleLong (void) { int val = 0; val = *data_p; val = val + (*(data_p+1)<<8); val = val + (*(data_p+2)<<16); val = val + (*(data_p+3)<<24); data_p += 4; return val; } static void FindNextChunk (const char *name) { while (1) { // Need at least 8 bytes for a chunk if (last_chunk + 8 >= iff_end) { data_p = NULL; return; } data_p = last_chunk + 4; iff_chunk_len = GetLittleLong(); if (iff_chunk_len < 0 || iff_chunk_len > iff_end - data_p) { data_p = NULL; Con_DPrintf("bad \"%s\" chunk length (%d)\n", name, iff_chunk_len); return; } last_chunk = data_p + ((iff_chunk_len + 1) & ~1); data_p -= 8; if (!strncmp((char *)data_p, name, 4)) return; } } static void FindChunk (const char *name) { last_chunk = iff_data; FindNextChunk (name); } #if 0 static void DumpChunks (void) { char str[5]; str[4] = 0; data_p = iff_data; do { memcpy (str, data_p, 4); data_p += 4; iff_chunk_len = GetLittleLong(); Con_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len); data_p += (iff_chunk_len + 1) & ~1; } while (data_p < iff_end); } #endif /* ============ GetWavinfo ============ */ wavinfo_t GetWavinfo (const char *name, byte *wav, int wavlength) { wavinfo_t info; int i; int format; int samples; memset (&info, 0, sizeof(info)); if (!wav) return info; iff_data = wav; iff_end = wav + wavlength; // find "RIFF" chunk FindChunk("RIFF"); if (!(data_p && !strncmp((char *)data_p + 8, "WAVE", 4))) { Con_Printf("%s missing RIFF/WAVE chunks\n", name); return info; } // get "fmt " chunk iff_data = data_p + 12; #if 0 DumpChunks (); #endif FindChunk("fmt "); if (!data_p) { Con_Printf("%s is missing fmt chunk\n", name); return info; } data_p += 8; format = GetLittleShort(); if (format != WAV_FORMAT_PCM) { Con_Printf("%s is not Microsoft PCM format\n", name); return info; } info.channels = GetLittleShort(); info.rate = GetLittleLong(); data_p += 4 + 2; i = GetLittleShort(); if (i != 8 && i != 16) return info; info.width = i / 8; // get cue chunk FindChunk("cue "); if (data_p) { data_p += 32; info.loopstart = GetLittleLong(); // Con_Printf("loopstart=%d\n", sfx->loopstart); // if the next chunk is a LIST chunk, look for a cue length marker FindNextChunk ("LIST"); if (data_p) { if (!strncmp((char *)data_p + 28, "mark", 4)) { // this is not a proper parse, but it works with cooledit... data_p += 24; i = GetLittleLong(); // samples in loop info.samples = info.loopstart + i; // Con_Printf("looped length: %i\n", i); } } } else info.loopstart = -1; // find data chunk FindChunk("data"); if (!data_p) { Con_Printf("%s is missing data chunk\n", name); return info; } data_p += 4; samples = GetLittleLong() / info.width; if (info.samples) { if (samples < info.samples) Sys_Error ("%s has a bad loop length", name); } else info.samples = samples; info.dataofs = data_p - wav; return info; } engine/h2shared/snd_mikmod.c000066400000000000000000000137431444734033100163260ustar00rootroot00000000000000/* * tracker music (module file) decoding support using libmikmod * * Copyright (C) 2013 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(USE_CODEC_MIKMOD) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_mikmod.h" #include #if ((LIBMIKMOD_VERSION+0) < 0x030105) #error libmikmod version is way too old and unusable. #endif #if (LIBMIKMOD_VERSION < 0x030107) /* ancient libmikmod */ #define S_MIKMOD_initlib(c) MikMod_Init() #else #define S_MIKMOD_initlib(c) MikMod_Init(c) #endif #ifndef DMODE_NOISEREDUCTION #define DMODE_NOISEREDUCTION 0x1000 /* Low pass filtering */ #endif #ifndef DMODE_SIMDMIXER #define DMODE_SIMDMIXER 0x0800 /* enable SIMD mixing */ #endif typedef struct _mik_priv { /* MREADER core members in libmikmod2/3: */ int (*Seek)(struct MREADER*, long, int); long (*Tell)(struct MREADER*); BOOL (*Read)(struct MREADER*, void*, size_t); int (*Get)(struct MREADER*); BOOL (*Eof)(struct MREADER*); /* no iobase members in libmikmod <= 3.2.0-beta2 */ long iobase, prev_iobase; fshandle_t *fh; MODULE *module; } mik_priv_t; static int MIK_Seek (MREADER *r, long ofs, int whence) { return FS_fseek(((mik_priv_t *)r)->fh, ofs, whence); } static long MIK_Tell (MREADER *r) { return FS_ftell(((mik_priv_t *)r)->fh); } static BOOL MIK_Read (MREADER *r, void *ptr, size_t siz) { return !!FS_fread(ptr, siz, 1, ((mik_priv_t *)r)->fh); } static int MIK_Get (MREADER *r) { return FS_fgetc(((mik_priv_t *)r)->fh); } static BOOL MIK_Eof (MREADER *r) { return FS_feof(((mik_priv_t *)r)->fh); } static qboolean S_MIKMOD_CodecInitialize (void) { if (mikmod_codec.initialized) return true; /* set mode flags to only we like: */ md_mode = 0; if ((shm->samplebits / 8) == 2) md_mode |= DMODE_16BITS; if (shm->channels == 2) md_mode |= DMODE_STEREO; md_mode |= DMODE_SOFT_MUSIC; /* this is a software-only mixer */ /* md_mixfreq is UWORD, so something like 96000 isn't OK */ md_mixfreq = (shm->speed < 65536)? shm->speed : 48000; /* keeping md_device as 0 which is default (auto-detect: we * only register drv_nos, and it will be the only one found.) * md_pansep (stereo channels separation) default 128 is OK. * no reverbation (md_reverb 0 (up to 15)) is OK. * md_musicvolume and md_sndfxvolume defaults are 128: OK. */ /* just tone down overall volume md_volume from 128 to 96? */ md_volume = 96; MikMod_RegisterDriver(&drv_nos); /* only need the "nosound" driver, none else */ MikMod_RegisterAllLoaders(); if (S_MIKMOD_initlib(NULL)) { Con_DPrintf("Could not initialize MikMod: %s\n", MikMod_strerror(MikMod_errno)); return false; } /* this can't get set with drv_nos, but whatever, be safe: */ md_mode &= ~DMODE_SIMDMIXER; /* SIMD mixer is buggy when combined with HQMIXER */ mikmod_codec.initialized = true; return true; } static void S_MIKMOD_CodecShutdown (void) { if (mikmod_codec.initialized) { mikmod_codec.initialized = false; MikMod_Exit(); } } static qboolean S_MIKMOD_CodecOpenStream (snd_stream_t *stream) { mik_priv_t *priv; stream->priv = Z_Malloc(sizeof(mik_priv_t), Z_MAINZONE); priv = (mik_priv_t *) stream->priv; priv->Seek = MIK_Seek; priv->Tell = MIK_Tell; priv->Read = MIK_Read; priv->Get = MIK_Get; priv->Eof = MIK_Eof; priv->fh = &stream->fh; priv->module = Player_LoadGeneric((MREADER *)stream->priv, 64, 0); if (!priv->module) { Con_DPrintf("Could not load module: %s\n", MikMod_strerror(MikMod_errno)); Z_Free(stream->priv); return false; } /* default values of module options set by Player_Init(): * fadeout (0): don't fade out volume during when last position of the * module is being played, * extspd (1): process Protracker extended speed effect, * panflag (1): process panning effects, * wrap (0): don't wrap to restart position when module is finished, * loop (1): process all in-module loops -- possible backward loops * would make the module to loop endlessly. */ priv->module->wrap = stream->loop; Player_Start(priv->module); stream->info.rate = md_mixfreq; stream->info.bits = (md_mode & DMODE_16BITS)? 16: 8; stream->info.width = stream->info.bits / 8; stream->info.channels = (md_mode & DMODE_STEREO)? 2 : 1; /* Con_DPrintf("Playing %s (%d chn)\n", priv->module->songname, priv->module->numchn);*/ return true; } static int S_MIKMOD_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { if (!Player_Active()) return 0; /* handle possible loop setting change: */ ((mik_priv_t *)stream->priv)->module->wrap = stream->loop; return (int) VC_WriteBytes((SBYTE *)buffer, bytes); } static void S_MIKMOD_CodecCloseStream (snd_stream_t *stream) { Player_Stop(); Player_Free(((mik_priv_t *)stream->priv)->module); Z_Free(stream->priv); S_CodecUtilClose(&stream); } static int S_MIKMOD_CodecJumpToOrder (snd_stream_t *stream, int to) { Player_SetPosition ((UWORD)to); return 0; } static int S_MIKMOD_CodecRewindStream (snd_stream_t *stream) { Player_SetPosition (0); /* FIXME: WRONG: THIS IS NOT A TIME SEEK */ return 0; } snd_codec_t mikmod_codec = { CODECTYPE_MOD, false, "s3m", S_MIKMOD_CodecInitialize, S_MIKMOD_CodecShutdown, S_MIKMOD_CodecOpenStream, S_MIKMOD_CodecReadStream, S_MIKMOD_CodecRewindStream, S_MIKMOD_CodecJumpToOrder, S_MIKMOD_CodecCloseStream, NULL }; #endif /* USE_CODEC_MIKMOD */ engine/h2shared/snd_mikmod.h000066400000000000000000000003531444734033100163240ustar00rootroot00000000000000/* module tracker decoding support using libmikmod */ #if !defined(_SND_MIKMOD_H_) #define _SND_MIKMOD_H_ #if defined(USE_CODEC_MIKMOD) extern snd_codec_t mikmod_codec; #endif /* USE_CODEC_MIKMOD */ #endif /* ! _SND_MIKMOD_H_ */ engine/h2shared/snd_mix.c000066400000000000000000000174061444734033100156430ustar00rootroot00000000000000/* snd_mix.c -- portable code to mix sounds for snd_dma.c. * * Copyright (C) 1996-2001 Id Software, Inc. * Copyright (C) 2010-2011 O. Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #define PAINTBUFFER_SIZE 2048 ASM_LINKAGE_BEGIN /* global vars referenced by asm */ portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE]; int snd_scaletable[32][256]; int *snd_p, snd_linear_count; short *snd_out; ASM_LINKAGE_END static int snd_vol; #if !id386 static void Snd_WriteLinearBlastStereo16 (void) { int i; int val; for (i = 0; i < snd_linear_count; i += 2) { val = snd_p[i] >> 8; if (val > 0x7fff) snd_out[i] = 0x7fff; else if (val < (short)0x8000) snd_out[i] = (short)0x8000; else snd_out[i] = val; val = snd_p[i+1] >> 8; if (val > 0x7fff) snd_out[i+1] = 0x7fff; else if (val < (short)0x8000) snd_out[i+1] = (short)0x8000; else snd_out[i+1] = val; } } #endif static void S_TransferStereo16 (int endtime) { int lpos; int lpaintedtime; snd_p = (int *) paintbuffer; lpaintedtime = paintedtime; while (lpaintedtime < endtime) { // handle recirculating buffer issues lpos = lpaintedtime & ((shm->samples >> 1) - 1); snd_out = (short *)shm->buffer + (lpos << 1); snd_linear_count = (shm->samples >> 1) - lpos; if (lpaintedtime + snd_linear_count > endtime) snd_linear_count = endtime - lpaintedtime; snd_linear_count <<= 1; // write a linear blast of samples Snd_WriteLinearBlastStereo16 (); snd_p += snd_linear_count; lpaintedtime += (snd_linear_count >> 1); } } static void S_TransferPaintBuffer (int endtime) { int out_idx, out_mask; int count, step, val; int *p; if (shm->samplebits == 16 && shm->channels == 2) { S_TransferStereo16 (endtime); return; } #if id68k if (shm->channels == 2 && ((shm->samplebits == 8 && (shm->signed8 & 2)) || shm->samplebits == 16)) { S_TransferStereoAmiga (endtime); return; } #endif p = (int *) paintbuffer; count = (endtime - paintedtime) * shm->channels; out_mask = shm->samples - 1; out_idx = paintedtime * shm->channels & out_mask; step = 3 - shm->channels; if (shm->samplebits == 16) { short *out = (short *)shm->buffer; while (count--) { val = *p >> 8; p+= step; if (val > 0x7fff) val = 0x7fff; else if (val < (short)0x8000) val = (short)0x8000; out[out_idx] = val; out_idx = (out_idx + 1) & out_mask; } } else if (shm->samplebits == 8 && !shm->signed8) { unsigned char *out = shm->buffer; while (count--) { val = *p >> 8; p+= step; if (val > 0x7fff) val = 0x7fff; else if (val < (short)0x8000) val = (short)0x8000; out[out_idx] = (val >> 8) + 128; out_idx = (out_idx + 1) & out_mask; } } else if (shm->samplebits == 8) /* S8 format, e.g. with Amiga AHI */ { signed char *out = (signed char *) shm->buffer; while (count--) { val = *p >> 8; p+= step; if (val > 0x7fff) val = 0x7fff; else if (val < (short)0x8000) val = (short)0x8000; out[out_idx] = (val >> 8); out_idx = (out_idx + 1) & out_mask; } } } /* =============================================================================== CHANNEL MIXING =============================================================================== */ #if !id386 static void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime); #endif static void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime); void S_PaintChannels (int endtime) { int i; int end, ltime, count; channel_t *ch; sfxcache_t *sc; snd_vol = sfxvolume.value * 256; while (paintedtime < endtime) { // if paintbuffer is smaller than DMA buffer end = endtime; if (endtime - paintedtime > PAINTBUFFER_SIZE) end = paintedtime + PAINTBUFFER_SIZE; // clear the paint buffer if (s_rawend < paintedtime) { // clear memset(paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t)); } else { // copy from the streaming sound source int s; int stop; stop = (end < s_rawend) ? end : s_rawend; for (i = paintedtime; i < stop; i++) { s = i & (MAX_RAW_SAMPLES - 1); paintbuffer[i - paintedtime] = s_rawsamples[s]; } // if (i != end) // Con_Printf ("partial stream\n"); // else // Con_Printf ("full stream\n"); for ( ; i < end; i++) { paintbuffer[i - paintedtime].left = paintbuffer[i - paintedtime].right = 0; } } // paint in the channels. ch = snd_channels; for (i = 0; i < total_channels; i++, ch++) { if (!ch->sfx) continue; if (!ch->leftvol && !ch->rightvol) continue; sc = S_LoadSound (ch->sfx); if (!sc) continue; ltime = paintedtime; while (ltime < end) { // paint up to end if (ch->end < end) count = ch->end - ltime; else count = end - ltime; if (count > 0) { if (sc->width == 1) SND_PaintChannelFrom8(ch, sc, count); else SND_PaintChannelFrom16(ch, sc, count); ltime += count; } // if at end of loop, restart if (ltime >= ch->end) { if (sc->loopstart >= 0) { ch->pos = sc->loopstart; ch->end = ltime + sc->length - ch->pos; } else { // channel just stopped ch->sfx = NULL; break; } } } } // transfer out according to DMA format S_TransferPaintBuffer(end); paintedtime = end; } } void SND_InitScaletable (void) { int i, j; int scale; for (i = 0; i < 32; i++) { scale = i * 8 * 256 * sfxvolume.value; for (j = 0; j < 256; j++) { /* When compiling with gcc-4.1.0 at optimisations O1 and higher, the tricky signed char type conversion is not guaranteed. Therefore we explicity calculate the signed value from the index as required. From Kevin Shanahan. See: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=26719 */ // snd_scaletable[i][j] = ((signed char)j) * scale; snd_scaletable[i][j] = ((j < 128) ? j : j - 256) * scale; } } } #if !id386 static void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count) { int data; int *lscale, *rscale; unsigned char *sfx; int i; if (ch->leftvol > 255) ch->leftvol = 255; if (ch->rightvol > 255) ch->rightvol = 255; lscale = snd_scaletable[ch->leftvol >> 3]; rscale = snd_scaletable[ch->rightvol >> 3]; sfx = (unsigned char *)sc->data + ch->pos; for (i = 0; i < count; i++) { data = sfx[i]; paintbuffer[i].left += lscale[data]; paintbuffer[i].right += rscale[data]; } ch->pos += count; } #endif /* !id386 */ static void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count) { int data; int left, right; int leftvol, rightvol; signed short *sfx; int i; leftvol = ch->leftvol * snd_vol; rightvol = ch->rightvol * snd_vol; leftvol >>= 8; rightvol >>= 8; sfx = (signed short *)sc->data + ch->pos; for (i = 0; i < count; i++) { data = sfx[i]; // this was causing integer overflow as observed in quakespasm // with the warpspasm mod moved >>8 to left/right volume above. // left = (data * leftvol) >> 8; // right = (data * rightvol) >> 8; left = data * leftvol; right = data * rightvol; paintbuffer[i].left += left; paintbuffer[i].right += right; } ch->pos += count; } engine/h2shared/snd_mixa.asm000066400000000000000000000070201444734033100163310ustar00rootroot00000000000000; ; snd_mixa.asm ; x86 assembly-language sound code ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix snd_scaletable _sym_prefix paintbuffer _sym_prefix snd_linear_count _sym_prefix snd_p ;_sym_prefix snd_vol _sym_prefix snd_out ; C-shared globals: _sym_prefix SND_PaintChannelFrom8 _sym_prefix Snd_WriteLinearBlastStereo16 %endif ; _sym_prefix ; externs from C code extern snd_scaletable extern paintbuffer extern snd_linear_count extern snd_p ;extern snd_vol extern snd_out ; externs from ASM-only code SEGMENT .text global SND_PaintChannelFrom8 SND_PaintChannelFrom8: push esi push edi push ebx push ebp mov ebx, dword [4+16+esp] mov esi, dword [8+16+esp] mov eax, dword [4+ebx] mov edx, dword [8+ebx] cmp eax,255 jna LLeftSet mov eax,255 LLeftSet: cmp edx,255 jna LRightSet mov edx,255 LRightSet: and eax,0F8h add esi,20 and edx,0F8h mov edi, dword [16+ebx] mov ecx, dword [12+16+esp] add esi,edi shl eax,7 add edi,ecx shl edx,7 mov dword [16+ebx],edi add eax,offset snd_scaletable add edx,offset snd_scaletable sub ebx,ebx mov bl, byte [-1+esi+ecx*1] test ecx,1 jz LMix8Loop mov edi, dword [eax+ebx*4] mov ebp, dword [edx+ebx*4] add edi, dword [paintbuffer+0-8+ecx*8] add ebp, dword [paintbuffer+4-8+ecx*8] mov dword [paintbuffer+0-8+ecx*8],edi mov dword [paintbuffer+4-8+ecx*8],ebp mov bl, byte [-2+esi+ecx*1] dec ecx jz LDone LMix8Loop: mov edi, dword [eax+ebx*4] mov ebp, dword [edx+ebx*4] add edi, dword [paintbuffer+0-8+ecx*8] add ebp, dword [paintbuffer+4-8+ecx*8] mov bl, byte [-2+esi+ecx*1] mov dword [paintbuffer+0-8+ecx*8],edi mov dword [paintbuffer+4-8+ecx*8],ebp mov edi, dword [eax+ebx*4] mov ebp, dword [edx+ebx*4] mov bl, byte [-3+esi+ecx*1] add edi, dword [paintbuffer+0-8*2+ecx*8] add ebp, dword [paintbuffer+4-8*2+ecx*8] mov dword [paintbuffer+0-8*2+ecx*8],edi mov dword [paintbuffer+4-8*2+ecx*8],ebp sub ecx,2 jnz LMix8Loop LDone: pop ebp pop ebx pop edi pop esi ret global Snd_WriteLinearBlastStereo16 Snd_WriteLinearBlastStereo16: ;push esi push edi push ebx mov ecx, dword [snd_linear_count] mov ebx, dword [snd_p] ;mov esi, dword [snd_vol] mov edi, dword [snd_out] LWLBLoopTop: mov eax, dword [-8+ebx+ecx*4] ;imul eax,esi sar eax,8 cmp eax,07FFFh jg LClampHigh cmp eax,0FFFF8000h jnl LClampDone mov eax,0FFFF8000h jmp LClampDone LClampHigh: mov eax,07FFFh LClampDone: mov edx, dword [-4+ebx+ecx*4] ;imul edx,esi sar edx,8 cmp edx,07FFFh jg LClampHigh2 cmp edx,0FFFF8000h jnl LClampDone2 mov edx,0FFFF8000h jmp LClampDone2 LClampHigh2: mov edx,07FFFh LClampDone2: shl edx,16 and eax,0FFFFh or edx,eax mov dword [-4+edi+ecx*2],edx sub ecx,2 jnz LWLBLoopTop pop ebx pop edi ;pop esi ret engine/h2shared/snd_mixamiga68k.s000066400000000000000000000105261444734033100172070ustar00rootroot00000000000000** ** Sound mixing routines for Amiga 68k ** Written by Frank Wille ** ** This implementation of S_TransferPaintBuffer() handles the following ** two formats: ** ** 1. 8-bits STEREO for Amiga native Paula sound-chip. ** DMA buffer layout (8 bits signed samples): ** <-- shm.samples bytes left ch. --><-- shm.samples bytes right ch. --> ** ** 2. 16-bits STEREO for AHI. ** DMA buffer layout (16 bits signed big-endian samples): ** <-- shm.samples * (<16bits left ch.>|<16 bits right ch.>) --> ** ; INCLUDE "quakedef68k.i" dma_samples equ 4 dma_samplebits equ 16 dma_buffer equ 28 code xref _shm xref _paintbuffer xref _paintedtime ; xref _volume xdef _S_TransferStereoAmiga cnop 0,4 _S_TransferStereoAmiga: movem.l d2-d7/a2-a3,-(sp) setso 4+8*4 .endtim so.l 1 move.l _shm,a1 ; a1 shm (struct dma_t) lea _paintbuffer,a0 ; a0 paintbuffer (int left,int right) ; fmove.s _volume+16,fp0 ; volume.value * 256 move.l .endtim(sp),d2 ; fmul.s #256.0,fp0 move.l _paintedtime,d6 sub.l d6,d2 ; d2 count beq .exit move.l dma_buffer(a1),a3 ; a3 dma buffer start address ; fmove.l fp0,d3 ; d3 snd_vol move.l dma_samples(a1),d0 cmp.l #8,dma_samplebits(a1) beq .paula8bit ; 16-bit AHI transfer lea (a3,d0.l*2),a2 ; a2 dma buffer end address lsr.l #1,d0 subq.l #1,d0 and.l d0,d6 lea (a3,d6.l*4),a1 ; a1 out move.l #$7fff,d4 ; d4 max val move.l d4,d5 not.l d5 ; d5 min val move.l a2,d6 sub.l a3,d6 ; d6 buffer size .loop16: move.l (a0)+,d0 ; muls.l d3,d0 move.l (a0)+,d1 asr.l #8,d0 ; muls.l d3,d1 cmp.l d4,d0 ble.b .161 move.l d4,d0 bra.b .162 .161: cmp.l d5,d0 bge.b .162 move.l d5,d0 .162: asr.l #8,d1 swap d0 cmp.l d4,d1 ble.b .163 move.l d4,d1 bra.b .164 .163: cmp.l d5,d1 bge.b .164 move.l d5,d1 .164: move.w d1,d0 ; d0 leftCh16.W | rightCh16.W move.l d0,(a1)+ cmp.l a2,a1 blo.b .165 sub.l d6,a1 .165: subq.l #1,d2 bne.b .loop16 movem.l (sp)+,d2-d7/a2-a3 rts .paula8bit: lsr.l #1,d0 ; dma_samples / 2 lea (a3,d0.l),a2 ; a2 dma buffer end address move.l d0,d1 subq.l #1,d0 and.l d0,d6 lea (a3,d6.l),a1 ; a1 out moveq #$7f,d4 ; d4 max val moveq #-$80,d5 ; d5 min val move.l a2,d6 sub.l a3,d6 ; d6 buffer size move.l d1,a3 ; a3 stereo buffer offset .loop8: move.l a1,d0 and.b #$fc,d0 ; 32-bit aligned output? beq.b .loop8aligned .loop8unaligned: move.l (a0)+,d0 ; muls.l d3,d0 move.l (a0)+,d1 swap d0 ; muls.l d3,d1 cmp.w d4,d0 ble.b .1 move.w d4,d0 bra.b .2 .1: cmp.w d5,d0 bge.b .2 move.w d5,d0 .2: move.b d0,(a1)+ ; left channel swap d1 cmp.w d4,d1 ble.b .3 move.w d4,d1 bra.b .4 .3: cmp.w d5,d1 bge.b .4 move.w d5,d1 .4: move.b d1,-1(a1,a3.l) ; right channel cmp.l a2,a1 blo.b .5 sub.l d6,a1 .5: subq.l #1,d2 bne.b .loop8 bra .exit .loop8aligned: cmp.l #4,d2 blo.b .loop8unaligned move.l (a0)+,d0 ; muls.l d3,d0 swap d0 cmp.w d4,d0 ble.b .10 move.w d4,d0 bra.b .11 .10: cmp.w d5,d0 bge.b .11 move.w d5,d0 .11: lsl.w #8,d0 ; left ch. byte 0 move.l (a0)+,d1 ; muls.l d3,d1 swap d1 cmp.w d4,d1 ble.b .12 move.w d4,d1 bra.b .13 .12: cmp.w d5,d1 bge.b .13 move.w d5,d1 .13: lsl.w #8,d1 ; right ch. byte 0 move.l (a0)+,d7 ; muls.l d3,d7 swap d7 cmp.w d4,d7 ble.b .14 move.w d4,d7 bra.b .15 .14: cmp.w d5,d7 bge.b .15 move.w d5,d7 .15: move.b d7,d0 ; left ch. byte 1 swap d0 move.l (a0)+,d7 ; muls.l d3,d7 swap d7 cmp.w d4,d7 ble.b .16 move.w d4,d7 bra.b .17 .16: cmp.w d5,d7 bge.b .17 move.w d5,d7 .17: move.b d7,d1 ; right ch. byte 1 swap d1 move.l (a0)+,d7 ; muls.l d3,d7 swap d7 cmp.w d4,d7 ble.b .18 move.w d4,d7 bra.b .19 .18: cmp.w d5,d7 bge.b .19 move.w d5,d7 .19: lsl.w #8,d7 move.w d7,d0 ; left ch. byte 2 move.l (a0)+,d7 ; muls.l d3,d7 swap d7 cmp.w d4,d7 ble.b .20 move.w d4,d7 bra.b .21 .20: cmp.w d5,d7 bge.b .21 move.w d5,d7 .21: lsl.w #8,d7 move.w d7,d1 ; right ch. byte 2 move.l (a0)+,d7 ; muls.l d3,d7 swap d7 cmp.w d4,d7 ble.b .22 move.w d4,d7 bra.b .23 .22: cmp.w d5,d7 bge.b .23 move.w d5,d7 .23: move.b d7,d0 ; left ch. byte 3 move.l (a0)+,d7 ; muls.l d3,d7 swap d7 move.l d0,(a1)+ ; write left channel cmp.w d4,d7 ble.b .24 move.w d4,d7 bra.b .25 .24: cmp.w d5,d7 bge.b .25 move.w d5,d7 .25: move.b d7,d1 ; right ch. byte 3 move.l d1,-4(a1,a3.l) ; write right channel cmp.l a2,a1 blo.b .30 sub.l d6,a1 .30: subq.l #4,d2 bne .loop8aligned .exit: movem.l (sp)+,d2-d7/a2-a3 rts engine/h2shared/snd_modplug.c000066400000000000000000000064661444734033100165210ustar00rootroot00000000000000/* * tracker music (module file) decoding support using libmodplug * * Copyright (C) 2013 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(USE_CODEC_MODPLUG) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_modplug.h" #include static void S_MODPLUG_SetSettings (snd_stream_t *stream) { ModPlug_Settings settings; ModPlug_GetSettings(&settings); settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING; settings.mChannels = shm->channels; settings.mBits = shm->samplebits; settings.mFrequency = shm->speed; settings.mResamplingMode = MODPLUG_RESAMPLE_SPLINE;/*MODPLUG_RESAMPLE_FIR*/ settings.mLoopCount = -1; /* to enable module internal loops */ ModPlug_SetSettings(&settings); if (stream) { stream->info.rate = shm->speed; stream->info.bits = shm->samplebits; stream->info.width = stream->info.bits / 8; stream->info.channels = shm->channels; } } static qboolean S_MODPLUG_CodecInitialize (void) { return true; } static void S_MODPLUG_CodecShutdown (void) { } static qboolean S_MODPLUG_CodecOpenStream (snd_stream_t *stream) { /* need to load the whole file into memory and pass it to libmodplug */ byte *moddata; long len; int mark; len = FS_filelength (&stream->fh); mark = Hunk_LowMark(); moddata = (byte *) Hunk_Alloc(len); FS_fread(moddata, 1, len, &stream->fh); S_MODPLUG_SetSettings(stream); stream->priv = ModPlug_Load(moddata, len); Hunk_FreeToLowMark(mark); /* free original file data */ if (!stream->priv) { Con_DPrintf("Could not load module %s\n", stream->name); return false; } ModPlug_Seek((ModPlugFile*)stream->priv, 0); /* default volume (128) sounds rather low? */ ModPlug_SetMasterVolume((ModPlugFile*)stream->priv, 384); /* 0-512 */ return true; } static int S_MODPLUG_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { return ModPlug_Read((ModPlugFile*)stream->priv, buffer, bytes); } static void S_MODPLUG_CodecCloseStream (snd_stream_t *stream) { ModPlug_Unload((ModPlugFile*)stream->priv); S_CodecUtilClose(&stream); } static int S_MODPLUG_CodecJumpToOrder (snd_stream_t *stream, int to) { ModPlug_SeekOrder((ModPlugFile*)stream->priv, to); return 0; } static int S_MODPLUG_CodecRewindStream (snd_stream_t *stream) { ModPlug_Seek((ModPlugFile*)stream->priv, 0); return 0; } snd_codec_t modplug_codec = { CODECTYPE_MOD, true, /* always available. */ "s3m", S_MODPLUG_CodecInitialize, S_MODPLUG_CodecShutdown, S_MODPLUG_CodecOpenStream, S_MODPLUG_CodecReadStream, S_MODPLUG_CodecRewindStream, S_MODPLUG_CodecJumpToOrder, S_MODPLUG_CodecCloseStream, NULL }; #endif /* USE_CODEC_MODPLUG */ engine/h2shared/snd_modplug.h000066400000000000000000000003611444734033100165120ustar00rootroot00000000000000/* module tracker decoding support using libmodplug */ #if !defined(_SND_MODPLUG_H_) #define _SND_MODPLUG_H_ #if defined(USE_CODEC_MODPLUG) extern snd_codec_t modplug_codec; #endif /* USE_CODEC_MODPLUG */ #endif /* ! _SND_MODPLUG_H_ */ engine/h2shared/snd_mp3.c000066400000000000000000000301361444734033100155400ustar00rootroot00000000000000/* MP3 decoding support using libmad: Adapted from the SoX library at * http://sourceforge.net/projects/sox/, LGPLv2, Copyright (c) 2007-2009 * SoX contributors, written by Fabrizio Gennari , * with the decoding part based on the decoder tutorial program madlld * written by Bertrand Petit (BSD license, see at * http://www.bsd-dk.dk/~elrond/audio/madlld/). * Adapted for use in Quake and Hexen II game engines by O.Sezer: * Copyright (C) 2010-2019 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(USE_CODEC_MP3) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_mp3.h" #include /* Under Windows, importing data from DLLs is a dicey proposition. This is true * when using dlopen, but also true if linking directly against the DLL if the * header does not mark the data as __declspec(dllexport), which mad.h does not. * Sidestep the issue by defining our own mad_timer_zero. This is needed because * mad_timer_zero is used in some of the mad.h macros. */ #define mad_timer_zero mad_timer_zero_stub static mad_timer_t const mad_timer_zero_stub = {0, 0}; /* MAD returns values with MAD_F_FRACBITS (28) bits of precision, though it's not certain that all of them are meaningful. Default to 16 bits to align with most users expectation of output file should be 16 bits. */ #define MP3_MAD_SAMPLEBITS 16 #define MP3_MAD_SAMPLEWIDTH 2 #define MP3_BUFFER_SIZE (5 * 8192) /* Private data */ typedef struct _mp3_priv_t { unsigned char mp3_buffer[MP3_BUFFER_SIZE]; struct mad_stream Stream; struct mad_frame Frame; struct mad_synth Synth; mad_timer_t Timer; ptrdiff_t cursamp; size_t FrameCount; } mp3_priv_t; /* (Re)fill the stream buffer that is to be decoded. If any data * still exists in the buffer then they are first shifted to be * front of the stream buffer. */ static int mp3_inputdata(snd_stream_t *stream) { mp3_priv_t *p = (mp3_priv_t *) stream->priv; size_t bytes_read; size_t remaining; remaining = p->Stream.bufend - p->Stream.next_frame; /* libmad does not consume all the buffer it's given. Some * data, part of a truncated frame, is left unused at the * end of the buffer. That data must be put back at the * beginning of the buffer and taken in account for * refilling the buffer. This means that the input buffer * must be large enough to hold a complete frame at the * highest observable bit-rate (currently 448 kb/s). * TODO: Is 2016 bytes the size of the largest frame? * (448000*(1152/32000))/8 */ memmove(p->mp3_buffer, p->Stream.next_frame, remaining); bytes_read = FS_fread(p->mp3_buffer + remaining, 1, MP3_BUFFER_SIZE - remaining, &stream->fh); if (bytes_read == 0) return -1; mad_stream_buffer(&p->Stream, p->mp3_buffer, bytes_read+remaining); p->Stream.error = MAD_ERROR_NONE; return 0; } static int mp3_startread(snd_stream_t *stream) { mp3_priv_t *p = (mp3_priv_t *) stream->priv; size_t ReadSize; mad_stream_init(&p->Stream); mad_frame_init(&p->Frame); mad_synth_init(&p->Synth); mad_timer_reset(&p->Timer); /* Decode at least one valid frame to find out the input * format. The decoded frame will be saved off so that it * can be processed later. */ ReadSize = FS_fread(p->mp3_buffer, 1, MP3_BUFFER_SIZE, &stream->fh); if (!ReadSize || FS_ferror(&stream->fh)) return -1; mad_stream_buffer(&p->Stream, p->mp3_buffer, ReadSize); /* Find a valid frame before starting up. This makes sure * that we have a valid MP3. */ p->Stream.error = MAD_ERROR_NONE; while (mad_frame_decode(&p->Frame,&p->Stream)) { /* check whether input buffer needs a refill */ if (p->Stream.error == MAD_ERROR_BUFLEN) { if (mp3_inputdata(stream) == -1) return -1;/* EOF with no valid data */ continue; } /* We know that a valid frame hasn't been found yet * so help libmad out and go back into frame seek mode. */ mad_stream_sync(&p->Stream); p->Stream.error = MAD_ERROR_NONE; } if (p->Stream.error) { Con_Printf("MP3: No valid MP3 frame found\n"); return -1; } switch(p->Frame.header.mode) { case MAD_MODE_SINGLE_CHANNEL: case MAD_MODE_DUAL_CHANNEL: case MAD_MODE_JOINT_STEREO: case MAD_MODE_STEREO: stream->info.channels = MAD_NCHANNELS(&p->Frame.header); break; default: Con_Printf("MP3: Cannot determine number of channels\n"); return -1; } p->FrameCount = 1; mad_timer_add(&p->Timer,p->Frame.header.duration); mad_synth_frame(&p->Synth,&p->Frame); stream->info.rate = p->Synth.pcm.samplerate; stream->info.bits = MP3_MAD_SAMPLEBITS; stream->info.width = MP3_MAD_SAMPLEWIDTH; p->cursamp = 0; return 0; } /* Read up to len samples from p->Synth * If needed, read some more MP3 data, decode them and synth them * Place in buf[]. * Return number of samples read. */ static int mp3_decode(snd_stream_t *stream, byte *buf, int len) { mp3_priv_t *p = (mp3_priv_t *) stream->priv; int donow, i, done = 0; mad_fixed_t sample; int chan, x; do { x = (p->Synth.pcm.length - p->cursamp) * stream->info.channels; donow = q_min(len, x); i = 0; while (i < donow) { for (chan = 0; chan < stream->info.channels; chan++) { sample = p->Synth.pcm.samples[chan][p->cursamp]; /* convert from fixed to short, * write in host-endian format. */ if (sample <= -MAD_F_ONE) sample = -0x7FFF; else if (sample >= MAD_F_ONE) sample = 0x7FFF; else sample >>= (MAD_F_FRACBITS + 1 - 16); #if (ENDIAN_RUNTIME_DETECT + 0) != 0 if (host_bigendian) { *buf++ = (sample >> 8) & 0xFF; *buf++ = sample & 0xFF; } else /* assumed LITTLE_ENDIAN. */ { *buf++ = sample & 0xFF; *buf++ = (sample >> 8) & 0xFF; } #elif (BYTE_ORDER == BIG_ENDIAN) *buf++ = (sample >> 8) & 0xFF; *buf++ = sample & 0xFF; #else *buf++ = sample & 0xFF; *buf++ = (sample >> 8) & 0xFF; #endif i++; } p->cursamp++; } len -= donow; done += donow; if (len == 0) break; /* check whether input buffer needs a refill */ if (p->Stream.error == MAD_ERROR_BUFLEN) { if (mp3_inputdata(stream) == -1) { /* check feof() ?? */ Con_DPrintf("mp3 EOF\n"); break; } } if (mad_frame_decode(&p->Frame, &p->Stream)) { if (MAD_RECOVERABLE(p->Stream.error)) { mad_stream_sync(&p->Stream); /* to frame seek mode */ continue; } else { if (p->Stream.error == MAD_ERROR_BUFLEN) continue; else { Con_Printf("MP3: unrecoverable frame level error (%s)\n", mad_stream_errorstr(&p->Stream)); break; } } } p->FrameCount++; mad_timer_add(&p->Timer, p->Frame.header.duration); mad_synth_frame(&p->Synth, &p->Frame); p->cursamp = 0; } while (1); return done; } static int mp3_stopread(snd_stream_t *stream) { mp3_priv_t *p = (mp3_priv_t*) stream->priv; mad_synth_finish(&p->Synth); mad_frame_finish(&p->Frame); mad_stream_finish(&p->Stream); return 0; } static int mp3_madseek(snd_stream_t *stream, unsigned long offset) { mp3_priv_t *p = (mp3_priv_t *) stream->priv; size_t initial_bitrate = p->Frame.header.bitrate; size_t consumed = 0; int vbr = 0; /* Variable Bit Rate, bool */ qboolean depadded = false; unsigned long to_skip_samples = 0; /* Reset all */ FS_rewind(&stream->fh); mad_timer_reset(&p->Timer); p->FrameCount = 0; /* They where opened in startread */ mad_synth_finish(&p->Synth); mad_frame_finish(&p->Frame); mad_stream_finish(&p->Stream); mad_stream_init(&p->Stream); mad_frame_init(&p->Frame); mad_synth_init(&p->Synth); offset /= stream->info.channels; to_skip_samples = offset; while (1) /* Read data from the MP3 file */ { int bytes_read, padding = 0; size_t leftover = p->Stream.bufend - p->Stream.next_frame; memcpy(p->mp3_buffer, p->Stream.this_frame, leftover); bytes_read = FS_fread(p->mp3_buffer + leftover, (size_t) 1, MP3_BUFFER_SIZE - leftover, &stream->fh); if (bytes_read <= 0) { Con_DPrintf("seek failure. unexpected EOF (frames=%lu leftover=%lu)\n", (unsigned long)p->FrameCount, (unsigned long)leftover); break; } for ( ; !depadded && padding < bytes_read && !p->mp3_buffer[padding]; ++padding) ; depadded = true; mad_stream_buffer(&p->Stream, p->mp3_buffer + padding, leftover + bytes_read - padding); while (1) /* Decode frame headers */ { static unsigned short samples; p->Stream.error = MAD_ERROR_NONE; /* Not an audio frame */ if (mad_header_decode(&p->Frame.header, &p->Stream) == -1) { if (p->Stream.error == MAD_ERROR_BUFLEN) break; /* Normal behaviour; get some more data from the file */ if (!MAD_RECOVERABLE(p->Stream.error)) { Con_DPrintf("unrecoverable MAD error\n"); break; } if (p->Stream.error == MAD_ERROR_LOSTSYNC) { Con_DPrintf("MAD lost sync\n"); } else { Con_DPrintf("recoverable MAD error\n"); } continue; } consumed += p->Stream.next_frame - p->Stream.this_frame; vbr |= (p->Frame.header.bitrate != initial_bitrate); samples = 32 * MAD_NSBSAMPLES(&p->Frame.header); p->FrameCount++; mad_timer_add(&p->Timer, p->Frame.header.duration); if (to_skip_samples <= samples) { mad_frame_decode(&p->Frame,&p->Stream); mad_synth_frame(&p->Synth, &p->Frame); p->cursamp = to_skip_samples; return 0; } else to_skip_samples -= samples; /* If not VBR, we can extrapolate frame size */ if (p->FrameCount == 64 && !vbr) { p->FrameCount = offset / samples; to_skip_samples = offset % samples; if (0 != FS_fseek(&stream->fh, (p->FrameCount * consumed / 64), SEEK_SET)) return -1; /* Reset Stream for refilling buffer */ mad_stream_finish(&p->Stream); mad_stream_init(&p->Stream); break; } } } return -1; } static qboolean S_MP3_CodecInitialize (void) { return true; } static void S_MP3_CodecShutdown (void) { } static qboolean S_MP3_CodecOpenStream (snd_stream_t *stream) { int err; if (mp3_skiptags(stream) < 0) { Con_Printf("Corrupt mp3 file (bad tags.)\n"); return false; } #if defined(CODECS_USE_ZONE) stream->priv = Z_Malloc(sizeof(mp3_priv_t), Z_SECZONE); #else stream->priv = calloc(1, sizeof(mp3_priv_t)); if (!stream->priv) { Con_Printf("Insufficient memory for MP3 audio\n"); return false; } #endif err = mp3_startread(stream); if (err != 0) { Con_Printf("%s is not a valid mp3 file\n", stream->name); } else if (stream->info.channels != 1 && stream->info.channels != 2) { Con_Printf("Unsupported number of channels %d in %s\n", stream->info.channels, stream->name); } else { return true; } #if defined(CODECS_USE_ZONE) Z_Free(stream->priv); #else free(stream->priv); #endif return false; } static int S_MP3_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { int res = mp3_decode(stream, (byte *)buffer, bytes / stream->info.width); return res * stream->info.width; } static void S_MP3_CodecCloseStream (snd_stream_t *stream) { mp3_stopread(stream); #if defined(CODECS_USE_ZONE) Z_Free(stream->priv); #else free(stream->priv); #endif S_CodecUtilClose(&stream); } static int S_MP3_CodecRewindStream (snd_stream_t *stream) { /* mp3_stopread(stream); FS_rewind(&stream->fh); return mp3_startread(stream); */ return mp3_madseek(stream, 0); } snd_codec_t mp3_codec = { CODECTYPE_MP3, true, /* always available. */ "mp3", S_MP3_CodecInitialize, S_MP3_CodecShutdown, S_MP3_CodecOpenStream, S_MP3_CodecReadStream, S_MP3_CodecRewindStream, NULL, /* jump */ S_MP3_CodecCloseStream, NULL }; #endif /* USE_CODEC_MP3 */ engine/h2shared/snd_mp3.h000066400000000000000000000003731444734033100155450ustar00rootroot00000000000000/* MP3 decoding support using libmad or libmpg123. */ #if !defined(_SND_MP3_H_) #define _SND_MP3_H_ #if defined(USE_CODEC_MP3) extern snd_codec_t mp3_codec; int mp3_skiptags(snd_stream_t *); #endif /* USE_CODEC_MP3 */ #endif /* ! _SND_MP3_H_ */ engine/h2shared/snd_mp3tag.c000066400000000000000000000344611444734033100162410ustar00rootroot00000000000000/* MP3 TAGS STUFF -- put together using public specs. * Copyright (C) 2018-2019 O. Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(USE_CODEC_MP3) #include "snd_codec.h" #include "q_ctype.h" static inline qboolean is_id3v1(const unsigned char *data, long length) { /* http://id3.org/ID3v1 : 3 bytes "TAG" identifier and 125 bytes tag data */ if (length < 128 || memcmp(data,"TAG",3) != 0) { return false; } return true; } static qboolean is_id3v2(const unsigned char *data, size_t length) { /* ID3v2 header is 10 bytes: http://id3.org/id3v2.4.0-structure */ /* bytes 0-2: "ID3" identifier */ if (length < 10 || memcmp(data,"ID3",3) != 0) { return false; } /* bytes 3-4: version num (major,revision), each byte always less than 0xff. */ if (data[3] == 0xff || data[4] == 0xff) { return false; } /* bytes 6-9 are the ID3v2 tag size: a 32 bit 'synchsafe' integer, i.e. the * highest bit 7 in each byte zeroed. i.e.: 7 bit information in each byte -> * effectively a 28 bit value. */ if (data[6] >= 0x80 || data[7] >= 0x80 || data[8] >= 0x80 || data[9] >= 0x80) { return false; } return true; } static long get_id3v2_len(const unsigned char *data, long length) { /* size is a 'synchsafe' integer (see above) */ long size = (long)((data[6]<<21) + (data[7]<<14) + (data[8]<<7) + data[9]); size += 10; /* header size */ /* ID3v2 header[5] is flags (bits 4-7 only, 0-3 are zero). * bit 4 set: footer is present (a copy of the header but * with "3DI" as ident.) */ if (data[5] & 0x10) { size += 10; /* footer size */ } /* optional padding (always zeroes) */ while (size < length && data[size] == 0) { ++size; } return size; } static qboolean is_apetag(const unsigned char *data, size_t length) { /* http://wiki.hydrogenaud.io/index.php?title=APEv2_specification * Header/footer is 32 bytes: bytes 0-7 ident, bytes 8-11 version, * bytes 12-17 size. bytes 24-31 are reserved: must be all zeroes. */ unsigned int v; if (length < 32 || memcmp(data,"APETAGEX",8) != 0) { return false; } v = (unsigned)((data[11]<<24) | (data[10]<<16) | (data[9]<<8) | data[8]); /* version */ if (v != 2000U && v != 1000U) { return false; } v = 0; /* reserved bits : */ if (memcmp(&data[24],&v,4) != 0 || memcmp(&data[28],&v,4) != 0) { return false; } return true; } static long get_ape_len(const unsigned char *data) { unsigned int flags, version; long size = (long)((data[15]<<24) | (data[14]<<16) | (data[13]<<8) | data[12]); version = (unsigned)((data[11]<<24) | (data[10]<<16) | (data[9]<<8) | data[8]); flags = (unsigned)((data[23]<<24) | (data[22]<<16) | (data[21]<<8) | data[20]); if (version == 2000U && (flags & (1U<<31))) size += 32; /* header present. */ return size; } static inline int is_lyrics3tag(const unsigned char *data, long length) { /* http://id3.org/Lyrics3 * http://id3.org/Lyrics3v2 */ if (length < 15) return 0; if (memcmp(data+6,"LYRICS200",9) == 0) return 2; /* v2 */ if (memcmp(data+6,"LYRICSEND",9) == 0) return 1; /* v1 */ return 0; } static long get_lyrics3v1_len(snd_stream_t *stream) { const char *p; long i, len; char buf[5104]; /* needs manual search: http://id3.org/Lyrics3 */ if (stream->fh.length < 20) return -1; len = (stream->fh.length > 5109)? 5109 : stream->fh.length; FS_fseek(&stream->fh, -len, SEEK_END); FS_fread(buf, 1, (len -= 9), &stream->fh); /* exclude footer */ /* strstr() won't work here. */ for (i = len - 11, p = buf; i >= 0; --i, ++p) { if (memcmp(p, "LYRICSBEGIN", 11) == 0) break; } if (i < 0) return -1; return len - (long)(p - buf) + 9 /* footer */; } static inline long get_lyrics3v2_len(const unsigned char *data, long length) { /* 6 bytes before the end marker is size in decimal format - * does not include the 9 bytes end marker and size field. */ if (length != 6) return 0; return strtol((const char *)data, NULL, 10) + 15; } static inline qboolean verify_lyrics3v2(const unsigned char *data, long length) { if (length < 11) return false; if (memcmp(data,"LYRICSBEGIN",11) == 0) return true; return false; } #define MMTAG_PARANOID static qboolean is_musicmatch(const unsigned char *data, long length) { /* From docs/musicmatch.txt in id3lib: https://sourceforge.net/projects/id3lib/ Overall tag structure: +-----------------------------+ | Header | | (256 bytes, OPTIONAL) | +-----------------------------+ | Image extension (4 bytes) | +-----------------------------+ | Image binary | | (var. length >= 4 bytes) | +-----------------------------+ | Unused (4 bytes) | +-----------------------------+ | Version info (256 bytes) | +-----------------------------+ | Audio meta-data | | (var. length >= 7868 bytes) | +-----------------------------+ | Data offsets (20 bytes) | +-----------------------------+ | Footer (48 bytes) | +-----------------------------+ */ if (length < 48) return false; /* sig: 19 bytes company name + 13 bytes space */ if (memcmp(data,"Brava Software Inc. ",32) != 0) { return false; } /* 4 bytes version: x.xx */ if (!q_isdigit(data[32]) || data[33] != '.' || !q_isdigit(data[34]) ||!q_isdigit(data[35])) { return false; } #ifdef MMTAG_PARANOID /* [36..47]: 12 bytes trailing space */ for (length = 36; length < 48; ++length) { if (data[length] != ' ') return false; } #endif return true; } static long get_musicmatch_len(snd_stream_t *stream) { const int metasizes[4] = { 7868, 7936, 8004, 8132 }; const unsigned char syncstr[10] = {'1','8','2','7','3','6','4','5',0,0}; unsigned char buf[256]; int i, j, imgext_ofs, version_ofs; long len; FS_fseek(&stream->fh, -68, SEEK_END); FS_fread(buf, 1, 20, &stream->fh); imgext_ofs = (int)((buf[3] <<24) | (buf[2] <<16) | (buf[1] <<8) | buf[0] ); version_ofs = (int)((buf[15]<<24) | (buf[14]<<16) | (buf[13]<<8) | buf[12]); if (version_ofs <= imgext_ofs) return -1; if (version_ofs <= 0 || imgext_ofs <= 0) return -1; /* Try finding the version info section: * Because metadata section comes after it, and because metadata section * has different sizes across versions (format ver. <= 3.00: always 7868 * bytes), we can _not_ directly calculate using deltas from the offsets * section. */ for (i = 0; i < 4; ++i) { /* 48: footer, 20: offsets, 256: version info */ len = metasizes[i] + 48 + 20 + 256; if (stream->fh.length < len) return -1; FS_fseek(&stream->fh, -len, SEEK_END); FS_fread(buf, 1, 256, &stream->fh); /* [0..9]: sync string, [30..255]: 0x20 */ #ifdef MMTAG_PARANOID for (j = 30; j < 256; ++j) { if (buf[j] != ' ') break; } if (j < 256) continue; #endif if (memcmp(buf, syncstr, 10) == 0) { break; } } if (i == 4) return -1; /* no luck. */ #ifdef MMTAG_PARANOID /* unused section: (4 bytes of 0x00) */ FS_fseek(&stream->fh, -(len + 4), SEEK_END); FS_fread(buf, 1, 4, &stream->fh); j = 0; if (memcmp(buf, &j, 4) != 0) return -1; #endif len += (version_ofs - imgext_ofs); if (stream->fh.length < len) return -1; FS_fseek(&stream->fh, -len, SEEK_END); FS_fread(buf, 1, 8, &stream->fh); j = (int)((buf[7] <<24) | (buf[6] <<16) | (buf[5] <<8) | buf[4]); if (j < 0) return -1; /* verify image size: */ /* without this, we may land at a wrong place. */ if (j + 12 != version_ofs - imgext_ofs) return -1; /* try finding the optional header */ if (stream->fh.length < len + 256) return len; FS_fseek(&stream->fh, -(len + 256), SEEK_END); FS_fread(buf, 1, 256, &stream->fh); /* [0..9]: sync string, [30..255]: 0x20 */ if (memcmp(buf, syncstr, 10) != 0) { return len; } #ifdef MMTAG_PARANOID for (j = 30; j < 256; ++j) { if (buf[j] != ' ') return len; } #endif return len + 256; /* header is present. */ } static int probe_id3v1(snd_stream_t *stream, unsigned char *buf, int atend) { if (stream->fh.length >= 128) { FS_fseek(&stream->fh, -128, SEEK_END); if (FS_fread(buf, 1, 128, &stream->fh) != 128) return -1; if (is_id3v1(buf, 128)) { if (!atend) { /* possible false positive? */ if (is_musicmatch(buf + 128 - 48, 48) || is_apetag (buf + 128 - 32, 32) || is_lyrics3tag(buf + 128 - 15, 15)) { return 0; } } stream->fh.length -= 128; Con_DPrintf("MP3: skipped %ld bytes ID3v1 tag\n", 128L); return 1; /* FIXME: handle possible double-ID3v1 tags? */ } } return 0; } static int probe_mmtag(snd_stream_t *stream, unsigned char *buf) { long len; if (stream->fh.length >= 68) { FS_fseek(&stream->fh, -48, SEEK_END); if (FS_fread(buf, 1, 48, &stream->fh) != 48) return -1; if (is_musicmatch(buf, 48)) { len = get_musicmatch_len(stream); if (len < 0) return -1; if (len >= stream->fh.length) return -1; stream->fh.length -= len; Con_DPrintf("MP3: skipped %ld bytes MusicMatch tag\n", len); return 1; } } return 0; } static int probe_apetag(snd_stream_t *stream, unsigned char *buf) { long len; if (stream->fh.length >= 32) { FS_fseek(&stream->fh, -32, SEEK_END); if (FS_fread(buf, 1, 32, &stream->fh) != 32) return -1; if (is_apetag(buf, 32)) { len = get_ape_len(buf); if (len >= stream->fh.length) return -1; stream->fh.length -= len; Con_DPrintf("MP3: skipped %ld bytes APE tag\n", len); return 1; } } return 0; } static int probe_lyrics3(snd_stream_t *stream, unsigned char *buf) { long len; if (stream->fh.length >= 15) { FS_fseek(&stream->fh, -15, SEEK_END); if (FS_fread(buf, 1, 15, &stream->fh) != 15) return -1; len = is_lyrics3tag(buf, 15); if (len == 2) { len = get_lyrics3v2_len(buf, 6); if (len >= stream->fh.length) return -1; if (len < 15) return -1; FS_fseek(&stream->fh, -len, SEEK_END); if (FS_fread(buf, 1, 11, &stream->fh) != 11) return -1; if (!verify_lyrics3v2(buf, 11)) return -1; stream->fh.length -= len; Con_DPrintf("MP3: skipped %ld bytes Lyrics3 tag\n", len); return 1; } else if (len == 1) { len = get_lyrics3v1_len(stream); if (len < 0) return -1; stream->fh.length -= len; Con_DPrintf("MP3: skipped %ld bytes Lyrics3 tag\n", len); return 1; } } return 0; } int mp3_skiptags(snd_stream_t *stream) { unsigned char buf[128]; long len; size_t readsize; int c_id3, c_ape, c_lyr, c_mm; int rc = -1; /* failsafe */ long oldlength = stream->fh.length; long oldstart = stream->fh.start; /* MP3 standard has no metadata format, so everyone invented * their own thing, even with extensions, until ID3v2 became * dominant: Hence the impossible mess here. * * Note: I don't yet care about freaky broken mp3 files with * double tags. -- O.S. */ readsize = FS_fread(buf, 1, 128, &stream->fh); if (!readsize || FS_ferror(&stream->fh)) goto fail; /* ID3v2 tag is at the start */ if (is_id3v2(buf, readsize)) { len = get_id3v2_len(buf, (long)readsize); if (len >= stream->fh.length) goto fail; stream->fh.start += len; stream->fh.length -= len; Con_DPrintf("MP3: skipped %ld bytes ID3v2 tag\n", len); } /* APE tag _might_ be at the start (discouraged * but not forbidden, either.) read the header. */ else if (is_apetag(buf, readsize)) { len = get_ape_len(buf); if (len >= stream->fh.length) goto fail; stream->fh.start += len; stream->fh.length -= len; Con_DPrintf("MP3: skipped %ld bytes APE tag\n", len); } /* it's not impossible that _old_ MusicMatch tag * placing itself after ID3v1. */ if ((c_mm = probe_mmtag(stream, buf)) < 0) { goto fail; } /* ID3v1 tag is at the end */ if ((c_id3 = probe_id3v1(stream, buf, !c_mm)) < 0) { goto fail; } /* we do not know the order of ape or lyrics3 * or musicmatch tags, hence the loop here.. */ c_ape = 0; c_lyr = 0; for (;;) { if (!c_lyr) { /* care about mp3s with double Lyrics3 tags? */ if ((c_lyr = probe_lyrics3(stream, buf)) < 0) goto fail; if (c_lyr) continue; } if (!c_mm) { if ((c_mm = probe_mmtag(stream, buf)) < 0) goto fail; if (c_mm) continue; } if (!c_ape) { if ((c_ape = probe_apetag(stream, buf)) < 0) goto fail; if (c_ape) continue; } break; } /* for (;;) */ rc = (stream->fh.length > 0)? 0 : -1; fail: if (rc < 0) { stream->fh.start = oldstart; stream->fh.length = oldlength; } FS_rewind(&stream->fh); return rc; } #endif /* USE_CODEC_MP3 */ engine/h2shared/snd_mpg123.c000066400000000000000000000123561444734033100160560ustar00rootroot00000000000000/* MP3 decoding support using libmpg123 * Copyright (C) 2011-2019 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(USE_CODEC_MP3) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_mp3.h" #include #define MPG123_DEF_SSIZE_T /* we do define ssize_t in our stdinc.h */ #include #if !defined(MPG123_API_VERSION) || (MPG123_API_VERSION < 24) #error minimum required libmpg123 version is 1.12.0 (api version 24) #endif /* Private data */ typedef struct _mp3_priv_t { mpg123_handle* handle; int handle_open; } mp3_priv_t; /* CALLBACKS: libmpg123 expects POSIX read/lseek() behavior! */ static ssize_t mp3_read (void *f, void *buf, size_t size) { ssize_t ret = (ssize_t) FS_fread(buf, 1, size, (fshandle_t *)f); if (ret == 0 && errno != 0) return -1; return ret; } static off_t mp3_seek (void *f, off_t offset, int whence) { if (f == NULL) return -1; if (FS_fseek((fshandle_t *)f, (long) offset, whence) < 0) return (off_t)-1; return (off_t) FS_ftell((fshandle_t *)f); } static qboolean S_MP3_CodecInitialize (void) { if (!mp3_codec.initialized) { if (mpg123_init() != MPG123_OK) { Con_Printf ("Could not initialize mpg123\n"); return false; } mp3_codec.initialized = true; } return true; } static void S_MP3_CodecShutdown (void) { if (mp3_codec.initialized) { mp3_codec.initialized = false; mpg123_exit(); } } static qboolean S_MP3_CodecOpenStream (snd_stream_t *stream) { long rate = 0; int encoding = 0, channels = 0; mp3_priv_t *priv = NULL; if (mp3_skiptags(stream) < 0) { Con_Printf("Corrupt mp3 file (bad tags.)\n"); return false; } stream->priv = Z_Malloc(sizeof(mp3_priv_t), Z_MAINZONE); priv = (mp3_priv_t *) stream->priv; priv->handle = mpg123_new(NULL, NULL); if (priv->handle == NULL) { Con_Printf("Unable to allocate mpg123 handle\n"); goto _fail; } if (mpg123_replace_reader_handle(priv->handle, mp3_read, mp3_seek, NULL) != MPG123_OK || mpg123_open_handle(priv->handle, &stream->fh) != MPG123_OK) { Con_Printf("Unable to open mpg123 handle\n"); goto _fail; } priv->handle_open = 1; if (mpg123_getformat(priv->handle, &rate, &channels, &encoding) != MPG123_OK) { Con_Printf("Unable to retrieve mpg123 format for %s\n", stream->name); goto _fail; } switch (channels) { case MPG123_MONO: stream->info.channels = 1; break; case MPG123_STEREO: stream->info.channels = 2; break; default: Con_Printf("Unsupported number of channels %d in %s\n", channels, stream->name); goto _fail; } stream->info.rate = rate; switch (encoding) { case MPG123_ENC_UNSIGNED_8: stream->info.bits = 8; stream->info.width = 1; break; case MPG123_ENC_SIGNED_8: /* unsupported: force mpg123 to convert */ stream->info.bits = 8; stream->info.width = 1; encoding = MPG123_ENC_UNSIGNED_8; break; case MPG123_ENC_SIGNED_16: stream->info.bits = 16; stream->info.width = 2; break; case MPG123_ENC_UNSIGNED_16: default: /* unsupported: force mpg123 to convert */ stream->info.bits = 16; stream->info.width = 2; encoding = MPG123_ENC_SIGNED_16; break; } if (mpg123_format_support(priv->handle, rate, encoding) == 0) { Con_Printf("Unsupported format for %s\n", stream->name); goto _fail; } mpg123_format_none(priv->handle); mpg123_format(priv->handle, rate, channels, encoding); return true; _fail: if (priv) { if (priv->handle) { if (priv->handle_open) mpg123_close(priv->handle); mpg123_delete(priv->handle); } Z_Free(stream->priv); } return false; } static int S_MP3_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { mp3_priv_t *priv = (mp3_priv_t *) stream->priv; size_t bytes_read = 0; int res = mpg123_read (priv->handle, (unsigned char *)buffer, (size_t)bytes, &bytes_read); switch (res) { case MPG123_DONE: Con_DPrintf("mp3 EOF\n"); case MPG123_OK: return (int)bytes_read; } return -1; /* error */ } static void S_MP3_CodecCloseStream (snd_stream_t *stream) { mp3_priv_t *priv = (mp3_priv_t *) stream->priv; mpg123_close(priv->handle); mpg123_delete(priv->handle); Z_Free(stream->priv); S_CodecUtilClose(&stream); } static int S_MP3_CodecRewindStream (snd_stream_t *stream) { mp3_priv_t *priv = (mp3_priv_t *) stream->priv; off_t res = mpg123_seek(priv->handle, 0, SEEK_SET); if (res >= 0) return 0; return res; } snd_codec_t mp3_codec = { CODECTYPE_MP3, false, "mp3", S_MP3_CodecInitialize, S_MP3_CodecShutdown, S_MP3_CodecOpenStream, S_MP3_CodecReadStream, S_MP3_CodecRewindStream, NULL, /* jump */ S_MP3_CodecCloseStream, NULL }; #endif /* USE_CODEC_MP3 */ engine/h2shared/snd_null.c000066400000000000000000000040231444734033100160070ustar00rootroot00000000000000/* * snd_null.c -- include this instead of all the other snd_* files * to have no sound code whatsoever * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" cvar_t bgmvolume = {"bgmvolume", "1", CVAR_ARCHIVE}; cvar_t bgmtype = {"bgmtype", "cd", CVAR_ARCHIVE}; // cd or midi cvar_t sfxvolume = {"volume", "0.7", CVAR_ARCHIVE}; cvar_t precache = {"precache", "1", CVAR_NONE}; void S_Init (void) { Cvar_RegisterVariable(&precache); Con_Printf("SOUND: disabled at compile time\n"); } void S_Shutdown (void) { } void S_TouchSound (const char *sample) { } void S_ClearBuffer (void) { } void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) { } void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation) { } void S_StopSound (int entnum, int entchannel) { } sfx_t *S_PrecacheSound (const char *name) { return NULL; } void S_ClearPrecache (void) { } void S_Update (vec3_t origin, vec3_t forward, vec3_t right, vec3_t up) { } void S_UpdateSoundPos (int entnum, int entchannel, vec3_t origin) { } void S_StopAllSounds (qboolean clear) { } void S_BeginPrecaching (void) { } void S_EndPrecaching (void) { } void S_ExtraUpdate (void) { } void S_LocalSound (const char *s) { } void S_BlockSound (void) { } void S_UnblockSound (void) { } engine/h2shared/snd_opus.c000066400000000000000000000114071444734033100160270ustar00rootroot00000000000000/* * Ogg/Opus streaming music support, loosely based on several open source * Quake engine based projects with many modifications. * * Copyright (C) 2012-2013 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" #if defined(USE_CODEC_OPUS) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_opus.h" #include #include /* CALLBACK FUNCTIONS: */ static int opc_fclose (void *f) { return 0; /* we fclose() elsewhere. */ } static int opc_fread (void *f, unsigned char *buf, int size) { int ret; if (size < 0) { errno = EINVAL; return -1; } ret = (int) FS_fread(buf, 1, (size_t)size, (fshandle_t *)f); if (ret == 0 && errno != 0) ret = -1; return ret; } static int opc_fseek (void *f, opus_int64 off, int whence) { if (f == NULL) return (-1); return FS_fseek((fshandle_t *)f, (long) off, whence); } static opus_int64 opc_ftell (void *f) { return (opus_int64) FS_ftell((fshandle_t *)f); } static const OpusFileCallbacks opc_qfs = { (int (*)(void *, unsigned char *, int)) opc_fread, (int (*)(void *, opus_int64, int)) opc_fseek, (opus_int64 (*)(void *)) opc_ftell, (int (*)(void *)) opc_fclose }; static qboolean S_OPUS_CodecInitialize (void) { return true; } static void S_OPUS_CodecShutdown (void) { } static qboolean S_OPUS_CodecOpenStream (snd_stream_t *stream) { OggOpusFile *opFile; const OpusHead *op_info; long numstreams; int res; opFile = op_open_callbacks(&stream->fh, &opc_qfs, NULL, 0, &res); if (!opFile) { Con_Printf("%s is not a valid Opus file (error %i).\n", stream->name, res); goto _fail; } stream->priv = opFile; if (!op_seekable(opFile)) { Con_Printf("Opus stream %s not seekable.\n", stream->name); goto _fail; } op_info = op_head(opFile, -1); if (!op_info) { Con_Printf("Unable to get stream information for %s.\n", stream->name); goto _fail; } /* FIXME: handle section changes */ numstreams = op_info->stream_count; if (numstreams != 1) { Con_Printf("More than one (%ld) stream in %s\n", (long)op_info->stream_count, stream->name); goto _fail; } if (op_info->channel_count != 1 && op_info->channel_count != 2) { Con_Printf("Unsupported number of channels %d in %s\n", op_info->channel_count, stream->name); goto _fail; } /* All Opus audio is coded at 48 kHz, and should also be decoded * at 48 kHz for playback: info->input_sample_rate only tells us * the sampling rate of the original input before opus encoding. * S_RawSamples() shall already downsample this, as necessary. */ stream->info.rate = 48000; stream->info.channels = op_info->channel_count; /* op_read() yields 16-bit output using native endian ordering: */ stream->info.bits = 16; stream->info.width = 2; return true; _fail: if (opFile) op_free(opFile); return false; } static int S_OPUS_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { int section; /* FIXME: handle section changes */ int cnt, res, rem; opus_int16 * ptr; rem = bytes / stream->info.width; if (rem / stream->info.channels <= 0) return 0; cnt = 0; ptr = (opus_int16 *) buffer; while (1) { /* op_read() yields 16-bit output using native endian ordering. returns * the number of samples read per channel on success, or a negative value * on failure. */ res = op_read((OggOpusFile *)stream->priv, ptr, rem, §ion); if (res <= 0) break; cnt += res; res *= stream->info.channels; rem -= res; if (rem <= 0) break; ptr += res; } if (res < 0) return res; cnt *= (stream->info.channels * stream->info.width); return cnt; } static void S_OPUS_CodecCloseStream (snd_stream_t *stream) { op_free((OggOpusFile *)stream->priv); S_CodecUtilClose(&stream); } static int S_OPUS_CodecRewindStream (snd_stream_t *stream) { return op_pcm_seek ((OggOpusFile *)stream->priv, 0); } snd_codec_t opus_codec = { CODECTYPE_OPUS, true, /* always available. */ "opus", S_OPUS_CodecInitialize, S_OPUS_CodecShutdown, S_OPUS_CodecOpenStream, S_OPUS_CodecReadStream, S_OPUS_CodecRewindStream, NULL, /* jump */ S_OPUS_CodecCloseStream, NULL }; #endif /* USE_CODEC_OPUS */ engine/h2shared/snd_opus.h000066400000000000000000000003231444734033100160270ustar00rootroot00000000000000/* Ogg/Opus streaming music support. */ #if !defined(_SND_OPUS_H_) #define _SND_OPUS_H_ 1 #if defined(USE_CODEC_OPUS) extern snd_codec_t opus_codec; #endif /* USE_CODEC_OPUS */ #endif /* ! _SND_OPUS_H_ */ engine/h2shared/snd_oss.c000066400000000000000000000176701444734033100156550ustar00rootroot00000000000000/* * snd_oss.c * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "snd_sys.h" #if HAVE_OSS_SOUND #include "snd_oss.h" #include #include #include #include #include #include /* FIXME: is by the book, but we might * have to take care of , * and someday. */ #include #include #if ENDIAN_RUNTIME_DETECT static int FORMAT_S16; #elif defined(AFMT_S16_NE) #define FORMAT_S16 AFMT_S16_NE #elif (BYTE_ORDER == BIG_ENDIAN) #define FORMAT_S16 AFMT_S16_BE #elif (BYTE_ORDER == LITTLE_ENDIAN) #define FORMAT_S16 AFMT_S16_LE #else #error "Unsupported endianness." #endif /* BYTE_ORDER */ static char s_oss_driver[] = "OSS"; static int audio_fd = -1; static const char oss_default[] = "/dev/dsp"; static const char *ossdev = oss_default; static unsigned long mmaplen; static qboolean S_OSS_Init (dma_t *dma) { int i, caps, tmp; unsigned long sz; struct audio_buf_info info; #if ENDIAN_RUNTIME_DETECT switch (host_byteorder) { case BIG_ENDIAN: FORMAT_S16 = AFMT_S16_BE; break; case LITTLE_ENDIAN: FORMAT_S16 = AFMT_S16_LE; break; default: Sys_Error("%s: Unsupported byte order.", __thisfunc__); break; } #endif /* ENDIAN_RUNTIME_DETECT */ tmp = COM_CheckParm("-ossdev"); if (tmp != 0 && tmp < com_argc - 1) ossdev = com_argv[tmp + 1]; Con_Printf ("OSS: Using device: %s\n", ossdev); audio_fd = open(ossdev, O_RDWR|O_NONBLOCK); if (audio_fd == -1) { /* retry up to 3 times if it's busy */ tmp = 3; while (audio_fd == -1 && tmp-- && (errno == EAGAIN || errno == EBUSY)) { usleep (300000); audio_fd = open(ossdev, O_RDWR|O_NONBLOCK); } if (audio_fd == -1) { Con_Printf("Could not open %s. %s\n", ossdev, strerror(errno)); return false; } } if (ioctl(audio_fd, SNDCTL_DSP_RESET, 0) == -1) { Con_Printf("Could not reset %s. %s\n", ossdev, strerror(errno)); goto error; } if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps) == -1) { Con_Printf("Couldn't retrieve soundcard capabilities. %s\n", strerror(errno)); goto error; } if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP)) { Con_Printf("Audio driver doesn't support mmap or trigger\n"); goto error; } memset ((void *) dma, 0, sizeof(dma_t)); shm = dma; /* set format & rate */ tmp = (desired_bits == 16) ? FORMAT_S16 : AFMT_U8; if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &tmp) == -1) { Con_Printf("Problems setting %d bit format, trying alternatives..\n", desired_bits); /* try what the device gives us */ if (ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &tmp) == -1) { Con_Printf("Unable to retrieve supported formats. %s\n", strerror(errno)); goto error; } i = tmp; if (i & FORMAT_S16) tmp = FORMAT_S16; else if (i & AFMT_U8) tmp = AFMT_U8; else if (i & AFMT_S8) tmp = AFMT_S8; else { Con_Printf("Neither 8 nor 16 bit format supported.\n"); goto error; } if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &tmp) == -1) { Con_Printf("Unable to set sound format. %s\n", strerror(errno)); goto error; } } if (tmp == FORMAT_S16) shm->samplebits = 16; else if (tmp == AFMT_U8) shm->samplebits = 8; else if (tmp == AFMT_S8) { shm->signed8 = 1; shm->samplebits = 8; } else /* unreached */ { goto error; } tmp = desired_speed; if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &tmp) == -1) { Con_Printf("Problems setting sample rate, trying alternatives..\n"); shm->speed = 0; for (i = 0; i < MAX_TRYRATES; i++) { tmp = tryrates[i]; if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &tmp) == -1) { Con_DPrintf ("Unable to set sample rate %d\n", tryrates[i]); } else { if (tmp != tryrates[i]) { Con_Printf ("Warning: Rate set (%d) didn't match requested rate (%d)!\n", tmp, tryrates[i]); /* goto error;*/ } shm->speed = tmp; break; } } if (shm->speed == 0) { Con_Printf("Unable to set any sample rates.\n"); goto error; } } else { if (tmp != desired_speed) { Con_Printf ("Warning: Rate set (%d) didn't match requested rate (%d)!\n", tmp, desired_speed); /* goto error;*/ } shm->speed = tmp; } tmp = (desired_channels == 2) ? 1 : 0; if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp) == -1) { Con_Printf ("Problems setting channels to %s, retrying for %s\n", (desired_channels == 2) ? "stereo" : "mono", (desired_channels == 2) ? "mono" : "stereo"); tmp = (desired_channels == 2) ? 0 : 1; if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp) == -1) { Con_Printf("unable to set desired channels. %s\n", strerror(errno)); goto error; } } shm->channels = tmp +1; if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) == -1) { Con_Printf("Couldn't retrieve buffer status. %s\n", strerror(errno)); goto error; } shm->samples = info.fragstotal * info.fragsize / (shm->samplebits / 8); shm->submission_chunk = 1; /* memory map the dma buffer */ sz = sysconf (_SC_PAGESIZE); mmaplen = info.fragstotal * info.fragsize; mmaplen += sz - 1; mmaplen &= ~(sz - 1); shm->buffer = (unsigned char *) mmap(NULL, mmaplen, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, audio_fd, 0); if (shm->buffer == MAP_FAILED) { Con_Printf("Could not mmap %s. %s\n", ossdev, strerror(errno)); goto error; } Con_Printf ("OSS: mmaped %lu bytes buffer\n", mmaplen); /* toggle the trigger & start her up */ tmp = 0; if (ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp) == -1) { Con_Printf("Could not toggle %s. %s\n", ossdev, strerror(errno)); goto error; } tmp = PCM_ENABLE_OUTPUT; if (ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp) == -1) { Con_Printf("Could not toggle %s. %s\n", ossdev, strerror(errno)); goto error; } shm->samplepos = 0; return true; error: if (shm->buffer && shm->buffer != MAP_FAILED) munmap (shm->buffer, mmaplen); shm->buffer = NULL; shm = NULL; close(audio_fd); audio_fd = -1; return false; } static int S_OSS_GetDMAPos (void) { struct count_info count; if (!shm) return 0; if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &count) == -1) { Con_Printf("Uh, sound dead. %s\n", strerror(errno)); munmap (shm->buffer, mmaplen); shm->buffer = NULL; shm = NULL; close(audio_fd); audio_fd = -1; return 0; } shm->samplepos = count.ptr / (shm->samplebits / 8); return shm->samplepos; } static void S_OSS_Shutdown (void) { if (shm) { int tmp = 0; Con_Printf ("Shutting down OSS sound\n"); munmap (shm->buffer, mmaplen); shm->buffer = NULL; shm = NULL; ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp); ioctl(audio_fd, SNDCTL_DSP_RESET, 0); close(audio_fd); audio_fd = -1; } } /* ============== SNDDMA_LockBuffer Makes sure dma buffer is valid ============== */ static void S_OSS_LockBuffer (void) { /* nothing to do here */ } /* ============== SNDDMA_Submit Unlock the dma buffer / Send sound to the device =============== */ static void S_OSS_Submit(void) { } static void S_OSS_BlockSound (void) { } static void S_OSS_UnblockSound (void) { } snd_driver_t snddrv_oss = { S_OSS_Init, S_OSS_Shutdown, S_OSS_GetDMAPos, S_OSS_LockBuffer, S_OSS_Submit, S_OSS_BlockSound, S_OSS_UnblockSound, s_oss_driver, SNDDRV_ID_OSS, false, NULL }; #endif /* HAVE_OSS_SOUND */ engine/h2shared/snd_oss.h000066400000000000000000000003011444734033100156410ustar00rootroot00000000000000/* Sound support using OSS. */ #if !defined(__HX2_SND_OSS) #define __HX2_SND_OSS #if HAVE_OSS_SOUND extern snd_driver_t snddrv_oss; #endif /* HAVE_OSS_SOUND */ #endif /* __HX2_SND_OSS */ engine/h2shared/snd_paula.c000066400000000000000000000135341444734033100161460ustar00rootroot00000000000000/* * Sound support using Amiga Paula, based on original work by * Frank Wille * Adapted to uHexen2 by Szilard Biro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" #include "snd_sys.h" #if HAVE_PAULA_SOUND #include "snd_paula.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CLOCK_PAL 3546895 #define CLOCK_NTSC 3579545 static char s_paula_driver[] = "Paula"; #define NSAMPLES 0x8000 /* custom chip base address */ static volatile struct Custom *custom = (struct Custom *)0xdff000; /* CIA A base address */ static volatile struct CIA *ciaa = (struct CIA *)0xbfe001; static unsigned char *dmabuf = NULL; static BYTE audio_dev = -1; static double speed; static struct MsgPort *audioport=NULL; static struct IOAudio *audioio=NULL; static struct Interrupt AudioInt; static struct Interrupt *OldInt; static UBYTE saved_filter; static double aud_start_time; static void AudioIntCode(void) { aud_start_time = Sys_DoubleTime(); custom->intreq = INTF_AUD0; } static qboolean S_PAULA_Init(dma_t *dma) { UBYTE channelalloc[] = {1|2}; UWORD period, length; LONG sysclock; UWORD *leftptr, *rightptr; shm = dma; memset ((void *) dma, 0, sizeof(dma_t)); /* set the clock constant */ if (GfxBase->DisplayFlags & REALLY_PAL) sysclock = CLOCK_PAL; else sysclock = CLOCK_NTSC; period = (UWORD)((float)sysclock / (float)desired_speed + 0.5f); /* allocate dma buffer */ dmabuf = (unsigned char *) AllocVec(NSAMPLES, MEMF_CHIP|MEMF_PUBLIC|MEMF_CLEAR); if (!dmabuf) { Con_Printf("Paula: Can't allocate the DMA buffer\n"); return false; } /* init shm */ shm->buffer = dmabuf; shm->channels = desired_channels; if (shm->channels > 2) shm->channels = 2; shm->speed = sysclock/(LONG)period; shm->samplebits = 8; shm->samples = NSAMPLES; shm->signed8 = 2; // signed + de-interleaved speed = (double)(shm->speed * shm->channels); /* open audio.device */ if ((audioport = CreateMsgPort())) { if ((audioio = (struct IOAudio *)CreateIORequest(audioport, sizeof(struct IOAudio)))) { audioio->ioa_Request.io_Message.mn_Node.ln_Pri = ADALLOC_MAXPREC; audioio->ioa_Request.io_Command = ADCMD_ALLOCATE; audioio->ioa_Request.io_Flags = ADIOF_NOWAIT; audioio->ioa_AllocKey = 0; audioio->ioa_Data = channelalloc; audioio->ioa_Length = sizeof(channelalloc); audio_dev = OpenDevice((STRPTR)AUDIONAME, 0, &audioio->ioa_Request, 0); } } if (audio_dev) { Con_Printf("Paula: Couldn't open audio.device\n"); return false; } /* disable the DMA and interrupts */ custom->dmacon = DMAF_AUD0|DMAF_AUD1; custom->intena = INTF_AUD0; custom->intreq = INTF_AUD0; /* set up audio interrupt */ AudioInt.is_Node.ln_Type = NT_INTERRUPT; AudioInt.is_Node.ln_Pri = 100; //AudioInt.is_Data = (APTR)&aud_start_time; AudioInt.is_Code = (void(*)())AudioIntCode; OldInt = SetIntVector(INTB_AUD0,&AudioInt); if (shm->channels == 2) { length = NSAMPLES / sizeof(WORD) / 2; leftptr = (UWORD *)dmabuf; rightptr = (UWORD *)(dmabuf + (NSAMPLES / 2)); } else { length = NSAMPLES / sizeof(WORD); leftptr = rightptr = (UWORD *)dmabuf; } /* set up left channel */ custom->aud[0].ac_len = length; custom->aud[0].ac_per = period; custom->aud[0].ac_vol = 64; custom->aud[0].ac_ptr = leftptr; /* set up right channel */ custom->aud[1].ac_len = length; custom->aud[1].ac_per = period; custom->aud[1].ac_vol = 64; custom->aud[1].ac_ptr = rightptr; aud_start_time = 0; /* enable the DMA and interrupts */ custom->intena = INTF_SETCLR|INTF_INTEN|INTF_AUD0; custom->dmacon = DMAF_SETCLR|DMAF_AUD0|DMAF_AUD1; saved_filter = ciaa->ciapra & CIAF_LED; ciaa->ciapra |= CIAF_LED; Con_Printf ("Paula initialized\n"); return true; } static int S_PAULA_GetDMAPos(void) { int pos = (int)((Sys_DoubleTime()-aud_start_time)*speed); pos &= NSAMPLES-1; return pos; } static void S_PAULA_Shutdown(void) { if (!saved_filter) { ciaa->ciapra &= ~CIAF_LED; } if (OldInt) { custom->dmacon = DMAF_AUD0|DMAF_AUD1; custom->intena = INTF_AUD0; SetIntVector(INTB_AUD0,OldInt); OldInt = NULL; } if (audio_dev == 0) { /* this is probably overkill */ audioio->ioa_Request.io_Command = CMD_RESET; DoIO((struct IORequest *)audioio); CloseDevice((struct IORequest *)audioio); } if (audioio) { DeleteIORequest((struct IORequest *)audioio); audioio = NULL; } if (audioport) { DeleteMsgPort(audioport); audioport = NULL; } if (dmabuf) { FreeVec(dmabuf); dmabuf = NULL; } shm = NULL; } static void S_PAULA_LockBuffer (void) { } static void S_PAULA_BlockSound (void) { } static void S_PAULA_UnblockSound (void) { } static void S_PAULA_Submit(void) { } snd_driver_t snddrv_paula = { S_PAULA_Init, S_PAULA_Shutdown, S_PAULA_GetDMAPos, S_PAULA_LockBuffer, S_PAULA_Submit, S_PAULA_BlockSound, S_PAULA_UnblockSound, s_paula_driver, SNDDRV_ID_PAULA, false, NULL }; #endif /* HAVE_PAULA_SOUND */ engine/h2shared/snd_paula.h000066400000000000000000000003251444734033100161450ustar00rootroot00000000000000/* Sound support using Amiga Paula. */ #if !defined(__HX2_SND_PAULA) #define __HX2_SND_PAULA #if HAVE_PAULA_SOUND extern snd_driver_t snddrv_paula; #endif /* HAVE_PAULA_SOUND */ #endif /* __HX2_SND_PAULA */ engine/h2shared/snd_pci.c000066400000000000000000000111461444734033100156140ustar00rootroot00000000000000/* snd_pci.c -- PCI sound card support for DOS through libau * Copyright (C) 2015 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" #include "snd_sys.h" #if HAVE_DOS_PCI_SOUND #include "snd_pci.h" #include "sys_dxe.h" #include "libau.h" static char s_pci_driver[] = "PCI Audio"; static au_context *ctx = NULL; #ifndef SNDPCI_DXE #define AU_search_fp AU_search #define AU_start_fp AU_start #define AU_close_fp AU_close #define AU_getinfo_fp AU_getinfo #define AU_setrate_fp AU_setrate #define AU_setmixer_all_fp AU_setmixer_all #define AU_cardbuf_space_fp AU_cardbuf_space #else static void *sndpci_dxe; static au_context * (*AU_search_fp)(unsigned int); static void (*AU_start_fp)(au_context *); static void (*AU_close_fp)(au_context *); static const struct auinfo_s * (*AU_getinfo_fp)(au_context *); static void (*AU_setrate_fp)(au_context *, unsigned int *, unsigned int *, unsigned int *); static void (*AU_setmixer_all_fp)(au_context *, unsigned int); static unsigned int (*AU_cardbuf_space_fp)(au_context *); static int load_sndpci_dxe(void) { if ((sndpci_dxe = Sys_dlopen("sndpci.dxe", 0)) == NULL) goto fail; AU_search_fp = (au_context * (*)(unsigned int)) Sys_dlsym(sndpci_dxe, "_AU_search"); AU_start_fp = (void (*)(au_context*)) Sys_dlsym(sndpci_dxe, "_AU_start"); AU_close_fp = (void (*)(au_context*)) Sys_dlsym(sndpci_dxe, "_AU_close"); AU_getinfo_fp = (const struct auinfo_s * (*)(au_context*)) Sys_dlsym(sndpci_dxe, "_AU_getinfo"); AU_setrate_fp = (void (*)(au_context*, unsigned int*, unsigned int*, unsigned int*)) Sys_dlsym(sndpci_dxe, "_AU_setrate"); AU_setmixer_all_fp = (void (*)(au_context*, unsigned int)) Sys_dlsym(sndpci_dxe, "_AU_setmixer_all"); AU_cardbuf_space_fp = (unsigned int (*)(au_context*)) Sys_dlsym(sndpci_dxe, "_AU_cardbuf_space"); if (!AU_search_fp || !AU_start_fp || !AU_close_fp || !AU_getinfo_fp || !AU_setrate_fp || !AU_setmixer_all_fp || !AU_cardbuf_space_fp) { fail: Con_Printf("PCI Audio: failed loading sndpci.dxe\n"); return -1; } return 0; } static void close_sndpci_dxe(void) { if (sndpci_dxe) Sys_dlclose(sndpci_dxe); sndpci_dxe = NULL; AU_search_fp = NULL; AU_start_fp = NULL; AU_close_fp = NULL; AU_getinfo_fp = NULL; AU_setrate_fp = NULL; AU_setmixer_all_fp = NULL; AU_cardbuf_space_fp = NULL; } #endif static qboolean S_PCI_Init(dma_t *dma) { const struct auinfo_s *aui; unsigned int speed, samplebits, channels; if (!COM_CheckParm("-sndpci")) return false; #ifdef SNDPCI_DXE if (load_sndpci_dxe() < 0) return false; #endif ctx = AU_search_fp(1);/* 1: stereo speaker output (meaningful only for Intel HDA chips) */ if (!ctx) { Con_Printf("PCI Audio: Detection failed.\n"); return false; } speed = desired_speed; samplebits = 16; channels = 2; AU_setrate_fp(ctx, &speed, &samplebits, &channels); memset (dma, 0, sizeof(dma_t)); shm = dma; aui = AU_getinfo_fp(ctx); shm->speed = aui->freq_card; shm->samplebits = aui->bits_set; shm->channels = aui->chan_set; shm->samples = aui->card_dmasize / aui->bytespersample_card; shm->samplepos = 0; shm->submission_chunk = 1; shm->buffer = (unsigned char *) aui->card_DMABUFF; Con_Printf("%s\n", aui->infostr); AU_setmixer_all_fp(ctx, 80); /* 80% volume */ AU_start_fp(ctx); /* also clears dma buffer */ return true; } static int S_PCI_GetDMAPos(void) { shm->samplepos = AU_cardbuf_space_fp(ctx); return shm->samplepos; } static void S_PCI_Shutdown(void) { if (shm) { AU_close_fp(ctx); ctx = NULL; shm->buffer = NULL; shm = NULL; } #ifdef SNDPCI_DXE close_sndpci_dxe(); #endif } static void S_PCI_LockBuffer (void) { } static void S_PCI_Submit (void) { } static void S_PCI_BlockSound (void) { } static void S_PCI_UnblockSound (void) { } snd_driver_t snddrv_pci = { S_PCI_Init, S_PCI_Shutdown, S_PCI_GetDMAPos, S_PCI_LockBuffer, S_PCI_Submit, S_PCI_BlockSound, S_PCI_UnblockSound, s_pci_driver, SNDDRV_ID_PCI_DOS, false, NULL }; #endif /* HAVE_DOS_PCI_SOUND */ engine/h2shared/snd_pci.h000066400000000000000000000003131444734033100156130ustar00rootroot00000000000000/* DOS PCI sound card support */ #if !defined(__HX2_SND_PCI) #define __HX2_SND_PCI #if HAVE_DOS_PCI_SOUND extern snd_driver_t snddrv_pci; #endif /* HAVE_DOS_PCI_SOUND */ #endif /* __HX2_SND_PCI */ engine/h2shared/snd_sb.c000066400000000000000000000304321444734033100154440ustar00rootroot00000000000000/* snd_sb.c -- sound support for dosquake. sound blaster code. * from quake1 source with minor adaptations for uhexen2. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "snd_sys.h" #if HAVE_DOS_SB_SOUND #include #include "snd_sb.h" #include "dosisms.h" /* =============================================================================== BLASTER SUPPORT =============================================================================== */ static char s_sb_driver[] = "BLASTER"; static int S_BLASTER_GetDMAPos (void); static void *dma_dosadr = NULL; /* as received from dos_getmemory() */ static short *dma_buffer = NULL; static int dma_size; static int dma; static int dsp_port; static int irq; static int low_dma; static int high_dma; static int mixer_port; static int mpu401_port; static int dsp_version; static int dsp_minor_version; static int timeconstant = -1; #if 0 static void PrintBits (byte b) { int i; char str[9]; for (i = 0; i < 8; i++) str[i] = '0' + ((b & (1<<(7-i))) > 0); str[8] = 0; Con_Printf ("%s (%u)\n", str, b); } #endif static void SB_Info_f (void) { Con_Printf("BLASTER=%s\n", getenv("BLASTER")); Con_Printf("dsp version=%d.%d\n", dsp_version, dsp_minor_version); Con_Printf("dma=%d\n", dma); if (timeconstant != -1) Con_Printf("timeconstant=%d\n", timeconstant); Con_Printf("dma position: %i\n", S_BLASTER_GetDMAPos ()); } // ======================================================================= // Interprets BLASTER variable // ======================================================================= static int GetBLASTER (void) { char *BLASTER; char *param; BLASTER = getenv("BLASTER"); if (!BLASTER) return 0; param = strchr(BLASTER, 'A'); if (!param) param = strchr(BLASTER, 'a'); if (!param) return 0; sscanf(param + 1, "%x", &dsp_port); param = strchr(BLASTER, 'I'); if (!param) param = strchr(BLASTER, 'i'); if (!param) return 0; sscanf(param + 1, "%d", &irq); param = strchr(BLASTER, 'D'); if (!param) param = strchr(BLASTER, 'd'); if (!param) return 0; sscanf(param + 1, "%d", &low_dma); param = strchr(BLASTER, 'H'); if (!param) param = strchr(BLASTER, 'h'); if (param) sscanf(param + 1, "%d", &high_dma); param = strchr(BLASTER, 'M'); if (!param) param = strchr(BLASTER, 'm'); if (param) sscanf(param + 1, "%x", &mixer_port); else mixer_port = dsp_port; param = strchr(BLASTER, 'P'); if (!param) param = strchr(BLASTER, 'p'); if (param) sscanf(param + 1, "%x", &mpu401_port); return 1; } // ================================================================== // Resets DSP. Returns 0 on success. // ================================================================== static int ResetDSP (void) { volatile int i; dos_outportb(dsp_port + 6, 1); for (i = 65536; i; i--) ; dos_outportb(dsp_port + 6, 0); for (i = 65536; i; i--) { if (!(dos_inportb(dsp_port + 0xe) & 0x80)) continue; if (dos_inportb(dsp_port + 0xa) == 0xaa) break; } if (i) return 0; else return 1; } static int ReadDSP (void) { while (!(dos_inportb(dsp_port + 0xe) & 0x80)) ; return dos_inportb(dsp_port + 0xa); } static void WriteDSP (int val) { while ((dos_inportb(dsp_port + 0xc) & 0x80)) ; dos_outportb(dsp_port + 0xc, val); } static int ReadMixer (int addr) { dos_outportb(mixer_port + 4, addr); return dos_inportb(mixer_port + 5); } static void WriteMixer (int addr, int val) { dos_outportb(mixer_port + 4, addr); dos_outportb(mixer_port + 5, val); } static int oldmixervalue; /* ================ StartSB ================ */ static void StartSB (void) { int i; // version 4.xx startup code if (dsp_version >= 4) { Con_Printf("Version 4 SB startup\n"); WriteDSP(0xd1); // turn on speaker WriteDSP(0x41); WriteDSP(shm->speed >> 8); WriteDSP(shm->speed & 0xff); WriteDSP(0xb6); // 16-bit output WriteDSP(0x30); // stereo WriteDSP((shm->samples - 1) & 0xff); // # of samples - 1 WriteDSP((shm->samples - 1) >> 8); } // version 3.xx startup code else if (dsp_version == 3) { Con_Printf("Version 3 SB startup\n"); WriteDSP(0xd1); // turn on speaker oldmixervalue = ReadMixer (0xe); WriteMixer (0xe, oldmixervalue | 0x2); // turn on stereo WriteDSP(0x14); // send one byte WriteDSP(0x0); WriteDSP(0x0); for (i = 0; i < 0x10000; i++) dos_inportb(dsp_port + 0xe); // ack the dsp timeconstant = 65536 - (256000000 / (shm->channels * shm->speed)); WriteDSP(0x40); WriteDSP(timeconstant >> 8); WriteMixer (0xe, ReadMixer(0xe) | 0x20); // turn off filter WriteDSP(0x48); WriteDSP((shm->samples - 1) & 0xff); // # of samples - 1 WriteDSP((shm->samples - 1) >> 8); WriteDSP(0x90); // high speed 8 bit stereo } // normal speed mono else { Con_Printf("Version 2 SB startup\n"); WriteDSP(0xd1); // turn on speaker timeconstant = 65536 - (256000000 / (shm->channels * shm->speed)); WriteDSP(0x40); WriteDSP(timeconstant >> 8); WriteDSP(0x48); WriteDSP((shm->samples - 1) & 0xff); // # of samples - 1 WriteDSP((shm->samples - 1) >> 8); WriteDSP(0x1c); // normal speed 8 bit mono } } static int page_reg[] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; static int addr_reg[] = { 0, 2, 4, 6, 0xc0, 0xc4, 0xc8, 0xcc }; static int count_reg[] = { 1, 3, 5, 7, 0xc2, 0xc6, 0xca, 0xce }; static int mode_reg; static int flipflop_reg; static int disable_reg; static int clear_reg; /* ================ StartDMA ================ */ static void BLASTER_StartDMA (void) { int mode; int realaddr; realaddr = ptr2real(dma_buffer); // use a high dma channel if specified if (high_dma && dsp_version >= 4) // 8 bit snd can never use 16 bit dma dma = high_dma; else dma = low_dma; Con_Printf ("Using DMA channel %i\n", dma); if (dma > 3) { mode_reg = 0xd6; flipflop_reg = 0xd8; disable_reg = 0xd4; clear_reg = 0xdc; } else { mode_reg = 0xb; flipflop_reg = 0xc; disable_reg = 0xa; clear_reg = 0xe; } dos_outportb(disable_reg, dma|4); // disable channel // set mode- see "undocumented pc", p.876 mode = (1 << 6) // single-cycle + (0 << 5) // address increment + (1 << 4) // auto-init dma + (2 << 2) // read + (dma & 3); // channel # dos_outportb(mode_reg, mode); // set address // set page dos_outportb(page_reg[dma], realaddr >> 16); if (dma > 3) { // address is in words dos_outportb(flipflop_reg, 0); // prepare to send 16-bit value dos_outportb(addr_reg[dma], (realaddr >> 1) & 0xff); dos_outportb(addr_reg[dma], (realaddr >> 9) & 0xff); dos_outportb(flipflop_reg, 0); // prepare to send 16-bit value dos_outportb(count_reg[dma], ((dma_size >> 1) - 1) & 0xff); dos_outportb(count_reg[dma], ((dma_size >> 1) - 1) >> 8); } else { // address is in bytes dos_outportb(flipflop_reg, 0); // prepare to send 16-bit value dos_outportb(addr_reg[dma], realaddr & 0xff); dos_outportb(addr_reg[dma], (realaddr >> 8) & 0xff); dos_outportb(flipflop_reg, 0); // prepare to send 16-bit value dos_outportb(count_reg[dma], (dma_size - 1) & 0xff); dos_outportb(count_reg[dma], (dma_size - 1) >> 8); } dos_outportb(clear_reg, 0); // clear write mask dos_outportb(disable_reg, dma & ~4); } /* ================== BLASTER_Init Returns false if nothing is found. ================== */ static qboolean S_BLASTER_Init (dma_t *dma) { int size; int realaddr; int rc; shm = NULL; // // must have a blaster variable set // if (!GetBLASTER()) { Con_Printf ("The BLASTER environment variable is not set,\n" "Sound Blaster support is disabled.\n"); return false; } if (ResetDSP()) { Con_Printf("Could not reset SB\n"); return false; } // // get dsp version // WriteDSP(0xe1); dsp_version = ReadDSP(); dsp_minor_version = ReadDSP(); // we need at least v2 for auto-init dma if (dsp_version < 2) { Con_Printf ("Sound blaster must be at least v2.0\n"); return false; } // allow command line parm to set quality down rc = COM_CheckParm ("-dsp"); if (rc && rc < com_argc - 1) { rc = atoi(com_argv[rc+1]); if (rc < 2 || rc > 4) Con_Printf ("-dsp parameter can only be 2, 3, or 4\n"); else if (rc > dsp_version) Con_Printf ("Can't -dsp %i on v%i hardware\n", rc, dsp_version); else dsp_version = rc; } // everyone does 11khz sampling rate unless told otherwise memset ((void *) dma, 0, sizeof(dma_t)); shm = dma; shm->speed = 11025; rc = COM_CheckParm("-sndspeed"); if (rc && rc < com_argc - 1) shm->speed = atoi(com_argv[rc+1]); // version 4 cards (sb 16) do 16 bit stereo if (dsp_version >= 4) { shm->channels = 2; shm->samplebits = 16; } // version 3 cards (sb pro) do 8 bit stereo else if (dsp_version == 3) { shm->channels = 2; shm->samplebits = 8; } // v2 cards do 8 bit mono else { shm->channels = 1; shm->samplebits = 8; } Cmd_AddCommand("sbinfo", SB_Info_f); // allocate 8k and get a 4k-aligned buffer from it size = 4096; dma_dosadr = dos_getmemory(size * 2); if (!dma_dosadr) { shm = NULL; Con_Printf("Couldn't allocate sound dma buffer\n"); return false; } realaddr = ptr2real(dma_dosadr); realaddr = (realaddr + size) & ~(size - 1); dma_buffer = (short *) real2ptr(realaddr); dma_size = size; memset(dma_buffer, 0, dma_size); shm->samples = size / (shm->samplebits / 8); shm->samplepos = 0; shm->submission_chunk = 1; shm->buffer = (unsigned char *) dma_buffer; BLASTER_StartDMA(); StartSB(); return true; } /* ============== BLASTER_GetDMAPos return the current sample position (in mono samples read) inside the recirculating dma buffer, so the mixing code will know how many sample are required to fill it up. =============== */ static int S_BLASTER_GetDMAPos (void) { int count; if (! dma_buffer) /* not initialized */ return 0; // this function is called often. acknowledge the transfer completions // all the time so that it loops if (dsp_version >= 4) dos_inportb(dsp_port + 0xf); // 16 bit audio else dos_inportb(dsp_port + 0xe); // 8 bit audio // clear 16-bit reg flip-flop // load the current dma count register if (dma < 4) { dos_outportb(0xc, 0); count = dos_inportb(dma * 2 + 1); count += dos_inportb(dma * 2 + 1) << 8; if (shm->samplebits == 16) count /= 2; count = shm->samples - (count + 1); } else { dos_outportb(0xd8, 0); count = dos_inportb(0xc0 + (dma - 4) * 4 + 2); count += dos_inportb(0xc0 + (dma - 4) * 4 + 2) << 8; if (shm->samplebits == 8) count *= 2; count = shm->samples - (count + 1); } // Con_Printf("DMA pos = 0x%x\n", count); shm->samplepos = count & (shm->samples - 1); return shm->samplepos; } /* ============== BLASTER_Shutdown Reset the sound device for exiting =============== */ static void S_BLASTER_Shutdown(void) { if (! dma_buffer) /* not initialized */ return; if (dsp_version >= 4) { } else if (dsp_version == 3) { ResetDSP (); // stop high speed mode WriteMixer (0xe, oldmixervalue); // turn stereo off and filter on } else { } WriteDSP(0xd3); // turn off speaker ResetDSP (); dos_outportb(disable_reg, dma|4); // disable dma channel dos_freememory(dma_dosadr); dma_dosadr = NULL; } /* ============== SNDDMA_LockBuffer Makes sure dma buffer is valid =============== */ static void S_BLASTER_LockBuffer (void) { /* nothing to do here */ } /* ============== SNDDMA_Submit Unlock the dma buffer / Send sound to the device =============== */ static void S_BLASTER_Submit (void) { /* nothing to do here */ } static void S_BLASTER_BlockSound (void) { } static void S_BLASTER_UnblockSound (void) { } snd_driver_t snddrv_blaster = { S_BLASTER_Init, S_BLASTER_Shutdown, S_BLASTER_GetDMAPos, S_BLASTER_LockBuffer, S_BLASTER_Submit, S_BLASTER_BlockSound, S_BLASTER_UnblockSound, s_sb_driver, SNDDRV_ID_SB_DOS, false, NULL }; #endif /* HAVE_DOS_SB_SOUND */ engine/h2shared/snd_sb.h000066400000000000000000000003431444734033100154470ustar00rootroot00000000000000/* DOS sound support for Sound Blaster. */ #if !defined(__HX2_SND_BLASTER) #define __HX2_SND_BLASTER #if HAVE_DOS_SB_SOUND extern snd_driver_t snddrv_blaster; #endif /* HAVE_DOS_SB_SOUND */ #endif /* __HX2_SND_BLASTER */ engine/h2shared/snd_sdl.c000066400000000000000000000126411444734033100156240ustar00rootroot00000000000000/* * snd_sdl.c - SDL audio driver for Hexen II: Hammer of Thyrion (uHexen2) * based on implementations found in the quakeforge and ioquake3 projects. * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "snd_sys.h" #if HAVE_SDL_SOUND #include "snd_sdl.h" #include "sdl_inc.h" /* whether to use hunk for dma buffer memory, either 1 or 0 */ #define USE_HUNK_ALLOC 0 static char s_sdl_driver[] = "SDLAudio"; static int buffersize; static void SDLCALL paint_audio (void *unused, Uint8 *stream, int len) { int pos, tobufend; int len1, len2; if (!shm) { /* shouldn't happen, but just in case */ memset(stream, 0, len); return; } pos = (shm->samplepos * (shm->samplebits / 8)); if (pos >= buffersize) shm->samplepos = pos = 0; tobufend = buffersize - pos; /* bytes to buffer's end. */ len1 = len; len2 = 0; if (len1 > tobufend) { len1 = tobufend; len2 = len - len1; } memcpy(stream, shm->buffer + pos, len1); if (len2 <= 0) { shm->samplepos += (len1 / (shm->samplebits / 8)); } else { /* wraparound? */ memcpy(stream + len1, shm->buffer, len2); shm->samplepos = (len2 / (shm->samplebits / 8)); } if (shm->samplepos >= buffersize) shm->samplepos = 0; } static qboolean S_SDL_Init (dma_t *dma) { SDL_AudioSpec desired, obtained; int tmp, val; char drivername[128]; if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { Con_Printf("Couldn't init SDL audio: %s\n", SDL_GetError()); return false; } /* Set up the desired format */ desired.freq = desired_speed; desired.format = (desired_bits == 16) ? AUDIO_S16SYS : AUDIO_U8; desired.channels = desired_channels; if (desired.freq <= 11025) desired.samples = 256; else if (desired.freq <= 22050) desired.samples = 512; else if (desired.freq <= 44100) desired.samples = 1024; else if (desired.freq <= 56000) desired.samples = 2048; /* for 48 kHz */ else desired.samples = 4096; /* for 96 kHz */ desired.callback = paint_audio; desired.userdata = NULL; /* Open the audio device */ if (SDL_OpenAudio(&desired, &obtained) == -1) { Con_Printf("Couldn't open SDL audio: %s\n", SDL_GetError()); SDL_QuitSubSystem(SDL_INIT_AUDIO); return false; } /* Make sure we can support the audio format */ switch (obtained.format) { case AUDIO_S8: /* maybe needed by AHI */ case AUDIO_U8: case AUDIO_S16SYS: /* Supported */ break; default: Con_Printf ("Unsupported audio format received (%u)\n", obtained.format); SDL_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); return false; } memset ((void *) dma, 0, sizeof(dma_t)); shm = dma; /* Fill the audio DMA information block */ shm->samplebits = (obtained.format & 0xFF); /* first byte of format is bits */ shm->signed8 = (obtained.format == AUDIO_S8); if (obtained.freq != desired_speed) Con_Printf ("Warning: Rate set (%d) didn't match requested rate (%d)!\n", obtained.freq, desired_speed); shm->speed = obtained.freq; shm->channels = obtained.channels; tmp = (obtained.samples * obtained.channels) * 10; if (tmp & (tmp - 1)) { /* make it a power of two */ val = 1; while (val < tmp) val <<= 1; tmp = val; } shm->samples = tmp; shm->samplepos = 0; shm->submission_chunk = 1; Con_Printf ("SDL audio spec : %d Hz, %d samples, %d channels\n", obtained.freq, obtained.samples, obtained.channels); if (SDL_AudioDriverName(drivername, sizeof(drivername)) == NULL) strcpy(drivername, "(UNKNOWN)"); buffersize = shm->samples * (shm->samplebits / 8); Con_Printf ("SDL audio driver: %s, %d bytes buffer\n", drivername, buffersize); #if USE_HUNK_ALLOC shm->buffer = (unsigned char *) Hunk_AllocName(buffersize, "sdl_audio"); #else shm->buffer = (unsigned char *) calloc (1, buffersize); if (!shm->buffer) { SDL_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); shm = NULL; Con_Printf ("Failed allocating memory for SDL audio\n"); return false; } #endif SDL_PauseAudio(0); return true; } static int S_SDL_GetDMAPos (void) { return shm->samplepos; } static void S_SDL_Shutdown (void) { if (shm) { Con_Printf ("Shutting down SDL sound\n"); SDL_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); #if !USE_HUNK_ALLOC if (shm->buffer) free (shm->buffer); #endif shm->buffer = NULL; shm = NULL; } } static void S_SDL_LockBuffer (void) { SDL_LockAudio (); } static void S_SDL_Submit (void) { SDL_UnlockAudio(); } static void S_SDL_BlockSound (void) { SDL_PauseAudio(1); } static void S_SDL_UnblockSound (void) { SDL_PauseAudio(0); } snd_driver_t snddrv_sdl = { S_SDL_Init, S_SDL_Shutdown, S_SDL_GetDMAPos, S_SDL_LockBuffer, S_SDL_Submit, S_SDL_BlockSound, S_SDL_UnblockSound, s_sdl_driver, SNDDRV_ID_SDL, false, NULL }; #endif /* HAVE_SDL_SOUND */ engine/h2shared/snd_sdl.h000066400000000000000000000003011444734033100156170ustar00rootroot00000000000000/* Sound support using SDL. */ #if !defined(__HX2_SND_SDL) #define __HX2_SND_SDL #if HAVE_SDL_SOUND extern snd_driver_t snddrv_sdl; #endif /* HAVE_SDL_SOUND */ #endif /* __HX2_SND_SDL */ engine/h2shared/snd_sun.c000066400000000000000000000131731444734033100156500ustar00rootroot00000000000000/* snd_sun.c -- SUN Audio driver for BSD and SunOS * Based on the code from Quake1 and the DarkPlaces project, with small * adaptations to make it work with Hexen II: Hammer of Thyrion (uHexen2) * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "snd_sys.h" #if HAVE_SUN_SOUND #include "snd_sun.h" #undef _SUNAUDIO_BSD #undef _SUNAUDIO_SUNOS #if defined(__sun) || defined(sun) #define _SUNAUDIO_BSD 0 #define _SUNAUDIO_SUNOS 1 #else /* NetBSD and OpenBSD */ #define _SUNAUDIO_BSD 1 #define _SUNAUDIO_SUNOS 0 #endif #include #include #include #include #include #if _SUNAUDIO_SUNOS #define FORMAT_U8 AUDIO_ENCODING_LINEAR8 #define FORMAT_S16 AUDIO_ENCODING_LINEAR #elif _SUNAUDIO_BSD #include #define FORMAT_U8 AUDIO_ENCODING_LINEAR8 #if ENDIAN_RUNTIME_DETECT static int FORMAT_S16; #elif (BYTE_ORDER == BIG_ENDIAN) #define FORMAT_S16 AUDIO_ENCODING_SLINEAR_BE #elif (BYTE_ORDER == LITTLE_ENDIAN) #define FORMAT_S16 AUDIO_ENCODING_SLINEAR_LE #else #error "Unsupported endianness." #endif /* BYTE_ORDER */ #endif /* _SUNAUDIO_BSD */ static char s_sun_driver[] = "SunAudio"; static int audio_fd = -1; #define SND_BUFF_SIZE 8192 static unsigned char dma_buffer [SND_BUFF_SIZE]; static unsigned char writebuf [1024]; static int wbufp; static qboolean S_SUN_Init (dma_t *dma) { const char *snddev; audio_info_t info; #if ENDIAN_RUNTIME_DETECT && _SUNAUDIO_BSD switch (host_byteorder) { case BIG_ENDIAN: FORMAT_S16 = AUDIO_ENCODING_SLINEAR_BE; break; case LITTLE_ENDIAN: FORMAT_S16 = AUDIO_ENCODING_SLINEAR_LE; break; default: Sys_Error("%s: Unsupported byte order.", __thisfunc__); break; } #endif /* ENDIAN_RUNTIME_DETECT */ /* Open the audio device */ #if defined(_PATH_SOUND) snddev = _PATH_SOUND; #elif _SUNAUDIO_SUNOS snddev = "/dev/audio"; #else /* bsd */ snddev = "/dev/sound"; #endif audio_fd = open (snddev, O_WRONLY | O_NDELAY | O_NONBLOCK); if (audio_fd == -1) { Con_Printf("Can't open the sound device (%s)\n", snddev); return false; } memset ((void *) dma, 0, sizeof(dma_t)); shm = dma; AUDIO_INITINFO (&info); /* these desired values are decided in snd_dma.c according * to the defaults and the user's command line parameters. */ info.play.sample_rate = desired_speed; info.play.channels = desired_channels; info.play.precision = desired_bits; info.play.encoding = (desired_bits == 8) ? FORMAT_U8 : FORMAT_S16; if (ioctl(audio_fd, AUDIO_SETINFO, &info) != 0) { /* TODO: also try other options of sampling * rate and format upon failure??? */ Con_Printf("Couldn't set desired sound output format (%d bit, %s, %d Hz)\n", desired_bits, (desired_channels == 2) ? "stereo" : "mono", desired_speed); close (audio_fd); shm = NULL; return false; } shm->channels = info.play.channels; shm->samplebits = info.play.precision; shm->speed = info.play.sample_rate; if (shm->speed != desired_speed) Con_Printf ("Warning: Rate set (%d) didn't match requested rate (%d)!\n", shm->speed, desired_speed); shm->samples = sizeof(dma_buffer) / (shm->samplebits / 8); shm->submission_chunk = 1; shm->samplepos = 0; shm->buffer = dma_buffer; return true; } static void S_SUN_Shutdown (void) { if (shm) { shm = NULL; close (audio_fd); audio_fd = -1; } } static int S_SUN_GetDMAPos (void) { audio_info_t info; if (!shm) return 0; if (ioctl(audio_fd, AUDIO_GETINFO, &info) == -1) { Con_Printf("Error: can't get audio info\n"); S_SUN_Shutdown (); return 0; } return ((info.play.samples * shm->channels) % shm->samples); } /* ============== SNDDMA_LockBuffer Makes sure dma buffer is valid ============== */ static void S_SUN_LockBuffer (void) { /* nothing to do here */ } /* ============== SNDDMA_Submit Unlock the dma buffer / Send sound to the device =============== */ static void S_SUN_Submit (void) { int bsize; int bytes, b; unsigned char *p; int idx; int stop = paintedtime; if (!shm) return; if (paintedtime < wbufp) wbufp = 0; /* reset */ bsize = shm->channels * shm->samplebits / 8; bytes = (paintedtime - wbufp) * bsize; if (!bytes) return; if (bytes > sizeof(writebuf)) { bytes = sizeof(writebuf); stop = wbufp + bytes / bsize; } /* transfer the sound data from the circular dma_buffer to writebuf */ /* TODO: using 2 memcpys instead of this loop should be faster */ p = writebuf; idx = (wbufp * bsize) & (sizeof(dma_buffer) - 1); for (b = bytes; b; b--) { *p++ = dma_buffer[idx]; idx = (idx + 1) & (sizeof(dma_buffer) - 1); } if (write(audio_fd, writebuf, bytes) < bytes) Con_Printf("audio can't keep up!\n"); wbufp = stop; } static void S_SUN_BlockSound (void) { } static void S_SUN_UnblockSound (void) { } snd_driver_t snddrv_sunaudio = { S_SUN_Init, S_SUN_Shutdown, S_SUN_GetDMAPos, S_SUN_LockBuffer, S_SUN_Submit, S_SUN_BlockSound, S_SUN_UnblockSound, s_sun_driver, SNDDRV_ID_SUN, false, NULL }; #endif /* HAVE_SUN_SOUND */ engine/h2shared/snd_sun.h000066400000000000000000000003131444734033100156450ustar00rootroot00000000000000/* Sound support using SunAudio. */ #if !defined(__HX2_SND_SUN) #define __HX2_SND_SUN #if HAVE_SUN_SOUND extern snd_driver_t snddrv_sunaudio; #endif /* HAVE_SUN_SOUND */ #endif /* __HX2_SND_SUN */ engine/h2shared/snd_sys.c000066400000000000000000000074711444734033100156650ustar00rootroot00000000000000/* * snd_sys.c -- pre-Init platform specific sound stuff * * Copyright (C) 2007-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "snd_sys.h" /* drivers' headers: */ #include "snd_alsa.h" #include "snd_oss.h" #include "snd_sdl.h" #include "snd_sun.h" #include "snd_win.h" #include "snd_dsound.h" #include "snd_sb.h" #include "snd_gus.h" #include "snd_pci.h" #include "snd_ahi.h" #include "snd_paula.h" static qboolean snd_sys_inited = false; /* dummy SNDDMA functions, just in case */ static char s_null_driver[] = "NULL"; static qboolean S_NULL_Init (dma_t *dma) { #if SOUND_NUMDRIVERS Con_Printf ("No sound\n"); #else Con_Printf ("SOUND: no drivers available\n"); #endif shm = NULL; return false; } static int S_NULL_GetDMAPos (void) { return 0; } #define S_NULL_Shutdown NULL_void_func #define S_NULL_LockBuffer NULL_void_func #define S_NULL_Submit NULL_void_func #define S_NULL_BlockSound NULL_void_func #define S_NULL_UnblockSound NULL_void_func static void NULL_void_func (void) { } static snd_driver_t snddrv_null = { /* do register this first so that it * stays last in the linked list. */ S_NULL_Init, S_NULL_Shutdown, S_NULL_GetDMAPos, S_NULL_LockBuffer, S_NULL_Submit, S_NULL_BlockSound, S_NULL_UnblockSound, s_null_driver, SNDDRV_ID_NULL, false, NULL }; static snd_driver_t *snd_drivers = &snddrv_null; static void S_RegisterDriver(snd_driver_t *driver) { driver->next = snd_drivers; snd_drivers = driver; } void S_DriversInit (void) { if (snd_sys_inited) return; snd_sys_inited = true; snd_drivers = NULL; S_RegisterDriver(&snddrv_null); if (safemode || COM_CheckParm("-nosound") || COM_CheckParm("-s")) snd_drivers->userpreferred = true; #if HAVE_SUN_SOUND S_RegisterDriver(&snddrv_sunaudio); if (COM_CheckParm ("-sndsun") || COM_CheckParm ("-sndbsd")) snd_drivers->userpreferred = true; #endif #if HAVE_ALSA_SOUND S_RegisterDriver(&snddrv_alsa); if (COM_CheckParm ("-sndalsa")) snd_drivers->userpreferred = true; #endif #if HAVE_OSS_SOUND S_RegisterDriver(&snddrv_oss); if (COM_CheckParm ("-sndoss")) snd_drivers->userpreferred = true; #endif #if HAVE_WIN_SOUND S_RegisterDriver(&snddrv_win); if (COM_CheckParm ("-wavonly")) snd_drivers->userpreferred = true; #endif #if HAVE_WIN_DX_SOUND S_RegisterDriver(&snddrv_dsound); #endif #if HAVE_DOS_SB_SOUND S_RegisterDriver(&snddrv_blaster); #endif #if HAVE_DOS_GUS_SOUND S_RegisterDriver(&snddrv_gus); #endif #if HAVE_DOS_PCI_SOUND S_RegisterDriver(&snddrv_pci); #endif #if HAVE_AHI_SOUND S_RegisterDriver(&snddrv_ahi); if (COM_CheckParm ("-sndahi")) snd_drivers->userpreferred = true; #endif #if HAVE_PAULA_SOUND S_RegisterDriver(&snddrv_paula); if (COM_CheckParm ("-sndpaula")) snd_drivers->userpreferred = true; #endif /* if sdl audio is compiled for any supported platform, then * register sdl audio the last to make it the default choice. */ #if HAVE_SDL_SOUND S_RegisterDriver(&snddrv_sdl); if (COM_CheckParm ("-sndsdl")) snd_drivers->userpreferred = true; #endif } void S_GetDriverList (snd_driver_t **p) { *p = snd_drivers; } void S_GetNullDriver (snd_driver_t **p) { *p = &snddrv_null; } engine/h2shared/snd_sys.h000066400000000000000000000076151444734033100156720ustar00rootroot00000000000000/* snd_sys.h * * Copyright (C) 2007-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_SND_SYS__ #define __HX2_SND_SYS__ #undef HAVE_SDL_SOUND #undef HAVE_OSS_SOUND #undef HAVE_SUN_SOUND #undef HAVE_ALSA_SOUND #undef HAVE_DOS_GUS_SOUND #undef HAVE_DOS_SB_SOUND #undef HAVE_DOS_PCI_SOUND #undef HAVE_WIN_SOUND #undef HAVE_WIN_DX_SOUND #undef HAVE_AHI_SOUND #undef HAVE_PAULA_SOUND #undef SOUND_NUMDRIVERS #if defined(NO_OSS_AUDIO) #define HAVE_OSS_SOUND 0 /* add more systems with OSS here */ #elif defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) #define HAVE_OSS_SOUND 1 #else #define HAVE_OSS_SOUND 0 #endif #if defined(NO_SUN_AUDIO) #define HAVE_SUN_SOUND 0 /* add more systems with SUN audio here */ #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__sun) || defined(sun) #define HAVE_SUN_SOUND 1 #else #define HAVE_SUN_SOUND 0 #endif #if defined(NO_ALSA_AUDIO) #define HAVE_ALSA_SOUND 0 #elif defined(__linux) || defined(__linux__) #define HAVE_ALSA_SOUND 1 #else #define HAVE_ALSA_SOUND 0 #endif #if defined(NO_SDL_AUDIO) #define HAVE_SDL_SOUND 0 #elif defined(SDLQUAKE) #define HAVE_SDL_SOUND 1 #else #define HAVE_SDL_SOUND 0 #endif #if defined(PLATFORM_WINDOWS) #define HAVE_WIN_SOUND 1 #define HAVE_WIN_DX_SOUND 1 #else #define HAVE_WIN_SOUND 0 #define HAVE_WIN_DX_SOUND 0 #endif #if defined(PLATFORM_DOS) #define HAVE_DOS_GUS_SOUND 1 #define HAVE_DOS_SB_SOUND 1 #if defined(NO_PCI_AUDIO) #define HAVE_DOS_PCI_SOUND 0 #else #define HAVE_DOS_PCI_SOUND 1 #endif #else #define HAVE_DOS_GUS_SOUND 0 #define HAVE_DOS_SB_SOUND 0 #define HAVE_DOS_PCI_SOUND 0 #endif #if defined(PLATFORM_AMIGA) #define HAVE_AHI_SOUND 1 #ifdef PLATFORM_AMIGAOS3 #define HAVE_PAULA_SOUND 1 #else #define HAVE_PAULA_SOUND 0 #endif #else #define HAVE_AHI_SOUND 0 #define HAVE_PAULA_SOUND 0 #endif #define SOUND_NUMDRIVERS (HAVE_SDL_SOUND + HAVE_OSS_SOUND + HAVE_SUN_SOUND + HAVE_ALSA_SOUND + HAVE_WIN_SOUND + HAVE_WIN_DX_SOUND + HAVE_DOS_SB_SOUND + HAVE_DOS_GUS_SOUND + HAVE_AHI_SOUND + HAVE_PAULA_SOUND + HAVE_DOS_PCI_SOUND) enum snddrv_id_t { SNDDRV_ID_NULL = 0, SNDDRV_ID_OSS, SNDDRV_ID_ALSA, SNDDRV_ID_SDL, SNDDRV_ID_SUN, SNDDRV_ID_WIN, SNDDRV_ID_DSOUND, SNDDRV_ID_GUS_DOS, SNDDRV_ID_SB_DOS, SNDDRV_ID_PCI_DOS, SNDDRV_ID_AHI, SNDDRV_ID_PAULA, SNDDRV_ID_MAX }; typedef struct snd_driver_s { qboolean (*Init)(dma_t *); /* initializes the sound dma driver */ void (*Shutdown)(void); /* shutdown the DMA xfer and driver */ int (*GetDMAPos)(void); /* returns the current dma position */ void (*LockBuffer)(void); /* validates & locks the dma buffer */ void (*Submit)(void); /* unlocks the dma buffer / sends sound to the device */ void (*BlockSound)(void); /* blocks sound output upon window focus loss */ void (*UnblockSound)(void); /* unblocks the output upon window focus gain */ const char *snddrv_name; /* active driver's name */ enum snddrv_id_t snddrv_id; /* enumerated ID number */ qboolean userpreferred; /* preferred by command line arguments */ struct snd_driver_s *next; /* next to try if previous failed */ } snd_driver_t; extern void S_DriversInit (void); extern void S_GetDriverList (snd_driver_t **); extern void S_GetNullDriver (snd_driver_t **); #endif /* __HX2_SND_SYS__ */ engine/h2shared/snd_timidity.c000066400000000000000000000151371444734033100167010ustar00rootroot00000000000000/* MIDI streaming music support using Timidity library. * libTiMidity v0.2.0 or newer needed * https://sf.net/p/libtimidity/ (local copy included under libs/) * * Copyright (C) 2010-2015 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(USE_CODEC_TIMIDITY) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_timidity.h" #include #include "filenames.h" #if !defined(LIBTIMIDITY_VERSION) || (LIBTIMIDITY_VERSION-0 < 0x000200L) #error libtimidity v0.2.0 or newer is required. #endif #define CACHEBUFFER_SIZE 4096 typedef struct _midi_buf_t { MidSong *song; sint8 midi_buffer[CACHEBUFFER_SIZE]; int pos, last; } midi_buf_t; static size_t timidity_fread (void *ctx, void *ptr, size_t size, size_t nmemb) { return FS_fread (ptr, size, nmemb, (fshandle_t *) ctx); } static int timidity_fseek (void *ctx, long offset, int whence) { return FS_fseek ((fshandle_t *) ctx, offset, whence); } static long timidity_ftell (void *ctx) { return FS_ftell ((fshandle_t *) ctx); } static int timidity_fclose (void *ctx) { return 0; /* we fclose() elsewhere. */ } static const char *cfgfile[] = { #if defined(__DJGPP__) /* prefer '/' instead of '\\' */ "C:/TIMIDITY", #elif defined(PLATFORM_DOS) || defined(PLATFORM_WINDOWS) || defined(PLATFORM_OS2) "C:\\TIMIDITY", #elif defined(__MORPHOS__) "LIBS:GerontoPlayer", #elif defined(__AROS__) "Timidity:",/* disable the system requester for this (see below) */ #elif defined(PLATFORM_AMIGA) /**/ #else /* unix, osx, riscos, ... */ "/etc", "/etc/timidity", "/usr/share/timidity", "/usr/local/share/timidity", "/usr/local/lib/timidity", #endif NULL /* last entry must be NULL */ }; static int TIMIDITY_InitHelper (const char *cfgdir) { char path[MAX_OSPATH]; int len; len = q_strlcpy(path, cfgdir, sizeof(path)); if (len >= (int)sizeof(path) - 1) return -1; if (len && !IS_DIR_SEPARATOR(path[len - 1])) path[len++] = DIR_SEPARATOR_CHAR; path[len] = '\0'; q_strlcat(path, "timidity.cfg", sizeof(path)); Con_DPrintf("Timidity: trying %s\n", path); #ifdef PLATFORM_AMIGA if (!Sys_PathExistsQuiet(path)) return -1; #endif return mid_init(path); } static qboolean S_TIMIDITY_CodecInitialize (void) { const char *timi_env; int i, err; long ver; if (timidity_codec.initialized) return true; err = -1; timi_env = getenv("TIMIDITY_CFG"); if (timi_env) { Con_DPrintf("Timidity: trying %s\n", timi_env); /* env is an override: if it fails, we * don't bother trying anything else. */ err = mid_init(timi_env); goto _finish; } #if DO_USERDIRS /* check under the user's directory first: */ err = TIMIDITY_InitHelper(FS_GetUserbase()); #endif /* then, check under the installation dir: */ if (err != 0) err = TIMIDITY_InitHelper(FS_GetBasedir()); /* lastly, check with the system locations: */ for (i = 0; err != 0 && cfgfile[i] != NULL; ++i) err = TIMIDITY_InitHelper(cfgfile[i]); _finish: if (err != 0) { Con_Printf ("Could not initialize Timidity\n"); return false; } ver = mid_get_version(); Con_Printf ("libTiMidity v%ld.%ld.%ld initialized\n", (ver>>16)&255, (ver>>8)&255, ver&255); timidity_codec.initialized = true; return true; } static void S_TIMIDITY_CodecShutdown (void) { if (!timidity_codec.initialized) return; timidity_codec.initialized = false; Con_Printf("Shutting down Timidity.\n"); mid_exit (); } static qboolean S_TIMIDITY_CodecOpenStream (snd_stream_t *stream) { midi_buf_t *data; MidSongOptions options; MidIStream *midistream; int width; if (!timidity_codec.initialized) return false; options.rate = shm->speed; width = shm->samplebits / 8; options.channels = shm->channels; if (width == 1) options.format = MID_AUDIO_U8; else if (host_byteorder == BIG_ENDIAN) options.format = MID_AUDIO_S16MSB; else /* assumed LITTLE_ENDIAN. */ options.format = MID_AUDIO_S16LSB; options.buffer_size = CACHEBUFFER_SIZE / (width * options.channels); midistream = mid_istream_open_callbacks (timidity_fread, timidity_fseek, timidity_ftell, timidity_fclose, & stream->fh); if (!midistream) { Con_Printf ("Couldn't create Timidity stream for %s\n", stream->name); return false; } data = (midi_buf_t *) Z_Malloc(sizeof(midi_buf_t), Z_MAINZONE); data->song = mid_song_load (midistream, &options); mid_istream_close (midistream); if (data->song == NULL) { Con_Printf ("%s is not a valid MIDI file\n", stream->name); Z_Free(data); return false; } stream->info.rate = options.rate; stream->info.bits = shm->samplebits; stream->info.width = width; stream->info.channels = options.channels; stream->priv = data; mid_song_set_volume (data->song, 100); mid_song_start (data->song); return true; } static int S_TIMIDITY_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { midi_buf_t *data = (midi_buf_t *) stream->priv; if (data->pos == 0) { data->last = mid_song_read_wave (data->song, data->midi_buffer, CACHEBUFFER_SIZE); if (data->last == 0) return 0; if (bytes > data->last) bytes = data->last; } else if (data->pos + bytes > data->last) { bytes = data->last - data->pos; } memcpy (buffer, & data->midi_buffer[data->pos], bytes); /* Timidity byte swaps according to the format with which * the stream is opened, therefore no swap needed here. */ data->pos += bytes; if (data->pos == data->last) data->pos = 0; return bytes; } static void S_TIMIDITY_CodecCloseStream (snd_stream_t *stream) { mid_song_free (((midi_buf_t *)stream->priv)->song); Z_Free(stream->priv); S_CodecUtilClose(&stream); } static int S_TIMIDITY_CodecRewindStream (snd_stream_t *stream) { mid_song_start (((midi_buf_t *)stream->priv)->song); return 0; } snd_codec_t timidity_codec = { CODECTYPE_MIDI, false, "mid", S_TIMIDITY_CodecInitialize, S_TIMIDITY_CodecShutdown, S_TIMIDITY_CodecOpenStream, S_TIMIDITY_CodecReadStream, S_TIMIDITY_CodecRewindStream, NULL, /* jump */ S_TIMIDITY_CodecCloseStream, NULL }; #endif /* USE_CODEC_TIMIDITY */ engine/h2shared/snd_timidity.h000066400000000000000000000003641444734033100167020ustar00rootroot00000000000000/* MIDI streaming music support using Timidity. */ #if !defined(_SND_TIMIDITY_H_) #define _SND_TIMIDITY_H_ #if defined(USE_CODEC_TIMIDITY) extern snd_codec_t timidity_codec; #endif /* USE_CODEC_TIMIDITY */ #endif /* ! _SND_TIMIDITY_H_ */ engine/h2shared/snd_umx.c000066400000000000000000000250341444734033100156530ustar00rootroot00000000000000/** * Unreal UMX container support. * UPKG parsing partially based on Unreal Media Ripper (UMR) v0.3 * by Andy Ward , with additional updates * by O. Sezer - see git repo at https://github.com/sezero/umr.git * * Copyright (C) 2013-2021 O. Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(USE_CODEC_UMX) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_umx.h" typedef int32_t fci_t; /* FCompactIndex */ #define UPKG_HDR_TAG 0x9e2a83c1 struct _genhist { /* for upkg versions >= 68 */ int32_t export_count; int32_t name_count; }; struct upkg_hdr { uint32_t tag; /* UPKG_HDR_TAG */ int32_t file_version; uint32_t pkg_flags; int32_t name_count; /* number of names in name table (>= 0) */ int32_t name_offset; /* offset to name table (>= 0) */ int32_t export_count; /* num. exports in export table (>= 0) */ int32_t export_offset; /* offset to export table (>= 0) */ int32_t import_count; /* num. imports in export table (>= 0) */ int32_t import_offset; /* offset to import table (>= 0) */ /* number of GUIDs in heritage table (>= 1) and table's offset: * only with versions < 68. */ int32_t heritage_count; int32_t heritage_offset; /* with versions >= 68: a GUID, a dword for generation count * and export_count and name_count dwords for each generation: */ uint32_t guid[4]; int32_t generation_count; #define UPKG_HDR_SIZE 64 /* 64 bytes up until here */ struct _genhist *gen; }; COMPILE_TIME_ASSERT(upkg_hdr, offsetof(struct upkg_hdr, gen) == UPKG_HDR_SIZE); #define UMUSIC_IT 0 #define UMUSIC_S3M 1 #define UMUSIC_XM 2 #define UMUSIC_MOD 3 #define UMUSIC_WAV 4 #define UMUSIC_MP2 5 static const char *mustype[] = { "IT", "S3M", "XM", "MOD", "WAV", "MP2", NULL }; /* decode an FCompactIndex. * original documentation by Tim Sweeney was at * http://unreal.epicgames.com/Packages.htm * also see Unreal Wiki: * http://wiki.beyondunreal.com/Legacy:Package_File_Format/Data_Details */ static fci_t get_fci (const char *in, int *pos) { int32_t a; int size; size = 1; a = in[0] & 0x3f; if (in[0] & 0x40) { size++; a |= (in[1] & 0x7f) << 6; if (in[1] & 0x80) { size++; a |= (in[2] & 0x7f) << 13; if (in[2] & 0x80) { size++; a |= (in[3] & 0x7f) << 20; if (in[3] & 0x80) { size++; a |= (in[4] & 0x3f) << 27; } } } } if (in[0] & 0x80) a = -a; *pos += size; return a; } static int get_objtype (fshandle_t *f, int32_t ofs, int type) { char sig[16]; _retry: memset(sig, 0, sizeof(sig)); FS_fseek(f, ofs, SEEK_SET); FS_fread(sig, 16, 1, f); if (type == UMUSIC_IT) { if (memcmp(sig, "IMPM", 4) == 0) return UMUSIC_IT; return -1; } if (type == UMUSIC_XM) { if (memcmp(sig, "Extended Module:", 16) != 0) return -1; FS_fread(sig, 16, 1, f); if (sig[0] != ' ') return -1; FS_fread(sig, 16, 1, f); if (sig[5] != 0x1a) return -1; return UMUSIC_XM; } if (type == UMUSIC_MP2) { unsigned char *p = (unsigned char *)sig; uint16_t u = ((p[0] << 8) | p[1]) & 0xFFFE; if (u == 0xFFFC || u == 0xFFF4) return UMUSIC_MP2; return -1; } if (type == UMUSIC_WAV) { if (memcmp(sig, "RIFF", 4) == 0 && memcmp(&sig[8], "WAVE", 4) == 0) return UMUSIC_WAV; return -1; } FS_fseek(f, ofs + 44, SEEK_SET); FS_fread(sig, 4, 1, f); if (type == UMUSIC_S3M) { if (memcmp(sig, "SCRM", 4) == 0) return UMUSIC_S3M; /*return -1;*/ /* SpaceMarines.umx and Starseek.umx from Return to NaPali * report as "s3m" whereas the actual music format is "it" */ type = UMUSIC_IT; goto _retry; } FS_fseek(f, ofs + 1080, SEEK_SET); FS_fread(sig, 4, 1, f); if (type == UMUSIC_MOD) { if (memcmp(sig, "M.K.", 4) == 0 || memcmp(sig, "M!K!", 4) == 0) return UMUSIC_MOD; return -1; } return -1; } static int read_export (fshandle_t *f, const struct upkg_hdr *hdr, int32_t *ofs, int32_t *objsize) { char buf[40]; int idx = 0, t; FS_fseek(f, *ofs, SEEK_SET); if (FS_fread(buf, 4, 10, f) < 10) return -1; if (hdr->file_version < 40) idx += 8; /* 00 00 00 00 00 00 00 00 */ if (hdr->file_version < 60) idx += 16; /* 81 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 00 */ get_fci(&buf[idx], &idx); /* skip junk */ t = get_fci(&buf[idx], &idx); /* type_name */ if (hdr->file_version > 61) idx += 4; /* skip export size */ *objsize = get_fci(&buf[idx], &idx); *ofs += idx; /* offset for real data */ return t; /* return type_name index */ } static int read_typname(fshandle_t *f, const struct upkg_hdr *hdr, int idx, char *out) { int i, s; long l; char buf[64]; if (idx >= hdr->name_count) return -1; memset(buf, 0, 64); for (i = 0, l = 0; i <= idx; i++) { if (FS_fseek(f, hdr->name_offset + l, SEEK_SET) < 0) return -1; if (!FS_fread(buf, 1, 63, f)) return -1; if (hdr->file_version >= 64) { s = *(signed char *)buf; /* numchars *including* terminator */ if (s <= 0) return -1; l += s + 5; /* 1 for buf[0], 4 for int32_t name_flags */ } else { l += (long)strlen(buf); l += 5; /* 1 for terminator, 4 for int32_t name_flags */ } } strcpy(out, (hdr->file_version >= 64)? &buf[1] : buf); return 0; } static int probe_umx (fshandle_t *f, const struct upkg_hdr *hdr, int32_t *ofs, int32_t *objsize) { int i, idx, t; int32_t s, pos; long fsiz; char buf[64]; idx = 0; fsiz = FS_filelength (f); if (hdr->name_offset >= fsiz || hdr->export_offset >= fsiz || hdr->import_offset >= fsiz) { Con_DPrintf("Illegal values in header.\n"); return -1; } /* Find the offset and size of the first IT, S3M or XM * by parsing the exports table. The umx files should * have only one export. Kran32.umx from Unreal has two, * but both pointing to the same music. */ if (hdr->export_offset >= fsiz) return -1; memset(buf, 0, 64); FS_fseek(f, hdr->export_offset, SEEK_SET); FS_fread(buf, 1, 64, f); get_fci(&buf[idx], &idx); /* skip class_index */ get_fci(&buf[idx], &idx); /* skip super_index */ if (hdr->file_version >= 60) idx += 4; /* skip int32 package_index */ get_fci(&buf[idx], &idx); /* skip object_name */ idx += 4; /* skip int32 object_flags */ s = get_fci(&buf[idx], &idx); /* get serial_size */ if (s <= 0) return -1; pos = get_fci(&buf[idx],&idx); /* get serial_offset */ if (pos < 0 || pos > fsiz - 40) return -1; if ((t = read_export(f, hdr, &pos, &s)) < 0) return -1; if (s <= 0 || s > fsiz - pos) return -1; if (read_typname(f, hdr, t, buf) < 0) return -1; for (i = 0; mustype[i] != NULL; i++) { if (!q_strcasecmp(buf, mustype[i])) { t = i; break; } } if (mustype[i] == NULL) return -1; if ((t = get_objtype(f, pos, t)) < 0) return -1; *ofs = pos; *objsize = s; return t; } static int32_t probe_header (fshandle_t *f, struct upkg_hdr *hdr) { if (FS_fread(hdr, 1, UPKG_HDR_SIZE, f) < UPKG_HDR_SIZE) return -1; /* byte swap the header - all members are 32 bit LE values */ hdr->tag = (uint32_t) LittleLong(hdr->tag); hdr->file_version = LittleLong(hdr->file_version); hdr->pkg_flags = (uint32_t) LittleLong(hdr->pkg_flags); hdr->name_count = LittleLong(hdr->name_count); hdr->name_offset = LittleLong(hdr->name_offset); hdr->export_count = LittleLong(hdr->export_count); hdr->export_offset = LittleLong(hdr->export_offset); hdr->import_count = LittleLong(hdr->import_count); hdr->import_offset = LittleLong(hdr->import_offset); if (hdr->tag != UPKG_HDR_TAG) { Con_DPrintf("Unknown header tag 0x%x\n", hdr->tag); return -1; } if (hdr->name_count < 0 || hdr->export_count < 0 || hdr->import_count < 0 || hdr->name_offset < 36 || hdr->export_offset < 36 || hdr->import_offset < 36) { Con_DPrintf("Illegal values in header.\n"); return -1; } #if 1 /* no need being overzealous */ return 0; #else switch (hdr->file_version) { case 35: case 37: /* Unreal beta - */ case 40: case 41: /* 1998 */ case 61:/* Unreal */ case 62:/* Unreal Tournament */ case 63:/* Return to NaPali */ case 64:/* Unreal Tournament */ case 66:/* Unreal Tournament */ case 68:/* Unreal Tournament */ case 69:/* Tactical Ops */ case 75:/* Harry Potter and the Philosopher's Stone */ case 76: /* mpeg layer II data */ case 83:/* Mobile Forces */ return 0; } Con_DPrintf("Unknown upkg version %d\n", hdr->file_version); return -1; #endif /* #if 0 */ } static int process_upkg (fshandle_t *f, int32_t *ofs, int32_t *objsize) { struct upkg_hdr header; memset(&header, 0, sizeof(header)); if (probe_header(f, &header) < 0) return -1; return probe_umx(f, &header, ofs, objsize); } static qboolean S_UMX_CodecInitialize (void) { return true; } static void S_UMX_CodecShutdown (void) { } static qboolean S_UMX_CodecOpenStream (snd_stream_t *stream) { int type; int32_t ofs = 0, size = 0; type = process_upkg(&stream->fh, &ofs, &size); if (type < 0) { Con_DPrintf("%s: unrecognized umx\n", stream->name); return false; } Con_DPrintf("%s: %s data @ 0x%x, %d bytes\n", stream->name, mustype[type], ofs, size); /* hack the fshandle_t start pos and length members so * that only the relevant data is accessed from now on */ stream->fh.start += ofs; stream->fh.length = size; FS_fseek(&stream->fh, 0, SEEK_SET); switch (type) { case UMUSIC_IT: case UMUSIC_S3M: case UMUSIC_XM: case UMUSIC_MOD: return S_CodecForwardStream(stream, CODECTYPE_MOD); case UMUSIC_WAV: return S_CodecForwardStream(stream, CODECTYPE_WAV); case UMUSIC_MP2: return S_CodecForwardStream(stream, CODECTYPE_MP3); } return false; } static int S_UMX_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { return -1; } static void S_UMX_CodecCloseStream (snd_stream_t *stream) { S_CodecUtilClose(&stream); } static int S_UMX_CodecRewindStream (snd_stream_t *stream) { return -1; } snd_codec_t umx_codec = { CODECTYPE_UMX, true, /* always available. */ "umx", S_UMX_CodecInitialize, S_UMX_CodecShutdown, S_UMX_CodecOpenStream, S_UMX_CodecReadStream, S_UMX_CodecRewindStream, NULL, /* jump */ S_UMX_CodecCloseStream, NULL }; #endif /* USE_CODEC_UMX */ engine/h2shared/snd_umx.h000066400000000000000000000003021444734033100156470ustar00rootroot00000000000000/* Unreal UMX format support */ #if !defined(_SND_UMX_H_) #define _SND_UMX_H_ #if defined(USE_CODEC_UMX) extern snd_codec_t umx_codec; #endif /* USE_CODEC_UMX */ #endif /* ! _SND_UMX_H_ */ engine/h2shared/snd_vorbis.c000066400000000000000000000116771444734033100163560ustar00rootroot00000000000000/* * Ogg/Vorbis streaming music support, loosely based on several open source * Quake engine based projects with many modifications. * * Copyright (C) 2010-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" #if defined(USE_CODEC_VORBIS) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_vorbis.h" #define OV_EXCLUDE_STATIC_CALLBACKS #if defined(VORBIS_USE_TREMOR) /* for Tremor / Vorbisfile api differences, * see doc/diff.html in the Tremor package. */ #include #else #include #endif /* Vorbis codec can return the samples in a number of different * formats, we use the standard signed short format. */ #define VORBIS_SAMPLEBITS 16 #define VORBIS_SAMPLEWIDTH 2 #define VORBIS_SIGNED_DATA 1 /* CALLBACK FUNCTIONS: */ static int ovc_fclose (void *f) { return 0; /* we fclose() elsewhere. */ } static int ovc_fseek (void *f, ogg_int64_t off, int whence) { if (f == NULL) return (-1); return FS_fseek((fshandle_t *)f, (long) off, whence); } static ov_callbacks ovc_qfs = { (size_t (*)(void *, size_t, size_t, void *)) FS_fread, (int (*)(void *, ogg_int64_t, int)) ovc_fseek, (int (*)(void *)) ovc_fclose, (long (*)(void *)) FS_ftell }; static qboolean S_VORBIS_CodecInitialize (void) { return true; } static void S_VORBIS_CodecShutdown (void) { } static qboolean S_VORBIS_CodecOpenStream (snd_stream_t *stream) { OggVorbis_File *ovFile; vorbis_info *ovf_info; long numstreams; int res; ovFile = (OggVorbis_File *) Z_Malloc(sizeof(OggVorbis_File), Z_MAINZONE); stream->priv = ovFile; res = ov_open_callbacks(&stream->fh, ovFile, NULL, 0, ovc_qfs); if (res != 0) { Con_Printf("%s is not a valid Ogg Vorbis file (error %i).\n", stream->name, res); goto _fail; } if (!ov_seekable(ovFile)) { Con_Printf("Stream %s not seekable.\n", stream->name); goto _fail; } ovf_info = ov_info(ovFile, 0); if (!ovf_info) { Con_Printf("Unable to get stream info for %s.\n", stream->name); goto _fail; } /* FIXME: handle section changes */ numstreams = ov_streams(ovFile); if (numstreams != 1) { Con_Printf("More than one (%ld) stream in %s.\n", numstreams, stream->name); goto _fail; } if (ovf_info->channels != 1 && ovf_info->channels != 2) { Con_Printf("Unsupported number of channels %d in %s\n", ovf_info->channels, stream->name); goto _fail; } stream->info.rate = ovf_info->rate; stream->info.channels = ovf_info->channels; stream->info.bits = VORBIS_SAMPLEBITS; stream->info.width = VORBIS_SAMPLEWIDTH; return true; _fail: if (res == 0) ov_clear(ovFile); Z_Free(ovFile); return false; } static int S_VORBIS_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { int section; /* FIXME: handle section changes */ int cnt, res, rem; char * ptr; cnt = 0; rem = bytes; ptr = (char *) buffer; while (1) { /* # ov_read() from libvorbisfile returns the decoded PCM audio * in requested endianness, signedness and word size. * # ov_read() from Tremor (libvorbisidec) returns decoded audio * always in host-endian, signed 16 bit PCM format. * # For both of the libraries, if the audio is multichannel, * the channels are interleaved in the output buffer. */ res = ov_read( (OggVorbis_File *)stream->priv, ptr, rem, #ifndef VORBIS_USE_TREMOR host_bigendian, VORBIS_SAMPLEWIDTH, VORBIS_SIGNED_DATA, #endif §ion ); if (res <= 0) break; rem -= res; cnt += res; if (rem <= 0) break; ptr += res; } if (res < 0) return res; return cnt; } static void S_VORBIS_CodecCloseStream (snd_stream_t *stream) { ov_clear((OggVorbis_File *)stream->priv); Z_Free(stream->priv); S_CodecUtilClose(&stream); } static int S_VORBIS_CodecRewindStream (snd_stream_t *stream) { /* for libvorbisfile, the ov_time_seek() position argument * is seconds as doubles, whereas for Tremor libvorbisidec * it is milliseconds as 64 bit integers. */ return ov_time_seek ((OggVorbis_File *)stream->priv, 0); } snd_codec_t vorbis_codec = { CODECTYPE_VORBIS, true, /* always available. */ "ogg", S_VORBIS_CodecInitialize, S_VORBIS_CodecShutdown, S_VORBIS_CodecOpenStream, S_VORBIS_CodecReadStream, S_VORBIS_CodecRewindStream, NULL, /* jump */ S_VORBIS_CodecCloseStream, NULL }; #endif /* USE_CODEC_VORBIS */ engine/h2shared/snd_vorbis.h000066400000000000000000000003411444734033100163450ustar00rootroot00000000000000/* Ogg/Vorbis streaming music support. */ #if !defined(_SND_VORBIS_H_) #define _SND_VORBIS_H_ 1 #if defined(USE_CODEC_VORBIS) extern snd_codec_t vorbis_codec; #endif /* USE_CODEC_VORBIS */ #endif /* ! _SND_VORBIS_H_ */ engine/h2shared/snd_wave.c000066400000000000000000000130301444734033100157750ustar00rootroot00000000000000/* * WAV streaming music support. Adapted from ioquake3 with changes. * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2005 Stuart Dalton * Copyright (C) 2010-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" #if defined(USE_CODEC_WAVE) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_wave.h" /* ================= FGetLittleLong ================= */ static int FGetLittleLong (FILE *f) { int v; fread(&v, 1, sizeof(v), f); return LittleLong(v); } /* ================= FGetLittleShort ================= */ static short FGetLittleShort(FILE *f) { short v; fread(&v, 1, sizeof(v), f); return LittleShort(v); } /* ================= WAV_ReadChunkInfo ================= */ static int WAV_ReadChunkInfo(FILE *f, char *name) { int len, r; name[4] = 0; r = fread(name, 1, 4, f); if (r != 4) return -1; len = FGetLittleLong(f); if (len < 0) { Con_Printf("WAV: Negative chunk length\n"); return -1; } return len; } /* ================= WAV_FindRIFFChunk Returns the length of the data in the chunk, or -1 if not found ================= */ static int WAV_FindRIFFChunk(FILE *f, const char *chunk) { char name[5]; int len; while ((len = WAV_ReadChunkInfo(f, name)) >= 0) { /* If this is the right chunk, return */ if (!strncmp(name, chunk, 4)) return len; len = ((len + 1) & ~1); /* pad by 2 . */ /* Not the right chunk - skip it */ fseek(f, len, SEEK_CUR); } return -1; } /* ================= WAV_ReadRIFFHeader ================= */ static qboolean WAV_ReadRIFFHeader(const char *name, FILE *file, snd_info_t *info) { char dump[16]; int wav_format; int fmtlen = 0; if (fread(dump, 1, 12, file) < 12 || strncmp(dump, "RIFF", 4) != 0 || strncmp(&dump[8], "WAVE", 4) != 0) { Con_Printf("%s is missing RIFF/WAVE chunks\n", name); return false; } /* Scan for the format chunk */ if ((fmtlen = WAV_FindRIFFChunk(file, "fmt ")) < 0) { Con_Printf("%s is missing fmt chunk\n", name); return false; } /* Save the parameters */ wav_format = FGetLittleShort(file); if (wav_format != WAV_FORMAT_PCM) { Con_Printf("%s is not Microsoft PCM format\n", name); return false; } info->channels = FGetLittleShort(file); info->rate = FGetLittleLong(file); FGetLittleLong(file); FGetLittleShort(file); info->bits = FGetLittleShort(file); if (info->bits != 8 && info->bits != 16) { Con_Printf("%s is not 8 or 16 bit\n", name); return false; } info->width = info->bits / 8; info->dataofs = 0; /* Skip the rest of the format chunk if required */ if (fmtlen > 16) { fmtlen -= 16; fseek(file, fmtlen, SEEK_CUR); } /* Scan for the data chunk */ if ((info->size = WAV_FindRIFFChunk(file, "data")) < 0) { Con_Printf("%s is missing data chunk\n", name); return false; } if (info->channels != 1 && info->channels != 2) { Con_Printf("Unsupported number of channels %d in %s\n", info->channels, name); return false; } info->samples = (info->size / info->width) / info->channels; if (info->samples == 0) { Con_Printf("%s has zero samples\n", name); return false; } return true; } /* ================= S_WAV_CodecOpenStream ================= */ static qboolean S_WAV_CodecOpenStream(snd_stream_t *stream) { long start = stream->fh.start; /* Read the RIFF header */ /* The file reads are sequential, therefore no need * for the FS_*() functions: We will manipulate the * file by ourselves from now on. */ if (!WAV_ReadRIFFHeader(stream->name, stream->fh.file, &stream->info)) return false; stream->fh.start = ftell(stream->fh.file); /* reset to data position */ if (stream->fh.start - start + stream->info.size > stream->fh.length) { Con_Printf("%s data size mismatch\n", stream->name); return false; } return true; } /* ================= S_WAV_CodecReadStream ================= */ int S_WAV_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer) { int remaining = stream->info.size - stream->fh.pos; int i, samples; if (remaining <= 0) return 0; if (bytes > remaining) bytes = remaining; stream->fh.pos += bytes; fread(buffer, 1, bytes, stream->fh.file); if (stream->info.width == 2) { samples = bytes / 2; for (i = 0; i < samples; i++) ((short *)buffer)[i] = LittleShort( ((short *)buffer)[i] ); } return bytes; } static void S_WAV_CodecCloseStream (snd_stream_t *stream) { S_CodecUtilClose(&stream); } static int S_WAV_CodecRewindStream (snd_stream_t *stream) { FS_rewind(&stream->fh); return 0; } static qboolean S_WAV_CodecInitialize (void) { return true; } static void S_WAV_CodecShutdown (void) { } snd_codec_t wav_codec = { CODECTYPE_WAVE, true, /* always available. */ "wav", S_WAV_CodecInitialize, S_WAV_CodecShutdown, S_WAV_CodecOpenStream, S_WAV_CodecReadStream, S_WAV_CodecRewindStream, NULL, /* jump */ S_WAV_CodecCloseStream, NULL }; #endif /* USE_CODEC_WAVE */ engine/h2shared/snd_wave.h000066400000000000000000000003131444734033100160020ustar00rootroot00000000000000/* WAV streaming music support. */ #if !defined(_SND_WAVE_H_) #define _SND_WAVE_H_ #if defined(USE_CODEC_WAVE) extern snd_codec_t wav_codec; #endif /* USE_CODEC_WAVE */ #endif /* ! _SND_WAVE_H_ */ engine/h2shared/snd_wildmidi.c000066400000000000000000000174171444734033100166520ustar00rootroot00000000000000/* MIDI streaming music support using WildMIDI library. * wildmidi at least v0.2.3.x is required at both compile and runtime: * Latest stable v0.3.14 (as of this writing) is highly recommended: * - wildmidi-0.2.2 has a horrific mistake of freeing the buffer that * you pass with WildMidi_OpenBuffer() when you do WildMidi_Close(). * - wildmidi-0.2.3.x-0.3.x had a regression, resulting in perversely * high amount of heap usage (up to about 500mb) because of crazily * repetitive malloc/free calls; fixed as of 0.3.5. * - wildmidi-0.2.x-0.3.x had a seek-to-0 bug, which might result in * truncated start issues with some midis; fixed as of 0.3.8. * - wildmidi-0.2.x-0.3.x had a 'source' directive config parsing bug; * fixed as of 0.3.12. * - wildmidi-0.2.x-0.3.x had security holes, fixed as of 0.3.14. * - the new wildmidi-0.4.x has some api changes against 0.2.3/0.3.x. * our client is adjusted for them, see LIBWILDMIDI_VERSION ifdefs * below. * * Copyright (C) 2010-2015 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(USE_CODEC_WILDMIDI) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_wildmidi.h" #include #include "filenames.h" #if !defined(LIBWILDMIDI_VERSION) || (LIBWILDMIDI_VERSION-0 < 0x000400L) #if !defined(WM_MO_ENHANCED_RESAMPLING) #error wildmidi version 0.2.3.4 or newer is required. #endif #endif #define CACHEBUFFER_SIZE 4096 typedef struct _midi_buf_t { midi *song; #if defined(LIBWILDMIDI_VERSION) && (LIBWILDMIDI_VERSION-0 >= 0x000400L) int8_t midi_buffer[CACHEBUFFER_SIZE]; #else char midi_buffer[CACHEBUFFER_SIZE]; #endif int pos, last; } midi_buf_t; static unsigned short wildmidi_rate; static unsigned short wildmidi_opts; static const char *cfgfile[] = { #if defined(__DJGPP__) /* prefer '/' instead of '\\' */ "C:/TIMIDITY", #elif defined(PLATFORM_DOS) || defined(PLATFORM_WINDOWS) || defined(PLATFORM_OS2) "C:\\TIMIDITY", #elif defined(__MORPHOS__) "LIBS:GerontoPlayer", #elif defined(__AROS__) "Timidity:",/* disable the system requester for this (see below) */ #elif defined(PLATFORM_AMIGA) /**/ #else /* unix, osx, riscos, ... */ "/etc", "/etc/wildmidi", "/etc/timidity", "/usr/share/timidity", "/usr/local/share/wildmidi", "/usr/local/share/timidity", "/usr/local/lib/timidity", #endif NULL /* last entry must be NULL */ }; static int WILDMIDI_InitHelper (const char *cfgdir) { char path[MAX_OSPATH]; int len; len = q_strlcpy(path, cfgdir, sizeof(path)); if (len >= (int)sizeof(path) - 1) return -1; if (len && !IS_DIR_SEPARATOR(path[len - 1])) path[len++] = DIR_SEPARATOR_CHAR; path[len] = '\0'; q_strlcat(path, "wildmidi.cfg", sizeof(path)); Con_DPrintf("WildMIDI: trying %s\n", path); #ifdef PLATFORM_AMIGA if(Sys_PathExistsQuiet(path)) #endif if (WildMidi_Init(path, wildmidi_rate, wildmidi_opts) == 0) return 0; path[len] = '\0'; q_strlcat(path, "timidity.cfg", sizeof(path)); Con_DPrintf("WildMIDI: trying %s\n", path); #ifdef PLATFORM_AMIGA if (!Sys_PathExistsQuiet(path)) return -1; #endif return WildMidi_Init(path, wildmidi_rate, wildmidi_opts); } static qboolean S_WILDMIDI_CodecInitialize (void) { const char *timi_env; int i, err; if (wildmidi_codec.initialized) return true; wildmidi_opts = 0;/* WM_MO_ENHANCED_RESAMPLING is a cpu hog: no need. */ if (shm->speed < 11025) wildmidi_rate = 11025; else if (shm->speed > 48000) wildmidi_rate = 44100; else wildmidi_rate = shm->speed; err = -1; timi_env = getenv("WILDMIDI_CFG"); if (timi_env == NULL) timi_env = getenv("TIMIDITY_CFG"); if (timi_env) { Con_DPrintf("WildMIDI: trying %s\n", timi_env); /* env is an override: if it fails, we * don't bother trying anything else. */ err = WildMidi_Init(timi_env, wildmidi_rate, wildmidi_opts); goto _finish; } #if DO_USERDIRS /* check under the user's directory first: */ err = WILDMIDI_InitHelper(FS_GetUserbase()); #endif /* then, check under the installation dir: */ if (err != 0) err = WILDMIDI_InitHelper(FS_GetBasedir()); /* lastly, check with the system locations: */ for (i = 0; err != 0 && cfgfile[i] != NULL; ++i) err = WILDMIDI_InitHelper(cfgfile[i]); _finish: if (err != 0) { Con_Printf ("Could not initialize WildMIDI\n"); return false; } Con_Printf ("WildMIDI initialized\n"); wildmidi_codec.initialized = true; return true; } static void S_WILDMIDI_CodecShutdown (void) { if (!wildmidi_codec.initialized) return; wildmidi_codec.initialized = false; Con_Printf("Shutting down WildMIDI.\n"); WildMidi_Shutdown(); } static qboolean S_WILDMIDI_CodecOpenStream (snd_stream_t *stream) { midi_buf_t *data; unsigned char *temp; long len; int mark; if (!wildmidi_codec.initialized) return false; len = FS_filelength(&stream->fh); mark = Hunk_LowMark(); temp = (unsigned char *) Hunk_Alloc(len); FS_fread(temp, 1, len, &stream->fh); data = (midi_buf_t *) Z_Malloc(sizeof(midi_buf_t), Z_MAINZONE); data->song = WildMidi_OpenBuffer (temp, len); Hunk_FreeToLowMark(mark); /* free original file data */ if (data->song == NULL) { Con_Printf ("%s is not a valid MIDI file\n", stream->name); Z_Free(data); return false; } stream->info.rate = wildmidi_rate; stream->info.width = 2; /* WildMIDI does 16 bit signed */ stream->info.bits = 16; stream->info.channels = 2; /* WildMIDI does stereo */ stream->priv = data; WildMidi_MasterVolume (100); return true; } #if !defined(LIBWILDMIDI_VERSION) || (LIBWILDMIDI_VERSION-0 < 0x000400L) static inline void S_WILDMIDI_ConvertSamples (char *dat, int samples) { /* libWildMidi-0.2.x/0.3.x return little-endian samples. */ short *swp = (short *) dat; int i = 0; for (; i < samples; i++) swp[i] = LittleShort(swp[i]); } #else /* libWildMidi >= 0.4.x */ static inline void S_WILDMIDI_ConvertSamples (int8_t *dat, int samples) { /* libWildMidi >= 0.4.x returns host-endian samples: no swap. */ } #endif /* LIBWILDMIDI_VERSION */ static int S_WILDMIDI_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { midi_buf_t *data = (midi_buf_t *) stream->priv; if (data->pos == 0) { data->last = WildMidi_GetOutput (data->song, data->midi_buffer, CACHEBUFFER_SIZE); if (data->last == 0) return 0; if (bytes > data->last) bytes = data->last; } else if (data->pos + bytes > data->last) { bytes = data->last - data->pos; } S_WILDMIDI_ConvertSamples (& data->midi_buffer[data->pos], bytes / 2); memcpy (buffer, & data->midi_buffer[data->pos], bytes); data->pos += bytes; if (data->pos == data->last) data->pos = 0; return bytes; } static void S_WILDMIDI_CodecCloseStream (snd_stream_t *stream) { WildMidi_Close (((midi_buf_t *)stream->priv)->song); Z_Free(stream->priv); S_CodecUtilClose(&stream); } static int S_WILDMIDI_CodecRewindStream (snd_stream_t *stream) { unsigned long pos = 0; return WildMidi_FastSeek (((midi_buf_t *)stream->priv)->song, &pos); } snd_codec_t wildmidi_codec = { CODECTYPE_MIDI, false, "mid", S_WILDMIDI_CodecInitialize, S_WILDMIDI_CodecShutdown, S_WILDMIDI_CodecOpenStream, S_WILDMIDI_CodecReadStream, S_WILDMIDI_CodecRewindStream, NULL, /* jump */ S_WILDMIDI_CodecCloseStream, NULL }; #endif /* USE_CODEC_WILDMIDI */ engine/h2shared/snd_wildmidi.h000066400000000000000000000003641444734033100166500ustar00rootroot00000000000000/* MIDI streaming music support using WildMIDI. */ #if !defined(_SND_WILDMIDI_H_) #define _SND_WILDMIDI_H_ #if defined(USE_CODEC_WILDMIDI) extern snd_codec_t wildmidi_codec; #endif /* USE_CODEC_WILDMIDI */ #endif /* ! _SND_WILDMIDI_H_ */ engine/h2shared/snd_win.c000066400000000000000000000200031444734033100156260ustar00rootroot00000000000000/* snd_win.c * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "snd_sys.h" #if HAVE_WIN_SOUND #include "winquake.h" #include "snd_win.h" #include static char s_wv_driver[] = "Windows WAVE"; //#define SNDBUFSIZE 65536 // 64K is > 1 second at 16-bit, 22050 Hz //#define WAV_BUFFERS 64 #define WAV_BUFFERS 128 #define WAV_MASK (WAV_BUFFERS - 1) static int sample16; static int snd_sent, snd_completed; static int wv_buf_size; /* whether to use hunk for wave sound memory, either 1 or 0 */ #define USE_HUNK_ALLOC 0 #if USE_HUNK_ALLOC static int allocMark = 0; #else static HANDLE hData; static HGLOBAL hWaveHdr; #endif static HPSTR lpData; static LPWAVEHDR lpWaveHdr; static HWAVEOUT hWaveOut; //WAVEOUTCAPS wavecaps; static DWORD gSndBufSize; /* ================== FreeSound ================== */ static void FreeSound (void) { int i; if (hWaveOut) { waveOutReset (hWaveOut); if (lpWaveHdr) { for (i = 0; i < WAV_BUFFERS; i++) waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)); } waveOutClose (hWaveOut); #if USE_HUNK_ALLOC /* These are now on the hunk and we have to be wary about deallocating them: Other stuff might have been allocated above. A nonzero allocMark is the case only if wave init failed, and we are here immediately after that, in which case it's safe to free whatever we allocated. In any other case, allocMark will be 0 and no action is performed. Pa3PyX */ if (allocMark) { Hunk_FreeToLowMark(allocMark); allocMark = 0; } #else if (hWaveHdr) { GlobalUnlock(hWaveHdr); GlobalFree(hWaveHdr); } if (hData) { GlobalUnlock(hData); GlobalFree(hData); } #endif } hWaveOut = 0; #if !USE_HUNK_ALLOC hData = 0; hWaveHdr = 0; #endif lpData = NULL; lpWaveHdr = NULL; } /* ================== SNDDM_InitWav Crappy windows multimedia base ================== */ static qboolean S_WIN_Init (dma_t *dma) { WAVEFORMATEX format; int i; HRESULT hr; snd_sent = 0; snd_completed = 0; memset((void *) dma, 0, sizeof(dma_t)); shm = dma; shm->channels = desired_channels; shm->samplebits = desired_bits; shm->speed = desired_speed; /* Calculate Wave buffer size to store 2 secs * of data, round up to the next power of 2. */ wv_buf_size = 1 << (Q_log2((desired_speed << 3) / WAV_BUFFERS) + 1); memset (&format, 0, sizeof(format)); format.wFormatTag = WAVE_FORMAT_PCM; format.nChannels = shm->channels; format.wBitsPerSample = shm->samplebits; format.nSamplesPerSec = shm->speed; format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; format.cbSize = 0; format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; /* Open a waveform device for output using window callback. */ hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, &format, 0, 0L, CALLBACK_NULL); if (hr != MMSYSERR_NOERROR) { if (hr != MMSYSERR_ALLOCATED) { Con_SafePrintf ("waveOutOpen failed\n"); return false; } Con_SafePrintf ("waveOutOpen failure, hardware already in use\n"); return false; } /* * Allocate and lock memory for the waveform data. The memory * for waveform data must be globally allocated with * GMEM_MOVEABLE and GMEM_SHARE flags. */ gSndBufSize = WAV_BUFFERS * wv_buf_size; #if USE_HUNK_ALLOC allocMark = Hunk_LowMark(); lpData = (HPSTR) Hunk_AllocName(gSndBufSize, "sndbuff"); #else hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize); if (!hData) { Con_SafePrintf ("Sound: Out of memory.\n"); goto fail; } lpData = (HPSTR) GlobalLock(hData); if (!lpData) { Con_SafePrintf ("Sound: Failed to lock.\n"); goto fail; } memset (lpData, 0, gSndBufSize); #endif /* * Allocate and lock memory for the header. This memory must * also be globally allocated with GMEM_MOVEABLE and * GMEM_SHARE flags. */ #if USE_HUNK_ALLOC lpWaveHdr = (LPWAVEHDR) Hunk_AllocName((DWORD)sizeof(WAVEHDR) * WAV_BUFFERS, "wavehdr"); #else hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS); if (hWaveHdr == NULL) { Con_SafePrintf ("Sound: Failed to Alloc header.\n"); goto fail; } lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr); if (lpWaveHdr == NULL) { Con_SafePrintf ("Sound: Failed to lock header.\n"); goto fail; } memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS); #endif /* After allocation, set up and prepare headers. */ for (i = 0; i < WAV_BUFFERS; i++) { lpWaveHdr[i].dwBufferLength = wv_buf_size; lpWaveHdr[i].lpData = lpData + i * wv_buf_size; if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) != MMSYSERR_NOERROR) { Con_SafePrintf ("Sound: failed to prepare wave headers\n"); goto fail; } } shm->samples = gSndBufSize / (shm->samplebits / 8); shm->samplepos = 0; shm->submission_chunk = 1; shm->buffer = (unsigned char *) lpData; sample16 = (shm->samplebits / 8) - 1; #if USE_HUNK_ALLOC /* Wave init succeeded, so DO NOT attempt to deallocate sound buffers from the hunk later on, otherwise we risk trashing everything that was allocated after them. Pa3PyX */ allocMark = 0; #endif Con_SafePrintf ("%d sound buffers, %d bytes/sound buffer\n", WAV_BUFFERS, wv_buf_size); Con_SafePrintf ("Wave sound initialized\n"); return true; fail: FreeSound (); Con_SafePrintf ("Wave sound failed to init\n"); return false; } /* ============== SNDDMA_GetDMAPos return the current sample position (in mono samples read) inside the recirculating dma buffer, so the mixing code will know how many sample are required to fill it up. =============== */ static int S_WIN_GetDMAPos (void) { int s; s = snd_sent * wv_buf_size; s >>= sample16; s &= (shm->samples - 1); return s; } /* ============== SNDDMA_LockBuffer Makes sure dma buffer is valid =============== */ static void S_WIN_LockBuffer (void) { } /* ============== SNDDMA_Submit Unlock the dma buffer / Send sound to the device =============== */ static void S_WIN_Submit (void) { LPWAVEHDR h; int wResult; // // find which sound blocks have completed // while (1) { if ( snd_completed == snd_sent ) { Con_DPrintf ("Sound overrun\n"); break; } if ( ! (lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE) ) { break; } snd_completed++; // this buffer has been played } // // submit two new sound blocks // while (((snd_sent - snd_completed) >> sample16) < 4) { h = lpWaveHdr + (snd_sent & WAV_MASK); snd_sent++; /* * Now the data block can be sent to the output device. The * waveOutWrite function returns immediately and waveform * data is sent to the output device in the background. */ wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR)); if (wResult != MMSYSERR_NOERROR) { Con_SafePrintf ("Failed to write block to device\n"); FreeSound (); return; } } } /* ================== SNDDMA_BlockSound ================== */ static void S_WIN_BlockSound (void) { waveOutReset (hWaveOut); } /* ================== SNDDMA_UnblockSound ================== */ static void S_WIN_UnblockSound (void) { } /* ============== SNDDMA_Shutdown Reset the sound device for exiting =============== */ static void S_WIN_Shutdown (void) { FreeSound (); } snd_driver_t snddrv_win = { S_WIN_Init, S_WIN_Shutdown, S_WIN_GetDMAPos, S_WIN_LockBuffer, S_WIN_Submit, S_WIN_BlockSound, S_WIN_UnblockSound, s_wv_driver, SNDDRV_ID_WIN, false, NULL }; #endif /* HAVE_WIN_SOUND */ engine/h2shared/snd_win.h000066400000000000000000000003231444734033100156360ustar00rootroot00000000000000/* Sound support using Windows multimedia api */ #if !defined(__HX2_SND_WIN) #define __HX2_SND_WIN #if HAVE_WIN_SOUND extern snd_driver_t snddrv_win; #endif /* HAVE_WIN_SOUND */ #endif /* __HX2_SND_WIN */ engine/h2shared/snd_xmp.c000066400000000000000000000120211444734033100156360ustar00rootroot00000000000000/* tracker music (module file) decoding support using libxmp >= v4.2.0 * https://sourceforge.net/projects/xmp/ * https://github.com/libxmp/libxmp.git * * Copyright (C) 2016 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(USE_CODEC_XMP) #include "snd_codec.h" #include "snd_codeci.h" #include "snd_xmp.h" #if defined(_WIN32) && defined(LIBXMP_STATIC) #define BUILDING_STATIC /* for old versions. */ #endif #include #if ((XMP_VERCODE+0) < 0x040200) #error libxmp version 4.2 or newer is required #endif static qboolean S_XMP_CodecInitialize (void) { return true; } static void S_XMP_CodecShutdown (void) { } #if (XMP_VERCODE >= 0x040500) static unsigned long xmp_fread(void *dest, unsigned long len, unsigned long nmemb, void *f) { return FS_fread(dest, len, nmemb, (fshandle_t *)f); } static int xmp_fseek(void *f, long offset, int whence) { return FS_fseek((fshandle_t *)f, offset, whence); } static long xmp_ftell(void *f) { return FS_ftell((fshandle_t *)f); } #endif static qboolean S_XMP_CodecOpenStream (snd_stream_t *stream) { /* need to load the whole file into memory and pass it to libxmp * using xmp_load_module_from_memory() which requires libxmp >= 4.2. * libxmp-4.0/4.1 only have xmp_load_module() which accepts a file * name which isn't good with files in containers like paks, etc. * On the other hand, libxmp >= 4.5 introduces file callbacks: use * if available. */ xmp_context c; #if (XMP_VERCODE >= 0x040500) struct xmp_callbacks file_callbacks = { xmp_fread, xmp_fseek, xmp_ftell, NULL }; #else byte *moddata; long len; int mark; #endif int fmt; c = xmp_create_context(); if (c == NULL) return false; #if (XMP_VERCODE >= 0x040500) if (xmp_load_module_from_callbacks(c, &stream->fh, file_callbacks) < 0) { Con_DPrintf("Could not load module %s\n", stream->name); goto err1; } #else len = FS_filelength (&stream->fh); mark = Hunk_LowMark(); moddata = (byte *) Hunk_Alloc(len); FS_fread(moddata, 1, len, &stream->fh); if (xmp_load_module_from_memory(c, moddata, len) < 0) { Hunk_FreeToLowMark(mark); Con_DPrintf("Could not load module %s\n", stream->name); goto err1; } Hunk_FreeToLowMark(mark); /* free original file data */ #endif stream->priv = c; if (shm->speed > XMP_MAX_SRATE) stream->info.rate = 44100; else if (shm->speed < XMP_MIN_SRATE) stream->info.rate = 11025; else stream->info.rate = shm->speed; stream->info.bits = shm->samplebits; stream->info.width = stream->info.bits / 8; stream->info.channels = shm->channels; fmt = 0; if (stream->info.channels == 1) fmt |= XMP_FORMAT_MONO; if (stream->info.width == 1) fmt |= XMP_FORMAT_8BIT|XMP_FORMAT_UNSIGNED; if (xmp_start_player(c, stream->info.rate, fmt) < 0) goto err2; /* interpolation type, default is XMP_INTERP_LINEAR */ xmp_set_player(c, XMP_PLAYER_INTERP, XMP_INTERP_SPLINE); return true; err2: xmp_release_module(c); err1: xmp_free_context(c); return false; } static int S_XMP_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer) { int r; /* xmp_play_buffer() requires libxmp >= 4.1. it will write * native-endian pcm data to the buffer. if the data write * is partial, the rest of the buffer will be zero-filled. * the last param is the max number that the current sequence * of song will be looped, or 0 to disable loop checking. */ r = xmp_play_buffer((xmp_context)stream->priv, buffer, bytes, !stream->loop); if (r == 0) { return bytes; } if (r == -XMP_END) { Con_DPrintf("XMP EOF\n"); return 0; } return -1; } static void S_XMP_CodecCloseStream (snd_stream_t *stream) { xmp_context c = (xmp_context)stream->priv; xmp_end_player(c); xmp_release_module(c); xmp_free_context(c); S_CodecUtilClose(&stream); } static int S_XMP_CodecJumpToOrder (snd_stream_t *stream, int to) { return xmp_set_position((xmp_context)stream->priv, to); } static int S_XMP_CodecRewindStream (snd_stream_t *stream) { int ret = xmp_seek_time((xmp_context)stream->priv, 0); if (ret < 0) return ret; xmp_play_buffer((xmp_context)stream->priv, NULL, 0, 0); /* reset internal state */ return 0; } snd_codec_t xmp_codec = { CODECTYPE_MOD, true, /* always available. */ "s3m", S_XMP_CodecInitialize, S_XMP_CodecShutdown, S_XMP_CodecOpenStream, S_XMP_CodecReadStream, S_XMP_CodecRewindStream, S_XMP_CodecJumpToOrder, S_XMP_CodecCloseStream, NULL }; #endif /* USE_CODEC_XMP */ engine/h2shared/snd_xmp.h000066400000000000000000000003251444734033100156470ustar00rootroot00000000000000/* module tracker decoding support using libxmp */ #if !defined(_SND_XMP_H_) #define _SND_XMP_H_ #if defined(USE_CODEC_XMP) extern snd_codec_t xmp_codec; #endif /* USE_CODEC_XMP */ #endif /* ! _SND_XMP_H_ */ engine/h2shared/surf16.asm000066400000000000000000000142541444734033100156640ustar00rootroot00000000000000; ; surf16.asm ; x86 assembly-language 16 bpp surface block drawing code. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix prowdestbase _sym_prefix pbasesource _sym_prefix lightright _sym_prefix lightrightstep _sym_prefix lightleft _sym_prefix lightleftstep _sym_prefix lightdeltastep _sym_prefix lightdelta _sym_prefix sourcetstep _sym_prefix surfrowbytes _sym_prefix colormap _sym_prefix blocksize _sym_prefix sourcesstep _sym_prefix blockdivshift _sym_prefix blockdivmask _sym_prefix r_lightptr _sym_prefix r_lightwidth _sym_prefix r_numvblocks _sym_prefix r_sourcemax _sym_prefix r_stepback ; C-shared globals: _sym_prefix R_Surf16Start _sym_prefix R_DrawSurfaceBlock16 _sym_prefix R_Surf16End _sym_prefix R_Surf16Patch %endif ; _sym_prefix ; externs from C code extern prowdestbase extern pbasesource extern lightright extern lightrightstep extern lightleft extern lightleftstep extern lightdeltastep extern lightdelta extern sourcetstep extern surfrowbytes extern colormap extern blocksize extern sourcesstep extern blockdivshift extern blockdivmask extern r_lightptr extern r_lightwidth extern r_numvblocks extern r_sourcemax extern r_stepback ; externs from ASM-only code SEGMENT .data k dd 0 loopentry dd 0 ALIGN 4 blockjumptable16: dd LEnter2_16 dd LEnter4_16 dd 0, LEnter8_16 dd 0, 0, 0, LEnter16_16 SEGMENT .text ALIGN 4 ;;;;;;;;;;;;;;;;;;;;;;;; ; R_Surf16Start ;;;;;;;;;;;;;;;;;;;;;;;; global R_Surf16Start R_Surf16Start: ;;;;;;;;;;;;;;;;;;;;;;;; ; R_DrawSurfaceBlock16 ;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 4 global R_DrawSurfaceBlock16 R_DrawSurfaceBlock16: push ebp push edi push esi push ebx mov eax, dword [blocksize] mov edi, dword [prowdestbase] mov esi, dword [pbasesource] mov ebx, dword [sourcesstep] mov ecx, dword [blockjumptable16-4+eax*2] mov dword [k],eax mov dword [loopentry],ecx mov edx, dword [lightleft] mov ebp, dword [lightright] Lblockloop16: sub ebp,edx mov cl, byte [blockdivshift] sar ebp,cl jns Lp1_16 test ebp, dword [blockdivmask] jz Lp1_16 inc ebp Lp1_16: sub eax,eax sub ecx,ecx jmp dword[loopentry] ALIGN 4 LEnter16_16: mov al, byte [esi] mov cl, byte [esi+ebx] mov ah,dh add edx,ebp mov ch,dh lea esi, [esi+ebx*2] mov ax, word [12345678h+eax*2] LBPatch0: add edx,ebp mov word [edi],ax mov cx, word [12345678h+ecx*2] LBPatch1: mov word [2+edi],cx add edi,04h mov al, byte [esi] mov cl, byte [esi+ebx] mov ah,dh add edx,ebp mov ch,dh lea esi, [esi+ebx*2] mov ax, word [12345678h+eax*2] LBPatch2: add edx,ebp mov word [edi],ax mov cx, word [12345678h+ecx*2] LBPatch3: mov word [2+edi],cx add edi,04h mov al, byte [esi] mov cl, byte [esi+ebx] mov ah,dh add edx,ebp mov ch,dh lea esi, [esi+ebx*2] mov ax, word [12345678h+eax*2] LBPatch4: add edx,ebp mov word [edi],ax mov cx, word [12345678h+ecx*2] LBPatch5: mov word [2+edi],cx add edi,04h mov al, byte [esi] mov cl, byte [esi+ebx] mov ah,dh add edx,ebp mov ch,dh lea esi, [esi+ebx*2] mov ax, word [12345678h+eax*2] LBPatch6: add edx,ebp mov word [edi],ax mov cx, word [12345678h+ecx*2] LBPatch7: mov word [2+edi],cx add edi,04h LEnter8_16: mov al, byte [esi] mov cl, byte [esi+ebx] mov ah,dh add edx,ebp mov ch,dh lea esi, [esi+ebx*2] mov ax, word [12345678h+eax*2] LBPatch8: add edx,ebp mov word [edi],ax mov cx, word [12345678h+ecx*2] LBPatch9: mov word [2+edi],cx add edi,04h mov al, byte [esi] mov cl, byte [esi+ebx] mov ah,dh add edx,ebp mov ch,dh lea esi, [esi+ebx*2] mov ax, word [12345678h+eax*2] LBPatch10: add edx,ebp mov word [edi],ax mov cx, word [12345678h+ecx*2] LBPatch11: mov word [2+edi],cx add edi,04h LEnter4_16: mov al, byte [esi] mov cl, byte [esi+ebx] mov ah,dh add edx,ebp mov ch,dh lea esi, [esi+ebx*2] mov ax, word [12345678h+eax*2] LBPatch12: add edx,ebp mov word [edi],ax mov cx, word [12345678h+ecx*2] LBPatch13: mov word [2+edi],cx add edi,04h LEnter2_16: mov al, byte [esi] mov cl, byte [esi+ebx] mov ah,dh add edx,ebp mov ch,dh lea esi, [esi+ebx*2] mov ax, word [12345678h+eax*2] LBPatch14: add edx,ebp mov word [edi],ax mov cx, word [12345678h+ecx*2] LBPatch15: mov word [2+edi],cx add edi,04h mov esi, dword [pbasesource] mov edx, dword [lightleft] mov ebp, dword [lightright] mov eax, dword [sourcetstep] mov ecx, dword [lightrightstep] mov edi, dword [prowdestbase] add esi,eax add ebp,ecx mov eax, dword [lightleftstep] mov ecx, dword [surfrowbytes] add edx,eax add edi,ecx mov dword [pbasesource],esi mov dword [lightright],ebp mov eax, dword [k] mov dword [lightleft],edx dec eax mov dword [prowdestbase],edi mov dword [k],eax jnz near Lblockloop16 pop ebx pop esi pop edi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; R_Surf16End ;;;;;;;;;;;;;;;;;;;;;;;; global R_Surf16End R_Surf16End: SEGMENT .data ALIGN 4 LPatchTable16: dd LBPatch0-4 dd LBPatch1-4 dd LBPatch2-4 dd LBPatch3-4 dd LBPatch4-4 dd LBPatch5-4 dd LBPatch6-4 dd LBPatch7-4 dd LBPatch8-4 dd LBPatch9-4 dd LBPatch10-4 dd LBPatch11-4 dd LBPatch12-4 dd LBPatch13-4 dd LBPatch14-4 dd LBPatch15-4 SEGMENT .text ALIGN 4 ;;;;;;;;;;;;;;;;;;;;;;;; ; R_Surf16Patch ;;;;;;;;;;;;;;;;;;;;;;;; global R_Surf16Patch R_Surf16Patch: push ebx mov eax, dword [colormap] mov ebx,offset LPatchTable16 mov ecx,16 LPatchLoop16: mov edx, dword [ebx] add ebx,4 mov dword [edx],eax dec ecx jnz LPatchLoop16 pop ebx ret engine/h2shared/surf8.asm000066400000000000000000000266531444734033100156130ustar00rootroot00000000000000; ; surf8.asm ; x86 assembly-language 8 bpp surface block drawing code. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: _sym_prefix prowdestbase _sym_prefix pbasesource _sym_prefix lightright _sym_prefix lightrightstep _sym_prefix lightleft _sym_prefix lightleftstep _sym_prefix lightdeltastep _sym_prefix lightdelta _sym_prefix sourcetstep _sym_prefix surfrowbytes _sym_prefix colormap _sym_prefix blocksize _sym_prefix sourcesstep _sym_prefix blockdivshift _sym_prefix blockdivmask _sym_prefix r_lightptr _sym_prefix r_lightwidth _sym_prefix r_numvblocks _sym_prefix r_sourcemax _sym_prefix r_stepback ; C-shared globals: _sym_prefix R_Surf8Start _sym_prefix R_DrawSurfaceBlock8_mip0 _sym_prefix R_DrawSurfaceBlock8_mip1 _sym_prefix R_DrawSurfaceBlock8_mip2 _sym_prefix R_DrawSurfaceBlock8_mip3 _sym_prefix R_Surf8End _sym_prefix R_Surf8Patch %endif ; _sym_prefix ; externs from C code extern prowdestbase extern pbasesource extern lightright extern lightrightstep extern lightleft extern lightleftstep extern lightdeltastep extern lightdelta extern sourcetstep extern surfrowbytes extern colormap extern blocksize extern sourcesstep extern blockdivshift extern blockdivmask extern r_lightptr extern r_lightwidth extern r_numvblocks extern r_sourcemax extern r_stepback ; externs from ASM-only code SEGMENT .data sb_v dd 0 SEGMENT .text ALIGN 4 ;;;;;;;;;;;;;;;;;;;;;;;; ; R_Surf8Start ;;;;;;;;;;;;;;;;;;;;;;;; global R_Surf8Start R_Surf8Start: ;;;;;;;;;;;;;;;;;;;;;;;; ; R_DrawSurfaceBlock8_mip0 ;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 4 global R_DrawSurfaceBlock8_mip0 R_DrawSurfaceBlock8_mip0: push ebp push edi push esi push ebx mov ebx, dword [r_lightptr] mov eax, dword [r_numvblocks] mov dword [sb_v],eax mov edi, dword [prowdestbase] mov esi, dword [pbasesource] Lv_loop_mip0: mov eax, dword [ebx] mov edx, dword [4+ebx] mov ebp,eax mov ecx, dword [r_lightwidth] mov dword [lightright],edx sub ebp,edx and ebp,0FFFFFh lea ebx, [ebx+ecx*4] mov dword [r_lightptr],ebx mov ecx, dword [4+ebx] mov ebx, dword [ebx] sub ebx,eax sub ecx,edx sar ecx,4 or ebp,0F0000000h sar ebx,4 mov dword [lightrightstep],ecx sub ebx,ecx and ebx,0FFFFFh or ebx,0F0000000h sub ecx,ecx mov dword [lightdeltastep],ebx sub ebx,ebx Lblockloop8_mip0: mov dword [lightdelta],ebp mov cl, byte [14+esi] sar ebp,4 mov bh,dh mov bl, byte [15+esi] add edx,ebp mov ch,dh add edx,ebp mov ah, byte [12345678h+ebx] LBPatch0: mov bl, byte [13+esi] mov al, byte [12345678h+ecx] LBPatch1: mov cl, byte [12+esi] mov bh,dh add edx,ebp ror eax,16 mov ch,dh add edx,ebp mov ah, byte [12345678h+ebx] LBPatch2: mov bl, byte [11+esi] mov al, byte [12345678h+ecx] LBPatch3: mov cl, byte [10+esi] mov dword [12+edi],eax mov bh,dh add edx,ebp mov ch,dh add edx,ebp mov ah, byte [12345678h+ebx] LBPatch4: mov bl, byte [9+esi] mov al, byte [12345678h+ecx] LBPatch5: mov cl, byte [8+esi] mov bh,dh add edx,ebp ror eax,16 mov ch,dh add edx,ebp mov ah, byte [12345678h+ebx] LBPatch6: mov bl, byte [7+esi] mov al, byte [12345678h+ecx] LBPatch7: mov cl, byte [6+esi] mov dword [8+edi],eax mov bh,dh add edx,ebp mov ch,dh add edx,ebp mov ah, byte [12345678h+ebx] LBPatch8: mov bl, byte [5+esi] mov al, byte [12345678h+ecx] LBPatch9: mov cl, byte [4+esi] mov bh,dh add edx,ebp ror eax,16 mov ch,dh add edx,ebp mov ah, byte [12345678h+ebx] LBPatch10: mov bl, byte [3+esi] mov al, byte [12345678h+ecx] LBPatch11: mov cl, byte [2+esi] mov dword [4+edi],eax mov bh,dh add edx,ebp mov ch,dh add edx,ebp mov ah, byte [12345678h+ebx] LBPatch12: mov bl, byte [1+esi] mov al, byte [12345678h+ecx] LBPatch13: mov cl, byte [esi] mov bh,dh add edx,ebp ror eax,16 mov ch,dh mov ah, byte [12345678h+ebx] LBPatch14: mov edx, dword [lightright] mov al, byte [12345678h+ecx] LBPatch15: mov ebp, dword [lightdelta] mov dword [edi],eax add esi, dword [sourcetstep] add edi, dword [surfrowbytes] add edx, dword [lightrightstep] add ebp, dword [lightdeltastep] mov dword [lightright],edx jc near Lblockloop8_mip0 cmp esi, dword [r_sourcemax] jb LSkip_mip0 sub esi, dword [r_stepback] LSkip_mip0: mov ebx, dword [r_lightptr] dec dword [sb_v] jnz near Lv_loop_mip0 pop ebx pop esi pop edi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; R_DrawSurfaceBlock8_mip1 ;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 4 global R_DrawSurfaceBlock8_mip1 R_DrawSurfaceBlock8_mip1: push ebp push edi push esi push ebx mov ebx, dword [r_lightptr] mov eax, dword [r_numvblocks] mov dword [sb_v],eax mov edi, dword [prowdestbase] mov esi, dword [pbasesource] Lv_loop_mip1: mov eax, dword [ebx] mov edx, dword [4+ebx] mov ebp,eax mov ecx, dword [r_lightwidth] mov dword [lightright],edx sub ebp,edx and ebp,0FFFFFh lea ebx, [ebx+ecx*4] mov dword [r_lightptr],ebx mov ecx, dword [4+ebx] mov ebx, dword [ebx] sub ebx,eax sub ecx,edx sar ecx,3 or ebp,070000000h sar ebx,3 mov dword [lightrightstep],ecx sub ebx,ecx and ebx,0FFFFFh or ebx,0F0000000h sub ecx,ecx mov dword [lightdeltastep],ebx sub ebx,ebx Lblockloop8_mip1: mov dword [lightdelta],ebp mov cl, byte [6+esi] sar ebp,3 mov bh,dh mov bl, byte [7+esi] add edx,ebp mov ch,dh add edx,ebp mov ah, byte [12345678h+ebx] LBPatch22: mov bl, byte [5+esi] mov al, byte [12345678h+ecx] LBPatch23: mov cl, byte [4+esi] mov bh,dh add edx,ebp ror eax,16 mov ch,dh add edx,ebp mov ah, byte [12345678h+ebx] LBPatch24: mov bl, byte [3+esi] mov al, byte [12345678h+ecx] LBPatch25: mov cl, byte [2+esi] mov dword [4+edi],eax mov bh,dh add edx,ebp mov ch,dh add edx,ebp mov ah, byte [12345678h+ebx] LBPatch26: mov bl, byte [1+esi] mov al, byte [12345678h+ecx] LBPatch27: mov cl, byte [esi] mov bh,dh add edx,ebp ror eax,16 mov ch,dh mov ah, byte [12345678h+ebx] LBPatch28: mov edx, dword [lightright] mov al, byte [12345678h+ecx] LBPatch29: mov ebp, dword [lightdelta] mov dword [edi],eax mov eax, dword [sourcetstep] add esi,eax mov eax, dword [surfrowbytes] add edi,eax mov eax, dword [lightrightstep] add edx,eax mov eax, dword [lightdeltastep] add ebp,eax mov dword [lightright],edx jc near Lblockloop8_mip1 cmp esi, dword [r_sourcemax] jb LSkip_mip1 sub esi, dword [r_stepback] LSkip_mip1: mov ebx, dword [r_lightptr] dec dword [sb_v] jnz near Lv_loop_mip1 pop ebx pop esi pop edi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; R_DrawSurfaceBlock8_mip2 ;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 4 global R_DrawSurfaceBlock8_mip2 R_DrawSurfaceBlock8_mip2: push ebp push edi push esi push ebx mov ebx, dword [r_lightptr] mov eax, dword [r_numvblocks] mov dword [sb_v],eax mov edi, dword [prowdestbase] mov esi, dword [pbasesource] Lv_loop_mip2: mov eax, dword [ebx] mov edx, dword [4+ebx] mov ebp,eax mov ecx, dword [r_lightwidth] mov dword [lightright],edx sub ebp,edx and ebp,0FFFFFh lea ebx, [ebx+ecx*4] mov dword [r_lightptr],ebx mov ecx, dword [4+ebx] mov ebx, dword [ebx] sub ebx,eax sub ecx,edx sar ecx,2 or ebp,030000000h sar ebx,2 mov dword [lightrightstep],ecx sub ebx,ecx and ebx,0FFFFFh or ebx,0F0000000h sub ecx,ecx mov dword [lightdeltastep],ebx sub ebx,ebx Lblockloop8_mip2: mov dword [lightdelta],ebp mov cl, byte [2+esi] sar ebp,2 mov bh,dh mov bl, byte [3+esi] add edx,ebp mov ch,dh add edx,ebp mov ah, byte [12345678h+ebx] LBPatch18: mov bl, byte [1+esi] mov al, byte [12345678h+ecx] LBPatch19: mov cl, byte [esi] mov bh,dh add edx,ebp ror eax,16 mov ch,dh mov ah, byte [12345678h+ebx] LBPatch20: mov edx, dword [lightright] mov al, byte [12345678h+ecx] LBPatch21: mov ebp, dword [lightdelta] mov dword [edi],eax mov eax, dword [sourcetstep] add esi,eax mov eax, dword [surfrowbytes] add edi,eax mov eax, dword [lightrightstep] add edx,eax mov eax, dword [lightdeltastep] add ebp,eax mov dword [lightright],edx jc Lblockloop8_mip2 cmp esi, dword [r_sourcemax] jb LSkip_mip2 sub esi, dword [r_stepback] LSkip_mip2: mov ebx, dword [r_lightptr] dec dword [sb_v] jnz near Lv_loop_mip2 pop ebx pop esi pop edi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; R_DrawSurfaceBlock8_mip3 ;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 4 global R_DrawSurfaceBlock8_mip3 R_DrawSurfaceBlock8_mip3: push ebp push edi push esi push ebx mov ebx, dword [r_lightptr] mov eax, dword [r_numvblocks] mov dword [sb_v],eax mov edi, dword [prowdestbase] mov esi, dword [pbasesource] Lv_loop_mip3: mov eax, dword [ebx] mov edx, dword [4+ebx] mov ebp,eax mov ecx, dword [r_lightwidth] mov dword [lightright],edx sub ebp,edx and ebp,0FFFFFh lea ebx, [ebx+ecx*4] mov dword [lightdelta],ebp mov dword [r_lightptr],ebx mov ecx, dword [4+ebx] mov ebx, dword [ebx] sub ebx,eax sub ecx,edx sar ecx,1 sar ebx,1 mov dword [lightrightstep],ecx sub ebx,ecx and ebx,0FFFFFh sar ebp,1 or ebx,0F0000000h mov dword [lightdeltastep],ebx sub ebx,ebx mov bl, byte [1+esi] sub ecx,ecx mov bh,dh mov cl, byte [esi] add edx,ebp mov ch,dh mov al, byte [12345678h+ebx] LBPatch16: mov edx, dword [lightright] mov byte [1+edi],al mov al, byte [12345678h+ecx] LBPatch17: mov byte [edi],al mov eax, dword [sourcetstep] add esi,eax mov eax, dword [surfrowbytes] add edi,eax mov eax, dword [lightdeltastep] mov ebp, dword [lightdelta] mov cl, byte [esi] add ebp,eax mov eax, dword [lightrightstep] sar ebp,1 add edx,eax mov bh,dh mov bl, byte [1+esi] add edx,ebp mov ch,dh mov al, byte [12345678h+ebx] LBPatch30: mov edx, dword [sourcetstep] mov byte [1+edi],al mov al, byte [12345678h+ecx] LBPatch31: mov byte [edi],al mov ebp, dword [surfrowbytes] add esi,edx add edi,ebp cmp esi, dword [r_sourcemax] jb LSkip_mip3 sub esi, dword [r_stepback] LSkip_mip3: mov ebx, dword [r_lightptr] dec dword [sb_v] jnz near Lv_loop_mip3 pop ebx pop esi pop edi pop ebp ret ;;;;;;;;;;;;;;;;;;;;;;;; ; R_Surf8End ;;;;;;;;;;;;;;;;;;;;;;;; global R_Surf8End R_Surf8End: SEGMENT .data ALIGN 4 LPatchTable8: dd LBPatch0-4 dd LBPatch1-4 dd LBPatch2-4 dd LBPatch3-4 dd LBPatch4-4 dd LBPatch5-4 dd LBPatch6-4 dd LBPatch7-4 dd LBPatch8-4 dd LBPatch9-4 dd LBPatch10-4 dd LBPatch11-4 dd LBPatch12-4 dd LBPatch13-4 dd LBPatch14-4 dd LBPatch15-4 dd LBPatch16-4 dd LBPatch17-4 dd LBPatch18-4 dd LBPatch19-4 dd LBPatch20-4 dd LBPatch21-4 dd LBPatch22-4 dd LBPatch23-4 dd LBPatch24-4 dd LBPatch25-4 dd LBPatch26-4 dd LBPatch27-4 dd LBPatch28-4 dd LBPatch29-4 dd LBPatch30-4 dd LBPatch31-4 SEGMENT .text ;;;;;;;;;;;;;;;;;;;;;;;; ; R_Surf8Patch ;;;;;;;;;;;;;;;;;;;;;;;; ALIGN 4 global R_Surf8Patch R_Surf8Patch: push ebx mov eax, dword [colormap] mov ebx,offset LPatchTable8 mov ecx,32 LPatchLoop8: mov edx, dword [ebx] add ebx,4 mov dword [edx],eax dec ecx jnz LPatchLoop8 pop ebx ret engine/h2shared/sv_model.c000066400000000000000000000647121444734033100160140ustar00rootroot00000000000000/* sv_model.c -- model loading and caching * models are the only shared resource between a client and server * running on the same machine. * * This version of model.[ch] are based on the quake dedicated server * application lhnqserver by Forest 'LordHavoc' Hale, with simplified * data structures and loading only brush models without the textures. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" static qmodel_t* loadmodel; static char loadname[MAX_QPATH]; /* for hunk tags */ static void Mod_LoadBrushModel (qmodel_t *mod, void *buffer); static qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash); static cvar_t external_ents = {"external_ents", "1", CVAR_ARCHIVE}; static byte mod_novis[MAX_MAP_LEAFS/8]; static int *surfedges; static medge_t *edges; #define MAX_MOD_KNOWN 2048 static qmodel_t mod_known[MAX_MOD_KNOWN]; static int mod_numknown; /* =============== Mod_Init =============== */ void Mod_Init (void) { Cvar_RegisterVariable (&external_ents); memset (mod_novis, 0xff, sizeof(mod_novis)); } /* =============== Mod_PointInLeaf =============== */ mleaf_t *Mod_PointInLeaf (vec3_t p, qmodel_t *model) { mnode_t *node; node = model->nodes; if (node->contents < 0) return (mleaf_t *)node; while (1) { node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist]; if (node->contents < 0) return (mleaf_t *)node; } return NULL; // never reached } /* =================== Mod_DecompressVis =================== */ static byte *Mod_DecompressVis (byte *in, qmodel_t *model) { static byte decompressed[MAX_MAP_LEAFS/8]; int c; byte *out; int row; row = (model->numleafs+7)>>3; out = decompressed; if (!in) { // no vis info, so make all visible while (row) { *out++ = 0xff; row--; } return decompressed; } do { if (*in) { *out++ = *in++; continue; } c = in[1]; in += 2; while (c) { *out++ = 0; c--; } } while (out - decompressed < row); return decompressed; } byte *Mod_LeafPVS (mleaf_t *leaf, qmodel_t *model) { if (leaf == model->leafs) return mod_novis; return Mod_DecompressVis (leaf->compressed_vis, model); } /* =================== Mod_ClearAll =================== */ void Mod_ClearAll (void) { int i; qmodel_t *mod; for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) mod->needload = NL_NEEDS_LOADED; } /* ================== Mod_FindName ================== */ qmodel_t *Mod_FindName (const char *name) { int i; qmodel_t *mod; if (!name[0]) Host_Error ("%s: NULL name", __thisfunc__); // // search the currently loaded models // for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { if (!strcmp (mod->name, name) ) break; } if (i == mod_numknown) { if (mod_numknown == MAX_MOD_KNOWN) Host_Error ("mod_numknown == MAX_MOD_KNOWN"); q_strlcpy (mod->name, name, MAX_QPATH); mod->needload = NL_NEEDS_LOADED; mod_numknown++; } return mod; } /* ================== Mod_LoadModel Loads a model into the cache ================== */ static qmodel_t *Mod_LoadModel (qmodel_t *mod, qboolean crash) { byte *buf; if (mod->needload == NL_PRESENT) return mod; // // load the file // buf = FS_LoadTempFile (mod->name, & mod->path_id); if (!buf) { if (crash) Host_Error ("%s: %s not found", __thisfunc__, mod->name); return NULL; } // // allocate a new model // COM_FileBase (mod->name, loadname, sizeof(loadname)); loadmodel = mod; // // fill it in // // call the apropriate loader mod->needload = NL_PRESENT; Mod_LoadBrushModel (mod, buf); return mod; } /* ================== Mod_ForName Loads in a model for the given name ================== */ qmodel_t *Mod_ForName (const char *name, qboolean crash) { qmodel_t *mod; mod = Mod_FindName (name); return Mod_LoadModel (mod, crash); } /* =============================================================================== BRUSHMODEL LOADING =============================================================================== */ static byte *mod_base; /* ================= Mod_LoadLighting ================= */ static void Mod_LoadLighting (lump_t *l) { if (!l->filelen) { loadmodel->lightdata = NULL; return; } loadmodel->lightdata = (byte *) Hunk_AllocName ( l->filelen, "light"); memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); } /* ================= Mod_LoadVisibility ================= */ static void Mod_LoadVisibility (lump_t *l) { if (!l->filelen) { loadmodel->visdata = NULL; return; } loadmodel->visdata = (byte *) Hunk_AllocName ( l->filelen, "vis"); memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); } /* ================= Mod_LoadEntities ================= */ static void Mod_LoadEntities (lump_t *l) { char entfilename[MAX_QPATH]; char *ents; int mark; unsigned int path_id; if (! external_ents.integer) goto _load_embedded; q_strlcpy(entfilename, loadmodel->name, sizeof(entfilename)); COM_StripExtension(entfilename, entfilename, sizeof(entfilename)); q_strlcat(entfilename, ".ent", sizeof(entfilename)); Con_DPrintf("trying to load %s\n", entfilename); mark = Hunk_LowMark(); ents = (char *) FS_LoadHunkFile (entfilename, &path_id); if (ents) { // use ent file only from the same gamedir as the map // itself or from a searchpath with higher priority. if (path_id < loadmodel->path_id) { Hunk_FreeToLowMark(mark); Con_DPrintf("ignored %s from a gamedir with lower priority\n", entfilename); } else { loadmodel->entities = ents; Con_DPrintf("Loaded external entity file %s\n", entfilename); return; } } _load_embedded: if (!l->filelen) { loadmodel->entities = NULL; return; } loadmodel->entities = (char *) Hunk_AllocName ( l->filelen, "entities"); memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); } /* ================= Mod_LoadVertexes ================= */ static void Mod_LoadVertexes (lump_t *l) { dvertex_t *in; mvertex_t *out; int i, count; in = (dvertex_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mvertex_t *) Hunk_AllocName (count * sizeof(*out), "vertexes"); loadmodel->vertexes = out; loadmodel->numvertexes = count; for (i = 0; i < count; i++, in++, out++) { out->position[0] = LittleFloat (in->point[0]); out->position[1] = LittleFloat (in->point[1]); out->position[2] = LittleFloat (in->point[2]); } } /* ================= Mod_LoadSubmodels ================= */ static void Mod_LoadSubmodels (lump_t *l) { dmodel_t *in; dmodel_t *out; int i, j, count; in = (dmodel_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (dmodel_t *) Hunk_AllocName (count * sizeof(*out), "submodels"); loadmodel->submodels = out; loadmodel->numsubmodels = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { // spread the mins / maxs by a pixel out->mins[j] = LittleFloat (in->mins[j]) - 1; out->maxs[j] = LittleFloat (in->maxs[j]) + 1; out->origin[j] = LittleFloat (in->origin[j]); } for (j = 0; j < MAX_MAP_HULLS; j++) out->headnode[j] = LittleLong (in->headnode[j]); out->visleafs = LittleLong (in->visleafs); out->firstface = LittleLong (in->firstface); out->numfaces = LittleLong (in->numfaces); } } /* ================= Mod_LoadEdges ================= */ #ifndef ENABLE_BSP2 #define Mod_LoadEdges(_l, _v) Mod_LoadEdges_V29((_l)) #else static void Mod_LoadEdges_V29 (lump_t *l); static void Mod_LoadEdges_BSP2(lump_t *l); static void Mod_LoadEdges (lump_t *l, qboolean bsp2) { if (bsp2) Mod_LoadEdges_BSP2(l); else Mod_LoadEdges_V29 (l); } static void Mod_LoadEdges_BSP2(lump_t *l) { dedge2_t *in; medge_t *out; int i, count; in = (dedge2_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (medge_t *) Hunk_AllocName ((count + 1) * sizeof(*out), "edges"); // loadmodel->edges = out; // loadmodel->numedges = count; edges = out; for (i = 0; i < count; i++, in++, out++) { out->v[0] = (unsigned int)LittleLong(in->v[0]); out->v[1] = (unsigned int)LittleLong(in->v[1]); } } #endif static void Mod_LoadEdges_V29 (lump_t *l) { dedge_t *in; medge_t *out; int i, count; in = (dedge_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (medge_t *) Hunk_AllocName ((count + 1) * sizeof(*out), "edges"); // loadmodel->edges = out; // loadmodel->numedges = count; edges = out; for (i = 0; i < count; i++, in++, out++) { out->v[0] = (unsigned short)LittleShort(in->v[0]); out->v[1] = (unsigned short)LittleShort(in->v[1]); } } /* ================= Mod_LoadTexinfo ================= */ static void Mod_LoadTexinfo (lump_t *l) { texinfo_t *in; mtexinfo_t *out; int i, j, count; in = (texinfo_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mtexinfo_t *) Hunk_AllocName (count * sizeof(*out), "texture"); loadmodel->texinfo = out; loadmodel->numtexinfo = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 4; j++) { out->vecs[0][j] = LittleFloat (in->vecs[0][j]); out->vecs[1][j] = LittleFloat (in->vecs[1][j]); } out->flags = LittleLong (in->flags); } } /* ================ CalcSurfaceExtents Fills in s->texturemins[] and s->extents[] ================ */ static void CalcSurfaceExtents (msurface_t *s, int firstedge, int numedges) { float mins[2], maxs[2], val; int i, j, e; mvertex_t *v; mtexinfo_t *tex; int bmins[2], bmaxs[2]; mins[0] = mins[1] = 9999999; /* FIXME: change these two to FLT_MAX/-FLT_MAX */ maxs[0] = maxs[1] = -9999999; tex = s->texinfo; for (i = 0; i < numedges; i++) { e = surfedges[firstedge+i]; if (e >= 0) v = &loadmodel->vertexes[edges[e].v[0]]; else v = &loadmodel->vertexes[edges[-e].v[1]]; for (j = 0; j < 2; j++) { /* added double casts so that 64 bit/sse2 builds' precision * matches that of x87 floating point. took from QuakeSpasm, * patch by Eric Wasylishen. */ val = ((double)v->position[0] * (double)tex->vecs[j][0]) + ((double)v->position[1] * (double)tex->vecs[j][1]) + ((double)v->position[2] * (double)tex->vecs[j][2]) + (double)tex->vecs[j][3]; if (val < mins[j]) mins[j] = val; if (val > maxs[j]) maxs[j] = val; } } for (i = 0; i < 2; i++) { bmins[i] = (int) floor(mins[i]/16); bmaxs[i] = (int) ceil(maxs[i]/16); s->texturemins[i] = bmins[i] * 16; s->extents[i] = (bmaxs[i] - bmins[i]) * 16; if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512 /* 256 */ ) Host_Error ("Bad surface extents"); } } /* ================= Mod_LoadFaces ================= */ #ifndef ENABLE_BSP2 #define Mod_LoadFaces(_l, _v) Mod_LoadFaces_V29((_l)) #else static void Mod_LoadFaces_BSP2(lump_t *l); static void Mod_LoadFaces_V29 (lump_t *l); static void Mod_LoadFaces (lump_t *l, qboolean bsp2) { if (bsp2) Mod_LoadFaces_BSP2(l); else Mod_LoadFaces_V29 (l); } static void Mod_LoadFaces_BSP2(lump_t *l) { dface2_t *in; msurface_t *out; int i, count, surfnum; int planenum, side; int firstedge, numedges; in = (dface2_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (msurface_t *) Hunk_AllocName (count * sizeof(*out), "faces"); loadmodel->surfaces = out; loadmodel->numsurfaces = count; for (surfnum = 0; surfnum < count; surfnum++, in++, out++) { firstedge = LittleLong(in->firstedge); numedges = LittleLong(in->numedges); out->flags = 0; planenum = LittleLong(in->planenum); side = LittleLong(in->side); if (side) out->flags |= SURF_PLANEBACK; out->plane = loadmodel->planes + planenum; out->texinfo = loadmodel->texinfo + LittleLong (in->texinfo); CalcSurfaceExtents (out, firstedge, numedges); // lighting info for (i = 0; i < MAXLIGHTMAPS; i++) out->styles[i] = in->styles[i]; i = LittleLong(in->lightofs); if (i == -1) { out->samples = NULL; } else { out->samples = loadmodel->lightdata + i; } } } #endif static void Mod_LoadFaces_V29 (lump_t *l) { dface_t *in; msurface_t *out; int i, count, surfnum; int planenum, side; int firstedge, numedges; in = (dface_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (msurface_t *) Hunk_AllocName (count * sizeof(*out), "faces"); loadmodel->surfaces = out; loadmodel->numsurfaces = count; for (surfnum = 0; surfnum < count; surfnum++, in++, out++) { firstedge = LittleLong(in->firstedge); numedges = LittleShort(in->numedges); out->flags = 0; planenum = LittleShort(in->planenum); side = LittleShort(in->side); if (side) out->flags |= SURF_PLANEBACK; out->plane = loadmodel->planes + planenum; out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo); CalcSurfaceExtents (out, firstedge, numedges); // lighting info for (i = 0; i < MAXLIGHTMAPS; i++) out->styles[i] = in->styles[i]; i = LittleLong(in->lightofs); if (i == -1) { out->samples = NULL; } else { out->samples = loadmodel->lightdata + i; } } } /* ================= Mod_SetParent ================= */ static void Mod_SetParent (mnode_t *node, mnode_t *parent) { node->parent = parent; if (node->contents < 0) return; Mod_SetParent (node->children[0], node); Mod_SetParent (node->children[1], node); } /* ================= Mod_LoadNodes ================= */ #ifndef ENABLE_BSP2 #define Mod_LoadNodes(_l, _v) Mod_LoadNodes_V29((_l)) #else static void Mod_LoadNodes_BSP2(lump_t *l); static void Mod_LoadNodes_V29 (lump_t *l); static void Mod_LoadNodes (lump_t *l, qboolean bsp2) { if (bsp2) Mod_LoadNodes_BSP2(l); else Mod_LoadNodes_V29 (l); } static void Mod_LoadNodes_BSP2(lump_t *l) { int i, j, count, p; dnode2_t *in; mnode_t *out; in = (dnode2_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mnode_t *) Hunk_AllocName (count * sizeof(*out), "nodes"); loadmodel->nodes = out; loadmodel->numnodes = count; for (i = 0; i < count; i++, in++, out++) { p = LittleLong(in->planenum); out->plane = loadmodel->planes + p; out->firstsurface = LittleLong (in->firstface); out->numsurfaces = LittleLong (in->numfaces); for (j = 0; j < 2; j++) { p = LittleLong (in->children[j]); if (p >= 0) out->children[j] = loadmodel->nodes + p; else out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); } } Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs } #endif static void Mod_LoadNodes_V29 (lump_t *l) { int i, j, count, p; dnode_t *in; mnode_t *out; in = (dnode_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mnode_t *) Hunk_AllocName (count * sizeof(*out), "nodes"); loadmodel->nodes = out; loadmodel->numnodes = count; for (i = 0; i < count; i++, in++, out++) { p = LittleLong(in->planenum); out->plane = loadmodel->planes + p; out->firstsurface = LittleShort (in->firstface); out->numsurfaces = LittleShort (in->numfaces); for (j = 0; j < 2; j++) { p = LittleShort (in->children[j]); if (p >= 0) out->children[j] = loadmodel->nodes + p; else out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); } } Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs } /* ================= Mod_LoadLeafs ================= */ #ifndef ENABLE_BSP2 #define Mod_LoadLeafs(_l, _v) Mod_LoadLeafs_V29((_l)) #else static void Mod_LoadLeafs_BSP2(lump_t *l); static void Mod_LoadLeafs_V29 (lump_t *l); static void Mod_LoadLeafs (lump_t *l, qboolean bsp2) { if (bsp2) Mod_LoadLeafs_BSP2(l); else Mod_LoadLeafs_V29 (l); } static void Mod_LoadLeafs_BSP2(lump_t *l) { dleaf2_t *in; mleaf_t *out; int i, count, p; in = (dleaf2_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mleaf_t *) Hunk_AllocName (count * sizeof(*out), "leafs"); loadmodel->leafs = out; loadmodel->numleafs = count; for (i = 0; i < count; i++, in++, out++) { p = LittleLong(in->contents); out->contents = p; p = LittleLong(in->visofs); if (p == -1) out->compressed_vis = NULL; else out->compressed_vis = loadmodel->visdata + p; } } #endif static void Mod_LoadLeafs_V29 (lump_t *l) { dleaf_t *in; mleaf_t *out; int i, count, p; in = (dleaf_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mleaf_t *) Hunk_AllocName (count * sizeof(*out), "leafs"); loadmodel->leafs = out; loadmodel->numleafs = count; for (i = 0; i < count; i++, in++, out++) { p = LittleLong(in->contents); out->contents = p; p = LittleLong(in->visofs); if (p == -1) out->compressed_vis = NULL; else out->compressed_vis = loadmodel->visdata + p; } } /* ================= Mod_LoadClipnodes ================= */ static void Mod_MakeHulls (int count); #ifndef ENABLE_BSP2 #define Mod_LoadClipnodes(_l, _v) Mod_LoadClipnodes_V29((_l)) #else static void Mod_LoadClipnodes_BSP2(lump_t *l); static void Mod_LoadClipnodes_V29 (lump_t *l); static void Mod_LoadClipnodes (lump_t *l, qboolean bsp2) { if (bsp2) Mod_LoadClipnodes_BSP2(l); else Mod_LoadClipnodes_V29 (l); } static void Mod_LoadClipnodes_BSP2(lump_t *l) { dclipnode2_t *in; mclipnode_t *out; int i, count; in = (dclipnode2_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mclipnode_t *) Hunk_AllocName (count * sizeof(*out), "clipnodes"); loadmodel->clipnodes = out; loadmodel->numclipnodes = count; for (i = 0; i < count; i++, out++, in++) { out->planenum = LittleLong(in->planenum); out->children[0] = LittleLong(in->children[0]); out->children[1] = LittleLong(in->children[1]); if (out->children[0] >= count || out->children[1] >= count) Host_Error("Corrupt clipping hull (out of range child)\n"); } Mod_MakeHulls(count); } #endif static void Mod_LoadClipnodes_V29 (lump_t *l) { dclipnode_t *in; mclipnode_t *out; int i, count; in = (dclipnode_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mclipnode_t *) Hunk_AllocName (count * sizeof(*out), "clipnodes"); loadmodel->clipnodes = out; loadmodel->numclipnodes = count; for (i = 0; i < count; i++, out++, in++) { out->planenum = LittleLong(in->planenum); out->children[0] = LittleShort(in->children[0]); out->children[1] = LittleShort(in->children[1]); if (out->children[0] >= count || out->children[1] >= count) Host_Error("Corrupt clipping hull (out of range child)\n"); } Mod_MakeHulls(count); } static void Mod_MakeHulls (int count) { hull_t *hull; //player hull = &loadmodel->hulls[1]; hull->clipnodes = loadmodel->clipnodes; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; hull->clip_mins[0] = -16; hull->clip_mins[1] = -16; hull->clip_mins[2] = -24; hull->clip_maxs[0] = 16; hull->clip_maxs[1] = 16; hull->clip_maxs[2] = 32; //scorpion hull = &loadmodel->hulls[2]; hull->clipnodes = loadmodel->clipnodes; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; hull->clip_mins[0] = -24; hull->clip_mins[1] = -24; hull->clip_mins[2] = -20; hull->clip_maxs[0] = 24; hull->clip_maxs[1] = 24; hull->clip_maxs[2] = 20; //crouch hull = &loadmodel->hulls[3]; hull->clipnodes = loadmodel->clipnodes; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; hull->clip_mins[0] = -16; hull->clip_mins[1] = -16; hull->clip_mins[2] = -12; hull->clip_maxs[0] = 16; hull->clip_maxs[1] = 16; hull->clip_maxs[2] = 16; //hydra -changing in MP to '-8 -8 -8', '8 8 8' for pentacles hull = &loadmodel->hulls[4]; hull->clipnodes = loadmodel->clipnodes; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; #ifdef H2W hull->clip_mins[0] = -40; hull->clip_mins[1] = -40; hull->clip_mins[2] = -42; hull->clip_maxs[0] = 40; hull->clip_maxs[1] = 40; hull->clip_maxs[2] = 42; #else hull->clip_mins[0] = -8; hull->clip_mins[1] = -8; hull->clip_mins[2] = -8; hull->clip_maxs[0] = 8; hull->clip_maxs[1] = 8; hull->clip_maxs[2] = 8; #endif //golem - maybe change to '-28 -28 -40', '28 28 40' for Yakman hull = &loadmodel->hulls[5]; hull->clipnodes = loadmodel->clipnodes; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; #if 0 //use yak sizes hull->clip_mins[0] = -28; hull->clip_mins[1] = -28; hull->clip_mins[2] = -40; hull->clip_maxs[0] = 28; hull->clip_maxs[1] = 28; hull->clip_maxs[2] = 40; #else hull->clip_mins[0] = -48; hull->clip_mins[1] = -48; hull->clip_mins[2] = -50; hull->clip_maxs[0] = 48; hull->clip_maxs[1] = 48; hull->clip_maxs[2] = 50; #endif } /* ================= Mod_MakeHull0 Duplicate the drawing hull structure as a clipping hull ================= */ static void Mod_MakeHull0 (void) { mnode_t *in, *child; mclipnode_t *out; int i, j, count; hull_t *hull; hull = &loadmodel->hulls[0]; in = loadmodel->nodes; count = loadmodel->numnodes; out = (mclipnode_t *) Hunk_AllocName (count * sizeof(*out), "hull0"); hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; for (i = 0; i < count; i++, out++, in++) { out->planenum = in->plane - loadmodel->planes; for (j = 0; j < 2; j++) { child = in->children[j]; if (child->contents < 0) out->children[j] = child->contents; else out->children[j] = child - loadmodel->nodes; } } } /* ================= Mod_LoadSurfedges ================= */ static void Mod_LoadSurfedges (lump_t *l) { int i, count; int *in, *out; in = (int *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (int *) Hunk_AllocName (count * sizeof(*out), "surfedges"); // loadmodel->surfedges = out; // loadmodel->numsurfedges = count; surfedges = out; for (i = 0; i < count; i++) out[i] = LittleLong (in[i]); } /* ================= Mod_LoadPlanes ================= */ static void Mod_LoadPlanes (lump_t *l) { int i, j; mplane_t *out; dplane_t *in; int count; int bits; in = (dplane_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("%s: funny lump size in %s", __thisfunc__, loadmodel->name); count = l->filelen / sizeof(*in); out = (mplane_t *) Hunk_AllocName (count * 2 * sizeof(*out), "planes"); loadmodel->planes = out; loadmodel->numplanes = count; for (i = 0; i < count; i++, in++, out++) { bits = 0; for (j = 0; j < 3; j++) { out->normal[j] = LittleFloat (in->normal[j]); if (out->normal[j] < 0) bits |= 1<dist = LittleFloat (in->dist); out->type = LittleLong (in->type); out->signbits = bits; } } /* ================= Mod_LoadBrushModel ================= */ static void Mod_LoadBrushModel (qmodel_t *mod, void *buffer) { int i, j; dheader_t *header; dmodel_t *bm; qboolean bsp2 = false; loadmodel->type = mod_brush; header = (dheader_t *)buffer; i = LittleLong (header->version); #ifndef ENABLE_BSP2 (void) bsp2; #else if (i == BSP2VERSION) bsp2 = true; else #endif if (i != BSPVERSION) Host_Error ("%s: %s has unsupported version %i", __thisfunc__, mod->name, i); // swap all the lumps mod_base = (byte *)header; for (i = 0; i < (int) sizeof(dheader_t) / 4; i++) ((int *)header)[i] = LittleLong ( ((int *)header)[i]); // load into heap Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); Mod_LoadEdges (&header->lumps[LUMP_EDGES], bsp2); Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]); Mod_LoadPlanes (&header->lumps[LUMP_PLANES]); Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]); Mod_LoadFaces (&header->lumps[LUMP_FACES], bsp2); surfedges = NULL; edges = NULL; Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); Mod_LoadLeafs (&header->lumps[LUMP_LEAFS], bsp2); Mod_LoadNodes (&header->lumps[LUMP_NODES], bsp2); Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES], bsp2); Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); Mod_MakeHull0 (); mod->numframes = 2; // regular and alternate animation // // set up the submodels (FIXME: this is confusing) // for (i = 0; i < mod->numsubmodels; i++) { bm = &mod->submodels[i]; mod->hulls[0].firstclipnode = bm->headnode[0]; for (j = 1; j < MAX_MAP_HULLS; j++) { mod->hulls[j].firstclipnode = bm->headnode[j]; mod->hulls[j].lastclipnode = mod->numclipnodes-1; } mod->firstmodelsurface = bm->firstface; mod->nummodelsurfaces = bm->numfaces; VectorCopy (bm->maxs, mod->maxs); VectorCopy (bm->mins, mod->mins); mod->numleafs = bm->visleafs; if (i < mod->numsubmodels-1) { // duplicate the basic information char name[10]; q_snprintf (name, sizeof(name), "*%i", i+1); loadmodel = Mod_FindName (name); *loadmodel = *mod; strcpy (loadmodel->name, name); mod = loadmodel; } } } engine/h2shared/sv_model.h000066400000000000000000000150731444734033100160150ustar00rootroot00000000000000/* * sv_model.h -- header for model loading and caching * * This version of model.[ch] are based on the quake dedicated server * application lhnqserver by Forest 'LordHavoc' Hale, with simplified * data structures and loading only brush models without the textures. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SV_MODEL_H #define SV_MODEL_H #include "genmodel.h" #include "spritegn.h" /* d*_t structures are on-disk representations m*_t structures are in-memory */ /* ============================================================================== BRUSH MODELS ============================================================================== */ // // in memory representation // // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct { vec3_t position; } mvertex_t; #define SIDE_FRONT 0 #define SIDE_BACK 1 #define SIDE_ON 2 // plane_t structure typedef struct mplane_s { vec3_t normal; float dist; byte type; // for texture axis selection and fast side tests byte signbits; // signx + signy<<1 + signz<<1 byte pad[2]; } mplane_t; #define SURF_PLANEBACK 2 #define SURF_DRAWSKY 4 #define SURF_DRAWSPRITE 8 #define SURF_DRAWTURB 0x10 #define SURF_DRAWTILED 0x20 #define SURF_DRAWBACKGROUND 0x40 #define SURF_TRANSLUCENT 0x80 /* r_edge.asm checks this */ #define SURF_DRAWBLACK 0x200 typedef struct { unsigned int v[2]; } medge_t; typedef struct { float vecs[2][4]; int flags; } mtexinfo_t; typedef struct msurface_s { mplane_t *plane; int flags; int texturemins[2]; int extents[2]; mtexinfo_t *texinfo; // lighting info byte styles[MAXLIGHTMAPS]; byte *samples; // [numstyles*surfsize] } msurface_t; typedef struct mnode_s { // common with leaf int contents; // 0, to differentiate from leafs struct mnode_s *parent; // node specific mplane_t *plane; struct mnode_s *children[2]; unsigned int firstsurface; unsigned int numsurfaces; } mnode_t; typedef struct mleaf_s { // common with node int contents; // wil be a negative contents number struct mnode_s *parent; // leaf specific byte *compressed_vis; } mleaf_t; #ifdef ENABLE_BSP2 typedef dclipnode2_t mclipnode_t; #else typedef dclipnode_t mclipnode_t; #endif // !!! if this is changed, it must be changed in asm_i386.h too !!! typedef struct { mclipnode_t *clipnodes; mplane_t *planes; int firstclipnode; int lastclipnode; vec3_t clip_mins; vec3_t clip_maxs; } hull_t; #define HULL_IMPLICIT 0 // Choose the hull based on bounding box- like in Quake #define HULL_POINT 1 // 0 0 0, 0 0 0 #define HULL_PLAYER 2 // '-16 -16 0', '16 16 56' #define HULL_SCORPION 3 // '-24 -24 -20', '24 24 20' #define HULL_CROUCH 4 // '-16 -16 0', '16 16 28' #define HULL_HYDRA 5 // '-28 -28 -24', '28 28 24' #define HULL_GOLEM 6 // ???,??? //=================================================================== // // entity effects // #ifndef H2W /* see below for hexenworld */ #define EF_BRIGHTFIELD 0x00000001 #endif #define EF_MUZZLEFLASH 0x00000002 #define EF_BRIGHTLIGHT 0x00000004 #define EF_DIMLIGHT 0x00000008 #define EF_DARKLIGHT 0x00000010 #define EF_DARKFIELD 0x00000020 #define EF_LIGHT 0x00000040 #define EF_NODRAW 0x00000080 #ifdef H2W /* The only difference between Raven's hw-0.15 binary release and the * later HexenC source release is the EF_BRIGHTFIELD and EF_ONFIRE values: * the original binary releases had them as 1 and 1024 respectively, but * the later hcode src releases have them flipped: EF_BRIGHTFIELD = 1024 * and EF_ONFIRE = 1, which is a BIG BOO BOO. (On the other hand, Siege * binary and source releases have EF_BRIGHTFIELD and EF_ONFIRE values as * 1 and 1024, which makes the mess even messier.. Sigh..) * The hexenworld engine src release also have EF_BRIGHTFIELD as 1024 and * EF_ONFIRE as 1, therefore uHexen2 sticks to those values. */ #define EF_ONFIRE 0x00000001 #define EF_BRIGHTFIELD 0x00000400 #define EF_POWERFLAMEBURN 0x00000800 #define EF_UPDATESOUND 0x00002000 #define EF_POISON_GAS 0x00200000 #define EF_ACIDBLOB 0x00400000 //#define EF_PURIFY2_EFFECT 0x00200000 //#define EF_AXE_EFFECT 0x00400000 //#define EF_SWORD_EFFECT 0x00800000 //#define EF_TORNADO_EFFECT 0x01000000 #define EF_ICESTORM_EFFECT 0x02000000 //#define EF_ICEBALL_EFFECT 0x04000000 //#define EF_METEOR_EFFECT 0x08000000 #define EF_HAMMER_EFFECTS 0x10000000 #define EF_BEETLE_EFFECTS 0x20000000 #endif /* H2W */ //=================================================================== // // Whole model // typedef enum {mod_brush, mod_sprite, mod_alias} modtype_t; typedef struct qmodel_s { char name[MAX_QPATH]; unsigned int path_id; // path id of the game directory // that this model came from int needload; // bmodels and sprites don't cache normally modtype_t type; int flags; int numframes; // // volume occupied by the model graphics // vec3_t mins, maxs; float radius; // // brush model // int firstmodelsurface, nummodelsurfaces; int numsubmodels; dmodel_t *submodels; int numplanes; mplane_t *planes; int numleafs; // number of visible leafs, not counting 0 mleaf_t *leafs; int numvertexes; mvertex_t *vertexes; int numnodes; mnode_t *nodes; int numtexinfo; mtexinfo_t *texinfo; int numsurfaces; msurface_t *surfaces; int numclipnodes; mclipnode_t *clipnodes; hull_t hulls[MAX_MAP_HULLS]; byte *visdata; byte *lightdata; char *entities; } qmodel_t; // values for qmodel_t->needload #define NL_PRESENT 0 #define NL_NEEDS_LOADED 1 #define NL_UNREFERENCED 2 //============================================================================ void Mod_Init (void); void Mod_ClearAll (void); qmodel_t *Mod_ForName (const char *name, qboolean crash); qmodel_t *Mod_FindName (const char *name); mleaf_t *Mod_PointInLeaf (vec3_t p, qmodel_t *model); byte *Mod_LeafPVS (mleaf_t *leaf, qmodel_t *model); #endif /* SV_MODEL_H */ engine/h2shared/sys.h000066400000000000000000000111521444734033100150150ustar00rootroot00000000000000/* sys.h: non-portable functions * relies on: arch_def.h * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef HX2_SYS_H #define HX2_SYS_H /* file IO */ int Sys_mkdir (const char *path, qboolean crash); int Sys_rmdir (const char *path); int Sys_unlink (const char *path); int Sys_rename (const char *oldp, const char *newp); int Sys_FileType (const char *path); /* returns an FS entity type, i.e. FS_ENT_FILE or FS_ENT_DIRECTORY. * returns FS_ENT_NONE (0) if no such file or directory is present. */ long Sys_filesize (const char *path); int Sys_CopyFile (const char *frompath, const char *topath); const char *Sys_FindFirstFile (const char *path, const char *pattern); const char *Sys_FindNextFile (void); void Sys_FindClose (void); #if defined(PLATFORM_AMIGA) && !defined(SERVERONLY) qboolean Sys_PathExistsQuiet (const char *p); /* File existence check with the "Please insert volume XXX" * system requester disabled. */ #endif /* memory protection */ void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length); /* user directories */ /* disable user directories on platforms where they * are not necessary or not possible. */ #if defined(PLATFORM_DOS) || defined(PLATFORM_AMIGA) || \ defined(PLATFORM_WINDOWS) || defined(PLATFORM_OS2) #undef DO_USERDIRS #define DO_USERDIRS 0 #endif /* DO_USERDIRS */ /* system IO */ FUNC_NORETURN void Sys_Quit (void); FUNC_NORETURN void Sys_Error (const char *error, ...) FUNC_PRINTF(1,2); /* cause the entire program to exit */ #ifdef __WATCOMC__ #pragma aux Sys_Error aborts; #pragma aux Sys_Quit aborts; #endif void Sys_PrintTerm (const char *msgtxt); /* print the given string to the terminal */ double Sys_DoubleTime (void); char *Sys_DateTimeString (char *buf); /* returns date + time string equivalent to the combination * of following calls: char buf[20]; t = time(NULL); * strftime (buf, sizeof(buf), "%m/%d/%Y %H:%M:%S", localtime(&t)); */ const char *Sys_ConsoleInput (void); void Sys_Sleep (unsigned long msecs); /* yield for about 'msecs' milliseconds */ void Sys_SendKeyEvents (void); /* perform Key_Event () callbacks until the input que is empty */ char *Sys_GetClipboardData (void); /* x86 asm support */ #if defined(USE_INTEL_ASM) && \ (defined(__i386) || defined(__i386__) || defined(__386__) || defined(_M_IX86) || defined(__I386__)) # define id386 1 #else /* not i386 or no intel asm */ # define id386 0 #endif /* m68k asm support */ #if defined(USE_M68K_ASM) && \ (defined(__mc68000__) || defined(__M68K__) || defined(__m68k__) || defined(__MC68K__)) # define id68k 1 #else /* !m68k or no m68k asm */ # define id68k 0 #endif /* C-linkage for C-ASM shared global vars and functions */ #if id386 # if defined(__cplusplus) # define ASM_LINKAGE_BEGIN extern "C" { # define ASM_LINKAGE_END } # else # define ASM_LINKAGE_BEGIN # define ASM_LINKAGE_END # endif #elif id68k # if defined(__cplusplus) # define ASM_LINKAGE_BEGIN extern "C" { # define ASM_LINKAGE_END } # else # define ASM_LINKAGE_BEGIN # define ASM_LINKAGE_END # endif #else # define ASM_LINKAGE_BEGIN # define ASM_LINKAGE_END #endif #if id386 /* fpu stuff with x86 asm */ ASM_LINKAGE_BEGIN void MaskExceptions (void); void Sys_SetFPCW (void); void Sys_LowFPPrecision (void); void Sys_HighFPPrecision (void); void Sys_PopFPCW (void); void Sys_PushFPCW_SetHigh (void); ASM_LINKAGE_END #else #define MaskExceptions() do {} while (0) #define Sys_SetFPCW() do {} while (0) #define Sys_LowFPPrecision() do {} while (0) #define Sys_HighFPPrecision() do {} while (0) #define Sys_PopFPCW() do {} while (0) #define Sys_PushFPCW_SetHigh() do {} while (0) #endif /* set UNALIGNED_OK 0 if unaligned accesses are not supported */ #if defined(__i386) || defined(__i386__) || defined(__386__) || defined(_M_IX86) || defined(__I386__) # define UNALIGNED_OK 1 #else # define UNALIGNED_OK 0 #endif #endif /* HX2_SYS_H */ engine/h2shared/sys_dxe.h000066400000000000000000000020371444734033100156570ustar00rootroot00000000000000/* Dynamic module loading/unloading with DJGPP DXE3 * Copyright (C) 2015 Q2DOS developers. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef HX2_DXE_H #define HX2_DXE_H void Sys_InitDXE3 (void); void *Sys_dlopen (const char *filename, qboolean globalmode); int Sys_dlclose (void *handle); void *Sys_dlsym (void *handle, const char *symbol); #endif /* HX2_DXE_H */ engine/h2shared/sys_ia32.asm000066400000000000000000000040501444734033100161630ustar00rootroot00000000000000; ; sys_ia32.asm ; x86 assembly-language misc system routines. ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2 of the License, or (at ; your option) any later version. ; ; This program is distributed in the hope that it will be useful, but ; WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License along ; with this program; if not, write to the Free Software Foundation, Inc., ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: ; C-shared globals: _sym_prefix MaskExceptions _sym_prefix Sys_LowFPPrecision _sym_prefix Sys_HighFPPrecision _sym_prefix Sys_PushFPCW_SetHigh _sym_prefix Sys_PopFPCW _sym_prefix Sys_SetFPCW %endif ; _sym_prefix SEGMENT .data ALIGN 4 fpenv: dd 0, 0, 0, 0, 0, 0, 0, 0 SEGMENT .text global MaskExceptions MaskExceptions: fnstenv [fpenv] or dword [fpenv],03Fh fldenv [fpenv] ret SEGMENT .data ALIGN 4 global ceil_cw, single_cw, full_cw, cw, pushed_cw ceil_cw dd 0 single_cw dd 0 full_cw dd 0 cw dd 0 pushed_cw dd 0 SEGMENT .text global Sys_LowFPPrecision Sys_LowFPPrecision: fldcw word [single_cw] ret global Sys_HighFPPrecision Sys_HighFPPrecision: fldcw word [full_cw] ret global Sys_PushFPCW_SetHigh Sys_PushFPCW_SetHigh: fnstcw word [pushed_cw] fldcw word [full_cw] ret global Sys_PopFPCW Sys_PopFPCW: fldcw word [pushed_cw] ret global Sys_SetFPCW Sys_SetFPCW: fnstcw word [cw] mov eax, dword [cw] and ah,0F0h or ah,003h mov dword [full_cw],eax and ah,0F0h or ah,00Ch mov dword [single_cw],eax and ah,0F0h or ah,008h mov dword [ceil_cw],eax ret engine/h2shared/sys_osx.h000066400000000000000000000006451444734033100157130ustar00rootroot00000000000000/* Mac OS X specifics needed by common sys_unix.c : */ #ifndef SYS_OSX_H #define SYS_OSX_H int OSX_GetBasedir (char *argv0, char *dst, size_t dstsize); #define Sys_GetBasedir OSX_GetBasedir void Cocoa_ErrorMessage (const char *errorMsg); #define Sys_ErrorMessage Cocoa_ErrorMessage #define Sys_GetClipboardData Sys_GetClipboardData /* this is public, therefore not OSX_GetClipboardData */ #endif /* SYS_OSX_H */ engine/h2shared/sys_osx.m000066400000000000000000000072771444734033100157300ustar00rootroot00000000000000/* Mac OS X specific functions needed by the common sys_unix.c. * * Copyright (C) 2008-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" #include "sys_osx.h" #include "userdir.h" #include /* perror() */ #include /* dirname() and basename() */ #include /* getcwd() */ #import /* NSRunCriticalAlertPanel() */ static char *OSX_StripAppBundle (char *dir) { /* based on the ioquake3 project at icculus.org. */ static char osx_path[MAX_OSPATH]; q_strlcpy (osx_path, dir, sizeof(osx_path)); if (strcmp(basename(osx_path), "MacOS")) return dir; q_strlcpy (osx_path, dirname(osx_path), sizeof(osx_path)); if (strcmp(basename(osx_path), "Contents")) return dir; q_strlcpy (osx_path, dirname(osx_path), sizeof(osx_path)); if (!strstr(basename(osx_path), ".app")) return dir; q_strlcpy (osx_path, dirname(osx_path), sizeof(osx_path)); return osx_path; } int OSX_GetBasedir (char *argv0, char *dst, size_t dstsize) { char *tmp; if (realpath(argv0, dst) == NULL) { perror("realpath"); if (getcwd(dst, dstsize - 1) == NULL) return -1; } else { /* strip off the binary name */ tmp = strdup (dst); if (!tmp) return -1; q_strlcpy (dst, dirname(tmp), dstsize); free (tmp); } tmp = OSX_StripAppBundle(dst); if (tmp != dst) q_strlcpy (dst, tmp, dstsize); return 0; } #ifndef MAC_OS_X_VERSION_10_12 #define NSAlertStyleCritical NSCriticalAlertStyle #endif /* Display message from Sys_Error() on a window: */ void Cocoa_ErrorMessage (const char *errorMsg) { #if (MAC_OS_X_VERSION_MIN_REQUIRED < 1040) /* ppc builds targeting 10.3 and older */ NSString* msg = [NSString stringWithCString:errorMsg]; #else NSString* msg = [NSString stringWithCString:errorMsg encoding:NSASCIIStringEncoding]; #endif #if MAC_OS_X_VERSION_MIN_REQUIRED < 1030 NSRunCriticalAlertPanel (@"Hexen II Error", msg, @"OK", nil, nil); #else NSAlert *alert = [[[NSAlert alloc] init] autorelease]; alert.alertStyle = NSAlertStyleCritical; alert.messageText = @"Hexen II Error"; alert.informativeText = msg; [alert runModal]; #endif } #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 #define NSPasteboardTypeString NSStringPboardType #endif #define MAX_CLIPBOARDTXT MAXCMDLINE /* 256 */ char *Sys_GetClipboardData (void) { char *data = NULL; NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; NSArray* types = [pasteboard types]; if ([types containsObject: NSPasteboardTypeString]) { NSString* clipboardString = [pasteboard stringForType: NSPasteboardTypeString]; if (clipboardString != NULL && [clipboardString length] > 0) { size_t sz = [clipboardString length] + 1; sz = q_min(MAX_CLIPBOARDTXT, sz); data = (char *) Z_Malloc(sz, Z_MAINZONE); #if (MAC_OS_X_VERSION_MIN_REQUIRED < 1040) /* for ppc builds targeting 10.3 and older */ q_strlcpy (data, [clipboardString cString], sz); #else q_strlcpy (data, [clipboardString cStringUsingEncoding: NSASCIIStringEncoding], sz); #endif } } return data; } engine/h2shared/sys_sdl.c000066400000000000000000000020111444734033100156440ustar00rootroot00000000000000/* * Alternative SDL-using implementations of system functions needed by * the common sys_unix.c when platform specific ones are not available. * * Copyright (C) 2008-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "sdl_inc.h" #include "sys_sdl.h" /* NOTHING HERE YET. */ engine/h2shared/sys_sdl.h000066400000000000000000000003451444734033100156610ustar00rootroot00000000000000/* alternative SDL-using implementations of system functions needed by * common sys_unix.c, when platform specific ones are not available. */ #ifndef SYS_SDL_H #define SYS_SDL_H /* NOTHING HERE YET */ #endif /* SYS_SDL_H */ engine/h2shared/userdir.h000066400000000000000000000031501444734033100156530ustar00rootroot00000000000000/* userdir.h -- arch specific user directory definitions * * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __USERDIR_H #define __USERDIR_H #if defined(DEMOBUILD) /* use a different user directory for the demo version, * so that the demo and retail versions can co-exist on * the same machine peacefully */ #define SYS_USERDIR_OSX "Library/Application Support/Hexen2 Demo" #define SYS_USERDIR_UNIX ".hexen2demo" #define SYS_USERDIR_HAIKU "config/settings/hexen2demo" #else /* for retail version: */ #define SYS_USERDIR_OSX "Library/Application Support/Hexen2" #define SYS_USERDIR_UNIX ".hexen2" #define SYS_USERDIR_HAIKU "config/settings/hexen2" #endif #if defined(PLATFORM_OSX) #define AOT_USERDIR SYS_USERDIR_OSX #elif defined(PLATFORM_HAIKU) #define AOT_USERDIR SYS_USERDIR_HAIKU #else /* unix: */ #define AOT_USERDIR SYS_USERDIR_UNIX #endif #endif /* __USERDIR_H */ engine/h2shared/vgamodes.h000066400000000000000000000344041444734033100160110ustar00rootroot00000000000000/* vgamodes.h -- VGA mode set tables for vid_vga.c * from quake1 source with minor adaptations for uhexen2. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __VGAMODES_H__ #define __VGAMODES_H__ #include "vregset.h" /* the following base mode descriptors plus extra data together provide all * the data needed to do VGA mode sets */ typedef struct { int vidbuffer; const int *pregset; } vextra_t; static int vrsnull[] = { VRS_END }; static const int vrs320x200x256planar[] = { // // switch to linear, non-chain4 mode // VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 1, VRS_BYTE_OUT, SC_INDEX, MEMORY_MODE, VRS_BYTE_RMW, SC_DATA, ~0x08, 0x04, VRS_BYTE_OUT, GC_INDEX, GRAPHICS_MODE, VRS_BYTE_RMW, GC_DATA, ~0x13, 0x00, VRS_BYTE_OUT, GC_INDEX, MISCELLANOUS, VRS_BYTE_RMW, GC_DATA, ~0x02, 0x00, VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 3, // // change the CRTC from doubleword to byte mode // VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE, VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00, VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL, VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40, VRS_END }; static const int vrs360x200x256planar[] = { // // switch to linear, non-chain4 mode // VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 1, VRS_WORD_OUT, SC_INDEX, 0x0604, VRS_BYTE_OUT, MISC_OUTPUT, 0x67, VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 3, // // unprotect CRTC0 through CRTC0 // VRS_BYTE_OUT, CRTC_INDEX, 0x11, VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00, // // change the CRTC from doubleword to byte mode // VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE, VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00, VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL, VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40, // // set up the CRT Controller // VRS_WORD_OUT, CRTC_INDEX, 0x6B00, VRS_WORD_OUT, CRTC_INDEX, 0x5901, VRS_WORD_OUT, CRTC_INDEX, 0x5A02, VRS_WORD_OUT, CRTC_INDEX, 0x8E03, VRS_WORD_OUT, CRTC_INDEX, 0x5E04, VRS_WORD_OUT, CRTC_INDEX, 0x8A05, VRS_WORD_OUT, CRTC_INDEX, 0x3013, VRS_END }; static const int vrs320x240x256planar[] = { // // switch to linear, non-chain4 mode // VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 1, VRS_BYTE_OUT, SC_INDEX, MEMORY_MODE, VRS_BYTE_RMW, SC_DATA, ~0x08, 0x04, VRS_BYTE_OUT, GC_INDEX, GRAPHICS_MODE, VRS_BYTE_RMW, GC_DATA, ~0x13, 0x00, VRS_BYTE_OUT, GC_INDEX, MISCELLANOUS, VRS_BYTE_RMW, GC_DATA, ~0x02, 0x00, VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 3, // // unprotect CRTC0 through CRTC0 // VRS_BYTE_OUT, CRTC_INDEX, 0x11, VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00, // // set up the CRT Controller // VRS_WORD_OUT, CRTC_INDEX, 0x0D06, VRS_WORD_OUT, CRTC_INDEX, 0x3E07, VRS_WORD_OUT, CRTC_INDEX, 0x4109, VRS_WORD_OUT, CRTC_INDEX, 0xEA10, VRS_WORD_OUT, CRTC_INDEX, 0xAC11, VRS_WORD_OUT, CRTC_INDEX, 0xDF12, VRS_WORD_OUT, CRTC_INDEX, 0x0014, VRS_WORD_OUT, CRTC_INDEX, 0xE715, VRS_WORD_OUT, CRTC_INDEX, 0x0616, VRS_WORD_OUT, CRTC_INDEX, 0xE317, VRS_END }; static const int vrs360x240x256planar[] = { // // switch to linear, non-chain4 mode // VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 1, VRS_WORD_OUT, SC_INDEX, 0x0604, VRS_BYTE_OUT, MISC_OUTPUT, 0xE7, VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 3, // // unprotect CRTC0 through CRTC0 // VRS_BYTE_OUT, CRTC_INDEX, 0x11, VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00, // // set up the CRT Controller // VRS_WORD_OUT, CRTC_INDEX, 0x6B00, VRS_WORD_OUT, CRTC_INDEX, 0x5901, VRS_WORD_OUT, CRTC_INDEX, 0x5A02, VRS_WORD_OUT, CRTC_INDEX, 0x8E03, VRS_WORD_OUT, CRTC_INDEX, 0x5E04, VRS_WORD_OUT, CRTC_INDEX, 0x8A05, VRS_WORD_OUT, CRTC_INDEX, 0x0D06, VRS_WORD_OUT, CRTC_INDEX, 0x3E07, VRS_WORD_OUT, CRTC_INDEX, 0x4109, VRS_WORD_OUT, CRTC_INDEX, 0xEA10, VRS_WORD_OUT, CRTC_INDEX, 0xAC11, VRS_WORD_OUT, CRTC_INDEX, 0xDF12, VRS_WORD_OUT, CRTC_INDEX, 0x3013, VRS_WORD_OUT, CRTC_INDEX, 0x0014, VRS_WORD_OUT, CRTC_INDEX, 0xE715, VRS_WORD_OUT, CRTC_INDEX, 0x0616, VRS_WORD_OUT, CRTC_INDEX, 0xE317, VRS_END }; static const int vrs320x350x256planar[] = { // // switch to linear, non-chain4 mode // VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 1, VRS_BYTE_OUT, SC_INDEX, MEMORY_MODE, VRS_BYTE_RMW, SC_DATA, ~0x08, 0x04, VRS_BYTE_OUT, GC_INDEX, GRAPHICS_MODE, VRS_BYTE_RMW, GC_DATA, ~0x10, 0x00, VRS_BYTE_OUT, GC_INDEX, MISCELLANOUS, VRS_BYTE_RMW, GC_DATA, ~0x02, 0x00, VRS_BYTE_OUT, MISC_OUTPUT, 0xA3, // 350-scan-line scan rate VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 3, // // unprotect CRTC0 through CRTC0 // VRS_BYTE_OUT, CRTC_INDEX, 0x11, VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00, // // stop scanning each line twice // VRS_BYTE_OUT, CRTC_INDEX, MAX_SCAN_LINE, VRS_BYTE_RMW, CRTC_DATA, ~0x1F, 0x00, // // change the CRTC from doubleword to byte mode // VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE, VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00, VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL, VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40, // // set the vertical counts for 350-scan-line mode // VRS_WORD_OUT, CRTC_INDEX, 0xBF06, VRS_WORD_OUT, CRTC_INDEX, 0x1F07, VRS_WORD_OUT, CRTC_INDEX, 0x8310, VRS_WORD_OUT, CRTC_INDEX, 0x8511, VRS_WORD_OUT, CRTC_INDEX, 0x5D12, VRS_WORD_OUT, CRTC_INDEX, 0x6315, VRS_WORD_OUT, CRTC_INDEX, 0xBA16, VRS_END }; static const int vrs360x350x256planar[] = { // // switch to linear, non-chain4 mode // VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 1, VRS_WORD_OUT, SC_INDEX, 0x0604, VRS_BYTE_OUT, MISC_OUTPUT, 0xA7, // 350-scan-line scan rate VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 3, // // unprotect CRTC0 through CRTC0 // VRS_BYTE_OUT, CRTC_INDEX, 0x11, VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00, // // stop scanning each line twice // VRS_BYTE_OUT, CRTC_INDEX, MAX_SCAN_LINE, VRS_BYTE_RMW, CRTC_DATA, ~0x1F, 0x00, // // change the CRTC from doubleword to byte mode // VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE, VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00, VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL, VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40, // // set the vertical counts for 350-scan-line mode and 360 pixels across // VRS_WORD_OUT, CRTC_INDEX, 0x6B00, VRS_WORD_OUT, CRTC_INDEX, 0x5901, VRS_WORD_OUT, CRTC_INDEX, 0x5A02, VRS_WORD_OUT, CRTC_INDEX, 0x8E03, VRS_WORD_OUT, CRTC_INDEX, 0x5E04, VRS_WORD_OUT, CRTC_INDEX, 0x8A05, VRS_WORD_OUT, CRTC_INDEX, 0xBF06, VRS_WORD_OUT, CRTC_INDEX, 0x1F07, VRS_WORD_OUT, CRTC_INDEX, 0x8310, VRS_WORD_OUT, CRTC_INDEX, 0x8511, VRS_WORD_OUT, CRTC_INDEX, 0x5D12, VRS_WORD_OUT, CRTC_INDEX, 0x3013, VRS_WORD_OUT, CRTC_INDEX, 0x6315, VRS_WORD_OUT, CRTC_INDEX, 0xBA16, VRS_END }; static const int vrs320x400x256planar[] = { // // switch to linear, non-chain4 mode // VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 1, VRS_BYTE_OUT, SC_INDEX, MEMORY_MODE, VRS_BYTE_RMW, SC_DATA, ~0x08, 0x04, VRS_BYTE_OUT, GC_INDEX, GRAPHICS_MODE, VRS_BYTE_RMW, GC_DATA, ~0x10, 0x00, VRS_BYTE_OUT, GC_INDEX, MISCELLANOUS, VRS_BYTE_RMW, GC_DATA, ~0x02, 0x00, VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 3, // // stop scanning each line twice // VRS_BYTE_OUT, CRTC_INDEX, MAX_SCAN_LINE, VRS_BYTE_RMW, CRTC_DATA, ~0x1F, 0x00, // // change the CRTC from doubleword to byte mode // VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE, VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00, VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL, VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40, VRS_END }; static const int vrs360x400x256planar[] = { // // switch to linear, non-chain4 mode // VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 1, VRS_WORD_OUT, SC_INDEX, 0x0604, VRS_BYTE_OUT, MISC_OUTPUT, 0x67, VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 3, // // unprotect CRTC0 through CRTC0 // VRS_BYTE_OUT, CRTC_INDEX, 0x11, VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00, // // stop scanning each line twice // VRS_BYTE_OUT, CRTC_INDEX, MAX_SCAN_LINE, VRS_BYTE_RMW, CRTC_DATA, ~0x1F, 0x00, // // change the CRTC from doubleword to byte mode // VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE, VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00, VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL, VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40, // // set up the CRT Controller // VRS_WORD_OUT, CRTC_INDEX, 0x6B00, VRS_WORD_OUT, CRTC_INDEX, 0x5901, VRS_WORD_OUT, CRTC_INDEX, 0x5A02, VRS_WORD_OUT, CRTC_INDEX, 0x8E03, VRS_WORD_OUT, CRTC_INDEX, 0x5E04, VRS_WORD_OUT, CRTC_INDEX, 0x8A05, VRS_WORD_OUT, CRTC_INDEX, 0x3013, VRS_END }; static const int vrs320x480x256planar[] = { // // switch to linear, non-chain4 mode // VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 1, VRS_BYTE_OUT, SC_INDEX, MEMORY_MODE, VRS_BYTE_RMW, SC_DATA, ~0x08, 0x04, VRS_BYTE_OUT, GC_INDEX, GRAPHICS_MODE, VRS_BYTE_RMW, GC_DATA, ~0x10, 0x00, VRS_BYTE_OUT, GC_INDEX, MISCELLANOUS, VRS_BYTE_RMW, GC_DATA, ~0x02, 0x00, VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 3, // // unprotect CRTC0 through CRTC0 // VRS_BYTE_OUT, CRTC_INDEX, 0x11, VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00, // // stop scanning each line twice // VRS_BYTE_OUT, CRTC_INDEX, MAX_SCAN_LINE, VRS_BYTE_RMW, CRTC_DATA, ~0x1F, 0x00, // // change the CRTC from doubleword to byte mode // VRS_BYTE_OUT, CRTC_INDEX, UNDERLINE, VRS_BYTE_RMW, CRTC_DATA, ~0x40, 0x00, VRS_BYTE_OUT, CRTC_INDEX, MODE_CONTROL, VRS_BYTE_RMW, CRTC_DATA, ~0x00, 0x40, // // set up the CRT Controller // VRS_WORD_OUT, CRTC_INDEX, 0x0D06, VRS_WORD_OUT, CRTC_INDEX, 0x3E07, VRS_WORD_OUT, CRTC_INDEX, 0xEA10, VRS_WORD_OUT, CRTC_INDEX, 0xAC11, VRS_WORD_OUT, CRTC_INDEX, 0xDF12, VRS_WORD_OUT, CRTC_INDEX, 0xE715, VRS_WORD_OUT, CRTC_INDEX, 0x0616, VRS_END }; static const int vrs360x480x256planar[] = { // // switch to linear, non-chain4 mode // VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 1, VRS_WORD_OUT, SC_INDEX, 0x0604, VRS_BYTE_OUT, MISC_OUTPUT, 0xE7, VRS_BYTE_OUT, SC_INDEX, SYNC_RESET, VRS_BYTE_OUT, SC_DATA, 3, // // unprotect CRTC0 through CRTC0 // VRS_BYTE_OUT, CRTC_INDEX, 0x11, VRS_BYTE_RMW, CRTC_DATA, ~0x80, 0x00, // // set up the CRT Controller // VRS_WORD_OUT, CRTC_INDEX, 0x6B00, VRS_WORD_OUT, CRTC_INDEX, 0x5901, VRS_WORD_OUT, CRTC_INDEX, 0x5A02, VRS_WORD_OUT, CRTC_INDEX, 0x8E03, VRS_WORD_OUT, CRTC_INDEX, 0x5E04, VRS_WORD_OUT, CRTC_INDEX, 0x8A05, VRS_WORD_OUT, CRTC_INDEX, 0x0D06, VRS_WORD_OUT, CRTC_INDEX, 0x3E07, VRS_WORD_OUT, CRTC_INDEX, 0x4009, VRS_WORD_OUT, CRTC_INDEX, 0xEA10, VRS_WORD_OUT, CRTC_INDEX, 0xAC11, VRS_WORD_OUT, CRTC_INDEX, 0xDF12, VRS_WORD_OUT, CRTC_INDEX, 0x3013, VRS_WORD_OUT, CRTC_INDEX, 0x0014, VRS_WORD_OUT, CRTC_INDEX, 0xE715, VRS_WORD_OUT, CRTC_INDEX, 0x0616, VRS_WORD_OUT, CRTC_INDEX, 0xE317, VRS_END }; // // extra VGA-specific data for vgavidmodes // static vextra_t extra320x200x256linear = { 1, vrsnull }; static vextra_t extra320x200x256planar = { 1, vrs320x200x256planar }; static vextra_t extra360x200x256planar = { 1, vrs360x200x256planar }; static vextra_t extra320x240x256planar = { 1, vrs320x240x256planar }; static vextra_t extra360x240x256planar = { 1, vrs360x240x256planar }; static vextra_t extra320x350x256planar = { 1, vrs320x350x256planar }; static vextra_t extra360x350x256planar = { 1, vrs360x350x256planar }; static vextra_t extra320x400x256planar = { 1, vrs320x400x256planar }; static vextra_t extra360x400x256planar = { 1, vrs360x400x256planar }; static vextra_t extra320x480x256planar = { 1, vrs320x480x256planar }; static vextra_t extra360x480x256planar = { 1, vrs360x480x256planar }; // // base mode descriptors, in ascending order of number of pixels // static vmode_t vgavidmodes[] = { { NULL, "320x200", " ***** standard VGA modes ***** ", 320, 200, (200.0/320.0)*(320.0/240.0), 320, 0, 1, &extra320x200x256linear, VGA_InitMode, VGA_SwapBuffers, VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect }, { NULL, "320x200", " ***** Mode X-style modes ***** ", 320, 200, (200.0/320.0)*(320.0/240.0), 320, 1, 1, &extra320x200x256planar, VGA_InitMode, VGA_SwapBuffers, VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect }, { NULL, "360x200", NULL, 360, 200, (200.0/360.0)*(320.0/240.0), 384, 1, 1, &extra360x200x256planar, VGA_InitMode, VGA_SwapBuffers, VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect }, { NULL, "320x240", NULL, 320, 240, (240.0/320.0)*(320.0/240.0), 320, 1, 1, &extra320x240x256planar, VGA_InitMode, VGA_SwapBuffers, VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect }, { NULL, "360x240", NULL, 360, 240, (240.0/360.0)*(320.0/240.0), 384, 1, 1, &extra360x240x256planar, VGA_InitMode, VGA_SwapBuffers, VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect }, { NULL, "320x350", NULL, 320, 350, (350.0/320.0)*(320.0/240.0), 320, 1, 1, &extra320x350x256planar, VGA_InitMode, VGA_SwapBuffers, VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect }, { NULL, "360x350", NULL, 360, 350, (350.0/360.0)*(320.0/240.0), 384, 1, 1, &extra360x350x256planar, VGA_InitMode, VGA_SwapBuffers, VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect }, { NULL, "320x400", NULL, 320, 400, (400.0/320.0)*(320.0/240.0), 320, 1, 1, &extra320x400x256planar, VGA_InitMode, VGA_SwapBuffers, VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect }, { NULL, "360x400", NULL, 360, 400, (400.0/360.0)*(320.0/240.0), 384, 1, 1, &extra360x400x256planar, VGA_InitMode, VGA_SwapBuffers, VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect }, { NULL, "320x480", NULL, 320, 480, (480.0/320.0)*(320.0/240.0), 320, 1, 1, &extra320x480x256planar, VGA_InitMode, VGA_SwapBuffers, VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect }, { NULL, "360x480", NULL, 360, 480, (480.0/360.0)*(320.0/240.0), 384, 1, 1, &extra360x480x256planar, VGA_InitMode, VGA_SwapBuffers, VGA_SetPalette, VGA_BeginDirectRect, VGA_EndDirectRect }, }; #endif /* __VGAMODES_H__ */ engine/h2shared/vid.h000066400000000000000000000102401444734033100147560ustar00rootroot00000000000000/* * vid.h -- video driver defs * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __VID_DEFS_H #define __VID_DEFS_H #define VID_CBITS 6 #define VID_GRADES (1 << VID_CBITS) #define GAMMA_MAX 3.0 // a pixel can be one, two, or four bytes typedef byte pixel_t; typedef struct vrect_s { int x, y, width, height; struct vrect_s *pnext; } vrect_t; typedef enum { MS_WINDOWED, MS_FULLSCREEN, MS_FULLDIB, MS_FULLDIRECT, MS_UNINIT } modestate_t; #define MODE_WINDOWED 0 #define NO_MODE (MODE_WINDOWED - 1) #define MODE_FULLSCREEN_DEFAULT (MODE_WINDOWED + 3) #define MODE_SETTABLE_WINDOW 2 typedef struct { pixel_t *buffer; // invisible buffer pixel_t *colormap; // 256 * VID_GRADES size unsigned short *colormap16; // 256 * VID_GRADES size int fullbright; // index of first fullbright color int rowbytes; // may be > width if displayed in a window int width; int height; float aspect; // width / height -- < 0 is taller than wide int numpages; int recalc_refdef; // if true, recalc vid-based stuff pixel_t *conbuffer; int conrowbytes; int conwidth; int conheight; int maxwarpwidth; int maxwarpheight; pixel_t *direct; // direct drawing to framebuffer, // if not NULL #if defined(PLATFORM_AMIGAOS3) && !defined(GLQUAKE) qboolean noadapt; // no fov_adapt for Amiga native chipset modes #endif } viddef_t; extern byte globalcolormap[VID_GRADES*256]; extern byte lastglobalcolor, *lastsourcecolormap; extern viddef_t vid; // global video state extern modestate_t modestate; extern qboolean in_mode_set; extern unsigned short d_8to16table[256]; extern unsigned int d_8to24table[256]; extern unsigned int d_8to24TranslucentTable[256]; extern cvar_t _enable_mouse; void VID_SetPalette (const unsigned char *palette); // called at startup and after any gamma correction void VID_ShiftPalette (const unsigned char *palette); // called for bonus and pain flashes, and for underwater color changes // in gl mode, used to update hardware gamma. void VID_Init (const unsigned char *palette); // Called at startup to set up translation tables, takes 256 8 bit RGB values // the palette data will go away after the call, so it must be copied off if // the video driver will need it again void VID_Shutdown (void); // Called at shutdown void VID_Update (vrect_t *rects); // flushes the given rectangles from the view buffer to the screen void VID_LockBuffer (void); void VID_UnlockBuffer (void); // video buffer locking. some drivers don't need it. void VID_HandlePause (qboolean paused); // releases the mouse when pause happens void VID_ToggleFullscreen (void); // from Steven // toggles between windowed/fullscreen modes. for unix/sdl #if defined(GLQUAKE) void VID_ChangeConsize (int dir); // changes effective console size. callback for the opengl features menu float VID_ReportConsize(void); // returns console size scale for the opengl features menu. #endif /* ! GLQUAKE */ void D_ShowLoadingSize (void); // displays progress bars while loading a map. (not used in hexenworld.) extern void (*vid_menudrawfn)(void); extern void (*vid_menukeyfn)(int key); // video menu function pointers #if defined(SDLQUAKE) qboolean VID_HasMouseOrInputFocus (void); qboolean VID_IsMinimized (void); #endif #if !defined(PLATFORM_WINDOWS) #define msg_suppress_1 false #else extern qboolean msg_suppress_1; // suppresses resolution and cache size console output a fullscreen // DIB focus gain/loss. used by the Win32 software (MGL) renderer. #endif #endif /* __VID_DEFS_H */ engine/h2shared/vid_cgx.c000066400000000000000000001045111444734033100156170ustar00rootroot00000000000000/* * vid_cgx.c -- CyberGraphX video driver for AmigaOS & variants. * Select window size and mode and init CGX in SOFTWARE mode. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2004-2005 Steven Atkinson * Copyright (C) 2005-2016 O.Sezer * Copyright (C) 2012-2016 Szilard Biro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include /* IPTR */ /* WriteLUTPixelArray not included in vbcc_target_m68k-amigaos.lha */ #if defined(__VBCC__) && defined(__M68K__) && !defined(WriteLUTPixelArray) ULONG __WriteLUTPixelArray(__reg("a6") void *, __reg("a0") APTR srcRect, __reg("d0") UWORD SrcX, __reg("d1") UWORD SrcY, __reg("d2") UWORD SrcMod, __reg("a1") struct RastPort * a1arg, __reg("a2") APTR a2arg, __reg("d3") UWORD DestX, __reg("d4") UWORD DestY, __reg("d5") UWORD SizeX, __reg("d6") UWORD SizeY, __reg("d7") UBYTE CTFormat)="\tjsr\t-198(a6)"; #define WriteLUTPixelArray(srcRect, SrcX, SrcY, SrcMod, a1arg, a2arg, DestX, DestY, SizeX, SizeY, CTFormat) __WriteLUTPixelArray(CyberGfxBase, (srcRect), (SrcX), (SrcY), (SrcMod), (a1arg), (a2arg), (DestX), (DestY), (SizeX), (SizeY), (CTFormat)) #endif #include "quakedef.h" #include "d_local.h" #include "cfgfile.h" #include "bgmusic.h" #include "cdaudio.h" #define MIN_WIDTH 320 #define MIN_HEIGHT 240 #define MAX_DESC 33 /* - CGX ----------------------------------- */ struct Window *window = NULL; /* used by in_amiga.c */ static struct Screen *screen = NULL; static ULONG spal[1 + (256 * 3) + 1]; static unsigned char ppal[256 * 4]; static pixel_t *buffer = NULL; static byte *directbitmap = NULL; #ifdef __CLIB2__ struct GfxBase *GfxBase = NULL; #endif #ifdef PLATFORM_AMIGAOS3 struct Library *CyberGfxBase = NULL; #ifdef USE_C2P static qboolean use_c2p = false; static int currentBitMap; static struct ScreenBuffer *sbuf[2]; typedef void (*c2p_write_bm_func)(REG(d0, WORD chunkyx), REG(d1, WORD chunkyy), REG(d2, WORD offsx), REG(d3, WORD offsy), REG(a0, APTR chunkyscreen), REG(a1, struct BitMap *bitmap)); static c2p_write_bm_func c2p_write_bm; ASM_LINKAGE_BEGIN extern void c2p1x1_8_c5_030_smcinit(REG(d0, WORD chunkyx), REG(d1, WORD chunkyy), REG(d3, WORD scroffsy), REG(d5, LONG bplsize)); extern void c2p1x1_8_c5_030(REG(a0, APTR c2pscreen), REG(a1, APTR bitplanes)); extern void c2p1x1_8_c5_040_init(REG(d0, WORD chunkyx), REG(d1, WORD chunkyy), REG(d3, WORD scroffsy), REG(d5, LONG bplsize)); extern void c2p1x1_8_c5_040(REG(a0, APTR c2pscreen), REG(a1, APTR bitplanes)); extern void c2p1x1_8_c5_bm(REG(d0, WORD chunkyx), REG(d1, WORD chunkyy), REG(d2, WORD offsx), REG(d3, WORD offsy), REG(a0, APTR chunkyscreen), REG(a1, struct BitMap *bitmap)); extern void c2p1x1_8_c5_bm_040(REG(d0, WORD chunkyx), REG(d1, WORD chunkyy), REG(d2, WORD offsx), REG(d3, WORD offsy), REG(a0, APTR chunkyscreen), REG(a1, struct BitMap *bitmap)); ASM_LINKAGE_END #endif /* USE_C2P */ #endif /* PLATFORM_AMIGAOS3 */ /* ----------------------------------------- */ unsigned short d_8to16table[256]; unsigned int d_8to24table[256]; byte globalcolormap[VID_GRADES*256], lastglobalcolor = 0; byte *lastsourcecolormap = NULL; qboolean in_mode_set; static int enable_mouse; static qboolean palette_changed; static int num_fmodes; static int num_wmodes; static int *nummodes; static qboolean vid_menu_fs; //static qboolean fs_toggle_works = false; viddef_t vid; // global video state // cvar vid_mode must be set before calling VID_SetMode, VID_ChangeVideoMode or VID_Restart_f static cvar_t vid_mode = {"vid_mode", "0", CVAR_NONE}; static cvar_t vid_config_glx = {"vid_config_glx", "640", CVAR_ARCHIVE}; static cvar_t vid_config_gly = {"vid_config_gly", "480", CVAR_ARCHIVE}; static cvar_t vid_config_swx = {"vid_config_swx", "320", CVAR_ARCHIVE}; static cvar_t vid_config_swy = {"vid_config_swy", "240", CVAR_ARCHIVE}; #ifdef PLATFORM_AMIGAOS3 static cvar_t vid_config_fscr= {"vid_config_fscr", "1", CVAR_ARCHIVE}; #else static cvar_t vid_config_fscr= {"vid_config_fscr", "0", CVAR_ARCHIVE}; #endif static cvar_t vid_config_mon = {"vid_config_mon", "0", CVAR_ARCHIVE}; static cvar_t vid_showload = {"vid_showload", "1", CVAR_NONE}; cvar_t _enable_mouse = {"_enable_mouse", "1", CVAR_ARCHIVE}; static int vid_default = -1; // modenum of 320x240 as a safe default static int vid_modenum = -1; // current video mode, set after mode setting succeeds static int vid_maxwidth = 640, vid_maxheight = 480; modestate_t modestate = MS_UNINIT; static byte *vid_surfcache; static int vid_surfcachesize; static int VID_highhunkmark; typedef struct { modestate_t type; int width; int height; int modenum; int fullscreen; int bpp; /* int halfscreen;*/ ULONG modeid; #ifdef PLATFORM_AMIGAOS3 qboolean noadapt; #endif char modedesc[MAX_DESC]; } vmode_t; typedef struct { int width; int height; } stdmode_t; #define RES_640X480 3 static const stdmode_t std_modes[] = { // NOTE: keep this list in order {320, 240}, // 0 {400, 300}, // 1 {512, 384}, // 2 {640, 480}, // 3 == RES_640X480, this is our default, below // this is the lowresmodes region. // either do not change its order, // or change the above define, too {800, 600}, // 4, RES_640X480 + 1 {1024, 768} // 5, RES_640X480 + 2 }; #define MAX_MODE_LIST 64 #define MAX_STDMODES (sizeof(std_modes) / sizeof(std_modes[0])) #define NUM_LOWRESMODES (RES_640X480) static vmode_t fmodelist[MAX_MODE_LIST+1]; // list of enumerated fullscreen modes static vmode_t wmodelist[MAX_STDMODES +1]; // list of standart 4:3 windowed modes static vmode_t *modelist; // modelist in use, points to one of the above lists static qboolean VID_SetMode (int modenum, const unsigned char *palette); static void VID_MenuDraw (void); static void VID_MenuKey (int key); // window manager stuff #if defined(H2W) # define WM_TITLEBAR_TEXT "HexenWorld" #else # define WM_TITLEBAR_TEXT "Hexen II" #endif //==================================== /* ================ ClearAllStates ================ */ static void ClearAllStates (void) { Key_ClearStates (); IN_ClearStates (); } /* ================ VID_AllocBuffers ================ */ static qboolean VID_AllocBuffers (int width, int height) { int tsize, tbuffersize; tbuffersize = width * height * sizeof (*d_pzbuffer); tsize = D_SurfaceCacheForRes (width, height); tbuffersize += tsize; // see if there's enough memory, allowing for the normal mode 0x13 pixel, // z, and surface buffers //if ((host_parms->memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + // 0x10000 * 3) < MINIMUM_MEMORY) // Pa3PyX: using hopefully better estimation now // if total memory < needed surface cache + (minimum operational memory // less surface cache for 320x200 and typical hunk state after init) if (host_parms->memsize < tbuffersize + 0x180000 + 0xC00000) { Con_SafePrintf ("Not enough memory for video mode\n"); return false; // not enough memory for mode } vid_surfcachesize = tsize; if (d_pzbuffer) { D_FlushCaches (); Hunk_FreeToHighMark (VID_highhunkmark); d_pzbuffer = NULL; } VID_highhunkmark = Hunk_HighMark (); d_pzbuffer = (short *) Hunk_HighAllocName (tbuffersize, "video"); vid_surfcache = (byte *)d_pzbuffer + width * height * sizeof (*d_pzbuffer); return true; } /* ================ VID_CheckAdequateMem ================ */ static qboolean VID_CheckAdequateMem (int width, int height) { int tbuffersize; tbuffersize = width * height * sizeof (*d_pzbuffer); tbuffersize += D_SurfaceCacheForRes (width, height); // see if there's enough memory, allowing for the normal mode 0x13 pixel, // z, and surface buffers //if ((host_parms->memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + // 0x10000 * 3) < MINIMUM_MEMORY) // Pa3PyX: using hopefully better estimation now // Experimentation: the heap should have at least 12.0 megs // remaining (after init) after setting video mode, otherwise // it's Hunk_Alloc failures and cache thrashes upon level load if (host_parms->memsize < tbuffersize + 0x180000 + 0xC00000) { return false; // not enough memory for mode } return true; } //------------------------------------ static int sort_modes (const void *arg1, const void *arg2) { const vmode_t *a1, *a2; a1 = (const vmode_t *) arg1; a2 = (const vmode_t *) arg2; if (a1->width == a2->width) return a1->height - a2->height; // lowres-to-highres // return a2->height - a1->height; // highres-to-lowres else return a1->width - a2->width; // lowres-to-highres // return a2->width - a1->width; // highres-to-lowres } static void VID_PrepareModes (void) { qboolean have_mem; ULONG id; unsigned int i; APTR handle; struct DimensionInfo diminfo; #ifdef PLATFORM_AMIGAOS3 ULONG monitorid; struct DisplayInfo dispinfo; struct NameInfo nameinfo; #endif num_fmodes = 0; num_wmodes = 0; // standard 4:3 windowed modes for (i = 0; i < (int)MAX_STDMODES; i++) { have_mem = VID_CheckAdequateMem(std_modes[i].width, std_modes[i].height); if (!have_mem) break; wmodelist[num_wmodes].width = std_modes[i].width; wmodelist[num_wmodes].height = std_modes[i].height; wmodelist[num_wmodes].fullscreen = 0; wmodelist[num_wmodes].bpp = 8; wmodelist[num_wmodes].modeid = INVALID_ID; #ifdef PLATFORM_AMIGAOS3 wmodelist[num_wmodes].noadapt = false; #endif q_snprintf (wmodelist[num_wmodes].modedesc, MAX_DESC, "%d x %d", std_modes[i].width, std_modes[i].height); num_wmodes++; } // fullscreen modes id = INVALID_ID; while((id = NextDisplayInfo(id)) != INVALID_ID) { #ifdef PLATFORM_AMIGAOS3 //if (!IsCyberModeID(id)) continue; monitorid = id & MONITOR_ID_MASK; if (monitorid == DEFAULT_MONITOR_ID || monitorid == A2024_MONITOR_ID) continue; #endif handle = FindDisplayInfo(id); if (!handle) continue; #ifdef PLATFORM_AMIGAOS3 if (!GetDisplayInfoData(handle, (UBYTE *)&dispinfo, sizeof(dispinfo), DTAG_DISP, 0)) continue; // this is a good way to filter out HAM, EHB, DPF modes if (!GetDisplayInfoData(handle, (UBYTE *)&nameinfo, sizeof(nameinfo), DTAG_NAME, 0)) continue; //Con_SafePrintf ("modeid %08x name %s\n", id, nameinfo.Name); #endif if (!GetDisplayInfoData(handle, (UBYTE *)&diminfo, sizeof(diminfo), DTAG_DIMS, 0)) continue; #ifdef __AROS__ if (diminfo.MaxDepth != 24 || diminfo.Nominal.MaxX + 1 < MIN_WIDTH) continue; #else if (diminfo.MaxDepth != 8 || diminfo.Nominal.MaxX + 1 < MIN_WIDTH) continue; #endif fmodelist[num_fmodes].width = diminfo.Nominal.MaxX + 1; fmodelist[num_fmodes].height = diminfo.Nominal.MaxY + 1; #ifdef PLATFORM_AMIGAOS3 // round down PAL resolutions to the nearest multiple of 240 if (fmodelist[num_fmodes].height % 256 == 0) fmodelist[num_fmodes].height -= fmodelist[num_fmodes].height % MIN_HEIGHT; #endif fmodelist[num_fmodes].fullscreen = 1; fmodelist[num_fmodes].bpp = 8; // diminfo.MaxDepth fmodelist[num_fmodes].modeid = id; q_snprintf (fmodelist[num_fmodes].modedesc, MAX_DESC, "%d x %d", (fmodelist[num_fmodes].width), (fmodelist[num_fmodes].height)); #ifdef PLATFORM_AMIGAOS3 if (dispinfo.PropertyFlags & DIPF_IS_LACE) q_strlcat(fmodelist[num_fmodes].modedesc, "i", MAX_DESC); if (monitorid == PAL_MONITOR_ID) q_strlcat(fmodelist[num_fmodes].modedesc, " PAL", MAX_DESC); else if (monitorid == NTSC_MONITOR_ID) q_strlcat(fmodelist[num_fmodes].modedesc, " NTSC", MAX_DESC); else if (monitorid == DBLPAL_MONITOR_ID) q_strlcat(fmodelist[num_fmodes].modedesc, " DblPAL", MAX_DESC); else if (monitorid == DBLNTSC_MONITOR_ID) q_strlcat(fmodelist[num_fmodes].modedesc, " DblNTSC", MAX_DESC); else if (monitorid == EURO36_MONITOR_ID) q_strlcat(fmodelist[num_fmodes].modedesc, " Euro36", MAX_DESC); else if (monitorid == EURO72_MONITOR_ID) q_strlcat(fmodelist[num_fmodes].modedesc, " Euro72", MAX_DESC); else if (monitorid == SUPER72_MONITOR_ID) q_strlcat(fmodelist[num_fmodes].modedesc, " Super72", MAX_DESC); else if (monitorid == VGA_MONITOR_ID) q_strlcat(fmodelist[num_fmodes].modedesc, " VGA", MAX_DESC); else q_strlcat(fmodelist[num_fmodes].modedesc, " RTG", MAX_DESC); fmodelist[num_fmodes].noadapt = (!CyberGfxBase || !IsCyberModeID(id)); #endif //Con_SafePrintf ("fmodelist[%d].modedesc = %s maxdepth %d id %08x\n", num_fmodes, fmodelist[num_fmodes].modedesc, diminfo.MaxDepth, id); if (++num_fmodes == MAX_MODE_LIST) break; } if (num_fmodes == 0) { Con_SafePrintf ("No fullscreen video modes available\n"); if (num_wmodes > RES_640X480) num_wmodes = RES_640X480 + 1; modelist = wmodelist; nummodes = &num_wmodes; vid_default = 0; Cvar_SetValueQuick (&vid_config_swx, modelist[vid_default].width); Cvar_SetValueQuick (&vid_config_swy, modelist[vid_default].height); return; } if (num_fmodes > 1) qsort(fmodelist, num_fmodes, sizeof(vmode_t), sort_modes); nummodes = &num_fmodes; modelist = fmodelist; /*vid_maxwidth = fmodelist[num_fmodes-1].width; vid_maxheight = fmodelist[num_fmodes-1].height;*/ if ((screen = LockPubScreen(NULL))) { vid_maxwidth = screen->Width; vid_maxheight = screen->Height; UnlockPubScreen(NULL, screen); screen = NULL; } // see if we have 320x240 among the available modes for (i = 0; i < num_fmodes; i++) { if (fmodelist[i].width == 320 && fmodelist[i].height == 240) { vid_default = i; break; } } if (vid_default < 0) { // 320x240 not found among the supported dimensions // set default to the lowest resolution reported vid_default = 0; } // limit the windowed (standard) modes list to desktop dimensions for (i = 0; i < num_wmodes; i++) { if (wmodelist[i].width > vid_maxwidth || wmodelist[i].height > vid_maxheight) break; } if (i < num_wmodes) num_wmodes = i; Cvar_SetValueQuick (&vid_config_swx, modelist[vid_default].width); Cvar_SetValueQuick (&vid_config_swy, modelist[vid_default].height); } static void VID_ListModes_f (void) { int i; Con_Printf ("Maximum allowed mode: %d x %d\n", vid_maxwidth, vid_maxheight); Con_Printf ("Windowed modes enabled:\n"); for (i = 0; i < num_wmodes; i++) Con_Printf ("%2d: %d x %d\n", i, wmodelist[i].width, wmodelist[i].height); Con_Printf ("Fullscreen modes enumerated:"); if (num_fmodes) { Con_Printf ("\n"); for (i = 0; i < num_fmodes; i++) Con_Printf ("%2d: %d x %d\n", i, fmodelist[i].width, fmodelist[i].height); } else { Con_Printf (" None\n"); } } static void VID_NumModes_f (void) { Con_Printf ("%d video modes in current list\n", *nummodes); } //==================================== static void VID_DestroyWindow (void) { extern cvar_t v_gamma; // always call VID_ShiftPalette after mode changes v_gamma.flags |= CVAR_CHANGED; if (window) { CloseWindow(window); window = NULL; } if (buffer) { free(buffer); buffer = NULL; } /*if (pointermem) { FreeVec(pointermem); pointermem = NULL; }*/ #if defined(PLATFORM_AMIGAOS3) && defined(USE_C2P) use_c2p = false; if (sbuf[0]) { FreeScreenBuffer(screen, sbuf[0]); sbuf[0] = NULL; } if (sbuf[1]) { FreeScreenBuffer(screen, sbuf[1]); sbuf[1] = NULL; } #endif if (screen) { CloseScreen(screen); screen = NULL; } } static qboolean VID_SetMode (int modenum, const unsigned char *palette) { ULONG flags; qboolean eightbit = false; in_mode_set = true; VID_DestroyWindow (); if (!vid_config_fscr.integer && (screen = LockPubScreen(NULL))) { eightbit = (GetBitMapAttr(screen->RastPort.BitMap, BMA_DEPTH) <= 8); UnlockPubScreen(NULL, screen); screen = NULL; if (eightbit) { struct EasyStruct es; es.es_StructSize = sizeof(es); es.es_Flags = 0; es.es_Title = (STRPTR) ENGINE_NAME; es.es_TextFormat = (STRPTR) "Windowed mode requires 15 bit or higher color depth screen\nFalling back to fullscreen."; es.es_GadgetFormat = (STRPTR) "OK"; EasyRequest(0, &es, 0, 0); Cvar_SetQuick (&vid_config_fscr, "1"); } } flags = WFLG_ACTIVATE | WFLG_RMBTRAP; if (vid_config_fscr.integer) { ULONG ModeID; #if defined(PLATFORM_AMIGAOS3) && defined(USE_C2P) struct BitMap *bm; #endif struct TagItem vctl[] = { {VTAG_BORDERBLANK_SET, TRUE}, {VC_IntermediateCLUpdate, FALSE}, {VTAG_END_CM, 0} }; /*ModeID = BestCModeIDTags( CYBRBIDTG_Depth, 8, CYBRBIDTG_NominalWidth, modelist[modenum].width, CYBRBIDTG_NominalHeight, modelist[modenum].height, TAG_DONE);*/ ModeID = modelist[modenum].modeid; screen = OpenScreenTags(0, ModeID != INVALID_ID ? SA_DisplayID : TAG_IGNORE, ModeID, SA_Width, modelist[modenum].width, SA_Height, modelist[modenum].height, SA_Depth, 8, SA_Quiet, TRUE, SA_Draggable, FALSE, SA_Type, CUSTOMSCREEN, SA_VideoControl, (IPTR)vctl, TAG_DONE); #if defined(PLATFORM_AMIGAOS3) && defined(USE_C2P) currentBitMap = 0; bm = screen->RastPort.BitMap; if ((GetBitMapAttr(bm, BMA_FLAGS) & BMF_STANDARD) && (modelist[modenum].width % 32) == 0) { if ((sbuf[0] = AllocScreenBuffer(screen, 0, SB_SCREEN_BITMAP)) && (sbuf[1] = AllocScreenBuffer(screen, 0, 0))) { use_c2p = true; // this fixes some RTG modes which would otherwise display garbage on the 1st buffer swap //VID_Update(NULL); } } #endif } if (screen) { flags |= WFLG_BACKDROP | WFLG_BORDERLESS; } else { if (eightbit) /* shouldn't happen, but.. */ Sys_Error("failed OpenScreen ()"); Cvar_SetQuick (&vid_config_fscr, "0"); flags |= WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET; } window = OpenWindowTags(0, WA_InnerWidth, modelist[modenum].width, WA_InnerHeight, modelist[modenum].height, WA_Title, (IPTR)WM_TITLEBAR_TEXT, WA_Flags, flags, screen ? WA_CustomScreen : TAG_IGNORE, (IPTR)screen, WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_ACTIVEWINDOW | IDCMP_INACTIVEWINDOW, TAG_DONE); if (window) { /*pointermem = AllocVec(256, MEMF_ANY | MEMF_CLEAR); if (pointermem) {*/ vid.height = vid.conheight = modelist[modenum].height; vid.rowbytes = vid.conrowbytes = vid.width = vid.conwidth = modelist[modenum].width; buffer = (pixel_t *) malloc(vid.width * vid.height); if (buffer) { vid.buffer = vid.direct = vid.conbuffer = buffer; vid.numpages = 1; vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0); if (VID_AllocBuffers (vid.width, vid.height)) { D_InitCaches (vid_surfcache, vid_surfcachesize); // real success. set vid_modenum properly. vid_modenum = modenum; modestate = (screen) ? MS_FULLDIB : MS_WINDOWED; Cvar_SetValueQuick (&vid_config_swx, modelist[vid_modenum].width); Cvar_SetValueQuick (&vid_config_swy, modelist[vid_modenum].height); if (screen) Cvar_SetValueQuick (&vid_config_mon, (float)(modelist[vid_modenum].modeid & MONITOR_ID_MASK)); //IN_HideMouse (); ClearAllStates(); VID_SetPalette (palette); Con_SafePrintf ("Video Mode: %ux%ux%d\n", vid.width, vid.height, modelist[modenum].bpp); #ifdef PLATFORM_AMIGAOS3 vid.noadapt = modelist[modenum].noadapt; #endif in_mode_set = false; vid.recalc_refdef = 1; return true; } free(buffer); buffer = NULL; } /* FreeVec(pointermem); pointermem = NULL; }*/ CloseWindow(window); window = NULL; } if (screen) { CloseScreen(screen); screen = NULL; } in_mode_set = false; return false; } static void VID_ChangeVideoMode (int newmode) { int temp; temp = scr_disabled_for_loading; scr_disabled_for_loading = true; CDAudio_Pause (); BGM_Pause (); S_ClearBuffer (); if (!VID_SetMode (newmode, NULL)) { if (vid_modenum == newmode) Sys_Error ("Couldn't set video mode"); // failed setting mode, probably due to insufficient // memory. go back to previous mode. Cvar_SetValueQuick (&vid_mode, vid_modenum); if (!VID_SetMode (vid_modenum, NULL)) Sys_Error ("Couldn't set video mode"); } CDAudio_Resume (); BGM_Resume (); scr_disabled_for_loading = temp; } static void VID_Restart_f (void) { if (vid_mode.integer < 0 || vid_mode.integer >= *nummodes) { Con_Printf ("Bad video mode %d\n", vid_mode.integer); Cvar_SetValueQuick (&vid_mode, vid_modenum); return; } Con_Printf ("Re-initializing video:\n"); VID_ChangeVideoMode (vid_mode.integer); } void VID_LockBuffer(void) { } void VID_UnlockBuffer(void) { } void VID_SetPalette(const unsigned char *palette) { const unsigned char *p; unsigned char *pp; int i; if (!palette) return; if (screen) { ULONG *sp = spal; *sp++ = 256 << 16; for (i = 0, p = palette; i < 256; i++) { *sp++ = ((ULONG) *p++) << 24; *sp++ = ((ULONG) *p++) << 24; *sp++ = ((ULONG) *p++) << 24; } *sp = 0; LoadRGB32(&screen->ViewPort, spal); } else { for (i = 0, p = palette, pp = ppal; i < 256; i++) { if (host_bigendian) { *pp++ = 0; *pp++ = *p++; *pp++ = *p++; *pp++ = *p++; } else { *pp++ = p[2]; *pp++ = p[1]; *pp++ = p[0]; *pp++ = 0; p += 3; } palette_changed = true; } } } void VID_ShiftPalette(const unsigned char *palette) { VID_SetPalette(palette); } void VID_Init (const unsigned char *palette) { int width, height, i, temp, monitor; const char *read_vars[] = { "vid_config_fscr", "vid_config_mon", "vid_config_swx", "vid_config_swy" }; #define num_readvars ( sizeof(read_vars)/sizeof(read_vars[0]) ) #ifdef __CLIB2__ GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0); if (!GfxBase) Sys_Error ("Cannot open graphics.library!"); #endif #ifdef PLATFORM_AMIGAOS3 CyberGfxBase = OpenLibrary("cybergraphics.library", 41); /*if (!CyberGfxBase) Sys_Error ("Cannot open cybergraphics.library!");*/ #ifdef USE_C2P if (SysBase->AttnFlags & AFF_68040) c2p_write_bm = c2p1x1_8_c5_bm_040; else c2p_write_bm = c2p1x1_8_c5_bm; #endif #endif temp = scr_disabled_for_loading; scr_disabled_for_loading = true; Cvar_RegisterVariable (&vid_config_fscr); Cvar_RegisterVariable (&vid_config_mon); Cvar_RegisterVariable (&vid_config_swy); Cvar_RegisterVariable (&vid_config_swx); Cvar_RegisterVariable (&vid_config_gly); Cvar_RegisterVariable (&vid_config_glx); Cvar_RegisterVariable (&vid_mode); Cvar_RegisterVariable (&_enable_mouse); Cvar_RegisterVariable (&vid_showload); Cmd_AddCommand ("vid_listmodes", VID_ListModes_f); Cmd_AddCommand ("vid_nummodes", VID_NumModes_f); Cmd_AddCommand ("vid_restart", VID_Restart_f); // prepare the modelists, find the actual modenum for vid_default VID_PrepareModes(); //VID_ListModes_f(); // set vid_mode to our safe default first Cvar_SetValueQuick (&vid_mode, vid_default); // perform an early read of config.cfg CFG_ReadCvars (read_vars, num_readvars); // windowed mode is default // see if the user wants fullscreen if (COM_CheckParm("-fullscreen") || COM_CheckParm("-f")) { Cvar_SetQuick (&vid_config_fscr, "1"); } else if (COM_CheckParm("-window") || COM_CheckParm("-w")) { Cvar_SetQuick (&vid_config_fscr, "0"); } if (vid_config_fscr.integer && !num_fmodes) // FIXME: see below, as well Sys_Error ("No fullscreen modes available at this color depth"); width = vid_config_swx.integer; height = vid_config_swy.integer; monitor = vid_config_mon.integer; // user is always right ... i = COM_CheckParm("-width"); if (i && i < com_argc-1) { // FIXME: this part doesn't know about a disaster case // like we aren't reported any fullscreen modes. width = atoi(com_argv[i+1]); i = COM_CheckParm("-height"); if (i && i < com_argc-1) height = atoi(com_argv[i+1]); else // proceed with 4/3 ratio height = 3 * width / 4; } // pick the appropriate list if (vid_config_fscr.integer) { modelist = fmodelist; nummodes = &num_fmodes; } else { modelist = wmodelist; nummodes = &num_wmodes; } // user requested a mode either from the config or from the // command line // scan existing modes to see if this is already available // if not, add this as the last "valid" video mode and set // vid_mode to it only if it doesn't go beyond vid_maxwidth i = 0; if (vid_config_fscr.integer) { while (i < *nummodes) { if (modelist[i].width == width && modelist[i].height == height && (modelist[i].modeid & MONITOR_ID_MASK) == monitor) break; i++; } } if (i == 0 || i == *nummodes) { i = 0; while (i < *nummodes) { if (modelist[i].width == width && modelist[i].height == height) break; i++; } } if (i < *nummodes) { Cvar_SetValueQuick (&vid_mode, i); } else if ( (width <= vid_maxwidth && width >= MIN_WIDTH && height <= vid_maxheight && height >= MIN_HEIGHT) || COM_CheckParm("-force") ) { modelist[*nummodes].width = width; modelist[*nummodes].height = height; modelist[*nummodes].fullscreen = 1; modelist[*nummodes].bpp = 8; modelist[*nummodes].modeid = INVALID_ID; #ifdef PLATFORM_AMIGAOS3 modelist[*nummodes].noadapt = false; #endif q_snprintf (modelist[*nummodes].modedesc, MAX_DESC, "%d x %d (user mode)", width, height); Cvar_SetValueQuick (&vid_mode, *nummodes); (*nummodes)++; } else { Con_SafePrintf ("ignoring invalid -width and/or -height arguments\n"); } vid.maxwarpwidth = WARP_WIDTH; vid.maxwarpheight = WARP_HEIGHT; vid.colormap = host_colormap; vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); if (!VID_SetMode (vid_mode.integer, palette)) { if (vid_mode.integer == vid_default) Sys_Error ("Couldn't set video mode"); // just one more try before dying Con_SafePrintf ("Couldn't set video mode %d\n" "Trying the default mode\n", vid_mode.integer); //Cvar_SetQuick (&vid_config_fscr, "0"); Cvar_SetValueQuick (&vid_mode, vid_default); if (!VID_SetMode (vid_default, palette)) Sys_Error ("Couldn't set video mode"); } // lock the early-read cvars until Host_Init is finished for (i = 0; i < (int)num_readvars; i++) Cvar_LockVar (read_vars[i]); scr_disabled_for_loading = temp; vid_menudrawfn = VID_MenuDraw; vid_menukeyfn = VID_MenuKey; } void VID_Shutdown (void) { VID_DestroyWindow (); #ifdef PLATFORM_AMIGAOS3 if (CyberGfxBase) { CloseLibrary(CyberGfxBase); CyberGfxBase = NULL; } #endif #ifdef __CLIB2__ if (GfxBase) { CloseLibrary((struct Library *)GfxBase); GfxBase = NULL; } #endif } static void FlipScreen (vrect_t *rects) { #ifdef USE_C2P if (use_c2p) { currentBitMap ^= 1; c2p_write_bm(vid.width, vid.height, 0, 0, vid.buffer, sbuf[currentBitMap]->sb_BitMap); ChangeScreenBuffer(screen, sbuf[currentBitMap]); return; } #endif while (rects) { #ifdef PLATFORM_AMIGAOS3 if (!CyberGfxBase) { WriteChunkyPixels(window->RPort, rects->x, rects->y, rects->width, rects->height, vid.buffer, vid.rowbytes); } else #endif if (screen) { WritePixelArray(vid.buffer, rects->x, rects->y, vid.rowbytes, window->RPort, rects->x, rects->y, rects->width, rects->height, RECTFMT_LUT8); } else { WriteLUTPixelArray(vid.buffer, rects->x, rects->y, vid.rowbytes, window->RPort, ppal, window->BorderLeft + rects->x, window->BorderTop + rects->y, rects->width, rects->height, CTABFMT_XRGB8); } rects = rects->pnext; } } void VID_Update(vrect_t *rects) { vrect_t rect; if (palette_changed) { palette_changed = false; rect.x = 0; rect.y = 0; rect.width = vid.width; rect.height = vid.height; rect.pnext = NULL; rects = ▭ } // We've drawn the frame; copy it to the screen FlipScreen (rects); // handle the mouse state when windowed if that's changed if (_enable_mouse.integer != enable_mouse /*&& modestate == MS_WINDOWED*/) { if (_enable_mouse.integer) IN_ActivateMouse (); else IN_DeactivateMouse (); enable_mouse = _enable_mouse.integer; } } /* ================ D_BeginDirectRect ================ */ void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) { directbitmap = pbitmap; } /* ================ D_EndDirectRect ================ */ void D_EndDirectRect (int x, int y, int width, int height) { if (!window || !directbitmap) return; if (x < 0) x = vid.width + x - 1; if (screen) { WritePixelArray(directbitmap, 0, 0, width, window->RPort, x, y, width, height, RECTFMT_LUT8); } else { WriteLUTPixelArray(directbitmap, 0, 0, width, window->RPort, ppal, window->BorderLeft + x, window->BorderTop + y, width, height, CTABFMT_XRGB8); } directbitmap = NULL; } #ifndef H2W // unused in hexenworld void D_ShowLoadingSize (void) { #if defined(DRAW_PROGRESSBARS) #error NOT IMPLEMENTED #endif } #endif //========================================================================== /* ================ VID_HandlePause ================ */ void VID_HandlePause (qboolean paused) { if (_enable_mouse.integer /*&& (modestate == MS_WINDOWED)*/) { // for consistency, don't show pointer - S.A if (paused) { IN_DeactivateMouse (); //SetPointer(window, pointermem, 16, 16, 0, 0); } else { IN_ActivateMouse (); //ClearPointer(window); } } } /* ================ VID_ToggleFullscreen Handles switching between fullscreen/windowed modes and brings the mouse to a proper state afterwards ================ */ //extern qboolean menu_disabled_mouse; void VID_ToggleFullscreen(void) { // implement this... } //======================================================== // Video menu stuff //======================================================== static int vid_menunum; static int vid_cursor; static vmode_t *vid_menulist; // this changes when vid_menu_fs changes static qboolean want_fstoggle, need_apply; static qboolean vid_menu_firsttime = true; enum { VID_FULLSCREEN, // make sure the fullscreen entry (0) VID_RESOLUTION, // is lower than resolution entry (1) VID_BLANKLINE, // spacer line VID_RESET, VID_APPLY, VID_ITEMS }; static void M_DrawYesNo (int x, int y, int on, int white) { if (on) { if (white) M_PrintWhite (x, y, "yes"); else M_Print (x, y, "yes"); } else { if (white) M_PrintWhite (x, y, "no"); else M_Print (x, y, "no"); } } /* ================ VID_MenuDraw ================ */ static void VID_MenuDraw (void) { ScrollTitle("gfx/menu/title7.lmp"); if (vid_menu_firsttime) { // settings for entering the menu first time vid_menunum = vid_modenum; vid_menu_fs = (modestate != MS_WINDOWED); vid_menulist = (modestate == MS_WINDOWED) ? wmodelist : fmodelist; vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; vid_menu_firsttime = false; } want_fstoggle = ( ((modestate == MS_WINDOWED) && vid_menu_fs) || ((modestate != MS_WINDOWED) && !vid_menu_fs) ); need_apply = (vid_menunum != vid_modenum) || want_fstoggle; M_Print (76, 92 + 8*VID_FULLSCREEN, "Fullscreen: "); M_DrawYesNo (76+12*8, 92 + 8*VID_FULLSCREEN, vid_menu_fs, !want_fstoggle); M_Print (76, 92 + 8*VID_RESOLUTION, "Resolution: "); if (vid_menunum == vid_modenum) M_PrintWhite (76+12*8, 92 + 8*VID_RESOLUTION, vid_menulist[vid_menunum].modedesc); else M_Print (76+12*8, 92 + 8*VID_RESOLUTION, vid_menulist[vid_menunum].modedesc); if (need_apply) { M_Print (76, 92 + 8*VID_RESET, "RESET CHANGES"); M_Print (76, 92 + 8*VID_APPLY, "APPLY CHANGES"); } M_DrawCharacter (64, 92 + vid_cursor*8, 12+((int)(realtime*4)&1)); } static int match_windowed_fullscr_modes (void) { int l; vmode_t *tmplist; int *tmpcount; // choose the new mode tmplist = (vid_menu_fs) ? fmodelist : wmodelist; tmpcount = (vid_menu_fs) ? &num_fmodes : &num_wmodes; for (l = 0; l < *tmpcount; l++) { if (tmplist[l].width == vid_menulist[vid_menunum].width && tmplist[l].height == vid_menulist[vid_menunum].height) { return l; } } return 0; } /* ================ VID_MenuKey ================ */ static void VID_MenuKey (int key) { int *tmpnum; switch (key) { case K_ESCAPE: vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; M_Menu_Options_f (); return; case K_ENTER: switch (vid_cursor) { case VID_RESET: vid_menu_fs = (modestate != MS_WINDOWED); vid_menunum = vid_modenum; vid_menulist = (modestate == MS_WINDOWED) ? wmodelist : fmodelist; vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; break; case VID_APPLY: if (need_apply) { Cvar_SetValueQuick(&vid_mode, vid_menunum); Cvar_SetValueQuick(&vid_config_fscr, vid_menu_fs); modelist = (vid_menu_fs) ? fmodelist : wmodelist; nummodes = (vid_menu_fs) ? &num_fmodes : &num_wmodes; VID_Restart_f(); } vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; break; } return; case K_LEFTARROW: switch (vid_cursor) { case VID_FULLSCREEN: vid_menu_fs = !vid_menu_fs; vid_menunum = match_windowed_fullscr_modes(); vid_menulist = (vid_menu_fs) ? fmodelist : wmodelist; /*if (fs_toggle_works) VID_ToggleFullscreen();*/ break; case VID_RESOLUTION: S_LocalSound ("raven/menu1.wav"); vid_menunum--; if (vid_menunum < 0) vid_menunum = 0; break; } return; case K_RIGHTARROW: switch (vid_cursor) { case VID_FULLSCREEN: vid_menu_fs = !vid_menu_fs; vid_menunum = match_windowed_fullscr_modes(); vid_menulist = (vid_menu_fs) ? fmodelist : wmodelist; /*if (fs_toggle_works) VID_ToggleFullscreen();*/ break; case VID_RESOLUTION: S_LocalSound ("raven/menu1.wav"); tmpnum = (vid_menu_fs) ? &num_fmodes : &num_wmodes; vid_menunum++; /*if (vid_menunum >= *nummodes) vid_menunum = *nummodes - 1;*/ if (vid_menunum >= *tmpnum) vid_menunum--; break; } return; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); vid_cursor--; if (vid_cursor < 0) { vid_cursor = (need_apply) ? VID_ITEMS-1 : VID_BLANKLINE-1; } else if (vid_cursor == VID_BLANKLINE) { vid_cursor--; } break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); vid_cursor++; if (vid_cursor >= VID_ITEMS) { vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; break; } if (vid_cursor >= VID_BLANKLINE) { if (need_apply) { if (vid_cursor == VID_BLANKLINE) vid_cursor++; } else { vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; } } break; default: return; } } engine/h2shared/vid_dos.c000066400000000000000000000400601444734033100156210ustar00rootroot00000000000000/* * vid_dos.c -- DOS-specific video routines. * from quake1 source with minor adaptations for uhexen2. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "quakedef.h" #include "d_local.h" #include "dosisms.h" #include "vid_dos.h" #include "cfgfile.h" modestate_t modestate = MS_UNINIT; static int vid_modenum; static vmode_t *pcurrentmode = NULL; static int vid_testingmode, vid_realmode; static double vid_testendtime; static cvar_t vid_mode = {"vid_mode", "0", CVAR_NONE}; cvar_t vid_wait = {"vid_wait", "0", CVAR_NONE}; cvar_t vid_nopageflip = {"vid_nopageflip", "0", CVAR_ARCHIVE}; cvar_t _vid_wait_override = {"_vid_wait_override", "0", CVAR_ARCHIVE}; static cvar_t _vid_default_mode = {"_vid_default_mode", "0", CVAR_ARCHIVE}; // compatibility with windows version: static cvar_t _vid_default_mode_win = {"_vid_default_mode_win", "1", CVAR_ARCHIVE}; static cvar_t vid_config_x = {"vid_config_x", "800", CVAR_ARCHIVE}; static cvar_t vid_config_y = {"vid_config_y", "600", CVAR_ARCHIVE}; static cvar_t vid_stretch_by_2 = {"vid_stretch_by_2", "1", CVAR_ARCHIVE}; cvar_t _enable_mouse = {"_enable_mouse", "0", CVAR_ARCHIVE}; static cvar_t vid_fullscreen_mode = {"vid_fullscreen_mode", "3", CVAR_ARCHIVE}; static cvar_t vid_windowed_mode = {"vid_windowed_mode", "0", CVAR_ARCHIVE}; viddef_t vid; /* global video state */ int numvidmodes; vmode_t *pvidmodes; static int firstupdate = 1; static void VID_TestMode_f (void); static void VID_NumModes_f (void); static void VID_DescribeCurrentMode_f (void); static void VID_DescribeMode_f (void); static void VID_DescribeModes_f (void); static qboolean VID_SetMode (int modenum, const unsigned char *palette); static byte vid_current_palette[768]; /* save for mode changes */ static qboolean nomodecheck = false; unsigned short d_8to16table[256]; /* not used in 8 bpp mode */ unsigned int d_8to24table[256]; /* not used in 8 bpp mode */ byte globalcolormap[VID_GRADES*256], lastglobalcolor = 0; byte *lastsourcecolormap = NULL; static void VID_MenuDraw (void); static void VID_MenuKey (int key); /* ================ VID_Init ================ */ void VID_Init (const unsigned char *palette) { const char *read_vars[] = { "_vid_default_mode" }; #define num_readvars ( sizeof(read_vars)/sizeof(read_vars[0]) ) Cvar_RegisterVariable (&vid_mode); Cvar_RegisterVariable (&vid_wait); Cvar_RegisterVariable (&vid_nopageflip); Cvar_RegisterVariable (&_vid_wait_override); Cvar_RegisterVariable (&_vid_default_mode); Cvar_RegisterVariable (&_vid_default_mode_win); Cvar_RegisterVariable (&vid_config_x); Cvar_RegisterVariable (&vid_config_y); Cvar_RegisterVariable (&vid_stretch_by_2); Cvar_RegisterVariable (&_enable_mouse); Cvar_RegisterVariable (&vid_fullscreen_mode); Cvar_RegisterVariable (&vid_windowed_mode); Cmd_AddCommand ("vid_testmode", VID_TestMode_f); Cmd_AddCommand ("vid_nummodes", VID_NumModes_f); Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f); Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f); Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f); // perform an early read of config.cfg CFG_ReadCvars (read_vars, num_readvars); // set up the mode list; note that later inits link in their modes ahead of // earlier ones, so the standard VGA modes are always first in the list. This // is important because mode 0 must always be VGA mode 0x13 if (!safemode && !COM_CheckParm("-stdvid")) VID_InitExtra (); VGA_Init (); vid_testingmode = 0; vid_modenum = vid_mode.integer; if (_vid_default_mode.integer < 0 || _vid_default_mode.integer >= numvidmodes) Cvar_SetQuick (&_vid_default_mode, "0"); Cvar_LockVar ("_vid_default_mode"); Cvar_SetROM ("sys_nostdout", "1"); // disable printing to terminal VID_SetMode (vid_modenum, palette); vid_realmode = vid_modenum; vid_menudrawfn = VID_MenuDraw; vid_menukeyfn = VID_MenuKey; } /* ================= VID_GetModePtr ================= */ static vmode_t *VID_GetModePtr (int modenum) { vmode_t *pv; pv = pvidmodes; if (!pv) Sys_Error ("VID_GetModePtr: empty vid mode list"); while (modenum--) { pv = pv->pnext; if (!pv) Sys_Error ("VID_GetModePtr: corrupt vid mode list"); } return pv; } /* ================ VID_NumModes ================ */ static int VID_NumModes (void) { return (numvidmodes); } /* ================ VID_ModeInfo ================ */ static const char *VID_ModeInfo (int modenum, const char **ppheader) { static const char badmodestr[] = "Bad mode number"; vmode_t *pv; pv = VID_GetModePtr (modenum); if (!pv) { if (ppheader) *ppheader = NULL; return badmodestr; } else { if (ppheader) *ppheader = pv->header; return pv->name; } } /* ================ VID_SetMode ================ */ static qboolean VID_SetMode (int modenum, const unsigned char *palette) { int status; vmode_t *pnewmode, *poldmode; if ((modenum >= numvidmodes) || (modenum < 0)) { Cvar_SetValueQuick (&vid_mode, (float)vid_modenum); nomodecheck = true; Con_Printf ("No such video mode: %d\n", modenum); nomodecheck = false; if (pcurrentmode == NULL) { modenum = 0; // mode hasn't been set yet, so initialize to base // mode since they gave us an invalid initial mode } else { return false; } } pnewmode = VID_GetModePtr (modenum); if (pnewmode == pcurrentmode) return true; // already in the desired mode // initialize the new mode poldmode = pcurrentmode; pcurrentmode = pnewmode; vid.width = pcurrentmode->width; vid.height = pcurrentmode->height; vid.aspect = pcurrentmode->aspect; vid.rowbytes = pcurrentmode->rowbytes; status = (*pcurrentmode->setmode) (&vid, pcurrentmode); if (status < 1) { if (status == 0) { // real, hard failure that requires resetting the mode if (!VID_SetMode (vid_modenum, palette)) // restore prior mode Sys_Error ("VID_SetMode: Unable to set any mode, probably " "because there's not enough memory available"); Con_Printf ("Failed to set mode %d\n", modenum); return false; } else if (status == -1) { // not enough memory; just put things back the way they were pcurrentmode = poldmode; vid.width = pcurrentmode->width; vid.height = pcurrentmode->height; vid.aspect = pcurrentmode->aspect; vid.rowbytes = pcurrentmode->rowbytes; return false; } else { Sys_Error ("VID_SetMode: invalid setmode return code %d", status); } } (*pcurrentmode->setpalette) (&vid, pcurrentmode, palette); vid_modenum = modenum; Cvar_SetValueQuick (&vid_mode, (float)vid_modenum); nomodecheck = true; Con_Printf ("%s\n", VID_ModeInfo (vid_modenum, NULL)); nomodecheck = false; vid.recalc_refdef = 1; return true; } /* ================ VID_SetPalette ================ */ void VID_SetPalette (const unsigned char *palette) { if (palette != vid_current_palette) memcpy(vid_current_palette, palette, 768); (*pcurrentmode->setpalette)(&vid, pcurrentmode, vid_current_palette); } /* ================ VID_ShiftPalette ================ */ void VID_ShiftPalette (const unsigned char *palette) { VID_SetPalette (palette); } /* ================ VID_Shutdown ================ */ void VID_Shutdown (void) { regs.h.ah = 0; regs.h.al = 0x3; dos_int86(0x10); vid_testingmode = 0; } /* ================ VID_Update ================ */ void VID_Update (vrect_t *rects) { if (firstupdate && host_initialized) { firstupdate = 0; Cvar_SetValueQuick (&vid_mode, _vid_default_mode.integer); } (*pcurrentmode->swapbuffers)(&vid, pcurrentmode, rects); if (nomodecheck) return; if (vid_testingmode) { if (realtime >= vid_testendtime) { VID_SetMode (vid_realmode, vid_current_palette); vid_testingmode = 0; } } else { if (vid_mode.integer != vid_realmode) { VID_SetMode (vid_mode.integer, vid_current_palette); Cvar_SetValueQuick (&vid_mode, (float)vid_modenum); // so if mode set fails, we don't keep on // trying to set it vid_realmode = vid_modenum; } } } /* ================= VID_NumModes_f ================= */ static void VID_NumModes_f (void) { int nummodes; nummodes = VID_NumModes (); if (nummodes == 1) Con_Printf ("%d video mode is available\n", VID_NumModes ()); else Con_Printf ("%d video modes are available\n", VID_NumModes ()); } /* ================= VID_DescribeCurrentMode_f ================= */ static void VID_DescribeCurrentMode_f (void) { Con_Printf ("%s\n", VID_ModeInfo (vid_modenum, NULL)); } /* ================= VID_DescribeMode_f ================= */ void VID_DescribeMode_f (void) { int modenum; modenum = atoi (Cmd_Argv(1)); Con_Printf ("%s\n", VID_ModeInfo (modenum, NULL)); } /* ================= VID_DescribeModes_f ================= */ void VID_DescribeModes_f (void) { int i, nummodes; const char *pinfo; const char *pheader; vmode_t *pv; qboolean na; na = false; nummodes = VID_NumModes (); for (i = 0; i < nummodes; i++) { pv = VID_GetModePtr (i); pinfo = VID_ModeInfo (i, &pheader); if (pheader) Con_Printf ("\n%s\n", pheader); if (VGA_CheckAdequateMem (pv->width, pv->height, pv->rowbytes, (pv->numpages == 1) || vid_nopageflip.integer)) { Con_Printf ("%2d: %s\n", i, pinfo); } else { Con_Printf ("**: %s\n", pinfo); na = true; } } if (na) { Con_Printf ("\n[**: not enough system RAM for mode]\n"); } } /* ================= VID_GetModeDescription ================= */ static const char *VID_GetModeDescription (int modenum) { const char *pinfo; const char *pheader; vmode_t *pv; pv = VID_GetModePtr (modenum); pinfo = VID_ModeInfo (modenum, &pheader); if (VGA_CheckAdequateMem (pv->width, pv->height, pv->rowbytes, (pv->numpages == 1) || vid_nopageflip.integer)) { return pinfo; } else { return NULL; } } /* ================= VID_TestMode_f ================= */ static void VID_TestMode_f (void) { int modenum; double testduration; if (!vid_testingmode) { modenum = atoi (Cmd_Argv(1)); if (VID_SetMode (modenum, vid_current_palette)) { vid_testingmode = 1; testduration = atof (Cmd_Argv(2)); if (testduration == 0) testduration = 5.0; vid_testendtime = realtime + testduration; } } } /* ================ D_BeginDirectRect ================ */ void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) { if (!vid.direct || !pcurrentmode) return; if ((width > 24) || (height > 24) || (width < 1) || (height < 1)) return; if (width & 0x03) return; (*pcurrentmode->begindirectrect) (&vid, pcurrentmode, x, y, pbitmap, width, height); } /* ================ D_EndDirectRect ================ */ void D_EndDirectRect (int x, int y, int width, int height) { if (!vid.direct || !pcurrentmode) return; if ((width > 24) || (height > 24) || (width < 1) || (height < 1)) return; if ((width & 0x03) || (height & 0x03)) return; (*pcurrentmode->enddirectrect) (&vid, pcurrentmode, x, y, width, height); } void D_ShowLoadingSize (void) { #if defined(DRAW_PROGRESSBARS) /* to be implemented. */ #endif /* !DRAW_PROGRESSBARS */ } void VID_LockBuffer (void) { /* nothing to do */ } void VID_UnlockBuffer (void) { /* nothing to do */ } void VID_HandlePause (qboolean paused) { if (paused) IN_DeactivateMouse (); else IN_ActivateMouse (); } //=========================================================================== static int vid_line, vid_wmodes, vid_column_size; typedef struct { int modenum; const char *desc; int iscur; } modedesc_t; #define MAX_COLUMN_SIZE 11 #define MAX_MODEDESCS (MAX_COLUMN_SIZE * 3) static modedesc_t modedescs[MAX_MODEDESCS]; /* ================ VID_MenuDraw ================ */ static void VID_MenuDraw (void) { const char *ptr; int nummodes, i, j, column, row, dup; char temp[100]; vid_wmodes = 0; nummodes = VID_NumModes (); ScrollTitle("gfx/menu/title7.lmp"); for (i = 0; i < nummodes; i++) { if (vid_wmodes < MAX_MODEDESCS) { if (i != 1) { ptr = VID_GetModeDescription (i); if (ptr) { dup = 0; for (j = 0; j < vid_wmodes; j++) { if (!strcmp (modedescs[j].desc, ptr)) { if (modedescs[j].modenum != 0) { modedescs[j].modenum = i; dup = 1; if (i == vid_modenum) modedescs[j].iscur = 1; } else { dup = 1; } break; } } if (!dup) { modedescs[vid_wmodes].modenum = i; modedescs[vid_wmodes].desc = ptr; modedescs[vid_wmodes].iscur = 0; if (i == vid_modenum) modedescs[vid_wmodes].iscur = 1; vid_wmodes++; } } } } } vid_column_size = (vid_wmodes + 2) / 3; column = 16; row = 36; for (i = 0; i < vid_wmodes; i++) { if (modedescs[i].iscur) M_PrintWhite (column, row, modedescs[i].desc); else M_Print (column, row, modedescs[i].desc); row += 8; if ((i % vid_column_size) == (vid_column_size - 1)) { column += 13*8; row = 36; } } // line cursor if (vid_testingmode) { q_snprintf (temp, sizeof(temp), "TESTING %s", modedescs[vid_line].desc); M_Print (13*8, 36 + MAX_COLUMN_SIZE * 8 + 8*4, temp); M_Print (9*8, 36 + MAX_COLUMN_SIZE * 8 + 8*6, "Please wait 5 seconds..."); } else { M_Print (9*8, 36 + MAX_COLUMN_SIZE * 8 + 8, "Press Enter to set mode"); M_Print (6*8, 36 + MAX_COLUMN_SIZE * 8 + 8*3, "T to test mode for 5 seconds"); ptr = VID_GetModeDescription (vid_modenum); q_snprintf (temp, sizeof(temp), "D to make %s the default", ptr); M_Print (6*8, 36 + MAX_COLUMN_SIZE * 8 + 8*5, temp); ptr = VID_GetModeDescription (_vid_default_mode.integer); if (ptr) { q_snprintf (temp, sizeof(temp), "Current default is %s", ptr); M_Print (7*8, 36 + MAX_COLUMN_SIZE * 8 + 8*6, temp); } M_Print (15*8, 36 + MAX_COLUMN_SIZE * 8 + 8*8, "Esc to exit"); row = 36 + (vid_line % vid_column_size) * 8; column = 8 + (vid_line / vid_column_size) * 13*8; M_DrawCharacter (column, row, 12+((int)(realtime*4)&1)); } } /* ================ VID_MenuKey ================ */ static void VID_MenuKey (int key) { if (vid_testingmode) return; switch (key) { case K_ESCAPE: S_LocalSound ("raven/menu1.wav"); M_Menu_Options_f (); break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); vid_line--; if (vid_line < 0) vid_line = vid_wmodes - 1; break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); vid_line++; if (vid_line >= vid_wmodes) vid_line = 0; break; case K_LEFTARROW: S_LocalSound ("raven/menu1.wav"); vid_line -= vid_column_size; if (vid_line < 0) { vid_line += ((vid_wmodes + (vid_column_size - 1)) / vid_column_size) * vid_column_size; while (vid_line >= vid_wmodes) vid_line -= vid_column_size; } break; case K_RIGHTARROW: S_LocalSound ("raven/menu1.wav"); vid_line += vid_column_size; if (vid_line >= vid_wmodes) { vid_line -= ((vid_wmodes + (vid_column_size - 1)) / vid_column_size) * vid_column_size; while (vid_line < 0) vid_line += vid_column_size; } break; case K_ENTER: S_LocalSound ("raven/menu1.wav"); VID_SetMode (modedescs[vid_line].modenum, vid_current_palette); break; case 'T': case 't': S_LocalSound ("raven/menu1.wav"); if (VID_SetMode (modedescs[vid_line].modenum, vid_current_palette)) { vid_testingmode = 1; vid_testendtime = realtime + 5.0; } break; case 'D': case 'd': S_LocalSound ("raven/menu1.wav"); firstupdate = 0; Cvar_SetValueQuick (&_vid_default_mode, vid_modenum); break; default: break; } } engine/h2shared/vid_dos.h000066400000000000000000000054771444734033100156430ustar00rootroot00000000000000/* * vid_dos.h -- header file for DOS-specific video stuff. * from quake1 source with minor adaptations for uhexen2. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __VID_DOS_H #define __VID_DOS_H typedef struct vmode_s { struct vmode_s *pnext; const char *name; const char *header; int width; int height; float aspect; int rowbytes; int planar; int numpages; void *pextradata; int (*setmode)(viddef_t *lvid, struct vmode_s *pcurrentmode); void (*swapbuffers)(viddef_t *lvid, struct vmode_s *pcurrentmode, vrect_t *rects); void (*setpalette)(viddef_t *lvid, struct vmode_s *pcurrentmode, const unsigned char *palette); void (*begindirectrect)(viddef_t *lvid, struct vmode_s *pcurrentmode, int x, int y, byte *pbitmap, int width, int height); void (*enddirectrect)(viddef_t *lvid, struct vmode_s *pcurrentmode, int x, int y, int width, int height); } vmode_t; // vid_wait settings #define VID_WAIT_NONE 0 #define VID_WAIT_VSYNC 1 #define VID_WAIT_DISPLAY_ENABLE 2 extern int numvidmodes; extern vmode_t *pvidmodes; ASM_LINKAGE_BEGIN extern int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes; extern byte *VGA_pagebase; ASM_LINKAGE_END extern vmode_t *VGA_pcurmode; extern cvar_t vid_wait; extern cvar_t vid_nopageflip; extern cvar_t _vid_wait_override; extern void *vid_surfcache; extern int vid_surfcachesize; void VGA_Init (void); void VID_InitExtra (void); void VGA_WaitVsync (void); void VGA_ClearVideoMem (int planar); qboolean VGA_FreeAndAllocVidbuffer (viddef_t *lvid, int allocnewbuffer); qboolean VGA_CheckAdequateMem (int width, int height, int rowbytes, int allocnewbuffer); void VGA_BeginDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, int x, int y, byte *pbitmap, int width, int height); void VGA_EndDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, int x, int y, int width, int height); ASM_LINKAGE_BEGIN void VGA_UpdatePlanarScreen (void *srcbuffer); void VGA_UpdateLinearScreen (void *srcptr, void *destptr, int width, int height, int srcrowbytes, int destrowbytes); ASM_LINKAGE_END #endif /* __VID_DOS_H */ engine/h2shared/vid_ext.c000066400000000000000000000472551444734033100156510ustar00rootroot00000000000000/* * vid_ext.c -- * extended video modes: VESA-specific DOS video stuff * TODO: make dependencies on vid_vga.c explicit or eliminate them * from quake1 source with minor adaptations for uhexen2. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "quakedef.h" #include "d_local.h" #include "dosisms.h" #include "vid_dos.h" #define MODE_SUPPORTED_IN_HW 0x0001 #define COLOR_MODE 0x0008 #define GRAPHICS_MODE 0x0010 #define VGA_INCOMPATIBLE 0x0020 #define LINEAR_FRAME_BUFFER 0x0080 #define LINEAR_MODE 0x4000 #define VESA_DONT_WAIT_VSYNC 0 /* when page flipping */ #define VESA_WAIT_VSYNC 0x80 #define MAX_VESA_MODES 30 /* we'll just take the first 30 if there are more */ typedef struct { int pages[3]; /* either 2 or 3 is valid */ int vesamode; /* LINEAR_MODE set if linear mode */ void *plinearmem; /* linear address of start of frame buffer */ qboolean vga_incompatible; } vesa_extra_t; static vmode_t vesa_modes[MAX_VESA_MODES] = { { NULL, NULL, " ********* VESA modes ********* "} }; static vesa_extra_t vesa_extra[MAX_VESA_MODES]; static char names[MAX_VESA_MODES][10]; static int VID_currentpage; static int VID_displayedpage; static int *VID_pagelist; static byte *VID_membase; static int VID_banked; typedef struct { int modenum; int mode_attributes; int winasegment; int winbsegment; int bytes_per_scanline; // bytes per logical scanline (+16) int win; // window number (A=0, B=1) int win_size; // window size (+6) int granularity; // how finely i can set the window in vid mem (+4) int width, height; // displayed width and height (+18, +20) int bits_per_pixel; // er, better be 8, 15, 16, 24, or 32 (+25) int bytes_per_pixel; // er, better be 1, 2, or 4 int memory_model; // and better be 4 or 6, packed or direct color (+27) int num_pages; // number of complete frame buffer pages (+29) int red_width; // the # of bits in the red component (+31) int red_pos; // the bit position of the red component (+32) int green_width; // etc.. (+33) int green_pos; // (+34) int blue_width; // (+35) int blue_pos; // (+36) int pptr; int pagesize; int numpages; } modeinfo_t; static modeinfo_t modeinfo; // all bytes to avoid problems with compiler field packing typedef struct vbeinfoblock_s { byte VbeSignature[4]; byte VbeVersion[2]; byte OemStringPtr[4]; byte Capabilities[4]; byte VideoModePtr[4]; byte TotalMemory[2]; byte OemSoftwareRev[2]; byte OemVendorNamePtr[4]; byte OemProductNamePtr[4]; byte OemProductRevPtr[4]; byte Reserved[222]; byte OemData[256]; } vbeinfoblock_t; static int totalvidmem; static byte *ppal; static qboolean vsync_exists, de_exists; static qboolean VID_ExtraGetModeInfo (int modenum); static int VID_ExtraInitMode (viddef_t *lvid, vmode_t *pcurrentmode); static void VID_ExtraSwapBuffers (viddef_t *lvid, vmode_t *pcurrentmode, vrect_t *rects); /* ================ VGA_BankedBeginDirectRect ================ */ static void VGA_BankedBeginDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, int x, int y, byte *pbitmap, int width, int height) { if (!lvid->direct) return; regs.x.ax = 0x4f05; regs.x.bx = 0; regs.x.dx = VID_displayedpage; dos_int86(0x10); VGA_BeginDirectRect (lvid, pcurrentmode, x, y, pbitmap, width, height); regs.x.ax = 0x4f05; regs.x.bx = 0; regs.x.dx = VID_currentpage; dos_int86(0x10); } /* ================ VGA_BankedEndDirectRect ================ */ static void VGA_BankedEndDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, int x, int y, int width, int height) { if (!lvid->direct) return; regs.x.ax = 0x4f05; regs.x.bx = 0; regs.x.dx = VID_displayedpage; dos_int86(0x10); VGA_EndDirectRect (lvid, pcurrentmode, x, y, width, height); regs.x.ax = 0x4f05; regs.x.bx = 0; regs.x.dx = VID_currentpage; dos_int86(0x10); } /* ================ VID_SetVESAPalette ================ */ static void VID_SetVESAPalette (viddef_t *lvid, vmode_t *pcurrentmode, const unsigned char *pal) { int i; byte *pp; Q_UNUSED(lvid); Q_UNUSED(pcurrentmode); pp = ppal; for (i = 0; i < 256; i++) { pp[2] = pal[0] >> 2; pp[1] = pal[1] >> 2; pp[0] = pal[2] >> 2; pp += 4; pal += 3; } regs.x.ax = 0x4F09; regs.x.bx = 0; regs.x.cx = 256; regs.x.dx = 0; regs.x.es = ptr2real(ppal) >> 4; regs.x.di = ptr2real(ppal) & 0xf; dos_int86(0x10); if (regs.x.ax != 0x4f) Sys_Error ("Unable to load VESA palette\n"); } /* ================ VID_ExtraFarToLinear ================ */ static void *VID_ExtraFarToLinear (unsigned long addr) { return real2ptr(((addr & 0xFFFF0000) >> 12) + (addr & 0xFFFF)); } /* ================ VID_ExtraWaitDisplayEnable ================ */ static void VID_ExtraWaitDisplayEnable (void) { while ((inportb (0x3DA) & 0x01) == 1) ; } /* ================ VID_ExtraVidLookForState ================ */ static qboolean VID_ExtraVidLookForState (unsigned int state, unsigned int mask) { int i; double starttime, time; starttime = Sys_DoubleTime (); do { for (i = 0; i < 100000; i++) { if ((inportb (0x3DA) & mask) == state) return true; } time = Sys_DoubleTime (); } while ((time - starttime) < 0.1); return false; } /* ================ VID_ExtraStateFound ================ */ static qboolean VID_ExtraStateFound (unsigned int state) { int i, workingstate; workingstate = 0; for (i = 0; i < 10; i++) { if (!VID_ExtraVidLookForState(workingstate, state)) { return false; } workingstate ^= state; } return true; } /* ================ VID_InitExtra ================ */ void VID_InitExtra (void) { int nummodes; short *pmodenums; vbeinfoblock_t *pinfoblock; unsigned long addr; __dpmi_meminfo phys_mem_info; pinfoblock = (vbeinfoblock_t *) dos_getmemory(sizeof(vbeinfoblock_t)); if (!pinfoblock) { Con_DPrintf("%s: Unable to allocate low memory.\n", __thisfunc__); return; } pinfoblock->VbeSignature[0] = 'V'; pinfoblock->VbeSignature[1] = 'B'; pinfoblock->VbeSignature[2] = 'E'; pinfoblock->VbeSignature[3] = '2'; // see if VESA support is available regs.x.ax = 0x4f00; regs.x.es = ptr2real(pinfoblock) >> 4; regs.x.di = ptr2real(pinfoblock) & 0xf; dos_int86(0x10); if (regs.x.ax != 0x4f) { dos_freememory(pinfoblock); return; // no VESA support } if (pinfoblock->VbeVersion[1] < 0x02) { dos_freememory(pinfoblock); return; // not VESA 2.0 or greater } addr = ( (pinfoblock->OemStringPtr[0] ) | (pinfoblock->OemStringPtr[1] << 8) | (pinfoblock->OemStringPtr[2] << 16) | (pinfoblock->OemStringPtr[3] << 24)); Con_Printf ("VESA 2.0 compliant adapter:\n%s\n", (char *) VID_ExtraFarToLinear(addr)); totalvidmem = ( (pinfoblock->TotalMemory[0] ) | (pinfoblock->TotalMemory[1] << 8) ) << 16; // Con_Printf ("%dk video memory\n", totalvidmem >> 10); addr = ( (pinfoblock->VideoModePtr[0] ) | (pinfoblock->VideoModePtr[1] << 8) | (pinfoblock->VideoModePtr[2] << 16) | (pinfoblock->VideoModePtr[3] << 24)); pmodenums = (short *) VID_ExtraFarToLinear(addr); // find 8 bit modes until we either run out of space or run out of modes nummodes = 0; while ((*pmodenums != -1) && (nummodes < MAX_VESA_MODES)) { if (VID_ExtraGetModeInfo (*pmodenums)) { vesa_modes[nummodes].pnext = &vesa_modes[nummodes+1]; if (modeinfo.width > 999) { if (modeinfo.height > 999) { sprintf (&names[nummodes][0], "%4dx%4d", modeinfo.width, modeinfo.height); names[nummodes][9] = 0; } else { sprintf (&names[nummodes][0], "%4dx%3d", modeinfo.width, modeinfo.height); names[nummodes][8] = 0; } } else { if (modeinfo.height > 999) { sprintf (&names[nummodes][0], "%3dx%4d", modeinfo.width, modeinfo.height); names[nummodes][8] = 0; } else { sprintf (&names[nummodes][0], "%3dx%3d", modeinfo.width, modeinfo.height); names[nummodes][7] = 0; } } vesa_modes[nummodes].name = &names[nummodes][0]; vesa_modes[nummodes].width = modeinfo.width; vesa_modes[nummodes].height = modeinfo.height; vesa_modes[nummodes].aspect = ((float)modeinfo.height / (float)modeinfo.width) * (320.0 / 240.0); vesa_modes[nummodes].rowbytes = modeinfo.bytes_per_scanline; vesa_modes[nummodes].planar = 0; vesa_modes[nummodes].pextradata = &vesa_extra[nummodes]; vesa_modes[nummodes].setmode = VID_ExtraInitMode; vesa_modes[nummodes].swapbuffers = VID_ExtraSwapBuffers; vesa_modes[nummodes].setpalette = VID_SetVESAPalette; if (modeinfo.mode_attributes & LINEAR_FRAME_BUFFER) { // add linear bit to mode for linear modes vesa_extra[nummodes].vesamode = modeinfo.modenum | LINEAR_MODE; vesa_extra[nummodes].pages[0] = 0; vesa_extra[nummodes].pages[1] = modeinfo.pagesize; vesa_extra[nummodes].pages[2] = modeinfo.pagesize * 2; vesa_modes[nummodes].numpages = modeinfo.numpages; vesa_modes[nummodes].begindirectrect = VGA_BeginDirectRect; vesa_modes[nummodes].enddirectrect = VGA_EndDirectRect; phys_mem_info.address = (int)modeinfo.pptr; phys_mem_info.size = 0x400000; if (__dpmi_physical_address_mapping(&phys_mem_info)) goto NextMode; vesa_extra[nummodes].plinearmem = real2ptr (phys_mem_info.address); } else { // banked at 0xA0000 vesa_extra[nummodes].vesamode = modeinfo.modenum; vesa_extra[nummodes].pages[0] = 0; vesa_extra[nummodes].plinearmem = real2ptr(modeinfo.winasegment<<4); vesa_modes[nummodes].begindirectrect = VGA_BankedBeginDirectRect; vesa_modes[nummodes].enddirectrect = VGA_BankedEndDirectRect; vesa_extra[nummodes].pages[1] = modeinfo.pagesize; vesa_extra[nummodes].pages[2] = modeinfo.pagesize * 2; vesa_modes[nummodes].numpages = modeinfo.numpages; } vesa_extra[nummodes].vga_incompatible = modeinfo.mode_attributes & VGA_INCOMPATIBLE; nummodes++; } NextMode: pmodenums++; } // add the VESA modes at the start of the mode list (if there are any) if (nummodes) { vesa_modes[nummodes-1].pnext = pvidmodes; pvidmodes = &vesa_modes[0]; numvidmodes += nummodes; ppal = (byte *) dos_getmemory(256 * 4); } dos_freememory(pinfoblock); } /* ================ VID_ExtraGetModeInfo ================ */ static qboolean VID_ExtraGetModeInfo (int modenum) { char *infobuf; int numimagepages; infobuf = (char *) dos_getmemory(256); regs.x.ax = 0x4f01; regs.x.cx = modenum; regs.x.es = ptr2real(infobuf) >> 4; regs.x.di = ptr2real(infobuf) & 0xf; dos_int86(0x10); if (regs.x.ax != 0x4f) { dos_freememory(infobuf); return false; } else { modeinfo.modenum = modenum; modeinfo.bits_per_pixel = *(char*)(infobuf + 25); modeinfo.bytes_per_pixel = (modeinfo.bits_per_pixel + 1) / 8; modeinfo.width = *(short*)(infobuf + 18); modeinfo.height = *(short*)(infobuf + 20); // we do only 8-bpp in software if ((modeinfo.bits_per_pixel != 8) || (modeinfo.bytes_per_pixel != 1) || (modeinfo.width > MAXWIDTH) || (modeinfo.height > MAXHEIGHT)) { dos_freememory(infobuf); return false; } modeinfo.mode_attributes = *(short*)infobuf; // we only want color graphics modes that are supported by the hardware if ((modeinfo.mode_attributes & (MODE_SUPPORTED_IN_HW | COLOR_MODE | GRAPHICS_MODE)) != (MODE_SUPPORTED_IN_HW | COLOR_MODE | GRAPHICS_MODE)) { dos_freememory(infobuf); return false; } // we only work with linear frame buffers, except for 320x200, which can // effectively be linear when banked at 0xA000 if (!(modeinfo.mode_attributes & LINEAR_FRAME_BUFFER)) { if ((modeinfo.width != 320) || (modeinfo.height != 200)) { dos_freememory(infobuf); return false; } } modeinfo.bytes_per_scanline = *(short*)(infobuf + 16); modeinfo.pagesize = modeinfo.bytes_per_scanline * modeinfo.height; if (modeinfo.pagesize > totalvidmem) { dos_freememory(infobuf); return false; } // force to one page if the adapter reports it doesn't support more pages // than that, no matter how much memory it has--it may not have hardware // support for page flipping numimagepages = *(unsigned char *)(infobuf + 29); if (numimagepages <= 0) { // wrong, but there seems to be an ATI VESA driver that reports 0 modeinfo.numpages = 1; } else if (numimagepages < 3) { modeinfo.numpages = numimagepages; } else { modeinfo.numpages = 3; } if (*(char*)(infobuf + 2) & 5) { modeinfo.winasegment = *(unsigned short*)(infobuf + 8); modeinfo.win = 0; } else if (*(char*)(infobuf + 3) & 5) { modeinfo.winbsegment = *(unsigned short*)(infobuf + 8); modeinfo.win = 1; } modeinfo.granularity = *(short*)(infobuf + 4) * 1024; modeinfo.win_size = *(short*)(infobuf + 6) * 1024; modeinfo.bits_per_pixel = *(char*)(infobuf + 25); modeinfo.bytes_per_pixel = (modeinfo.bits_per_pixel + 1) / 8; modeinfo.memory_model = *(unsigned char*)(infobuf + 27); modeinfo.num_pages = *(char*)(infobuf + 29) + 1; modeinfo.red_width = *(char*)(infobuf + 31); modeinfo.red_pos = *(char*)(infobuf + 32); modeinfo.green_width = *(char*)(infobuf + 33); modeinfo.green_pos = *(char*)(infobuf + 34); modeinfo.blue_width = *(char*)(infobuf + 35); modeinfo.blue_pos = *(char*)(infobuf + 36); modeinfo.pptr = *(long *)(infobuf + 40); #if 0 Sys_Printf("VID: (VESA) info for mode 0x%x\n", modeinfo.modenum); Sys_Printf(" mode attrib = 0x%0x\n", modeinfo.mode_attributes); Sys_Printf(" win a attrib = 0x%0x\n", *(unsigned char*)(infobuf + 2)); Sys_Printf(" win b attrib = 0x%0x\n", *(unsigned char*)(infobuf + 3)); Sys_Printf(" win a seg 0x%0x\n", (int) modeinfo.winasegment); Sys_Printf(" win b seg 0x%0x\n", (int) modeinfo.winbsegment); Sys_Printf(" bytes per scanline = %d\n", modeinfo.bytes_per_scanline); Sys_Printf(" width = %d, height = %d\n", modeinfo.width, modeinfo.height); Sys_Printf(" win = %c\n", 'A' + modeinfo.win); Sys_Printf(" win granularity = %d\n", modeinfo.granularity); Sys_Printf(" win size = %d\n", modeinfo.win_size); Sys_Printf(" bits per pixel = %d\n", modeinfo.bits_per_pixel); Sys_Printf(" bytes per pixel = %d\n", modeinfo.bytes_per_pixel); Sys_Printf(" memory model = 0x%x\n", modeinfo.memory_model); Sys_Printf(" num pages = %d\n", modeinfo.num_pages); Sys_Printf(" red width = %d\n", modeinfo.red_width); Sys_Printf(" red pos = %d\n", modeinfo.red_pos); Sys_Printf(" green width = %d\n", modeinfo.green_width); Sys_Printf(" green pos = %d\n", modeinfo.green_pos); Sys_Printf(" blue width = %d\n", modeinfo.blue_width); Sys_Printf(" blue pos = %d\n", modeinfo.blue_pos); Sys_Printf(" phys mem = %x\n", modeinfo.pptr); #endif } dos_freememory(infobuf); return true; } /* ================ VID_ExtraInitMode ================ */ static int VID_ExtraInitMode (viddef_t *lvid, vmode_t *pcurrentmode) { vesa_extra_t *pextra; int pageoffset; pextra = (vesa_extra_t *) pcurrentmode->pextradata; if (vid_nopageflip.integer) lvid->numpages = 1; else lvid->numpages = pcurrentmode->numpages; // clean up any old vid buffer lying around, alloc new if needed if (!VGA_FreeAndAllocVidbuffer (lvid, lvid->numpages == 1)) return -1; // memory alloc failed // clear the screen and wait for the next frame. VGA_pcurmode, which // VGA_ClearVideoMem relies on, is guaranteed to be set because mode 0 is // always the first mode set in a session if (VGA_pcurmode) VGA_ClearVideoMem (VGA_pcurmode->planar); // set the mode regs.x.ax = 0x4f02; regs.x.bx = pextra->vesamode; dos_int86(0x10); if (regs.x.ax != 0x4f) return 0; VID_banked = !(pextra->vesamode & LINEAR_MODE); VID_membase = (byte *) pextra->plinearmem; VGA_width = lvid->width; VGA_height = lvid->height; VGA_rowbytes = lvid->rowbytes; lvid->colormap = host_colormap; VID_pagelist = &pextra->pages[0]; // wait for display enable by default only when triple-buffering on a VGA- // compatible machine that actually has a functioning display enable status vsync_exists = VID_ExtraStateFound (0x08); de_exists = VID_ExtraStateFound (0x01); if (!pextra->vga_incompatible && (lvid->numpages == 3) && de_exists && (_vid_wait_override.integer == 0)) { Cvar_SetValueQuick (&vid_wait, (float)VID_WAIT_DISPLAY_ENABLE); VID_displayedpage = 0; VID_currentpage = 1; } else { if ((lvid->numpages == 1) && (_vid_wait_override.integer == 0)) { Cvar_SetValueQuick (&vid_wait, (float)VID_WAIT_NONE); VID_displayedpage = VID_currentpage = 0; } else { Cvar_SetValueQuick (&vid_wait, (float)VID_WAIT_VSYNC); VID_displayedpage = 0; if (lvid->numpages > 1) VID_currentpage = 1; else VID_currentpage = 0; } } // TODO: really should be a call to a function pageoffset = VID_pagelist[VID_displayedpage]; regs.x.ax = 0x4f07; regs.x.bx = 0x80; // wait for vsync so we know page 0 is visible regs.x.cx = pageoffset % VGA_rowbytes; regs.x.dx = pageoffset / VGA_rowbytes; dos_int86(0x10); if (VID_banked) { regs.x.ax = 0x4f05; regs.x.bx = 0; regs.x.dx = VID_currentpage; dos_int86(0x10); VGA_pagebase = VID_membase; } else { VGA_pagebase = VID_membase + VID_pagelist[VID_currentpage]; } if (lvid->numpages > 1) { lvid->buffer = VGA_pagebase; lvid->conbuffer = lvid->buffer; } else { lvid->rowbytes = lvid->width; } lvid->direct = VGA_pagebase; lvid->conrowbytes = lvid->rowbytes; lvid->conwidth = lvid->width; lvid->conheight = lvid->height; lvid->maxwarpwidth = WARP_WIDTH; lvid->maxwarpheight = WARP_HEIGHT; VGA_pcurmode = pcurrentmode; D_InitCaches (vid_surfcache, vid_surfcachesize); return 1; } /* ================ VID_ExtraSwapBuffers ================ */ static void VID_ExtraSwapBuffers (viddef_t *lvid, vmode_t *pcurrentmode, vrect_t *rects) { int pageoffset; Q_UNUSED(rects); Q_UNUSED(pcurrentmode); pageoffset = VID_pagelist[VID_currentpage]; // display the newly finished page if (lvid->numpages > 1) { // page flipped regs.x.ax = 0x4f07; if (vid_wait.integer != VID_WAIT_VSYNC) { if ((vid_wait.integer == VID_WAIT_DISPLAY_ENABLE) && de_exists) VID_ExtraWaitDisplayEnable (); regs.x.bx = VESA_DONT_WAIT_VSYNC; } else { regs.x.bx = VESA_WAIT_VSYNC; // double buffered has to wait } regs.x.cx = pageoffset % VGA_rowbytes; regs.x.dx = pageoffset / VGA_rowbytes; dos_int86(0x10); VID_displayedpage = VID_currentpage; if (++VID_currentpage >= lvid->numpages) VID_currentpage = 0; // // set the new write window if this is a banked mode; otherwise, set the // new address to which to write // if (VID_banked) { regs.x.ax = 0x4f05; regs.x.bx = 0; regs.x.dx = VID_currentpage; dos_int86(0x10); } else { lvid->direct = lvid->buffer; // direct drawing goes to the currently displayed page lvid->buffer = VID_membase + VID_pagelist[VID_currentpage]; lvid->conbuffer = lvid->buffer; } VGA_pagebase = lvid->buffer; } else { // non-page-flipped if (vsync_exists && (vid_wait.integer == VID_WAIT_VSYNC)) { VGA_WaitVsync (); } while (rects) { VGA_UpdateLinearScreen ( lvid->buffer + rects->x + (rects->y * lvid->rowbytes), VGA_pagebase + rects->x + (rects->y * VGA_rowbytes), rects->width, rects->height, lvid->rowbytes, VGA_rowbytes); rects = rects->pnext; } } } engine/h2shared/vid_sdl.c000066400000000000000000000737571444734033100156410ustar00rootroot00000000000000/* * vid_sdl.c -- SDL video driver * Select window size and mode and init SDL in SOFTWARE mode. * * Changed by S.A. 7/11/04, 27/12/04 * Options are now: -fullscreen | -window, -height , -width * Currently bpp is 8 bit and it seems fairly hardwired at this depth * * Changed by O.S 7/01/06 * - Added video modes enumeration via SDL * - Added video mode changing on the fly. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2004-2005 Steven Atkinson * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "d_local.h" #include "cfgfile.h" #include "bgmusic.h" #include "cdaudio.h" #include "sdl_inc.h" #define MIN_WIDTH 320 //#define MIN_HEIGHT 200 #define MIN_HEIGHT 240 #define MAX_DESC 13 static unsigned char vid_curpal[256*3]; /* save for mode changes */ unsigned short d_8to16table[256]; unsigned int d_8to24table[256]; byte globalcolormap[VID_GRADES*256], lastglobalcolor = 0; byte *lastsourcecolormap = NULL; static qboolean vid_initialized = false; static int lockcount; qboolean in_mode_set; static int enable_mouse; static qboolean palette_changed; static int num_fmodes; static int num_wmodes; static int *nummodes; //static int bpp = 8; static SDL_Surface *screen; static qboolean vid_menu_fs; static qboolean fs_toggle_works = true; viddef_t vid; // global video state // cvar vid_mode must be set before calling VID_SetMode, VID_ChangeVideoMode or VID_Restart_f static cvar_t vid_mode = {"vid_mode", "0", CVAR_NONE}; static cvar_t vid_config_glx = {"vid_config_glx", "640", CVAR_ARCHIVE}; static cvar_t vid_config_gly = {"vid_config_gly", "480", CVAR_ARCHIVE}; static cvar_t vid_config_swx = {"vid_config_swx", "320", CVAR_ARCHIVE}; static cvar_t vid_config_swy = {"vid_config_swy", "240", CVAR_ARCHIVE}; static cvar_t vid_config_fscr= {"vid_config_fscr", "0", CVAR_ARCHIVE}; static cvar_t vid_showload = {"vid_showload", "1", CVAR_NONE}; cvar_t _enable_mouse = {"_enable_mouse", "1", CVAR_ARCHIVE}; static int vid_default = -1; // modenum of 320x240 as a safe default static int vid_modenum = -1; // current video mode, set after mode setting succeeds static int vid_maxwidth = 640, vid_maxheight = 480; modestate_t modestate = MS_UNINIT; static byte *vid_surfcache; static int vid_surfcachesize; static int VID_highhunkmark; typedef struct { modestate_t type; int width; int height; int modenum; int fullscreen; int bpp; int halfscreen; char modedesc[MAX_DESC]; } vmode_t; typedef struct { int width; int height; } stdmode_t; #define RES_640X480 3 static const stdmode_t std_modes[] = { // NOTE: keep this list in order {320, 240}, // 0 {400, 300}, // 1 {512, 384}, // 2 {640, 480}, // 3 == RES_640X480, this is our default, below // this is the lowresmodes region. // either do not change its order, // or change the above define, too {800, 600}, // 4, RES_640X480 + 1 {1024, 768} // 5, RES_640X480 + 2 }; #define MAX_MODE_LIST 64 #define MAX_STDMODES (sizeof(std_modes) / sizeof(std_modes[0])) #define NUM_LOWRESMODES (RES_640X480) static vmode_t fmodelist[MAX_MODE_LIST+1]; // list of enumerated fullscreen modes static vmode_t wmodelist[MAX_STDMODES +1]; // list of standart 4:3 windowed modes static vmode_t *modelist; // modelist in use, points to one of the above lists static qboolean VID_SetMode (int modenum, const unsigned char *palette); static void VID_MenuDraw (void); static void VID_MenuKey (int key); // window manager stuff #if defined(H2W) # define WM_TITLEBAR_TEXT "HexenWorld" # define WM_ICON_TEXT "HexenWorld" //#elif defined(H2MP) //# define WM_TITLEBAR_TEXT "Hexen II+" //# define WM_ICON_TEXT "HEXEN2MP" #else # define WM_TITLEBAR_TEXT "Hexen II" # define WM_ICON_TEXT "HEXEN2" #endif //==================================== qboolean VID_HasMouseOrInputFocus (void) { return (SDL_GetAppState() & (SDL_APPMOUSEFOCUS | SDL_APPINPUTFOCUS)) != 0; } qboolean VID_IsMinimized (void) { return !(SDL_GetAppState() & SDL_APPACTIVE); } /* ================ ClearAllStates ================ */ static void ClearAllStates (void) { Key_ClearStates (); IN_ClearStates (); } /* ================ VID_AllocBuffers ================ */ static qboolean VID_AllocBuffers (int width, int height) { int tsize, tbuffersize; tbuffersize = width * height * sizeof (*d_pzbuffer); tsize = D_SurfaceCacheForRes (width, height); tbuffersize += tsize; // see if there's enough memory, allowing for the normal mode 0x13 pixel, // z, and surface buffers //if ((host_parms->memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + // 0x10000 * 3) < MINIMUM_MEMORY) // Pa3PyX: using hopefully better estimation now // if total memory < needed surface cache + (minimum operational memory // less surface cache for 320x200 and typical hunk state after init) if (host_parms->memsize < tbuffersize + 0x180000 + 0xC00000) { Con_SafePrintf ("Not enough memory for video mode\n"); return false; // not enough memory for mode } vid_surfcachesize = tsize; if (d_pzbuffer) { D_FlushCaches (); Hunk_FreeToHighMark (VID_highhunkmark); d_pzbuffer = NULL; } VID_highhunkmark = Hunk_HighMark (); d_pzbuffer = (short *) Hunk_HighAllocName (tbuffersize, "video"); vid_surfcache = (byte *)d_pzbuffer + width * height * sizeof (*d_pzbuffer); return true; } /* ================ VID_CheckAdequateMem ================ */ static qboolean VID_CheckAdequateMem (int width, int height) { int tbuffersize; tbuffersize = width * height * sizeof (*d_pzbuffer); tbuffersize += D_SurfaceCacheForRes (width, height); // see if there's enough memory, allowing for the normal mode 0x13 pixel, // z, and surface buffers //if ((host_parms->memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + // 0x10000 * 3) < MINIMUM_MEMORY) // Pa3PyX: using hopefully better estimation now // Experimentation: the heap should have at least 12.0 megs // remaining (after init) after setting video mode, otherwise // it's Hunk_Alloc failures and cache thrashes upon level load if (host_parms->memsize < tbuffersize + 0x180000 + 0xC00000) { return false; // not enough memory for mode } return true; } static void VID_SetIcon (void) { /* from Kristian Duske: "AFAIK, the application icon must be present in * Contents/Resources and it must be set in the Info.plist file. It will * then be used by Finder and Dock as well as for individual windows * unless overridden by a document icon. So SDL_WM_SetIcon() is probably * not necessary, and will likely use a low-res image anyway." */ #if !defined(PLATFORM_OSX) # include "xbm_icon.h" /* the xbm data */ SDL_Surface *icon; SDL_Color color; Uint8 *ptr; int i, mask; icon = SDL_CreateRGBSurface(SDL_SWSURFACE, hx2icon_width, hx2icon_height, 8, 0, 0, 0, 0); if (icon == NULL) return; SDL_SetColorKey(icon, SDL_SRCCOLORKEY, 0); color.r = 255; color.g = 255; color.b = 255; SDL_SetColors(icon, &color, 0, 1); /* just in case */ color.r = 192; color.g = 0; color.b = 0; SDL_SetColors(icon, &color, 1, 1); ptr = (Uint8 *)icon->pixels; /* one bit represents a pixel, black or white: each * byte in the xbm array contains data for 8 pixels. */ for (i = 0; i < (int) sizeof(hx2icon_bits); i++) { for (mask = 1; mask != 0x100; mask <<= 1) { *ptr = (hx2icon_bits[i] & mask) ? 1 : 0; ptr++; } } SDL_WM_SetIcon(icon, NULL); SDL_FreeSurface(icon); #endif /* !OSX */ } static int sort_modes (const void *arg1, const void *arg2) { const SDL_Rect *a1, *a2; a1 = *(SDL_Rect **) arg1; a2 = *(SDL_Rect **) arg2; if (a1->w == a2->w) return a1->h - a2->h; // lowres-to-highres // return a2->h - a1->h; // highres-to-lowres else return a1->w - a2->w; // lowres-to-highres // return a2->w - a1->w; // highres-to-lowres } static void VID_PrepareModes (SDL_Rect **sdl_modes) { int i, j; qboolean have_mem, is_multiple; SDL_Rect **cpy_modes; num_fmodes = 0; num_wmodes = 0; // Add the standart 4:3 modes to the windowed modes list // In an unlikely case that we receive no fullscreen modes, // this will be our modes list (kind of...) for (i = 0; i < (int)MAX_STDMODES; i++) { have_mem = VID_CheckAdequateMem(std_modes[i].width, std_modes[i].height); if (!have_mem) break; wmodelist[num_wmodes].width = std_modes[i].width; wmodelist[num_wmodes].height = std_modes[i].height; wmodelist[num_wmodes].halfscreen = 0; wmodelist[num_wmodes].fullscreen = 0; wmodelist[num_wmodes].bpp = 8; q_snprintf (wmodelist[num_wmodes].modedesc, MAX_DESC, "%d x %d", std_modes[i].width, std_modes[i].height); num_wmodes++; } // disaster scenario #1: no fullscreen modes. bind to the // windowed modes list. limit it to 640x480 max. because // we don't know the desktop dimensions if (sdl_modes == (SDL_Rect **)0) { no_fmodes: Con_SafePrintf ("No fullscreen video modes available\n"); if (num_wmodes > RES_640X480) num_wmodes = RES_640X480 + 1; modelist = wmodelist; nummodes = &num_wmodes; vid_default = 0; Cvar_SetValueQuick (&vid_config_swx, modelist[vid_default].width); Cvar_SetValueQuick (&vid_config_swy, modelist[vid_default].height); return; } // another disaster scenario (#2) if (sdl_modes == (SDL_Rect **)-1) { // Really should NOT HAVE happened! this return value is // for windowed modes! Since this means all resolutions // are supported, use our standart modes as modes list. Con_SafePrintf ("Unexpectedly received -1 from SDL_ListModes\n"); vid_maxwidth = MAXWIDTH; vid_maxheight = MAXHEIGHT; // num_fmodes = -1; num_fmodes = num_wmodes; nummodes = &num_wmodes; modelist = wmodelist; vid_default = 0; Cvar_SetValueQuick (&vid_config_swx, modelist[vid_default].width); Cvar_SetValueQuick (&vid_config_swy, modelist[vid_default].height); return; } #if 0 // print the un-processed modelist as reported by SDL for (j = 0; sdl_modes[j]; ++j) { Con_SafePrintf ("%d x %d\n", sdl_modes[j]->w, sdl_modes[j]->h); } Con_SafePrintf ("Total %d entries\n", j); #endif // sort the original list from low-res to high-res // so that the low resolutions take priority // but do so using a copy so that the original order // doesn't change, otherwise weird things may happen // with at least SDL >= 1.2.14. for (j = 0; sdl_modes[j]; ++j) ; cpy_modes = (SDL_Rect **) Z_Malloc ((j + 1) * sizeof(SDL_Rect *), Z_MAINZONE); for (j = 0; sdl_modes[j]; ++j) cpy_modes[j] = sdl_modes[j]; sdl_modes = cpy_modes; if (j > 1) qsort(sdl_modes, j, sizeof *sdl_modes, sort_modes); for (i = 0; sdl_modes[i] && num_fmodes < MAX_MODE_LIST; ++i) { // avoid multiple listings of the same dimension is_multiple = false; for (j = 0; j < num_fmodes; ++j) { if (fmodelist[j].width == sdl_modes[i]->w && fmodelist[j].height == sdl_modes[i]->h) { is_multiple = true; break; } } if (is_multiple) continue; // avoid resolutions < 320x240 if (sdl_modes[i]->w < MIN_WIDTH || sdl_modes[i]->h < MIN_HEIGHT) continue; // avoid very high resolutions otherwise waterwarp will segfault // (see r_shared.h) if (sdl_modes[i]->w > MAXWIDTH || sdl_modes[i]->h > MAXHEIGHT) continue; // automatically strip-off resolutions that we // don't have enough memory for have_mem = VID_CheckAdequateMem(sdl_modes[i]->w, sdl_modes[i]->h); if (!have_mem) continue; fmodelist[num_fmodes].width = sdl_modes[i]->w; fmodelist[num_fmodes].height = sdl_modes[i]->h; // FIXME: look at vid_win.c and learn how to // really functionalize the halfscreen field? fmodelist[num_fmodes].halfscreen = 0; fmodelist[num_fmodes].fullscreen = 1; fmodelist[num_fmodes].bpp = 8; q_snprintf (fmodelist[num_fmodes].modedesc, MAX_DESC, "%d x %d", sdl_modes[i]->w, sdl_modes[i]->h); num_fmodes++; } Z_Free (cpy_modes); if (!num_fmodes) goto no_fmodes; // At his point, we have a list of valid fullscreen modes: // Let's bind to it and use it for windowed modes, as well. // The only downside is that if SDL doesn't report any low // resolutions to us, we shall not have any for windowed // rendering where they would be perfectly legitimate... // Since our fullscreen/windowed toggling is instant and // doesn't require a vid_restart, switching lists won't be // feasible, either. The -width/-height commandline args // remain as the user's trusty old friends here. nummodes = &num_fmodes; modelist = fmodelist; vid_maxwidth = fmodelist[num_fmodes-1].width; vid_maxheight = fmodelist[num_fmodes-1].height; // see if we have 320x240 among the available modes for (i = 0; i < num_fmodes; i++) { if (fmodelist[i].width == 320 && fmodelist[i].height == 240) { vid_default = i; break; } } if (vid_default < 0) { // 320x240 not found among the supported dimensions // set default to the lowest resolution reported vid_default = 0; } // limit the windowed (standart) modes list to desktop dimensions for (i = 0; i < num_wmodes; i++) { if (wmodelist[i].width > vid_maxwidth || wmodelist[i].height > vid_maxheight) break; } if (i < num_wmodes) num_wmodes = i; Cvar_SetValueQuick (&vid_config_swx, modelist[vid_default].width); Cvar_SetValueQuick (&vid_config_swy, modelist[vid_default].height); } static void VID_ListModes_f (void) { int i; Con_Printf ("Maximum allowed mode: %d x %d\n", vid_maxwidth, vid_maxheight); Con_Printf ("Windowed modes enabled:\n"); for (i = 0; i < num_wmodes; i++) Con_Printf ("%2d: %d x %d\n", i, wmodelist[i].width, wmodelist[i].height); Con_Printf ("Fullscreen modes enumerated:"); if (num_fmodes) { Con_Printf ("\n"); for (i = 0; i < num_fmodes; i++) Con_Printf ("%2d: %d x %d\n", i, fmodelist[i].width, fmodelist[i].height); } else { Con_Printf (" None\n"); } } static void VID_NumModes_f (void) { Con_Printf ("%d video modes in current list\n", *nummodes); } static qboolean VID_SetMode (int modenum, const unsigned char *palette) { Uint32 flags; int is_fullscreen; in_mode_set = true; if (screen) SDL_FreeSurface(screen); flags = (SDL_SWSURFACE|SDL_HWPALETTE); if (vid_config_fscr.integer) flags |= SDL_FULLSCREEN; VID_SetIcon(); // Set the mode screen = SDL_SetVideoMode(modelist[modenum].width, modelist[modenum].height, modelist[modenum].bpp, flags); if (!screen) return false; // initial success. adjust vid vars. vid.height = vid.conheight = modelist[modenum].height; vid.width = vid.conwidth = modelist[modenum].width; vid.buffer = vid.conbuffer = vid.direct = (pixel_t *) screen->pixels; vid.rowbytes = vid.conrowbytes = screen->pitch; vid.numpages = 1; vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0); if (!VID_AllocBuffers (vid.width, vid.height)) return false; D_InitCaches (vid_surfcache, vid_surfcachesize); // real success. set vid_modenum properly. vid_modenum = modenum; is_fullscreen = (screen->flags & SDL_FULLSCREEN) ? 1 : 0; modestate = (is_fullscreen) ? MS_FULLDIB : MS_WINDOWED; Cvar_SetValueQuick (&vid_config_swx, modelist[vid_modenum].width); Cvar_SetValueQuick (&vid_config_swy, modelist[vid_modenum].height); Cvar_SetValueQuick (&vid_config_fscr, is_fullscreen); IN_HideMouse (); ClearAllStates(); VID_SetPalette (palette); SDL_WM_SetCaption(WM_TITLEBAR_TEXT, WM_ICON_TEXT); Con_SafePrintf ("Video Mode: %ux%ux%d\n", vid.width, vid.height, modelist[modenum].bpp); in_mode_set = false; vid.recalc_refdef = 1; return true; } // // VID_ChangeVideoMode // intended only as a callback for VID_Restart_f // static void VID_ChangeVideoMode (int newmode) { int temp; if (!screen) return; temp = scr_disabled_for_loading; scr_disabled_for_loading = true; CDAudio_Pause (); BGM_Pause (); S_ClearBuffer (); if (!VID_SetMode (newmode, vid_curpal)) { if (vid_modenum == newmode) Sys_Error ("Couldn't set video mode: %s", SDL_GetError()); // failed setting mode, probably due to insufficient // memory. go back to previous mode. Cvar_SetValueQuick (&vid_mode, vid_modenum); if (!VID_SetMode (vid_modenum, vid_curpal)) Sys_Error ("Couldn't set video mode: %s", SDL_GetError()); } CDAudio_Resume (); BGM_Resume (); scr_disabled_for_loading = temp; } static void VID_Restart_f (void) { if (vid_mode.integer < 0 || vid_mode.integer >= *nummodes) { Con_Printf ("Bad video mode %d\n", vid_mode.integer); Cvar_SetValueQuick (&vid_mode, vid_modenum); return; } Con_Printf ("Re-initializing video:\n"); VID_ChangeVideoMode (vid_mode.integer); } void VID_LockBuffer (void) { lockcount++; if (lockcount > 1) return; SDL_LockSurface(screen); // Update surface pointer for linear access modes vid.buffer = vid.conbuffer = vid.direct = (pixel_t *) screen->pixels; vid.rowbytes = vid.conrowbytes = screen->pitch; if (r_dowarp) d_viewbuffer = r_warpbuffer; else d_viewbuffer = vid.buffer; if (r_dowarp) screenwidth = WARP_WIDTH; else screenwidth = vid.rowbytes; } void VID_UnlockBuffer (void) { lockcount--; if (lockcount > 0) return; if (lockcount < 0) Sys_Error ("Unbalanced unlock"); SDL_UnlockSurface (screen); // to turn up any unlocked accesses //vid.buffer = vid.conbuffer = vid.direct = d_viewbuffer = NULL; } void VID_SetPalette (const unsigned char *palette) { int i; SDL_Color colors[256]; palette_changed = true; if (palette != vid_curpal) memcpy(vid_curpal, palette, sizeof(vid_curpal)); for (i = 0; i < 256; ++i) { colors[i].r = *palette++; colors[i].g = *palette++; colors[i].b = *palette++; } SDL_SetColors(screen, colors, 0, 256); } void VID_ShiftPalette (const unsigned char *palette) { VID_SetPalette (palette); } /* =================== VID_Init =================== */ void VID_Init (const unsigned char *palette) { int width, height, i, temp; SDL_Rect **enumlist; const char *read_vars[] = { "vid_config_fscr", "vid_config_swx", "vid_config_swy" }; #define num_readvars ( sizeof(read_vars)/sizeof(read_vars[0]) ) temp = scr_disabled_for_loading; scr_disabled_for_loading = true; Cvar_RegisterVariable (&vid_config_fscr); Cvar_RegisterVariable (&vid_config_swy); Cvar_RegisterVariable (&vid_config_swx); Cvar_RegisterVariable (&vid_config_gly); Cvar_RegisterVariable (&vid_config_glx); Cvar_RegisterVariable (&vid_mode); Cvar_RegisterVariable (&_enable_mouse); Cvar_RegisterVariable (&vid_showload); Cmd_AddCommand ("vid_listmodes", VID_ListModes_f); Cmd_AddCommand ("vid_nummodes", VID_NumModes_f); Cmd_AddCommand ("vid_restart", VID_Restart_f); // init sdl // the first check is actually unnecessary if ((SDL_WasInit(SDL_INIT_VIDEO)) == 0) { if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) Sys_Error("Couldn't init video: %s", SDL_GetError()); } // retrieve the list of fullscreen modes enumlist = SDL_ListModes(NULL, SDL_SWSURFACE|SDL_HWPALETTE|SDL_FULLSCREEN); // prepare the modelists, find the actual modenum for vid_default VID_PrepareModes(enumlist); // set vid_mode to our safe default first Cvar_SetValueQuick (&vid_mode, vid_default); // perform an early read of config.cfg CFG_ReadCvars (read_vars, num_readvars); // windowed mode is default // see if the user wants fullscreen if (COM_CheckParm("-fullscreen") || COM_CheckParm("-f")) { Cvar_SetQuick (&vid_config_fscr, "1"); } else if (COM_CheckParm("-window") || COM_CheckParm("-w")) { Cvar_SetQuick (&vid_config_fscr, "0"); } if (vid_config_fscr.integer && !num_fmodes) // FIXME: see below, as well Sys_Error ("No fullscreen modes available at this color depth"); width = vid_config_swx.integer; height = vid_config_swy.integer; // user is always right ... i = COM_CheckParm("-width"); if (i && i < com_argc-1) { // FIXME: this part doesn't know about a disaster case // like we aren't reported any fullscreen modes. width = atoi(com_argv[i+1]); i = COM_CheckParm("-height"); if (i && i < com_argc-1) height = atoi(com_argv[i+1]); else // proceed with 4/3 ratio height = 3 * width / 4; } // user requested a mode either from the config or from the // command line // scan existing modes to see if this is already available // if not, add this as the last "valid" video mode and set // vid_mode to it only if it doesn't go beyond vid_maxwidth i = 0; while (i < *nummodes) { if (modelist[i].width == width && modelist[i].height == height) break; i++; } if (i < *nummodes) { Cvar_SetValueQuick (&vid_mode, i); } else if ( (width <= vid_maxwidth && width >= MIN_WIDTH && height <= vid_maxheight && height >= MIN_HEIGHT) || COM_CheckParm("-force") ) { modelist[*nummodes].width = width; modelist[*nummodes].height = height; modelist[*nummodes].halfscreen = 0; modelist[*nummodes].fullscreen = 1; modelist[*nummodes].bpp = 8; q_snprintf (modelist[*nummodes].modedesc, MAX_DESC, "%d x %d (user mode)", width, height); Cvar_SetValueQuick (&vid_mode, *nummodes); (*nummodes)++; } else { Con_SafePrintf ("ignoring invalid -width and/or -height arguments\n"); } vid.maxwarpwidth = WARP_WIDTH; vid.maxwarpheight = WARP_HEIGHT; vid.colormap = host_colormap; vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); if (!VID_SetMode(vid_mode.integer, palette)) { if (vid_mode.integer == vid_default) Sys_Error ("Couldn't set video mode: %s", SDL_GetError()); // just one more try before dying Con_SafePrintf ("Couldn't set video mode %d\n" "Trying the default mode\n", vid_mode.integer); //Cvar_SetQuick (&vid_config_fscr, "0"); Cvar_SetValueQuick (&vid_mode, vid_default); if (!VID_SetMode(vid_default, palette)) Sys_Error ("Couldn't set video mode: %s", SDL_GetError()); } // lock the early-read cvars until Host_Init is finished for (i = 0; i < (int)num_readvars; i++) Cvar_LockVar (read_vars[i]); scr_disabled_for_loading = temp; vid_initialized = true; vid_menudrawfn = VID_MenuDraw; vid_menukeyfn = VID_MenuKey; } void VID_Shutdown (void) { if (vid_initialized) { if (screen != NULL && lockcount > 0) SDL_UnlockSurface (screen); vid_initialized = 0; SDL_QuitSubSystem(SDL_INIT_VIDEO); } } /* ================ FlipScreen ================ */ static void FlipScreen (vrect_t *rects) { while (rects) { SDL_UpdateRect (screen, rects->x, rects->y, rects->width, rects->height); rects = rects->pnext; } } void VID_Update (vrect_t *rects) { vrect_t rect; if (palette_changed) { palette_changed = false; rect.x = 0; rect.y = 0; rect.width = vid.width; rect.height = vid.height; rect.pnext = NULL; rects = ▭ } // We've drawn the frame; copy it to the screen FlipScreen (rects); // handle the mouse state when windowed if that's changed if (_enable_mouse.integer != enable_mouse /*&& modestate == MS_WINDOWED*/) { if (_enable_mouse.integer) IN_ActivateMouse (); else IN_DeactivateMouse (); enable_mouse = _enable_mouse.integer; } } /* ================ D_BeginDirectRect ================ */ void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) { // these bits from quakeforge Uint8 *offset; if (!screen) return; if (x < 0) x = screen->w + x - 1; offset = (Uint8 *) screen->pixels + y * screen->pitch + x; while (height--) { memcpy (offset, pbitmap, width); offset += screen->pitch; pbitmap += width; } } /* ================ D_EndDirectRect ================ */ void D_EndDirectRect (int x, int y, int width, int height) { // these bits from quakeforge if (!screen) return; if (x < 0) x = screen->w + x - 1; SDL_UpdateRect (screen, x, y, width, height); } #ifndef H2W // unused in hexenworld void D_ShowLoadingSize (void) { #if defined(DRAW_PROGRESSBARS) static int prev_perc; int cur_perc; vrect_t rect; viddef_t save_vid; // global video state if (!vid_showload.integer) return; if (!vid_initialized) return; cur_perc = loading_stage * 100; if (total_loading_size) cur_perc += current_loading_size * 100 / total_loading_size; if (cur_perc == prev_perc) return; prev_perc = cur_perc; save_vid = vid; if (vid.numpages == 1) { VID_LockBuffer (); if (!vid.direct) Sys_Error ("NULL vid.direct pointer"); vid.buffer = vid.direct; SCR_DrawLoading(); VID_UnlockBuffer (); rect.x = 0; rect.y = 0; rect.width = vid.width; rect.height = 112; rect.pnext = NULL; FlipScreen (&rect); } else { vid.buffer = (byte *)screen->pixels; vid.rowbytes = screen->pitch; SCR_DrawLoading(); } vid = save_vid; #endif /* DRAW_PROGRESSBARS */ } #endif /* ============================ Gamma functions for UNIX/SDL ============================ */ #if 0 static void VID_SetGamma (void) { float value = (v_gamma.value > (1.0 / GAMMA_MAX))? (1.0 / v_gamma.value) : GAMMA_MAX; SDL_SetGamma(value,value,value); } #endif //========================================================================== /* ================ VID_HandlePause ================ */ void VID_HandlePause (qboolean paused) { if (_enable_mouse.integer /*&& (modestate == MS_WINDOWED)*/) { // for consistency, don't show pointer - S.A if (paused) { IN_DeactivateMouse (); // IN_ShowMouse (); } else { IN_ActivateMouse (); // IN_HideMouse (); } } } /* ================ VID_ToggleFullscreen Handles switching between fullscreen/windowed modes and brings the mouse to a proper state afterwards ================ */ extern qboolean menu_disabled_mouse; void VID_ToggleFullscreen (void) { int is_fullscreen; if (!screen) return; if (!fs_toggle_works) return; if (!num_fmodes) return; S_ClearBuffer (); fs_toggle_works = (SDL_WM_ToggleFullScreen(screen) == 1); if (fs_toggle_works) { is_fullscreen = (screen->flags & SDL_FULLSCREEN) ? 1 : 0; Cvar_SetValueQuick(&vid_config_fscr, is_fullscreen); modestate = (is_fullscreen) ? MS_FULLDIB : MS_WINDOWED; if (is_fullscreen) { // activate mouse in fullscreen mode // in_sdl.c handles other non-moused cases if (menu_disabled_mouse) IN_ActivateMouse(); } else { // windowed mode: // deactivate mouse if we are in menus if (menu_disabled_mouse) IN_DeactivateMouse(); } // update the video menu option vid_menu_fs = (modestate != MS_WINDOWED); } else { Con_Printf ("SDL_WM_ToggleFullScreen failed\n"); } } //======================================================== // Video menu stuff //======================================================== static int vid_menunum; static int vid_cursor; static qboolean want_fstoggle, need_apply; static qboolean vid_menu_firsttime = true; enum { VID_FULLSCREEN, // make sure the fullscreen entry (0) VID_RESOLUTION, // is lower than resolution entry (1) VID_BLANKLINE, // spacer line VID_RESET, VID_APPLY, VID_ITEMS }; static void M_DrawYesNo (int x, int y, int on, int white) { if (on) { if (white) M_PrintWhite (x, y, "yes"); else M_Print (x, y, "yes"); } else { if (white) M_PrintWhite (x, y, "no"); else M_Print (x, y, "no"); } } /* ================ VID_MenuDraw ================ */ static void VID_MenuDraw (void) { ScrollTitle("gfx/menu/title7.lmp"); if (vid_menu_firsttime) { // settings for entering the menu first time vid_menunum = vid_modenum; vid_menu_fs = (modestate != MS_WINDOWED); vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; vid_menu_firsttime = false; } want_fstoggle = ( ((modestate == MS_WINDOWED) && vid_menu_fs) || ((modestate != MS_WINDOWED) && !vid_menu_fs) ); need_apply = (vid_menunum != vid_modenum) || want_fstoggle; M_Print (76, 92 + 8*VID_FULLSCREEN, "Fullscreen: "); M_DrawYesNo (76+12*8, 92 + 8*VID_FULLSCREEN, vid_menu_fs, !want_fstoggle); M_Print (76, 92 + 8*VID_RESOLUTION, "Resolution: "); if (vid_menunum == vid_modenum) M_PrintWhite (76+12*8, 92 + 8*VID_RESOLUTION, modelist[vid_menunum].modedesc); else M_Print (76+12*8, 92 + 8*VID_RESOLUTION, modelist[vid_menunum].modedesc); if (need_apply) { M_Print (76, 92 + 8*VID_RESET, "RESET CHANGES"); M_Print (76, 92 + 8*VID_APPLY, "APPLY CHANGES"); } M_DrawCharacter (64, 92 + vid_cursor*8, 12+((int)(realtime*4)&1)); } /* ================ VID_MenuKey ================ */ static void VID_MenuKey (int key) { switch (key) { case K_ESCAPE: vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; M_Menu_Options_f (); return; case K_ENTER: switch (vid_cursor) { case VID_RESET: vid_menu_fs = (modestate != MS_WINDOWED); vid_menunum = vid_modenum; vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; break; case VID_APPLY: if (need_apply) { Cvar_SetValueQuick(&vid_mode, vid_menunum); Cvar_SetValueQuick(&vid_config_fscr, vid_menu_fs); VID_Restart_f(); } vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; break; } return; case K_LEFTARROW: switch (vid_cursor) { case VID_FULLSCREEN: vid_menu_fs = !vid_menu_fs; if (fs_toggle_works) VID_ToggleFullscreen(); break; case VID_RESOLUTION: S_LocalSound ("raven/menu1.wav"); vid_menunum--; if (vid_menunum < 0) vid_menunum = 0; break; } return; case K_RIGHTARROW: switch (vid_cursor) { case VID_FULLSCREEN: vid_menu_fs = !vid_menu_fs; if (fs_toggle_works) VID_ToggleFullscreen(); break; case VID_RESOLUTION: S_LocalSound ("raven/menu1.wav"); vid_menunum++; if (vid_menunum >= *nummodes) vid_menunum = *nummodes - 1; break; } return; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); vid_cursor--; if (vid_cursor < 0) { vid_cursor = (need_apply) ? VID_ITEMS-1 : VID_BLANKLINE-1; } else if (vid_cursor == VID_BLANKLINE) { vid_cursor--; } break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); vid_cursor++; if (vid_cursor >= VID_ITEMS) { vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; break; } if (vid_cursor >= VID_BLANKLINE) { if (need_apply) { if (vid_cursor == VID_BLANKLINE) vid_cursor++; } else { vid_cursor = (num_fmodes) ? 0 : VID_RESOLUTION; } } break; default: return; } } engine/h2shared/vid_svgalib.c000066400000000000000000000405071444734033100164710ustar00rootroot00000000000000/* vid_svgalib.c: Linux SVGALIB specific video driver. * from quake1 source with minor adaptations for uhexen2. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #if 0 /* these should be for vt stuff */ #include #include #include #include #include #include #include #endif #include "q_stdinc.h" /*#include */ /* outb()*/ #include "vga.h" #include "quakedef.h" #include "d_local.h" unsigned short d_8to16table[256]; /* not used in 8 bpp mode */ unsigned int d_8to24table[256]; /* not used in 8 bpp mode */ byte globalcolormap[VID_GRADES*256], lastglobalcolor = 0; byte *lastsourcecolormap = NULL; viddef_t vid; /* global video state */ int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes; byte *VGA_pagebase; static byte *framebuffer_ptr; static int VGA_planar; static byte backingbuf[48*24]; static byte *vid_surfcache; static int VID_highhunkmark; static int num_modes; static vga_modeinfo *modes; static int current_mode; static byte vid_current_palette[768]; /* save for mode changes */ static int svgalib_inited = 0; static int svgalib_backgrounded = 0; static cvar_t vid_mode = {"vid_mode", "5", CVAR_NONE}; static cvar_t vid_redrawfull = {"vid_redrawfull", "0", CVAR_NONE}; static cvar_t vid_waitforrefresh = {"vid_waitforrefresh", "0", CVAR_ARCHIVE}; /* globals for compatibility: */ modestate_t modestate = MS_UNINIT; cvar_t _enable_mouse = {"_enable_mouse", "1", CVAR_ARCHIVE}; #if 0 static void vtswitch (int newconsole) { int fd; struct vt_stat x; /* switch consoles and wait until reactivated */ fd = open("/dev/console", O_RDONLY); ioctl(fd, VT_GETSTATE, &x); ioctl(fd, VT_ACTIVATE, newconsole); ioctl(fd, VT_WAITACTIVE, x.v_active); close(fd); } #endif /* ================= VID_Gamma_f Keybinding command ================= */ #if 0 static void VID_Gamma_f (void) { float gamma, f, inf; unsigned char palette[768]; int i; if (Cmd_Argc () == 2) { gamma = atof (Cmd_Argv(1)); for (i = 0; i < 768; i++) { f = pow ((host_basepal[i] + 1) / 256.0, gamma); inf = f*255 + 0.5; if (inf < 0) inf = 0; if (inf > 255) inf = 255; palette[i] = inf; } VID_SetPalette (palette); vid.recalc_refdef = 1; /* force a surface cache flush */ } } #endif static void VID_DescribeMode_f (void) { int modenum; modenum = atoi (Cmd_Argv(1)); if ((modenum >= num_modes) || (modenum < 0 ) || !modes[modenum].width) Con_Printf("Invalid video mode: %d!\n", modenum); else { Con_Printf("%d: %d x %d - ", modenum, modes[modenum].width, modes[modenum].height); if (modes[modenum].bytesperpixel == 0) Con_Printf("ModeX\n"); else Con_Printf("%d bpp\n", modes[modenum].bytesperpixel<<3); } } static void VID_DescribeModes_f (void) { int i; for (i = 0; i < num_modes; i++) { if (modes[i].width) { Con_Printf("%d: %d x %d - ", i, modes[i].width, modes[i].height); if (modes[i].bytesperpixel == 0) Con_Printf("ModeX\n"); else Con_Printf("%d bpp\n", modes[i].bytesperpixel<<3); } } } /* ================ VID_NumModes ================ */ static int VID_NumModes (void) { int i, i1 = 0; for (i = 0; i < num_modes; i++) i1 += (modes[i].width ? 1 : 0); return (i1); } static void VID_NumModes_f (void) { Con_Printf("%d modes\n", VID_NumModes()); } static void VID_Debug_f (void) { Con_Printf("mode: %d\n", current_mode); Con_Printf("height x width: %d x %d\n", vid.height, vid.width); Con_Printf("bpp: %d\n", modes[current_mode].bytesperpixel*8); Con_Printf("vid.aspect: %f\n", vid.aspect); } static void VID_InitModes (void) { int i; /* get complete information on all modes */ num_modes = vga_lastmodenumber() + 1; modes = (vga_modeinfo *) Z_Malloc(num_modes * sizeof(vga_modeinfo), Z_MAINZONE); for (i = 0; i < num_modes; i++) { if (vga_hasmode(i)) memcpy(&modes[i], vga_getmodeinfo(i), sizeof(vga_modeinfo)); else modes[i].width = 0; /* means not available */ } /* filter for modes i don't support */ for (i = 0; i < num_modes; i++) { if (modes[i].bytesperpixel != 1 && modes[i].colors != 256) modes[i].width = 0; } } static int get_mode (char *name, int width, int height, int depth) { int i; if (name) { i = vga_getmodenumber(name); if (i < 0 || !modes[i].width) { Sys_Printf("Mode [%s] not supported\n", name); i = -1; } } else { int need, match; need = (!!width) | ((!!height) << 1) | ((!!depth) << 2); for (i = 0; i < num_modes; i++) { if (!modes[i].width) continue; if (width && modes[i].width != width) continue; if (height && modes[i].height != height) continue; if (depth && modes[i].bytesperpixel != depth/8) continue; match = ((modes[i].width == width) << 0) | ((modes[i].height == height) << 1) | ((modes[i].bytesperpixel == depth/8) << 2); if (match & need) break; /* got a match */ } if (i == num_modes) { Sys_Printf("Mode %dx%d (%d bits) not supported\n", width, height, depth); i = -1; } } return i; } void VID_Shutdown (void) { if (!svgalib_inited) return; vga_setmode(TEXT); svgalib_inited = 0; } void VID_SetPalette (const unsigned char *palette) { static int tmppal[256*3]; int *tp; int i; if (!svgalib_inited) return; if (svgalib_backgrounded) return; if (palette != vid_current_palette) memcpy(vid_current_palette, palette, sizeof(vid_current_palette)); if (vga_getcolors() == 256) { tp = tmppal; for (i = 256 * 3; i; i--) *(tp++) = *(palette++) >> 2; if (vga_oktowrite()) vga_setpalvec(0, 256, tmppal); } } void VID_ShiftPalette (const unsigned char *palette) { VID_SetPalette (palette); } static qboolean VID_SetMode (int modenum, const unsigned char *palette) { int bsize, zsize, tsize; if ((modenum >= num_modes) || (modenum < 0) || !modes[modenum].width) { Cvar_SetValueQuick (&vid_mode, (float)current_mode); Con_Printf("No such video mode: %d\n",modenum); return false; } Cvar_SetValueQuick (&vid_mode, (float)modenum); current_mode = modenum; vid.width = modes[current_mode].width; vid.height = modes[current_mode].height; VGA_width = modes[current_mode].width; VGA_height = modes[current_mode].height; VGA_planar = modes[current_mode].bytesperpixel == 0; VGA_rowbytes = modes[current_mode].linewidth; vid.rowbytes = modes[current_mode].linewidth; if (VGA_planar) { VGA_bufferrowbytes = modes[current_mode].linewidth * 4; vid.rowbytes = modes[current_mode].linewidth*4; } vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0); vid.colormap = (pixel_t *) host_colormap; vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); vid.conrowbytes = vid.rowbytes; vid.conwidth = vid.width; vid.conheight = vid.height; vid.numpages = 1; vid.maxwarpwidth = WARP_WIDTH; vid.maxwarpheight = WARP_HEIGHT; /* alloc zbuffer and surface cache */ if (d_pzbuffer) { D_FlushCaches(); Hunk_FreeToHighMark (VID_highhunkmark); d_pzbuffer = NULL; vid_surfcache = NULL; } bsize = vid.rowbytes * vid.height; tsize = D_SurfaceCacheForRes (vid.width, vid.height); zsize = vid.width * vid.height * sizeof(*d_pzbuffer); VID_highhunkmark = Hunk_HighMark (); d_pzbuffer = (short *) Hunk_HighAllocName (bsize + tsize + zsize, "video"); vid_surfcache = ((byte *)d_pzbuffer) + zsize; vid.conbuffer = vid.buffer = (pixel_t *)(((byte *)d_pzbuffer) + zsize + tsize); D_InitCaches (vid_surfcache, tsize); /* get goin' */ if (vga_setmode(current_mode) != 0) Sys_Error("Unable to set mode %d", current_mode); /*if (vga_setlinearaddressing() > 0)*/ VGA_pagebase = vid.direct = framebuffer_ptr = (byte *) vga_getgraphmem(); if (!framebuffer_ptr) Sys_Error("Unable to get framebuffer ptr for mode %d", current_mode); VID_SetPalette(palette); vid.recalc_refdef = 1; /* force a surface cache flush */ vga_setpage(0); svgalib_inited = 1; return true; } /* backgrounding fixes (quakeforge) */ static void goto_background (void) { svgalib_backgrounded = 1; } static void comefrom_background (void) { svgalib_backgrounded = 0; VID_SetPalette(vid_current_palette); } void VID_Init (const unsigned char *palette) { int i, w, h, d; char *modename; if (svgalib_inited) return; if (vga_init() != 0) Sys_Error ("SVGALib failed to allocate a new VC"); i = vga_runinbackground_version(); if (i > 0) { Sys_Printf ("SVGALIB background support %i detected\n", i); vga_runinbackground (VGA_GOTOBACK, goto_background); vga_runinbackground (VGA_COMEFROMBACK, comefrom_background); vga_runinbackground (1); } else { vga_runinbackground (0); } VID_InitModes(); if (!VID_NumModes()) Sys_Error ("No supported modes reported by SVGAlib"); // Cvar_RegisterVariable (&_enable_mouse); Cvar_RegisterVariable (&vid_mode); Cvar_RegisterVariable (&vid_redrawfull); Cvar_RegisterVariable (&vid_waitforrefresh); // Cmd_AddCommand ("gamma", VID_Gamma_f); Cmd_AddCommand("vid_nummodes", VID_NumModes_f); Cmd_AddCommand("vid_describemode", VID_DescribeMode_f); Cmd_AddCommand("vid_describemodes", VID_DescribeModes_f); Cmd_AddCommand("vid_debug", VID_Debug_f); /* interpret command-line params */ current_mode = -1; w = h = d = 0; modename = getenv("GSVGAMODE"); if (modename) current_mode = get_mode(modename, 0, 0, 0); else { i = COM_CheckParm("-mode"); if (i) { if (i >= com_argc - 1) Sys_Error("%s: -mode ", __thisfunc__); current_mode = get_mode(com_argv[i + 1], 0, 0, 0); } } if (current_mode == -1) { i = COM_CheckParm("-width"); if (i) { if (i >= com_argc - 1) Sys_Error("%s: -width ", __thisfunc__); w = atoi(com_argv[i + 1]); } i = COM_CheckParm("-height"); if (i) { if (i >= com_argc - 1) Sys_Error("%s: -height ", __thisfunc__); h = atoi(com_argv[i + 1]); } i = COM_CheckParm("-bpp"); if (i) { if (i >= com_argc - 1) Sys_Error("%s: -bpp ", __thisfunc__); d = atoi(com_argv[i + 1]); } if (w || h || d) current_mode = get_mode(NULL, w, h, d); } if (current_mode == -1) { if (vga_hasmode(G320x200x256)) current_mode = G320x200x256; else { Sys_Printf ("Mode 13h not supported\n"); #if defined(G320x200x256V) /* svgalib-1.9 may do this */ if (vga_hasmode(G320x200x256V)) current_mode = G320x200x256V; else #endif /* try the first available */ for (i = 0; i < num_modes; i++) { if (modes[i].width != 0) { current_mode = i; break; } } Sys_Printf ("Will try setting mode %d\n", current_mode); } } /* set vid parameters */ if (!VID_SetMode (current_mode, palette)) Sys_Error ("Unable to set a video mode"); VID_SetPalette(palette); } void VID_Update (vrect_t *rects) { if (!svgalib_inited) return; if (svgalib_backgrounded) return; if (!vga_oktowrite()) return; /* can't update screen if it's not active */ if (vid_waitforrefresh.integer) vga_waitretrace(); if (VGA_planar) { /* VGA_UpdatePlanarScreen() of d_copy.asm only works with * /dev/mem, i.e. without the svgalib_helper kernel module * introduced in svgalib-1.9.x. must use an appropriate * svgalib api function, such as vga_copytoplanar256() */ vga_copytoplanar256(vid.buffer, VGA_bufferrowbytes, 0, VGA_rowbytes, VGA_width, VGA_height); } else if (vid_redrawfull.integer) { int total = vid.rowbytes * vid.height; int offset; for (offset = 0; offset < total; offset += 0x10000) { vga_setpage(offset / 0x10000); memcpy(framebuffer_ptr, vid.buffer + offset, ((total-offset > 0x10000) ? 0x10000 : (total - offset))); } } else { int ycount; int offset; int vidpage = 0; vga_setpage(0); while (rects) { ycount = rects->height; offset = rects->y * vid.rowbytes + rects->x; while (ycount--) { register int i = offset % 0x10000; if ((offset / 0x10000) != vidpage) { vidpage = offset / 0x10000; vga_setpage(vidpage); } if (rects->width + i > 0x10000) { memcpy(framebuffer_ptr + i, vid.buffer + offset, 0x10000 - i); vga_setpage(++vidpage); memcpy(framebuffer_ptr, vid.buffer + offset + 0x10000 - i, rects->width - 0x10000 + i); } else memcpy(framebuffer_ptr + i, vid.buffer + offset, rects->width); offset += vid.rowbytes; } rects = rects->pnext; } } if (vid_mode.integer != current_mode) VID_SetMode (vid_mode.integer, vid_current_palette); } /* ================ D_BeginDirectRect ================ */ void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) { int i, j, reps, repshift, offset, vidpage, off; if (!svgalib_inited || !vid.direct || !vga_oktowrite()) return; if (svgalib_backgrounded) return; if (vid.aspect > 1.5) { reps = 2; repshift = 1; } else { reps = 1; repshift = 0; } vidpage = 0; vga_setpage(0); if (VGA_planar) { #if 0 /* with svgalib-1.9.x, we can't use outb() directly because * it only works for /dev/mem, i.e. without svgalib_helper * kernel module. the equivalent __svgalib_port_out () is * an internal helper function of svgalib (libvga.h) and is * not officially exported. therefore disabling this part * which isn't an important functionality at all. */ int k, plane; for (plane = 0; plane < 4; plane++) { /* select the correct plane for reading and writing */ outb(0x02, 0x3C4); outb(1 << plane, 0x3C5); outb(4, 0x3CE); outb(plane, 0x3CF); for (i = 0; i < (height << repshift); i += reps) { for (k = 0; k < reps; k++) { for (j = 0; j < (width >> 2); j++) { backingbuf[(i + k) * 24 + (j << 2) + plane] = vid.direct[(y + i + k) * VGA_rowbytes + (x >> 2) + j]; vid.direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] = pbitmap[(i >> repshift) * 24 + (j << 2) + plane]; } } } } #endif } else { for (i = 0; i < (height << repshift); i += reps) { for (j = 0; j < reps; j++) { offset = x + ((y << repshift) + i + j) * vid.rowbytes; off = offset % 0x10000; if ((offset / 0x10000) != vidpage) { vidpage = offset / 0x10000; vga_setpage(vidpage); } memcpy (&backingbuf[(i + j) * 24], vid.direct + off, width); memcpy (vid.direct + off, &pbitmap[(i >> repshift)*width], width); } } } } /* ================ D_EndDirectRect ================ */ void D_EndDirectRect (int x, int y, int width, int height) { int i, j, reps, repshift, offset, vidpage, off; if (!svgalib_inited || !vid.direct || !vga_oktowrite()) return; if (svgalib_backgrounded) return; if (vid.aspect > 1.5) { reps = 2; repshift = 1; } else { reps = 1; repshift = 0; } vidpage = 0; vga_setpage(0); if (VGA_planar) { #if 0 /* see in D_BeginDirectRect() */ int k, plane; for (plane = 0; plane < 4; plane++) { /* select the correct plane for writing */ outb(2, 0x3C4); outb(1 << plane, 0x3C5); outb(4, 0x3CE); outb(plane, 0x3CF); for (i = 0; i < (height << repshift); i += reps) { for (k = 0; k < reps; k++) { for (j = 0; j < (width >> 2); j++) { vid.direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] = backingbuf[(i + k) * 24 + (j << 2) + plane]; } } } } #endif } else { for (i = 0; i < (height << repshift); i += reps) { for (j = 0; j < reps; j++) { offset = x + ((y << repshift) + i + j) * vid.rowbytes; off = offset % 0x10000; if ((offset / 0x10000) != vidpage) { vidpage = offset / 0x10000; vga_setpage(vidpage); } memcpy (vid.direct + off, &backingbuf[(i +j)*24], width); } } } } void D_ShowLoadingSize (void) { #if defined(DRAW_PROGRESSBARS) /* to be implemented. */ #endif /* !DRAW_PROGRESSBARS */ } void VID_LockBuffer (void) { /* nothing to do */ } void VID_UnlockBuffer (void) { /* nothing to do */ } void VID_HandlePause (qboolean paused) { if (paused) IN_DeactivateMouse (); else IN_ActivateMouse (); } engine/h2shared/vid_vga.c000066400000000000000000000272241444734033100156200ustar00rootroot00000000000000/* vid_vga.c -- VGA-specific DOS video stuff * TODO: proper handling of page-swap failure * from quake1 source with minor adaptations for uhexen2. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "quakedef.h" #include "d_local.h" #include "dosisms.h" #include "vid_dos.h" int VGA_width, VGA_height, VGA_rowbytes, VGA_bufferrowbytes; byte *VGA_pagebase; vmode_t *VGA_pcurmode; static int VGA_planar; static int VGA_numpages; static int VGA_buffersize; static int VGA_highhunkmark; /* these two are shared with vid_ext.c: */ void *vid_surfcache; int vid_surfcachesize; static byte backingbuf[48*24]; static int VGA_InitMode (viddef_t *lvid, vmode_t *pcurrentmode); static void VGA_SetPalette (viddef_t *lvid, vmode_t *pcurrentmode, const unsigned char *pal); static void VGA_SwapBuffers (viddef_t *lvid, vmode_t *pcurrentmode, vrect_t *rects); static void VGA_SwapBuffersCopy (viddef_t *lvid, vmode_t *pcurrentmode, vrect_t *rects); #include "vgamodes.h" #define NUMVIDMODES (sizeof(vgavidmodes) / sizeof(vgavidmodes[0])) /* ================ VGA_BeginDirectRect ================ */ void VGA_BeginDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, int x, int y, byte *pbitmap, int width, int height) { int i, j, k, plane, reps, repshift; if (!lvid->direct) return; if (lvid->aspect > 1.5) { reps = 2; repshift = 1; } else { reps = 1; repshift = 0; } if (pcurrentmode->planar) { for (plane = 0; plane < 4; plane++) { // select the correct plane for reading and writing outportb (SC_INDEX, MAP_MASK); outportb (SC_DATA, 1 << plane); outportb (GC_INDEX, READ_MAP); outportb (GC_DATA, plane); for (i = 0; i < (height << repshift); i += reps) { for (k = 0; k < reps; k++) { for (j = 0; j < (width >> 2); j++) { backingbuf[(i + k) * 24 + (j << 2) + plane] = lvid->direct[(y + i + k) * VGA_rowbytes + (x >> 2) + j]; lvid->direct[(y + i + k) * VGA_rowbytes + (x>>2) + j] = pbitmap[(i >> repshift) * 24 + (j << 2) + plane]; } } } } } else { for (i = 0; i < (height << repshift); i += reps) { for (j = 0; j < reps; j++) { memcpy (&backingbuf[(i + j) * 24], lvid->direct + x + ((y << repshift) + i + j) * VGA_rowbytes, width); memcpy (lvid->direct + x + ((y << repshift) + i + j) * VGA_rowbytes, &pbitmap[(i >> repshift) * width], width); } } } } /* ================ VGA_EndDirectRect ================ */ void VGA_EndDirectRect (viddef_t *lvid, struct vmode_s *pcurrentmode, int x, int y, int width, int height) { int i, j, k, plane, reps, repshift; if (!lvid->direct) return; if (lvid->aspect > 1.5) { reps = 2; repshift = 1; } else { reps = 1; repshift = 0; } if (pcurrentmode->planar) { for (plane = 0; plane < 4; plane++) { // select the correct plane for writing outportb (SC_INDEX, MAP_MASK); outportb (SC_DATA, 1 << plane); for (i = 0; i < (height << repshift); i += reps) { for (k = 0; k < reps; k++) { for (j = 0; j < (width >> 2); j++) { lvid->direct[(y + i + k) * VGA_rowbytes + (x >> 2) + j] = backingbuf[(i + k) * 24 + (j << 2) + plane]; } } } } } else { for (i = 0; i < (height << repshift); i += reps) { for (j = 0; j < reps; j++) { memcpy (lvid->direct + x + ((y << repshift) + i + j) * VGA_rowbytes, &backingbuf[(i + j) * 24], width); } } } } /* ================ VGA_Init ================ */ void VGA_Init (void) { int i; // link together all the VGA modes for (i = 0; i < (NUMVIDMODES - 1); i++) { vgavidmodes[i].pnext = &vgavidmodes[i+1]; } // add the VGA modes at the start of the mode list vgavidmodes[NUMVIDMODES-1].pnext = pvidmodes; pvidmodes = &vgavidmodes[0]; numvidmodes += NUMVIDMODES; } /* ================ VGA_WaitVsync ================ */ void VGA_WaitVsync (void) { while ((inportb (0x3DA) & 0x08) == 0) ; } /* ================ VGA_ClearVideoMem ================ */ void VGA_ClearVideoMem (int planar) { if (planar) { // enable all planes for writing outportb (SC_INDEX, MAP_MASK); outportb (SC_DATA, 0x0F); } memset (VGA_pagebase, 0, VGA_rowbytes * VGA_height); } /* ================ VGA_FreeAndAllocVidbuffer ================ */ qboolean VGA_FreeAndAllocVidbuffer (viddef_t *lvid, int allocnewbuffer) { int tsize, tbuffersize; if (allocnewbuffer) { // alloc an extra line in case we want to wrap, and allocate the z-buffer tbuffersize = (lvid->rowbytes * (lvid->height + 1)) + (lvid->width * lvid->height * sizeof (*d_pzbuffer)); } else { // just allocate the z-buffer tbuffersize = lvid->width * lvid->height * sizeof (*d_pzbuffer); } tsize = D_SurfaceCacheForRes (lvid->width, lvid->height); tbuffersize += tsize; // see if there's enough memory, allowing for the normal mode 0x13 pixel, // z, and surface buffers //if ((host_parms->memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + // 0x10000 * 3) < MINIMUM_MEMORY) // Pa3PyX: using hopefully better estimation now // if total memory < needed surface cache + (minimum operational memory // less surface cache for 320x200 and typical hunk state after init) if (host_parms->memsize < tbuffersize + 0x180000 + 0xC00000) { Con_Printf ("Not enough memory for video mode\n"); VGA_pcurmode = NULL; // so no further accesses to the buffer are // attempted, particularly when clearing return false; // not enough memory for mode } VGA_buffersize = tbuffersize; vid_surfcachesize = tsize; if (d_pzbuffer) { D_FlushCaches (); Hunk_FreeToHighMark (VGA_highhunkmark); d_pzbuffer = NULL; } VGA_highhunkmark = Hunk_HighMark (); d_pzbuffer = (short *) Hunk_HighAllocName (VGA_buffersize, "video"); vid_surfcache = (byte *)d_pzbuffer + lvid->width * lvid->height * sizeof (*d_pzbuffer); if (allocnewbuffer) { lvid->buffer = (pixel_t *)( (byte *)vid_surfcache + vid_surfcachesize); lvid->conbuffer = lvid->buffer; } return true; } /* ================ VGA_CheckAdequateMem ================ */ qboolean VGA_CheckAdequateMem (int width, int height, int rowbytes, int allocnewbuffer) { int tbuffersize; tbuffersize = width * height * sizeof (*d_pzbuffer); if (allocnewbuffer) { // alloc an extra line in case we want to wrap, and allocate the z-buffer tbuffersize += (rowbytes * (height + 1)); } tbuffersize += D_SurfaceCacheForRes (width, height); // see if there's enough memory, allowing for the normal mode 0x13 pixel, // z, and surface buffers //if ((host_parms->memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + // 0x10000 * 3) < MINIMUM_MEMORY) // Pa3PyX: using hopefully better estimation now // if total memory < needed surface cache + (minimum operational memory // less surface cache for 320x200 and typical hunk state after init) if (host_parms->memsize < tbuffersize + 0x180000 + 0xC00000) { return false; // not enough memory for mode } return true; } /* ================ VGA_InitMode ================ */ static int VGA_InitMode (viddef_t *lvid, vmode_t *pcurrentmode) { vextra_t *pextra; pextra = (vextra_t *) pcurrentmode->pextradata; if (!VGA_FreeAndAllocVidbuffer (lvid, pextra->vidbuffer)) return -1; // memory alloc failed if (VGA_pcurmode) VGA_ClearVideoMem (VGA_pcurmode->planar); // mode 0x13 is the base for all the Mode X-class mode sets regs.h.ah = 0; regs.h.al = 0x13; dos_int86(0x10); VGA_pagebase = (byte *)real2ptr(0xa0000); lvid->direct = (pixel_t *)VGA_pagebase; // set additional registers as needed VideoRegisterSet (pextra->pregset); VGA_numpages = 1; lvid->numpages = VGA_numpages; VGA_width = (lvid->width + 0x1F) & ~0x1F; VGA_height = lvid->height; VGA_planar = pcurrentmode->planar; if (VGA_planar) VGA_rowbytes = lvid->rowbytes / 4; else VGA_rowbytes = lvid->rowbytes; VGA_bufferrowbytes = lvid->rowbytes; lvid->colormap = host_colormap; lvid->fullbright = 256 - LittleLong (*((int *)lvid->colormap + 2048)); lvid->maxwarpwidth = WARP_WIDTH; lvid->maxwarpheight = WARP_HEIGHT; lvid->conbuffer = lvid->buffer; lvid->conrowbytes = lvid->rowbytes; lvid->conwidth = lvid->width; lvid->conheight = lvid->height; VGA_pcurmode = pcurrentmode; VGA_ClearVideoMem (pcurrentmode->planar); if (_vid_wait_override.integer) { Cvar_SetValueQuick (&vid_wait, (float)VID_WAIT_VSYNC); } else { Cvar_SetValueQuick (&vid_wait, (float)VID_WAIT_NONE); } D_InitCaches (vid_surfcache, vid_surfcachesize); return 1; } /* ================ VGA_SetPalette ================ */ static void VGA_SetPalette (viddef_t *lvid, vmode_t *pcurrentmode, const unsigned char *pal) { int shiftcomponents = 2; int i; Q_UNUSED(lvid); Q_UNUSED(pcurrentmode); dos_outportb(0x3c8, 0); for (i = 0; i < 768; i++) outportb(0x3c9, pal[i]>>shiftcomponents); } #if !id386 void VGA_UpdatePlanarScreen (void *srcbuffer) { /* based on vga_copytoplanar256() of SVGAlib */ byte *p; int ofs; int plane, x, y; plane = 0; /* Copy pixels that belong in plane. */ _docopy: outportb(SC_INDEX, MAP_MASK); outportb(SC_DATA, 1 << plane); p = (byte *) srcbuffer; ofs = 0; for (y = 0; y < VGA_height; y++) { x = 0; while (x * 4 + 32 < VGA_width) { VGA_pagebase[ofs + x + 0] = p[x * 4 + plane + 0]; VGA_pagebase[ofs + x + 1] = p[x * 4 + plane + 4]; VGA_pagebase[ofs + x + 2] = p[x * 4 + plane + 8]; VGA_pagebase[ofs + x + 3] = p[x * 4 + plane + 12]; VGA_pagebase[ofs + x + 4] = p[x * 4 + plane + 16]; VGA_pagebase[ofs + x + 5] = p[x * 4 + plane + 20]; VGA_pagebase[ofs + x + 6] = p[x * 4 + plane + 24]; VGA_pagebase[ofs + x + 7] = p[x * 4 + plane + 28]; x += 8; } while (x * 4 < VGA_width) { VGA_pagebase[ofs + x] = p[x * 4 + plane]; x++; } p += VGA_bufferrowbytes; /* Next line. */ ofs += VGA_rowbytes; } if (++plane < 4) goto _docopy; } void VGA_UpdateLinearScreen ( void *srcptr, void *destptr, int width, int height, int srcrowbytes, int destrowbytes) { /* quick'n'dirty C-only version */ byte *s, *d; int y; s = (byte *) srcptr; d = (byte *) destptr; for (y = 0; y < height; y++) { memcpy (d, s, width); s += srcrowbytes; d += destrowbytes; } } #endif /* !id386 */ /* ================ VGA_SwapBuffersCopy ================ */ static void VGA_SwapBuffersCopy (viddef_t *lvid, vmode_t *pcurrentmode, vrect_t *rects) { Q_UNUSED(pcurrentmode); // TODO: can write a dword at a time // TODO: put in ASM // TODO: copy only specified rectangles if (VGA_planar) { // TODO: copy only specified rectangles VGA_UpdatePlanarScreen (lvid->buffer); } else { while (rects) { VGA_UpdateLinearScreen ( lvid->buffer + rects->x + (rects->y * lvid->rowbytes), VGA_pagebase + rects->x + (rects->y * VGA_rowbytes), rects->width, rects->height, lvid->rowbytes, VGA_rowbytes); rects = rects->pnext; } } } /* ================ VGA_SwapBuffers ================ */ static void VGA_SwapBuffers (viddef_t *lvid, vmode_t *pcurrentmode, vrect_t *rects) { if (vid_wait.integer == VID_WAIT_VSYNC) VGA_WaitVsync (); VGA_SwapBuffersCopy (lvid, pcurrentmode, rects); } engine/h2shared/vid_win.c000066400000000000000000001505061444734033100156400ustar00rootroot00000000000000/* * vid_win.c -- Win32 video driver * * Adapted from Quake2 and from an initial work by MH with many * modifications to make it work in Hexen II: Hammer of Thyrion. * Uses only DIB sections/GDI. * * - TODO: Add back more low resolutions. * - TODO: Add DDRAW (see Quake2) * - TODO: Better video mode management? * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "bgmusic.h" #include "cdaudio.h" #include "cfgfile.h" #include "winquake.h" #include #include "d_local.h" #include "resource.h" #if defined(H2W) #define WM_CLASSNAME "HexenWorld" #define WM_WINDOWNAME "HexenWorld" #else #define WM_CLASSNAME "HexenII" #define WM_WINDOWNAME "HexenII" #endif #define MAX_MODE_LIST 64 #define MAX_DESC 13 #define VID_ROW_SIZE 3 byte globalcolormap[VID_GRADES*256], lastglobalcolor = 0; byte *lastsourcecolormap = NULL; HWND mainwindow; qboolean msg_suppress_1 = false; static int DIBWidth, DIBHeight; static RECT WindowRect; static LONG WindowStyle, ExWindowStyle; int window_center_x, window_center_y, window_x, window_y, window_width, window_height; RECT window_rect; static DEVMODE gdevmode; static qboolean startwindowed = false; static qboolean firstupdate = true; static qboolean vid_initialized = false, vid_palettized; static int vid_fulldib_on_focus_mode; static qboolean force_minimized, force_mode_set; static int enable_mouse; static qboolean palette_changed, vid_mode_set; static HICON hIcon; viddef_t vid; // global video state qboolean in_mode_set; // 0 is MODE_WINDOWED, 3 is MODE_FULLSCREEN_DEFAULT static cvar_t vid_mode = {"vid_mode", "0", CVAR_NONE}; static cvar_t _vid_default_mode_win = {"_vid_default_mode_win", "3", CVAR_ARCHIVE}; // compatibility with dos version: static cvar_t _vid_default_mode = {"_vid_default_mode", "0", CVAR_ARCHIVE}; static cvar_t vid_config_x = {"vid_config_x", "800", CVAR_ARCHIVE}; static cvar_t vid_config_y = {"vid_config_y", "600", CVAR_ARCHIVE}; static cvar_t vid_fullscreen_mode = {"vid_fullscreen_mode", "3", CVAR_ARCHIVE}; static cvar_t vid_windowed_mode = {"vid_windowed_mode", "0", CVAR_ARCHIVE}; cvar_t _enable_mouse = {"_enable_mouse", "0", CVAR_ARCHIVE}; static int vid_modenum = NO_MODE; static int vid_testingmode, vid_realmode; static double vid_testendtime; static int vid_default = MODE_WINDOWED; static int windowed_default; modestate_t modestate = MS_UNINIT; static byte *vid_surfcache; static int vid_surfcachesize; static int VID_highhunkmark; static unsigned char vid_curpal[256*3]; /* save for mode changes */ unsigned short d_8to16table[256]; unsigned int d_8to24table[256]; typedef struct { modestate_t type; int width; int height; int modenum; int fullscreen; char modedesc[MAX_DESC]; } vmode_t; static vmode_t modelist[MAX_MODE_LIST]; static int nummodes; static vmode_t badmode; //static byte backingbuf[48*24]; static byte backingbuf[48*48]; typedef union _dibinfo { struct { BITMAPINFOHEADER header; RGBQUAD acolors[256]; }; BITMAPINFO bi; } dibinfo_t; static HGDIOBJ previously_selected_GDI_obj = NULL; static HBITMAP hDIBSection; static void *pDIBBase = NULL; static HDC hdcDIBSection = NULL; static HDC maindc = NULL; static void VID_MenuDraw (void); static void VID_MenuKey (int key); static qboolean VID_SetMode (int modenum, const unsigned char *palette); static void AppActivate(BOOL fActive, BOOL minimize); static LRESULT WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); /* ================ VID_UpdateWindowStatus ================ */ static void VID_UpdateWindowStatus (void) { window_rect.left = window_x; window_rect.top = window_y; window_rect.right = window_x + window_width; window_rect.bottom = window_y + window_height; window_center_x = (window_rect.left + window_rect.right) / 2; window_center_y = (window_rect.top + window_rect.bottom) / 2; IN_UpdateClipCursor (); } /* ================ ClearAllStates ================ */ static void ClearAllStates (void) { Key_ClearStates (); IN_ClearStates (); } /* ================ VID_CheckAdequateMem ================ */ static qboolean VID_CheckAdequateMem (int width, int height) { int tbuffersize; tbuffersize = width * height * sizeof (*d_pzbuffer); tbuffersize += D_SurfaceCacheForRes (width, height); // see if there's enough memory, allowing for the normal mode 0x13 pixel, // z, and surface buffers //if ((host_parms->memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + // 0x10000 * 3) < MINIMUM_MEMORY) // Pa3PyX: using hopefully better estimation now // Experimentation: the heap should have at least 12.0 megs // remaining (after init) after setting video mode, otherwise // it's Hunk_Alloc failures and cache thrashes upon level load if (host_parms->memsize < tbuffersize + 0x180000 + 0xC00000) { return false; // not enough memory for mode } return true; } /* ================ VID_AllocBuffers ================ */ static qboolean VID_AllocBuffers (int width, int height) { int tsize, tbuffersize; tbuffersize = width * height * sizeof (*d_pzbuffer); tsize = D_SurfaceCacheForRes (width, height); tbuffersize += tsize; // see if there's enough memory, allowing for the normal mode 0x13 pixel, // z, and surface buffers //if ((host_parms->memsize - tbuffersize + SURFCACHE_SIZE_AT_320X200 + // 0x10000 * 3) < MINIMUM_MEMORY) // Pa3PyX: using hopefully better estimation now // if total memory < needed surface cache + (minimum operational memory // less surface cache for 320x200 and typical hunk state after init) if (host_parms->memsize < tbuffersize + 0x180000 + 0xC00000) { Con_SafePrintf ("Not enough memory for video mode\n"); return false; // not enough memory for mode } vid_surfcachesize = tsize; if (d_pzbuffer) { D_FlushCaches (); Hunk_FreeToHighMark (VID_highhunkmark); d_pzbuffer = NULL; } VID_highhunkmark = Hunk_HighMark (); d_pzbuffer = (short *) Hunk_HighAllocName (tbuffersize, "video"); vid_surfcache = (byte *)d_pzbuffer + width * height * sizeof (*d_pzbuffer); return true; } /* ================= VID_Windowed_f ================= */ static void VID_Windowed_f (void) { VID_SetMode (vid_windowed_mode.integer, vid_curpal); } /* ================= VID_Fullscreen_f ================= */ static void VID_Fullscreen_f (void) { VID_SetMode (vid_fullscreen_mode.integer, vid_curpal); } static void VID_ShutdownDIB (void) { if (hdcDIBSection) { SelectObject (hdcDIBSection, previously_selected_GDI_obj); DeleteDC (hdcDIBSection); hdcDIBSection = NULL; } if (hDIBSection) { DeleteObject (hDIBSection); hDIBSection = NULL; pDIBBase = NULL; } if (maindc) { // if maindc exists mainwindow must also be valid ReleaseDC (mainwindow, maindc); maindc = NULL; } } static void VID_CreateDIB (int width, int height, const unsigned char *palette) { dibinfo_t dibheader; BITMAPINFO *pbmiDIB = &dibheader.bi; int i; maindc = GetDC (mainwindow); memset (&dibheader, 0, sizeof (dibheader)); // fill in the bitmap info pbmiDIB->bmiHeader.biSize = sizeof (BITMAPINFOHEADER); pbmiDIB->bmiHeader.biWidth = width; pbmiDIB->bmiHeader.biHeight = height; pbmiDIB->bmiHeader.biPlanes = 1; pbmiDIB->bmiHeader.biBitCount = 8; pbmiDIB->bmiHeader.biCompression = BI_RGB; pbmiDIB->bmiHeader.biSizeImage = 0; pbmiDIB->bmiHeader.biXPelsPerMeter = 0; pbmiDIB->bmiHeader.biYPelsPerMeter = 0; pbmiDIB->bmiHeader.biClrUsed = 256; pbmiDIB->bmiHeader.biClrImportant = 256; // fill in the palette for (i = 0; i < 256; i++) { // d_8to24table isn't filled in yet, so this is just for testing dibheader.acolors[i].rgbRed = palette[i * 3]; dibheader.acolors[i].rgbGreen = palette[i * 3 + 1]; dibheader.acolors[i].rgbBlue = palette[i * 3 + 2]; } // create the DIB section hDIBSection = CreateDIBSection (maindc, pbmiDIB, DIB_RGB_COLORS, &pDIBBase, NULL, 0); if (hDIBSection == NULL) Sys_Error ("DIB_Init() - CreateDIBSection failed\n"); // set video buffers if (pbmiDIB->bmiHeader.biHeight > 0) { // bottom-up vid.buffer = (pixel_t *)pDIBBase + (height - 1) * width; vid.rowbytes = -width; } else { // top-down vid.buffer = (pixel_t *)pDIBBase; vid.rowbytes = vid.width; } vid.conbuffer = vid.direct = vid.buffer; vid.conrowbytes = vid.rowbytes; // clear the buffer if (height < 0) // negative height was sent for top-down bitmap memset (pDIBBase, 0xff, width * -height); else memset (pDIBBase, 0xff, width * height); if ((hdcDIBSection = CreateCompatibleDC (maindc)) == NULL) Sys_Error ("DIB_Init() - CreateCompatibleDC failed\n"); if ((previously_selected_GDI_obj = SelectObject (hdcDIBSection, hDIBSection)) == NULL) Sys_Error ("DIB_Init() - SelectObject failed\n"); } static void VID_RegisterWndClass (HINSTANCE hInstance) { WNDCLASS wc; wc.style = CS_OWNDC; wc.lpfnWndProc = MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = 0; wc.hCursor = LoadCursor (NULL, IDC_ARROW); wc.hbrBackground = NULL; wc.lpszMenuName = 0; wc.lpszClassName = WM_CLASSNAME; if (!RegisterClass(&wc)) Sys_Error ("Couldn't register main window class"); } static void VID_InitModes (HINSTANCE hInstance) { HDC hdc; hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ICON2)); /* Register the frame class */ VID_RegisterWndClass(hInstance); modelist[0].type = MS_WINDOWED; modelist[0].width = 320; modelist[0].height = 240; q_strlcpy (modelist[0].modedesc, "320x240", MAX_DESC); modelist[0].modenum = MODE_WINDOWED; modelist[0].fullscreen = 0; modelist[1].type = MS_WINDOWED; modelist[1].width = 640; modelist[1].height = 480; q_strlcpy (modelist[1].modedesc, "640x480", MAX_DESC); modelist[1].modenum = MODE_WINDOWED + 1; modelist[1].fullscreen = 0; modelist[2].type = MS_WINDOWED; modelist[2].width = 800; modelist[2].height = 600; q_strlcpy (modelist[2].modedesc, "800x600", MAX_DESC); modelist[2].modenum = MODE_WINDOWED + 2; modelist[2].fullscreen = 0; // automatically stretch the default mode up if > 640x480 desktop resolution hdc = GetDC(NULL); if ((GetDeviceCaps(hdc, HORZRES) > 800) && !COM_CheckParm("-noautostretch")) { vid_default = MODE_WINDOWED + 2; } else if ((GetDeviceCaps(hdc, HORZRES) > 640) && !COM_CheckParm("-noautostretch")) { vid_default = MODE_WINDOWED + 1; } else { vid_default = MODE_WINDOWED; } windowed_default = vid_default; ReleaseDC(NULL,hdc); nummodes = 3; // reserve space for windowed mode } /* ================= VID_GetDisplayModes ================= */ static void VID_GetDisplayModes (void) { DEVMODE devmode; int i, modenum, existingmode, originalnummodes, lowestres; BOOL status; // enumerate > 8 bpp modes originalnummodes = nummodes; modenum = 0; lowestres = 99999; do { status = EnumDisplaySettings (NULL, modenum, &devmode); if ((devmode.dmPelsWidth <= MAXWIDTH) && (devmode.dmPelsHeight <= MAXHEIGHT) && (devmode.dmPelsWidth >= 640) && (devmode.dmPelsHeight >= 480) && (nummodes < MAX_MODE_LIST)) { devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; if (ChangeDisplaySettings (&devmode, CDS_TEST | CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL) { modelist[nummodes].type = MS_FULLDIB; modelist[nummodes].width = devmode.dmPelsWidth; modelist[nummodes].height = devmode.dmPelsHeight; modelist[nummodes].modenum = 0; modelist[nummodes].fullscreen = 1; q_snprintf (modelist[nummodes].modedesc, MAX_DESC, "%dx%d", (int)devmode.dmPelsWidth, (int)devmode.dmPelsHeight); // see if the mode is already there // (same dimensions but different refresh rate) for (i = originalnummodes, existingmode = 0; i < nummodes; i++) { if ((modelist[nummodes].width == modelist[i].width) && (modelist[nummodes].height == modelist[i].height)) { existingmode = 1; break; } } // if it's not add it to the list if (!existingmode) { if (modelist[nummodes].width < lowestres) lowestres = modelist[nummodes].width; nummodes++; } } } modenum++; } while (status); if (nummodes != originalnummodes) vid_default = MODE_FULLSCREEN_DEFAULT; else { Cvar_SetValueQuick (&_vid_default_mode_win, vid_default); Con_SafePrintf ("No fullscreen DIB modes found\n"); } } /* ================= VID_NumModes ================= */ static int VID_NumModes (void) { return nummodes; } /* ================= VID_GetModePtr ================= */ static vmode_t *VID_GetModePtr (int modenum) { if ((modenum >= 0) && (modenum < nummodes)) return &modelist[modenum]; else return &badmode; } /* ================= VID_CheckModedescFixup ================= */ static void VID_CheckModedescFixup (int modenum) { int x, y; if (modenum == MODE_SETTABLE_WINDOW) { x = vid_config_x.integer; y = vid_config_y.integer; q_snprintf (modelist[modenum].modedesc, MAX_DESC, "%dx%d", x, y); modelist[modenum].width = x; modelist[modenum].height = y; } } /* ================= VID_GetModeDescriptionMemCheck ================= */ static const char *VID_GetModeDescriptionMemCheck (int modenum) { const char *pinfo; vmode_t *pv; if ((modenum < 0) || (modenum >= nummodes)) return NULL; VID_CheckModedescFixup (modenum); pv = VID_GetModePtr (modenum); pinfo = pv->modedesc; if (VID_CheckAdequateMem (pv->width, pv->height)) { return pinfo; } else { return NULL; } } /* ================= VID_GetModeDescription ================= */ static const char *VID_GetModeDescription (int modenum) { const char *pinfo; vmode_t *pv; if ((modenum < 0) || (modenum >= nummodes)) return NULL; VID_CheckModedescFixup (modenum); pv = VID_GetModePtr (modenum); pinfo = pv->modedesc; return pinfo; } /* ================= VID_GetModeDescription2 Tacks on "windowed" or "fullscreen" ================= */ static const char *VID_GetModeDescription2 (int modenum) { static char pinfo[40]; vmode_t *pv; if ((modenum < 0) || (modenum >= nummodes)) return NULL; VID_CheckModedescFixup (modenum); pv = VID_GetModePtr (modenum); if (modelist[modenum].type == MS_FULLSCREEN) { q_snprintf(pinfo, sizeof(pinfo), "%s fullscreen", pv->modedesc); } else if (modelist[modenum].type == MS_FULLDIB) { q_snprintf(pinfo, sizeof(pinfo), "%s fullscreen", pv->modedesc); } else { q_snprintf(pinfo, sizeof(pinfo), "%s windowed", pv->modedesc); } return pinfo; } // KJB: Added this to return the mode driver name in description for console static const char *VID_GetExtModeDescription (int modenum) { static char pinfo[40]; vmode_t *pv; if ((modenum < 0) || (modenum >= nummodes)) return NULL; VID_CheckModedescFixup (modenum); pv = VID_GetModePtr (modenum); if (modelist[modenum].type == MS_FULLDIB) { q_snprintf(pinfo, sizeof(pinfo), "%s fullscreen", pv->modedesc); } else { q_snprintf(pinfo, sizeof(pinfo), "%s windowed", pv->modedesc); } return pinfo; } static void VID_DestroyWindow (void) { if (modestate == MS_FULLDIB) ChangeDisplaySettings (NULL, CDS_FULLSCREEN); VID_ShutdownDIB (); } static void CenterWindow (HWND hWndCenter, int width, int height) { int CenterX, CenterY; CenterX = (GetSystemMetrics(SM_CXSCREEN) - width) / 2; CenterY = (GetSystemMetrics(SM_CYSCREEN) - height) / 2; if (CenterX > 2*CenterY) CenterX >>= 1; // dual screen? if (CenterX < 0) CenterX = 0; if (CenterY < 0) CenterY = 0; SetWindowPos (hWndCenter, NULL, CenterX, CenterY, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW | SWP_DRAWFRAME); } static qboolean VID_SetWindowedMode (int modenum) { int lastmodestate; VID_CheckModedescFixup (modenum); lastmodestate = modestate; VID_DestroyWindow (); WindowRect.top = WindowRect.left = 0; WindowRect.right = modelist[modenum].width; WindowRect.bottom = modelist[modenum].height; DIBWidth = modelist[modenum].width; DIBHeight = modelist[modenum].height; WindowStyle = WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; ExWindowStyle = 0; AdjustWindowRectEx(&WindowRect, WindowStyle, FALSE, 0); // the first time we're called to set the mode, create the window we'll use // for the rest of the session if (!vid_mode_set) { mainwindow = CreateWindowEx ( ExWindowStyle, WM_CLASSNAME, WM_WINDOWNAME, WindowStyle, 0, 0, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top, NULL, NULL, global_hInstance, NULL); if (!mainwindow) Sys_Error ("Couldn't create DIB window"); vid_mode_set = true; } else { SetWindowLongPtr (mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE); SetWindowLongPtr (mainwindow, GWL_EXSTYLE, ExWindowStyle); } if (!SetWindowPos (mainwindow, NULL, 0, 0, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top, SWP_NOCOPYBITS | SWP_NOZORDER | SWP_HIDEWINDOW)) { Sys_Error ("Couldn't resize DIB window"); } // position and show the DIB window CenterWindow(mainwindow, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top); if (force_minimized) ShowWindow (mainwindow, SW_MINIMIZE); else ShowWindow (mainwindow, SW_SHOWDEFAULT); UpdateWindow (mainwindow); modestate = MS_WINDOWED; vid_fulldib_on_focus_mode = 0; vid.numpages = 1; vid.maxwarpwidth = WARP_WIDTH; vid.maxwarpheight = WARP_HEIGHT; vid.height = vid.conheight = DIBHeight; vid.width = vid.conwidth = DIBWidth; vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0); SendMessage (mainwindow, WM_SETICON, (WPARAM)TRUE, (LPARAM)hIcon); SendMessage (mainwindow, WM_SETICON, (WPARAM)FALSE, (LPARAM)hIcon); return true; } static qboolean VID_SetFullDIBMode (int modenum) { int lastmodestate; VID_DestroyWindow (); gdevmode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; gdevmode.dmPelsWidth = modelist[modenum].width; gdevmode.dmPelsHeight = modelist[modenum].height; gdevmode.dmSize = sizeof (gdevmode); if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) Sys_Error ("Couldn't set fullscreen DIB mode"); lastmodestate = modestate; modestate = MS_FULLDIB; vid_fulldib_on_focus_mode = modenum; WindowRect.top = WindowRect.left = 0; WindowRect.right = modelist[modenum].width; WindowRect.bottom = modelist[modenum].height; DIBWidth = modelist[modenum].width; DIBHeight = modelist[modenum].height; WindowStyle = WS_POPUP | WS_SYSMENU | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; ExWindowStyle = 0; AdjustWindowRectEx(&WindowRect, WindowStyle, FALSE, 0); SetWindowLongPtr (mainwindow, GWL_STYLE, WindowStyle | WS_VISIBLE); SetWindowLongPtr (mainwindow, GWL_EXSTYLE, ExWindowStyle); if (!SetWindowPos (mainwindow, NULL, 0, 0, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top, SWP_NOCOPYBITS | SWP_NOZORDER)) { Sys_Error ("Couldn't resize DIB window"); } // position and show the DIB window SetWindowPos (mainwindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_DRAWFRAME); ShowWindow (mainwindow, SW_SHOWDEFAULT); UpdateWindow (mainwindow); vid.numpages = 1; vid.maxwarpwidth = WARP_WIDTH; vid.maxwarpheight = WARP_HEIGHT; vid.height = vid.conheight = DIBHeight; vid.width = vid.conwidth = DIBWidth; vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0); // needed because we're not getting WM_MOVE messages fullscreen on NT window_x = 0; window_y = 0; return true; } static void VID_RestoreOldMode (int original_mode) { static qboolean inerror = false; if (inerror) return; in_mode_set = false; inerror = true; // make sure mode set happens (video mode changes) vid_modenum = original_mode - 1; if (!VID_SetMode (original_mode, vid_curpal)) { vid_modenum = MODE_WINDOWED - 1; if (!VID_SetMode (windowed_default, vid_curpal)) Sys_Error ("Can't set any video mode"); } inerror = false; } static qboolean VID_SetMode (int modenum, const unsigned char *palette) { int original_mode, temp; qboolean status; MSG msg; HDC hdc; while ((modenum >= nummodes) || (modenum < 0)) { if (vid_modenum == NO_MODE) { if (modenum == vid_default) { modenum = windowed_default; } else { modenum = vid_default; } Cvar_SetValueQuick (&vid_mode, (float)modenum); } else { Cvar_SetValueQuick (&vid_mode, (float)vid_modenum); return false; } } if (!force_mode_set && (modenum == vid_modenum)) return true; // so Con_Printfs don't mess us up by forcing vid and snd updates temp = scr_disabled_for_loading; scr_disabled_for_loading = true; in_mode_set = true; CDAudio_Pause (); S_ClearBuffer (); if (vid_modenum == NO_MODE) original_mode = windowed_default; else original_mode = vid_modenum; // Set either the fullscreen or windowed mode if (modelist[modenum].type == MS_WINDOWED) { if (_enable_mouse.integer) { status = VID_SetWindowedMode(modenum); IN_ActivateMouse (); IN_HideMouse (); } else { IN_DeactivateMouse (); IN_ShowMouse (); status = VID_SetWindowedMode(modenum); } } else { status = VID_SetFullDIBMode(modenum); IN_ActivateMouse (); IN_HideMouse (); } // VID_CreateDIB(DIBWidth,-DIBHeight, palette); // top-down VID_CreateDIB(DIBWidth, DIBHeight, palette); // bottom-up window_width = vid.width; window_height = vid.height; VID_UpdateWindowStatus (); CDAudio_Resume (); scr_disabled_for_loading = temp; if (!status) { VID_RestoreOldMode (original_mode); return false; } // now we try to make sure we get the focus on the mode switch, because // sometimes in some systems we don't. We grab the foreground, then // finish setting up, pump all our messages, and sleep for a little while // to let messages finish bouncing around the system, then we put // ourselves at the top of the z order, then grab the foreground again, // Who knows if it helps, but it probably doesn't hurt if (!force_minimized) SetForegroundWindow (mainwindow); hdc = GetDC(NULL); if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) vid_palettized = true; else vid_palettized = false; VID_SetPalette (palette); ReleaseDC(NULL,hdc); vid_modenum = modenum; Cvar_SetValueQuick (&vid_mode, (float)vid_modenum); if (!VID_AllocBuffers (vid.width, vid.height)) { // couldn't get memory for this mode; try to fall back to previous mode VID_RestoreOldMode (original_mode); return false; } D_InitCaches (vid_surfcache, vid_surfcachesize); while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage (&msg); DispatchMessage (&msg); } Sleep (100); if (!force_minimized) { SetWindowPos (mainwindow, HWND_TOP, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOCOPYBITS); SetForegroundWindow (mainwindow); } // fix the leftover Alt from any Alt-Tab or the like that switched us away ClearAllStates (); if (!msg_suppress_1) Con_SafePrintf ("%s\n", VID_GetModeDescription (vid_modenum)); VID_SetPalette (palette); in_mode_set = false; vid.recalc_refdef = 1; return true; } void VID_LockBuffer (void) { /* nothing. */ } void VID_UnlockBuffer (void) { /* nothing. */ } void VID_SetPalette (const unsigned char *palette) { int i; RGBQUAD colors[256]; const unsigned char *pal; pal = palette; if (!Minimized) { if (hdcDIBSection) { // incoming palette is 3 component for (i = 0; i < 256; i++, pal += 3) { colors[i].rgbRed = pal[0]; colors[i].rgbGreen = pal[1]; colors[i].rgbBlue = pal[2]; colors[i].rgbReserved = 0; } colors[0].rgbRed = 0; colors[0].rgbGreen = 0; colors[0].rgbBlue = 0; colors[255].rgbRed = 0xff; colors[255].rgbGreen = 0xff; colors[255].rgbBlue = 0xff; if (SetDIBColorTable (hdcDIBSection, 0, 256, colors) == 0) { Con_SafePrintf ("DIB_SetPalette() - SetDIBColorTable failed\n"); } } } if (palette != vid_curpal) memcpy(vid_curpal, palette, sizeof (vid_curpal)); } void VID_ShiftPalette (const unsigned char *palette) { VID_SetPalette (palette); } /* ================= VID_DescribeCurrentMode_f ================= */ static void VID_DescribeCurrentMode_f (void) { Con_Printf ("%s\n", VID_GetExtModeDescription (vid_modenum)); } /* ================= VID_NumModes_f ================= */ static void VID_NumModes_f (void) { if (nummodes == 1) Con_Printf ("%d video mode is available\n", nummodes); else Con_Printf ("%d video modes are available\n", nummodes); } /* ================= VID_DescribeMode_f ================= */ static void VID_DescribeMode_f (void) { int modenum; modenum = atoi (Cmd_Argv(1)); Con_Printf ("%s\n", VID_GetExtModeDescription (modenum)); } /* ================= VID_DescribeModes_f ================= */ static void VID_DescribeModes_f (void) { int i, lnummodes; const char *pinfo; qboolean na; vmode_t *pv; na = false; lnummodes = VID_NumModes (); for (i = 0; i < lnummodes; i++) { pv = VID_GetModePtr (i); pinfo = VID_GetExtModeDescription (i); if (VID_CheckAdequateMem (pv->width, pv->height)) { Con_Printf ("%2d: %s\n", i, pinfo); } else { Con_Printf ("**: %s\n", pinfo); na = true; } } if (na) { Con_Printf ("\n[**: not enough system RAM for mode]\n"); } } /* ================= VID_TestMode_f ================= */ static void VID_TestMode_f (void) { int modenum; double testduration; if (!vid_testingmode) { modenum = atoi (Cmd_Argv(1)); if (VID_SetMode (modenum, vid_curpal)) { vid_testingmode = 1; testduration = atof (Cmd_Argv(2)); if (testduration == 0) testduration = 5.0; vid_testendtime = realtime + testduration; } } } /* ================= VID_Minimize_f ================= */ static void VID_Minimize_f (void) { // we only support minimizing windows; if you're fullscreen, // switch to windowed first if (modestate == MS_WINDOWED) ShowWindow (mainwindow, SW_MINIMIZE); } /* ================= VID_ForceMode_f ================= */ static void VID_ForceMode_f (void) { int modenum; if (!vid_testingmode) { modenum = atoi (Cmd_Argv(1)); force_mode_set = true; VID_SetMode (modenum, vid_curpal); force_mode_set = false; } } void VID_Init (const unsigned char *palette) { int i, bestmatch, bestmatchmetric, t, dr, dg, db; byte *ptmp; const char *read_vars[] = { "_vid_default_mode_win" }; #define num_readvars ( sizeof(read_vars)/sizeof(read_vars[0]) ) Cvar_RegisterVariable (&vid_mode); Cvar_RegisterVariable (&_vid_default_mode); Cvar_RegisterVariable (&_vid_default_mode_win); Cvar_RegisterVariable (&vid_config_x); Cvar_RegisterVariable (&vid_config_y); Cvar_RegisterVariable (&_enable_mouse); Cvar_RegisterVariable (&vid_fullscreen_mode); Cvar_RegisterVariable (&vid_windowed_mode); Cmd_AddCommand ("vid_testmode", VID_TestMode_f); Cmd_AddCommand ("vid_nummodes", VID_NumModes_f); Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f); Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f); Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f); Cmd_AddCommand ("vid_forcemode", VID_ForceMode_f); Cmd_AddCommand ("vid_windowed", VID_Windowed_f); Cmd_AddCommand ("vid_fullscreen", VID_Fullscreen_f); Cmd_AddCommand ("vid_minimize", VID_Minimize_f); // perform an early read of config.cfg CFG_ReadCvars (read_vars, num_readvars); VID_InitModes (global_hInstance); VID_GetDisplayModes (); vid.maxwarpwidth = WARP_WIDTH; vid.maxwarpheight = WARP_HEIGHT; vid.colormap = host_colormap; vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048)); vid_testingmode = 0; // GDI doesn't let us remap palette index 0, so we'll remap color // mappings from that black to another one bestmatchmetric = 256*256*3; bestmatch = 0; // FIXME - uninitialized, guessing 0... for (i = 1; i < 256; i++) { dr = palette[0] - palette[i*3]; dg = palette[1] - palette[i*3+1]; db = palette[2] - palette[i*3+2]; t = (dr * dr) + (dg * dg) + (db * db); if (t < bestmatchmetric) { bestmatchmetric = t; bestmatch = i; if (t == 0) break; } } for (i = 0, ptmp = vid.colormap; i < (1 << (VID_CBITS + 8)); i++, ptmp++) { if (*ptmp == 0) *ptmp = bestmatch; } if (COM_CheckParm("-startwindowed") || COM_CheckParm("-window") || COM_CheckParm("-w")) { startwindowed = true; Cvar_SetValueQuick (&_vid_default_mode_win, windowed_default); vid_default = windowed_default; } if (_vid_default_mode_win.integer < 0 || _vid_default_mode_win.integer >= nummodes) Cvar_SetValueQuick (&_vid_default_mode_win, windowed_default); Cvar_LockVar ("_vid_default_mode_win"); /* so that config.cfg doesn't break -window */ #if !defined(NO_SPLASHES) if (hwnd_dialog) { DestroyWindow (hwnd_dialog); hwnd_dialog = NULL; } #endif /* ! NO_SPLASHES */ /* set default fullscreen mode only if * setting the windowed default fails: */ if (!VID_SetMode(MODE_WINDOWED, palette)) { force_mode_set = true; VID_SetMode(vid_default, palette); force_mode_set = false; } vid_initialized = true; vid_realmode = vid_modenum; VID_SetPalette (palette); vid_menudrawfn = VID_MenuDraw; vid_menukeyfn = VID_MenuKey; q_strlcpy (badmode.modedesc, "Bad mode", MAX_DESC); } void VID_Shutdown (void) { if (vid_initialized) { if (modestate == MS_FULLDIB) ChangeDisplaySettings (NULL, CDS_FULLSCREEN); PostMessage (HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)mainwindow, (LPARAM)0); PostMessage (HWND_BROADCAST, WM_SYSCOLORCHANGE, (WPARAM)0, (LPARAM)0); AppActivate(false, false); VID_DestroyWindow (); #if !defined(NO_SPLASHES) if (hwnd_dialog) { DestroyWindow (hwnd_dialog); hwnd_dialog = NULL; } #endif /* ! NO_SPLASHES */ if (mainwindow) DestroyWindow(mainwindow); vid_testingmode = 0; vid_initialized = 0; } } /* ================ FlipScreen ================ */ static void FlipScreen (vrect_t *rects) { if (hdcDIBSection) { int numrects = 0; while (rects) { BitBlt (maindc, rects->x, rects->y, rects->x + rects->width, rects->y + rects->height, hdcDIBSection, rects->x, rects->y, SRCCOPY); numrects++; rects = rects->pnext; } } } void VID_Update (vrect_t *rects) { vrect_t rect; if (!vid_palettized && palette_changed) { palette_changed = false; rect.x = 0; rect.y = 0; rect.width = vid.width; rect.height = vid.height; rect.pnext = NULL; rects = ▭ } if (firstupdate && host_initialized) { firstupdate = false; Cvar_SetValueQuick (&vid_mode, _vid_default_mode_win.integer); } // We've drawn the frame; copy it to the screen FlipScreen (rects); if (vid_testingmode) { if (realtime >= vid_testendtime) { VID_SetMode (vid_realmode, vid_curpal); vid_testingmode = 0; } } else { if (vid_mode.integer != vid_realmode) { VID_SetMode (vid_mode.integer, vid_curpal); Cvar_SetValueQuick (&vid_mode, vid_modenum); // so if mode set fails, we don't keep on // trying to set it vid_realmode = vid_modenum; } } // handle the mouse state when windowed if that's changed if (modestate == MS_WINDOWED) { if (_enable_mouse.integer != enable_mouse) { if (_enable_mouse.integer) { IN_ActivateMouse (); IN_HideMouse (); } else { IN_DeactivateMouse (); IN_ShowMouse (); } enable_mouse = _enable_mouse.integer; } } } /* ================ D_BeginDirectRect ================ */ void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) { int i, j, reps, repshift; vrect_t rect; if (!vid_initialized) return; if (vid.aspect > 1.5) { reps = 2; repshift = 1; } else { reps = 1; repshift = 0; } // if (vid.numpages == 1) // { VID_LockBuffer (); if (!vid.direct) Sys_Error ("NULL vid.direct pointer"); for (i = 0; i < (height << repshift); i += reps) { for (j = 0; j < reps; j++) { memcpy (&backingbuf[(i + j) * width] /* &backingbuf[(i + j) * 24] */, vid.direct + x + ((y << repshift) + i + j) * vid.rowbytes, width); memcpy (vid.direct + x + ((y << repshift) + i + j) * vid.rowbytes, &pbitmap[(i >> repshift) * width], width); } } VID_UnlockBuffer (); rect.x = x; rect.y = y; rect.width = width; rect.height = height << repshift; rect.pnext = NULL; FlipScreen (&rect); // } // else // { // } } #ifndef H2W /* unused in hexenworld */ void D_ShowLoadingSize (void) { #if defined(DRAW_PROGRESSBARS) static int prev_perc; int cur_perc; vrect_t rect; viddef_t save_vid; /* global video state */ if (!vid_initialized) return; cur_perc = loading_stage * 100; if (total_loading_size) cur_perc += current_loading_size * 100 / total_loading_size; if (cur_perc == prev_perc) return; prev_perc = cur_perc; save_vid = vid; // if (vid.numpages == 1) // { VID_LockBuffer (); if (!vid.direct) Sys_Error ("NULL vid.direct pointer"); vid.buffer = vid.direct; SCR_DrawLoading(); VID_UnlockBuffer (); #if 1 /* original code */ rect.x = 0; rect.y = 0; rect.width = vid.width; #else /* Pa3PyX: tweaking sizes - faster redraw */ rect.x = (vid.width >> 1) - 100; rect.y = 0; rect.width = 200; #endif rect.height = 112; rect.pnext = NULL; FlipScreen (&rect); // } // else // { // } vid = save_vid; #endif /* !DRAW_PROGRESSBARS */ } #endif /* ! H2W */ /* ================ D_EndDirectRect ================ */ void D_EndDirectRect (int x, int y, int width, int height) { int i, j, reps, repshift; vrect_t rect; if (!vid_initialized) return; if (vid.aspect > 1.5) { reps = 2; repshift = 1; } else { reps = 1; repshift = 0; } // if (vid.numpages == 1) // { VID_LockBuffer (); if (!vid.direct) Sys_Error ("NULL vid.direct pointer"); for (i = 0; i < (height << repshift); i += reps) { for (j = 0; j < reps; j++) { memcpy (vid.direct + x + ((y << repshift) + i + j) * vid.rowbytes, &backingbuf[(i + j) * width] /* &backingbuf[(i + j) * 24] */, width); } } VID_UnlockBuffer (); rect.x = x; rect.y = y; rect.width = width; rect.height = height << repshift; rect.pnext = NULL; FlipScreen (&rect); // } // else // { // } } //========================================================================== /* ======= MapKey Map from windows to quake keynums ======= */ static byte scantokey[128] = { // 0 1 2 3 4 5 6 7 // 8 9 A B C D E F 0 , 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', K_ENTER, K_CTRL, 'a', 's', // 1 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', K_SHIFT, '\\', 'z', 'x', 'c', 'v', // 2 'b', 'n', 'm', ',', '.', '/', K_SHIFT, K_KP_STAR, K_ALT, ' ', 0 , K_F1, K_F2, K_F3, K_F4, K_F5, // 3 K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, 0 , K_HOME, K_UPARROW,K_PGUP,K_KP_MINUS,K_LEFTARROW,K_KP_5,K_RIGHTARROW,K_KP_PLUS,K_END, // 4 K_DOWNARROW,K_PGDN,K_INS,K_DEL, 0 , 0 , 0 , K_F11, K_F12, 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 5 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 6 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 }; #if 0 /* not used */ static byte shiftscantokey[128] = { // 0 1 2 3 4 5 6 7 // 8 9 A B C D E F 0 , 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', K_BACKSPACE, 9, // 0 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', K_ENTER, K_CTRL, 'A', 'S', // 1 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"' , '~', K_SHIFT, '|', 'Z', 'X', 'C', 'V', // 2 'B', 'N', 'M', '<', '>', '?', K_SHIFT, K_KP_STAR, K_ALT, ' ', 0 , K_F1, K_F2, K_F3, K_F4, K_F5, // 3 K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, 0 , K_HOME, K_UPARROW,K_PGUP,K_KP_MINUS,K_LEFTARROW,K_KP_5,K_RIGHTARROW,K_KP_PLUS,K_END, // 4 K_DOWNARROW,K_PGDN,K_INS,K_DEL, 0 , 0 , 0 , K_F11, K_F12, 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 5 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 6 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 }; #endif static int MapKey (int key) { int result = (key >> 16) & 255; if (result > 127) return 0; result = scantokey[result]; if (key & (1 << 24)) /* extended */ { switch (result) { case K_PAUSE: return (Key_IsGameKey()) ? K_KP_NUMLOCK : 0; case K_ENTER: return (Key_IsGameKey()) ? K_KP_ENTER : K_ENTER; case '/': return (Key_IsGameKey()) ? K_KP_SLASH : '/'; } } else /* standart */ { switch (result) { case K_KP_STAR: return (Key_IsGameKey()) ? K_KP_STAR : '*'; case K_KP_PLUS: return (Key_IsGameKey()) ? K_KP_PLUS : '+'; case K_KP_MINUS: return (Key_IsGameKey()) ? K_KP_MINUS : '-'; case K_HOME: return (Key_IsGameKey()) ? K_KP_HOME : (GetKeyState(VK_NUMLOCK) & 0x01) ? '7' : K_HOME; case K_UPARROW: return (Key_IsGameKey()) ? K_KP_UPARROW : (GetKeyState(VK_NUMLOCK) & 0x01) ? '8' : K_UPARROW; case K_PGUP: return (Key_IsGameKey()) ? K_KP_PGUP : (GetKeyState(VK_NUMLOCK) & 0x01) ? '9' : K_PGUP; case K_LEFTARROW: return (Key_IsGameKey()) ? K_KP_LEFTARROW : (GetKeyState(VK_NUMLOCK) & 0x01) ? '4' : K_LEFTARROW; case K_KP_5: return (Key_IsGameKey()) ? K_KP_5 : '5'; case K_RIGHTARROW: return (Key_IsGameKey()) ? K_KP_RIGHTARROW : (GetKeyState(VK_NUMLOCK) & 0x01) ? '6' : K_RIGHTARROW; case K_END: return (Key_IsGameKey()) ? K_KP_END : (GetKeyState(VK_NUMLOCK) & 0x01) ? '1' : K_END; case K_DOWNARROW: return (Key_IsGameKey()) ? K_KP_DOWNARROW : (GetKeyState(VK_NUMLOCK) & 0x01) ? '2' : K_DOWNARROW; case K_PGDN: return (Key_IsGameKey()) ? K_KP_PGDN : (GetKeyState(VK_NUMLOCK) & 0x01) ? '3' : K_PGDN; case K_INS: return (Key_IsGameKey()) ? K_KP_INS : (GetKeyState(VK_NUMLOCK) & 0x01) ? '0' : K_INS; case K_DEL: return (Key_IsGameKey()) ? K_KP_DEL : (GetKeyState(VK_NUMLOCK) & 0x01) ? '.' : K_DEL; } } return result; } static void AppActivate (BOOL fActive, BOOL minimize) /**************************************************************************** * * Function: AppActivate * Parameters: fActive - True if app is activating * * Description: If the application is activating, then swap the system * into SYSPAL_NOSTATIC mode so that our palettes will display * correctly. * ****************************************************************************/ { HDC hdc; int i; qboolean t; static qboolean sound_active; ActiveApp = fActive; // messy, but it seems to work if (vid_fulldib_on_focus_mode) { Minimized = minimize; if (Minimized) ActiveApp = false; } if (vid_initialized) { // yield the palette if we're losing the focus hdc = GetDC(NULL); if (!Minimized) VID_SetPalette (vid_curpal); scr_fullupdate = 0; ReleaseDC(NULL,hdc); } // enable/disable sound on focus gain/loss if (!ActiveApp && sound_active) { S_BlockSound (); sound_active = false; } else if (ActiveApp && !sound_active) { S_UnblockSound (); sound_active = true; } // minimize/restore fulldib windows/mouse-capture normal windows on demand if (!in_mode_set) { if (ActiveApp) { if (vid_fulldib_on_focus_mode) { if (vid_initialized) { msg_suppress_1 = true; // don't want to see normal mode set message VID_SetMode (vid_fulldib_on_focus_mode, vid_curpal); msg_suppress_1 = false; t = in_mode_set; in_mode_set = true; AppActivate (true, false); in_mode_set = t; } IN_ActivateMouse (); IN_HideMouse (); } else if (modestate == MS_WINDOWED && _enable_mouse.integer) { // with winmouse, we may fail having our // window back from the iconified state. yuck... if (dinput_init) { IN_ActivateMouse (); IN_HideMouse (); } } } if (!ActiveApp) { if (modestate == MS_FULLDIB) { if (vid_initialized) { force_minimized = true; i = vid_fulldib_on_focus_mode; msg_suppress_1 = true; // don't want to see normal mode set message VID_SetMode (windowed_default, vid_curpal); msg_suppress_1 = false; vid_fulldib_on_focus_mode = i; force_minimized = false; // we never seem to get WM_ACTIVATE inactive from this mode set, so we'll // do it manually t = in_mode_set; in_mode_set = true; AppActivate (false, true); in_mode_set = t; } IN_DeactivateMouse (); IN_ShowMouse (); } else if (modestate == MS_WINDOWED && _enable_mouse.integer) { IN_DeactivateMouse (); IN_ShowMouse (); } } } } /* ================ VID_HandlePause ================ */ void VID_HandlePause (qboolean paused) { if (modestate == MS_WINDOWED && _enable_mouse.integer) { if (paused) { IN_DeactivateMouse (); IN_ShowMouse (); } else { IN_ActivateMouse (); IN_HideMouse (); } } } void VID_ToggleFullscreen (void) { } /* =================================================================== MAIN WINDOW =================================================================== */ static int MWheelAccumulator; static UINT uMSG_MOUSEWHEEL = 0; extern cvar_t mwheelthreshold; /* main window procedure */ static LRESULT WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT ret = 0; int fActive, fMinimized, temp; HDC hdc; PAINTSTRUCT ps; if (uMSG_MOUSEWHEEL && uMsg == uMSG_MOUSEWHEEL) { /* Win95/WinNT-3.51 code using MSH_MOUSEWHEEL, see: * http://msdn.microsoft.com/en-us/library/ms645617.aspx */ if (mwheelthreshold.integer >= 1) { MWheelAccumulator += (int) wParam; while (MWheelAccumulator >= mwheelthreshold.integer) { Key_Event(K_MWHEELUP, true); Key_Event(K_MWHEELUP, false); MWheelAccumulator -= mwheelthreshold.integer; } while (MWheelAccumulator <= -mwheelthreshold.integer) { Key_Event(K_MWHEELDOWN, true); Key_Event(K_MWHEELDOWN, false); MWheelAccumulator += mwheelthreshold.integer; } } return DefWindowProc (hWnd, uMsg, wParam, lParam); } switch (uMsg) { case WM_CREATE: if (Win95) { uMSG_MOUSEWHEEL = RegisterWindowMessage("MSWHEEL_ROLLMSG"); if (!uMSG_MOUSEWHEEL) Con_SafePrintf ("couldn't register mousewheel\n"); } break; case WM_SYSCOMMAND: // Check for maximize being hit switch (wParam & ~0x0F) { case SC_MAXIMIZE: // if minimized, bring up as a window before going fullscreen, // so MGL will have the right state to restore if (Minimized) { force_mode_set = true; VID_SetMode (vid_modenum, vid_curpal); force_mode_set = false; } VID_SetMode (vid_fullscreen_mode.integer, vid_curpal); break; case SC_SCREENSAVE: case SC_MONITORPOWER: if (modestate != MS_WINDOWED) { // don't call DefWindowProc() because we don't want to start // the screen saver fullscreen break; } // fall through windowed and allow the screen saver to start default: if (!in_mode_set) S_BlockSound (); ret = DefWindowProc (hWnd, uMsg, wParam, lParam); if (!in_mode_set) S_UnblockSound (); } break; case WM_MOVE: window_x = (short) LOWORD(lParam); window_y = (short) HIWORD(lParam); VID_UpdateWindowStatus (); break; case WM_SIZE: Minimized = false; if (!(wParam & SIZE_RESTORED)) { if (wParam & SIZE_MINIMIZED) Minimized = true; } break; case WM_SYSCHAR: // keep Alt-Space from happening break; case WM_ACTIVATE: fActive = LOWORD(wParam); fMinimized = (BOOL) HIWORD(wParam); AppActivate(!(fActive == WA_INACTIVE), fMinimized); // fix the leftover Alt from any Alt-Tab or the like that switched us away ClearAllStates (); if (!in_mode_set) VID_SetPalette(vid_curpal); break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); if (!in_mode_set && host_initialized) SCR_UpdateWholeScreen (); EndPaint(hWnd, &ps); break; case WM_KEYDOWN: case WM_SYSKEYDOWN: if (in_mode_set) break; Key_Event (MapKey(lParam), true); break; case WM_KEYUP: case WM_SYSKEYUP: if (in_mode_set) break; Key_Event (MapKey(lParam), false); break; // this is complicated because Win32 seems to pack multiple mouse // events into one update sometimes, so we always check all states // and look for events case WM_LBUTTONDOWN: case WM_LBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_XBUTTONDOWN: case WM_XBUTTONUP: case WM_MOUSEMOVE: if (in_mode_set) break; temp = 0; if (wParam & MK_LBUTTON) temp |= 1; if (wParam & MK_RBUTTON) temp |= 2; if (wParam & MK_MBUTTON) temp |= 4; // intellimouse explorer if (wParam & MK_XBUTTON1) temp |= 8; if (wParam & MK_XBUTTON2) temp |= 16; IN_MouseEvent (temp); break; case WM_MOUSEWHEEL: if (in_mode_set) return 0; if ((short) HIWORD(wParam) > 0) { Key_Event(K_MWHEELUP, true); Key_Event(K_MWHEELUP, false); } else { Key_Event(K_MWHEELDOWN, true); Key_Event(K_MWHEELDOWN, false); } return 0; case WM_DISPLAYCHANGE: if (!in_mode_set && (modestate == MS_WINDOWED) && !vid_fulldib_on_focus_mode) { force_mode_set = true; VID_SetMode (vid_modenum, vid_curpal); force_mode_set = false; } break; case WM_CLOSE: if (in_mode_set) break; // this causes Close in the right-click task bar menu not to // work, but right now bad things happen if Close is handled // in that case (garbage and a crash on Win95) if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit", MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES) { Sys_Quit (); } break; case MM_MCINOTIFY: #if !defined(_NO_CDAUDIO) ret = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); #endif /* ! _NO_CDAUDIO */ break; #if !defined(_NO_MIDIDRV) case WM_MSTREAM_UPDATEVOLUME: MIDI_SetChannelVolume((DWORD)wParam, (DWORD)lParam); return 1; case WM_MSTREAM_UPDATEVOLUMES: MIDI_SetAllChannelVolumes((DWORD) wParam); return 1; #endif /* ! _NO_MIDIDRV */ default: /* pass all unhandled messages to DefWindowProc */ ret = DefWindowProc (hWnd, uMsg, wParam, lParam); break; } /* return 1 if handled message, 0 if not */ return ret; } //======================================================== // Video menu stuff //======================================================== static int vid_line, vid_wmodes; typedef struct { int modenum; const char *desc; int iscur; int width; } modedesc_t; #define MAX_COLUMN_SIZE 5 #define MODE_AREA_HEIGHT (MAX_COLUMN_SIZE + 6) #define MAX_MODEDESCS (MAX_COLUMN_SIZE * 3) static modedesc_t modedescs[MAX_MODEDESCS]; static const char no_desc[] = "N/A"; /* ================ VID_MenuDraw ================ */ static void VID_MenuDraw (void) { const char *ptr; int lnummodes, i, j, k, column, row, dup, dupmode; char temp[100]; vmode_t *pv; modedesc_t tmodedesc; ScrollTitle("gfx/menu/title7.lmp"); for (i = 0; i < 3; i++) { ptr = VID_GetModeDescriptionMemCheck (i); modedescs[i].modenum = modelist[i].modenum; modedescs[i].desc = ptr ? ptr : no_desc; modedescs[i].iscur = 0; if (vid_modenum == i) modedescs[i].iscur = 1; } vid_wmodes = 3; lnummodes = VID_NumModes (); for (i = 3; i < lnummodes; i++) { ptr = VID_GetModeDescriptionMemCheck (i); pv = VID_GetModePtr (i); // we only have room for 15 fullscreen modes, so don't allow // 360-wide modes, because if there are 5 320-wide modes and // 5 360-wide modes, we'll run out of space if (ptr && ((pv->width != 360) || COM_CheckParm("-allow360"))) { dup = 0; for (j = 3; j < vid_wmodes; j++) { if (!strcmp (modedescs[j].desc, ptr)) { dup = 1; dupmode = j; break; } } if (dup || (vid_wmodes < MAX_MODEDESCS)) { if (!dup || COM_CheckParm("-noforcevga")) { if (dup) { k = dupmode; } else { k = vid_wmodes; } modedescs[k].modenum = i; modedescs[k].desc = ptr; modedescs[k].iscur = 0; modedescs[k].width = pv->width; if (i == vid_modenum) modedescs[k].iscur = 1; if (!dup) vid_wmodes++; } } } } // sort the modes on width (to handle picking up oddball dibonly modes // after all the others) for (i = 3; i< (vid_wmodes-1); i++) { for (j = (i+1); j < vid_wmodes; j++) { if (modedescs[i].width > modedescs[j].width) { tmodedesc = modedescs[i]; modedescs[i] = modedescs[j]; modedescs[j] = tmodedesc; } } } M_Print (13*8, 60, "Windowed Modes"); column = 16; row = 60+2*8; for (i = 0; i < 3; i++) { if (modedescs[i].iscur) M_PrintWhite (column, row, modedescs[i].desc); else M_Print (column, row, modedescs[i].desc); column += 13*8; } if (vid_wmodes > 3) { M_Print (12*8, 60+4*8, "Fullscreen Modes"); column = 16; row = 60+6*8; for (i = 3; i < vid_wmodes; i++) { if (modedescs[i].iscur) M_PrintWhite (column, row, modedescs[i].desc); else M_Print (column, row, modedescs[i].desc); column += 13*8; if (((i - 3) % VID_ROW_SIZE) == (VID_ROW_SIZE - 1)) { column = 16; row += 8; } } } // line cursor if (vid_testingmode) { q_snprintf (temp, sizeof(temp), "TESTING %s", modedescs[vid_line].desc); M_Print (13*8, 60 + MODE_AREA_HEIGHT * 8 + 8*4, temp); M_Print (9*8, 60 + MODE_AREA_HEIGHT * 8 + 8*6, "Please wait 5 seconds..."); } else { M_Print (9*8, 60 + MODE_AREA_HEIGHT * 8, "Press Enter to set mode"); M_Print (6*8, 60 + MODE_AREA_HEIGHT * 8 + 8*1, "T to test mode for 5 seconds"); ptr = VID_GetModeDescription2 (vid_modenum); if (ptr) { q_snprintf (temp, sizeof(temp), "D to set default: %s", ptr); M_Print (2*8, 60 + MODE_AREA_HEIGHT * 8 + 8*2, temp); } ptr = VID_GetModeDescription2 (_vid_default_mode_win.integer); if (ptr) { q_snprintf (temp, sizeof(temp), "Current default: %s", ptr); M_Print (3*8, 60 + MODE_AREA_HEIGHT * 8 + 8*5, temp); } M_Print (15*8, 60 + MODE_AREA_HEIGHT * 8 + 8*6, "Esc to exit"); row = 60 + 2*8 + (vid_line / VID_ROW_SIZE) * 8; column = 8 + (vid_line % VID_ROW_SIZE) * 13*8; if (vid_line >= 3) row += 3*8; M_DrawCharacter (column, row, 12+((int)(realtime*4)&1)); } } /* ================ VID_MenuKey ================ */ static void VID_MenuKey (int key) { if (vid_testingmode) return; switch (key) { case K_ESCAPE: S_LocalSound ("raven/menu1.wav"); M_Menu_Options_f (); break; case K_LEFTARROW: S_LocalSound ("raven/menu1.wav"); vid_line = ((vid_line / VID_ROW_SIZE) * VID_ROW_SIZE) + ((vid_line + 2) % VID_ROW_SIZE); if (vid_line >= vid_wmodes) vid_line = vid_wmodes - 1; break; case K_RIGHTARROW: S_LocalSound ("raven/menu1.wav"); vid_line = ((vid_line / VID_ROW_SIZE) * VID_ROW_SIZE) + ((vid_line + 4) % VID_ROW_SIZE); if (vid_line >= vid_wmodes) vid_line = (vid_line / VID_ROW_SIZE) * VID_ROW_SIZE; break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); vid_line -= VID_ROW_SIZE; if (vid_line < 0) { vid_line += ((vid_wmodes + (VID_ROW_SIZE - 1)) / VID_ROW_SIZE) * VID_ROW_SIZE; while (vid_line >= vid_wmodes) vid_line -= VID_ROW_SIZE; } break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); vid_line += VID_ROW_SIZE; if (vid_line >= vid_wmodes) { vid_line -= ((vid_wmodes + (VID_ROW_SIZE - 1)) / VID_ROW_SIZE) * VID_ROW_SIZE; while (vid_line < 0) vid_line += VID_ROW_SIZE; } break; case K_ENTER: S_LocalSound ("raven/menu1.wav"); VID_SetMode (modedescs[vid_line].modenum, vid_curpal); break; case 'T': case 't': S_LocalSound ("raven/menu1.wav"); // have to set this before setting the mode because WM_PAINT // happens during the mode set and does a VID_Update, which // checks vid_testingmode vid_testingmode = 1; vid_testendtime = realtime + 5.0; if (!VID_SetMode (modedescs[vid_line].modenum, vid_curpal)) vid_testingmode = 0; break; case 'D': case 'd': S_LocalSound ("raven/menu1.wav"); firstupdate = false; Cvar_SetValueQuick (&_vid_default_mode_win, vid_modenum); break; default: break; } } engine/h2shared/view.h000066400000000000000000000024261444734033100151550ustar00rootroot00000000000000/* view.h -- player eye positioning * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_VIEW_H #define __HX2_VIEW_H extern cvar_t v_gamma; extern cvar_t crosshair; extern cvar_t cl_crossx, cl_crossy; extern cvar_t crosshaircolor; #ifdef GLQUAKE extern float v_blend[4]; #endif extern byte gammatable[256]; // palette is sent through this extern unsigned short ramps[3][256]; void V_Init (void); void V_RenderView (void); void V_CalcBlend (void); float V_CalcRoll (vec3_t angles, vec3_t velocity); void V_UpdatePalette (void); #endif /* __HX2_VIEW_H */ engine/h2shared/vregset.c000066400000000000000000000031631444734033100156540ustar00rootroot00000000000000/* * vregset.c -- video register-setting interpreter. * from quake1 source with minor adaptations for uhexen2. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "quakedef.h" #include "vregset.h" /* ================ VideoRegisterSet ================ */ void VideoRegisterSet (const int *pregset) { int port, temp0, temp1, temp2; for ( ;; ) { switch (*pregset++) { case VRS_END: return; case VRS_BYTE_OUT: port = *pregset++; outportb (port, *pregset++); break; case VRS_BYTE_RMW: port = *pregset++; temp0 = *pregset++; temp1 = *pregset++; temp2 = inportb (port); temp2 &= temp0; temp2 |= temp1; outportb (port, temp2); break; case VRS_WORD_OUT: port = *pregset++; outportb (port, *pregset & 0xFF); outportb (port+1, *pregset >> 8); pregset++; break; default: Sys_Error ("%s: Invalid command", __thisfunc__); } } } engine/h2shared/vregset.h000066400000000000000000000030171444734033100156570ustar00rootroot00000000000000/* * vregset.h -- header file for video register-setting interpreter. * from quake1 source with minor adaptations for uhexen2. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __VREGSET_H__ #define __VREGSET_H__ /* * registers & subregisters */ #define MISC_OUTPUT 0x3C2 #define SC_INDEX 0x3C4 #define SC_DATA 0x3C5 #define SYNC_RESET 0 #define MAP_MASK 2 #define MEMORY_MODE 4 #define GC_INDEX 0x3CE #define GC_DATA 0x3CF #define READ_MAP 4 #define GRAPHICS_MODE 5 #define MISCELLANOUS 6 #define CRTC_INDEX 0x3D4 #define CRTC_DATA 0x3D5 #define MAX_SCAN_LINE 9 #define UNDERLINE 0x14 #define MODE_CONTROL 0x17 /* * register-set commands */ #define VRS_END 0 #define VRS_BYTE_OUT 1 #define VRS_BYTE_RMW 2 #define VRS_WORD_OUT 3 void VideoRegisterSet (const int *pregset); #endif /* __VREGSET_H__ */ engine/h2shared/wad.c000066400000000000000000000071021444734033100147450ustar00rootroot00000000000000/* * wad.c -- wad file loading * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" int wad_numlumps; lumpinfo_t *wad_lumps; byte *wad_base = NULL; void SwapPic (qpic_t *pic); /* ================== W_CleanupName Lowercases name and pads with spaces and a terminating 0 to the length of lumpinfo_t->name. Used so lumpname lookups can proceed rapidly by comparing 4 chars at a time Space padding is so names can be printed nicely in tables. Can safely be performed in place. ================== */ static void W_CleanupName (const char *in, char *out) { int i; int c; for (i = 0; i < 16; i++ ) { c = in[i]; if (!c) break; if (c >= 'A' && c <= 'Z') c += ('a' - 'A'); out[i] = c; } for ( ; i < 16; i++ ) out[i] = 0; } /* ==================== W_LoadWadFile ==================== */ void W_LoadWadFile (const char *filename) { lumpinfo_t *lump_p; wadinfo_t *header; int i; int infotableofs; if (wad_base) Z_Free (wad_base); wad_base = FS_LoadZoneFile (filename, Z_SECZONE, NULL); if (!wad_base) Sys_Error ("%s: couldn't load %s", __thisfunc__, filename); header = (wadinfo_t *)wad_base; if (header->identification[0] != 'W' || header->identification[1] != 'A' || header->identification[2] != 'D' || header->identification[3] != '2') { Sys_Error ("Wad file %s doesn't have WAD2 id\n", filename); } wad_numlumps = LittleLong(header->numlumps); infotableofs = LittleLong(header->infotableofs); wad_lumps = (lumpinfo_t *)(wad_base + infotableofs); for (i = 0, lump_p = wad_lumps; i < wad_numlumps; i++, lump_p++) { lump_p->filepos = LittleLong(lump_p->filepos); lump_p->size = LittleLong(lump_p->size); W_CleanupName (lump_p->name, lump_p->name); // CAUTION: in-place editing!!! if (lump_p->type == TYP_QPIC) SwapPic ( (qpic_t *)(wad_base + lump_p->filepos)); } } /* ============= W_GetLumpinfo ============= */ lumpinfo_t *W_GetLumpinfo (const char *name) { int i; lumpinfo_t *lump_p; char clean[16]; W_CleanupName (name, clean); for (lump_p = wad_lumps, i = 0; i < wad_numlumps; i++, lump_p++) { if (!strcmp(clean, lump_p->name)) return lump_p; } Sys_Error ("%s: %s not found", __thisfunc__, name); return NULL; } void *W_GetLumpName (const char *name) { lumpinfo_t *lump; lump = W_GetLumpinfo (name); return (void *)(wad_base + lump->filepos); } #if 0 /* no callers */ void *W_GetLumpNum (int num) { lumpinfo_t *lump; if (num < 0 || num > wad_numlumps) Sys_Error ("%s: bad number: %i", __thisfunc__, num); lump = wad_lumps + num; return (void *)(wad_base + lump->filepos); } #endif /* ============================================================================= automatic byte swapping ============================================================================= */ void SwapPic (qpic_t *pic) { pic->width = LittleLong(pic->width); pic->height = LittleLong(pic->height); } engine/h2shared/wad.h000066400000000000000000000034001444734033100147470ustar00rootroot00000000000000/* * wad.h -- wad file loading * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef WAD_H_ #define WAD_H_ //=============== // TYPES //=============== #define CMP_NONE 0 #define CMP_LZSS 1 #define TYP_NONE 0 #define TYP_LABEL 1 #define TYP_LUMPY 64 // 64 + grab command number #define TYP_PALETTE 64 #define TYP_QTEX 65 #define TYP_QPIC 66 #define TYP_SOUND 67 #define TYP_MIPTEX 68 typedef struct { int width, height; byte data[4]; // variably sized } qpic_t; typedef struct { char identification[4]; // should be WAD2 or 2DAW int numlumps; int infotableofs; } wadinfo_t; typedef struct { int filepos; int disksize; int size; // uncompressed char type; char compression; char pad1, pad2; char name[16]; // must be null terminated } lumpinfo_t; extern int wad_numlumps; extern lumpinfo_t *wad_lumps; extern byte *wad_base; void W_LoadWadFile (const char *filename); void *W_GetLumpName (const char *name); lumpinfo_t *W_GetLumpinfo (const char *name); void SwapPic (qpic_t *pic); #endif /* WAD_H_ */ engine/h2shared/wgl_func.h000066400000000000000000000056711444734033100160140ustar00rootroot00000000000000/* wgl_func.h -- WGL functions we may need to link to * make sure NOT to protect this file against multiple inclusions! * * Copyright (C) 2005-2016 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* core wgl functions */ #ifdef GL_DLSYM #ifndef GL_FUNCTION #define GL_FUNCTION(ret, func, params) \ typedef ret (WINAPI *func##_f) params; \ static func##_f func##_fp; #endif GL_FUNCTION(PROC, wglGetProcAddress, (LPCSTR)) GL_FUNCTION(HGLRC, wglCreateContext, (HDC)) GL_FUNCTION(BOOL, wglDeleteContext, (HGLRC)) GL_FUNCTION(BOOL, wglMakeCurrent, (HDC, HGLRC)) GL_FUNCTION(HGLRC, wglGetCurrentContext, (VOID)) GL_FUNCTION(HDC, wglGetCurrentDC, (VOID)) #else #ifndef WGL_FUNC_H #define WGL_FUNC_H #define wglGetProcAddress_fp wglGetProcAddress #define wglCreateContext_fp wglCreateContext #define wglDeleteContext_fp wglDeleteContext #define wglMakeCurrent_fp wglMakeCurrent #define wglGetCurrentContext_fp wglGetCurrentContext #define wglGetCurrentDC_fp wglGetCurrentDC #endif /* WGL_FUNC_H */ #endif /* !defined(GL_DLSYM) */ #undef GL_FUNCTION /* wgl funcs needed when using a standalone (minigl) driver */ #ifdef GL_DLSYM #ifndef GL_FUNCTION_OPT #define GL_FUNCTION_OPT(ret, func, def, params) \ typedef ret (WINAPI *func##_f) params; \ static func##_f func##_fp; #endif GL_FUNCTION_OPT(int, wglChoosePixelFormat, ChoosePixelFormat, (HDC, CONST PIXELFORMATDESCRIPTOR *)) GL_FUNCTION_OPT(int, wglDescribePixelFormat, DescribePixelFormat, (HDC, int, UINT, LPPIXELFORMATDESCRIPTOR)) GL_FUNCTION_OPT(int, wglGetPixelFormat, GetPixelFormat, (HDC)) GL_FUNCTION_OPT(BOOL, wglSetPixelFormat, SetPixelFormat, (HDC, int, CONST PIXELFORMATDESCRIPTOR *)) GL_FUNCTION_OPT(BOOL, wglSwapBuffers, SwapBuffers, (HDC)) #else #ifndef WGL_OPTFUNC_H #define WGL_OPTFUNC_H #define wglChoosePixelFormat_fp ChoosePixelFormat #define wglDescribePixelFormat_fp DescribePixelFormat #define wglGetPixelFormat_fp GetPixelFormat #define wglSetPixelFormat_fp SetPixelFormat #define wglSwapBuffers_fp SwapBuffers #endif /* WGL_OPTFUNC_H */ #endif /* !defined(GL_DLSYM) */ #undef GL_FUNCTION_OPT /* typedefs for wgl functions linked to at runtime */ #ifndef WGL_FUNC_TYPEDEFS #define WGL_FUNC_TYPEDEFS typedef BOOL (WINAPI *wglSwapIntervalEXT_f) (int); #endif /* WGL_FUNC_TYPEDEFS */ engine/h2shared/win32res.rc000066400000000000000000000017531444734033100160360ustar00rootroot00000000000000// win32res.rc -- Win32 resource file for Hexen II / HexenWorld #include #include "resource.h" // Icon IDI_ICON2 ICON DISCARDABLE RES_ICONFILE // Dialog #if !defined(NO_SPLASHES) #if !defined(GLQUAKE) IDD_DIALOG1 DIALOGEX 0, 0, 78, 21 STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | DS_CENTER | WS_POPUP | WS_VISIBLE EXSTYLE WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE FONT 16, "Times New Roman", 0, 0, 0x1 BEGIN CTEXT SPLASH_STR, IDC_STATIC, 2, 3, 74, 15, SS_CENTERIMAGE | SS_SUNKEN END #else // OpenGL version IDD_DIALOG1 DIALOGEX 0, 0, 167, 27 STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | DS_CENTER | WS_POPUP | WS_VISIBLE EXSTYLE WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE FONT 16, "Times New Roman", 0, 0, 0x1 BEGIN CONTROL 112, IDC_STATIC, "Static", SS_BITMAP | SS_REALSIZEIMAGE, 0, 0, 167, 28 END // Bitmap IDB_HXBITMAP BITMAP DISCARDABLE SPLASH_BMP #endif // GLQUAKE #endif // NO_SPLASHES // String Table STRINGTABLE DISCARDABLE BEGIN IDS_STRING1 RES_STRING END engine/h2shared/winquake.h000066400000000000000000000043171444734033100160300ustar00rootroot00000000000000/* * winquake.h -- Windows-specific Quake header file * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __WINQUAKE_H #define __WINQUAKE_H /* include windows.h here, because we need the data types */ #include /* required compatibility versions for directx components */ #define DIRECTDRAW_VERSION 0x0300 #define DIRECTSOUND_VERSION 0x0300 #define DIRECTINPUT_VERSION 0x0300 #if !defined(__cplusplus) && !defined(CINTERFACE) #define CINTERFACE /* for directx macros. */ #endif extern HINSTANCE global_hInstance; extern int global_nCmdShow; #ifndef WM_MOUSEWHEEL #define WM_MOUSEWHEEL 0x020A #endif /* IntelliMouse explorer buttons: These are ifdef'ed out for < Win2000 in the Feb. 2001 version of MS's platform SDK, but we need them for compilation. */ #ifndef WM_XBUTTONDOWN #define WM_XBUTTONDOWN 0x020B #define WM_XBUTTONUP 0x020C #endif #ifndef MK_XBUTTON1 #define MK_XBUTTON1 0x0020 #define MK_XBUTTON2 0x0040 #endif extern qboolean dinput_init; extern HWND mainwindow; extern qboolean ActiveApp, Minimized; extern qboolean Win95, Win95old, WinNT, WinVista; extern int window_center_x, window_center_y; extern RECT window_rect; #if !defined(NO_SPLASHES) extern HWND hwnd_dialog; #endif LRESULT CDAudio_MessageHandler (HWND, UINT, WPARAM, LPARAM); #define WM_MSTREAM_UPDATEVOLUME (WM_USER + 101) #define WM_MSTREAM_UPDATEVOLUMES (WM_USER + 102) void MIDI_SetChannelVolume(DWORD chn, DWORD percent); void MIDI_SetAllChannelVolumes (DWORD percent); #endif /* __WINQUAKE_H */ engine/h2shared/worlda.asm000066400000000000000000000046271444734033100160310ustar00rootroot00000000000000; ; worlda.asm ; x86 assembly-language server testing stuff ; this file uses NASM syntax. ; ; Copyright (C) 1996-1997 Id Software, Inc. ; ; This program is free software; you can redistribute it and/or ; modify it under the terms of the GNU General Public License ; as published by the Free Software Foundation; either version 2 ; of the License, or (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to: ; Free Software Foundation, Inc. ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ; %include "asm_nasm.inc" %include "worlda.inc" ; underscore prefix handling ; for C-shared symbols: %ifmacro _sym_prefix ; C-shared externs: ; C-shared globals: _sym_prefix SV_HullPointContents %endif ; _sym_prefix SEGMENT .data Ltemp dd 0 SEGMENT .text ALIGN 4 global SV_HullPointContents SV_HullPointContents: push edi mov eax, dword [8+4+esp] test eax,eax js Lhquickout push ebx mov ebx, dword [4+8+esp] push ebp mov edx, dword [12+12+esp] mov edi, dword [0+ebx] mov ebp, dword [4+ebx] sub ebx,ebx push esi Lhloop: %ifdef ENABLE_BSP2 lea eax, [eax+eax*2] ;eax*=3 mov ecx, dword [0+edi+eax*4] %else mov ecx, dword [0+edi+eax*8] mov eax, dword [4+edi+eax*8] mov esi,eax ror eax,16 %endif lea ecx, [ecx+ecx*4] mov bl, byte [16+ebp+ecx*4] cmp bl,3 jb Lnodot fld dword [0+ebp+ecx*4] fmul dword [0+edx] fld dword [0+4+ebp+ecx*4] fmul dword [4+edx] fld dword [0+8+ebp+ecx*4] fmul dword [8+edx] fxch st1 faddp st2,st0 faddp st1,st0 fsub dword [12+ebp+ecx*4] jmp Lsub Lnodot: fld dword [12+ebp+ecx*4] fsubr dword [edx+ebx*4] Lsub: %ifdef ENABLE_BSP2 ; if dist is negative(float's sign bit is set), copy child[1] into eax fstp dword [Ltemp] test dword [Ltemp],080000000h jns Lpos mov eax, dword [8+edi+eax*4] test eax,eax jns Lhloop jmp Lhdone Lpos: ; otherwise copy child[0] into eax mov eax,dword [4+edi+eax*4] test eax,eax %else sar eax,16 sar esi,16 fstp dword [Ltemp] mov ecx, dword [Ltemp] sar ecx,31 and esi,ecx xor ecx,0FFFFFFFFh and eax,ecx or eax,esi %endif jns Lhloop Lhdone: pop esi pop ebp pop ebx Lhquickout: pop edi ret engine/h2shared/worlda.inc000066400000000000000000000001751444734033100160140ustar00rootroot00000000000000;comment-out the following to disable BSP2 support ;!! remember to do the same thing in h2config.h !! %define ENABLE_BSP2 1 engine/h2shared/xbm_icon.h000066400000000000000000000020521444734033100157740ustar00rootroot00000000000000/* * xbm_icon.h -- wrapper header file including the correct xbm * icon file for different Hexen II flavors * * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #if defined(H2W) #include "../../resource/hexenworld.xbm" #elif defined(H2MP) #include "../resource/h2mp.xbm" #else #include "../resource/hexen2.xbm" #endif engine/h2shared/zone.c000066400000000000000000000675101444734033100151560ustar00rootroot00000000000000/* zone.c -- Memory management * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" /* whether Z_Malloc should check zone * integrity before every allocation */ #define Z_CHECKHEAP 0 /* whether we compile the debug report * commands. see in Memory_Init() */ #define Z_DEBUG_COMMANDS 0 #define ZONE_MINSIZE 0x40000 #define ZONE_MAXSIZE 0x200000 #if defined(SERVERONLY) #define ZONE_DEFSIZE 0x40000 #else #define ZONE_DEFSIZE 0x60000 #endif /* SERVERONLY */ #define ZMAGIC 0x1d4a11 #define ZMAGIC2 0xf382da #define HUNK_SENTINAL 0x1df001ed #define MINFRAGMENT 64 /* setup size for secondary zone: */ #define MEM_STATIC_TEX 0x40000 #define MEM_CODEC_MEM 0 #if defined(CODECS_USE_ZONE) /* this setup assumes only one codec * would be active at a time. */ #if defined(USE_CODEC_MP3) #if (MEM_CODEC_MEM < LIBMAD_NEEDMEM) #undef MEM_CODEC_MEM #define MEM_CODEC_MEM LIBMAD_NEEDMEM #endif #endif /* LIBMAD */ #if defined(USE_CODEC_VORBIS) #if (MEM_CODEC_MEM < VORBIS_NEEDMEM) #undef MEM_CODEC_MEM #define MEM_CODEC_MEM VORBIS_NEEDMEM #endif #endif /* VORBIS */ #endif /* CODECS_USE_ZONE */ #if defined(SERVERONLY) #undef MEM_STATIC_TEX #define MEM_STATIC_TEX 0 #undef MEM_CODEC_MEM #define MEM_CODEC_MEM 0 #endif #define SECZONE_SIZE \ (MEM_STATIC_TEX + MEM_CODEC_MEM) typedef struct memblock_s { int size; /* including the header and possibly tiny fragments */ int tag; /* a tag of 0 is a free block */ int magic; /* should be ZMAGIC */ int pad; /* pad to 64 bit boundary */ struct memblock_s *next, *prev; } memblock_t; typedef struct memzone_s { int size; /* total bytes malloced, including header */ memblock_t blocklist; /* start / end cap for linked list */ memblock_t *rover; } memzone_t; typedef struct zonelist_s { int id, magic; const char *name; memzone_t *zone; struct zonelist_s *next; } zonelist_t; #if defined (SERVERONLY) #define Cache_FreeLow(x) #define Cache_FreeHigh(x) #else static void Cache_FreeLow (int new_low_hunk); static void Cache_FreeHigh (int new_high_hunk); #endif /* ============================================================================== ZONE MEMORY ALLOCATION There is never any space between memblocks, and there will never be two contiguous free memblocks. The rover can be left pointing at a non-empty block The zone calls are pretty much only used for small strings and structures, all big things are allocated on the hunk. ============================================================================== */ static zonelist_t *zonelist; static char mainzone[] = "MAINZONE"; #if (SECZONE_SIZE > 0) static char sec_zone[] = "SEC_ZONE"; #endif /* ======================== Z_Free ======================== */ void Z_Free (void *ptr) { zonelist_t *z; memblock_t *block, *other; if (!ptr) Sys_Error ("%s: NULL pointer", __thisfunc__); block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t)); if (block->tag == 0) Sys_Error ("%s: freed a freed pointer", __thisfunc__); z = zonelist; while (z != NULL) { if (z->magic == block->magic) break; z = z->next; } if (z == NULL) Sys_Error ("%s: freed a pointer without ZMAGIC", __thisfunc__); block->tag = 0; /* mark as free */ other = block->prev; if (!other->tag) { /* merge with previous free block */ other->size += block->size; other->next = block->next; other->next->prev = other; if (block == z->zone->rover) z->zone->rover = other; block = other; } other = block->next; if (!other->tag) { /* merge the next free block onto the end */ block->size += other->size; block->next = other->next; block->next->prev = block; if (other == z->zone->rover) z->zone->rover = block; } } static void *Z_TagMalloc (zonelist_t *z, int size, int tag) { int extra; memblock_t *start, *rover, *newblock, *base; if (!tag) Sys_Error ("%s: tried to use a 0 tag", __thisfunc__); /* scan through the block list looking for the first free block * of sufficient size */ size += sizeof(memblock_t); /* account for size of block header */ size += 4; /* space for memory trash tester */ size = (size + 7) & ~7; /* align to 8-byte boundary */ base = rover = z->zone->rover; start = base->prev; do { if (rover == start) /* scaned all the way around the list */ return NULL; if (rover->tag) base = rover = rover->next; else rover = rover->next; } while (base->tag || base->size < size); /* found a block big enough */ extra = base->size - size; if (extra > MINFRAGMENT) { /* there will be a free fragment after the allocated block */ newblock = (memblock_t *) ((byte *)base + size); newblock->size = extra; newblock->tag = 0; /* free block */ newblock->prev = base; newblock->magic = z->magic; newblock->next = base->next; newblock->next->prev = newblock; base->next = newblock; base->size = size; } base->tag = tag; /* no longer a free block */ z->zone->rover = base->next; /* next allocation will start looking here */ base->magic = z->magic; /* marker for memory trash testing */ *(int *)((byte *)base + base->size - 4) = z->magic; return (void *) ((byte *)base + sizeof(memblock_t)); } /* ======================== Z_CheckHeap ======================== */ #if Z_CHECKHEAP static void Z_CheckHeap (memzone_t *zone) { memblock_t *block; for (block = zone->blocklist.next ; ; block = block->next) { if (block->next == &zone->blocklist) break; /* all blocks have been hit */ if ( (byte *)block + block->size != (byte *)block->next) Sys_Error ("%s: block size does not touch the next block", __thisfunc__); if ( block->next->prev != block) Sys_Error ("%s: next block doesn't have proper back link", __thisfunc__); if (!block->tag && !block->next->tag) Sys_Error ("%s: two consecutive free blocks", __thisfunc__); } } #endif /* Z_CHECKHEAP */ /* ======================== Z_Malloc ======================== */ void *Z_Malloc (int size, int zone_id) { void *buf; zonelist_t* z; z = zonelist; while (z != NULL) { if (z->id & zone_id) break; z = z->next; } if (z == NULL) Sys_Error ("%s: Bad zone id %i", __thisfunc__, zone_id); #if Z_CHECKHEAP Z_CheckHeap (z->zone); /* DEBUG */ #endif buf = Z_TagMalloc (z, size, 1); if (!buf) Sys_Error ("%s: failed on allocation of %i bytes", __thisfunc__, size); memset (buf, 0, size); return buf; } void *Z_Realloc (void *ptr, int size, int zone_id) { int old_size; void *old_ptr; zonelist_t *z; memblock_t *block; if (!ptr) return Z_Malloc (size, zone_id); block = (memblock_t *) ((byte *) ptr - sizeof (memblock_t)); if (block->tag == 0) Sys_Error ("%s: realloced a freed pointer", __thisfunc__); z = zonelist; while (z != NULL) { if (z->magic == block->magic) break; z = z->next; } if (z == NULL) Sys_Error ("%s: realloced a pointer without ZMAGIC", __thisfunc__); old_size = block->size; old_size -= (4 + (int)sizeof(memblock_t)); /* see Z_TagMalloc() */ old_ptr = ptr; Z_Free (ptr); z = zonelist; while (z != NULL) { if (z->id & zone_id) break; z = z->next; } if (z == NULL) Sys_Error ("%s: Bad zone id %i", __thisfunc__, zone_id); ptr = Z_TagMalloc (z, size, 1); if (!ptr) Sys_Error ("%s: failed on allocation of %i bytes", __thisfunc__, size); if (ptr != old_ptr) memmove (ptr, old_ptr, q_min(old_size, size)); if (old_size < size) memset ((byte *)ptr + old_size, 0, size - old_size); return ptr; } char *Z_Strdup (const char *s) { size_t sz = strlen(s) + 1; char *ptr = (char *) Z_Malloc (sz, Z_MAINZONE); memcpy (ptr, s, sz); return ptr; } /*============================================================================*/ #define HUNKNAME_LEN 24 typedef struct { int sentinal; int size; /* including sizeof(hunk_t), -1 = not allocated */ char name[HUNKNAME_LEN]; } hunk_t; static byte *hunk_base; static int hunk_size; static int hunk_low_used; static int hunk_high_used; static qboolean hunk_tempactive; static int hunk_tempmark; /* ============== Hunk_Check Run consistancy and sentinal trashing checks ============== */ void Hunk_Check (void) { hunk_t *h; for (h = (hunk_t *)hunk_base ; (byte *)h != hunk_base + hunk_low_used ; ) { if (h->sentinal != HUNK_SENTINAL) Sys_Error ("%s: trashed sentinal", __thisfunc__); if (h->size < (int) sizeof(hunk_t) || h->size + (byte *)h - hunk_base > hunk_size) Sys_Error ("%s: bad size", __thisfunc__); h = (hunk_t *)((byte *)h + h->size); } } /* =================== Hunk_AllocName =================== */ void *Hunk_AllocName (int size, const char *name) { hunk_t *h; #ifdef PARANOID Hunk_Check (); #endif if (size < 0) Sys_Error ("%s: bad size: %i for %s", __thisfunc__, size, name); size = sizeof(hunk_t) + ((size + 15) & ~15); if (hunk_size - hunk_low_used - hunk_high_used < size) Sys_Error ("%s: failed on %i bytes for %s", __thisfunc__, size, name); h = (hunk_t *)(hunk_base + hunk_low_used); hunk_low_used += size; Cache_FreeLow (hunk_low_used); memset (h, 0, size); h->size = size; h->sentinal = HUNK_SENTINAL; q_strlcpy (h->name, name, HUNKNAME_LEN); return (void *)(h + 1); } /* =================== Hunk_Alloc =================== */ void *Hunk_Alloc (int size) { return Hunk_AllocName (size, "unknown"); } int Hunk_LowMark (void) { return hunk_low_used; } void Hunk_FreeToLowMark (int mark) { if (mark < 0 || mark > hunk_low_used) Sys_Error ("%s: bad mark %i", __thisfunc__, mark); memset (hunk_base + mark, 0, hunk_low_used - mark); hunk_low_used = mark; } int Hunk_HighMark (void) { if (hunk_tempactive) { hunk_tempactive = false; Hunk_FreeToHighMark (hunk_tempmark); } return hunk_high_used; } void Hunk_FreeToHighMark (int mark) { if (hunk_tempactive) { hunk_tempactive = false; Hunk_FreeToHighMark (hunk_tempmark); } if (mark < 0 || mark > hunk_high_used) Sys_Error ("%s: bad mark %i", __thisfunc__, mark); memset (hunk_base + hunk_size - hunk_high_used, 0, hunk_high_used - mark); hunk_high_used = mark; } /* =================== Hunk_HighAllocName =================== */ void *Hunk_HighAllocName (int size, const char *name) { hunk_t *h; if (size < 0) Sys_Error ("%s: bad size: %i", __thisfunc__, size); if (hunk_tempactive) { Hunk_FreeToHighMark (hunk_tempmark); hunk_tempactive = false; } #ifdef PARANOID Hunk_Check (); #endif size = sizeof(hunk_t) + ((size + 15) & ~15); if (hunk_size - hunk_low_used - hunk_high_used < size) { Con_Printf ("%s: failed on %i bytes\n", __thisfunc__, size); return NULL; } hunk_high_used += size; Cache_FreeHigh (hunk_high_used); h = (hunk_t *)(hunk_base + hunk_size - hunk_high_used); memset (h, 0, size); h->size = size; h->sentinal = HUNK_SENTINAL; q_strlcpy (h->name, name, HUNKNAME_LEN); return (void *)(h + 1); } /* ================= Hunk_TempAlloc Return space from the top of the hunk ================= */ void *Hunk_TempAlloc (int size) { void *buf; size = (size + 15) & ~15; if (hunk_tempactive) { Hunk_FreeToHighMark (hunk_tempmark); hunk_tempactive = false; } hunk_tempmark = Hunk_HighMark (); buf = Hunk_HighAllocName (size, "temp"); hunk_tempactive = true; return buf; } char *Hunk_Strdup (const char *s, const char *name) { size_t sz = strlen(s) + 1; char *ptr = (char *) Hunk_AllocName (sz, name); memcpy (ptr, s, sz); return ptr; } /* =============================================================================== CACHE MEMORY =============================================================================== */ #if !defined(SERVERONLY) /* CACHE not used in dedicated server apps */ #define CACHENAME_LEN 32 typedef struct cache_system_s { int size; /* including this header */ cache_user_t *user; char name[CACHENAME_LEN]; struct cache_system_s *prev, *next; struct cache_system_s *lru_prev, *lru_next; /* for LRU flushing */ } cache_system_t; static cache_system_t *Cache_TryAlloc (int size, qboolean nobottom); static cache_system_t cache_head; /* =========== Cache_Move =========== */ static void Cache_Move ( cache_system_t *c) { cache_system_t *new_cs; /* we are clearing up space at the bottom, so only allocate it late */ new_cs = Cache_TryAlloc (c->size, true); if (new_cs) { /* Con_Printf ("cache_move ok\n");*/ memcpy (new_cs+1, c+1, c->size - sizeof(cache_system_t)); new_cs->user = c->user; memcpy (new_cs->name, c->name, sizeof(new_cs->name)); Cache_Free (c->user); new_cs->user->data = (void *)(new_cs + 1); } else { /* Con_Printf ("cache_move failed\n");*/ Cache_Free (c->user); /* tough luck... */ } } /* ============ Cache_FreeLow Throw things out until the hunk can be expanded to the given point ============ */ static void Cache_FreeLow (int new_low_hunk) { cache_system_t *c; while (1) { c = cache_head.next; if (c == &cache_head) return; /* nothing in cache at all */ if ((byte *)c >= hunk_base + new_low_hunk) return; /* there is space to grow the hunk */ Cache_Move ( c ); /* reclaim the space */ } } /* ============ Cache_FreeHigh Throw things out until the hunk can be expanded to the given point ============ */ static void Cache_FreeHigh (int new_high_hunk) { cache_system_t *c, *prev; prev = NULL; while (1) { c = cache_head.prev; if (c == &cache_head) return; /* nothing in cache at all */ if ( (byte *)c + c->size <= hunk_base + hunk_size - new_high_hunk) return; /* there is space to grow the hunk */ if (c == prev) Cache_Free (c->user); /* didn't move out of the way */ else { Cache_Move (c); /* try to move it */ prev = c; } } } static void Cache_UnlinkLRU (cache_system_t *cs) { if (!cs->lru_next || !cs->lru_prev) Sys_Error ("%s: NULL link", __thisfunc__); cs->lru_next->lru_prev = cs->lru_prev; cs->lru_prev->lru_next = cs->lru_next; cs->lru_prev = cs->lru_next = NULL; } static void Cache_MakeLRU (cache_system_t *cs) { if (cs->lru_next || cs->lru_prev) Sys_Error ("%s: active link", __thisfunc__); cache_head.lru_next->lru_prev = cs; cs->lru_next = cache_head.lru_next; cs->lru_prev = &cache_head; cache_head.lru_next = cs; } /* ============ Cache_TryAlloc Looks for a free block of memory between the high and low hunk marks Size should already include the header and padding ============ */ static cache_system_t *Cache_TryAlloc (int size, qboolean nobottom) { cache_system_t *cs, *new_cs; /* is the cache completely empty? */ if (!nobottom && cache_head.prev == &cache_head) { if (hunk_size - hunk_high_used - hunk_low_used < size) Sys_Error ("%s: out of hunk memory (failed to allocate %i bytes)", __thisfunc__, size); new_cs = (cache_system_t *) (hunk_base + hunk_low_used); memset (new_cs, 0, sizeof(*new_cs)); new_cs->size = size; cache_head.prev = cache_head.next = new_cs; new_cs->prev = new_cs->next = &cache_head; Cache_MakeLRU (new_cs); return new_cs; } /* search from the bottom up for space */ new_cs = (cache_system_t *) (hunk_base + hunk_low_used); cs = cache_head.next; do { if (!nobottom || cs != cache_head.next) { if ((byte *)cs - (byte *)new_cs >= size) { /* found space */ memset (new_cs, 0, sizeof(*new_cs)); new_cs->size = size; new_cs->next = cs; new_cs->prev = cs->prev; cs->prev->next = new_cs; cs->prev = new_cs; Cache_MakeLRU (new_cs); return new_cs; } } /* continue looking */ new_cs = (cache_system_t *)((byte *)cs + cs->size); cs = cs->next; } while (cs != &cache_head); /* try to allocate one at the very end */ if (hunk_base + hunk_size - hunk_high_used - (byte *)new_cs >= size) { memset (new_cs, 0, sizeof(*new_cs)); new_cs->size = size; new_cs->next = &cache_head; new_cs->prev = cache_head.prev; cache_head.prev->next = new_cs; cache_head.prev = new_cs; Cache_MakeLRU (new_cs); return new_cs; } return NULL; /* couldn't allocate */ } /* ============ Cache_Flush Throw everything out, so new data will be demand cached ============ */ void Cache_Flush (void) { while (cache_head.next != &cache_head) Cache_Free ( cache_head.next->user ); /* reclaim the space */ } /* ============ Cache_Report ============ */ void Cache_Report (void) { Con_DPrintf ("%4.1f megabyte data cache\n", (hunk_size - hunk_high_used - hunk_low_used) / (float)(1024*1024) ); } /* ============ Cache_Init ============ */ static void Cache_Init (void) { cache_head.next = cache_head.prev = &cache_head; cache_head.lru_next = cache_head.lru_prev = &cache_head; } /* ============== Cache_Free Frees the memory and removes it from the LRU list ============== */ void Cache_Free (cache_user_t *c) { cache_system_t *cs; if (!c->data) Sys_Error ("%s: not allocated", __thisfunc__); cs = ((cache_system_t *)c->data) - 1; cs->prev->next = cs->next; cs->next->prev = cs->prev; cs->next = cs->prev = NULL; c->data = NULL; Cache_UnlinkLRU (cs); } /* ============== Cache_Check ============== */ void *Cache_Check (cache_user_t *c) { cache_system_t *cs; if (!c->data) return NULL; cs = ((cache_system_t *)c->data) - 1; /* move to head of LRU */ Cache_UnlinkLRU (cs); Cache_MakeLRU (cs); return c->data; } /* ============== Cache_Alloc ============== */ void *Cache_Alloc (cache_user_t *c, int size, const char *name) { cache_system_t *cs; if (c->data) Sys_Error ("%s: %s is already allocated", __thisfunc__, name); if (size <= 0) Sys_Error ("%s: bad size %i for %s", __thisfunc__, size, name); size = (size + sizeof(cache_system_t) + 15) & ~15; /* find memory for it */ while (1) { cs = Cache_TryAlloc (size, false); if (cs) { q_strlcpy (cs->name, name, CACHENAME_LEN); c->data = (void *)(cs + 1); cs->user = c; break; } /* free the least recently used cahedat */ if (cache_head.lru_prev == &cache_head) /* not enough memory at all */ Sys_Error ("%s: out of memory", __thisfunc__); Cache_Free ( cache_head.lru_prev->user ); } return Cache_Check (c); } #endif /* ! SERVERONLY */ /* ============================================================================== CONSOLE COMMANDS ============================================================================== */ #if Z_DEBUG_COMMANDS #if defined(__GNUC__) && \ !(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) #define MEM_Printf(FH, fmt, args...) \ do { \ if ((FH)) fprintf((FH), fmt, ##args); \ else Con_Printf(fmt, ##args); \ } while (0) #else #define MEM_Printf(FH, ...) \ do { \ if ((FH)) fprintf((FH), __VA_ARGS__); \ else Con_Printf(__VA_ARGS__); \ } while (0) #endif /* ============== Hunk_Print If "all" is specified, every single allocation is printed. Otherwise, allocations with the same name will be totaled up before printing. ============== */ static void Hunk_Print (qboolean all, qboolean write_file) { hunk_t *h, *next, *endlow, *starthigh, *endhigh; int count, sum; int totalblocks; FILE *FH; count = 0; sum = 0; totalblocks = 0; FH = NULL; if (write_file) FH = fopen(FS_MakePath(FS_USERDIR,NULL,"memory.txt"), "w"); h = (hunk_t *)hunk_base; endlow = (hunk_t *)(hunk_base + hunk_low_used); starthigh = (hunk_t *)(hunk_base + hunk_size - hunk_high_used); endhigh = (hunk_t *)(hunk_base + hunk_size); MEM_Printf(FH," :%8i total hunk size\n", hunk_size); MEM_Printf(FH,"-------------------------\n"); while (1) { /* skip to the high hunk if done with low hunk */ if (h == endlow) { MEM_Printf(FH,"-------------------------\n"); MEM_Printf(FH," :%8i REMAINING\n", hunk_size - hunk_low_used - hunk_high_used); MEM_Printf(FH,"-------------------------\n"); h = starthigh; } /* if totally done, break */ if (h == endhigh) break; /* run consistancy checks */ if (h->sentinal != HUNK_SENTINAL) { Sys_Error ("%s: trashed sentinal", __thisfunc__); } if (h->size < (int)sizeof(hunk_t) || h->size + (byte *)h - hunk_base > hunk_size) { Sys_Error ("%s: bad size", __thisfunc__); } next = (hunk_t *)((byte *)h + h->size); count++; totalblocks++; sum += h->size; /* print the single block */ if (all) { MEM_Printf(FH,"%8p :%8i %8s\n",h, h->size, h->name); } /* print the total */ if (next == endlow || next == endhigh || strncmp (h->name, next->name, HUNKNAME_LEN - 1)) { if (!all) { MEM_Printf(FH," :%8i %8s (TOTAL)\n",sum, h->name); } count = 0; sum = 0; } h = next; } MEM_Printf(FH,"-------------------------\n"); MEM_Printf(FH,"%8i total blocks\n", totalblocks); if (FH) { fclose(FH); Con_Printf ("Wrote to memory.txt\n"); } } static void Memory_Display_f(void) { int num_args, counter; qboolean all, write_file; all = true; write_file = false; num_args = Cmd_Argc(); for (counter = 1; counter < num_args; counter++) { if (q_strcasecmp(Cmd_Argv(counter),"short") == 0) all = false; else if (q_strcasecmp(Cmd_Argv(counter),"save") == 0) write_file = true; } Hunk_Print(all, write_file); } # if !defined(SERVERONLY) static void Cache_Print (qboolean write_file) { cache_system_t *cd; FILE *FH; int count, sum; int num_mod, sum_mod; int num_wav, sum_wav; char temp[128]; FH = NULL; if (write_file) FH = fopen(FS_MakePath(FS_USERDIR,NULL,"cache.txt"), "w"); count = sum = 0; num_mod = sum_mod = 0; num_wav = sum_wav = 0; for (cd = cache_head.next ; cd != &cache_head ; cd = cd->next) { MEM_Printf(FH,"%8i : %s\n", cd->size, cd->name); count++; sum += cd->size; q_strlcpy (temp, cd->name, sizeof(temp)); q_strlwr(temp); if (strstr(temp,".mdl")) { num_mod++; sum_mod += cd->size; } else if (strstr(temp,".wav")) { num_wav++; sum_wav += cd->size; } } MEM_Printf(FH,"-------- ------------------\n"); MEM_Printf(FH,"%8i : Total of %i items\n",sum,count); MEM_Printf(FH,"%8i : Total .MDL of %i items\n",sum_mod,num_mod); MEM_Printf(FH,"%8i : Total .WAV of %i items\n",sum_wav,num_wav); if (FH) { fclose(FH); Con_Printf ("Wrote to cache.txt\n"); } } static void Cache_Display_f(void) { int num_args, counter; qboolean write_file; write_file = false; num_args = Cmd_Argc(); for (counter = 1; counter < num_args; counter++) { if (q_strcasecmp(Cmd_Argv(counter),"save") == 0) write_file = true; } Cache_Print(write_file); } # endif /* SERVERONLY */ /* ======================== Z_Print ======================== */ static void Z_Print (memzone_t *zone, FILE *f) { memblock_t *block; MEM_Printf (f, "zone size: %i location: %p\n", zone->size, zone); for (block = zone->blocklist.next ; ; block = block->next) { MEM_Printf (f, "block: %p size: %7i tag: %3i\n", block, block->size, block->tag); if (block->next == &zone->blocklist) break; /* all blocks have been hit */ if ( (byte *)block + block->size != (byte *)block->next) { MEM_Printf (f, "ERROR: block size does not touch the next block\n"); } if ( block->next->prev != block) { MEM_Printf (f, "ERROR: next block doesn't have proper back link\n"); } if (!block->tag && !block->next->tag) { MEM_Printf (f, "ERROR: two consecutive free blocks\n"); } } } static void Zone_Display_f(void) { FILE *FH; zonelist_t *z; if ((Cmd_Argc() == 2) && (q_strcasecmp(Cmd_Argv(1),"save") == 0)) { FH = fopen(FS_MakePath(FS_USERDIR,NULL,"zone.txt"), "w"); } else { FH = NULL; } z = zonelist; while (z != NULL) { MEM_Printf(FH,"-------------------------\n"); MEM_Printf(FH," %s :\n", z->name); MEM_Printf(FH,"-------------------------\n"); Z_Print (z->zone, FH); MEM_Printf(FH,"\n"); z = z->next; } if (FH) { fclose (FH); Con_Printf ("Wrote to zone.txt\n"); } } #define NUM_GROUPS 18 static const char *MemoryGroups[NUM_GROUPS+1] = { "texture", "light", "vis", "entities", "vertexes", "submodels", "edges", "faces", "nodes", "leafs", "clipnodes", "hull0", "marksurfaces", "surfedges", "planes", "video", "progs.dat", "edicts", "misc" }; static void Memory_Stats_f(void) { hunk_t *h, *next, *endlow, *starthigh, *endhigh; int num_args, count, sum, counter; int GroupCount[NUM_GROUPS+1], GroupSum[NUM_GROUPS+1]; FILE *FH; qboolean write_file; write_file = false; num_args = Cmd_Argc(); for (counter = 1; counter < num_args; counter++) { if (q_strcasecmp(Cmd_Argv(counter),"save") == 0) write_file = true; } memset(GroupCount,0,sizeof(GroupCount)); memset(GroupSum,0,sizeof(GroupSum)); h = (hunk_t *)hunk_base; endlow = (hunk_t *)(hunk_base + hunk_low_used); starthigh = (hunk_t *)(hunk_base + hunk_size - hunk_high_used); endhigh = (hunk_t *)(hunk_base + hunk_size); while (1) { if (h == endlow) { h = starthigh; } /* if totally done, break */ if (h == endhigh) break; next = (hunk_t *)((byte *)h + h->size); for (counter = 0; counter < NUM_GROUPS; counter++) { if (q_strcasecmp(h->name,MemoryGroups[counter]) == 0) { GroupCount[counter]++; GroupSum[counter] += h->size; break; } } if (counter >= NUM_GROUPS) { GroupCount[NUM_GROUPS]++; GroupSum[NUM_GROUPS] += h->size; } h = next; } count = 0; sum = 0; FH = NULL; if (write_file) FH = fopen(FS_MakePath(FS_USERDIR,NULL,"stats.txt"), "w"); MEM_Printf(FH,"Group Count Size\n"); MEM_Printf(FH,"--------------- ----- --------\n"); for (counter = 0; counter < NUM_GROUPS+1; counter++) { MEM_Printf(FH,"%-15s %-5i %i\n",MemoryGroups[counter],GroupCount[counter],GroupSum[counter]); count += GroupCount[counter]; sum += GroupSum[counter]; } MEM_Printf(FH,"--------------- ----- --------\n"); MEM_Printf(FH,"%-15s %-5i %i\n","Total",count,sum); if (FH) { fclose(FH); Con_Printf ("Wrote to stats.txt\n"); } } #endif /* Z_DEBUG_COMMANDS */ /*============================================================================*/ /* ======================== Memory_Init ======================== */ static void Memory_InitZone (const char *name, int id, int magic, int size) { zonelist_t *z; memblock_t *block; z = (zonelist_t *) Hunk_AllocName (sizeof(zonelist_t), name); z->id = id; z->magic = magic; z->name = name; z->zone = (memzone_t *) Hunk_AllocName (size, name); /* set the entire zone to one free block */ z->zone->blocklist.next = z->zone->blocklist.prev = block = (memblock_t *)( (byte *)z->zone + sizeof(memzone_t) ); z->zone->blocklist.tag = 1; /* in use block */ z->zone->blocklist.magic = 0; z->zone->blocklist.size = 0; z->zone->rover = block; block->prev = block->next = &z->zone->blocklist; block->tag = 0; /* free block */ block->magic = magic; block->size = size - sizeof(memzone_t); /* add to linked list */ z->next = zonelist; zonelist = z; } void Memory_Init (void *buf, int size) { int p; int zonesize = ZONE_DEFSIZE; hunk_base = (byte *) buf; hunk_size = size; hunk_low_used = 0; hunk_high_used = 0; #if !defined(SERVERONLY) Cache_Init (); #endif /* SERVERONLY */ p = COM_CheckParm ("-zone"); if (p && p < com_argc-1) { zonesize = atoi (com_argv[p+1]) * 1024; } Memory_InitZone (mainzone, Z_MAINZONE, ZMAGIC, zonesize); #if (SECZONE_SIZE > 0) zonesize = 0; if (!isDedicated) { zonesize += MEM_STATIC_TEX; zonesize += MEM_CODEC_MEM; } if (zonesize > 0) Memory_InitZone (sec_zone, Z_SECZONE, ZMAGIC2, zonesize); #endif /* SECONDARY ZONE */ #if !defined(SERVERONLY) Cmd_AddCommand ("flush", Cache_Flush); #endif /* SERVERONLY */ #if Z_DEBUG_COMMANDS Cmd_AddCommand ("sys_memory", Memory_Display_f); Cmd_AddCommand ("sys_zone", Zone_Display_f); Cmd_AddCommand ("sys_stats", Memory_Stats_f); #if !defined(SERVERONLY) Cmd_AddCommand ("sys_cache", Cache_Display_f); #endif /* SERVERONLY */ #endif /* Z_DEBUG_COMMANDS */ } engine/h2shared/zone.h000066400000000000000000000071271444734033100151610ustar00rootroot00000000000000/* zone.h * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ZZONE_H #define ZZONE_H /* Memory allocation H_??? The hunk manages the entire memory block given to quake. It must be contiguous. Memory can be allocated from either the low or high end in a stack fashion. The only way memory is released is by resetting one of the pointers. Hunk allocations should be given a name, so the Hunk_Print () function can display usage. Hunk allocations are guaranteed to be 16 byte aligned. The video buffers are allocated high to avoid leaving a hole underneath server allocations when changing to a higher video mode. Z_??? Zone memory functions used for small, dynamic allocations like text strings from command input. There is only about 48K for it, allocated at the very bottom of the hunk. Cache_??? Cache memory is for objects that can be dynamically loaded and can usefully stay persistant between levels. The size of the cache fluctuates from level to level. To allocate a cachable object Temp_??? Temp memory is used for file loading and surface caching. The size of the cache memory is adjusted so that there is a minimum of 512k remaining for temp memory. ------ Top of Memory ------- high hunk allocations <--- high hunk reset point held by vid video buffer z buffer surface cache <--- high hunk used cachable memory <--- low hunk used client and server low hunk allocations <-- low hunk reset point held by host startup hunk allocations Zone block ----- Bottom of Memory ----- */ void Memory_Init (void *buf, int size); /* valid values zone_idx arg: */ #define Z_MAINZONE (1 << 0) #define Z_SECZONE (1 << 1) #ifdef __cplusplus extern "C" { #endif void Z_Free (void *ptr); void *Z_Malloc (int size, int zone_id); /* returns 0 filled memory */ void *Z_Realloc (void *ptr, int size, int zone_id); char *Z_Strdup (const char *s); #ifdef __cplusplus } #endif void *Hunk_Alloc (int size); /* returns 0 filled memory */ void *Hunk_AllocName (int size, const char *name); void *Hunk_HighAllocName (int size, const char *name); char *Hunk_Strdup (const char *s, const char *name); void *Hunk_TempAlloc (int size); int Hunk_LowMark (void); void Hunk_FreeToLowMark (int mark); int Hunk_HighMark (void); void Hunk_FreeToHighMark (int mark); void Hunk_Check (void); #if !defined(SERVERONLY) typedef struct cache_user_s { void *data; } cache_user_t; void Cache_Flush (void); void *Cache_Check (cache_user_t *c); /* returns the cached data, and moves to the head of the LRU list * if present, otherwise returns NULL */ void Cache_Free (cache_user_t *c); void *Cache_Alloc (cache_user_t *c, int size, const char *name); /* Returns NULL if all purgable data was tossed and there still * wasn't enough room */ void Cache_Report (void); #endif /* SERVERONLY */ #endif /* ZZONE_H */ engine/hexen2/000077500000000000000000000000001444734033100135175ustar00rootroot00000000000000engine/hexen2/Makefile000066400000000000000000001171411444734033100151640ustar00rootroot00000000000000# GNU Makefile for hexen2 client/server binaries using GCC. # # Remember to "make clean" between different types of builds or targets. # Unix platforms require SDL !!! # # To cross-compile for Win32 on Unix: either pass the W32BUILD=1 # argument to make, or export it. Also see build_cross_win32.sh. # Requires: a mingw or mingw-w64 compiler toolchain. # # To cross-compile for Win64 on Unix: either pass the W64BUILD=1 # argument to make, or export it. Also see build_cross_win64.sh. # Requires: a mingw-w64 compiler toolchain. # # To cross-compile for MacOSX on Unix: either pass the OSXBUILD=1 # argument to make, or export it. You would also need to pass a # suitable MACH_TYPE=xxx (ppc, x86, x86_64, or ppc64) argument to # make. Also see build_cross_osx.sh. # # To (cross-)compile for DOS: either pass the DOSBUILD=1 argument # to make, or export it. Also see build_cross_dos.sh. Requires: a # djgpp compiler toolchain. # # To see valid targets: make help # # To use a compiler other than gcc: make CC=compiler_name [other stuff] # # To use a different nasm-compatible assembler, such as yasm: # make NASM=yasm [other stuff] # # To specify X installation at somewhere other than /usr/X11R6 # such as /usr/X11R7 : make X11BASE=/your/x11/path [other stuff] # # To build for the demo version: make DEMO=1 [other stuff] # # To build a debug version: make DEBUG=1 [other stuff] # # PATH SETTINGS: UHEXEN2_TOP:=../.. ENGINE_TOP:=.. COMMONDIR:=$(ENGINE_TOP)/h2shared UHEXEN2_SHARED:=$(UHEXEN2_TOP)/common LIBS_DIR:=$(UHEXEN2_TOP)/libs OSLIBS:=$(UHEXEN2_TOP)/oslibs # GENERAL OPTIONS (customize as required) # X directory X11BASE =/usr/X11R6 # the sdl-config command SDL_CONFIG =sdl-config SDL_CFLAGS = $(shell $(SDL_CONFIG) --cflags 2> /dev/null) SDL_LIBS = $(shell $(SDL_CONFIG) --libs 2> /dev/null) # use fast x86 assembly on ia32 machines? (auto-disabled for # any other cpu.) USE_X86_ASM=yes # use optimized m68k assembly for amiga # requires vasm from http://sun.hasenbraten.de/vasm/ USE_M68K_ASM=yes # link to the opengl libraries at compile time? (defaults # to no, so the binaries will dynamically load the necessary # libraries and functions at runtime.) LINK_GL_LIBS=no # enable evil 3dfx glide hacks for native hardware gamma for # the old Voodoo Graphics and Voodoo2 cards? (Linux/FreeBSD) USE_3DFXGAMMA=no # enable sound support? USE_SOUND=yes # ALSA audio support? (req: alsa-lib and alsa-kernel modules # >= 1.0.1. v0.9.8 and v1.0.0 might work, but not supported. # If not Linux, ALSA will be automatically be disabled.) USE_ALSA=yes # OSS audio support? (for Unix. enabled on Linux and FreeBSD. # automatically disabled on other platforms: see snd_sys.h) USE_OSS=yes # SUN audio support? (enabled on OpenBSD, NetBSD and SUN. # automatically disabled on others: see snd_sys.h) USE_SUNAUDIO=yes # SDL audio support? (enabled on all unix-like platforms.) USE_SDLAUDIO=yes # include target's MIDI driver if available? USE_MIDI=yes # CDAudio support? USE_CDAUDIO=yes # use SDL cdaudio? (otherwise platform specific cdrom code will # be used. The only problem with SDL_cdrom is that it lacks # proper volume controls. See cd_unix.h for the availability of # platform specific cdaudio drivers. # NOTE: SDL dropped cdaudio support in version 1.3.0 and later!) USE_SDLCD=no # link to the directx libraries at compile time? (otherwise, load # the necessary DLLs and functions dynamically at runtime, which # ensures our exe to function on ancient windows versions without # a directx installation.) LINK_DIRECTX=no # enable startup splash screens? (windows) WITH_SPLASHES=yes # use WinSock2 instead of WinSock-1.1? (disabled for w32 for compat. # with old Win95 machines.) (enabled for Win64 in the win64 section.) USE_WINSOCK2=no # use Serial driver for DOS networking? USE_SERIAL=yes # use WatTCP (WATT-32) for DOS UDP networking? USE_WATT32=yes # Enable/disable codecs for streaming music support: USE_CODEC_WAVE=yes USE_CODEC_FLAC=no USE_CODEC_MP3=yes USE_CODEC_VORBIS=yes USE_CODEC_OPUS=no # either xmp or mikmod (or modplug) USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no # either timidity (preferred) or wildmidi (both possible # but not needed nor meaningful) USE_CODEC_TIMIDITY=yes USE_CODEC_WILDMIDI=no # which library to use for mp3 decoding: mad or mpg123 MP3LIB=mad # which library to use for ogg decoding: vorbis or tremor VORBISLIB=vorbis # whether the codecs allocate on the zone instead of # system memory (set to yes for DOS builds, for example) CODECS_USE_ZONE=no # include the common dirty stuff include $(UHEXEN2_TOP)/scripts/makefile.inc # Names of the binaries ifeq ($(TARGET_OS),win32) SW_BINARY:=h2$(exe_ext) GL_BINARY:=glh2$(exe_ext) endif ifeq ($(TARGET_OS),win64) SW_BINARY:=h2$(exe_ext) GL_BINARY:=glh2$(exe_ext) endif ifeq ($(TARGET_OS),dos) SW_BINARY:=h2dos$(exe_ext) GL_BINARY:=glh2dos$(exe_ext) endif ifeq ($(TARGET_OS),os2) SW_BINARY:=h2$(exe_ext) GL_BINARY:=glh2$(exe_ext) endif ifeq ($(TARGET_OS),unix) SW_BINARY:=hexen2$(exe_ext) GL_BINARY:=glhexen2$(exe_ext) endif ifeq ($(TARGET_OS),darwin) SW_BINARY:=hexen2$(exe_ext) GL_BINARY:=glhexen2$(exe_ext) endif ifeq ($(TARGET_OS),aros) SW_BINARY:=hexen2$(exe_ext) GL_BINARY:=glhexen2$(exe_ext) endif ifeq ($(TARGET_OS),morphos) SW_BINARY:=hexen2$(exe_ext) GL_BINARY:=glhexen2$(exe_ext) endif ifeq ($(TARGET_OS),amigaos) SW_BINARY:=hexen2$(exe_ext) GL_BINARY:=glhexen2$(exe_ext) endif ############################################################# # Compiler flags ############################################################# ifeq ($(MACH_TYPE),x86) CPU_X86=-march=i586 endif # Overrides for the default CPUFLAGS CPUFLAGS=$(CPU_X86) CFLAGS += -Wall CFLAGS += $(CPUFLAGS) ifdef DEBUG CFLAGS += -g else # optimization flags CFLAGS += -O2 -DNDEBUG=1 -ffast-math # NOTE: -fomit-frame-pointer is broken with ancient gcc versions!! CFLAGS += -fomit-frame-pointer endif CPPFLAGS= LDFLAGS = # linkage may be sensitive to order: add SYSLIBS after all others. SYSLIBS = # compiler includes INCLUDES= -I. -I$(COMMONDIR) -I$(UHEXEN2_SHARED) # nasm includes: the trailing directory separator matters NASM_INC= -I./ -I$(COMMONDIR)/ # windows resource compiler includes RC_INC = -I. -I$(COMMONDIR) # end of compiler flags ############################################################# ############################################################# # Other build flags ############################################################# # disable x86 assembly if it is not an x86. ifneq ($(MACH_TYPE),x86) USE_X86_ASM=no endif # disable m68k assembly if it is not a m68k. ifneq ($(MACH_TYPE),m68k) USE_M68K_ASM=no endif ifdef DEMO CPPFLAGS+= -DDEMOBUILD endif ifdef DEBUG # This activates some extra code in hexen2/hexenworld C source CPPFLAGS+= -DDEBUG=1 -DDEBUG_BUILD=1 endif ############################################################# # OpenGL settings ############################################################# GL_DEFS = -DGLQUAKE GL_LIBS = ############################################################# # streaming music initial setup ############################################################# ifneq ($(USE_SOUND),yes) USE_CODEC_WAVE=no USE_CODEC_FLAC=no USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no USE_CODEC_MP3=no USE_CODEC_VORBIS=no USE_CODEC_OPUS=no endif # sanity checking for decoder library options ifneq ($(VORBISLIB),vorbis) ifneq ($(VORBISLIB),tremor) $(error Invalid VORBISLIB setting) endif endif ifneq ($(MP3LIB),mpg123) ifneq ($(MP3LIB),mad) $(error Invalid MP3LIB setting) endif endif ############################################################# # Mac OS X flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),darwin) NASMFLAGS=-f macho CPUFLAGS= # @rpath can be used when targeting 10.5+ USE_RPATH=no ifeq ($(MACH_TYPE),x86) # x86 requires 10.4. CFLAGS +=-mmacosx-version-min=10.4 LDFLAGS +=-mmacosx-version-min=10.4 endif ifeq ($(MACH_TYPE),ppc) # require 10.2 for ppc builds (midi_osx.c requirement.) CFLAGS +=-mmacosx-version-min=10.2 LDFLAGS +=-mmacosx-version-min=10.2 endif ifeq ($(MACH_TYPE),ppc64) # require 10.5 for ppc64 (actually SDL doesn't support ppc64.) CFLAGS +=-mmacosx-version-min=10.5 LDFLAGS +=-mmacosx-version-min=10.5 USE_RPATH=yes endif ifeq ($(MACH_TYPE),x86_64) # require 10.6 for amd64 builds (not 10.5: SDL's requirement.) # bundle1.o is needed for dyld_stub_binding_helper CFLAGS +=-mmacosx-version-min=10.6 LDFLAGS +=-mmacosx-version-min=10.6 -Wl,-lbundle1.o USE_RPATH=yes endif ifeq ($(USE_RPATH),yes) LDFLAGS +=-Wl,-rpath,@executable_path/../Frameworks endif ifeq ($(USE_SOUND),yes) # enable the extra codecs USE_CODEC_FLAC=yes USE_CODEC_OPUS=yes USE_CODEC_MIKMOD=yes USE_CODEC_UMX=yes endif # no need for timidity, we have a midi driver USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no ifeq ($(USE_MIDI),yes) # note: midi_mac.c requires -Wl,-framework,QuickTime which we are not using anymore. LDFLAGS += -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit -Wl,-framework,CoreServices endif ifeq ($(USE_SOUND),yes) ifneq ($(USE_SDLAUDIO),yes) CPPFLAGS+= -DNO_SDL_AUDIO endif endif USE_SDLCD=yes ifeq ($(USE_CDAUDIO),yes) ifeq ($(USE_SDLCD),yes) CPPFLAGS+= -DWITH_SDLCD endif endif # Unix builds rely on SDL: # SDLQUAKE must be defined for all SDL using platforms/targets CPPFLAGS+= -DSDLQUAKE # not relying on sdl-config command and assuming # /Library/Frameworks/SDL.framework is available SDL_CFLAGS =-D_GNU_SOURCE=1 -D_THREAD_SAFE SDL_CFLAGS+=-DSDL_FRAMEWORK -DNO_SDL_CONFIG SDL_LIBS = # default to our local SDL[2].framework for build SDL_FRAMEWORK_PATH ?=$(OSLIBS)/macosx ifneq ($(SDL_FRAMEWORK_PATH),) SDL_LIBS +=-F$(SDL_FRAMEWORK_PATH) SDL_CFLAGS+=-F$(SDL_FRAMEWORK_PATH) endif SDL_LIBS +=-Wl,-framework,SDL -Wl,-framework,Cocoa CFLAGS += $(SDL_CFLAGS) LDFLAGS += $(SDL_LIBS) GL_LINK=-Wl,-framework,OpenGL ifeq ($(USE_CODEC_FLAC),yes) CODEC_INC = -I$(OSLIBS)/macosx/codecs/include CODEC_LINK= -L$(OSLIBS)/macosx/codecs/lib endif ifeq ($(USE_CODEC_VORBIS),yes) CODEC_INC = -I$(OSLIBS)/macosx/codecs/include CODEC_LINK= -L$(OSLIBS)/macosx/codecs/lib endif ifeq ($(USE_CODEC_MP3),yes) CODEC_INC = -I$(OSLIBS)/macosx/codecs/include CODEC_LINK= -L$(OSLIBS)/macosx/codecs/lib endif ifeq ($(USE_CODEC_OPUS),yes) CODEC_INC = -I$(OSLIBS)/macosx/codecs/include CODEC_LINK= -L$(OSLIBS)/macosx/codecs/lib endif ifeq ($(USE_CODEC_MIKMOD),yes) CODEC_INC = -I$(OSLIBS)/macosx/codecs/include CODEC_LINK= -L$(OSLIBS)/macosx/codecs/lib endif ifeq ($(USE_CODEC_XMP),yes) CODEC_INC = -I$(OSLIBS)/macosx/codecs/include CODEC_LINK= -L$(OSLIBS)/macosx/codecs/lib endif ifeq ($(USE_CODEC_MODPLUG),yes) CODEC_INC = -I$(OSLIBS)/macosx/codecs/include CODEC_LINK= -L$(OSLIBS)/macosx/codecs/lib endif INCLUDES += $(CODEC_INC) LDFLAGS += $(CODEC_LINK) endif # End of Mac OS X settings ############################################################# ############################################################# # Unix flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),unix) # common unix: NASMFLAGS=-f elf -d_NO_PREFIX ifneq ($(X11BASE),) INCLUDES+= -I$(X11BASE)/include endif ifeq ($(HOST_OS),qnx) SYSLIBS += -lsocket endif ifeq ($(HOST_OS),haiku) SYSLIBS += -lnetwork endif ifeq ($(HOST_OS),sunos) SYSLIBS += -lsocket -lnsl -lresolv endif SYSLIBS += -lm ifneq ($(X11BASE),) GL_LINK=-L$(X11BASE)/lib -lGL else GL_LINK=-lGL endif ifeq ($(USE_SOUND),yes) ifneq ($(HOST_OS),linux) # alsa is only for linux USE_ALSA=no endif ifneq ($(USE_ALSA),yes) CPPFLAGS+= -DNO_ALSA_AUDIO else # snd_alsa uses dlopen() & co. SYSLIBS += -ldl endif ifneq ($(USE_OSS),yes) CPPFLAGS+= -DNO_OSS_AUDIO endif ifneq ($(USE_SUNAUDIO),yes) CPPFLAGS+= -DNO_SUN_AUDIO endif ifneq ($(USE_SDLAUDIO),yes) CPPFLAGS+= -DNO_SDL_AUDIO endif endif ifeq ($(USE_CDAUDIO),yes) ifeq ($(USE_SDLCD),yes) CPPFLAGS+= -DWITH_SDLCD endif endif # Unix builds rely on SDL: # SDLQUAKE must be defined for all SDL using platforms/targets CPPFLAGS+= -DSDLQUAKE CFLAGS += $(SDL_CFLAGS) LDFLAGS += $(SDL_LIBS) ifeq ($(USE_CODEC_OPUS),yes) # opus and opusfile put their *.h under /opus, # but they include the headers without the opus directory # prefix and rely on pkg-config. ewww... INCLUDES+= $(shell pkg-config --cflags opusfile) LDFLAGS += $(shell pkg-config --libs opusfile) endif endif # End of Unix settings ############################################################# ############################################################# # OS/2 flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),os2) # OS/2, using EMX: INCLUDES+= -I$(OSLIBS)/os2/emx/include CFLAGS += -Zmt LDFLAGS += -Zmt ifndef DEBUG LDFLAGS += -s # -fomit-frame-pointer/-ffast-math seems to cause trouble # with EMX at least with the old compilers I tried. CFLAGS += -fno-omit-frame-pointer endif # using SDL for now: SDL_CFLAGS=-I$(OSLIBS)/os2/SDL/include SDL_LIBS = -L$(OSLIBS)/os2/SDL/lib -lSDL12 USE_SDLCD=yes USE_SDLAUDIO=yes NASMFLAGS=-f aout SYSLIBS += -lsocket LINK_GL_LIBS=yes GL_LINK=-lopengl ifeq ($(USE_SOUND),yes) ifneq ($(USE_SDLAUDIO),yes) CPPFLAGS+= -DNO_SDL_AUDIO endif endif ifeq ($(USE_CDAUDIO),yes) ifeq ($(USE_SDLCD),yes) CPPFLAGS+= -DWITH_SDLCD endif endif # SDLQUAKE must be defined for all SDL using platforms/targets CPPFLAGS+= -DSDLQUAKE CFLAGS += $(SDL_CFLAGS) LDFLAGS += $(SDL_LIBS) # use Tremor as Vorbis decoder for OS/2? VORBISLIB=tremor ifeq ($(USE_CODEC_VORBIS),yes) CODEC_INC = -I$(OSLIBS)/os2/codecs/include CODEC_LINK= -L$(OSLIBS)/os2/codecs/lib endif ifeq ($(USE_CODEC_MP3),yes) CODEC_INC = -I$(OSLIBS)/os2/codecs/include CODEC_LINK= -L$(OSLIBS)/os2/codecs/lib endif ifeq ($(USE_CODEC_FLAC),yes) CODEC_INC = -I$(OSLIBS)/os2/codecs/include CODEC_LINK= -L$(OSLIBS)/os2/codecs/lib endif ifeq ($(USE_CODEC_OPUS),yes) CODEC_INC = -I$(OSLIBS)/os2/codecs/include CODEC_LINK= -L$(OSLIBS)/os2/codecs/lib endif ifeq ($(USE_CODEC_MIKMOD),yes) CODEC_INC = -I$(OSLIBS)/os2/codecs/include CODEC_LINK= -L$(OSLIBS)/os2/codecs/lib endif ifeq ($(USE_CODEC_XMP),yes) CODEC_INC = -I$(OSLIBS)/os2/codecs/include CODEC_LINK= -L$(OSLIBS)/os2/codecs/lib endif ifeq ($(USE_CODEC_MODPLUG),yes) CODEC_INC = -I$(OSLIBS)/os2/codecs/include CODEC_LINK= -L$(OSLIBS)/os2/codecs/lib endif INCLUDES += $(CODEC_INC) LDFLAGS += $(CODEC_LINK) endif # End of OS/2 settings ############################################################# ############################################################# # DOS flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),dos) NASMFLAGS=-f coff # Use x86 assembly for DOS USE_X86_ASM=yes USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no # no need for Opus on DOS USE_CODEC_OPUS=no # no need for Flac on DOS USE_CODEC_FLAC=no # use Tremor as Vorbis decoder for DOS? VORBISLIB=tremor # make DOS decoders allocate on the zone CODECS_USE_ZONE=yes INCLUDES += -I$(OSLIBS)/dos ifeq ($(USE_SERIAL),yes) CPPFLAGS+= -DUSE_SERIAL endif ifeq ($(USE_WATT32),yes) CPPFLAGS+= -DUSE_WATT32 -DWATT32_NO_OLDIES INCLUDES+= -I$(OSLIBS)/dos/watt32/inc LDFLAGS += -L$(OSLIBS)/dos/watt32/lib -lwatt endif # DOS GL experiments: INCLUDES += -I$(OSLIBS)/dos/opengl/include INCLUDES += -I$(OSLIBS)/dos/glide3/include GL_LINK = -L$(OSLIBS)/dos/opengl/lib/$(GLDIR) -lgl # enable dynamic loading of gl functions LINK_GL_LIBS=no # enable 3dfx gamma hacks USE_3DFXGAMMA=yes ifeq ($(LINK_GL_LIBS),yes) # compile which gl driver in: mesa, sage, or fxmesa. (only one.) DOSGL_DRIVER=fxmesa ifeq ($(DOSGL_DRIVER),fxmesa) GL_DEFS+= -DREFGL_FXMESA endif ifeq ($(DOSGL_DRIVER),mesa) GL_DEFS+= -DREFGL_MESA endif ifeq ($(DOSGL_DRIVER),sage) GL_DEFS+= -DREFGL_SAGE endif GLDIR=$(DOSGL_DRIVER) else SYSOBJ_GL_DXE=yes # choose which dosgl api(s) to support. (at least one.) GL_DEFS+= -DREFGL_FXMESA GL_DEFS+= -DREFGL_MESA GL_DEFS+= -DREFGL_SAGE endif SYSLIBS += $(OSLIBS)/dos/djtime/djtime.a -lc -lgcc #SYSLIBS += -lm # PCI sound card support through libau USE_PCIAUDIO=yes # PCI sound card support as a DXE module PCIAUDIO_DXE=yes ifeq ($(USE_SOUND),yes) ifneq ($(USE_PCIAUDIO),yes) CPPFLAGS+= -DNO_PCI_AUDIO else INCLUDES+= -I$(OSLIBS)/dos/libau/include ifeq ($(PCIAUDIO_DXE),yes) SYSOBJ_SOFT_DXE=yes SYSOBJ_GL_DXE=yes CPPFLAGS+= -DSNDPCI_DXE else LDFLAGS += -L$(OSLIBS)/dos/libau/lib -lau endif endif endif ifeq ($(USE_CODEC_VORBIS),yes) CODEC_INC = -I$(OSLIBS)/dos/codecs/include CODEC_LINK= -L$(OSLIBS)/dos/codecs/lib endif ifeq ($(USE_CODEC_MP3),yes) CODEC_INC = -I$(OSLIBS)/dos/codecs/include CODEC_LINK= -L$(OSLIBS)/dos/codecs/lib endif ifeq ($(USE_CODEC_FLAC),yes) CODEC_INC = -I$(OSLIBS)/dos/codecs/include CODEC_LINK= -L$(OSLIBS)/dos/codecs/lib endif ifeq ($(USE_CODEC_OPUS),yes) CODEC_INC = -I$(OSLIBS)/dos/codecs/include CODEC_LINK= -L$(OSLIBS)/dos/codecs/lib endif ifeq ($(USE_CODEC_MIKMOD),yes) CODEC_INC = -I$(OSLIBS)/dos/codecs/include CODEC_LINK= -L$(OSLIBS)/dos/codecs/lib endif ifeq ($(USE_CODEC_XMP),yes) CODEC_INC = -I$(OSLIBS)/dos/codecs/include CODEC_LINK= -L$(OSLIBS)/dos/codecs/lib endif ifeq ($(USE_CODEC_MODPLUG),yes) CODEC_INC = -I$(OSLIBS)/dos/codecs/include CODEC_LINK= -L$(OSLIBS)/dos/codecs/lib endif INCLUDES += $(CODEC_INC) LDFLAGS += $(CODEC_LINK) endif # End of DOS settings ############################################################# ############################################################# # Win32 flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),win32) RC_DEFS=$(CPPFLAGS) RCFLAGS=--output-format=coff --target=pe-i386 NASMFLAGS=-f win32 GL_LINK=-lopengl32 ifeq ($(USE_SOUND),yes) # enable the extra codecs USE_CODEC_FLAC=yes USE_CODEC_OPUS=yes USE_CODEC_MIKMOD=yes USE_CODEC_UMX=yes endif # timidity not needed, we have a midi driver. if you want it, # comment out the following USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no ifeq ($(USE_WINSOCK2),yes) CPPFLAGS+=-D_USE_WINSOCK2 LIBWINSOCK=ws2_32 else LIBWINSOCK=wsock32 endif CPPFLAGS+= -DWIN32_LEAN_AND_MEAN INCLUDES+= -I$(OSLIBS)/windows/misc/include -I$(OSLIBS)/windows/dxsdk/include CFLAGS += -m32 LDFLAGS += -m32 -mwindows LDFLAGS += -l$(LIBWINSOCK) -lwinmm ifneq ($(LINK_DIRECTX),yes) CPPFLAGS+= -DDX_DLSYM else LDFLAGS += -L$(OSLIBS)/windows/dxsdk/x86 -ldsound -ldinput -ldxguid endif ifneq ($(WITH_SPLASHES),yes) CPPFLAGS+= -DNO_SPLASHES endif ifeq ($(USE_CODEC_FLAC),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x86 endif ifeq ($(USE_CODEC_VORBIS),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x86 endif ifeq ($(USE_CODEC_MP3),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x86 endif ifeq ($(USE_CODEC_OPUS),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x86 endif ifeq ($(USE_CODEC_MIKMOD),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x86 endif ifeq ($(USE_CODEC_XMP),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x86 endif ifeq ($(USE_CODEC_MODPLUG),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x86 endif INCLUDES += $(CODEC_INC) LDFLAGS += $(CODEC_LINK) endif # End of Win32 settings ############################################################# ############################################################# # Win64 flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),win64) NASMFLAGS=-f win64 -d_NO_PREFIX RC_DEFS=$(CPPFLAGS) RCFLAGS=--output-format=coff --target=pe-x86-64 GL_LINK=-lopengl32 ifeq ($(USE_SOUND),yes) # enable the extra codecs USE_CODEC_FLAC=yes USE_CODEC_OPUS=yes USE_CODEC_MIKMOD=yes USE_CODEC_UMX=yes endif # timidity not needed, we have a midi driver. if you want it, # comment out the following USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no # use winsock2 for win64 USE_WINSOCK2=yes ifeq ($(USE_WINSOCK2),yes) CPPFLAGS+=-D_USE_WINSOCK2 LIBWINSOCK=ws2_32 else LIBWINSOCK=wsock32 endif CPPFLAGS+= -DWIN32_LEAN_AND_MEAN INCLUDES+= -I$(OSLIBS)/windows/misc/include -I$(OSLIBS)/windows/dxsdk/include CFLAGS += -m64 LDFLAGS += -m64 -mwindows LDFLAGS += -l$(LIBWINSOCK) -lwinmm ifneq ($(LINK_DIRECTX),yes) CPPFLAGS+= -DDX_DLSYM else LDFLAGS += -L$(OSLIBS)/windows/dxsdk/x64 -ldsound -ldinput -ldxguid endif ifneq ($(WITH_SPLASHES),yes) CPPFLAGS+= -DNO_SPLASHES endif ifeq ($(USE_CODEC_FLAC),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x64 endif ifeq ($(USE_CODEC_VORBIS),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x64 endif ifeq ($(USE_CODEC_MP3),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x64 endif ifeq ($(USE_CODEC_OPUS),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x64 endif ifeq ($(USE_CODEC_MIKMOD),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x64 endif ifeq ($(USE_CODEC_XMP),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x64 endif ifeq ($(USE_CODEC_MODPLUG),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x64 endif INCLUDES += $(CODEC_INC) LDFLAGS += $(CODEC_LINK) endif # End of Win64 settings ############################################################# ############################################################# # MorphOS flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),morphos) # MorphOS builds can use SDL or native code USE_SDL=no ifneq ($(USE_SDL),yes) USE_SDLCD=no USE_SDLAUDIO=no # no native cdaudio code yet USE_CDAUDIO=no endif # don't build the CAMD MIDI driver yet USE_MIDI=no CFLAGS += -noixemul LDFLAGS += -noixemul SYSLIBS += -lm INCLUDES += -I$(OSLIBS)/morphos/misc/include GL_LINK=-lGL # dynamic loading of ogl functions doesn't work LINK_GL_LIBS=yes USE_CODEC_FLAC=no USE_CODEC_VORBIS=yes USE_CODEC_MP3=yes USE_CODEC_OPUS=no USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no ifeq ($(USE_CODEC_FLAC),yes) CODEC_INC = -I$(OSLIBS)/morphos/codecs/include CODEC_LINK= -L$(OSLIBS)/morphos/codecs/lib endif ifeq ($(USE_CODEC_VORBIS),yes) CODEC_INC = -I$(OSLIBS)/morphos/codecs/include CODEC_LINK= -L$(OSLIBS)/morphos/codecs/lib endif ifeq ($(USE_CODEC_MP3),yes) CODEC_INC = -I$(OSLIBS)/morphos/codecs/include CODEC_LINK= -L$(OSLIBS)/morphos/codecs/lib endif ifeq ($(USE_CODEC_OPUS),yes) CODEC_INC = -I$(OSLIBS)/morphos/codecs/include CODEC_LINK= -L$(OSLIBS)/morphos/codecs/lib endif INCLUDES += $(CODEC_INC) LDFLAGS += $(CODEC_LINK) ifeq ($(USE_SOUND),yes) ifneq ($(USE_SDLAUDIO),yes) CPPFLAGS+= -DNO_SDL_AUDIO endif endif ifeq ($(USE_CDAUDIO),yes) ifeq ($(USE_SDLCD),yes) CPPFLAGS+= -DWITH_SDLCD endif endif ifeq ($(USE_SDL),yes) # SDLQUAKE must be defined for all SDL using platforms/targets CPPFLAGS+= -DSDLQUAKE CFLAGS += $(SDL_CFLAGS) LDFLAGS += $(SDL_LIBS) endif endif # End of MorphOS settings ############################################################# ############################################################# # AROS flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),aros) CFLAGS += -fno-common # AROS builds can use SDL or native code USE_SDL=no ifneq ($(USE_SDL),yes) USE_SDLCD=no USE_SDLAUDIO=no # no native cdaudio code yet USE_CDAUDIO=no # native ogl code need this: LINK_GL_LIBS=yes endif # don't build the CAMD MIDI driver yet USE_MIDI=no NASMFLAGS=-f elf -d_NO_PREFIX #USE_X86_ASM=no INCLUDES+= -I$(OSLIBS)/aros-$(MACH_TYPE)/misc/include # use the old AROSMesa api (default), or the new glA api USE_GLA=no ifeq ($(USE_GLA),yes) GL_DEFS += -DAROS_USE_GLA endif GL_LINK=-lGL USE_CODEC_FLAC=no USE_CODEC_OPUS=no USE_CODEC_MIKMOD=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no # both WildMidi and the local timidity work nice on AROS USE_CODEC_WILDMIDI=no USE_CODEC_TIMIDITY=yes ifeq ($(USE_CODEC_FLAC),yes) CODEC_INC = -I$(OSLIBS)/aros-$(MACH_TYPE)/codecs/include CODEC_LINK= -L$(OSLIBS)/aros-$(MACH_TYPE)/codecs/lib endif ifeq ($(USE_CODEC_MP3),yes) CODEC_INC = -I$(OSLIBS)/aros-$(MACH_TYPE)/codecs/include CODEC_LINK= -L$(OSLIBS)/aros-$(MACH_TYPE)/codecs/lib endif ifeq ($(USE_CODEC_VORBIS),yes) CODEC_INC = -I$(OSLIBS)/aros-$(MACH_TYPE)/codecs/include CODEC_LINK= -L$(OSLIBS)/aros-$(MACH_TYPE)/codecs/lib endif ifeq ($(USE_CODEC_OPUS),yes) CODEC_INC = -I$(OSLIBS)/aros-$(MACH_TYPE)/codecs/include CODEC_LINK= -L$(OSLIBS)/aros-$(MACH_TYPE)/codecs/lib endif INCLUDES += $(CODEC_INC) LDFLAGS += $(CODEC_LINK) ifeq ($(USE_SOUND),yes) ifneq ($(USE_SDLAUDIO),yes) CPPFLAGS+= -DNO_SDL_AUDIO endif endif ifeq ($(USE_CDAUDIO),yes) ifeq ($(USE_SDLCD),yes) CPPFLAGS+= -DWITH_SDLCD endif endif ifeq ($(USE_SDL),yes) # SDLQUAKE must be defined for all SDL using platforms/targets CPPFLAGS+= -DSDLQUAKE CFLAGS += $(SDL_CFLAGS) LDFLAGS += $(SDL_LIBS) endif endif # End of AROS settings ############################################################# ############################################################# # AmigaOS flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),amigaos) # use Bebbo's GCC6 toolchain BEBBO_TOOLCHAIN=yes # crt: libnix or clib2: USE_CLIB2=yes ifeq ($(BEBBO_TOOLCHAIN),yes) USE_CLIB2=no endif # Don't use SDL on AmigaOS USE_SDL=no ifneq ($(USE_SDL),yes) USE_SDLCD=no USE_SDLAUDIO=no endif ifndef DEBUG ifneq ($(BEBBO_TOOLCHAIN),yes) # -fomit-frame-pointer / -ffast-math causes trouble with gcc2.95 CFLAGS += -fno-omit-frame-pointer else # these break the game with bebbo's gcc-6.x: CFLAGS += -fbbb=- -fno-tree-forwprop -fno-tree-ter -fno-tree-fre -fno-tree-sra -fno-move-loop-invariants -fno-inline-functions-called-once endif endif ifeq ($(USE_CLIB2),yes) CRT_FLAGS=-mcrt=clib2 else CRT_FLAGS=-noixemul endif CFLAGS += $(CRT_FLAGS) -m68060 -m68881 LDFLAGS += $(CRT_FLAGS) -m68060 -m68881 SYSLIBS += -lm # for M68K assembly: AS=vasm ASFLAGS = -Faout -m68060 -phxass -quiet ifeq ($(USE_M68K_ASM),yes) CPPFLAGS+= -DUSE_M68K_ASM endif # for extra missing headers INCLUDES += -I$(OSLIBS)/amigaos/include ifneq ($(BEBBO_TOOLCHAIN),yes) # Roadshow SDK NET_INC = -I$(OSLIBS)/amigaos/netinclude endif # Build for which Amiga GL implementation: # Either amesa (StormMesa), or minigl (Hyperion's MiniGL 1.2) AMIGA_GLIMP=minigl ifeq ($(AMIGA_GLIMP),minigl) GL_DEFS+= -DREFGL_MINIGL #GL_DEFS+= -DUSE_MGLAPI=1 GL_DEFS+= -I$(OSLIBS)/amigaos/MiniGL/include GL_LINK = -L$(OSLIBS)/amigaos/MiniGL/lib -lmgl endif ifeq ($(AMIGA_GLIMP),amesa) GL_DEFS+= -DREFGL_AMESA GL_DEFS+= -I$(OSLIBS)/amigaos/StormMesa/include GL_LINK = -L$(OSLIBS)/amigaos/StormMesa/lib -lgl endif # dynamic loading of ogl functions doesn't work LINK_GL_LIBS=yes USE_CODEC_TIMIDITY=no USE_CODEC_FLAC=no USE_CODEC_MP3=no USE_CODEC_VORBIS=no USE_CODEC_OPUS=no USE_CODEC_MIKMOD=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no #use integer-only Tremor as Vorbis decoder for m68k-amigaos VORBISLIB=tremor ifeq ($(USE_CODEC_MP3),yes) CODEC_INC = -I$(OSLIBS)/amigaos/codecs/include CODEC_LINK= -L$(OSLIBS)/amigaos/codecs/lib endif ifeq ($(USE_CODEC_VORBIS),yes) CODEC_INC = -I$(OSLIBS)/amigaos/codecs/include CODEC_LINK= -L$(OSLIBS)/amigaos/codecs/lib endif INCLUDES += $(CODEC_INC) LDFLAGS += $(CODEC_LINK) ifeq ($(USE_SOUND),yes) ifneq ($(USE_SDLAUDIO),yes) CPPFLAGS+= -DNO_SDL_AUDIO endif endif ifeq ($(USE_CDAUDIO),yes) ifeq ($(USE_SDLCD),yes) CPPFLAGS+= -DWITH_SDLCD endif endif ifeq ($(USE_SDL),yes) # SDLQUAKE must be defined for all SDL using platforms/targets CPPFLAGS+= -DSDLQUAKE CFLAGS += $(SDL_CFLAGS) LDFLAGS += $(SDL_LIBS) endif endif # End of AmigaOS settings ############################################################# ############################################################# # Streaming music settings ############################################################# ifeq ($(MP3LIB),mad) mp3_obj=snd_mp3 lib_mp3dec=-lmad endif ifeq ($(MP3LIB),mpg123) mp3_obj=snd_mpg123 lib_mp3dec=-lmpg123 endif ifeq ($(VORBISLIB),vorbis) cpp_vorbisdec= lib_vorbisdec=-lvorbisfile -lvorbis -logg endif ifeq ($(VORBISLIB),tremor) cpp_vorbisdec=-DVORBIS_USE_TREMOR lib_vorbisdec=-lvorbisidec -logg endif ifeq ($(USE_CODEC_FLAC),yes) CPPFLAGS+= -DUSE_CODEC_FLAC LDFLAGS+= -lFLAC endif ifeq ($(USE_CODEC_WAVE),yes) CPPFLAGS+= -DUSE_CODEC_WAVE endif ifeq ($(USE_CODEC_OPUS),yes) CPPFLAGS+= -DUSE_CODEC_OPUS ifneq ($(TARGET_OS),unix) # we use pkg-config on unix LDFLAGS+= -lopusfile -lopus -logg endif endif ifeq ($(USE_CODEC_VORBIS),yes) CPPFLAGS+= -DUSE_CODEC_VORBIS $(cpp_vorbisdec) LDFLAGS+= $(lib_vorbisdec) endif ifeq ($(USE_CODEC_MP3),yes) CPPFLAGS+= -DUSE_CODEC_MP3 LDFLAGS+= $(lib_mp3dec) endif ifeq ($(USE_CODEC_MIKMOD),yes) CPPFLAGS+= -DUSE_CODEC_MIKMOD LDFLAGS+= -lmikmod endif ifeq ($(USE_CODEC_XMP),yes) CPPFLAGS+= -DUSE_CODEC_XMP LDFLAGS+= -lxmp endif ifeq ($(USE_CODEC_MODPLUG),yes) CPPFLAGS+= -DUSE_CODEC_MODPLUG LDFLAGS+= -lmodplug endif ifeq ($(USE_CODEC_UMX),yes) CPPFLAGS+= -DUSE_CODEC_UMX endif ifeq ($(USE_CODEC_TIMIDITY),yes) CPPFLAGS+= -DUSE_CODEC_TIMIDITY LDFLAGS+= -L$(LIBS_DIR)/timidity -ltimidity ifeq ($(TARGET_OS),os2) LIBTIMIDITY=timidity.a else LIBTIMIDITY=libtimidity.a endif TIMIDEPS=$(LIBS_DIR)/timidity/$(LIBTIMIDITY) else TIMIDEPS= endif ifeq ($(USE_CODEC_WILDMIDI),yes) CPPFLAGS+= -DUSE_CODEC_WILDMIDI LDFLAGS+= -lWildMidi endif ifeq ($(CODECS_USE_ZONE),yes) CPPFLAGS+=-DCODECS_USE_ZONE endif # End of streaming music settings ############################################################# ############################################################# # Finalize things after the system specific overrides: ############################################################# ifeq ($(USE_X86_ASM),yes) CPPFLAGS+= -DUSE_INTEL_ASM endif ifeq ($(USE_3DFXGAMMA),yes) # linux 3dfx gamma evil hack GL_DEFS+= -DUSE_3DFXGAMMA endif ifneq ($(LINK_GL_LIBS),yes) GL_DEFS+= -DGL_DLSYM else GL_LIBS+= $(GL_LINK) endif # ############################################################# # Rules for turning source files into .o files %.o: %.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(COMMONDIR)/%.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(UHEXEN2_SHARED)/%.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: %.m $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(COMMONDIR)/%.m $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(UHEXEN2_SHARED)/%.m $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: %.asm $(NASM) $(NASM_INC) $(NASMFLAGS) -o $@ $< %.o: $(COMMONDIR)/%.asm $(NASM) $(NASM_INC) $(NASMFLAGS) -o $@ $< %.res: %.rc $(WINDRES) $(RC_DEFS) $(RC_INC) $(RCFLAGS) -o $@ $< %.res: $(COMMONDIR)/%.rc $(WINDRES) $(RC_DEFS) $(RC_INC) $(RCFLAGS) -o $@ $< ifeq ($(TARGET_OS),amigaos) #%.o: %.s # $(AS) $(ASFLAGS) -o $@ $< %.o: $(COMMONDIR)/%.s $(AS) $(ASFLAGS) -o $@ $< %.o: $(UHEXEN2_SHARED)/%.s $(AS) $(ASFLAGS) -o $@ $< endif # Objects # Intel asm objects ifeq ($(USE_X86_ASM),yes) COMMON_ASM= math.o \ sys_ia32.o SOFT_ASM = \ d_draw.o \ d_draw16.o \ d_draw16t.o \ d_parta.o \ d_partb.o \ d_polysa.o \ d_polysa2.o \ d_polysa3.o \ d_polysa4.o \ d_polysa5.o \ d_scana.o \ d_spr8.o \ d_spr8t.o \ d_spr8t2.o \ d_varsa.o \ r_aclipa.o \ r_aliasa.o \ r_drawa.o \ r_edgea.o \ r_edgeb.o \ r_varsa.o \ surf8.o \ surf16.o SOUND_ASM = snd_mixa.o WORLD_ASM = worlda.o else # M68K asm objects ifeq ($(USE_M68K_ASM),yes) SOFT_ASM = \ amiga_r_68k.o \ d_part68k.o \ d_polyse68k.o \ d_scan68k.o \ d_sky68k.o \ d_sprite68k.o \ r_aclip68k.o \ r_alias68k.o \ r_bsp68k.o \ r_edge68k.o \ r_sky68k.o \ r_surf68k.o COMMON_ASM = swap_68k.o SOUND_ASM = snd_mixamiga68k.o WORLD_ASM = else SOFT_ASM = COMMON_ASM = SOUND_ASM = WORLD_ASM = endif endif # Sound objects ifneq ($(USE_SOUND),yes) MUSIC_OBJS:= bgmnull.o SOUND_ASM := CPPFLAGS += -D_NO_SOUND SYSOBJ_SND := COMOBJ_SND := snd_null.o $(MUSIC_OBJS) else MUSIC_OBJS:= bgmusic.o \ snd_codec.o \ snd_flac.o \ snd_wave.o \ snd_vorbis.o \ snd_opus.o \ $(mp3_obj).o \ snd_mp3tag.o \ snd_mikmod.o \ snd_modplug.o \ snd_xmp.o \ snd_umx.o \ snd_timidity.o \ snd_wildmidi.o COMOBJ_SND := snd_sys.o snd_dma.o snd_mix.o $(SOUND_ASM) snd_mem.o $(MUSIC_OBJS) ifeq ($(TARGET_OS),win32) SYSOBJ_SND := snd_win.o snd_dsound.o endif ifeq ($(TARGET_OS),win64) SYSOBJ_SND := snd_win.o snd_dsound.o endif ifeq ($(TARGET_OS),dos) SYSOBJ_SND := snd_sb.o snd_gus.o snd_pci.o endif ifeq ($(TARGET_OS),os2) SYSOBJ_SND := snd_sdl.o endif ifeq ($(TARGET_OS),unix) SYSOBJ_SND := snd_oss.o snd_alsa.o snd_sun.o snd_sdl.o endif ifeq ($(TARGET_OS),darwin) SYSOBJ_SND := snd_sdl.o endif ifeq ($(TARGET_OS),aros) SYSOBJ_SND := snd_ahi.o snd_sdl.o endif ifeq ($(TARGET_OS),morphos) SYSOBJ_SND := snd_ahi.o snd_sdl.o endif ifeq ($(TARGET_OS),amigaos) SYSOBJ_SND := snd_ahi.o snd_paula.o snd_sdl.o endif # end of Sound objects endif # MIDI objects ifneq ($(USE_MIDI),yes) SYSOBJ_MIDI:= midi_nul.o CPPFLAGS += -D_NO_MIDIDRV else ifeq ($(TARGET_OS),win32) SYSOBJ_MIDI:= midi_win.o mid2strm.o endif ifeq ($(TARGET_OS),win64) SYSOBJ_MIDI:= midi_win.o mid2strm.o endif ifeq ($(TARGET_OS),dos) SYSOBJ_MIDI:= midi_nul.o CPPFLAGS += -D_NO_MIDIDRV endif ifeq ($(TARGET_OS),os2) SYSOBJ_MIDI:= midi_nul.o CPPFLAGS += -D_NO_MIDIDRV endif ifeq ($(TARGET_OS),unix) SYSOBJ_MIDI:= midi_nul.o CPPFLAGS += -D_NO_MIDIDRV endif ifeq ($(TARGET_OS),darwin) # not using midi_mac.c SYSOBJ_MIDI:= midi_osx.o endif ifeq ($(TARGET_OS),aros) SYSOBJ_MIDI:= midi_camd.o endif ifeq ($(TARGET_OS),morphos) SYSOBJ_MIDI:= midi_camd.o endif ifeq ($(TARGET_OS),amigaos) SYSOBJ_MIDI:= midi_camd.o endif # end of MIDI objects endif # CDAudio objects ifneq ($(USE_CDAUDIO),yes) SYSOBJ_CDA:= cd_null.o CPPFLAGS += -D_NO_CDAUDIO else ifeq ($(TARGET_OS),win32) SYSOBJ_CDA := cd_win.o endif ifeq ($(TARGET_OS),win64) SYSOBJ_CDA := cd_win.o endif ifeq ($(TARGET_OS),dos) SYSOBJ_CDA := cd_dos.o endif ifeq ($(TARGET_OS),os2) SYSOBJ_CDA := cd_sdl.o endif ifeq ($(TARGET_OS),unix) SYSOBJ_CDA := cd_sdl.o cd_bsd.o cd_linux.o endif ifeq ($(TARGET_OS),darwin) SYSOBJ_CDA := cd_sdl.o endif ifeq ($(TARGET_OS),aros) SYSOBJ_CDA := cd_sdl.o endif ifeq ($(TARGET_OS),morphos) SYSOBJ_CDA := cd_sdl.o endif ifeq ($(TARGET_OS),amigaos) SYSOBJ_CDA := cd_amiga.o endif # end of CDAudio objects endif # Other platform specific object settings ifeq ($(TARGET_OS),win32) SYSOBJ_INPUT := in_win.o SYSOBJ_GL_VID:= gl_vidnt.o SYSOBJ_SOFT_VID:= vid_win.o SYSOBJ_NET := net_win.o net_wins.o net_wipx.o SYSOBJ_SYS := sys_win.o win32res.res endif ifeq ($(TARGET_OS),win64) SYSOBJ_INPUT := in_win.o SYSOBJ_GL_VID:= gl_vidnt.o SYSOBJ_SOFT_VID:= vid_win.o SYSOBJ_NET := net_win.o net_wins.o net_wipx.o SYSOBJ_SYS := sys_win.o win32res.res endif ifeq ($(TARGET_OS),dos) SYSOBJ_INPUT := in_dos.o SYSOBJ_SOFT_VID:= vid_vga.o vid_ext.o vid_dos.o vregset.o SYSOBJ_GL_VID:= dos_dmesa.o dos_fxmesa.o dos_sage.o gl_viddos.o ifeq ($(SYSOBJ_GL_DXE),yes) SYSOBJ_GL_VID+= dxe.o else SYSOBJ_GL_VID+= no_dxe.o endif ifeq ($(USE_3DFXGAMMA),yes) SYSOBJ_GL_VID+= fx_gamma.o endif ifeq ($(USE_X86_ASM),yes) SYSOBJ_SOFT_VID+= d_copy.o endif ifeq ($(SYSOBJ_SOFT_DXE),yes) SYSOBJ_SOFT_VID+= dxe.o else SYSOBJ_SOFT_VID+= no_dxe.o endif SYSOBJ_NET := dos/net_dos.o dos/net_ipx.o ifeq ($(USE_WATT32),yes) SYSOBJ_NET += net_udp.o endif ifeq ($(USE_SERIAL),yes) SYSOBJ_NET += dos/net_ser.o endif SYSOBJ_SYS := dos_v2.o sys_dos.o # dosasm.o endif ifeq ($(TARGET_OS),os2) SYSOBJ_INPUT := in_sdl.o SYSOBJ_GL_VID:= gl_vidsdl.o SYSOBJ_SOFT_VID:= vid_sdl.o SYSOBJ_NET := net_bsd.o net_udp.o SYSOBJ_SYS := sys_os2.o SYSOBJ_SYS += sys_sdl.o endif ifeq ($(TARGET_OS),unix) # unix clients use SDL: SYSOBJ_INPUT := in_sdl.o SYSOBJ_GL_VID:= gl_vidsdl.o ifeq ($(USE_3DFXGAMMA),yes) SYSOBJ_GL_VID+= fx_gamma.o endif SYSOBJ_SOFT_VID:= vid_sdl.o SYSOBJ_NET := net_bsd.o net_udp.o SYSOBJ_SYS := sys_unix.o SYSOBJ_SYS += sys_sdl.o endif ifeq ($(TARGET_OS),darwin) # OSX clients use SDL: SYSOBJ_INPUT := in_sdl.o SYSOBJ_GL_VID:= gl_vidsdl.o SYSOBJ_SOFT_VID:= vid_sdl.o SYSOBJ_NET := net_bsd.o net_udp.o SYSOBJ_SYS := sys_unix.o SYSOBJ_SYS += sys_osx.o SYSOBJ_SYS += SDLMain.o endif ifeq ($(TARGET_OS),aros) ifeq ($(USE_SDL),yes) SYSOBJ_INPUT := in_sdl.o SYSOBJ_GL_VID:= gl_vidsdl.o SYSOBJ_SOFT_VID:= vid_sdl.o else SYSOBJ_INPUT := in_amiga.o SYSOBJ_GL_VID:= gl_vidamiga.o SYSOBJ_SOFT_VID:= vid_cgx.o endif SYSOBJ_NET := net_bsd.o net_udp.o SYSOBJ_SYS := sys_amiga.o endif ifeq ($(TARGET_OS),morphos) ifeq ($(USE_SDL),yes) SYSOBJ_INPUT := in_sdl.o SYSOBJ_GL_VID:= gl_vidsdl.o SYSOBJ_SOFT_VID:= vid_sdl.o else SYSOBJ_INPUT := in_amiga.o SYSOBJ_GL_VID:= gl_vidamiga.o SYSOBJ_SOFT_VID:= vid_cgx.o endif SYSOBJ_NET := net_bsd.o net_udp.o SYSOBJ_SYS := sys_amiga.o endif ifeq ($(TARGET_OS),amigaos) ifeq ($(USE_SDL),yes) SYSOBJ_INPUT := in_sdl.o SYSOBJ_GL_VID:= gl_vidsdl.o SYSOBJ_SOFT_VID:= vid_sdl.o else SYSOBJ_INPUT := in_amiga.o SYSOBJ_GL_VID:= gl_vidamiga.o SYSOBJ_SOFT_VID:= vid_cgx.o ifeq ($(USE_M68K_ASM),yes) CPPFLAGS+= -DUSE_C2P SYSOBJ_SOFT_VID+= c2p1x1_8_c5_bm.o c2p1x1_8_c5_bm_040.o ## c2p1x1_8_c5_030.o c2p1x1_8_c5_040.o endif endif SYSOBJ_NET := net_bsd.o net_udp.o SYSOBJ_SYS := sys_amiga.o endif # Final list of objects SOFTOBJS = \ d_edge.o \ d_fill.o \ d_init.o \ d_modech.o \ d_part.o \ d_polyse.o \ d_scan.o \ d_sky.o \ d_sprite.o \ d_surf.o \ d_vars.o \ d_zpoint.o \ r_aclip.o \ r_alias.o \ r_bsp.o \ r_draw.o \ r_edge.o \ r_efrag.o \ r_light.o \ r_main.o \ r_misc.o \ r_part.o \ r_sky.o \ r_sprite.o \ r_surf.o \ r_vars.o \ screen.o \ $(SYSOBJ_SOFT_VID) \ draw.o \ model.o GLOBJS = \ gl_refrag.o \ gl_rlight.o \ gl_rmain.o \ gl_rmisc.o \ r_part.o \ gl_rsurf.o \ gl_screen.o \ gl_warp.o \ $(SYSOBJ_GL_VID) \ gl_draw.o \ gl_mesh.o \ gl_model.o COMMONOBJS = \ $(SYSOBJ_INPUT) \ $(COMOBJ_SND) \ $(SYSOBJ_SND) \ $(SYSOBJ_CDA) \ $(SYSOBJ_MIDI) \ $(SYSOBJ_NET) \ net_dgrm.o \ net_loop.o \ net_main.o \ chase.o \ cl_demo.o \ cl_effect.o \ cl_inlude.o \ cl_input.o \ cl_main.o \ cl_parse.o \ cl_string.o \ cl_tent.o \ cl_cmd.o \ console.o \ keys.o \ menu.o \ sbar.o \ view.o \ wad.o \ cmd.o \ q_endian.o \ link_ops.o \ sizebuf.o \ strlcat.o \ strlcpy.o \ qsnprint.o \ msg_io.o \ common.o \ debuglog.o \ quakefs.o \ crc.o \ cvar.o \ cfgfile.o \ host.o \ host_cmd.o \ host_string.o \ mathlib.o \ pr_cmds.o \ pr_edict.o \ pr_exec.o \ sv_effect.o \ sv_main.o \ sv_move.o \ sv_phys.o \ sv_user.o \ $(WORLD_ASM) \ world.o \ zone.o \ hashindex.o \ $(SYSOBJ_SYS) # Targets .PHONY: help clean distclean localclean report $(TIMIDEPS) #default: glh2 #all: default help: report @echo @echo "Valid targets: (read/edit the makefile for several options)" @echo @echo "* $(MAKE) help : this help" @echo "* $(MAKE) clean : delete all files produced by the build" @echo "* $(MAKE) h2 : hexen2 binary, software renderer" @echo "* $(MAKE) glh2 : hexen2 binary, opengl renderer" @echo h2: $(SW_BINARY) glh2: $(GL_BINARY) $(GL_BINARY): CPPFLAGS+= $(GL_DEFS) ifeq ($(TARGET_OS),amigaos) # workaround stupid AmiTCP SDK mess for old aos3 net_bsd.o: INCLUDES+= $(NET_INC) net_udp.o: INCLUDES+= $(NET_INC) net_dgrm.o: INCLUDES+= $(NET_INC) net_loop.o: INCLUDES+= $(NET_INC) net_main.o: INCLUDES+= $(NET_INC) endif # deps for *.c including another *.c dos/net_ser.o: dos/net_comx.c # extra rule for snd_timidity.c because of timidity.h and libtimidity.a snd_timidity.o: $(COMMONDIR)/snd_timidity.c $(CC) -c $(INCLUDES) -I$(LIBS_DIR)/timidity $(CPPFLAGS) $(CFLAGS) -DTIMIDITY_STATIC=1 -o $@ $(COMMONDIR)/snd_timidity.c include $(LIBS_DIR)/timidity/_timi.mak $(SW_BINARY): $(SOFT_ASM) $(SOFTOBJS) $(COMMON_ASM) $(COMMONOBJS) $(TIMIDEPS) $(LINKER) $(SOFT_ASM) $(SOFTOBJS) $(COMMON_ASM) $(COMMONOBJS) $(LDFLAGS) $(SYSLIBS) -o $@ $(GL_BINARY): $(GLOBJS) $(COMMON_ASM) $(COMMONOBJS) $(TIMIDEPS) $(LINKER) $(GLOBJS) $(COMMON_ASM) $(COMMONOBJS) $(LDFLAGS) $(GL_LIBS) $(SYSLIBS) -o $@ localclean: rm -f *.o *.res dos/*.o core clean: localclean timi_clean distclean: clean rm -f $(SW_BINARY) $(GL_BINARY) report: @echo "Host OS :" $(HOST_OS) @echo "Target OS:" $(TARGET_OS) @echo "Machine :" $(MACH_TYPE) engine/hexen2/Makefile.os2000066400000000000000000000227741444734033100156750ustar00rootroot00000000000000# makefile to build h2.exe for OS/2 using Open Watcom: # wmake -f Makefile.os2 # # to build opengl version (glh2.exe) : # wmake -f Makefile.os2 BUILDGL=1 # PATH SETTINGS: !ifndef __UNIX__ PATH_SEP=\ UHEXEN2_TOP=..\.. ENGINE_TOP=.. COMMONDIR=$(ENGINE_TOP)\h2shared UHEXEN2_SHARED=$(UHEXEN2_TOP)\common LIBS_DIR=$(UHEXEN2_TOP)\libs OSLIBS=$(UHEXEN2_TOP)\oslibs !else PATH_SEP=/ UHEXEN2_TOP=../.. ENGINE_TOP=.. COMMONDIR=$(ENGINE_TOP)/h2shared UHEXEN2_SHARED=$(UHEXEN2_TOP)/common LIBS_DIR=$(UHEXEN2_TOP)/libs OSLIBS=$(UHEXEN2_TOP)/oslibs !endif # GENERAL OPTIONS (customize as required) # link to the opengl libraries at compile time? (defaults # to no, so the binaries will dynamically load the necessary # libraries and functions at runtime.) LINK_GL_LIBS=yes # use fast x86 assembly on ia32 machines? (auto-disabled for # any other cpu.) USE_X86_ASM=yes # enable sound support? USE_SOUND=yes # include target's MIDI driver if available? USE_MIDI=yes # CDAudio support? USE_CDAUDIO=yes # Enable/disable codecs for streaming music support: USE_CODEC_WAVE=yes USE_CODEC_FLAC=no USE_CODEC_MP3=yes USE_CODEC_VORBIS=yes USE_CODEC_OPUS=no # either xmp or mikmod (or modplug) USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no # either timidity (preferred) or wildmidi (both possible # but not needed nor meaningful) USE_CODEC_TIMIDITY=yes USE_CODEC_WILDMIDI=no # which library to use for mp3 decoding: mad or mpg123 MP3LIB=mad # which library to use for ogg decoding: vorbis or tremor VORBISLIB=tremor # Names of the binaries SW_BINARY=h2.exe GL_BINARY=glh2.exe ############################################################# # Compiler flags ############################################################# CFLAGS = -zq -wx -bm -bt=os2 -5s -sg -otexan -fp5 -fpi87 -ei -j -zp8 # newer OpenWatcom versions enable W303 by default CFLAGS+= -wcd=303 # compiler includes INCLUDES= -I. -I$(COMMONDIR) -I$(UHEXEN2_SHARED) # nasm includes: the trailing directory separator matters NASM_INC= -I.$(PATH_SEP) -I$(COMMONDIR)$(PATH_SEP) # end of compiler flags ############################################################# ############################################################# # Other build flags ############################################################# !ifdef DEMO CPPFLAGS+= -DDEMOBUILD !endif !ifdef DEBUG CFLAGS += -d2 # This activates some extra code in hexen2/hexenworld C source CPPFLAGS+= -DDEBUG=1 -DDEBUG_BUILD=1 !endif ############################################################# # OpenGL settings ############################################################# GL_DEFS = -DGLQUAKE GL_LIBS = ############################################################# # streaming music initial setup ############################################################# !ifneq USE_SOUND yes USE_CODEC_WAVE=no USE_CODEC_FLAC=no USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no USE_CODEC_MP3=no USE_CODEC_VORBIS=no USE_CODEC_OPUS=no !endif ############################################################# # OS/2 flags/settings and overrides: ############################################################# NASMFLAGS=-f obj -d__OS2__ -d_NO_PREFIX !ifndef __UNIX__ INCLUDES+= -I$(OSLIBS)\os2\codecs\include CODECLIBS= $(OSLIBS)\os2\codecs\lib\ SDL_CFLAGS=-I$(OSLIBS)\os2\SDL\include SDL_LIBS = $(OSLIBS)\os2\SDL\lib\SDL12.lib !else INCLUDES+= -I$(OSLIBS)/os2/codecs/include CODECLIBS= $(OSLIBS)/os2/codecs/lib/ SDL_CFLAGS=-I$(OSLIBS)/os2/SDL/include SDL_LIBS = $(OSLIBS)/os2/SDL/lib/SDL12.lib !endif # use SDL for now CPPFLAGS+= -DSDLQUAKE CFLAGS += $(SDL_CFLAGS) LIBS += $(SDL_LIBS) GL_LINK=opengl.lib ############################################################# # Streaming music settings ############################################################# !ifeq MP3LIB mad mp3_obj=snd_mp3 lib_mp3dec=$(CODECLIBS)mad.lib !endif !ifeq MP3LIB mpg123 mp3_obj=snd_mpg123 lib_mp3dec=$(CODECLIBS)mpg123.lib !endif !ifeq VORBISLIB vorbis cpp_vorbisdec= lib_vorbisdec=$(CODECLIBS)vorbisfile.lib $(CODECLIBS)vorbis.lib $(CODECLIBS)ogg.lib !endif !ifeq VORBISLIB tremor cpp_vorbisdec=-DVORBIS_USE_TREMOR lib_vorbisdec=$(CODECLIBS)vorbisidec.lib $(CODECLIBS)ogg.lib !endif !ifeq USE_CODEC_FLAC yes CPPFLAGS+= -DUSE_CODEC_FLAC LIBS += $(CODECLIBS)FLAC.lib !endif !ifeq USE_CODEC_WAVE yes CPPFLAGS+= -DUSE_CODEC_WAVE !endif !ifeq USE_CODEC_OPUS yes CPPFLAGS+= -DUSE_CODEC_OPUS LIBS += $(CODECLIBS)opusfile.lib $(CODECLIBS)opus.lib !ifneq USE_CODEC_VORBIS yes LIBS += $(CODECLIBS)ogg.lib !endif !endif !ifeq USE_CODEC_VORBIS yes CPPFLAGS+= -DUSE_CODEC_VORBIS $(cpp_vorbisdec) LIBS += $(lib_vorbisdec) !endif !ifeq USE_CODEC_MP3 yes CPPFLAGS+= -DUSE_CODEC_MP3 LIBS += $(lib_mp3dec) !endif !ifeq USE_CODEC_MIKMOD yes CPPFLAGS+= -DUSE_CODEC_MIKMOD LIBS += $(CODECLIBS)mikmod.lib !endif !ifeq USE_CODEC_XMP yes CPPFLAGS+= -DUSE_CODEC_XMP LIBS += $(CODECLIBS)libxmp.lib !endif !ifeq USE_CODEC_MODPLUG yes CPPFLAGS+= -DUSE_CODEC_MODPLUG LIBS += $(CODECLIBS)modplug.lib !endif !ifeq USE_CODEC_UMX yes CPPFLAGS+= -DUSE_CODEC_UMX !endif !ifeq USE_CODEC_TIMIDITY yes CPPFLAGS+= -DUSE_CODEC_TIMIDITY LIBS += $(CODECLIBS)timidity.lib !endif !ifeq USE_CODEC_WILDMIDI yes CPPFLAGS+= -DUSE_CODEC_WILDMIDI LIBS += $(CODECLIBS)WildMidi.lib !endif # End of streaming music settings ############################################################# ############################################################# # Finalize things after the system specific overrides: ############################################################# !ifeq USE_X86_ASM yes CPPFLAGS+= -DUSE_INTEL_ASM !endif !ifneq LINK_GL_LIBS yes GL_DEFS+= -DGL_DLSYM !else GL_LIBS+= $(GL_LINK) !endif !ifndef BUILDGL BUILD_TARGET=$(SW_BINARY) !else CPPFLAGS+= $(GL_DEFS) BUILD_TARGET=$(GL_BINARY) !endif # ############################################################# .c: $(COMMONDIR);$(UHEXEN2_SHARED) .asm: $(COMMONDIR) .c.obj: wcc386 $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -fo=$^@ $< .asm.obj: nasm $(NASM_INC) $(NASMFLAGS) -o $^@ $< # Objects # Intel asm objects !ifeq USE_X86_ASM yes COMMON_ASM= math.obj & sys_ia32.obj SOFT_ASM = & d_draw.obj & d_draw16.obj & d_draw16t.obj & d_parta.obj & d_partb.obj & d_polysa.obj & d_polysa2.obj & d_polysa3.obj & d_polysa4.obj & d_polysa5.obj & d_scana.obj & d_spr8.obj & d_spr8t.obj & d_spr8t2.obj & d_varsa.obj & r_aclipa.obj & r_aliasa.obj & r_drawa.obj & r_edgea.obj & r_edgeb.obj & r_varsa.obj & surf8.obj & surf16.obj SOUND_ASM = snd_mixa.obj WORLD_ASM = worlda.obj !else SOFT_ASM = COMMON_ASM = SOUND_ASM = WORLD_ASM = !endif # Sound objects !ifneq USE_SOUND yes MUSIC_OBJS= bgmnull.obj SOUND_ASM = CPPFLAGS += -D_NO_SOUND SYSOBJ_SND = COMOBJ_SND = snd_null.obj $(MUSIC_OBJS) !else MUSIC_OBJS= bgmusic.obj & snd_codec.obj & snd_flac.obj & snd_wave.obj & snd_vorbis.obj & snd_opus.obj & $(mp3_obj).obj & snd_mp3tag.obj & snd_mikmod.obj & snd_modplug.obj & snd_xmp.obj & snd_umx.obj & snd_timidity.obj & snd_wildmidi.obj COMOBJ_SND = snd_sys.obj snd_dma.obj snd_mix.obj $(SOUND_ASM) snd_mem.obj $(MUSIC_OBJS) SYSOBJ_SND = snd_sdl.obj !endif !ifneq USE_MIDI yes SYSOBJ_MIDI= midi_nul.obj CPPFLAGS += -D_NO_MIDIDRV !else # FIXME: need to cook something. SYSOBJ_MIDI= midi_nul.obj CPPFLAGS += -D_NO_MIDIDRV !endif # CDAudio objects !ifneq USE_CDAUDIO yes SYSOBJ_CDA= cd_null.obj CPPFLAGS += -D_NO_CDAUDIO !else CPPFLAGS+= -DWITH_SDLCD SYSOBJ_CDA = cd_sdl.obj !endif SYSOBJ_INPUT = in_sdl.obj SYSOBJ_GL_VID= gl_vidsdl.obj SYSOBJ_SOFT_VID= vid_sdl.obj SYSOBJ_NET = net_bsd.obj net_udp.obj SYSOBJ_SYS = sys_os2.obj # Final list of objects SOFTOBJS = & d_edge.obj & d_fill.obj & d_init.obj & d_modech.obj & d_part.obj & d_polyse.obj & d_scan.obj & d_sky.obj & d_sprite.obj & d_surf.obj & d_vars.obj & d_zpoint.obj & r_aclip.obj & r_alias.obj & r_bsp.obj & r_draw.obj & r_edge.obj & r_efrag.obj & r_light.obj & r_main.obj & r_misc.obj & r_part.obj & r_sky.obj & r_sprite.obj & r_surf.obj & r_vars.obj & screen.obj & $(SYSOBJ_SOFT_VID) & draw.obj & model.obj GLOBJS = & gl_refrag.obj & gl_rlight.obj & gl_rmain.obj & gl_rmisc.obj & r_part.obj & gl_rsurf.obj & gl_screen.obj & gl_warp.obj & $(SYSOBJ_GL_VID) & gl_draw.obj & gl_mesh.obj & gl_model.obj COMMONOBJS = & $(SYSOBJ_INPUT) & $(COMOBJ_SND) & $(SYSOBJ_SND) & $(SYSOBJ_CDA) & $(SYSOBJ_MIDI) & $(SYSOBJ_NET) & net_dgrm.obj & net_loop.obj & net_main.obj & chase.obj & cl_demo.obj & cl_effect.obj & cl_inlude.obj & cl_input.obj & cl_main.obj & cl_parse.obj & cl_string.obj & cl_tent.obj & cl_cmd.obj & console.obj & keys.obj & menu.obj & sbar.obj & view.obj & wad.obj & cmd.obj & q_endian.obj & link_ops.obj & sizebuf.obj & strlcat.obj & strlcpy.obj & qsnprint.obj & msg_io.obj & common.obj & debuglog.obj & quakefs.obj & crc.obj & cvar.obj & cfgfile.obj & host.obj & host_cmd.obj & host_string.obj & mathlib.obj & pr_cmds.obj & pr_edict.obj & pr_exec.obj & sv_effect.obj & sv_main.obj & sv_move.obj & sv_phys.obj & sv_user.obj & $(WORLD_ASM) & world.obj & zone.obj & hashindex.obj & $(SYSOBJ_SYS) all: $(BUILD_TARGET) # 1 MB stack size. $(SW_BINARY): $(SOFT_ASM) $(SOFTOBJS) $(COMMON_ASM) $(COMMONOBJS) wlink N $@ SYS OS2V2 OPTION q OPTION STACK=0x100000 LIBR {$(LIBS)} F {$(SOFT_ASM) $(SOFTOBJS) $(COMMON_ASM) $(COMMONOBJS)} # 1 MB stack size. $(GL_BINARY): $(GLOBJS) $(COMMON_ASM) $(COMMONOBJS) wlink N $@ SYS OS2V2 OPTION q OPTION STACK=0x100000 LIBR {$(LIBS) $(GL_LIBS)} F {$(GLOBJS) $(COMMON_ASM) $(COMMONOBJS)} clean: .symbolic rm -f *.obj *.res *.err distclean: clean .symbolic rm -f $(SW_BINARY) $(GL_BINARY) engine/hexen2/Makefile.svga000066400000000000000000000245331444734033100161250ustar00rootroot00000000000000# GNU Makefile for hexen2 svgalib-client using GCC. # # To use a compiler other than gcc: make CC=compiler_name [other stuff] # To use a different nasm-compatible assembler, such as yasm: # make NASM=yasm [other stuff] # To build for the demo version: make DEMO=1 [other stuff] # To build a debug version: make DEBUG=1 [other stuff] # # PATH SETTINGS: UHEXEN2_TOP:=../.. ENGINE_TOP:=.. COMMONDIR:=$(ENGINE_TOP)/h2shared UHEXEN2_SHARED:=$(UHEXEN2_TOP)/common LIBS_DIR:=$(UHEXEN2_TOP)/libs OSLIBS:=$(UHEXEN2_TOP)/oslibs # GENERAL OPTIONS (customize as required) # use fast x86 assembly on ia32 machines? (auto-disabled for # any other cpu.) USE_X86_ASM=yes # enable sound support? USE_SOUND=yes # ALSA audio support? (req: alsa-lib and alsa-kernel modules # >= 1.0.1. v0.9.8 and v1.0.0 might work, but not supported. # If not Linux, ALSA will be automatically be disabled.) USE_ALSA=yes # OSS audio support? (for Unix. enabled on Linux and FreeBSD. # automatically disabled on other platforms: see snd_sys.h) USE_OSS=yes # include target's MIDI driver if available? USE_MIDI=no # CDAudio support? USE_CDAUDIO=yes # Enable/disable codecs for streaming music support: USE_CODEC_WAVE=yes USE_CODEC_FLAC=no USE_CODEC_MP3=yes USE_CODEC_VORBIS=yes USE_CODEC_OPUS=no # either xmp or mikmod (or modplug) USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no # either timidity (preferred) or wildmidi (both possible # but not needed nor meaningful) USE_CODEC_TIMIDITY=yes USE_CODEC_WILDMIDI=no # which library to use for mp3 decoding: mad or mpg123 MP3LIB=mad # which library to use for ogg decoding: vorbis or tremor VORBISLIB=vorbis # whether the codecs allocate on the zone instead of # system memory (set to yes for DOS builds, for example) CODECS_USE_ZONE=no # include the common dirty stuff include $(UHEXEN2_TOP)/scripts/makefile.inc # Names of the binaries SW_BINARY:=hexen2.svga ############################################################# # Compiler flags ############################################################# ifeq ($(MACH_TYPE),x86) CPU_X86=-march=i586 endif # Overrides for the default CPUFLAGS CPUFLAGS=$(CPU_X86) CFLAGS += -Wall CFLAGS += $(CPUFLAGS) ifdef DEBUG CFLAGS += -g else # optimization flags CFLAGS += -O2 -DNDEBUG=1 -ffast-math # NOTE: -fomit-frame-pointer is broken with ancient gcc versions!! CFLAGS += -fomit-frame-pointer endif CPPFLAGS= LDFLAGS = # linkage may be sensitive to order: add SYSLIBS after all others. SYSLIBS = # compiler includes INCLUDES= -I. -I$(COMMONDIR) -I$(UHEXEN2_SHARED) # nasm includes: the trailing directory separator matters NASM_INC= -I./ -I$(COMMONDIR)/ # end of compiler flags ############################################################# ############################################################# # Other build flags ############################################################# # disable x86 assembly if it is not an x86. ifneq ($(MACH_TYPE),x86) USE_X86_ASM=no endif ifdef DEMO CPPFLAGS+= -DDEMOBUILD endif ifdef DEBUG # This activates some extra code in hexen2/hexenworld C source CPPFLAGS+= -DDEBUG=1 -DDEBUG_BUILD=1 endif ############################################################# # streaming music initial setup ############################################################# ifneq ($(USE_SOUND),yes) USE_CODEC_WAVE=no USE_CODEC_FLAC=no USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no USE_CODEC_MP3=no USE_CODEC_VORBIS=no USE_CODEC_OPUS=no endif # sanity checking for decoder library options ifneq ($(VORBISLIB),vorbis) ifneq ($(VORBISLIB),tremor) $(error Invalid VORBISLIB setting) endif endif ifneq ($(MP3LIB),mpg123) ifneq ($(MP3LIB),mad) $(error Invalid MP3LIB setting) endif endif ############################################################# # Unix flags/settings and overrides: ############################################################# NASMFLAGS=-f elf -d_NO_PREFIX SYSLIBS += -lvga -lm CPPFLAGS+= -DSVGAQUAKE ifeq ($(USE_SOUND),yes) ifneq ($(HOST_OS),linux) # alsa is only for linux USE_ALSA=no endif ifneq ($(USE_ALSA),yes) CPPFLAGS+= -DNO_ALSA_AUDIO else # snd_alsa uses dlopen() & co. SYSLIBS += -ldl endif ifneq ($(USE_OSS),yes) CPPFLAGS+= -DNO_OSS_AUDIO endif CPPFLAGS+= -DNO_SUN_AUDIO CPPFLAGS+= -DNO_SDL_AUDIO endif ifeq ($(USE_CODEC_OPUS),yes) # opus and opusfile put their *.h under /opus, # but they include the headers without the opus directory # prefix and rely on pkg-config. ewww... INCLUDES+= $(shell pkg-config --cflags opusfile) LDFLAGS += $(shell pkg-config --libs opusfile) endif # End of Unix settings ############################################################# ############################################################# # Streaming music settings ############################################################# ifeq ($(MP3LIB),mad) mp3_obj=snd_mp3 lib_mp3dec=-lmad endif ifeq ($(MP3LIB),mpg123) mp3_obj=snd_mpg123 lib_mp3dec=-lmpg123 endif ifeq ($(VORBISLIB),vorbis) cpp_vorbisdec= lib_vorbisdec=-lvorbisfile -lvorbis -logg endif ifeq ($(VORBISLIB),tremor) cpp_vorbisdec=-DVORBIS_USE_TREMOR lib_vorbisdec=-lvorbisidec -logg endif ifeq ($(USE_CODEC_FLAC),yes) CPPFLAGS+= -DUSE_CODEC_FLAC LDFLAGS+= -lFLAC endif ifeq ($(USE_CODEC_WAVE),yes) CPPFLAGS+= -DUSE_CODEC_WAVE endif ifeq ($(USE_CODEC_OPUS),yes) CPPFLAGS+= -DUSE_CODEC_OPUS endif ifeq ($(USE_CODEC_VORBIS),yes) CPPFLAGS+= -DUSE_CODEC_VORBIS $(cpp_vorbisdec) LDFLAGS+= $(lib_vorbisdec) endif ifeq ($(USE_CODEC_MP3),yes) CPPFLAGS+= -DUSE_CODEC_MP3 LDFLAGS+= $(lib_mp3dec) endif ifeq ($(USE_CODEC_MIKMOD),yes) CPPFLAGS+= -DUSE_CODEC_MIKMOD LDFLAGS+= -lmikmod endif ifeq ($(USE_CODEC_XMP),yes) CPPFLAGS+= -DUSE_CODEC_XMP LDFLAGS+= -lxmp endif ifeq ($(USE_CODEC_MODPLUG),yes) CPPFLAGS+= -DUSE_CODEC_MODPLUG LDFLAGS+= -lmodplug endif ifeq ($(USE_CODEC_UMX),yes) CPPFLAGS+= -DUSE_CODEC_UMX endif ifeq ($(USE_CODEC_TIMIDITY),yes) CPPFLAGS+= -DUSE_CODEC_TIMIDITY LDFLAGS+= -L$(LIBS_DIR)/timidity -ltimidity LIBTIMIDITY=libtimidity.a TIMIDEPS=$(LIBS_DIR)/timidity/$(LIBTIMIDITY) else TIMIDEPS= endif ifeq ($(USE_CODEC_WILDMIDI),yes) CPPFLAGS+= -DUSE_CODEC_WILDMIDI LDFLAGS+= -lWildMidi endif ifeq ($(CODECS_USE_ZONE),yes) CPPFLAGS+=-DCODECS_USE_ZONE endif # End of streaming music settings ############################################################# ############################################################# # Finalize things after the system specific overrides: ############################################################# ifeq ($(USE_X86_ASM),yes) CPPFLAGS+= -DUSE_INTEL_ASM endif # ############################################################# # Rules for turning source files into .o files %.o: %.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(COMMONDIR)/%.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(UHEXEN2_SHARED)/%.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: %.asm $(NASM) $(NASM_INC) $(NASMFLAGS) -o $@ $< %.o: $(COMMONDIR)/%.asm $(NASM) $(NASM_INC) $(NASMFLAGS) -o $@ $< # Objects # Intel asm objects ifeq ($(USE_X86_ASM),yes) COMMON_ASM= math.o \ sys_ia32.o SOFT_ASM = \ d_draw.o \ d_draw16.o \ d_draw16t.o \ d_parta.o \ d_partb.o \ d_polysa.o \ d_polysa2.o \ d_polysa3.o \ d_polysa4.o \ d_polysa5.o \ d_scana.o \ d_spr8.o \ d_spr8t.o \ d_spr8t2.o \ d_varsa.o \ r_aclipa.o \ r_aliasa.o \ r_drawa.o \ r_edgea.o \ r_edgeb.o \ r_varsa.o \ surf8.o \ surf16.o SOUND_ASM = snd_mixa.o WORLD_ASM = worlda.o else SOFT_ASM = COMMON_ASM = SOUND_ASM = WORLD_ASM = endif # Sound objects ifneq ($(USE_SOUND),yes) MUSIC_OBJS:= bgmnull.o SOUND_ASM := CPPFLAGS += -D_NO_SOUND SYSOBJ_SND := COMOBJ_SND := snd_null.o $(MUSIC_OBJS) else MUSIC_OBJS:= bgmusic.o \ snd_codec.o \ snd_flac.o \ snd_wave.o \ snd_vorbis.o \ snd_opus.o \ $(mp3_obj).o \ snd_mp3tag.o \ snd_mikmod.o \ snd_modplug.o \ snd_xmp.o \ snd_umx.o \ snd_timidity.o \ snd_wildmidi.o COMOBJ_SND := snd_sys.o snd_dma.o snd_mix.o $(SOUND_ASM) snd_mem.o $(MUSIC_OBJS) SYSOBJ_SND := snd_oss.o snd_alsa.o # end of Sound objects endif # MIDI objects ifneq ($(USE_MIDI),yes) SYSOBJ_MIDI:= midi_nul.o CPPFLAGS += -D_NO_MIDIDRV else $(error Midi driver not implemented yet.) # end of MIDI objects endif # CDAudio objects ifneq ($(USE_CDAUDIO),yes) SYSOBJ_CDA:= cd_null.o CPPFLAGS += -D_NO_CDAUDIO else SYSOBJ_CDA := cd_bsd.o cd_linux.o # end of CDAudio objects endif # Other platform specific object settings SYSOBJ_INPUT := in_svgalib.o SYSOBJ_SOFT_VID:= vid_svgalib.o # d_copy.o not needed anymore SYSOBJ_NET := net_bsd.o net_udp.o SYSOBJ_SYS := sys_unix.o # Final list of objects SOFTOBJS = \ d_edge.o \ d_fill.o \ d_init.o \ d_modech.o \ d_part.o \ d_polyse.o \ d_scan.o \ d_sky.o \ d_sprite.o \ d_surf.o \ d_vars.o \ d_zpoint.o \ r_aclip.o \ r_alias.o \ r_bsp.o \ r_draw.o \ r_edge.o \ r_efrag.o \ r_light.o \ r_main.o \ r_misc.o \ r_part.o \ r_sky.o \ r_sprite.o \ r_surf.o \ r_vars.o \ screen.o \ $(SYSOBJ_SOFT_VID) \ draw.o \ model.o COMMONOBJS = \ $(SYSOBJ_INPUT) \ $(COMOBJ_SND) \ $(SYSOBJ_SND) \ $(SYSOBJ_CDA) \ $(SYSOBJ_MIDI) \ $(SYSOBJ_NET) \ net_dgrm.o \ net_loop.o \ net_main.o \ chase.o \ cl_demo.o \ cl_effect.o \ cl_inlude.o \ cl_input.o \ cl_main.o \ cl_parse.o \ cl_string.o \ cl_tent.o \ cl_cmd.o \ console.o \ keys.o \ menu.o \ sbar.o \ view.o \ wad.o \ cmd.o \ q_endian.o \ link_ops.o \ sizebuf.o \ strlcat.o \ strlcpy.o \ qsnprint.o \ msg_io.o \ common.o \ debuglog.o \ quakefs.o \ crc.o \ cvar.o \ cfgfile.o \ host.o \ host_cmd.o \ host_string.o \ mathlib.o \ pr_cmds.o \ pr_edict.o \ pr_exec.o \ sv_effect.o \ sv_main.o \ sv_move.o \ sv_phys.o \ sv_user.o \ $(WORLD_ASM) \ world.o \ zone.o \ hashindex.o \ $(SYSOBJ_SYS) # Targets .PHONY: clean distclean localclean $(TIMIDEPS) default: $(SW_BINARY) all: default h2: $(SW_BINARY) hexen2: $(SW_BINARY) # extra rule for snd_timidity.c because of timidity.h and libtimidity.a snd_timidity.o: $(COMMONDIR)/snd_timidity.c $(CC) -c $(INCLUDES) -I$(LIBS_DIR)/timidity $(CPPFLAGS) $(CFLAGS) -DTIMIDITY_STATIC=1 -o $@ $(COMMONDIR)/snd_timidity.c include $(LIBS_DIR)/timidity/_timi.mak $(SW_BINARY): $(SOFT_ASM) $(SOFTOBJS) $(COMMON_ASM) $(COMMONOBJS) $(TIMIDEPS) $(LINKER) $(SOFT_ASM) $(SOFTOBJS) $(COMMON_ASM) $(COMMONOBJS) $(LDFLAGS) $(SYSLIBS) -o $@ localclean: rm -f *.o *.res dos/*.o core clean: localclean timi_clean distclean: clean rm -f $(SW_BINARY) report: @echo "Host OS :" $(HOST_OS) @echo "Target OS:" $(TARGET_OS) @echo "Machine :" $(MACH_TYPE) engine/hexen2/Makefile.wat000066400000000000000000000261071444734033100157570ustar00rootroot00000000000000# makefile to build h2.exe for Windows using Open Watcom: # wmake -f Makefile.wat # # to build opengl version (glh2.exe) : # wmake -f Makefile.wat BUILDGL=1 # PATH SETTINGS: !ifndef __UNIX__ PATH_SEP=\ UHEXEN2_TOP=..\.. ENGINE_TOP=.. COMMONDIR=$(ENGINE_TOP)\h2shared UHEXEN2_SHARED=$(UHEXEN2_TOP)\common LIBS_DIR=$(UHEXEN2_TOP)\libs OSLIBS=$(UHEXEN2_TOP)\oslibs !else PATH_SEP=/ UHEXEN2_TOP=../.. ENGINE_TOP=.. COMMONDIR=$(ENGINE_TOP)/h2shared UHEXEN2_SHARED=$(UHEXEN2_TOP)/common LIBS_DIR=$(UHEXEN2_TOP)/libs OSLIBS=$(UHEXEN2_TOP)/oslibs !endif # GENERAL OPTIONS (customize as required) # link to the opengl libraries at compile time? (defaults # to no, so the binaries will dynamically load the necessary # libraries and functions at runtime.) LINK_GL_LIBS=no # use fast x86 assembly on ia32 machines? (auto-disabled for # any other cpu.) USE_X86_ASM=yes # enable sound support? USE_SOUND=yes # include target's MIDI driver if available? USE_MIDI=yes # CDAudio support? USE_CDAUDIO=yes # link to the directx libraries at compile time? (otherwise, load # the necessary DLLs and functions dynamically at runtime, which # ensures our exe to function on ancient windows versions without # a directx installation.) LINK_DIRECTX=no # enable startup splash screens? (windows) WITH_SPLASHES=yes # use WinSock2 instead of WinSock-1.1? (disabled for w32 for compat. # with old Win95 machines.) (enabled for Win64 in the win64 section.) USE_WINSOCK2=no # Enable/disable codecs for streaming music support: USE_CODEC_WAVE=yes USE_CODEC_FLAC=no USE_CODEC_MP3=yes USE_CODEC_VORBIS=yes USE_CODEC_OPUS=no # either xmp or mikmod (or modplug) USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no # either timidity (preferred) or wildmidi (both possible # but not needed nor meaningful) USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no # which library to use for mp3 decoding: mad or mpg123 MP3LIB=mad # which library to use for ogg decoding: vorbis or tremor VORBISLIB=tremor # Names of the binaries SW_NAME=h2 GL_NAME=glh2 SW_BINARY=$(SW_NAME).exe GL_BINARY=$(GL_NAME).exe ############################################################# # Compiler flags ############################################################# CFLAGS = -zq -wx -bm -bt=nt -5s -sg -otexan -fp5 -fpi87 -ei -j -zp8 # newer OpenWatcom versions enable W303 by default CFLAGS+= -wcd=303 # compiler includes INCLUDES= -I. -I$(COMMONDIR) -I$(UHEXEN2_SHARED) # nasm includes: the trailing directory separator matters NASM_INC= -I.$(PATH_SEP) -I$(COMMONDIR)$(PATH_SEP) # windows resource compiler includes RC_INC = -I. -I$(COMMONDIR) # end of compiler flags ############################################################# ############################################################# # Other build flags ############################################################# !ifdef DEMO CPPFLAGS+= -DDEMOBUILD !endif !ifdef DEBUG CFLAGS += -d2 # This activates some extra code in hexen2/hexenworld C source CPPFLAGS+= -DDEBUG=1 -DDEBUG_BUILD=1 !endif ############################################################# # OpenGL settings ############################################################# GL_DEFS = -DGLQUAKE GL_LIBS = ############################################################# # streaming music initial setup ############################################################# !ifneq USE_SOUND yes USE_CODEC_WAVE=no USE_CODEC_FLAC=no USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no USE_CODEC_MP3=no USE_CODEC_VORBIS=no USE_CODEC_OPUS=no !endif ############################################################# # Win32 flags/settings and overrides: ############################################################# RC_DEFS=$(CPPFLAGS) RCFLAGS=-bt=nt NASMFLAGS=-f win32 -d_NO_PREFIX !ifndef __UNIX__ INCLUDES+= -I$(OSLIBS)\windows\dxsdk\include INCLUDES+= -I$(OSLIBS)\windows\misc\include INCLUDES+= -I$(OSLIBS)\windows\codecs\include DXLIBS = $(OSLIBS)\windows\dxsdk\x86\ CODECLIBS= $(OSLIBS)\windows\codecs\x86-watcom\ !else INCLUDES+= -I$(OSLIBS)/windows/dxsdk/include INCLUDES+= -I$(OSLIBS)/windows/misc/include INCLUDES+= -I$(OSLIBS)/windows/codecs/include DXLIBS = $(OSLIBS)/windows/dxsdk/x86/ CODECLIBS= $(OSLIBS)/windows/codecs/x86-watcom/ !endif GL_LINK=opengl32.lib !ifeq USE_WINSOCK2 yes CPPFLAGS+=-D_USE_WINSOCK2 LIBWINSOCK=ws2_32.lib !else LIBWINSOCK=wsock32.lib !endif CPPFLAGS+= -DWIN32_LEAN_AND_MEAN LIBS += $(LIBWINSOCK) winmm.lib !ifneq LINK_DIRECTX yes CPPFLAGS+= -DDX_DLSYM !else LIBS += $(DXLIBS)dsound.lib $(DXLIBS)dinput.lib $(DXLIBS)dxguid.lib !endif !ifneq WITH_SPLASHES yes CPPFLAGS+= -DNO_SPLASHES !endif ############################################################# # Streaming music settings ############################################################# !ifeq MP3LIB mad mp3_obj=snd_mp3 lib_mp3dec=$(CODECLIBS)mad.lib !endif !ifeq MP3LIB mpg123 mp3_obj=snd_mpg123 lib_mp3dec=$(CODECLIBS)mpg123.lib !endif !ifeq VORBISLIB vorbis cpp_vorbisdec= lib_vorbisdec=$(CODECLIBS)vorbisfile.lib $(CODECLIBS)vorbis.lib $(CODECLIBS)ogg.lib !endif !ifeq VORBISLIB tremor cpp_vorbisdec=-DVORBIS_USE_TREMOR lib_vorbisdec=$(CODECLIBS)vorbisidec.lib $(CODECLIBS)ogg.lib !endif !ifeq USE_CODEC_FLAC yes CPPFLAGS+= -DUSE_CODEC_FLAC # for static linkage CPPFLAGS+= -DFLAC__NO_DLL LIBS += $(CODECLIBS)FLAC.lib !endif !ifeq USE_CODEC_WAVE yes CPPFLAGS+= -DUSE_CODEC_WAVE !endif !ifeq USE_CODEC_OPUS yes CPPFLAGS+= -DUSE_CODEC_OPUS LIBS += $(CODECLIBS)opusfile.lib $(CODECLIBS)opus.lib !ifneq USE_CODEC_VORBIS yes LIBS += $(CODECLIBS)ogg.lib !endif !endif !ifeq USE_CODEC_VORBIS yes CPPFLAGS+= -DUSE_CODEC_VORBIS $(cpp_vorbisdec) LIBS += $(lib_vorbisdec) !endif !ifeq USE_CODEC_MP3 yes CPPFLAGS+= -DUSE_CODEC_MP3 LIBS += $(lib_mp3dec) !endif !ifeq USE_CODEC_MIKMOD yes CPPFLAGS+= -DUSE_CODEC_MIKMOD # for static linkage CPPFLAGS+= -DMIKMOD_STATIC LIBS += $(CODECLIBS)mikmod.lib !endif !ifeq USE_CODEC_XMP yes CPPFLAGS+= -DUSE_CODEC_XMP # for static linkage CPPFLAGS+= -DLIBXMP_STATIC LIBS += $(CODECLIBS)libxmp.lib !endif !ifeq USE_CODEC_MODPLUG yes CPPFLAGS+= -DUSE_CODEC_MODPLUG CPPFLAGS+= -DMODPLUG_STATIC LIBS += $(CODECLIBS)modplug.lib !endif !ifeq USE_CODEC_UMX yes CPPFLAGS+= -DUSE_CODEC_UMX !endif !ifeq USE_CODEC_TIMIDITY yes CPPFLAGS+= -DUSE_CODEC_TIMIDITY LIBS += $(CODECLIBS)timidity.lib !endif !ifeq USE_CODEC_WILDMIDI yes CPPFLAGS+= -DUSE_CODEC_WILDMIDI LIBS += $(CODECLIBS)WildMidi.lib !endif # End of streaming music settings ############################################################# ############################################################# # Finalize things after the system specific overrides: ############################################################# !ifeq USE_X86_ASM yes CPPFLAGS+= -DUSE_INTEL_ASM !endif !ifneq LINK_GL_LIBS yes GL_DEFS+= -DGL_DLSYM !else GL_LIBS+= $(GL_LINK) !endif !ifndef BUILDGL TARGET_NAME=$(SW_NAME) BUILD_TARGET=$(SW_BINARY) !else CPPFLAGS+= $(GL_DEFS) TARGET_NAME=$(GL_NAME) BUILD_TARGET=$(GL_BINARY) !endif # ############################################################# .EXTENSIONS: .res .rc .c: $(COMMONDIR);$(UHEXEN2_SHARED) .asm: $(COMMONDIR) .rc: $(COMMONDIR) .c.obj: wcc386 $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -fo=$^@ $< .asm.obj: nasm $(NASM_INC) $(NASMFLAGS) -o $^@ $< .rc.res: wrc -q -r $(RCFLAGS) $(RC_DEFS) $(RC_INC) -fo=$^@ $< # Objects # Intel asm objects !ifeq USE_X86_ASM yes COMMON_ASM= math.obj & sys_ia32.obj SOFT_ASM = & d_draw.obj & d_draw16.obj & d_draw16t.obj & d_parta.obj & d_partb.obj & d_polysa.obj & d_polysa2.obj & d_polysa3.obj & d_polysa4.obj & d_polysa5.obj & d_scana.obj & d_spr8.obj & d_spr8t.obj & d_spr8t2.obj & d_varsa.obj & r_aclipa.obj & r_aliasa.obj & r_drawa.obj & r_edgea.obj & r_edgeb.obj & r_varsa.obj & surf8.obj & surf16.obj SOUND_ASM = snd_mixa.obj WORLD_ASM = worlda.obj !else SOFT_ASM = COMMON_ASM = SOUND_ASM = WORLD_ASM = !endif # Sound objects !ifneq USE_SOUND yes MUSIC_OBJS= bgmnull.obj SOUND_ASM = CPPFLAGS += -D_NO_SOUND SYSOBJ_SND = COMOBJ_SND = snd_null.obj $(MUSIC_OBJS) !else MUSIC_OBJS= bgmusic.obj & snd_codec.obj & snd_flac.obj & snd_wave.obj & snd_vorbis.obj & snd_opus.obj & $(mp3_obj).obj & snd_mp3tag.obj & snd_mikmod.obj & snd_modplug.obj & snd_xmp.obj & snd_umx.obj & snd_timidity.obj & snd_wildmidi.obj COMOBJ_SND = snd_sys.obj snd_dma.obj snd_mix.obj $(SOUND_ASM) snd_mem.obj $(MUSIC_OBJS) SYSOBJ_SND = snd_win.obj snd_dsound.obj !endif !ifneq USE_MIDI yes SYSOBJ_MIDI= midi_nul.obj CPPFLAGS += -D_NO_MIDIDRV !else SYSOBJ_MIDI= midi_win.obj mid2strm.obj !endif # CDAudio objects !ifneq USE_CDAUDIO yes SYSOBJ_CDA= cd_null.obj CPPFLAGS += -D_NO_CDAUDIO !else SYSOBJ_CDA = cd_win.obj !endif SYSOBJ_INPUT = in_win.obj SYSOBJ_GL_VID= gl_vidnt.obj SYSOBJ_SOFT_VID= vid_win.obj SYSOBJ_NET = net_win.obj net_wins.obj net_wipx.obj SYSOBJ_SYS = sys_win.obj SYSOBJ_RES = $(TARGET_NAME).res # Final list of objects SOFTOBJS = & d_edge.obj & d_fill.obj & d_init.obj & d_modech.obj & d_part.obj & d_polyse.obj & d_scan.obj & d_sky.obj & d_sprite.obj & d_surf.obj & d_vars.obj & d_zpoint.obj & r_aclip.obj & r_alias.obj & r_bsp.obj & r_draw.obj & r_edge.obj & r_efrag.obj & r_light.obj & r_main.obj & r_misc.obj & r_part.obj & r_sky.obj & r_sprite.obj & r_surf.obj & r_vars.obj & screen.obj & $(SYSOBJ_SOFT_VID) & draw.obj & model.obj GLOBJS = & gl_refrag.obj & gl_rlight.obj & gl_rmain.obj & gl_rmisc.obj & r_part.obj & gl_rsurf.obj & gl_screen.obj & gl_warp.obj & $(SYSOBJ_GL_VID) & gl_draw.obj & gl_mesh.obj & gl_model.obj COMMONOBJS = & $(SYSOBJ_INPUT) & $(COMOBJ_SND) & $(SYSOBJ_SND) & $(SYSOBJ_CDA) & $(SYSOBJ_MIDI) & $(SYSOBJ_NET) & net_dgrm.obj & net_loop.obj & net_main.obj & chase.obj & cl_demo.obj & cl_effect.obj & cl_inlude.obj & cl_input.obj & cl_main.obj & cl_parse.obj & cl_string.obj & cl_tent.obj & cl_cmd.obj & console.obj & keys.obj & menu.obj & sbar.obj & view.obj & wad.obj & cmd.obj & q_endian.obj & link_ops.obj & sizebuf.obj & strlcat.obj & strlcpy.obj & qsnprint.obj & msg_io.obj & common.obj & debuglog.obj & quakefs.obj & crc.obj & cvar.obj & cfgfile.obj & host.obj & host_cmd.obj & host_string.obj & mathlib.obj & pr_cmds.obj & pr_edict.obj & pr_exec.obj & sv_effect.obj & sv_main.obj & sv_move.obj & sv_phys.obj & sv_user.obj & $(WORLD_ASM) & world.obj & zone.obj & hashindex.obj & $(SYSOBJ_SYS) all: $(BUILD_TARGET) $(TARGET_NAME).res: win32res.rc wrc -q -r $(RCFLAGS) $(RC_DEFS) $(RC_INC) -fo=$^@ $< snd_timidity.obj: $(COMMONDIR)/snd_timidity.c wcc386 $(INCLUDES) -I$(LIBS_DIR)/timidity $(CPPFLAGS) $(CFLAGS) -DTIMIDITY_STATIC=1 -fo=$^@ $< # 1 MB stack size. $(SW_BINARY): $(SOFT_ASM) $(SOFTOBJS) $(COMMON_ASM) $(COMMONOBJS) $(SYSOBJ_RES) wlink N $@ SYS NT_WIN OPTION q OPTION STACK=0x100000 OPTION RESOURCE=$^*.res LIBR {$(LIBS)} F {$(SOFT_ASM) $(SOFTOBJS) $(COMMON_ASM) $(COMMONOBJS)} # 1 MB stack size. $(GL_BINARY): $(GLOBJS) $(COMMON_ASM) $(COMMONOBJS) $(SYSOBJ_RES) wlink N $@ SYS NT_WIN OPTION q OPTION STACK=0x100000 OPTION RESOURCE=$^*.res LIBR {$(LIBS) $(GL_LIBS)} F {$(GLOBJS) $(COMMON_ASM) $(COMMONOBJS)} clean: .symbolic rm -f *.obj *.res *.err distclean: clean .symbolic rm -f $(SW_BINARY) $(GL_BINARY) engine/hexen2/bugs.txt000066400000000000000000000055741444734033100152330ustar00rootroot00000000000000Bug/Problem Priority Status Where to test it ---------------------------------------- -------- ------ ---------------- big spider web causes pain sound High FIXED keep4 camera_remote allows mouse looking HIGH FIXED monster-dropped puzzle pieces don't High FIXED have netnames Golems don't have a dormant flag High FIXED Necromancer weapons broken (maybe 3 HIGH FIXED gets stuck) Death Message for Pentacles High SEMI-FIXED experience value default on pentacles High FIXED Trains & stop sounds and decap all fucked SUPERHIGH FIXED!!! Tibet4 up! Monsters can't go up stairs and slopes SUPERHIGH Any to NE nor down SW Snow auto-adjust speed and count cap SUPERHIGH JFM Snow shut-off at distance SUPERHIGH JFM Yaks and Mezzomen can't hit you if you're HIGH FIXED above or below them Pentacles killing causes no free edicts HIGH error? Too many gas glyphs crash game High FIXED Move rest of chunks and particle High good enough effects to client Add delay to exploding glyphs to prevent High fixed cascade Tome of Power (use/die/can't use) High? CNR Proper death messages for Succubus High fixed ZEbrains spawns 5 blood sprites LOW - change to client effect Succubus must reset her gravity when dies Med fixed Fallen Angel & Imp flapping sound Med FIXED Keep5 doesn't stop Make yakman's attacks more network friendly Med fixed Fadespeed on lights too fast Low bad push sound for Pentacles? Low MG Need to make player untargetable and Low fixed invincible when in camera mode Give exp for damaging a creature, and Low fixed remove that exp from it's value Make everything network friendly Low good as it gets Features Needed ------------------ need bone chunks for obj_skeleton - made, DONE ice_chunks for func_train - NOT BROKEN sound types for trains : Rolling Boulder - DONE Spinning prayer wheel - DONE find function that works on any field-NO ability to turn off ambient sounds?-NO MG_DONE Invincibility effect on Succubus TEST! Succubus 2nd ability - Done? (gain mana when taking damage) check thresholds, and amounts NO-group player's frames into client side anim for less net traffic in multiplayer? MG_DONE Coop view for multiplayer (impulse 33?) save config - DONE JFM_DONE - add Always MLook to menu, JFM_DONE - add Crosshair to menu, JFM_DONE - add objectives key to customize controls JFM_DONE - add menu option to start mission pack directly JFM_DONE - sort out hcode precache commands to properly build our new pak4. More level of detail options (eg.: dynamic lights) MAYBE - add snow options to menu switch new game and new mission in menu add play demo to main menu New flame model for Kenn -DONE Lion statue -DONE team chat get autolooping sounds (C-side) to update their position when they start their loop again? More Funk _ never can get enough engine/hexen2/build_all.sh000077500000000000000000000006101444734033100160020ustar00rootroot00000000000000#!/bin/sh HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac $MAKE_CMD clean $MAKE_CMD h2 $* || exit 1 $MAKE_CMD localclean $MAKE_CMD glh2 USE_X86_ASM=no $* || exit 1 $MAKE_CMD -C server clean $MAKE_CMD -C server $* || exit 1 $MAKE_CMD clean $MAKE_CMD -C server clean engine/hexen2/build_cross_amigaos.sh000077500000000000000000000012401444734033100200630ustar00rootroot00000000000000#!/bin/sh UHEXEN2_TOP=../.. . $UHEXEN2_TOP/scripts/cross_defs.amigaos if test "$1" = "strip"; then $STRIPPER -S hexen2 glhexen2 server/h2ded exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac if test "$1" = "h2ded"; then shift $MAKE_CMD -C server $* || exit 1 exit 0 fi if test "$1" = "all"; then shift $MAKE_CMD clean $MAKE_CMD h2 $* || exit 1 $MAKE_CMD clean $MAKE_CMD glh2 $* || exit 1 $MAKE_CMD -C server clean $MAKE_CMD -C server $* || exit 1 $MAKE_CMD clean $MAKE_CMD -C server clean exit 0 fi exec $MAKE_CMD $* engine/hexen2/build_cross_aros.sh000077500000000000000000000013111444734033100174060ustar00rootroot00000000000000#!/bin/sh # for x86-AROS cross-builds UHEXEN2_TOP=../.. . $UHEXEN2_TOP/scripts/cross_defs.aros if test "$1" = "strip"; then $STRIPPER -S hexen2 glhexen2 server/h2ded exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac if test "$1" = "h2ded"; then shift $MAKE_CMD -C server $* || exit 1 exit 0 fi if test "$1" = "all"; then shift $MAKE_CMD clean $MAKE_CMD h2 $* || exit 1 $MAKE_CMD clean $MAKE_CMD glh2 USE_X86_ASM=no $* || exit 1 $MAKE_CMD -C server clean $MAKE_CMD -C server $* || exit 1 $MAKE_CMD clean $MAKE_CMD -C server clean exit 0 fi exec $MAKE_CMD $* engine/hexen2/build_cross_aros64.sh000077500000000000000000000013131444734033100175620ustar00rootroot00000000000000#!/bin/sh # for x86_64-AROS cross-builds UHEXEN2_TOP=../.. . $UHEXEN2_TOP/scripts/cross_defs.aros64 if test "$1" = "strip"; then $STRIPPER -S hexen2 glhexen2 server/h2ded exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac if test "$1" = "h2ded"; then shift $MAKE_CMD -C server $* || exit 1 exit 0 fi if test "$1" = "all"; then shift $MAKE_CMD clean $MAKE_CMD h2 $* || exit 1 $MAKE_CMD clean $MAKE_CMD glh2 USE_GLA=yes $* || exit 1 $MAKE_CMD -C server clean $MAKE_CMD -C server $* || exit 1 $MAKE_CMD clean $MAKE_CMD -C server clean exit 0 fi exec $MAKE_CMD $* engine/hexen2/build_cross_morphos.sh000077500000000000000000000012401444734033100201320ustar00rootroot00000000000000#!/bin/sh UHEXEN2_TOP=../.. . $UHEXEN2_TOP/scripts/cross_defs.morphos if test "$1" = "strip"; then $STRIPPER -S hexen2 glhexen2 server/h2ded exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac if test "$1" = "h2ded"; then shift $MAKE_CMD -C server $* || exit 1 exit 0 fi if test "$1" = "all"; then shift $MAKE_CMD clean $MAKE_CMD h2 $* || exit 1 $MAKE_CMD clean $MAKE_CMD glh2 $* || exit 1 $MAKE_CMD -C server clean $MAKE_CMD -C server $* || exit 1 $MAKE_CMD clean $MAKE_CMD -C server clean exit 0 fi exec $MAKE_CMD $* engine/hexen2/chase.c000066400000000000000000000057241444734033100147560ustar00rootroot00000000000000/* chase.c -- chase camera code * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" static cvar_t chase_back = {"chase_back", "100", CVAR_NONE}; static cvar_t chase_up = {"chase_up", "16", CVAR_NONE}; static cvar_t chase_right = {"chase_right", "0", CVAR_NONE}; cvar_t chase_active = {"chase_active", "0", CVAR_NONE}; static vec3_t chase_dest; void Chase_Init (void) { Cvar_RegisterVariable (&chase_back); Cvar_RegisterVariable (&chase_up); Cvar_RegisterVariable (&chase_right); Cvar_RegisterVariable (&chase_active); } void Chase_Reset (void) { // for respawning and teleporting // start position 12 units behind head } static void TraceLine (vec3_t start, vec3_t end, vec3_t impact) { trace_t trace; memset (&trace, 0, sizeof(trace)); SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace); VectorCopy (trace.endpos, impact); } void Chase_Update (void) { float dist; vec3_t forward, up, right; vec3_t dest, stop; // if can't see player, reset AngleVectors (cl.viewangles, forward, right, up); // calc exact destination chase_dest[0] = r_refdef.vieworg[0] - forward[0] * chase_back.value - right[0] * chase_right.value; chase_dest[1] = r_refdef.vieworg[1] - forward[1] * chase_back.value - right[1] * chase_right.value; #if 0 chase_dest[2] = r_refdef.vieworg[2] - forward[2] * chase_back.value - right[] * chase_right.value; chase_dest[2] += chase_up.value; #endif chase_dest[2] = r_refdef.vieworg[2] + chase_up.value; // find the spot the player is looking at VectorMA (r_refdef.vieworg, 4096, forward, dest); TraceLine (r_refdef.vieworg, dest, stop); // calculate pitch to look at the same spot from camera VectorSubtract (stop, r_refdef.vieworg, stop); dist = DotProduct (stop, forward); if (dist < 1) dist = 1; r_refdef.viewangles[PITCH] = -atan(stop[2] / dist) / M_PI * 180; // check for walls between player and camera (from quakeforge) TraceLine(r_refdef.vieworg, chase_dest, stop); dist = VectorLengthFast(stop); if (dist <= -((float)DIST_EPSILON) || dist >= ((float)DIST_EPSILON)) { chase_dest[0] = stop[0] + forward[0] * 8; chase_dest[1] = stop[1] + forward[1] * 8; chase_dest[2] = stop[2] + forward[2] * 8; } // move towards destination VectorCopy (chase_dest, r_refdef.vieworg); } engine/hexen2/cl_cmd.c000066400000000000000000000035401444734033100151060ustar00rootroot00000000000000/* * cl_cmd.c -- client command forwarding to server * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" /* =================== Cmd_ForwardToServer Sends the entire command line over to the server =================== */ void Cmd_ForwardToServer (void) { if (cls.state != ca_connected) { Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0)); return; } if (cls.demoplayback) return; // not really connected MSG_WriteByte (&cls.message, clc_stringcmd); SZ_Print (&cls.message, Cmd_Argv(0)); if (Cmd_Argc() > 1) { SZ_Print (&cls.message, " "); SZ_Print (&cls.message, Cmd_Args()); } } // This is the command variant of the above. The only difference // is that it doesn't forward the first argument, which is "cmd" void Cmd_ForwardToServer_f (void) { if (cls.state != ca_connected) { Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0)); return; } if (cls.demoplayback) return; // not really connected if (Cmd_Argc() > 1) { MSG_WriteByte (&cls.message, clc_stringcmd); SZ_Print (&cls.message, Cmd_Args()); } } void CL_Cmd_Init (void) { Cmd_AddCommand ("cmd", Cmd_ForwardToServer_f); } engine/hexen2/cl_demo.c000066400000000000000000000234631444734033100152750ustar00rootroot00000000000000/* * cl_demo.c -- demo recording and playback * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" static void CL_FinishTimeDemo (void); /* vars for the mission pack intro */ qboolean intro_playing = false; #if 0 /* these should have been used for * recording the mission pack intro */ qboolean skip_start = false; int num_intro_msg = 0; #endif /* ============================================================================== DEMO CODE When a demo is playing back, all NET_SendMessages are skipped, and NET_GetMessages are read from the demo file. Whenever cl.time gets past the last received message, another message is read from the demo file. ============================================================================== */ /* ============== CL_StopPlayback Called when a demo file runs out, or the user starts a game ============== */ void CL_StopPlayback (void) { if (!cls.demoplayback) return; if (intro_playing) M_ToggleMenu_f(); intro_playing = false; // num_intro_msg = 0; fclose (cls.demofile); cls.demoplayback = false; cls.demofile = NULL; cls.state = ca_disconnected; if (cls.timedemo) CL_FinishTimeDemo (); } /* ==================== CL_WriteDemoMessage Dumps the current net message, prefixed by the length and view angles ==================== */ static void CL_WriteDemoMessage (void) { int len; int i; float f; len = LittleLong (net_message.cursize); fwrite (&len, 4, 1, cls.demofile); // fwrite (&len, 4, 1, cls.introdemofile); for (i = 0; i < 3; i++) { f = LittleFloat (cl.viewangles[i]); fwrite (&f, 4, 1, cls.demofile); // fwrite (&f, 4, 1, cls.introdemofile); } fwrite (net_message.data, net_message.cursize, 1, cls.demofile); fflush (cls.demofile); // fwrite (net_message.data, net_message.cursize, 1, cls.introdemofile); // fflush (cls.introdemofile); } /* ==================== CL_GetDemoMessage ==================== */ static int CL_GetDemoMessage (void) { int r, i; float f; // decide if it is time to grab the next message if (cls.signon == SIGNONS) // always grab until fully connected { if (cls.timedemo) { if (host_framecount == cls.td_lastframe) return 0; // already read this frame's message cls.td_lastframe = host_framecount; // if this is the second frame, grab the real td_starttime // so the bogus time on the first frame doesn't count if (host_framecount == cls.td_startframe + 1) cls.td_starttime = realtime; } else if (/* cl.time > 0 && */ cl.time <= cl.mtime[0]) { return 0; // don't need another message yet } } // get the next message /* if (intro_playing && num_intro_msg > 0 && num_intro_msg < 21) V_DarkFlash_f(); // Fade into demo */ /* if (skip_start && num_intro_msg > 3) { while (num_intro_msg < 1110) { fread (&net_message.cursize, 4, 1, cls.demofile); VectorCopy (cl.mviewangles[0], cl.mviewangles[1]); for (i = 0; i < 3; i++) { r = fread (&f, 4, 1, cls.demofile); cl.mviewangles[0][i] = LittleFloat (f); } net_message.cursize = LittleLong (net_message.cursize); num_intro_msg++; if (net_message.cursize > MAX_MSGLEN) Sys_Error ("Demo message > MAX_MSGLEN"); r = fread (net_message.data, net_message.cursize, 1, cls.demofile); if (r != 1) { CL_StopPlayback (); return 0; } if (num_intro_msg == 174 || num_intro_msg == 178 || num_intro_msg == 428 || num_intro_msg == 553 || num_intro_msg == 1012) break; } if (num_intro_msg == 1110) skip_start = false; goto skipit; } */ fread (&net_message.cursize, 4, 1, cls.demofile); VectorCopy (cl.mviewangles[0], cl.mviewangles[1]); for (i = 0 ; i < 3 ; i++) { r = fread (&f, 4, 1, cls.demofile); cl.mviewangles[0][i] = LittleFloat (f); } net_message.cursize = LittleLong (net_message.cursize); // num_intro_msg++; if (net_message.cursize > MAX_MSGLEN) Sys_Error ("Demo message > MAX_MSGLEN"); r = fread (net_message.data, net_message.cursize, 1, cls.demofile); if (r != 1) { CL_StopPlayback (); return 0; } /* skipit: if (cls.demorecording) CL_WriteDemoMessage (); */ return 1; } /* ==================== CL_GetMessage Handles recording and playback of demos, on top of NET_ code ==================== */ int CL_GetMessage (void) { int r; if (cls.demoplayback) return CL_GetDemoMessage (); while (1) { r = NET_GetMessage (cls.netcon); if (r != 1 && r != 2) return r; // discard nop keepalive message if (net_message.cursize == 1 && net_message.data[0] == svc_nop) Con_Printf ("<-- server to client keepalive\n"); else break; } if (cls.demorecording) CL_WriteDemoMessage (); return r; } /* ==================== CL_Stop_f stop recording a demo ==================== */ void CL_Stop_f (void) { if (cmd_source != src_command) return; if (!cls.demorecording) { Con_Printf ("Not recording a demo.\n"); return; } intro_playing = false; // num_intro_msg = 0; // write a disconnect message to the demo file SZ_Clear (&net_message); MSG_WriteByte (&net_message, svc_disconnect); CL_WriteDemoMessage (); // finish up // fclose (cls.introdemofile); // cls.introdemofile = NULL; fclose (cls.demofile); cls.demofile = NULL; cls.demorecording = false; Con_Printf ("Completed demo\n"); } /* ==================== CL_Record_f record [cd track] ==================== */ void CL_Record_f (void) { int c; char name[MAX_OSPATH]; const char *p; int track; if (cmd_source != src_command) return; if (cls.demorecording) CL_Stop_f(); c = Cmd_Argc(); if (c != 2 && c != 3 && c != 4) { Con_Printf ("record [ [cd track]]\n"); return; } p = Cmd_Argv(1); if (*p == '.' || strstr(p, "..")) { Con_Printf ("Invalid demo name.\n"); return; } if (c == 2 && cls.state == ca_connected) { Con_Printf("Can not record - already connected to server\nClient demo recording must be started before connecting\n"); return; } // write the forced cd track number, or -1 if (c == 4) { track = atoi(Cmd_Argv(3)); Con_Printf ("Forcing CD track to %i\n", cls.forcetrack); } else { track = -1; } FS_MakePath_BUF (FS_USERDIR, NULL, name, sizeof(name), p); // start the map up if (c > 2) { Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command); if (cls.state != ca_connected) return; } // open the demo file COM_AddExtension (name, ".dem", sizeof(name)); Con_Printf ("recording to %s.\n", name); cls.demofile = fopen (name, "wb"); if (!cls.demofile) { Con_Printf ("ERROR: couldn't create %s\n", name); return; } cls.forcetrack = track; fprintf (cls.demofile, "%i\n", cls.forcetrack); cls.demorecording = true; } /* ==================== CL_PlayDemo_f play [demoname] ==================== */ void CL_PlayDemo_f (void) { char name[MAX_OSPATH]; if (cmd_source != src_command) return; if (Cmd_Argc() != 2) { Con_Printf ("playdemo : plays a demo\n"); return; } // disconnect from server CL_Disconnect (); // open the demo file q_strlcpy (name, Cmd_Argv(1), sizeof(name)); intro_playing = false; if (gameflags & GAME_PORTALS) { /* the mission pack specific intro is actually * a pre-recorded demo named t9.dem, which is * plain crap, because it reserves an ordinary * name for a special purpose... */ if (q_strcasecmp(name, "t9") == 0) { intro_playing = true; // skip_start = true; } } COM_AddExtension (name, ".dem", sizeof(name)); Con_Printf ("Playing demo from %s.\n", name); /* if (intro_playing) { cls.demorecording = true; cls.introdemofile = fopen("t9.dem", "wb"); } */ FS_OpenFile (name, &cls.demofile, NULL); if (!cls.demofile) { Con_Printf ("ERROR: couldn't open %s\n", name); cls.demonum = -1; // stop demo loop return; } // ZOID, fscanf is evil // O.S.: if a space character e.g. 0x20 (' ') follows '\n', // fscanf skips that byte too and screws up further reads. // fscanf (cls.demofile, "%i\n", &cls.forcetrack); if (fscanf (cls.demofile, "%i", &cls.forcetrack) != 1 || fgetc (cls.demofile) != '\n') { fclose (cls.demofile); cls.demofile = NULL; cls.demonum = -1; // stop demo loop Con_Printf ("ERROR: demo \"%s\" is invalid\n", name); return; } cls.demoplayback = true; cls.state = ca_connected; // get rid of the menu and/or console Key_SetDest (key_game); } /* ==================== CL_FinishTimeDemo ==================== */ static void CL_FinishTimeDemo (void) { int frames; float time; cls.timedemo = false; // the first frame didn't count frames = (host_framecount - cls.td_startframe) - 1; time = realtime - cls.td_starttime; if (!time) time = 1; Con_Printf ("%i frames %5.1f seconds %5.1f fps\n", frames, time, frames/time); } /* ==================== CL_TimeDemo_f timedemo [demoname] ==================== */ void CL_TimeDemo_f (void) { if (cmd_source != src_command) return; if (Cmd_Argc() != 2) { Con_Printf ("timedemo : gets demo speeds\n"); return; } CL_PlayDemo_f (); if (!cls.demofile) return; // cls.td_starttime will be grabbed at the second frame of the demo, so // all the loading time doesn't get counted cls.timedemo = true; cls.td_startframe = host_framecount; cls.td_lastframe = -1; // get a new message this frame } engine/hexen2/cl_effect.c000066400000000000000000001371031444734033100156020ustar00rootroot00000000000000/* cl_effect.c -- Client side effects. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // HEADER FILES ------------------------------------------------------------ #include "quakedef.h" // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- #define MAX_EFFECT_ENTITIES 256 // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static int NewEffectEntity (void); static void FreeEffectEntity (int idx); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- // PRIVATE DATA DEFINITIONS ------------------------------------------------ static entity_t EffectEntities[MAX_EFFECT_ENTITIES]; static qboolean EntityUsed[MAX_EFFECT_ENTITIES]; static int EffectEntityCount; // CODE -------------------------------------------------------------------- //========================================================================== // // CL_InitTEnts // //========================================================================== void CL_InitEffects (void) { } void CL_ClearEffects (void) { memset(cl.Effects, 0, sizeof(cl.Effects)); memset(EntityUsed, 0, sizeof(EntityUsed)); EffectEntityCount = 0; } static void CL_FreeEffect (int idx) { int i; switch (cl.Effects[idx].type) { case CE_RAIN: break; case CE_SNOW: break; case CE_FOUNTAIN: break; case CE_QUAKE: break; case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK1: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: case CE_ACID_MUZZFL: case CE_FLAMESTREAM: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: FreeEffectEntity(cl.Effects[idx].ef.Smoke.entity_index); break; /* Just go through animation and then remove */ case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_FLOOR_EXPLOSION3: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_BRN_BOUNCE: case CE_LSHOCK: case CE_ACID_HIT: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_LBALL_EXPL: case CE_FBOOM: case CE_BOMB: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: FreeEffectEntity(cl.Effects[idx].ef.Smoke.entity_index); break; /* Go forward then backward through animation then remove */ case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_RED_FLASH: FreeEffectEntity(cl.Effects[idx].ef.Flash.entity_index); break; case CE_RIDER_DEATH: break; case CE_GRAVITYWELL: break; case CE_TELEPORTERPUFFS: for (i = 0 ; i < 8 ; ++i) FreeEffectEntity(cl.Effects[idx].ef.Teleporter.entity_index[i]); break; case CE_TELEPORTERBODY: FreeEffectEntity(cl.Effects[idx].ef.Teleporter.entity_index[0]); break; case CE_BONESHARD: case CE_BONESHRAPNEL: FreeEffectEntity(cl.Effects[idx].ef.Missile.entity_index); break; case CE_CHUNK: for (i = 0 ; i < cl.Effects[idx].ef.Chunk.numChunks ; i++) FreeEffectEntity(cl.Effects[idx].ef.Chunk.entity_index[i]); break; } memset(&cl.Effects[idx], 0, sizeof(struct EffectT)); } //========================================================================== // // CL_ParseEffect // //========================================================================== // All changes need to be in SV_SendEffect(), SV_ParseEffect(), // SV_SaveEffects(), SV_LoadEffects(), CL_ParseEffect() void CL_ParseEffect (void) { int i, idx; qboolean ImmediateFree; entity_t *ent; int dir; float sinval, cosval; float skinnum; float final; ImmediateFree = false; idx = MSG_ReadByte(); if (cl.Effects[idx].type) CL_FreeEffect(idx); memset(&cl.Effects[idx], 0, sizeof(struct EffectT)); cl.Effects[idx].type = MSG_ReadByte(); switch (cl.Effects[idx].type) { case CE_RAIN: cl.Effects[idx].ef.Rain.min_org[0] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.min_org[1] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.min_org[2] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.max_org[0] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.max_org[1] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.max_org[2] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.e_size[0] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.e_size[1] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.e_size[2] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.dir[0] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.dir[1] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.dir[2] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.color = MSG_ReadShort(); cl.Effects[idx].ef.Rain.count = MSG_ReadShort(); cl.Effects[idx].ef.Rain.wait = MSG_ReadFloat(); break; case CE_SNOW: cl.Effects[idx].ef.Rain.min_org[0] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.min_org[1] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.min_org[2] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.max_org[0] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.max_org[1] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.max_org[2] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.flags = MSG_ReadByte(); cl.Effects[idx].ef.Rain.dir[0] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.dir[1] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.dir[2] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.count = MSG_ReadByte(); //cl.Effects[idx].ef.Rain.veer = MSG_ReadShort(); break; case CE_FOUNTAIN: cl.Effects[idx].ef.Fountain.pos[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Fountain.pos[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Fountain.pos[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Fountain.angle[0] = MSG_ReadAngle (); cl.Effects[idx].ef.Fountain.angle[1] = MSG_ReadAngle (); cl.Effects[idx].ef.Fountain.angle[2] = MSG_ReadAngle (); cl.Effects[idx].ef.Fountain.movedir[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Fountain.movedir[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Fountain.movedir[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Fountain.color = MSG_ReadShort (); cl.Effects[idx].ef.Fountain.cnt = MSG_ReadByte (); AngleVectors (cl.Effects[idx].ef.Fountain.angle, cl.Effects[idx].ef.Fountain.vforward, cl.Effects[idx].ef.Fountain.vright, cl.Effects[idx].ef.Fountain.vup); break; case CE_QUAKE: cl.Effects[idx].ef.Quake.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Quake.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Quake.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Quake.radius = MSG_ReadFloat (); break; case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK1: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: case CE_ACID_MUZZFL: case CE_FLAMESTREAM: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: cl.Effects[idx].ef.Smoke.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Smoke.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Smoke.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Smoke.velocity[0] = MSG_ReadFloat (); cl.Effects[idx].ef.Smoke.velocity[1] = MSG_ReadFloat (); cl.Effects[idx].ef.Smoke.velocity[2] = MSG_ReadFloat (); cl.Effects[idx].ef.Smoke.framelength = MSG_ReadFloat (); /* smoke frame is a mission pack thing only. */ if (cl_protocol > PROTOCOL_RAVEN_111) cl.Effects[idx].ef.Smoke.frame = MSG_ReadFloat (); if ((cl.Effects[idx].ef.Smoke.entity_index = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Smoke.entity_index]; VectorCopy(cl.Effects[idx].ef.Smoke.origin, ent->origin); if ((cl.Effects[idx].type == CE_WHITE_SMOKE) || (cl.Effects[idx].type == CE_SLOW_WHITE_SMOKE)) ent->model = Mod_ForName("models/whtsmk1.spr", true); else if (cl.Effects[idx].type == CE_GREEN_SMOKE) ent->model = Mod_ForName("models/grnsmk1.spr", true); else if (cl.Effects[idx].type == CE_GREY_SMOKE) ent->model = Mod_ForName("models/grysmk1.spr", true); else if (cl.Effects[idx].type == CE_RED_SMOKE) ent->model = Mod_ForName("models/redsmk1.spr", true); else if (cl.Effects[idx].type == CE_TELESMK1) ent->model = Mod_ForName("models/telesmk1.spr", true); else if (cl.Effects[idx].type == CE_TELESMK2) ent->model = Mod_ForName("models/telesmk2.spr", true); else if (cl.Effects[idx].type == CE_REDCLOUD) ent->model = Mod_ForName("models/rcloud.spr", true); else if (cl.Effects[idx].type == CE_FLAMESTREAM) ent->model = Mod_ForName("models/flamestr.spr", true); else if (cl.Effects[idx].type == CE_ACID_MUZZFL) { ent->model = Mod_ForName("models/muzzle1.spr", true); ent->drawflags = DRF_TRANSLUCENT | MLS_ABSLIGHT; ent->abslight = 51; } else if (cl.Effects[idx].type == CE_FLAMEWALL) ent->model = Mod_ForName("models/firewal1.spr", true); else if (cl.Effects[idx].type == CE_FLAMEWALL2) ent->model = Mod_ForName("models/firewal2.spr", true); else if (cl.Effects[idx].type == CE_ONFIRE) { float rdm = rand() & 3; if (rdm < 1) ent->model = Mod_ForName("models/firewal1.spr", true); else if (rdm < 2) ent->model = Mod_ForName("models/firewal2.spr", true); else ent->model = Mod_ForName("models/firewal3.spr", true); ent->drawflags = DRF_TRANSLUCENT; ent->abslight = 255; ent->frame = cl.Effects[idx].ef.Smoke.frame; } if (cl.Effects[idx].type != CE_REDCLOUD && cl.Effects[idx].type != CE_ACID_MUZZFL && cl.Effects[idx].type != CE_FLAMEWALL) ent->drawflags = DRF_TRANSLUCENT; if (cl.Effects[idx].type == CE_FLAMESTREAM) { ent->drawflags = DRF_TRANSLUCENT | MLS_ABSLIGHT; ent->abslight = 255; ent->frame = cl.Effects[idx].ef.Smoke.frame; } if (cl.Effects[idx].type == CE_GHOST) { ent->model = Mod_ForName("models/ghost.spr", true); ent->drawflags = DRF_TRANSLUCENT | MLS_ABSLIGHT; ent->abslight = 127; } } else ImmediateFree = true; break; case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_FLOOR_EXPLOSION3: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_BRN_BOUNCE: case CE_LSHOCK: case CE_ACID_HIT: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_LBALL_EXPL: case CE_FBOOM: case CE_BOMB: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: cl.Effects[idx].ef.Smoke.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Smoke.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Smoke.origin[2] = MSG_ReadCoord (); if ((cl.Effects[idx].ef.Smoke.entity_index = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Smoke.entity_index]; VectorCopy(cl.Effects[idx].ef.Smoke.origin, ent->origin); if (cl.Effects[idx].type == CE_BLUESPARK) ent->model = Mod_ForName("models/bspark.spr", true); else if (cl.Effects[idx].type == CE_YELLOWSPARK) ent->model = Mod_ForName("models/spark.spr", true); else if (cl.Effects[idx].type == CE_SM_CIRCLE_EXP) ent->model = Mod_ForName("models/fcircle.spr", true); else if (cl.Effects[idx].type == CE_BG_CIRCLE_EXP) ent->model = Mod_ForName("models/xplod29.spr", true); else if (cl.Effects[idx].type == CE_SM_WHITE_FLASH) ent->model = Mod_ForName("models/sm_white.spr", true); else if (cl.Effects[idx].type == CE_YELLOWRED_FLASH) { ent->model = Mod_ForName("models/yr_flsh.spr", true); ent->drawflags = DRF_TRANSLUCENT; } else if (cl.Effects[idx].type == CE_SM_EXPLOSION) ent->model = Mod_ForName("models/sm_expld.spr", true); else if (cl.Effects[idx].type == CE_LG_EXPLOSION) ent->model = Mod_ForName("models/bg_expld.spr", true); else if (cl.Effects[idx].type == CE_FLOOR_EXPLOSION) ent->model = Mod_ForName("models/fl_expld.spr", true); else if (cl.Effects[idx].type == CE_FLOOR_EXPLOSION3) ent->model = Mod_ForName("models/biggy.spr", true); else if (cl.Effects[idx].type == CE_BLUE_EXPLOSION) ent->model = Mod_ForName("models/xpspblue.spr", true); else if (cl.Effects[idx].type == CE_REDSPARK) ent->model = Mod_ForName("models/rspark.spr", true); else if (cl.Effects[idx].type == CE_GREENSPARK) ent->model = Mod_ForName("models/gspark.spr", true); else if (cl.Effects[idx].type == CE_ICEHIT) ent->model = Mod_ForName("models/icehit.spr", true); else if (cl.Effects[idx].type == CE_MEDUSA_HIT) ent->model = Mod_ForName("models/medhit.spr", true); else if (cl.Effects[idx].type == CE_MEZZO_REFLECT) ent->model = Mod_ForName("models/mezzoref.spr", true); else if (cl.Effects[idx].type == CE_FLOOR_EXPLOSION2) ent->model = Mod_ForName("models/flrexpl2.spr", true); else if (cl.Effects[idx].type == CE_XBOW_EXPLOSION) ent->model = Mod_ForName("models/xbowexpl.spr", true); else if (cl.Effects[idx].type == CE_NEW_EXPLOSION) ent->model = Mod_ForName("models/gen_expl.spr", true); else if (cl.Effects[idx].type == CE_MAGIC_MISSILE_EXPLOSION) ent->model = Mod_ForName("models/mm_expld.spr", true); else if (cl.Effects[idx].type == CE_BONE_EXPLOSION) ent->model = Mod_ForName("models/bonexpld.spr", true); else if (cl.Effects[idx].type == CE_BLDRN_EXPL) ent->model = Mod_ForName("models/xplsn_1.spr", true); else if (cl.Effects[idx].type == CE_ACID_HIT) ent->model = Mod_ForName("models/axplsn_2.spr", true); else if (cl.Effects[idx].type == CE_ACID_SPLAT) ent->model = Mod_ForName("models/axplsn_1.spr", true); else if (cl.Effects[idx].type == CE_ACID_EXPL) { ent->model = Mod_ForName("models/axplsn_5.spr", true); ent->drawflags = MLS_ABSLIGHT; ent->abslight = 255; } else if (cl.Effects[idx].type == CE_FBOOM) ent->model = Mod_ForName("models/fboom.spr", true); else if (cl.Effects[idx].type == CE_BOMB) ent->model = Mod_ForName("models/pow.spr", true); else if (cl.Effects[idx].type == CE_LBALL_EXPL) ent->model = Mod_ForName("models/Bluexp3.spr", true); else if (cl.Effects[idx].type == CE_FIREWALL_SMALL) ent->model = Mod_ForName("models/firewal1.spr", true); else if (cl.Effects[idx].type == CE_FIREWALL_MEDIUM) ent->model = Mod_ForName("models/firewal5.spr", true); else if (cl.Effects[idx].type == CE_FIREWALL_LARGE) ent->model = Mod_ForName("models/firewal4.spr", true); else if (cl.Effects[idx].type == CE_BRN_BOUNCE) ent->model = Mod_ForName("models/spark.spr", true); else if (cl.Effects[idx].type == CE_LSHOCK) { ent->model = Mod_ForName("models/vorpshok.mdl", true); ent->drawflags = MLS_TORCH; ent->angles[2] = 90; ent->scale = 255; } } else { ImmediateFree = true; } break; case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_RED_FLASH: cl.Effects[idx].ef.Flash.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Flash.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Flash.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Flash.reverse = 0; if ((cl.Effects[idx].ef.Flash.entity_index = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Flash.entity_index]; VectorCopy(cl.Effects[idx].ef.Flash.origin, ent->origin); if (cl.Effects[idx].type == CE_WHITE_FLASH) ent->model = Mod_ForName("models/gryspt.spr", true); else if (cl.Effects[idx].type == CE_BLUE_FLASH) ent->model = Mod_ForName("models/bluflash.spr", true); else if (cl.Effects[idx].type == CE_SM_BLUE_FLASH) ent->model = Mod_ForName("models/sm_blue.spr", true); else if (cl.Effects[idx].type == CE_RED_FLASH) ent->model = Mod_ForName("models/redspt.spr", true); ent->drawflags = DRF_TRANSLUCENT; } else { ImmediateFree = true; } break; case CE_RIDER_DEATH: cl.Effects[idx].ef.RD.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.RD.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.RD.origin[2] = MSG_ReadCoord (); break; case CE_GRAVITYWELL: cl.Effects[idx].ef.RD.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.RD.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.RD.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.RD.color = MSG_ReadShort (); cl.Effects[idx].ef.RD.lifetime = MSG_ReadFloat (); break; case CE_TELEPORTERPUFFS: cl.Effects[idx].ef.Teleporter.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Teleporter.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Teleporter.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Teleporter.framelength = .05; dir = 0; for (i = 0 ; i < 8 ; ++i) { if ((cl.Effects[idx].ef.Teleporter.entity_index[i] = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Teleporter.entity_index[i]]; VectorCopy(cl.Effects[idx].ef.Teleporter.origin, ent->origin); q_sincosdeg(dir, &sinval, &cosval); cl.Effects[idx].ef.Teleporter.velocity[i][0] = 10*cosval; cl.Effects[idx].ef.Teleporter.velocity[i][1] = 10*sinval; cl.Effects[idx].ef.Teleporter.velocity[i][2] = 0; dir += 45; ent->model = Mod_ForName("models/telesmk2.spr", true); ent->drawflags = DRF_TRANSLUCENT; } else { ImmediateFree = true; break; } } if (ImmediateFree) { for (i = 0 ; i < 8 ; ++i) { if (cl.Effects[idx].ef.Teleporter.entity_index[i] == -1) break; FreeEffectEntity(cl.Effects[idx].ef.Teleporter.entity_index[i]); } break; } break; case CE_TELEPORTERBODY: cl.Effects[idx].ef.Teleporter.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Teleporter.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Teleporter.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Teleporter.velocity[0][0] = MSG_ReadFloat (); cl.Effects[idx].ef.Teleporter.velocity[0][1] = MSG_ReadFloat (); cl.Effects[idx].ef.Teleporter.velocity[0][2] = MSG_ReadFloat (); skinnum = MSG_ReadFloat (); cl.Effects[idx].ef.Teleporter.framelength = .05; dir = 0; if ((cl.Effects[idx].ef.Teleporter.entity_index[0] = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Teleporter.entity_index[0]]; VectorCopy(cl.Effects[idx].ef.Teleporter.origin, ent->origin); ent->model = Mod_ForName("models/teleport.mdl", true); ent->drawflags = SCALE_TYPE_XYONLY | DRF_TRANSLUCENT; ent->scale = 100; ent->skinnum = skinnum; } else { ImmediateFree = true; } break; case CE_BONESHARD: case CE_BONESHRAPNEL: cl.Effects[idx].ef.Missile.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Missile.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Missile.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Missile.velocity[0] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.velocity[1] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.velocity[2] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.angle[0] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.angle[1] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.angle[2] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.avelocity[0] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.avelocity[1] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.avelocity[2] = MSG_ReadFloat (); if ((cl.Effects[idx].ef.Missile.entity_index = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Missile.entity_index]; VectorCopy(cl.Effects[idx].ef.Missile.origin, ent->origin); if (cl.Effects[idx].type == CE_BONESHARD) ent->model = Mod_ForName("models/boneshot.mdl", true); else if (cl.Effects[idx].type == CE_BONESHRAPNEL) ent->model = Mod_ForName("models/boneshrd.mdl", true); } else ImmediateFree = true; break; case CE_CHUNK: cl.Effects[idx].ef.Chunk.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Chunk.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Chunk.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Chunk.type = MSG_ReadByte (); cl.Effects[idx].ef.Chunk.srcVel[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Chunk.srcVel[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Chunk.srcVel[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Chunk.numChunks = MSG_ReadByte (); cl.Effects[idx].ef.Chunk.time_amount = 4.0; cl.Effects[idx].ef.Chunk.aveScale = 30 + 100 * (cl.Effects[idx].ef.Chunk.numChunks / 40.0); if (cl.Effects[idx].ef.Chunk.numChunks > 16) cl.Effects[idx].ef.Chunk.numChunks = 16; for (i = 0 ; i < cl.Effects[idx].ef.Chunk.numChunks ; i++) { if ((cl.Effects[idx].ef.Chunk.entity_index[i] = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Chunk.entity_index[i]]; VectorCopy(cl.Effects[idx].ef.Chunk.origin, ent->origin); VectorCopy(cl.Effects[idx].ef.Chunk.srcVel, cl.Effects[idx].ef.Chunk.velocity[i]); VectorScale(cl.Effects[idx].ef.Chunk.velocity[i], .80 + ((rand() % 4) / 10.0), cl.Effects[idx].ef.Chunk.velocity[i]); /* temp modify them... */ cl.Effects[idx].ef.Chunk.velocity[i][0] += (rand() % 140) - 70; cl.Effects[idx].ef.Chunk.velocity[i][1] += (rand() % 140) - 70; cl.Effects[idx].ef.Chunk.velocity[i][2] += (rand() % 140) - 70; /* are these in degrees or radians? */ ent->angles[0] = rand() % 360; ent->angles[1] = rand() % 360; ent->angles[2] = rand() % 360; ent->scale = cl.Effects[idx].ef.Chunk.aveScale + (rand() % 40); /* make this overcomplicated */ final = (rand() % 100) * .01; if ((cl.Effects[idx].ef.Chunk.type == THINGTYPE_GLASS) || (cl.Effects[idx].ef.Chunk.type == THINGTYPE_REDGLASS) || (cl.Effects[idx].ef.Chunk.type == THINGTYPE_CLEARGLASS) || (cl.Effects[idx].ef.Chunk.type == THINGTYPE_WEBS)) { if (final < 0.20) ent->model = Mod_ForName ("models/shard1.mdl", true); else if (final < 0.40) ent->model = Mod_ForName ("models/shard2.mdl", true); else if (final < 0.60) ent->model = Mod_ForName ("models/shard3.mdl", true); else if (final < 0.80) ent->model = Mod_ForName ("models/shard4.mdl", true); else ent->model = Mod_ForName ("models/shard5.mdl", true); if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_CLEARGLASS) { ent->skinnum = 1; ent->drawflags |= DRF_TRANSLUCENT; } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_REDGLASS) { ent->skinnum = 2; } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_WEBS) { ent->skinnum = 3; ent->drawflags |= DRF_TRANSLUCENT; } } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_WOOD) { if (final < 0.25) ent->model = Mod_ForName ("models/splnter1.mdl", true); else if (final < 0.50) ent->model = Mod_ForName ("models/splnter2.mdl", true); else if (final < 0.75) ent->model = Mod_ForName ("models/splnter3.mdl", true); else ent->model = Mod_ForName ("models/splnter4.mdl", true); } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_METAL) { if (final < 0.25) ent->model = Mod_ForName ("models/metlchk1.mdl", true); else if (final < 0.50) ent->model = Mod_ForName ("models/metlchk2.mdl", true); else if (final < 0.75) ent->model = Mod_ForName ("models/metlchk3.mdl", true); else ent->model = Mod_ForName ("models/metlchk4.mdl", true); } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_FLESH) { if (final < 0.33) ent->model = Mod_ForName ("models/flesh1.mdl", true); else if (final < 0.66) ent->model = Mod_ForName ("models/flesh2.mdl", true); else ent->model = Mod_ForName ("models/flesh3.mdl", true); } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_BROWNSTONE) { if (final < 0.25) ent->model = Mod_ForName ("models/schunk1.mdl", true); else if (final < 0.50) ent->model = Mod_ForName ("models/schunk2.mdl", true); else if (final < 0.75) ent->model = Mod_ForName ("models/schunk3.mdl", true); else ent->model = Mod_ForName ("models/schunk4.mdl", true); ent->skinnum = 1; } else if ((cl.Effects[idx].ef.Chunk.type == THINGTYPE_CLAY) || (cl.Effects[idx].ef.Chunk.type == THINGTYPE_BONE)) { if (final < 0.25) ent->model = Mod_ForName ("models/clshard1.mdl", true); else if (final < 0.50) ent->model = Mod_ForName ("models/clshard2.mdl", true); else if (final < 0.75) ent->model = Mod_ForName ("models/clshard3.mdl", true); else ent->model = Mod_ForName ("models/clshard4.mdl", true); if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_BONE) { ent->skinnum = 1; /* bone skin is second */ } } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_LEAVES) { if (final < 0.33) ent->model = Mod_ForName ("models/leafchk1.mdl", true); else if (final < 0.66) ent->model = Mod_ForName ("models/leafchk2.mdl", true); else ent->model = Mod_ForName ("models/leafchk3.mdl", true); } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_HAY) { if (final < 0.33) ent->model = Mod_ForName ("models/hay1.mdl", true); else if (final < 0.66) ent->model = Mod_ForName ("models/hay2.mdl", true); else ent->model = Mod_ForName ("models/hay3.mdl", true); } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_CLOTH) { if (final < 0.33) ent->model = Mod_ForName ("models/clthchk1.mdl", true); else if (final < 0.66) ent->model = Mod_ForName ("models/clthchk2.mdl", true); else ent->model = Mod_ForName ("models/clthchk3.mdl", true); } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_WOOD_LEAF) { if (final < 0.14) ent->model = Mod_ForName ("models/splnter1.mdl", true); else if (final < 0.28) ent->model = Mod_ForName ("models/leafchk1.mdl", true); else if (final < 0.42) ent->model = Mod_ForName ("models/splnter2.mdl", true); else if (final < 0.56) ent->model = Mod_ForName ("models/leafchk2.mdl", true); else if (final < 0.70) ent->model = Mod_ForName ("models/splnter3.mdl", true); else if (final < 0.84) ent->model = Mod_ForName ("models/leafchk3.mdl", true); else ent->model = Mod_ForName ("models/splnter4.mdl", true); } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_WOOD_METAL) { if (final < 0.125) ent->model = Mod_ForName ("models/splnter1.mdl", true); else if (final < 0.25) ent->model = Mod_ForName ("models/metlchk1.mdl", true); else if (final < 0.375) ent->model = Mod_ForName ("models/splnter2.mdl", true); else if (final < 0.50) ent->model = Mod_ForName ("models/metlchk2.mdl", true); else if (final < 0.625) ent->model = Mod_ForName ("models/splnter3.mdl", true); else if (final < 0.75) ent->model = Mod_ForName ("models/metlchk3.mdl", true); else if (final < 0.875) ent->model = Mod_ForName ("models/splnter4.mdl", true); else ent->model = Mod_ForName ("models/metlchk4.mdl", true); } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_WOOD_STONE) { if (final < 0.125) ent->model = Mod_ForName ("models/splnter1.mdl", true); else if (final < 0.25) ent->model = Mod_ForName ("models/schunk1.mdl", true); else if (final < 0.375) ent->model = Mod_ForName ("models/splnter2.mdl", true); else if (final < 0.50) ent->model = Mod_ForName ("models/schunk2.mdl", true); else if (final < 0.625) ent->model = Mod_ForName ("models/splnter3.mdl", true); else if (final < 0.75) ent->model = Mod_ForName ("models/schunk3.mdl", true); else if (final < 0.875) ent->model = Mod_ForName ("models/splnter4.mdl", true); else ent->model = Mod_ForName ("models/schunk4.mdl", true); } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_METAL_STONE) { if (final < 0.125) ent->model = Mod_ForName ("models/metlchk1.mdl", true); else if (final < 0.25) ent->model = Mod_ForName ("models/schunk1.mdl", true); else if (final < 0.375) ent->model = Mod_ForName ("models/metlchk2.mdl", true); else if (final < 0.50) ent->model = Mod_ForName ("models/schunk2.mdl", true); else if (final < 0.625) ent->model = Mod_ForName ("models/metlchk3.mdl", true); else if (final < 0.75) ent->model = Mod_ForName ("models/schunk3.mdl", true); else if (final < 0.875) ent->model = Mod_ForName ("models/metlchk4.mdl", true); else ent->model = Mod_ForName ("models/schunk4.mdl", true); } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_METAL_CLOTH) { if (final < 0.14) ent->model = Mod_ForName ("models/metlchk1.mdl", true); else if (final < 0.28) ent->model = Mod_ForName ("models/clthchk1.mdl", true); else if (final < 0.42) ent->model = Mod_ForName ("models/metlchk2.mdl", true); else if (final < 0.56) ent->model = Mod_ForName ("models/clthchk2.mdl", true); else if (final < 0.70) ent->model = Mod_ForName ("models/metlchk3.mdl", true); else if (final < 0.84) ent->model = Mod_ForName ("models/clthchk3.mdl", true); else ent->model = Mod_ForName ("models/metlchk4.mdl", true); } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_ICE) { ent->model = Mod_ForName("models/shard.mdl", true); ent->skinnum = 0; ent->frame = rand() % 2; ent->drawflags |= DRF_TRANSLUCENT|MLS_ABSLIGHT; ent->abslight = 127; } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_METEOR) { ent->model = Mod_ForName("models/tempmetr.mdl", true); ent->skinnum = 0; } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_ACID) { /* no spinning if possible... */ ent->model = Mod_ForName("models/sucwp2p.mdl", true); ent->skinnum = 0; } else if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_GREENFLESH) { /* spider guts */ if (final < 0.33) ent->model = Mod_ForName ("models/sflesh1.mdl", true); else if (final < 0.66) ent->model = Mod_ForName ("models/sflesh2.mdl", true); else ent->model = Mod_ForName ("models/sflesh3.mdl", true); ent->skinnum = 0; } else// if (cl.Effects[idx].ef.Chunk.type == THINGTYPE_GREYSTONE) { if (final < 0.25) ent->model = Mod_ForName ("models/schunk1.mdl", true); else if (final < 0.50) ent->model = Mod_ForName ("models/schunk2.mdl", true); else if (final < 0.75) ent->model = Mod_ForName ("models/schunk3.mdl", true); else ent->model = Mod_ForName ("models/schunk4.mdl", true); ent->skinnum = 0; } } else { ImmediateFree = true; break; } } if (ImmediateFree) { for (i = 0 ; i < cl.Effects[idx].ef.Chunk.numChunks ; i++) { if (cl.Effects[idx].ef.Chunk.entity_index[i] == -1) break; FreeEffectEntity(cl.Effects[idx].ef.Chunk.entity_index[i]); } break; } for (i = 0 ; i < 3 ; i++) { cl.Effects[idx].ef.Chunk.avel[i][0] = (rand() % 850) - 425; cl.Effects[idx].ef.Chunk.avel[i][1] = (rand() % 850) - 425; cl.Effects[idx].ef.Chunk.avel[i][2] = (rand() % 850) - 425; } break; default: Host_Error ("%s: bad type", __thisfunc__); } if (ImmediateFree) { cl.Effects[idx].type = CE_NONE; } } void CL_EndEffect (void) { int idx; idx = MSG_ReadByte(); CL_FreeEffect(idx); } static void CL_LinkEntity (entity_t *ent) { if (cl_numvisedicts < MAX_VISEDICTS) { cl_visedicts[cl_numvisedicts++] = ent; } } void CL_UpdateEffects (void) { int i; int idx, cur_frame; vec3_t mymin, mymax; float frametime; // edict_t test; // trace_t trace; vec3_t org, org2, alldir, snow_org; int x_dir, y_dir; entity_t *ent; float distance, smoketime; if (cls.state == ca_disconnected) return; frametime = cl.time - cl.oldtime; if (!frametime) return; // Con_Printf("Here at %f\n",cl.time); for (idx = 0 ; idx < MAX_EFFECTS ; idx++) { if (!cl.Effects[idx].type) continue; switch (cl.Effects[idx].type) { case CE_RAIN: org[0] = cl.Effects[idx].ef.Rain.min_org[0]; org[1] = cl.Effects[idx].ef.Rain.min_org[1]; org[2] = cl.Effects[idx].ef.Rain.max_org[2]; org2[0] = cl.Effects[idx].ef.Rain.e_size[0]; org2[1] = cl.Effects[idx].ef.Rain.e_size[1]; org2[2] = cl.Effects[idx].ef.Rain.e_size[2]; x_dir = cl.Effects[idx].ef.Rain.dir[0]; y_dir = cl.Effects[idx].ef.Rain.dir[1]; cl.Effects[idx].ef.Rain.next_time += frametime; if (cl.Effects[idx].ef.Rain.next_time >= cl.Effects[idx].ef.Rain.wait) { R_RainEffect(org, org2, x_dir, y_dir, cl.Effects[idx].ef.Rain.color, cl.Effects[idx].ef.Rain.count); cl.Effects[idx].ef.Rain.next_time = 0; } break; case CE_SNOW: VectorCopy(cl.Effects[idx].ef.Rain.min_org, org); VectorCopy(cl.Effects[idx].ef.Rain.max_org, org2); VectorCopy(cl.Effects[idx].ef.Rain.dir, alldir); VectorAdd(org, org2, snow_org); snow_org[0] *= 0.5; snow_org[1] *= 0.5; snow_org[2] *= 0.5; snow_org[2] = r_origin[2]; VectorSubtract(snow_org, r_origin, snow_org); distance = VectorNormalizeFast(snow_org); cl.Effects[idx].ef.Rain.next_time += frametime; /* jfm: fixme, check distance to player first */ if (cl.Effects[idx].ef.Rain.next_time >= 0.10 && distance < 1024) { R_SnowEffect(org, org2, cl.Effects[idx].ef.Rain.flags, alldir, cl.Effects[idx].ef.Rain.count); cl.Effects[idx].ef.Rain.next_time = 0; } break; case CE_FOUNTAIN: mymin[0] = (-3 * cl.Effects[idx].ef.Fountain.vright[0] * cl.Effects[idx].ef.Fountain.movedir[0]) + (-3 * cl.Effects[idx].ef.Fountain.vforward[0] * cl.Effects[idx].ef.Fountain.movedir[1]) + (2 * cl.Effects[idx].ef.Fountain.vup[0] * cl.Effects[idx].ef.Fountain.movedir[2]); mymin[1] = (-3 * cl.Effects[idx].ef.Fountain.vright[1] * cl.Effects[idx].ef.Fountain.movedir[0]) + (-3 * cl.Effects[idx].ef.Fountain.vforward[1] * cl.Effects[idx].ef.Fountain.movedir[1]) + (2 * cl.Effects[idx].ef.Fountain.vup[1] * cl.Effects[idx].ef.Fountain.movedir[2]); mymin[2] = (-3 * cl.Effects[idx].ef.Fountain.vright[2] * cl.Effects[idx].ef.Fountain.movedir[0]) + (-3 * cl.Effects[idx].ef.Fountain.vforward[2] * cl.Effects[idx].ef.Fountain.movedir[1]) + (2 * cl.Effects[idx].ef.Fountain.vup[2] * cl.Effects[idx].ef.Fountain.movedir[2]); mymin[0] *= 15; mymin[1] *= 15; mymin[2] *= 15; mymax[0] = (3 * cl.Effects[idx].ef.Fountain.vright[0] * cl.Effects[idx].ef.Fountain.movedir[0]) + (3 * cl.Effects[idx].ef.Fountain.vforward[0] * cl.Effects[idx].ef.Fountain.movedir[1]) + (10 * cl.Effects[idx].ef.Fountain.vup[0] * cl.Effects[idx].ef.Fountain.movedir[2]); mymax[1] = (3 * cl.Effects[idx].ef.Fountain.vright[1] * cl.Effects[idx].ef.Fountain.movedir[0]) + (3 * cl.Effects[idx].ef.Fountain.vforward[1] * cl.Effects[idx].ef.Fountain.movedir[1]) + (10 * cl.Effects[idx].ef.Fountain.vup[1] * cl.Effects[idx].ef.Fountain.movedir[2]); mymax[2] = (3 * cl.Effects[idx].ef.Fountain.vright[2] * cl.Effects[idx].ef.Fountain.movedir[0]) + (3 * cl.Effects[idx].ef.Fountain.vforward[2] * cl.Effects[idx].ef.Fountain.movedir[1]) + (10 * cl.Effects[idx].ef.Fountain.vup[2] * cl.Effects[idx].ef.Fountain.movedir[2]); mymax[0] *= 15; mymax[1] *= 15; mymax[2] *= 15; R_RunParticleEffect2 (cl.Effects[idx].ef.Fountain.pos, mymin, mymax, cl.Effects[idx].ef.Fountain.color, pt_fastgrav, cl.Effects[idx].ef.Fountain.cnt); /* memset(&test, 0, sizeof(test)); trace = SV_Move (cl.Effects[idx].ef.Fountain.pos, mymin, mymax, mymin, false, &test); Con_Printf("Fraction is %f\n", trace.fraction); */ break; case CE_QUAKE: R_RunQuakeEffect (cl.Effects[idx].ef.Quake.origin, cl.Effects[idx].ef.Quake.radius); break; case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK1: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: case CE_FLAMESTREAM: case CE_ACID_MUZZFL: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: cl.Effects[idx].ef.Smoke.time_amount += frametime; ent = &EffectEntities[cl.Effects[idx].ef.Smoke.entity_index]; smoketime = cl.Effects[idx].ef.Smoke.framelength; if (!smoketime) smoketime = HX_FRAME_TIME; ent->origin[0] += (frametime/smoketime) * cl.Effects[idx].ef.Smoke.velocity[0]; ent->origin[1] += (frametime/smoketime) * cl.Effects[idx].ef.Smoke.velocity[1]; ent->origin[2] += (frametime/smoketime) * cl.Effects[idx].ef.Smoke.velocity[2]; while (cl.Effects[idx].ef.Smoke.time_amount >= smoketime) { ent->frame++; cl.Effects[idx].ef.Smoke.time_amount -= smoketime; } if (ent->frame >= ent->model->numframes) { CL_FreeEffect(idx); } else { CL_LinkEntity(ent); } break; /* Just go through animation and then remove */ case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_FLOOR_EXPLOSION3: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_BRN_BOUNCE: case CE_ACID_HIT: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_LBALL_EXPL: case CE_FBOOM: case CE_BOMB: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: cl.Effects[idx].ef.Smoke.time_amount += frametime; ent = &EffectEntities[cl.Effects[idx].ef.Smoke.entity_index]; if (cl.Effects[idx].type != CE_BG_CIRCLE_EXP) { while (cl.Effects[idx].ef.Smoke.time_amount >= HX_FRAME_TIME) { ent->frame++; cl.Effects[idx].ef.Smoke.time_amount -= HX_FRAME_TIME; } } else { while (cl.Effects[idx].ef.Smoke.time_amount >= HX_FRAME_TIME * 2) { ent->frame++; cl.Effects[idx].ef.Smoke.time_amount -= HX_FRAME_TIME * 2; } } if (ent->frame >= ent->model->numframes) { CL_FreeEffect(idx); } else { CL_LinkEntity(ent); } break; case CE_LSHOCK: ent = &EffectEntities[cl.Effects[idx].ef.Smoke.entity_index]; if (ent->skinnum == 0) ent->skinnum = 1; else if (ent->skinnum == 1) ent->skinnum = 0; ent->scale -= 10; if (ent->scale <= 10) { CL_FreeEffect(idx); } else { CL_LinkEntity(ent); } break; /* Go forward then backward through animation then remove */ case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_RED_FLASH: cl.Effects[idx].ef.Flash.time_amount += frametime; ent = &EffectEntities[cl.Effects[idx].ef.Flash.entity_index]; while (cl.Effects[idx].ef.Flash.time_amount >= HX_FRAME_TIME) { if (!cl.Effects[idx].ef.Flash.reverse) { if (ent->frame >= ent->model->numframes-1) { /* Ran through forward animation */ cl.Effects[idx].ef.Flash.reverse = 1; ent->frame--; } else ent->frame++; } else { ent->frame--; } cl.Effects[idx].ef.Flash.time_amount -= HX_FRAME_TIME; } if ((ent->frame <= 0) && (cl.Effects[idx].ef.Flash.reverse)) { CL_FreeEffect(idx); } else { CL_LinkEntity(ent); } break; case CE_RIDER_DEATH: { float sinval, cosval; cl.Effects[idx].ef.RD.time_amount += frametime; if (cl.Effects[idx].ef.RD.time_amount >= 1) { cl.Effects[idx].ef.RD.stage++; cl.Effects[idx].ef.RD.time_amount -= 1; } VectorCopy(cl.Effects[idx].ef.RD.origin, org); q_sincosrad(cl.Effects[idx].ef.RD.time_amount * 2 * M_PI, &sinval, &cosval); org[0] += sinval * 30; org[1] += cosval * 30; if (cl.Effects[idx].ef.RD.stage <= 6) { // RiderParticle(cl.Effects[idx].ef.RD.stage + 1, cl.Effects[idx].ef.RD.origin); RiderParticle(cl.Effects[idx].ef.RD.stage + 1, org); } else { /* To set the rider's origin point for the particles */ RiderParticle(0, org); if (cl.Effects[idx].ef.RD.stage == 7) { cl.cshifts[CSHIFT_BONUS].destcolor[0] = 255; cl.cshifts[CSHIFT_BONUS].destcolor[1] = 255; cl.cshifts[CSHIFT_BONUS].destcolor[2] = 255; cl.cshifts[CSHIFT_BONUS].percent = 256; } else if (cl.Effects[idx].ef.RD.stage > 13) { // cl.Effects[idx].ef.RD.stage = 0; CL_FreeEffect(idx); } } } break; case CE_GRAVITYWELL: { float sinval, cosval; cl.Effects[idx].ef.RD.time_amount += frametime*2; if (cl.Effects[idx].ef.RD.time_amount >= 1) cl.Effects[idx].ef.RD.time_amount -= 1; VectorCopy(cl.Effects[idx].ef.RD.origin, org); q_sincosrad(cl.Effects[idx].ef.RD.time_amount * 2 * M_PI, &sinval, &cosval); org[0] += sinval * 30; org[1] += cosval * 30; if (cl.Effects[idx].ef.RD.lifetime < cl.time) { CL_FreeEffect(idx); } else { GravityWellParticle(rand() % 8, org, cl.Effects[idx].ef.RD.color); } } break; case CE_TELEPORTERPUFFS: cl.Effects[idx].ef.Teleporter.time_amount += frametime; smoketime = cl.Effects[idx].ef.Teleporter.framelength; ent = &EffectEntities[cl.Effects[idx].ef.Teleporter.entity_index[0]]; while (cl.Effects[idx].ef.Teleporter.time_amount >= HX_FRAME_TIME) { ent->frame++; cl.Effects[idx].ef.Teleporter.time_amount -= HX_FRAME_TIME; } cur_frame = ent->frame; if (cur_frame >= ent->model->numframes) { CL_FreeEffect(idx); break; } for (i = 0 ; i < 8 ; ++i) { ent = &EffectEntities[cl.Effects[idx].ef.Teleporter.entity_index[i]]; ent->origin[0] += (frametime/smoketime) * cl.Effects[idx].ef.Teleporter.velocity[i][0]; ent->origin[1] += (frametime/smoketime) * cl.Effects[idx].ef.Teleporter.velocity[i][1]; ent->origin[2] += (frametime/smoketime) * cl.Effects[idx].ef.Teleporter.velocity[i][2]; ent->frame = cur_frame; CL_LinkEntity(ent); } break; case CE_TELEPORTERBODY: cl.Effects[idx].ef.Teleporter.time_amount += frametime; smoketime = cl.Effects[idx].ef.Teleporter.framelength; ent = &EffectEntities[cl.Effects[idx].ef.Teleporter.entity_index[0]]; while (cl.Effects[idx].ef.Teleporter.time_amount >= HX_FRAME_TIME) { ent->scale -= 15; cl.Effects[idx].ef.Teleporter.time_amount -= HX_FRAME_TIME; } ent = &EffectEntities[cl.Effects[idx].ef.Teleporter.entity_index[0]]; ent->angles[1] += 45; if (ent->scale <= 10) { CL_FreeEffect(idx); } else { CL_LinkEntity(ent); } break; case CE_BONESHARD: case CE_BONESHRAPNEL: cl.Effects[idx].ef.Missile.time_amount += frametime; ent = &EffectEntities[cl.Effects[idx].ef.Missile.entity_index]; // ent->angles[0] = cl.Effects[idx].ef.Missile.angle[0]; // ent->angles[1] = cl.Effects[idx].ef.Missile.angle[1]; // ent->angles[2] = cl.Effects[idx].ef.Missile.angle[2]; ent->angles[0] += frametime * cl.Effects[idx].ef.Missile.avelocity[0]; ent->angles[1] += frametime * cl.Effects[idx].ef.Missile.avelocity[1]; ent->angles[2] += frametime * cl.Effects[idx].ef.Missile.avelocity[2]; ent->origin[0] += frametime * cl.Effects[idx].ef.Missile.velocity[0]; ent->origin[1] += frametime * cl.Effects[idx].ef.Missile.velocity[1]; ent->origin[2] += frametime * cl.Effects[idx].ef.Missile.velocity[2]; CL_LinkEntity(ent); break; case CE_CHUNK: cl.Effects[idx].ef.Chunk.time_amount -= frametime; if (cl.Effects[idx].ef.Chunk.time_amount < 0) { CL_FreeEffect(idx); } else { for (i = 0 ; i < cl.Effects[idx].ef.Chunk.numChunks ; i++) { vec3_t oldorg; mleaf_t *l; int moving = 1; ent = &EffectEntities[cl.Effects[idx].ef.Chunk.entity_index[i]]; VectorCopy(ent->origin, oldorg); ent->origin[0] += frametime * cl.Effects[idx].ef.Chunk.velocity[i][0]; ent->origin[1] += frametime * cl.Effects[idx].ef.Chunk.velocity[i][1]; ent->origin[2] += frametime * cl.Effects[idx].ef.Chunk.velocity[i][2]; l = Mod_PointInLeaf (ent->origin, cl.worldmodel); if (l->contents != CONTENTS_EMPTY) //|| in_solid == true { /* bouncing prolly won't work... */ VectorCopy(oldorg, ent->origin); cl.Effects[idx].ef.Chunk.velocity[i][0] = 0; cl.Effects[idx].ef.Chunk.velocity[i][1] = 0; cl.Effects[idx].ef.Chunk.velocity[i][2] = 0; moving = 0; } else { ent->angles[0] += frametime * cl.Effects[idx].ef.Chunk.avel[i%3][0]; ent->angles[1] += frametime * cl.Effects[idx].ef.Chunk.avel[i%3][1]; ent->angles[2] += frametime * cl.Effects[idx].ef.Chunk.avel[i%3][2]; } if (cl.Effects[idx].ef.Chunk.time_amount < frametime * 3) { /* chunk leaves in 3 frames */ ent->scale *= .7; } CL_LinkEntity(ent); cl.Effects[idx].ef.Chunk.velocity[i][2] -= frametime * 500; /* apply gravity */ switch (cl.Effects[idx].ef.Chunk.type) { case THINGTYPE_GREYSTONE: break; case THINGTYPE_WOOD: break; case THINGTYPE_METAL: break; case THINGTYPE_FLESH: if (moving) R_RocketTrail (oldorg, ent->origin, 17); break; case THINGTYPE_FIRE: break; case THINGTYPE_CLAY: case THINGTYPE_BONE: break; case THINGTYPE_LEAVES: break; case THINGTYPE_HAY: break; case THINGTYPE_BROWNSTONE: break; case THINGTYPE_CLOTH: break; case THINGTYPE_WOOD_LEAF: break; case THINGTYPE_WOOD_METAL: break; case THINGTYPE_WOOD_STONE: break; case THINGTYPE_METAL_STONE: break; case THINGTYPE_METAL_CLOTH: break; case THINGTYPE_WEBS: break; case THINGTYPE_GLASS: break; case THINGTYPE_ICE: if (moving) R_RocketTrail (oldorg, ent->origin, rt_ice); break; case THINGTYPE_CLEARGLASS: break; case THINGTYPE_REDGLASS: break; case THINGTYPE_ACID: if (moving) R_RocketTrail (oldorg, ent->origin, rt_acidball); break; case THINGTYPE_METEOR: R_RocketTrail (oldorg, ent->origin, 1); break; case THINGTYPE_GREENFLESH: if (moving) R_RocketTrail (oldorg, ent->origin, rt_acidball); break; } } } break; } } } //========================================================================== // // NewEffectEntity // //========================================================================== static int NewEffectEntity (void) { entity_t *ent; int counter; if (cl_numvisedicts == MAX_VISEDICTS) return -1; if (EffectEntityCount == MAX_EFFECT_ENTITIES) return -1; for (counter = 0 ; counter < MAX_EFFECT_ENTITIES ; counter++) { if (!EntityUsed[counter]) break; } EntityUsed[counter] = true; EffectEntityCount++; ent = &EffectEntities[counter]; memset(ent, 0, sizeof(*ent)); ent->colormap = vid.colormap; return counter; } static void FreeEffectEntity (int idx) { EntityUsed[idx] = false; EffectEntityCount--; } engine/hexen2/cl_inlude.c000066400000000000000000000122561444734033100156270ustar00rootroot00000000000000/* cl_interlude.c: Setup the Hexen II intermission screen flags. * Only the intermission index is sent by the server, therefore the * rest of the stuff is unfortunately hardcoded here in the engine. * * Copyright (C) 2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" void CL_SetupIntermission (int num) { if (oem.integer && num == 1) cl.intermission = 9; else cl.intermission = num; switch (cl.intermission) { case 1: /* defeated famine: episode 1 (village) to 2 (mazaera) */ cl.completed_time = cl.time; cl.message_index = 1 + 394; cl.intermission_flags = 0; cl.intermission_pic = "gfx/meso.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 2: /* defeated death: episode 2 (mazaera) to 3 (egypt) */ cl.completed_time = cl.time; cl.message_index = 2 + 394; cl.intermission_flags = 0; cl.intermission_pic = "gfx/egypt.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 3: /* defeated pestilence: episode 3 (egypt) to 4 (roman) */ cl.completed_time = cl.time; cl.message_index = 3 + 394; cl.intermission_flags = 0; cl.intermission_pic = "gfx/roman.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 4: /* defeated war: episode 4 (roman) to last (castle) */ cl.completed_time = cl.time; cl.message_index = 4 + 394; cl.intermission_flags = 0; cl.intermission_pic = "gfx/castle.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 5: /* finale for the demo version */ cl.completed_time = cl.time; /* DEMO_MSG_INDEX is 408 for H2, 410 for H2MP strings.txt. * in uHexen2, the demo version isn't allowed in combination * with the mission pack, so we are good with 408. */ cl.message_index = 408; cl.intermission_flags = 0; cl.intermission_pic = "gfx/castle.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 6: /* defeated eidolon: finale, part 1/3 */ cl.completed_time = cl.time; cl.message_index = 6 + 386; cl.intermission_flags = INTERMISSION_PRINT_DELAY|INTERMISSION_PRINT_WHITE|INTERMISSION_PRINT_TOP; cl.intermission_pic = "gfx/end-1.lmp"; cl.lasting_time = 15; cl.intermission_next = 7; break; case 7: /* defeated eidolon: finale, part 2/3 */ cl.completed_time = cl.time; cl.message_index = 7 + 386; cl.intermission_flags = INTERMISSION_PRINT_DELAY|INTERMISSION_PRINT_WHITE|INTERMISSION_PRINT_TOP; cl.intermission_pic = "gfx/end-2.lmp"; cl.lasting_time = 15; cl.intermission_next = 8; break; case 8: /* defeated eidolon: finale, part 2/3 */ cl.completed_time = cl.time; cl.message_index = 8 + 386; cl.intermission_flags = INTERMISSION_PRINT_WHITE|INTERMISSION_PRINT_DELAY|INTERMISSION_PRINT_TOPMOST; cl.intermission_pic = "gfx/end-3.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 9: /* finale for the bundle (oem) version */ cl.completed_time = cl.time; cl.message_index = 391; cl.intermission_flags = INTERMISSION_PRINT_WHITE; cl.intermission_pic = "gfx/castle.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 10: /* defeated praevus: mission pack finale */ cl.completed_time = cl.time; cl.message_index = 538; cl.intermission_flags = 0; cl.intermission_pic = "gfx/mpend.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 11: /* mission pack, episode change to tibet */ cl.completed_time = cl.time; cl.message_index = 545; cl.intermission_flags = 0; cl.intermission_pic = "gfx/mpmid.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 12: /* mission pack, displayed before first map */ /* intermission #12 is only started by the menu system. * it isn't progs controlled. it is activated without * a connection to a server, so it cannot use cl.time. * since the user may just have started the game, we must * manually load strings.txt for the scrolling text. * when the user hits a key, Key_Event () gets us out of * the intermission by running the keep1 map. */ cl.completed_time = realtime; cl.message_index = 561; cl.intermission_flags = INTERMISSION_NOT_CONNECTED|INTERMISSION_NO_MENUS; cl.intermission_pic = "gfx/end-3.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; Host_LoadStrings (); break; default: /* unexpected: */ cl.completed_time = cl.time; cl.message_index = Q_MAXINT; cl.intermission_flags = 0; cl.intermission_pic = NULL; cl.lasting_time = 0; cl.intermission_next = 0; Host_Error("%s: Bad intermission number %d", __thisfunc__, cl.intermission); break; } } engine/hexen2/cl_input.c000066400000000000000000000341511444734033100155040ustar00rootroot00000000000000/* * cl.input.c -- builds an intended movement command to send to the server * * Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. * All rights reserved. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" /* =============================================================================== KEY BUTTONS Continuous button event tracking is complicated by the fact that two different input sources (say, mouse button 1 and the control key) can both press the same button, but the button should only be released when both of the pressing key have been released. When a key event issues a button command (+forward, +attack, etc), it appends its key number as a parameter to the command so it can be matched up with the release. state bit 0 is the current state of the key state bit 1 is edge triggered on the up to down transition state bit 2 is edge triggered on the down to up transition =============================================================================== */ kbutton_t in_mlook, in_klook; kbutton_t in_left, in_right, in_forward, in_back; kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright; kbutton_t in_strafe, in_speed, in_use, in_jump, in_attack; kbutton_t in_up, in_down, in_crouch; kbutton_t in_infoplaque; int in_impulse; qboolean info_up = false; static void KeyDown (kbutton_t *b) { int k; const char *c; c = Cmd_Argv(1); if (c[0]) k = atoi(c); else k = -1; // typed manually at the console for continuous down if (k == b->down[0] || k == b->down[1]) return; // repeating key if (!b->down[0]) b->down[0] = k; else if (!b->down[1]) b->down[1] = k; else { Con_Printf ("Three keys down for a button!\n"); return; } if (b->state & 1) return; // still down b->state |= 1 + 2; // down + impulse down } static void KeyUp (kbutton_t *b) { int k; const char *c; c = Cmd_Argv(1); if (c[0]) { k = atoi(c); } else { // typed manually at the console, assume for unsticking, so clear all b->down[0] = b->down[1] = 0; b->state = 4; // impulse up return; } if (b->down[0] == k) b->down[0] = 0; else if (b->down[1] == k) b->down[1] = 0; else return; // key up without coresponding down (menu pass through) if (b->down[0] || b->down[1]) return; // some other key is still holding it down if (!(b->state & 1)) return; // still up (this should not happen) b->state &= ~1; // now up b->state |= 4; // impulse up } static void IN_KLookDown (void) { KeyDown(&in_klook); } static void IN_KLookUp (void) { KeyUp(&in_klook); } static void IN_MLookDown (void) { KeyDown(&in_mlook); } static void IN_MLookUp (void) { KeyUp(&in_mlook); if (!(in_mlook.state & 1) && lookspring.integer) V_StartPitchDrift(); } static void IN_UpDown (void) { KeyDown(&in_up); } static void IN_UpUp (void) { KeyUp(&in_up); } static void IN_DownDown (void) { KeyDown(&in_down); } static void IN_DownUp (void) { KeyUp(&in_down); } static void IN_LeftDown (void) { KeyDown(&in_left); } static void IN_LeftUp (void) { KeyUp(&in_left); } static void IN_RightDown (void) { KeyDown(&in_right); } static void IN_RightUp (void) { KeyUp(&in_right); } static void IN_ForwardDown (void) { KeyDown(&in_forward); } static void IN_ForwardUp (void) { KeyUp(&in_forward); } static void IN_BackDown (void) { KeyDown(&in_back); } static void IN_BackUp (void) { KeyUp(&in_back); } static void IN_LookupDown (void) { KeyDown(&in_lookup); } static void IN_LookupUp (void) { KeyUp(&in_lookup); } static void IN_LookdownDown (void) { KeyDown(&in_lookdown); } static void IN_LookdownUp (void) { KeyUp(&in_lookdown); } static void IN_MoveleftDown (void) { KeyDown(&in_moveleft); } static void IN_MoveleftUp (void) { KeyUp(&in_moveleft); } static void IN_MoverightDown (void) { KeyDown(&in_moveright); } static void IN_MoverightUp (void) { KeyUp(&in_moveright); } static void IN_SpeedDown (void) { KeyDown(&in_speed); } static void IN_SpeedUp (void) { KeyUp(&in_speed); } static void IN_StrafeDown (void) { KeyDown(&in_strafe); } static void IN_StrafeUp (void) { KeyUp(&in_strafe); } static void IN_AttackDown (void) { KeyDown(&in_attack); } static void IN_AttackUp (void) { KeyUp(&in_attack); } static void IN_UseDown (void) { KeyDown(&in_use); } static void IN_UseUp (void) { KeyUp(&in_use); } static void IN_JumpDown (void) { KeyDown(&in_jump); } static void IN_JumpUp (void) { KeyUp(&in_jump); } static void IN_Impulse (void) { in_impulse = atoi(Cmd_Argv(1)); } static void IN_CrouchDown (void) { if (Key_GetDest() == key_game) { // int state = in_crouch.state; KeyDown(&in_crouch); // if (!(state & 1) && (in_crouch.state & 1)) // in_impulse = 22; } } static void IN_CrouchUp (void) { // if (Key_GetDest() == key_game) // { // int state = in_crouch.state; KeyUp(&in_crouch); // if ((state & 1) && !(in_crouch.state & 1)) // in_impulse = 22; // } } static void IN_infoPlaqueUp (void) { // lower the plaque // if (Key_GetDest() == key_game) // { info_up = false; KeyUp(&in_infoplaque); // } } static void IN_infoPlaqueDown (void) { // show the plaque if (Key_GetDest() == key_game) { info_up = true; KeyDown(&in_infoplaque); } } /* =============== CL_KeyState Returns 0.25 if a key was pressed and released during the frame, 0.5 if it was pressed and held 0 if held then released, and 1.0 if held for the entire time =============== */ static float CL_KeyState (kbutton_t *key) { float val; qboolean impulsedown, impulseup, down; impulsedown = key->state & 2; impulseup = key->state & 4; down = key->state & 1; val = 0; if (impulsedown && !impulseup) { if (down) val = 0.5; // pressed and held this frame else val = 0; } if (impulseup && !impulsedown) { if (down) val = 0; else val = 0; // released this frame } if (!impulsedown && !impulseup) { if (down) val = 1.0; // held the entire frame else val = 0; // up the entire frame } if (impulsedown && impulseup) { if (down) val = 0.75; // released and re-pressed this frame else val = 0.25; // pressed and released this frame } key->state &= 1; // clear impulses return val; } //========================================================================== cvar_t cl_upspeed = {"cl_upspeed", "200", CVAR_NONE}; cvar_t cl_forwardspeed = {"cl_forwardspeed", "200", CVAR_ARCHIVE}; cvar_t cl_backspeed = {"cl_backspeed", "200", CVAR_ARCHIVE}; cvar_t cl_sidespeed = {"cl_sidespeed", "225", CVAR_NONE}; cvar_t cl_movespeedkey = {"cl_movespeedkey", "2.0", CVAR_NONE}; cvar_t cl_yawspeed = {"cl_yawspeed", "140", CVAR_NONE}; cvar_t cl_pitchspeed = {"cl_pitchspeed", "150", CVAR_NONE}; cvar_t cl_anglespeedkey = {"cl_anglespeedkey", "1.5", CVAR_NONE}; /* ================ CL_AdjustAngles Moves the local angle positions ================ */ static void CL_AdjustAngles (void) { float speed; float up, down; if (in_speed.state & 1) speed = host_frametime * cl_anglespeedkey.value; else speed = host_frametime; if (!(in_strafe.state & 1)) { cl.viewangles[YAW] -= speed*cl_yawspeed.value*CL_KeyState (&in_right); cl.viewangles[YAW] += speed*cl_yawspeed.value*CL_KeyState (&in_left); cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]); } if (in_klook.state & 1) { V_StopPitchDrift (); cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * CL_KeyState (&in_forward); cl.viewangles[PITCH] += speed*cl_pitchspeed.value * CL_KeyState (&in_back); } // FIXME: This is a cheap way of doing this, it belongs in V_CalcViewRoll // but I don't see where I can get the yaw velocity. cl.idealroll = 0; if (cl.v.movetype == MOVETYPE_FLY) { if (CL_KeyState (&in_left) != 0) cl.idealroll = -10; else if (CL_KeyState (&in_right) != 0) cl.idealroll = 10; } up = CL_KeyState (&in_lookup); down = CL_KeyState(&in_lookdown); cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * up; cl.viewangles[PITCH] += speed*cl_pitchspeed.value * down; if (up || down) V_StopPitchDrift (); if (cl.viewangles[PITCH] > 80) cl.viewangles[PITCH] = 80; if (cl.viewangles[PITCH] < -70) cl.viewangles[PITCH] = -70; if (cl.viewangles[ROLL] > 50) cl.viewangles[ROLL] = 50; if (cl.viewangles[ROLL] < -50) cl.viewangles[ROLL] = -50; } /* ================ CL_BaseMove Send the intended movement message to the server ================ */ void CL_BaseMove (usercmd_t *cmd) { if (cls.signon != SIGNONS) return; if (cl.v.cameramode) // stuck in a different camera so don't move { memset (cmd, 0, sizeof(*cmd)); return; } CL_AdjustAngles (); memset (cmd, 0, sizeof(*cmd)); if (in_strafe.state & 1) { // cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_right); // cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_left); cmd->sidemove += 225 * CL_KeyState (&in_right); cmd->sidemove -= 225 * CL_KeyState (&in_left); } // cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_moveright); // cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_moveleft); cmd->sidemove += 225 * CL_KeyState (&in_moveright); cmd->sidemove -= 225 * CL_KeyState (&in_moveleft); cmd->upmove += cl_upspeed.value * CL_KeyState (&in_up); cmd->upmove -= cl_upspeed.value * CL_KeyState (&in_down); if (! (in_klook.state & 1)) { // cmd->forwardmove += cl_forwardspeed.value * CL_KeyState (&in_forward); cmd->forwardmove += 200 * CL_KeyState (&in_forward); // cmd->forwardmove -= cl_backspeed.value * CL_KeyState (&in_back); cmd->forwardmove -= 200 * CL_KeyState (&in_back); } // adjust for speed key, but not if "always run" has been chosen // speed key now acts as slow key when always run is chosen - OS // if ( ((cl_forwardspeed.value > 200) ||(in_speed.state & 1)) if ( ((cl_forwardspeed.value > 200) ^ (in_speed.state & 1)) && (cl.v.hasted <= 1) ) { cmd->forwardmove *= cl_movespeedkey.value; cmd->sidemove *= cl_movespeedkey.value; cmd->upmove *= cl_movespeedkey.value; } // Hasted player? if (cl.v.hasted) { cmd->forwardmove = cmd->forwardmove * cl.v.hasted; cmd->sidemove = cmd->sidemove * cl.v.hasted; cmd->upmove = cmd->upmove * cl.v.hasted; } cmd->lightlevel = cl.light_level; } /* ============== CL_SendMove ============== */ void CL_SendMove (const usercmd_t *cmd) { int i, bits; sizebuf_t buf; byte data[128]; SZ_Init (&buf, data, sizeof(data)); cl.cmd = *cmd; // send the movement message MSG_WriteByte (&buf, clc_frame); MSG_WriteByte (&buf, cl.reference_frame); MSG_WriteByte (&buf, cl.current_sequence); MSG_WriteByte (&buf, clc_move); MSG_WriteFloat (&buf, cl.mtime[0]); // so server can get ping times for (i = 0; i < 3; i++) MSG_WriteAngle (&buf, cl.viewangles[i]); MSG_WriteShort (&buf, cmd->forwardmove); MSG_WriteShort (&buf, cmd->sidemove); MSG_WriteShort (&buf, cmd->upmove); // send button bits bits = 0; if (in_attack.state & 3) bits |= 1; in_attack.state &= ~2; if (in_jump.state & 3) bits |= 2; in_jump.state &= ~2; if (in_crouch.state & 1) bits |= 4; MSG_WriteByte (&buf, bits); MSG_WriteByte (&buf, in_impulse); in_impulse = 0; // light level MSG_WriteByte (&buf, cmd->lightlevel); // deliver the message if (cls.demoplayback) return; // always dump the first two message, because it may // contain leftover inputs from the last level if (++cl.movemessages <= 2) return; if (NET_SendUnreliableMessage (cls.netcon, &buf) == -1) { Con_Printf ("%s: lost server connection\n", __thisfunc__); CL_Disconnect (); } } /* ============ CL_InitInput ============ */ void CL_InitInput (void) { Cmd_AddCommand ("+moveup",IN_UpDown); Cmd_AddCommand ("-moveup",IN_UpUp); Cmd_AddCommand ("+movedown",IN_DownDown); Cmd_AddCommand ("-movedown",IN_DownUp); Cmd_AddCommand ("+left",IN_LeftDown); Cmd_AddCommand ("-left",IN_LeftUp); Cmd_AddCommand ("+right",IN_RightDown); Cmd_AddCommand ("-right",IN_RightUp); Cmd_AddCommand ("+forward",IN_ForwardDown); Cmd_AddCommand ("-forward",IN_ForwardUp); Cmd_AddCommand ("+back",IN_BackDown); Cmd_AddCommand ("-back",IN_BackUp); Cmd_AddCommand ("+lookup", IN_LookupDown); Cmd_AddCommand ("-lookup", IN_LookupUp); Cmd_AddCommand ("+lookdown", IN_LookdownDown); Cmd_AddCommand ("-lookdown", IN_LookdownUp); Cmd_AddCommand ("+strafe", IN_StrafeDown); Cmd_AddCommand ("-strafe", IN_StrafeUp); Cmd_AddCommand ("+moveleft", IN_MoveleftDown); Cmd_AddCommand ("-moveleft", IN_MoveleftUp); Cmd_AddCommand ("+moveright", IN_MoverightDown); Cmd_AddCommand ("-moveright", IN_MoverightUp); Cmd_AddCommand ("+speed", IN_SpeedDown); Cmd_AddCommand ("-speed", IN_SpeedUp); Cmd_AddCommand ("+attack", IN_AttackDown); Cmd_AddCommand ("-attack", IN_AttackUp); Cmd_AddCommand ("+use", IN_UseDown); Cmd_AddCommand ("-use", IN_UseUp); Cmd_AddCommand ("+jump", IN_JumpDown); Cmd_AddCommand ("-jump", IN_JumpUp); Cmd_AddCommand ("impulse", IN_Impulse); Cmd_AddCommand ("+klook", IN_KLookDown); Cmd_AddCommand ("-klook", IN_KLookUp); Cmd_AddCommand ("+mlook", IN_MLookDown); Cmd_AddCommand ("-mlook", IN_MLookUp); Cmd_AddCommand ("+crouch", IN_CrouchDown); Cmd_AddCommand ("-crouch", IN_CrouchUp); Cmd_AddCommand ("+infoplaque", IN_infoPlaqueDown); Cmd_AddCommand ("-infoplaque", IN_infoPlaqueUp); } /* ============ CL_ClearStates ============ */ #if 0 void CL_ClearStates (void) { in_mlook.state = 0; in_klook.state = 0; in_left.state = 0; in_right.state = 0; in_forward.state = 0; in_back.state = 0; in_lookup.state = 0; in_lookdown.state = 0; in_moveleft.state = 0; in_moveright.state = 0; in_strafe.state = 0; in_speed.state = 0; in_use.state = 0; in_jump.state = 0; in_attack.state = 0; in_up.state = 0; in_down.state = 0; in_crouch.state = 0; } #endif engine/hexen2/cl_main.c000066400000000000000000000530771444734033100153010ustar00rootroot00000000000000/* * cl_main.c -- hexen2 client main loop * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "bgmusic.h" #include "cdaudio.h" // we need to declare some mouse variables here, because the menu system // references them even when on a unix system. // these two are not intended to be set directly cvar_t cl_name = {"_cl_name", "player", CVAR_ARCHIVE}; cvar_t cl_color = {"_cl_color", "0", CVAR_ARCHIVE}; cvar_t cl_playerclass = {"_cl_playerclass", "1", CVAR_ARCHIVE}; cvar_t cl_shownet = {"cl_shownet", "0", CVAR_NONE}; // can be 0, 1, or 2 cvar_t cl_nolerp = {"cl_nolerp", "0", CVAR_NONE}; cvar_t cfg_unbindall = {"cfg_unbindall", "1", CVAR_ARCHIVE}; cvar_t lookspring = {"lookspring", "0", CVAR_ARCHIVE}; cvar_t lookstrafe = {"lookstrafe", "0", CVAR_ARCHIVE}; cvar_t sensitivity = {"sensitivity", "3", CVAR_ARCHIVE}; cvar_t mwheelthreshold = {"mwheelthreshold", "120", CVAR_ARCHIVE}; cvar_t m_pitch = {"m_pitch", "0.022", CVAR_ARCHIVE}; cvar_t m_yaw = {"m_yaw", "0.022", CVAR_ARCHIVE}; cvar_t m_forward = {"m_forward", "1", CVAR_ARCHIVE}; cvar_t m_side = {"m_side", "0.8", CVAR_ARCHIVE}; client_static_t cls; client_state_t cl; // FIXME: put these on hunk? efrag_t cl_efrags[MAX_EFRAGS]; entity_t cl_entities[MAX_EDICTS]; entity_t cl_static_entities[MAX_STATIC_ENTITIES]; lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; dlight_t cl_dlights[MAX_DLIGHTS]; int cl_numvisedicts; entity_t *cl_visedicts[MAX_VISEDICTS]; /* ===================== CL_ClearState ===================== */ void CL_ClearState (void) { int i; if (!sv.active) Host_ClearMemory (); // wipe the entire cl structure memset (&cl, 0, sizeof(cl)); SZ_Clear (&cls.message); // clear other arrays memset (cl_efrags, 0, sizeof(cl_efrags)); memset (cl_entities, 0, sizeof(cl_entities)); memset (cl_dlights, 0, sizeof(cl_dlights)); memset (cl_lightstyle, 0, sizeof(cl_lightstyle)); CL_ClearTEnts(); CL_ClearEffects(); // allocate the efrags and chain together into a free list cl.free_efrags = cl_efrags; for (i = 0; i < MAX_EFRAGS-1; i++) cl.free_efrags[i].entnext = &cl.free_efrags[i+1]; cl.free_efrags[i].entnext = NULL; cl.current_frame = cl.current_sequence = 99; cl.reference_frame = cl.last_frame = cl.last_sequence = 199; cl.need_build = 2; SCR_SetPlaqueMessage(""); SB_InvReset(); } /* ===================== CL_Disconnect Sends a disconnect message to the server This is also called on Host_Error, so it shouldn't cause any errors ===================== */ void CL_Disconnect (void) { // don't get stuck in chat mode if (Key_GetDest() == key_message) Key_EndChat (); // reset any active palette shift memset (cl.cshifts, 0, sizeof(cl.cshifts)); //jfm: need to clear parts because some now check world R_ClearParticles (); // stop sounds (especially looping!) S_StopAllSounds (true); BGM_Stop(); CDAudio_Stop(); loading_stage = 0; // bring the console down and fade the colors back to normal // SCR_BringDownConsole (); // if running a local server, shut it down if (cls.demoplayback) { CL_StopPlayback (); } else if (cls.state == ca_connected) { if (cls.demorecording) CL_Stop_f (); Con_DPrintf ("Sending clc_disconnect\n"); SZ_Clear (&cls.message); MSG_WriteByte (&cls.message, clc_disconnect); NET_SendUnreliableMessage (cls.netcon, &cls.message); SZ_Clear (&cls.message); NET_Close (cls.netcon); cls.state = ca_disconnected; if (sv.active) Host_ShutdownServer(false); Host_RemoveGIPFiles(NULL); } cls.demoplayback = cls.timedemo = false; cls.signon = 0; cl.intermission = 0; } void CL_Disconnect_f (void) { CL_Disconnect (); if (sv.active) Host_ShutdownServer (false); } /* ===================== CL_EstablishConnection Host should be either "local" or a net address to be passed on ===================== */ void CL_EstablishConnection (const char *host) { if (cls.state == ca_dedicated) return; if (cls.demoplayback) return; CL_Disconnect (); cls.netcon = NET_Connect (host); if (!cls.netcon) Host_Error ("%s: connect failed", __thisfunc__); Con_DPrintf ("%s: connected to %s\n", __thisfunc__, host); cls.demonum = -1; // not in the demo loop now cls.state = ca_connected; cls.signon = 0; // need all the signon messages before playing MSG_WriteByte (&cls.message, clc_nop); // fixes connection getting stuck after displaying // the "Connection accepted" msg.. } /* ===================== CL_SignonReply An svc_signonnum has been received, perform a client side setup ===================== */ void CL_SignonReply (void) { char str[8192]; Con_DPrintf ("%s: %i\n", __thisfunc__, cls.signon); switch (cls.signon) { case 1: MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, "prespawn"); break; case 2: MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, va("name \"%s\"\n", cl_name.string)); MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, va("playerclass %i\n", cl_playerclass.integer)); MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, va("color %i %i\n", cl_color.integer >> 4, cl_color.integer & 15)); MSG_WriteByte (&cls.message, clc_stringcmd); q_snprintf (str, sizeof(str), "spawn %s", cls.spawnparms); MSG_WriteString (&cls.message, str); break; case 3: MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, "begin"); Cache_Report (); // print remaining memory break; case 4: SCR_EndLoadingPlaque (); // allow normal screen updates break; } } /* ===================== CL_NextDemo Called to play the next demo in the demo loop ===================== */ void CL_NextDemo (void) { char str[1024]; if (cls.demonum == -1) return; // don't play demos if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS) { cls.demonum = 0; if (!cls.demos[cls.demonum][0]) { Con_Printf ("No demos listed with startdemos\n"); cls.demonum = -1; CL_Disconnect(); return; } } SCR_BeginLoadingPlaque (); q_snprintf (str, sizeof(str),"playdemo %s\n", cls.demos[cls.demonum]); Cbuf_InsertText (str); cls.demonum++; } /* ============== CL_PrintEntities_f ============== */ static void CL_PrintEntities_f (void) { entity_t *ent; int i; if (cls.state != ca_connected) return; for (i = 0, ent = cl_entities; i < cl.num_entities; i++, ent++) { Con_Printf ("%3i:",i); if (!ent->model) { Con_Printf ("EMPTY\n"); continue; } Con_Printf ("%s:%2i (%5.1f,%5.1f,%5.1f) [%5.1f %5.1f %5.1f]\n", ent->model->name, ent->frame, ent->origin[0], ent->origin[1], ent->origin[2], ent->angles[0], ent->angles[1], ent->angles[2]); } } /* ============= CL_Viewpos_f -- display client's position and angles from FitzQuake ============= */ void CL_Viewpos_f (void) { if (cls.state != ca_connected) return; #if 0 //camera position Con_Printf ("Viewpos: (%i %i %i) %i %i %i\n", (int)r_refdef.vieworg[0], (int)r_refdef.vieworg[1], (int)r_refdef.vieworg[2], (int)r_refdef.viewangles[PITCH], (int)r_refdef.viewangles[YAW], (int)r_refdef.viewangles[ROLL]); #else //player position Con_Printf ("Viewpos: (%i %i %i) %i %i %i\n", (int)cl_entities[cl.viewentity].origin[0], (int)cl_entities[cl.viewentity].origin[1], (int)cl_entities[cl.viewentity].origin[2], (int)cl.viewangles[PITCH], (int)cl.viewangles[YAW], (int)cl.viewangles[ROLL]); #endif } /* =============== SetPal Debugging tool, just flashes the screen =============== */ static void SetPal (int i) { #if 0 static int old; byte pal[768]; int c; if (i == old) return; old = i; if (i == 0) VID_SetPalette (host_basepal); else if (i == 1) { for (c = 0; c < 768; c += 3) { pal[c] = 0; pal[c+1] = 255; pal[c+2] = 0; } VID_SetPalette (pal); } else { for (c = 0; c < 768; c += 3) { pal[c] = 0; pal[c+1] = 0; pal[c+2] = 255; } VID_SetPalette (pal); } #endif } /* =============== CL_AllocDlight =============== */ dlight_t *CL_AllocDlight (int key) { int i; dlight_t *dl; // first look for an exact key match dl = cl_dlights; if (key) { for (i = 0; i < MAX_DLIGHTS; i++, dl++) { if (dl->key == key) goto done; } } // then look for anything else dl = cl_dlights; for (i = 0; i < MAX_DLIGHTS; i++, dl++) { if (dl->die < cl.time) goto done; } dl = &cl_dlights[0]; done: memset (dl, 0, sizeof(*dl)); dl->key = key; dl->color[0] = dl->color[1] = dl->color[2] = dl->color[3] = 1.0; return dl; } /* =============== CL_DecayLights =============== */ void CL_DecayLights (void) { dlight_t *dl; float time; int i; time = cl.time - cl.oldtime; dl = cl_dlights; for (i = 0; i < MAX_DLIGHTS; i++, dl++) { if (dl->die < cl.time || !dl->radius) continue; if (dl->radius > 0) { dl->radius -= time*dl->decay; if (dl->radius < 0) dl->radius = 0; } else { dl->radius += time*dl->decay; if (dl->radius > 0) dl->radius = 0; } } } /* =============== CL_LerpPoint Determines the fraction between the last two messages that the objects should be put at. =============== */ static float CL_LerpPoint (void) { float f, frac; f = cl.mtime[0] - cl.mtime[1]; if (!f || cl_nolerp.integer || cls.timedemo || sv.active) { cl.time = cl.mtime[0]; return 1; } if (f > 0.1) { // dropped packet, or start of demo cl.mtime[1] = cl.mtime[0] - 0.1; f = 0.1; } frac = (cl.time - cl.mtime[1]) / f; //Con_Printf ("frac: %f\n",frac); if (frac < 0) { if (frac < -0.01) { SetPal(1); cl.time = cl.mtime[1]; // Con_Printf ("low frac\n"); } frac = 0; } else if (frac > 1) { if (frac > 1.01) { SetPal(2); cl.time = cl.mtime[0]; // Con_Printf ("high frac\n"); } frac = 1; } else SetPal(0); return frac; } /* =============== CL_RelinkEntities =============== */ static void CL_RelinkEntities (void) { entity_t *ent; int i, j; float frac, f, d; vec3_t delta; vec3_t oldorg; dlight_t *dl; // determine partial update time frac = CL_LerpPoint (); cl_numvisedicts = 0; // interpolate player info for (i = 0; i < 3; i++) cl.velocity[i] = cl.mvelocity[1][i] + frac * (cl.mvelocity[0][i] - cl.mvelocity[1][i]); if (cls.demoplayback && !intro_playing) { // interpolate the angles for (j = 0; j < 3; j++) { d = cl.mviewangles[0][j] - cl.mviewangles[1][j]; if (d > 180) d -= 360; else if (d < -180) d += 360; cl.viewangles[j] = cl.mviewangles[1][j] + frac*d; } } // start on the entity after the world for (i = 1, ent = cl_entities+1; i < cl.num_entities; i++, ent++) { if (!ent->model) { // empty slot if (ent->forcelink) R_RemoveEfrags (ent); // just became empty continue; } // if the object wasn't included in the last packet, remove it if (ent->msgtime != cl.mtime[0] && !(ent->baseline.flags & BE_ON)) { ent->model = NULL; continue; } VectorCopy (ent->origin, oldorg); if (ent->forcelink || ent->msgtime != cl.mtime[0]) { // the entity was not updated in the last message // so move to the final spot VectorCopy (ent->msg_origins[0], ent->origin); VectorCopy (ent->msg_angles[0], ent->angles); } else { // if the delta is large, assume a teleport and don't lerp f = frac; for (j = 0; j < 3; j++) { delta[j] = ent->msg_origins[0][j] - ent->msg_origins[1][j]; if (delta[j] > 100 || delta[j] < -100) f = 1; // assume a teleportation, not a motion } // interpolate the origin and angles for (j = 0; j < 3; j++) { ent->origin[j] = ent->msg_origins[1][j] + f*delta[j]; d = ent->msg_angles[0][j] - ent->msg_angles[1][j]; if (d > 180) d -= 360; else if (d < -180) d += 360; ent->angles[j] = ent->msg_angles[1][j] + f*d; } } // if (ent->effects & EF_BRIGHTFIELD); // R_EntityParticles (ent); if (ent->effects & EF_DARKFIELD) R_DarkFieldParticles (ent); if (ent->effects & EF_MUZZLEFLASH) { vec3_t fv, rv, uv; dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->origin[2] += 16; AngleVectors (ent->angles, fv, rv, uv); VectorMA (dl->origin, 18, fv, dl->origin); dl->radius = 200 + (rand() & 31); dl->minlight = 32; dl->die = cl.time + 0.1; # ifdef GLQUAKE if (gl_colored_dynamic_lights.integer) { // Make the dynamic light yellow dl->color[0] = 1.0; dl->color[1] = 1.0; dl->color[2] = 0.5; dl->color[3] = 0.7; } # endif } if (ent->effects & EF_BRIGHTLIGHT) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->origin[2] += 16; dl->radius = 400 + (rand() & 31); dl->die = cl.time + 0.001; # ifdef GLQUAKE if (gl_colored_dynamic_lights.integer) { dl->color[0] = 0.8; dl->color[1] = 0.8; dl->color[2] = 1.0; dl->color[3] = 0.7; } # endif } if (ent->effects & EF_DIMLIGHT) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200 + (rand() & 31); dl->die = cl.time + 0.001; # ifdef GLQUAKE if (gl_colored_dynamic_lights.integer) { dl->color[0] = 0.8; dl->color[1] = 0.6; dl->color[2] = 0.2; dl->color[3] = 0.7; } # endif } if (ent->effects & EF_DARKLIGHT) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200.0 + (rand() & 31); dl->die = cl.time + 0.001; dl->dark = true; } if (ent->effects & EF_LIGHT) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200; dl->die = cl.time + 0.001; # ifdef GLQUAKE if (gl_colored_dynamic_lights.integer) { dl->color[0] = 0.8; dl->color[1] = 0.4; dl->color[2] = 0.2; dl->color[3] = 0.7; } # endif } if (ent->model->flags & EF_GIB) R_RocketTrail (oldorg, ent->origin, 2); else if (ent->model->flags & EF_ZOMGIB) R_RocketTrail (oldorg, ent->origin, 4); else if (ent->model->flags & EF_BLOODSHOT) R_RocketTrail (oldorg, ent->origin, 17); else if (ent->model->flags & EF_TRACER) R_RocketTrail (oldorg, ent->origin, 3); else if (ent->model->flags & EF_TRACER2) R_RocketTrail (oldorg, ent->origin, 5); else if (ent->model->flags & EF_ROCKET) { R_RocketTrail (oldorg, ent->origin, 0); /* dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200; dl->die = cl.time + 0.01; */ } else if (ent->model->flags & EF_FIREBALL) { R_RocketTrail (oldorg, ent->origin, rt_fireball); dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 120 - (rand() % 20); dl->die = cl.time + 0.01; # ifdef GLQUAKE if (gl_colored_dynamic_lights.integer) { dl->color[0] = 0.8; dl->color[1] = 0.2; dl->color[2] = 0.2; dl->color[3] = 0.7; } # endif } else if (ent->model->flags & EF_ACIDBALL) { R_RocketTrail (oldorg, ent->origin, rt_acidball); dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 120 - (rand() % 20); dl->die = cl.time + 0.01; # ifdef GLQUAKE if (gl_colored_dynamic_lights.integer) { // Make the dynamic light green dl->color[0] = 0.2; dl->color[1] = 0.8; dl->color[2] = 0.2; dl->color[3] = 0.7; } # endif } else if (ent->model->flags & EF_ICE) { R_RocketTrail (oldorg, ent->origin, rt_ice); } else if (ent->model->flags & EF_SPIT) { R_RocketTrail (oldorg, ent->origin, rt_spit); dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = -120 - (rand() % 20); dl->die = cl.time + 0.05; # ifdef GLQUAKE if (gl_colored_dynamic_lights.integer) { // Make the dynamic light green dl->color[0] = 0.2; dl->color[1] = 0.6; dl->color[2] = 0.2; dl->color[3] = 0.7; } # endif } else if (ent->model->flags & EF_SPELL) { R_RocketTrail (oldorg, ent->origin, rt_spell); } else if (ent->model->flags & EF_GRENADE) R_RocketTrail (oldorg, ent->origin, 1); else if (ent->model->flags & EF_TRACER3) R_RocketTrail (oldorg, ent->origin, 6); else if (ent->model->flags & EF_VORP_MISSILE) { R_RocketTrail (oldorg, ent->origin, rt_vorpal); # ifdef GLQUAKE // extra dynamic lights if (gl_extra_dynamic_lights.integer) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 240 - (rand() % 20); dl->die = cl.time + 0.01; if (gl_colored_dynamic_lights.integer) { // Make the dynamic light blue dl->color[0] = 0.3; dl->color[1] = 0.3; dl->color[2] = 0.8; dl->color[3] = 0.7; } } # endif } else if (ent->model->flags & EF_SET_STAFF) { R_RocketTrail (oldorg, ent->origin,rt_setstaff); } else if (ent->model->flags & EF_MAGICMISSILE) { if ((rand() & 3) < 1) R_RocketTrail (oldorg, ent->origin, rt_magicmissile); # ifdef GLQUAKE // extra dynamic lights if (gl_extra_dynamic_lights.integer) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 240 - (rand() % 20); dl->die = cl.time + 0.01; if (gl_colored_dynamic_lights.integer) { // Make the dynamic light blue dl->color[0] = 0.1; dl->color[1] = 0.1; dl->color[2] = 0.8; dl->color[3] = 0.7; } } # endif } else if (ent->model->flags & EF_BONESHARD) { R_RocketTrail (oldorg, ent->origin, rt_boneshard); } else if (ent->model->flags & EF_SCARAB) { R_RocketTrail (oldorg, ent->origin, rt_scarab); # ifdef GLQUAKE // extra dynamic lights if (gl_extra_dynamic_lights.integer) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 240 - (rand() % 20); dl->die = cl.time + 0.01; if (gl_colored_dynamic_lights.integer) { // Make the dynamic light orange dl->color[0] = 0.9; dl->color[1] = 0.6; dl->color[2] = 0.1; dl->color[3] = 0.7; } } # endif } ent->forcelink = false; if (i == cl.viewentity && !chase_active.integer) continue; if (ent->effects & EF_NODRAW) continue; if (cl_numvisedicts < MAX_VISEDICTS) { cl_visedicts[cl_numvisedicts] = ent; cl_numvisedicts++; } } } /* =============== CL_ReadFromServer Read all incoming data from the server =============== */ int CL_ReadFromServer (void) { int ret; cl.oldtime = cl.time; cl.time += host_frametime; do { ret = CL_GetMessage (); if (ret == -1) Host_Error ("%s: lost server connection", __thisfunc__); if (!ret) break; cl.last_received_message = realtime; CL_ParseServerMessage (); } while (ret && cls.state == ca_connected); if (cl_shownet.integer) Con_Printf ("\n"); CL_RelinkEntities (); CL_UpdateTEnts (); // bring the links up to date return 0; } /* ================= CL_SendCmd ================= */ void CL_SendCmd (void) { usercmd_t cmd; if (cls.state != ca_connected) return; if (cls.signon == SIGNONS) { // get basic movement from keyboard CL_BaseMove (&cmd); // allow mice or other external controllers to add to the move IN_Move (&cmd); // send the unreliable message CL_SendMove (&cmd); } if (cls.demoplayback) { SZ_Clear (&cls.message); return; } // send the reliable message if (!cls.message.cursize) return; // no message at all if (!NET_CanSendMessage (cls.netcon)) { Con_DPrintf ("%s: can't send\n", __thisfunc__); return; } if (NET_SendMessage (cls.netcon, &cls.message) == -1) Host_Error ("%s: lost server connection", __thisfunc__); SZ_Clear (&cls.message); } static void CL_Sensitivity_save_f (void) { static float save_sensitivity = 3; if (Cmd_Argc() != 2) { Con_Printf ("sensitivity_save \n"); } else if (q_strcasecmp(Cmd_Argv(1),"save") == 0) { save_sensitivity = sensitivity.value; } else if (q_strcasecmp(Cmd_Argv(1),"restore") == 0) { Cvar_SetValueQuick (&sensitivity, save_sensitivity); } } /* ================= CL_Init ================= */ void CL_Init (void) { SZ_Init (&cls.message, NULL, 1024); CL_InitInput (); CL_InitTEnts (); CL_InitEffects(); // // register our commands // Cvar_RegisterVariable (&cl_name); Cvar_RegisterVariable (&cl_color); Cvar_RegisterVariable (&cl_playerclass); Cvar_RegisterVariable (&cl_upspeed); Cvar_RegisterVariable (&cl_forwardspeed); Cvar_RegisterVariable (&cl_backspeed); Cvar_RegisterVariable (&cl_sidespeed); Cvar_RegisterVariable (&cl_movespeedkey); Cvar_RegisterVariable (&cl_yawspeed); Cvar_RegisterVariable (&cl_pitchspeed); Cvar_RegisterVariable (&cl_anglespeedkey); Cvar_RegisterVariable (&cl_shownet); Cvar_RegisterVariable (&cl_nolerp); Cvar_RegisterVariable (&lookspring); Cvar_RegisterVariable (&lookstrafe); Cvar_RegisterVariable (&sensitivity); Cvar_RegisterVariable (&mwheelthreshold); Cvar_RegisterVariable (&m_pitch); Cvar_RegisterVariable (&m_yaw); Cvar_RegisterVariable (&m_forward); Cvar_RegisterVariable (&m_side); Cvar_RegisterVariable (&cfg_unbindall); Cmd_AddCommand ("entities", CL_PrintEntities_f); Cmd_AddCommand ("disconnect", CL_Disconnect_f); Cmd_AddCommand ("record", CL_Record_f); Cmd_AddCommand ("stop", CL_Stop_f); Cmd_AddCommand ("playdemo", CL_PlayDemo_f); Cmd_AddCommand ("timedemo", CL_TimeDemo_f); Cmd_AddCommand ("sensitivity_save", CL_Sensitivity_save_f); Cmd_AddCommand ("viewpos", CL_Viewpos_f); } engine/hexen2/cl_parse.c000066400000000000000000001267671444734033100154760ustar00rootroot00000000000000/* * cl_parse.c -- parse a message received from the server * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "bgmusic.h" #include "cdaudio.h" #include "r_shared.h" static const char *svc_strings[] = { "svc_bad", "svc_nop", "svc_disconnect", "svc_updatestat", "svc_version", // [long] server version "svc_setview", // [short] entity number "svc_sound", // "svc_time", // [float] server time "svc_print", // [string] null terminated string "svc_stufftext", // [string] stuffed into client's console buffer // the string should be \n terminated "svc_setangle", // [vec3] set the view angle to this absolute value "svc_serverinfo", // [long] version // [string] signon string // [string]..[0]model cache [string]...[0]sounds cache // [string]..[0]item cache "svc_lightstyle", // [byte] [string] "svc_updatename", // [byte] [string] "svc_updatefrags", // [byte] [short] "svc_clientdata", // "svc_stopsound", // "svc_updatecolors", // [byte] [byte] "svc_particle", // [vec3] "svc_damage", // [byte] impact [byte] blood [vec3] from "svc_spawnstatic", "svc_raineffect", "svc_spawnbaseline", "svc_temp_entity", // "svc_setpause", "svc_signonnum", "svc_centerprint", "svc_killedmonster", "svc_foundsecret", "svc_spawnstaticsound", "svc_intermission", "svc_finale", // [string] music [string] text "svc_cdtrack", // [byte] track [byte] looptrack "svc_sellscreen", "svc_particle2", // [vec3] "svc_cutscene", "svc_midi_name", "svc_updateclass", // [byte] client [byte] class "svc_particle3", "svc_particle4", "svc_set_view_flags", "svc_clear_view_flags", "svc_start_effect", "svc_end_effect", "svc_plaque", "svc_particle_explosion", "svc_set_view_tint", "svc_reference", "svc_clear_edicts", "svc_update_inv", "svc_setangle_interpolate", "svc_update_kingofhill", "svc_toggle_statbar", "svc_sound_update_pos", "svc_mod_name", // UQE v1.13 by Korax, music file name "svc_skybox" // UQE v1.13 by Korax, skybox name }; #define NUM_SVC_STRINGS (sizeof(svc_strings) / sizeof(svc_strings[0])) int cl_protocol; /* protocol version used by the server */ int LastServerMessageSize; qmodel_t *player_models[MAX_PLAYER_CLASS]; extern cvar_t precache; extern qboolean menu_disabled_mouse; //============================================================================= /* =============== CL_EntityNum This error checks and tracks the total number of entities =============== */ static entity_t *CL_EntityNum (int num) { if (num >= cl.num_entities) { if (num >= MAX_EDICTS) Host_Error ("%s: %i is an invalid number", __thisfunc__, num); while (cl.num_entities <= num) { cl_entities[cl.num_entities].colormap = vid.colormap; cl.num_entities++; } } return &cl_entities[num]; } /* ================== CL_ParseStartSoundPacket ================== */ static void CL_ParseStartSoundPacket(void) { vec3_t pos; int channel, ent; int sound_num, volume, field_mask; float attenuation; int i; field_mask = MSG_ReadByte(); if (field_mask & SND_VOLUME) volume = MSG_ReadByte (); else volume = DEFAULT_SOUND_PACKET_VOLUME; if (field_mask & SND_ATTENUATION) attenuation = MSG_ReadByte () / 64.0; else attenuation = DEFAULT_SOUND_PACKET_ATTENUATION; channel = MSG_ReadShort (); sound_num = MSG_ReadByte (); if (field_mask & SND_OVERFLOW) sound_num += MAX_SOUNDS_OLD; if (field_mask & SND_OVERFLOW2) { /* this means the server has MAX_SOUNDS > 512 (== 1024) * and is sending it to us as a byte: Kor Skarn's code. * UQE does this but I haven't seen this in real life yet. * Currently not supported. TODO: in a newer protocol. */ sound_num += MAX_SOUNDS_H2MP; /* +512 */ Con_DPrintf("%s: field_mask & SND_OVERFLOW2 (%d)\n", __thisfunc__, sound_num); return; } ent = channel >> 3; channel &= 7; if (ent > MAX_EDICTS) Host_Error ("%s: ent = %i", __thisfunc__, ent); for (i = 0; i < 3; i++) pos[i] = MSG_ReadCoord (); S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation); } /* ================== CL_KeepaliveMessage When the client is taking a long time to load stuff, send keepalive messages so the server doesn't disconnect. ================== */ static void CL_KeepaliveMessage (void) { float time; static float lastmsg; int ret; sizebuf_t old; byte olddata[NET_MAXMESSAGE]; if (sv.active) return; // no need if server is local if (cls.demoplayback) return; // read messages from server, should just be nops old = net_message; memcpy (olddata, net_message.data, net_message.cursize); do { ret = CL_GetMessage (); switch (ret) { default: Host_Error ("%s: CL_GetMessage failed", __thisfunc__); case 0: break; // nothing waiting case 1: Host_Error ("%s: received a message", __thisfunc__); break; case 2: if (MSG_ReadByte() != svc_nop) Host_Error ("%s: datagram wasn't a nop", __thisfunc__); break; } } while (ret); net_message = old; memcpy (net_message.data, olddata, net_message.cursize); // check time time = Sys_DoubleTime (); if (time - lastmsg < 5) return; lastmsg = time; // write out a nop Con_Printf ("--> client to server keepalive\n"); MSG_WriteByte (&cls.message, clc_nop); NET_SendMessage (cls.netcon, &cls.message); SZ_Clear (&cls.message); } /* ================== CL_ParseServerInfo ================== */ static void CL_ParseServerInfo (void) { const char *str; int i; int nummodels, numsounds; char model_precache[MAX_MODELS][MAX_QPATH]; char sound_precache[MAX_SOUNDS][MAX_QPATH]; // rjr edict_t *ent; Con_DPrintf ("Serverinfo packet received.\n"); // bring up loading plaque for map changes within a demo. // it will be hidden in CL_SignonReply() -- ericw if (cls.demoplayback) SCR_BeginLoadingPlaque(); // // wipe the client_state_t struct // CL_ClearState (); // parse protocol version number cl_protocol = MSG_ReadLong (); switch (cl_protocol) { case PROTOCOL_RAVEN_111: case PROTOCOL_RAVEN_112: case PROTOCOL_UQE_113: Con_DPrintf ("\nServer using protocol %i\n", cl_protocol); break; default: Con_Printf ("\nServer returned version %i, not %i or %i\n", cl_protocol, PROTOCOL_RAVEN_112, PROTOCOL_UQE_113); return; } // parse maxclients cl.maxclients = MSG_ReadByte (); if (cl.maxclients < 1 || cl.maxclients > MAX_CLIENTS) { Con_Printf("Bad maxclients (%d) from server\n", cl.maxclients); return; } cl.scores = (scoreboard_t *) Hunk_AllocName (cl.maxclients*sizeof(*cl.scores), "scores"); // parse gametype cl.gametype = MSG_ReadByte (); if (cl.gametype == GAME_DEATHMATCH && cl_protocol > PROTOCOL_RAVEN_111) sv_kingofhill = MSG_ReadShort (); // parse signon message str = MSG_ReadString (); q_strlcpy (cl.levelname, str, sizeof(cl.levelname)); // seperate the printfs so the server message can have a color Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n"); Con_Printf ("%c%s\n", 2, str); // // first we go through and touch all of the precache data that still // happens to be in the cache, so precaching something else doesn't // needlessly purge it // // precache models memset (cl.model_precache, 0, sizeof(cl.model_precache)); for (nummodels = 1 ; ; nummodels++) { str = MSG_ReadString (); if (!str[0]) break; if (nummodels == MAX_MODELS) { Con_Printf ("Server sent too many model precaches\n"); return; } q_strlcpy (model_precache[nummodels], str, MAX_QPATH); Mod_TouchModel (str); } // precache sounds memset (cl.sound_precache, 0, sizeof(cl.sound_precache)); for (numsounds = 1 ; ; numsounds++) { str = MSG_ReadString (); if (!str[0]) break; if (numsounds == MAX_SOUNDS) { Con_Printf ("Server sent too many sound precaches\n"); return; } q_strlcpy (sound_precache[numsounds], str, MAX_QPATH); S_TouchSound (str); } // // now we try to load everything else until a cache allocation fails // if (precache.integer) { total_loading_size = nummodels + numsounds; current_loading_size = 1; loading_stage = 2; } // copy the naked name of the map file to the cl structure COM_FileBase (model_precache[1], cl.mapname, sizeof(cl.mapname)); //always precache the world!!! cl.model_precache[1] = Mod_ForName (model_precache[1], false); for (i = 2; i < nummodels; i++) { if (precache.integer) { cl.model_precache[i] = Mod_ForName (model_precache[i], false); current_loading_size++; D_ShowLoadingSize(); } else cl.model_precache[i] = (qmodel_t *)Mod_FindName (model_precache[i]); if (cl.model_precache[i] == NULL) { Host_Error("Model %s not found", model_precache[i]); return; } CL_KeepaliveMessage (); } player_models[0] = (qmodel_t *)Mod_FindName ("models/paladin.mdl"); player_models[1] = !(gameflags & GAME_OLD_DEMO) ? (qmodel_t *)Mod_FindName ("models/crusader.mdl") : NULL; player_models[2] = !(gameflags & GAME_OLD_DEMO) ? (qmodel_t *)Mod_FindName ("models/necro.mdl") : NULL; player_models[3] = (qmodel_t *)Mod_FindName ("models/assassin.mdl"); player_models[4] = (gameflags & GAME_PORTALS) ? (qmodel_t *)Mod_FindName ("models/succubus.mdl") : NULL; S_BeginPrecaching (); for (i = 1; i < numsounds; i++) { cl.sound_precache[i] = S_PrecacheSound (sound_precache[i]); if (precache.integer) { current_loading_size++; D_ShowLoadingSize(); } CL_KeepaliveMessage (); } S_EndPrecaching (); total_loading_size = 0; loading_stage = 0; // local state cl_entities[0].model = cl.worldmodel = cl.model_precache[1]; R_NewMap (); if (!sv.active) Host_LoadStrings(); CL_LoadPuzzleStrings(); // mission pack, objectives strings if (gameflags & GAME_PORTALS) CL_LoadInfoStrings(); Hunk_Check (); // make sure nothing is hurt // we connected to the server, make sure the mouse is going - S.A. menu_disabled_mouse = false; IN_ActivateMouse(); } /* ================== CL_ParseUpdate Parse an entity update message from the server If an entities model or origin changes from frame to frame, it must be relinked. Other attributes can change without relinking. ================== */ static void CL_ParseUpdate (int bits) { int i; qmodel_t *model; int modnum; qboolean forcelink; entity_t *ent; int num; entity_state2_t *ref_ent, *set_ent, build_ent, dummy; if (cls.signon == SIGNONS - 1) { // first update is the final signon stage cls.signon = SIGNONS; CL_SignonReply (); } if (bits & U_MOREBITS) { i = MSG_ReadByte (); bits |= (i<<8); } if (bits & U_MOREBITS2) { i = MSG_ReadByte (); bits |= (i<<16); } if (bits & U_LONGENTITY) num = MSG_ReadShort (); else num = MSG_ReadByte (); ent = CL_EntityNum (num); ent->baseline.flags |= BE_ON; ref_ent = NULL; for (i = 0; i < cl.frames[0].count; i++) { if (cl.frames[0].states[i].index == num) { ref_ent = &cl.frames[0].states[i]; break; } } if (!ref_ent) { ref_ent = &build_ent; build_ent.index = num; build_ent.origin[0] = ent->baseline.origin[0]; build_ent.origin[1] = ent->baseline.origin[1]; build_ent.origin[2] = ent->baseline.origin[2]; build_ent.angles[0] = ent->baseline.angles[0]; build_ent.angles[1] = ent->baseline.angles[1]; build_ent.angles[2] = ent->baseline.angles[2]; build_ent.modelindex = ent->baseline.modelindex; build_ent.frame = ent->baseline.frame; build_ent.colormap = ent->baseline.colormap; build_ent.skin = ent->baseline.skin; build_ent.effects = ent->baseline.effects; build_ent.scale = ent->baseline.scale; build_ent.drawflags = ent->baseline.drawflags; build_ent.abslight = ent->baseline.abslight; } if (cl.need_build) { // new sequence, first valid frame set_ent = &cl.frames[1].states[cl.frames[1].count]; cl.frames[1].count++; } else set_ent = &dummy; if (bits & U_CLEAR_ENT) { memset(ent, 0, sizeof(entity_t)); memset(ref_ent, 0, sizeof(*ref_ent)); ref_ent->index = num; } *set_ent = *ref_ent; if (ent->msgtime != cl.mtime[1]) forcelink = true; // no previous frame to lerp from else forcelink = false; ent->msgtime = cl.mtime[0]; if (bits & U_MODEL) { modnum = MSG_ReadShort (); if (modnum >= MAX_MODELS) Host_Error ("%s: bad modnum", __thisfunc__); } else modnum = ref_ent->modelindex; model = cl.model_precache[modnum]; set_ent->modelindex = modnum; if (model != ent->model) { ent->model = model; // automatic animation (torches, etc) can // be either all together or randomized if (model) { if (model->synctype == ST_RAND) ent->syncbase = rand() * (1.0 / RAND_MAX); else ent->syncbase = 0.0; } else forcelink = true; // hack to make null model players work #ifdef GLQUAKE if (num > 0 && num <= cl.maxclients) R_TranslatePlayerSkin (num - 1); #endif } if (bits & U_FRAME) set_ent->frame = ent->frame = MSG_ReadByte (); else ent->frame = ref_ent->frame; if (bits & U_COLORMAP) set_ent->colormap = i = MSG_ReadByte(); else i = ref_ent->colormap; if (num && num <= cl.maxclients) ent->colormap = ent->sourcecolormap = cl.scores[num-1].translations; else ent->sourcecolormap = vid.colormap; #ifdef GLQUAKE // ent->colormap = vid.colormap; #endif if (!i) { ent->colorshade = i; ent->colormap = ent->sourcecolormap; } else { ent->colorshade = i; #ifdef GLQUAKE // ent->colormap = vid.colormap; ent->colormap = globalcolormap; #else ent->colormap = globalcolormap; #endif } if (bits & U_SKIN) { set_ent->skin = ent->skinnum = MSG_ReadByte(); set_ent->drawflags = ent->drawflags = MSG_ReadByte(); } else { ent->skinnum = ref_ent->skin; ent->drawflags = ref_ent->drawflags; } if (bits & U_EFFECTS) set_ent->effects = ent->effects = MSG_ReadByte(); else ent->effects = ref_ent->effects; // shift the known values for interpolation VectorCopy (ent->msg_origins[0], ent->msg_origins[1]); VectorCopy (ent->msg_angles[0], ent->msg_angles[1]); if (bits & U_ORIGIN1) set_ent->origin[0] = ent->msg_origins[0][0] = MSG_ReadCoord (); else ent->msg_origins[0][0] = ref_ent->origin[0]; if (bits & U_ANGLE1) set_ent->angles[0] = ent->msg_angles[0][0] = MSG_ReadAngle(); else ent->msg_angles[0][0] = ref_ent->angles[0]; if (bits & U_ORIGIN2) set_ent->origin[1] = ent->msg_origins[0][1] = MSG_ReadCoord (); else ent->msg_origins[0][1] = ref_ent->origin[1]; if (bits & U_ANGLE2) set_ent->angles[1] = ent->msg_angles[0][1] = MSG_ReadAngle(); else ent->msg_angles[0][1] = ref_ent->angles[1]; if (bits & U_ORIGIN3) set_ent->origin[2] = ent->msg_origins[0][2] = MSG_ReadCoord (); else ent->msg_origins[0][2] = ref_ent->origin[2]; if (bits & U_ANGLE3) set_ent->angles[2] = ent->msg_angles[0][2] = MSG_ReadAngle(); else ent->msg_angles[0][2] = ref_ent->angles[2]; if (bits & U_SCALE) { set_ent->scale = ent->scale = MSG_ReadByte(); set_ent->abslight = ent->abslight = MSG_ReadByte(); } else { ent->scale = ref_ent->scale; ent->abslight = ref_ent->abslight; } if (bits & U_NOLERP) ent->forcelink = true; if (forcelink) { // didn't have an update last message VectorCopy (ent->msg_origins[0], ent->msg_origins[1]); VectorCopy (ent->msg_origins[0], ent->origin); VectorCopy (ent->msg_angles[0], ent->msg_angles[1]); VectorCopy (ent->msg_angles[0], ent->angles); ent->forcelink = true; } } static void CL_ParseUpdate2 (int bits) { int i; if (bits & U_MOREBITS) { i = MSG_ReadByte (); bits |= (i<<8); } if (bits & U_MOREBITS2) { i = MSG_ReadByte (); bits |= (i<<16); } if (bits & U_LONGENTITY) MSG_ReadShort (); else MSG_ReadByte (); if (bits & U_MODEL) MSG_ReadShort (); if (bits & U_FRAME) MSG_ReadByte (); if (bits & U_COLORMAP) MSG_ReadByte(); if (bits & U_SKIN) { MSG_ReadByte(); MSG_ReadByte(); } if (bits & U_EFFECTS) MSG_ReadByte(); if (bits & U_ORIGIN1) MSG_ReadCoord (); if (bits & U_ANGLE1) MSG_ReadAngle(); if (bits & U_ORIGIN2) MSG_ReadCoord (); if (bits & U_ANGLE2) MSG_ReadAngle(); if (bits & U_ORIGIN3) MSG_ReadCoord (); if (bits & U_ANGLE3) MSG_ReadAngle(); if (bits & U_SCALE) { MSG_ReadByte(); MSG_ReadByte(); } } /* ================== CL_ParseBaseline ================== */ static void CL_ParseBaseline (entity_t *ent) { int i; ent->baseline.modelindex = MSG_ReadShort (); ent->baseline.frame = MSG_ReadByte (); ent->baseline.colormap = MSG_ReadByte(); ent->baseline.skin = MSG_ReadByte(); ent->baseline.scale = MSG_ReadByte(); ent->baseline.drawflags = MSG_ReadByte(); ent->baseline.abslight = MSG_ReadByte(); for (i = 0; i < 3; i++) { ent->baseline.origin[i] = MSG_ReadCoord (); ent->baseline.angles[i] = MSG_ReadAngle (); } } /* ================== CL_ParseClientdata Server information pertaining to this client only ================== */ static void CL_ParseClientdata (int bits) { int i, j; if (bits & SU_VIEWHEIGHT) cl.viewheight = MSG_ReadChar (); //rjr else cl.viewheight = DEFAULT_VIEWHEIGHT; if (bits & SU_IDEALPITCH) cl.idealpitch = MSG_ReadChar (); if (bits & SU_IDEALROLL) cl.idealroll = MSG_ReadChar (); //rjr else cl.idealroll = 0; VectorCopy (cl.mvelocity[0], cl.mvelocity[1]); for (i = 0; i < 3; i++) { if (bits & (SU_PUNCH1<= cl.maxclients) Sys_Error ("%s: slot > cl.maxclients", __thisfunc__); if (!cl.scores[slot].playerclass) return; #ifdef GLQUAKE R_TranslatePlayerSkin (slot); return; #else dest = cl.scores[slot].translations; source = vid.colormap; memcpy (dest, vid.colormap, sizeof(cl.scores[slot].translations)); top = (cl.scores[slot].colors & 0xf0) >> 4; bottom = (cl.scores[slot].colors & 15); if (top > 11 || bottom > 11) Con_Printf("Invalid Player Color: %d,%d\n", top, bottom); if (top > 10) top = 0; if (bottom > 10) bottom = 0; top -= 1; bottom -= 1; // Con_Printf("Class is %d for slot %d\n",(int)cl.scores[slot].playerclass,slot); for (i = 0; i < VID_GRADES; i++, dest += 256, source += 256) { colorA = playerTranslation + 256 + color_offsets[(int)cl.scores[slot].playerclass-1]; colorB = colorA + 256; sourceA = colorB + 256 + (top * 256); sourceB = colorB + 256 + (bottom * 256); for (j = 0; j < 256; j++, colorA++, colorB++, sourceA++, sourceB++) { if (top >= 0 && (*colorA != 255)) dest[j] = source[*sourceA]; if (bottom >= 0 && (*colorB != 255)) dest[j] = source[*sourceB]; } } #endif } /* ===================== CL_ParseStatic ===================== */ static void CL_ParseStatic (void) { entity_t *ent; int i; i = cl.num_statics; if (i >= MAX_STATIC_ENTITIES) Host_Error ("Too many static entities"); ent = &cl_static_entities[i]; cl.num_statics++; CL_ParseBaseline (ent); // copy it to the current state ent->model = cl.model_precache[ent->baseline.modelindex]; ent->frame = ent->baseline.frame; ent->colormap = vid.colormap; ent->skinnum = ent->baseline.skin; ent->scale = ent->baseline.scale; ent->effects = ent->baseline.effects; ent->drawflags = ent->baseline.drawflags; ent->abslight = ent->baseline.abslight; VectorCopy (ent->baseline.origin, ent->origin); VectorCopy (ent->baseline.angles, ent->angles); R_AddEfrags (ent); } /* =================== CL_ParseStaticSound =================== */ static void CL_ParseStaticSound (void) { vec3_t org; int sound_num, vol, atten; int i; for (i = 0; i < 3; i++) org[i] = MSG_ReadCoord (); if (cl_protocol == PROTOCOL_RAVEN_111) sound_num = MSG_ReadByte (); else sound_num = MSG_ReadShort(); vol = MSG_ReadByte (); atten = MSG_ReadByte (); S_StaticSound (cl.sound_precache[sound_num], org, vol, atten); } static void CL_Plaque(void) { int idx; idx = MSG_ReadShort (); if (idx > 0 && idx <= host_string_count) SCR_SetPlaqueMessage (Host_GetString(idx - 1)); else SCR_SetPlaqueMessage (""); } static void CL_ParticleExplosion(void) { vec3_t org; short color, radius, counter; org[0] = MSG_ReadCoord(); org[1] = MSG_ReadCoord(); org[2] = MSG_ReadCoord(); color = MSG_ReadShort(); radius = MSG_ReadShort(); counter = MSG_ReadShort(); R_ColoredParticleExplosion(org,color,radius,counter); } static void CL_ParseRainEffect(void) { vec3_t org, e_size; short color,count; int x_dir, y_dir; org[0] = MSG_ReadCoord(); org[1] = MSG_ReadCoord(); org[2] = MSG_ReadCoord(); e_size[0] = MSG_ReadCoord(); e_size[1] = MSG_ReadCoord(); e_size[2] = MSG_ReadCoord(); x_dir = MSG_ReadAngle(); y_dir = MSG_ReadAngle(); color = MSG_ReadShort(); count = MSG_ReadShort(); R_RainEffect(org,e_size,x_dir,y_dir,color,count); } #if 0 /* for debugging. from fteqw. */ static void CL_DumpPacket (void) { int i, pos; unsigned char *packet = net_message.data; Con_Printf("%s, BEGIN:\n", __thisfunc__); pos = 0; while (pos < net_message.cursize) { Con_Printf("%5i ", pos); for (i = 0; i < 16; i++) { if (pos >= net_message.cursize) Con_Printf(" X "); else Con_Printf("%2x ", packet[pos]); pos++; } pos -= 16; for (i = 0; i < 16; i++) { if (pos >= net_message.cursize) Con_Printf("X"); else if (packet[pos] == 0) Con_Printf("."); else Con_Printf("%c", packet[pos]); pos++; } Con_Printf("\n"); } Con_Printf("%s, --- END ---\n", __thisfunc__); } #endif /* CL_DumpPacket */ #define SHOWNET(S) \ do { \ if (cl_shownet.integer == 2) \ Con_Printf ("%3i:%s\n", msg_readcount-1, (S)); \ } while (0) /* ===================== CL_ParseServerMessage ===================== */ void CL_ParseServerMessage (void) { int cmd; int i, j, k; int EntityCount = 0; int EntitySize = 0; int before; static double lasttime; static qboolean packet_loss = false; entity_t *ent; int sc1, sc2; byte test; float compangles[2][3]; vec3_t deltaangles; // if recording demos, copy the message out if (net_message.cursize > LastServerMessageSize) { LastServerMessageSize = net_message.cursize; } if (cl_shownet.integer == 1) { Con_Printf ("Time: %2.2f Pck: %i ", realtime - lasttime, net_message.cursize); lasttime = realtime; } else if (cl_shownet.integer == 2) { Con_Printf ("------------------\n"); } cl.onground = false; // unless the server says otherwise // parse the message MSG_BeginReading (); while (1) { if (msg_badread) Host_Error ("%s: Bad server message", __thisfunc__); cmd = MSG_ReadByte (); if (cmd == -1) { if (cl_shownet.integer == 1) Con_Printf ("Ent: %i (%i bytes)", EntityCount, EntitySize); SHOWNET("END OF MESSAGE"); return; // end of message } // if the high bit of the command byte is set, it is a fast update if (cmd & 128) { before = msg_readcount; SHOWNET("fast update"); if (packet_loss) CL_ParseUpdate2 (cmd & 127); else CL_ParseUpdate (cmd & 127); EntityCount++; EntitySize += msg_readcount - before + 1; continue; } if (cmd < (int)NUM_SVC_STRINGS) { SHOWNET(svc_strings[cmd]); } // other commands switch (cmd) { default: // CL_DumpPacket (); Host_Error ("%s: Illegible server message %d", __thisfunc__, cmd); break; case svc_nop: // Con_Printf ("svc_nop\n"); break; case svc_time: cl.mtime[1] = cl.mtime[0]; cl.mtime[0] = MSG_ReadFloat (); break; case svc_clientdata: i = MSG_ReadShort (); CL_ParseClientdata (i); break; case svc_version: cl_protocol = MSG_ReadLong (); switch (cl_protocol) { case PROTOCOL_RAVEN_111: case PROTOCOL_RAVEN_112: case PROTOCOL_UQE_113: Con_Printf ("Server using protocol %i\n", cl_protocol); break; default: Host_Error ("%s: Server is protocol %i instead of %i or %i", __thisfunc__, cl_protocol, PROTOCOL_RAVEN_112, PROTOCOL_UQE_113); } break; case svc_disconnect: Host_EndGame ("Server disconnected\n"); break; case svc_print: if (intro_playing) { MSG_ReadString (); break; } Con_Printf ("%s", MSG_ReadString ()); break; case svc_centerprint: SCR_CenterPrint (MSG_ReadString ()); break; case svc_stufftext: Cbuf_AddText (MSG_ReadString ()); break; case svc_damage: V_ParseDamage (); break; case svc_serverinfo: CL_ParseServerInfo (); vid.recalc_refdef = true; // leave intermission full screen break; case svc_setangle: for (i = 0; i < 3; i++) cl.viewangles[i] = MSG_ReadAngle (); break; case svc_setangle_interpolate: compangles[0][0] = MSG_ReadAngle(); compangles[0][1] = MSG_ReadAngle(); compangles[0][2] = MSG_ReadAngle(); for (i = 0; i < 3; i++) { compangles[1][i] = cl.viewangles[i]; for (j = 0; j < 2; j++) {//standardize both old and new angles to +-180 if (compangles[j][i] >= 360) compangles[j][i] -= 360*((int)(compangles[j][i]/360)); else if (compangles[j][i] <= 360) compangles[j][i] += 360*(1+(int)(-compangles[j][i]/360)); if (compangles[j][i] > 180) compangles[j][i] = -360 + compangles[j][i]; else if (compangles[j][i] < -180) compangles[j][i] = 360 + compangles[j][i]; } //get delta deltaangles[i] = compangles[0][i] - compangles[1][i]; //cap delta to <=180,>=-180 if (deltaangles[i] > 180) deltaangles[i] += -360; else if (deltaangles[i] < -180) deltaangles[i] += 360; //add the delta cl.viewangles[i]+=(deltaangles[i]/8);//8 step interpolation //cap newangles to +-180 if (cl.viewangles[i] >= 360) cl.viewangles[i] -= 360*((int)(cl.viewangles[i]/360)); else if (cl.viewangles[i] <= 360) cl.viewangles[i] += 360*(1+(int)(-cl.viewangles[i]/360)); if (cl.viewangles[i] > 180) cl.viewangles[i] += -360; else if (cl.viewangles[i] < -180) cl.viewangles[i] += 360; } break; case svc_setview: cl.viewentity = MSG_ReadShort (); break; case svc_lightstyle: i = MSG_ReadByte (); if (i >= MAX_LIGHTSTYLES) Sys_Error ("svc_lightstyle > MAX_LIGHTSTYLES"); q_strlcpy (cl_lightstyle[i].map, MSG_ReadString(), MAX_STYLESTRING); cl_lightstyle[i].length = strlen(cl_lightstyle[i].map); break; case svc_sound: CL_ParseStartSoundPacket(); break; case svc_sound_update_pos: { // FIXME: put a field on the entity that lists the channels // it should update when it moves- if a certain flag // is on the ent, this update_channels field could // be set automatically by each sound and stopSound // called for this ent? vec3_t pos; int channel, ent_num; channel = MSG_ReadShort (); ent_num = channel >> 3; channel &= 7; if (ent_num > MAX_EDICTS) Host_Error ("svc_sound_update_pos: ent = %i", ent_num); for (i = 0; i < 3; i++) pos[i] = MSG_ReadCoord (); S_UpdateSoundPos (ent_num, channel, pos); } break; case svc_stopsound: i = MSG_ReadShort(); S_StopSound(i>>3, i&7); break; case svc_updatename: Sbar_Changed(); i = MSG_ReadByte (); if (i >= cl.maxclients) Host_Error ("%s: svc_updatename > MAX_CLIENTS", __thisfunc__); q_strlcpy (cl.scores[i].name, MSG_ReadString(), MAX_SCOREBOARDNAME); break; case svc_updateclass: Sbar_Changed(); i = MSG_ReadByte (); if (i >= cl.maxclients) Host_Error ("%s: svc_updateclass > MAX_CLIENTS", __thisfunc__); cl.scores[i].playerclass = (float)MSG_ReadByte(); CL_NewTranslation(i); // update the color break; case svc_updatefrags: Sbar_Changed(); i = MSG_ReadByte (); if (i >= cl.maxclients) Host_Error ("%s: svc_updatefrags > MAX_CLIENTS", __thisfunc__); cl.scores[i].frags = MSG_ReadShort (); break; case svc_update_kingofhill: sv_kingofhill = MSG_ReadShort() - 1; break; case svc_updatecolors: Sbar_Changed(); i = MSG_ReadByte (); if (i >= cl.maxclients) Host_Error ("%s: svc_updatecolors > MAX_CLIENTS", __thisfunc__); cl.scores[i].colors = MSG_ReadByte (); CL_NewTranslation (i); break; case svc_particle: R_ParseParticleEffect (); break; case svc_particle2: R_ParseParticleEffect2 (); break; case svc_particle3: R_ParseParticleEffect3 (); break; case svc_particle4: R_ParseParticleEffect4 (); break; case svc_spawnbaseline: i = MSG_ReadShort (); // must use CL_EntityNum() to force cl.num_entities up CL_ParseBaseline (CL_EntityNum(i)); break; case svc_spawnstatic: CL_ParseStatic (); break; case svc_raineffect: CL_ParseRainEffect(); break; case svc_temp_entity: CL_ParseTEnt (); break; case svc_setpause: cl.paused = MSG_ReadByte (); if (cl.paused) { CDAudio_Pause (); BGM_Pause (); VID_HandlePause (true); } else { CDAudio_Resume (); BGM_Resume (); VID_HandlePause (false); } break; case svc_signonnum: i = MSG_ReadByte (); if (i <= cls.signon) Host_Error ("Received signon %i when at %i", i, cls.signon); cls.signon = i; CL_SignonReply (); break; case svc_killedmonster: cl.stats[STAT_MONSTERS]++; break; case svc_foundsecret: cl.stats[STAT_SECRETS]++; break; case svc_updatestat: i = MSG_ReadByte (); if (i < 0 || i >= MAX_CL_STATS) Sys_Error ("svc_updatestat: %i is invalid", i); cl.stats[i] = MSG_ReadLong (); break; case svc_spawnstaticsound: CL_ParseStaticSound (); break; case svc_cdtrack: cl.cdtrack = MSG_ReadByte (); cl.looptrack = MSG_ReadByte (); if (q_strcasecmp(bgmtype.string,"cd") != 0) CDAudio_Stop (); else if ((cls.demoplayback || cls.demorecording) && cls.forcetrack != -1) CDAudio_Play ((byte)cls.forcetrack, true); else CDAudio_Play ((byte)cl.cdtrack, true); break; case svc_midi_name: q_strlcpy (cl.midi_name, MSG_ReadString(), sizeof(cl.midi_name)); if (q_strcasecmp(bgmtype.string,"midi") != 0) BGM_Stop(); else BGM_PlayMIDIorMusic(cl.midi_name); break; case svc_toggle_statbar: break; case svc_intermission: CL_SetupIntermission (MSG_ReadByte()); vid.recalc_refdef = true; // go to full screen break; /* case svc_finale: cl.intermission = 2; cl.completed_time = cl.time; vid.recalc_refdef = true; // go to full screen SCR_CenterPrint (MSG_ReadString ()); break; case svc_cutscene: cl.intermission = 3; cl.completed_time = cl.time; vid.recalc_refdef = true; // go to full screen SCR_CenterPrint (MSG_ReadString ()); break; case svc_sellscreen: Cmd_ExecuteString ("help", src_command); break; */ case svc_set_view_flags: cl.viewent.drawflags |= MSG_ReadByte(); break; case svc_clear_view_flags: cl.viewent.drawflags &= ~MSG_ReadByte(); break; case svc_start_effect: CL_ParseEffect(); break; case svc_end_effect: CL_EndEffect(); break; case svc_plaque: CL_Plaque(); break; case svc_particle_explosion: CL_ParticleExplosion(); break; case svc_set_view_tint: i = MSG_ReadByte(); cl.viewent.colorshade = i; break; case svc_reference: { short RemovePlace, OrigPlace, NewPlace, AddedIndex; packet_loss = false; cl.last_frame = cl.current_frame; cl.last_sequence = cl.current_sequence; cl.current_frame = MSG_ReadByte(); cl.current_sequence = MSG_ReadByte(); if (cl.need_build == 2) { // Con_Printf("CL: NB2 CL(%d,%d) R(%d)\n", // cl.current_sequence, cl.current_frame, cl.reference_frame); cl.frames[0].count = cl.frames[1].count = cl.frames[2].count = 0; cl.need_build = 1; cl.reference_frame = cl.current_frame; } else if (cl.last_sequence != cl.current_sequence) { // Con_Printf("CL: Sequence CL(%d,%d) R(%d)\n", // cl.current_sequence, cl.current_frame, cl.reference_frame); if (cl.reference_frame >= 1 && cl.reference_frame <= MAX_FRAMES) { RemovePlace = OrigPlace = NewPlace = AddedIndex = 0; for (i = 0; i < cl.num_entities; i++) { if (RemovePlace >= cl.NumToRemove || cl.RemoveList[RemovePlace] != i) { if (NewPlace < cl.frames[1].count && cl.frames[1].states[NewPlace].index == i) { cl.frames[2].states[AddedIndex] = cl.frames[1].states[NewPlace]; AddedIndex++; cl.frames[2].count++; } else if (OrigPlace < cl.frames[0].count && cl.frames[0].states[OrigPlace].index == i) { cl.frames[2].states[AddedIndex] = cl.frames[0].states[OrigPlace]; AddedIndex++; cl.frames[2].count++; } } else RemovePlace++; if (cl.frames[0].states[OrigPlace].index == i) OrigPlace++; if (cl.frames[1].states[NewPlace].index == i) NewPlace++; } cl.frames[0] = cl.frames[2]; } cl.frames[1].count = cl.frames[2].count = 0; cl.need_build = 1; cl.reference_frame = cl.current_frame; } else { // Con_Printf("CL: Normal CL(%d,%d) R(%d)\n", // cl.current_sequence, cl.current_frame,cl.reference_frame); cl.need_build = 0; } for (i = 1, ent = cl_entities+1; i < cl.num_entities; i++, ent++) ent->baseline.flags &= ~BE_ON; for (i = 0; i < cl.frames[0].count; i++) { ent = CL_EntityNum (cl.frames[0].states[i].index); ent->model = cl.model_precache[cl.frames[0].states[i].modelindex]; ent->baseline.flags |= BE_ON; } } break; case svc_clear_edicts: j = MSG_ReadByte(); if (cl.need_build) cl.NumToRemove = j; for (i = 0; i < j; i++) { k = MSG_ReadShort(); if (cl.need_build) cl.RemoveList[i] = k; ent = CL_EntityNum (k); ent->baseline.flags &= ~BE_ON; } break; case svc_update_inv: sc1 = sc2 = 0; test = MSG_ReadByte(); if (test & 1) sc1 |= ((int)MSG_ReadByte()); if (test & 2) sc1 |= ((int)MSG_ReadByte())<<8; if (test & 4) sc1 |= ((int)MSG_ReadByte())<<16; if (test & 8) sc1 |= ((int)MSG_ReadByte())<<24; if (test & 16) sc2 |= ((int)MSG_ReadByte()); if (test & 32) sc2 |= ((int)MSG_ReadByte())<<8; if (test & 64) sc2 |= ((int)MSG_ReadByte())<<16; if (test & 128) sc2 |= ((int)MSG_ReadByte())<<24; if (sc1 & SC1_HEALTH) cl.v.health = MSG_ReadShort(); if (sc1 & SC1_LEVEL) cl.v.level = MSG_ReadByte(); if (sc1 & SC1_INTELLIGENCE) cl.v.intelligence = MSG_ReadByte(); if (sc1 & SC1_WISDOM) cl.v.wisdom = MSG_ReadByte(); if (sc1 & SC1_STRENGTH) cl.v.strength = MSG_ReadByte(); if (sc1 & SC1_DEXTERITY) cl.v.dexterity = MSG_ReadByte(); if (sc1 & SC1_WEAPON) cl.v.weapon = MSG_ReadByte(); if (sc1 & SC1_BLUEMANA) cl.v.bluemana = MSG_ReadByte(); if (sc1 & SC1_GREENMANA) cl.v.greenmana = MSG_ReadByte(); if (sc1 & SC1_EXPERIENCE) cl.v.experience = MSG_ReadLong(); if (sc1 & SC1_CNT_TORCH) cl.v.cnt_torch = MSG_ReadByte(); if (sc1 & SC1_CNT_H_BOOST) cl.v.cnt_h_boost = MSG_ReadByte(); if (sc1 & SC1_CNT_SH_BOOST) cl.v.cnt_sh_boost = MSG_ReadByte(); if (sc1 & SC1_CNT_MANA_BOOST) cl.v.cnt_mana_boost = MSG_ReadByte(); if (sc1 & SC1_CNT_TELEPORT) cl.v.cnt_teleport = MSG_ReadByte(); if (sc1 & SC1_CNT_TOME) cl.v.cnt_tome = MSG_ReadByte(); if (sc1 & SC1_CNT_SUMMON) cl.v.cnt_summon = MSG_ReadByte(); if (sc1 & SC1_CNT_INVISIBILITY) cl.v.cnt_invisibility = MSG_ReadByte(); if (sc1 & SC1_CNT_GLYPH) cl.v.cnt_glyph = MSG_ReadByte(); if (sc1 & SC1_CNT_HASTE) cl.v.cnt_haste = MSG_ReadByte(); if (sc1 & SC1_CNT_BLAST) cl.v.cnt_blast = MSG_ReadByte(); if (sc1 & SC1_CNT_POLYMORPH) cl.v.cnt_polymorph = MSG_ReadByte(); if (sc1 & SC1_CNT_FLIGHT) cl.v.cnt_flight = MSG_ReadByte(); if (sc1 & SC1_CNT_CUBEOFFORCE) cl.v.cnt_cubeofforce = MSG_ReadByte(); if (sc1 & SC1_CNT_INVINCIBILITY) cl.v.cnt_invincibility = MSG_ReadByte(); if (sc1 & SC1_ARTIFACT_ACTIVE) cl.v.artifact_active = MSG_ReadFloat(); if (sc1 & SC1_ARTIFACT_LOW) cl.v.artifact_low = MSG_ReadFloat(); if (sc1 & SC1_MOVETYPE) cl.v.movetype = MSG_ReadByte(); if (sc1 & SC1_CAMERAMODE) cl.v.cameramode = MSG_ReadByte(); if (sc1 & SC1_HASTED) cl.v.hasted = MSG_ReadFloat(); if (sc1 & SC1_INVENTORY) cl.v.inventory = MSG_ReadByte(); if (sc1 & SC1_RINGS_ACTIVE) cl.v.rings_active = MSG_ReadFloat(); if (sc2 & SC2_RINGS_LOW) cl.v.rings_low = MSG_ReadFloat(); if (sc2 & SC2_AMULET) cl.v.armor_amulet = MSG_ReadByte(); if (sc2 & SC2_BRACER) cl.v.armor_bracer = MSG_ReadByte(); if (sc2 & SC2_BREASTPLATE) cl.v.armor_breastplate = MSG_ReadByte(); if (sc2 & SC2_HELMET) cl.v.armor_helmet = MSG_ReadByte(); if (sc2 & SC2_FLIGHT_T) cl.v.ring_flight = MSG_ReadByte(); if (sc2 & SC2_WATER_T) cl.v.ring_water = MSG_ReadByte(); if (sc2 & SC2_TURNING_T) cl.v.ring_turning = MSG_ReadByte(); if (sc2 & SC2_REGEN_T) cl.v.ring_regeneration = MSG_ReadByte(); if (sc2 & SC2_HASTE_T) cl.v.haste_time = MSG_ReadFloat(); if (sc2 & SC2_TOME_T) cl.v.tome_time = MSG_ReadFloat(); if (sc2 & SC2_PUZZLE1) q_snprintf(cl.puzzle_pieces[0], sizeof(cl.puzzle_pieces[0]), "%.9s", MSG_ReadString()); if (sc2 & SC2_PUZZLE2) q_snprintf(cl.puzzle_pieces[1], sizeof(cl.puzzle_pieces[0]), "%.9s", MSG_ReadString()); if (sc2 & SC2_PUZZLE3) q_snprintf(cl.puzzle_pieces[2], sizeof(cl.puzzle_pieces[0]), "%.9s", MSG_ReadString()); if (sc2 & SC2_PUZZLE4) q_snprintf(cl.puzzle_pieces[3], sizeof(cl.puzzle_pieces[0]), "%.9s", MSG_ReadString()); if (sc2 & SC2_PUZZLE5) q_snprintf(cl.puzzle_pieces[4], sizeof(cl.puzzle_pieces[0]), "%.9s", MSG_ReadString()); if (sc2 & SC2_PUZZLE6) q_snprintf(cl.puzzle_pieces[5], sizeof(cl.puzzle_pieces[0]), "%.9s", MSG_ReadString()); if (sc2 & SC2_PUZZLE7) q_snprintf(cl.puzzle_pieces[6], sizeof(cl.puzzle_pieces[0]), "%.9s", MSG_ReadString()); if (sc2 & SC2_PUZZLE8) q_snprintf(cl.puzzle_pieces[7], sizeof(cl.puzzle_pieces[0]), "%.9s", MSG_ReadString()); if (sc2 & SC2_MAXHEALTH) cl.v.max_health = MSG_ReadShort(); if (sc2 & SC2_MAXMANA) cl.v.max_mana = MSG_ReadByte(); if (sc2 & SC2_FLAGS) cl.v.flags = MSG_ReadFloat(); /* SC2_OBJ, SC2_OBJ2: mission pack objectives * With protocol 18 (PROTOCOL_RAVEN_111), these * bits get set somehow (?!): let's avoid them. */ if (cl_protocol > PROTOCOL_RAVEN_111) { if (sc2 & SC2_OBJ) cl.info_mask = MSG_ReadLong(); if (sc2 & SC2_OBJ2) cl.info_mask2 = MSG_ReadLong(); } if ((sc1 & SC1_STAT_BAR) || (sc2 & SC2_STAT_BAR)) Sbar_Changed(); if ((sc1 & SC1_INV) || (sc2 & SC2_INV)) SB_InvChanged(); break; case svc_mod_name: case svc_skybox: MSG_ReadString(); Con_DPrintf ("Ignored server msg %d (%s)\n", cmd, svc_strings[cmd]); break; } } } engine/hexen2/cl_string.c000066400000000000000000000122421444734033100156500ustar00rootroot00000000000000/* * cl_string.c: Hexen II internationalized stuff * strings for the client-side puzzle piece and objectives displays. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" /* puzzle piece strings for Sbar_PuzzlePieceOverlay() */ static char *puzzle_strings = NULL; static int *puzzle_string_index = NULL; int puzzle_string_count = 0; /* strings for the mission pack's Objectives display. */ static char *info_strings = NULL; static int *info_string_index = NULL; int info_string_count = 0; void CL_LoadPuzzleStrings (void) { int i, j, count = 0; char *start, *end, *space; puzzle_string_index = NULL; puzzle_string_count = 0; puzzle_strings = (char *)FS_LoadHunkFile ("puzzles.txt", NULL); if (!puzzle_strings) return; /* * Format of puzzles.txt: * Line #1 : * Line #2+: */ /* Doing an advanced parsing here in order to overcome any borked files */ j = atoi(puzzle_strings); /* the intended number of lines */ if (j < 1) return; if (j > 256) j = 256; start = puzzle_strings; while (*start && *start != '\r' && *start != '\n') { /* find first newline, clear the start */ *start++ = 0; } if (! *start) return; while ( *start && /* skip and clear all leading space, '\n' and '\r' */ (*start == '\n' || *start == '\r' || *start == ' ' || *start == '\t') ) { *start++ = 0; } if (! *start) /* EOF */ return; while (count <= j) { i = 0; end = start; while (*end && *end != '\r' && *end != '\n') end++; if (! *end) /* EOF */ end = NULL; else *end = 0; space = start; while (*space && *space != ' ' && *space != '\t') space++; if (*space) { /* is there a word after the whitespace? */ while (space[i] == ' ' || space[i] == '\t') { space[i] = 0; ++i; } if (space[i]) /* we have the full name */ { count++; /* clear the trailing space */ while (space[i]) ++i; --i; while (space[i] == ' ' || space[i] == '\t') { space[i] = 0; --i; } if (!end) break; goto forward; } else /* .. no full name: we hit the *end = 0 mark we * made, or the EOF. clear until the next entry. */ { if (!end) break; memset (start, 0, end - start); goto forward; } } else /* no space in the line. clear until the next entry. */ { if (!end) break; memset (start, 0, end - start); forward: start = ++end; while ( *start == '\r' || *start == '\n' || *start == ' ' || *start == '\t' ) { *start++ = 0; } if (*start == 0) /* EOF */ break; } } if (!count) return; puzzle_string_count = count * 2; puzzle_string_index = (int *)Hunk_AllocName (puzzle_string_count*sizeof(int), "puzzle_string_index"); i = 0; start = puzzle_strings; while (i < puzzle_string_count) { while (*start == 0) start++; puzzle_string_index[i] = start - puzzle_strings; while (*start != 0) start++; ++i; } Con_DPrintf("Read in %d puzzle piece names\n", count); } const char *CL_FindPuzzleString (const char *shortname) { int i; for (i = 0; i < puzzle_string_count; i += 2) { if (q_strcasecmp(shortname, &puzzle_strings[puzzle_string_index[i]]) == 0) return &puzzle_strings[puzzle_string_index[i + 1]]; } return NULL; } void CL_LoadInfoStrings (void) { int i, count, start; signed char newline_char; info_strings = (char *)FS_LoadHunkFile ("infolist.txt", NULL); if (!info_strings) Host_Error ("%s: couldn't load infolist.txt", __thisfunc__); newline_char = -1; for (i = count = 0; info_strings[i] != 0; i++) { if (info_strings[i] == '\r' || info_strings[i] == '\n') { if (newline_char == info_strings[i] || newline_char == -1) { newline_char = info_strings[i]; count++; } } } if (!count) { Host_Error ("%s: no string lines found", __thisfunc__); } info_string_index = (int *)Hunk_AllocName ((count + 1)*sizeof(int), "info_string_index"); for (i = count = start = 0; info_strings[i] != 0; i++) { if (info_strings[i] == '\r' || info_strings[i] == '\n') { if (newline_char == info_strings[i]) { info_string_index[count] = start; start = i + 1; count++; } else { start++; } info_strings[i] = 0; } } info_string_count = count; Con_DPrintf("Read in %d objectives\n", count); } const char *CL_GetInfoString (int idx) { return &info_strings[info_string_index[idx]]; } engine/hexen2/cl_tent.c000066400000000000000000000371241444734033100153220ustar00rootroot00000000000000/* * cl_tent.c -- Client side temporary entity effects. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // HEADER FILES ------------------------------------------------------------ #include "quakedef.h" // MACROS ------------------------------------------------------------------ #define MAX_STREAMS 32 #define MAX_STREAM_ENTITIES 128 #define STREAM_ATTACHED 16 #define STREAM_TRANSLUCENT 32 #define TE_SPIKE 0 #define TE_SUPERSPIKE 1 #define TE_GUNSHOT 2 #define TE_EXPLOSION 3 //#define TE_TAREXPLOSION 4 #define TE_LIGHTNING1 5 #define TE_LIGHTNING2 6 #define TE_WIZSPIKE 7 #define TE_KNIGHTSPIKE 8 #define TE_LIGHTNING3 9 #define TE_LAVASPLASH 10 #define TE_TELEPORT 11 //#define TE_EXPLOSION2 12 #define TE_STREAM_CHAIN 25 #define TE_STREAM_SUNSTAFF1 26 #define TE_STREAM_SUNSTAFF2 27 #define TE_STREAM_LIGHTNING 28 #define TE_STREAM_COLORBEAM 29 #define TE_STREAM_ICECHUNKS 30 #define TE_STREAM_GAZE 31 #define TE_STREAM_FAMINE 32 #define TE_STREAM_LIGHTNING_SMALL 24 // TYPES ------------------------------------------------------------------- typedef struct { int type; int entity; int tag; int flags; int skin; struct qmodel_s *models[4]; vec3_t source; vec3_t dest; vec3_t offset; float endTime; float lastTrailTime; } stream_t; // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void ParseStream(int type); static stream_t *NewStream(int ent, int tag); static entity_t *NewStreamEntity(void); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- // PRIVATE DATA DEFINITIONS ------------------------------------------------ static stream_t cl_Streams[MAX_STREAMS]; static entity_t StreamEntities[MAX_STREAM_ENTITIES]; static int StreamEntityCount; //static sfx_t *cl_sfx_wizhit; //static sfx_t *cl_sfx_knighthit; static sfx_t *cl_sfx_tink1; static sfx_t *cl_sfx_ric1; static sfx_t *cl_sfx_ric2; static sfx_t *cl_sfx_ric3; static sfx_t *cl_sfx_r_exp3; #ifdef QUAKE2 static sfx_t *cl_sfx_imp; static sfx_t *cl_sfx_rail; #endif // CODE -------------------------------------------------------------------- //========================================================================== // // CL_InitTEnts // //========================================================================== void CL_InitTEnts(void) { cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav"); cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav"); cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav"); cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav"); cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav"); #ifdef QUAKE2 cl_sfx_imp = S_PrecacheSound ("shambler/sattck1.wav"); cl_sfx_rail = S_PrecacheSound ("weapons/lstart.wav"); #endif } //========================================================================== // // CL_ClearTEnts // //========================================================================== void CL_ClearTEnts(void) { memset(cl_Streams, 0, sizeof(cl_Streams)); } //========================================================================== // // CL_ParseTEnt // //========================================================================== void CL_ParseTEnt(void) { int type; vec3_t pos; #ifdef QUAKE2 vec3_t endpos; #endif dlight_t *dl; int rnd; // int colorStart, colorLength; type = MSG_ReadByte(); switch (type) { case TE_WIZSPIKE: // spike hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 20, 30); // S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1); break; case TE_KNIGHTSPIKE: // spike hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 226, 20); // S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1); break; case TE_SPIKE: // spike hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 0, 10); if (rand() % 5) S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1); else { rnd = rand() & 3; if (rnd == 1) S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1); else if (rnd == 2) S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1); else S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1); } break; case TE_SUPERSPIKE: // super spike hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 0, 20); if (rand() % 5) S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1); else { rnd = rand() & 3; if (rnd == 1) S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1); else if (rnd == 2) S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1); else S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1); } break; case TE_GUNSHOT: // bullet hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 0, 20); break; case TE_EXPLOSION: // rocket explosion pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_ParticleExplosion (pos); // break; // why was this here??? dl = CL_AllocDlight (0); VectorCopy (pos, dl->origin); dl->radius = 350; dl->die = cl.time + 0.5; dl->decay = 300; # ifdef GLQUAKE if (gl_colored_dynamic_lights.integer) { // Make the dynamic light red dl->color[0] = 0.8; dl->color[1] = 0.2; dl->color[2] = 0.2; dl->color[3] = 0.7; } # endif S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); break; case TE_LIGHTNING1: case TE_LIGHTNING2: case TE_LIGHTNING3: MSG_ReadShort(); MSG_ReadCoord(); MSG_ReadCoord(); MSG_ReadCoord(); MSG_ReadCoord(); MSG_ReadCoord(); MSG_ReadCoord(); break; case TE_STREAM_CHAIN: case TE_STREAM_SUNSTAFF1: case TE_STREAM_SUNSTAFF2: case TE_STREAM_LIGHTNING: case TE_STREAM_LIGHTNING_SMALL: case TE_STREAM_COLORBEAM: case TE_STREAM_ICECHUNKS: case TE_STREAM_GAZE: case TE_STREAM_FAMINE: ParseStream(type); break; case TE_LAVASPLASH: pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_LavaSplash (pos); break; case TE_TELEPORT: pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_TeleportSplash (pos); break; default: Sys_Error ("%s: bad type", __thisfunc__); } } //========================================================================== // // ParseStream // //========================================================================== static void ParseStream(int type) { int ent, tag, flags, skin; vec3_t source, dest; float duration; stream_t *stream; qmodel_t *models[4]; ent = MSG_ReadShort(); flags = MSG_ReadByte(); tag = flags&15; duration = (float)MSG_ReadByte()*0.05; skin = 0; if (type == TE_STREAM_COLORBEAM) skin = MSG_ReadByte(); source[0] = MSG_ReadCoord(); source[1] = MSG_ReadCoord(); source[2] = MSG_ReadCoord(); dest[0] = MSG_ReadCoord(); dest[1] = MSG_ReadCoord(); dest[2] = MSG_ReadCoord(); models[1] = models[2] = models[3] = NULL; switch (type) { case TE_STREAM_CHAIN: models[0] = Mod_ForName("models/stchain.mdl", true); break; case TE_STREAM_SUNSTAFF1: models[0] = Mod_ForName("models/stsunsf1.mdl", true); models[1] = Mod_ForName("models/stsunsf2.mdl", true); models[2] = Mod_ForName("models/stsunsf3.mdl", true); models[3] = Mod_ForName("models/stsunsf4.mdl", true); break; case TE_STREAM_SUNSTAFF2: models[0] = Mod_ForName("models/stsunsf5.mdl", true); models[2] = Mod_ForName("models/stsunsf3.mdl", true); models[3] = Mod_ForName("models/stsunsf4.mdl", true); break; case TE_STREAM_LIGHTNING: models[0] = Mod_ForName("models/stlghtng.mdl", true); // duration *= 2; break; case TE_STREAM_LIGHTNING_SMALL: models[0] = Mod_ForName("models/stltng2.mdl", true); // duration *= 2; break; case TE_STREAM_FAMINE: models[0] = Mod_ForName("models/fambeam.mdl", true); break; case TE_STREAM_COLORBEAM: models[0] = Mod_ForName("models/stclrbm.mdl", true); break; case TE_STREAM_ICECHUNKS: models[0] = Mod_ForName("models/stice.mdl", true); break; case TE_STREAM_GAZE: models[0] = Mod_ForName("models/stmedgaz.mdl", true); break; default: models[0] = NULL; break; } if (models[0] == NULL) Sys_Error("%s: bad type", __thisfunc__); if ((stream = NewStream(ent, tag)) == NULL) { Con_Printf("stream list overflow\n"); return; } stream->type = type; stream->tag = tag; stream->flags = flags; stream->entity = ent; stream->skin = skin; stream->models[0] = models[0]; stream->models[1] = models[1]; stream->models[2] = models[2]; stream->models[3] = models[3]; stream->endTime = cl.time+duration; stream->lastTrailTime = 0; VectorCopy(source, stream->source); VectorCopy(dest, stream->dest); if (flags & STREAM_ATTACHED) { VectorSubtract(source, cl_entities[ent].origin, stream->offset); } } //========================================================================== // // NewStream // //========================================================================== static stream_t *NewStream(int ent, int tag) { stream_t *stream; int i; // Search for a stream with matching entity and tag for (i = 0, stream = cl_Streams; i < MAX_STREAMS; i++, stream++) { if (stream->entity == ent && stream->tag == tag) return stream; } // Search for a free stream for (i = 0, stream = cl_Streams; i < MAX_STREAMS; i++, stream++) { if (!stream->models[0] || stream->endTime < cl.time) return stream; } return NULL; } //========================================================================== // // CL_UpdateTEnts // //========================================================================== void CL_UpdateTEnts(void) { int i, j, offset; stream_t *stream; vec3_t dist, org; float d; entity_t *ent; float yaw, pitch, forward; // Update streams StreamEntityCount = 0; for (i = 0, stream = cl_Streams; i < MAX_STREAMS; i++, stream++) { if (!stream->models[0])// || stream->endTime < cl.time) { // Inactive continue; } if (stream->endTime < cl.time) { // Inactive if (stream->type != TE_STREAM_LIGHTNING && stream->type != TE_STREAM_LIGHTNING_SMALL) continue; else if (stream->endTime + 0.25 < cl.time) continue; } if (stream->flags & STREAM_ATTACHED && stream->endTime >= cl.time) { // Attach the start position to owner VectorAdd(cl_entities[stream->entity].origin, stream->offset, stream->source); } VectorSubtract(stream->dest, stream->source, dist); if (dist[1] == 0 && dist[0] == 0) { yaw = 0; if (dist[2] > 0) pitch = 90; else pitch = 270; } else { yaw = (int)(atan2(dist[1], dist[0])*180/M_PI); if (yaw < 0) yaw += 360; forward = Q_sqrt(dist[0]*dist[0]+dist[1]*dist[1]); pitch = (int)(atan2(dist[2], forward)*180/M_PI); if (pitch < 0) pitch += 360; } VectorCopy(stream->source, org); d = VectorNormalizeFast(dist); if (stream->type == TE_STREAM_ICECHUNKS) { offset = (int)(cl.time*40)%30; for (j = 0; j < 3; j++) org[j] += dist[j]*offset; } while (d > 0) { ent = NewStreamEntity(); if (!ent) return; VectorCopy(org, ent->origin); ent->model = stream->models[0]; ent->angles[0] = pitch; ent->angles[1] = yaw; switch (stream->type) { case TE_STREAM_CHAIN: ent->angles[2] = 0; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; break; case TE_STREAM_SUNSTAFF1: ent->angles[2] = (int)(cl.time*10)%360; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; //ent->frame = (int)(cl.time*20)%20; ent = NewStreamEntity(); if (!ent) return; VectorCopy(org, ent->origin); ent->model = stream->models[1]; ent->angles[0] = pitch; ent->angles[1] = yaw; ent->angles[2] = (int)(cl.time*50)%360; ent->drawflags = MLS_ABSLIGHT|DRF_TRANSLUCENT; ent->abslight = 128; break; case TE_STREAM_SUNSTAFF2: ent->angles[2] = (int)(cl.time*10)%360; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; ent->frame = (int)(cl.time*10)%8; break; case TE_STREAM_LIGHTNING: if (stream->endTime < cl.time) {//fixme: keep last non-translucent frame and angle ent->drawflags = MLS_ABSLIGHT|DRF_TRANSLUCENT; ent->abslight = 128 + (stream->endTime - cl.time)*192; } else { ent->angles[2] = rand() % 360; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; ent->frame = rand() % 6; } break; case TE_STREAM_LIGHTNING_SMALL: if (stream->endTime < cl.time) { ent->drawflags = MLS_ABSLIGHT|DRF_TRANSLUCENT; ent->abslight = 128 + (stream->endTime - cl.time)*192; } else { ent->angles[2] = rand() % 360; ent->frame = rand() % 6; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; } break; case TE_STREAM_FAMINE: ent->angles[2] = rand() % 360; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; ent->frame = 0; break; case TE_STREAM_COLORBEAM: ent->angles[2] = 0; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; ent->skinnum = stream->skin; break; case TE_STREAM_GAZE: ent->angles[2] = 0; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; ent->frame = (int)(cl.time*40)%36; break; case TE_STREAM_ICECHUNKS: ent->angles[2] = rand() % 360; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; ent->frame = rand() % 5; break; default: ent->angles[2] = 0; } for (j = 0; j < 3; j++) org[j] += dist[j]*30; d -= 30; } if (stream->type == TE_STREAM_SUNSTAFF1 || stream->type == TE_STREAM_SUNSTAFF2) { if (stream->lastTrailTime+0.2 < cl.time) { stream->lastTrailTime = cl.time; R_SunStaffTrail(stream->source, stream->dest); } ent = NewStreamEntity(); if (ent == NULL) return; VectorCopy(stream->dest, ent->origin); ent->model = stream->models[2]; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; ent->scale = 80 + (rand() & 15); //ent->frame = (int)(cl.time*20)%20; ent = NewStreamEntity(); if (ent == NULL) return; VectorCopy(stream->dest, ent->origin); ent->model = stream->models[3]; ent->drawflags = MLS_ABSLIGHT|DRF_TRANSLUCENT; ent->abslight = 128; ent->scale = 150 + (rand() & 15); } } } //========================================================================== // // NewStreamEntity // //========================================================================== static entity_t *NewStreamEntity(void) { entity_t *ent; if (cl_numvisedicts == MAX_VISEDICTS) return NULL; if (StreamEntityCount == MAX_STREAM_ENTITIES) return NULL; ent = &StreamEntities[StreamEntityCount++]; memset(ent, 0, sizeof(*ent)); cl_visedicts[cl_numvisedicts++] = ent; ent->colormap = vid.colormap; return ent; } engine/hexen2/client.h000066400000000000000000000261261444734033100151550ustar00rootroot00000000000000/* hexen2/client.h -- client main header * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_CLIENT_H #define __HX2_CLIENT_H #define MAX_SCOREBOARDNAME 32 typedef struct { char name[MAX_SCOREBOARDNAME]; float entertime; int frags; int colors; // two 4 bit fields byte translations[VID_GRADES*256]; float playerclass; } scoreboard_t; typedef struct { int destcolor[3]; int percent; // 0-256 } cshift_t; #define CSHIFT_CONTENTS 0 #define CSHIFT_DAMAGE 1 #define CSHIFT_BONUS 2 #define CSHIFT_POWERUP 3 #define CSHIFT_INTERVENTION 4 #define NUM_CSHIFTS 5 #define NAME_LENGTH 64 // // client_state_t should hold all pieces of the client state // #define SIGNONS 4 // signon messages to receive before connected #define MAX_DLIGHTS 32 typedef struct { vec3_t origin; float radius; float die; // stop lighting after this time float decay; // drop this each second float minlight; // don't add when contributing less int key; qboolean dark; // subtracts light instead of adding float color[4]; // LordHavoc: colored lights support } dlight_t; typedef struct { int length; char map[MAX_STYLESTRING]; } lightstyle_t; #define MAX_EFRAGS 640 #define MAX_MAPSTRING 2048 #define MAX_DEMOS 8 #define MAX_DEMONAME 16 typedef enum { ca_dedicated, // a dedicated server with no ability to start a client ca_disconnected, // full screen console with no connection ca_connected, // valid netcon, talking to a server ca_active = ca_connected // simply an alias for hexenworld compatibility } cactive_t; // // the client_static_t structure is persistant through an arbitrary number // of server connections // typedef struct { cactive_t state; // personalization data sent to server char spawnparms[MAX_MAPSTRING]; // to restart a level // demo loop control int demonum; // -1 = don't play demos char demos[MAX_DEMOS][MAX_DEMONAME]; // when not playing // demo recording info must be here, because record is started before // entering a map (and clearing client_state_t) qboolean demorecording; qboolean demoplayback; qboolean timedemo; int forcetrack; // -1 = use normal cd track FILE *demofile; // FILE *introdemofile; int td_lastframe; // to meter out one message a frame int td_startframe; // host_framecount at start float td_starttime; // realtime at second frame of timedemo // connection information int signon; // 0 to SIGNONS struct qsocket_s *netcon; sizebuf_t message; // writing buffer to send to server } client_static_t; extern client_static_t cls; // // the client_state_t structure is wiped completely at every // server signon // typedef struct { int movemessages; // since connecting to this server // throw out the first couple, so the player // doesn't accidentally do something the // first frame usercmd_t cmd; // last command sent to the server // information for local display int stats[MAX_CL_STATS]; // health, etc int inv_order[MAX_INVENTORY]; int inv_count, inv_startpos, inv_selected; int items; // inventory bit flags float item_gettime[32]; // cl.time of aquiring item, for blinking float faceanimtime; // use anim frame if cl.time < this entvars_t v; // NOTE: not every field will be update // you must specifically add them in // functions SV_WriteClientdatatToMessage() // and CL_ParseClientdata() cshift_t cshifts[NUM_CSHIFTS]; // color shifts for damage, powerups cshift_t prev_cshifts[NUM_CSHIFTS]; // and content types char puzzle_pieces[8][10]; // puzzle piece names // the client maintains its own idea of view angles, which are // sent to the server each frame. The server sets punchangle when // the view is temporarliy offset, and an angle reset commands at the start // of each level and after teleporting. vec3_t mviewangles[2]; // during demo playback viewangles is lerped // between these vec3_t viewangles; vec3_t mvelocity[2]; // update by server, used for lean+bob // (0 is newest) vec3_t velocity; // lerped between mvelocity[0] and [1] vec3_t punchangle; // temporary offset float idealroll; float rollvel; // pitch drifting vars float idealpitch; float pitchvel; qboolean nodrift; float driftmove; double laststop; float viewheight; float crouch; // local amount for smoothing stepups qboolean paused; // send over by server qboolean onground; qboolean inwater; // intermissions: setup by CL_SetupIntermission() and run by SB_IntermissionOverlay() int intermission; // don't change view angle, full screen, etc int completed_time; // latched at intermission start int message_index; int intermission_flags; const char *intermission_pic; int lasting_time; int intermission_next; double mtime[2]; // the timestamp of last two messages double time; // clients view of time, should be between // servertime and oldservertime to generate // a lerp point for other data double oldtime; // previous cl.time, time-oldtime is used // to decay light values and smooth step ups float last_received_message; // (realtime) for net trouble icon // // information that is static for the entire time connected to a server // struct qmodel_s *model_precache[MAX_MODELS]; struct sfx_s *sound_precache[MAX_SOUNDS]; char mapname[40]; char levelname[40]; // for display on solo scoreboard int viewentity; // cl_entitites[cl.viewentity] = player int maxclients; int gametype; // refresh related state struct qmodel_s *worldmodel; // cl_entitites[0].model struct efrag_s *free_efrags; int num_entities; // held in cl_entities array int num_statics; // held in cl_staticentities array entity_t viewent; // the gun model struct EffectT Effects[MAX_EFFECTS]; int cdtrack, looptrack; // cd audio char midi_name[128]; // midi file name byte current_frame, last_frame, reference_frame; byte current_sequence, last_sequence; byte need_build; // frag scoreboard scoreboard_t *scores; // [cl.maxclients] // light level at player's position including dlights // this is sent back to the server each frame // architectually ugly but it works int light_level; client_frames2_t frames[3]; // 0 = base, 1 = building, 2 = 0 & 1 merged short RemoveList[MAX_CLIENT_STATES], NumToRemove; // mission pack, objectives strings unsigned int info_mask, info_mask2; } client_state_t; // // cvars // extern cvar_t cl_name; extern cvar_t cl_color; extern cvar_t cl_playerclass; extern cvar_t cl_upspeed; extern cvar_t cl_forwardspeed; extern cvar_t cl_backspeed; extern cvar_t cl_sidespeed; extern cvar_t cl_movespeedkey; extern cvar_t cl_yawspeed; extern cvar_t cl_pitchspeed; extern cvar_t cl_anglespeedkey; extern cvar_t cl_shownet; extern cvar_t cl_nolerp; extern cvar_t cfg_unbindall; extern cvar_t cl_pitchdriftspeed; extern cvar_t lookspring; extern cvar_t lookstrafe; extern cvar_t sensitivity; extern cvar_t m_pitch; extern cvar_t m_yaw; extern cvar_t m_forward; extern cvar_t m_side; #define MAX_STATIC_ENTITIES 256 // torches, etc extern client_state_t cl; // FIXME, allocate dynamically extern efrag_t cl_efrags[MAX_EFRAGS]; extern entity_t cl_entities[MAX_EDICTS]; extern entity_t cl_static_entities[MAX_STATIC_ENTITIES]; extern lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; extern dlight_t cl_dlights[MAX_DLIGHTS]; //============================================================================= // // cl_main // dlight_t *CL_AllocDlight (int key); void CL_DecayLights (void); void CL_Init (void); void CL_ClearState (void); void CL_EstablishConnection (const char *host); void CL_SignonReply (void); int CL_ReadFromServer (void); void CL_Disconnect (void); void CL_Disconnect_f (void); void CL_NextDemo (void); #define MAX_VISEDICTS 256 extern int cl_numvisedicts; extern entity_t *cl_visedicts[MAX_VISEDICTS]; // // cl_cmd // void Cmd_ForwardToServer (void); void CL_Cmd_Init (void); // // cl_input // typedef struct { int down[2]; // key nums holding it down int state; // low bit is down state } kbutton_t; extern kbutton_t in_mlook, in_klook; extern kbutton_t in_strafe; extern kbutton_t in_speed; extern int in_impulse; extern qboolean info_up; void CL_InitInput (void); void CL_SendCmd (void); void CL_BaseMove (usercmd_t *cmd); void CL_SendMove (const usercmd_t *cmd); // // cl_demo.c // void CL_StopPlayback (void); int CL_GetMessage (void); void CL_Stop_f (void); void CL_Record_f (void); void CL_PlayDemo_f (void); void CL_TimeDemo_f (void); extern qboolean intro_playing; /* whether the mission pack intro is playing */ extern qboolean skip_start; /* for the mission pack intro */ extern int num_intro_msg; /* for the mission pack intro */ /* skip_start and num_intro_msg are not used at present - O.S */ // // cl_string.c // extern int puzzle_string_count; void CL_LoadPuzzleStrings (void); const char *CL_FindPuzzleString (const char *shortname); /* mission pack objectives strings */ extern int info_string_count; void CL_LoadInfoStrings (void); const char *CL_GetInfoString (int idx); // // cl_interlude.c // #define INTERMISSION_NOT_CONNECTED (1<<0) /* can not use cl.time, use realtime */ #define INTERMISSION_NO_MENUS (1<<1) /* don't allow drawing the menus */ #define INTERMISSION_NO_MESSAGE (1<<2) /* doesn't need a valid message index */ #define INTERMISSION_PRINT_TOP (1<<3) /* print centered in top half of screen */ #define INTERMISSION_PRINT_TOPMOST (1<<4) /* print at top-most side of the screen */ /* without either of the above two, prints centered on the whole screen */ #define INTERMISSION_PRINT_WHITE (1<<5) /* print in white, not in red */ #define INTERMISSION_PRINT_DELAY (1<<6) /* delay message print for ca. 2.5s */ void CL_SetupIntermission (int n); // // cl_parse.c // void CL_ParseServerMessage (void); extern int cl_protocol; /* protocol version used by the server */ // // view // void V_StartPitchDrift (void); void V_StopPitchDrift (void); void V_RenderView (void); void V_UpdatePalette (void); void V_Register (void); void V_ParseDamage (void); void V_SetContentsColor (int contents); // // cl_effect // void CL_InitEffects (void); void CL_ClearEffects (void); void CL_EndEffect (void); void CL_ParseEffect (void); void CL_UpdateEffects (void); // // cl_tent // void CL_InitTEnts (void); void CL_ClearTEnts (void); void CL_ParseTEnt (void); void CL_UpdateTEnts (void); // // chase // extern cvar_t chase_active; void Chase_Init (void); void Chase_Reset (void); void Chase_Update (void); #endif /* __HX2_CLIENT_H */ engine/hexen2/console.c000066400000000000000000000312331444734033100153270ustar00rootroot00000000000000/* console.c -- in-game console and chat message buffer handling * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "debuglog.h" console_t *con; qboolean con_initialized; static int con_linewidth; // characters across screen static int con_vislines; int con_notifylines; // scan lines to clear for notify lines int con_totallines; // total lines in console scrollback static float con_cursorspeed = 4; qboolean con_forcedup; // because no entities to refresh int con_ormask; static cvar_t con_notifytime = {"con_notifytime", "3", CVAR_NONE}; //seconds #define NUM_CON_TIMES 4 static float con_times[NUM_CON_TIMES]; // realtime time the line was generated // for transparent notify lines extern qboolean menu_disabled_mouse; static void Key_ClearTyping (void) { key_lines[edit_line][1] = 0; // clear any typing key_linepos = 1; } /* ================ Con_ToggleConsole_f ================ */ void Con_ToggleConsole_f (void) { keydest_t dest = Key_GetDest(); // activate mouse when in console in // case it is disabled somewhere else menu_disabled_mouse = false; IN_ActivateMouse (); Key_ClearTyping (); if (dest == key_console || (dest == key_game && con_forcedup)) { if (cls.state == ca_active) Key_SetDest (key_game); else M_Menu_Main_f (); } else { Key_SetDest (key_console); } SCR_EndLoadingPlaque (); Con_ClearNotify (); } /* ================ Con_Clear_f ================ */ static void Con_Clear_f (void) { int i; for (i = 0; i < CON_TEXTSIZE; i++) con->text[i] = ' '; } /* ================ Con_ClearNotify ================ */ void Con_ClearNotify (void) { int i; for (i = 0; i < NUM_CON_TIMES; i++) con_times[i] = 0; } /* ================ Con_MessageMode_f ================ */ static void Con_MessageMode_f (void) { if (cls.state != ca_active || cls.demoplayback) return; chat_team = false; Key_SetDest (key_message); } /* ================ Con_MessageMode2_f ================ */ static void Con_MessageMode2_f (void) { if (cls.state != ca_active || cls.demoplayback) return; chat_team = true; Key_SetDest (key_message); } /* ================ Con_CheckResize If the line width has changed, reformat the buffer. ================ */ void Con_CheckResize (void) { int i, j, width, oldwidth, oldtotallines, numlines, numchars; short tbuf[CON_TEXTSIZE]; width = (vid.width >> 3) - 2; if (width == con_linewidth) return; if (width < 1) // video hasn't been initialized yet { width = 38; con_linewidth = width; con_totallines = CON_TEXTSIZE / con_linewidth; Con_Clear_f(); } else { oldwidth = con_linewidth; con_linewidth = width; oldtotallines = con_totallines; con_totallines = CON_TEXTSIZE / con_linewidth; numlines = oldtotallines; if (con_totallines < numlines) numlines = con_totallines; numchars = oldwidth; if (con_linewidth < numchars) numchars = con_linewidth; memcpy (tbuf, con->text, CON_TEXTSIZE*sizeof(short)); Con_Clear_f(); for (i = 0; i < numlines; i++) { for (j = 0; j < numchars; j++) { con->text[(con_totallines - 1 - i) * con_linewidth + j] = tbuf[((con->current - i + oldtotallines) % oldtotallines) * oldwidth + j]; } } Con_ClearNotify (); } con->current = con_totallines - 1; con->display = con->current; } /* ================ Con_Init ================ */ void Con_Init (void) { con = (console_t *) Hunk_AllocName (sizeof(console_t), "con_main"); con_linewidth = -1; Con_CheckResize (); Con_Printf ("Console initialized.\n"); Cvar_RegisterVariable (&con_notifytime); Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f); Cmd_AddCommand ("messagemode", Con_MessageMode_f); Cmd_AddCommand ("messagemode2", Con_MessageMode2_f); Cmd_AddCommand ("clear", Con_Clear_f); con_initialized = true; } /* =============== Con_Linefeed =============== */ static void Con_Linefeed (void) { int i, j; con->x = 0; if (con->display == con->current) con->display++; con->current++; j = (con->current%con_totallines) * con_linewidth; for (i = 0; i < con_linewidth; i++) con->text[i+j] = ' '; } /* ================ Con_Print Handles cursor positioning, line wrapping, etc All console printing must go through this in order to be logged to disk If no console is visible, the notify window will pop up. ================ */ static void Con_Print (const char *txt) { int y; int c, l; static int cr; int mask; qboolean boundary; if (txt[0] == 1) { mask = 256; // go to colored text S_LocalSound ("misc/comm.wav"); // play talk wav txt++; } else if (txt[0] == 2) { mask = 256; // go to colored text txt++; } else mask = 0; boundary = true; while ( (c = (byte)*txt) ) { if (c <= ' ') { boundary = true; } else if (boundary) { // count word length for (l = 0; l < con_linewidth; l++) if (txt[l] <= ' ') break; // word wrap if (l != con_linewidth && (con->x + l > con_linewidth)) con->x = 0; boundary = false; } txt++; if (cr) { con->current--; cr = false; } if (!con->x) { Con_Linefeed (); // mark time for transparent overlay if (con->current >= 0) con_times[con->current % NUM_CON_TIMES] = realtime; } switch (c) { case '\n': con->x = 0; break; case '\r': con->x = 0; cr = 1; break; default: // display character and advance y = con->current % con_totallines; con->text[y*con_linewidth+con->x] = c | mask | con_ormask; con->x++; if (con->x >= con_linewidth) con->x = 0; break; } } } /* ================ CON_Printf Prepare the message to be printed and send it to the proper handlers. ================ */ void CON_Printf (unsigned int flags, const char *fmt, ...) { va_list argptr; char msg[MAX_PRINTMSG]; static qboolean inupdate; if (flags & _PRINT_DEVEL && !developer.integer) { if (con_debuglog & LOG_DEVEL) /* full logging */ { va_start (argptr, fmt); q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); LOG_Print (msg); } return; } va_start (argptr, fmt); q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); Sys_PrintTerm (msg); // echo to the terminal if (con_debuglog) LOG_Print (msg); if (flags & _PRINT_TERMONLY || !con_initialized) return; if (cls.state == ca_dedicated) return; // no graphics mode // write it to the scrollable buffer Con_Print (msg); if (flags & _PRINT_SAFE) return; // safe: doesn't update the screen // update the screen immediately if the console is displayed if (cls.signon != SIGNONS && !scr_disabled_for_loading ) { // protect against infinite loop if SCR_UpdateScreen // itself calls Con_Printf if (!inupdate) { inupdate = true; SCR_UpdateScreen (); inupdate = false; } } } /* ================== Con_ShowList Tyrann's ShowList ported by S.A.: Prints a given list to the console with columnized formatting ================== */ void Con_ShowList (int cnt, const char **list) { const char *s; char *line; int i, j, max_len, len, cols, rows; // Lay them out in columns max_len = 0; for (i = 0; i < cnt; ++i) { len = (int) strlen(list[i]); if (len > max_len) max_len = len; } line = (char *) Z_Malloc(con_linewidth + 1, Z_MAINZONE); cols = con_linewidth / (max_len + 2); rows = cnt / cols + 1; // Looks better if we have a few rows before spreading out if (rows < 5) { cols = cnt / 5 + 1; rows = cnt / cols + 1; } for (i = 0; i < rows; ++i) { line[0] = '\0'; for (j = 0; j < cols; ++j) { if (j * rows + i >= cnt) break; s = list[j * rows + i]; len = (int) strlen(s); q_strlcat(line, s, con_linewidth+1); if (j < cols - 1) { while (len < max_len) { q_strlcat(line, " ", con_linewidth+1); len++; } q_strlcat(line, " ", con_linewidth+1); } } if (line[0] != '\0') Con_Printf("%s\n", line); } Z_Free(line); } /* ============================================================================== DRAWING ============================================================================== */ /* ================ Con_DrawInput The input line scrolls horizontally if typing goes beyond the right edge ================ */ static void Con_DrawInput (void) { int i, y; size_t pos; char editlinecopy[MAXCMDLINE], *text; if (Key_GetDest() != key_console && !con_forcedup) return; // don't draw anything pos = q_strlcpy(editlinecopy, key_lines[edit_line], sizeof(editlinecopy)); text = editlinecopy; // fill out remainder with spaces for ( ; pos < MAXCMDLINE; ++pos) text[pos] = ' '; // add the cursor frame if ((int)(realtime * con_cursorspeed) & 1) // cursor is visible text[key_linepos] = (key_insert) ? 11 : 95; // underscore for overwrite mode, square for insert // prestep if horizontally scrolling if (key_linepos >= con_linewidth) text += 1 + key_linepos - con_linewidth; // draw it y = con_vislines - 22; for (i = 0; i < con_linewidth; i++) Draw_Character ((i + 1)<<3, y, text[i]); } /* ================ Con_DrawNotify Draws the last few lines of output transparently over the game top ================ */ void Con_DrawNotify (void) { int i, x, v; const short *text; float time; v = 0; for (i = con->current-NUM_CON_TIMES+1; i <= con->current; i++) { if (i < 0) continue; time = con_times[i % NUM_CON_TIMES]; if (time == 0) continue; time = realtime - time; if (time > con_notifytime.value) continue; text = con->text + (i % con_totallines)*con_linewidth; if (scr_viewsize.integer < 100) clearnotify = 0; scr_copytop = 1; for (x = 0; x < con_linewidth; x++) Draw_Character ((x+1)<<3, v, text[x]); v += 8; } if (Key_GetDest() == key_message) { const char *s; if (scr_viewsize.integer < 100) clearnotify = 0; scr_copytop = 1; if (chat_team) { Draw_String (8, v, "say_team:"); x = 11; } else { Draw_String (8, v, "say:"); x = 6; } s = Key_GetChatBuffer(); i = Key_GetChatMsgLen(); if (i > (vid.width>>3) - x - 1) s += i - (vid.width>>3) + x + 1; while (*s) { Draw_Character (x<<3, v, *s); s++; x++; } Draw_Character (x<<3, v, 10 + ((int)(realtime*con_cursorspeed)&1)); v += 8; } if (v > con_notifylines) con_notifylines = v; } /* ================ Con_DrawConsole Draws the console with the solid background ================ */ void Con_DrawConsole (int lines) { int i, x, y; int row, rows; const short *text; if (lines <= 0) return; // draw the background Draw_ConsoleBackground (lines); // draw the text con_vislines = lines; // changed to line things up better rows = (lines-22)>>3; // rows of text to draw y = lines - 30; // draw from the bottom up if (con->display != con->current) { // draw arrows to show the buffer is backscrolled for (x = 0; x < con_linewidth; x += 4) Draw_Character ( (x+1)<<3, y, '^'); y -= 8; rows--; } row = con->display; for (i = 0; i < rows; i++, y -= 8, row--) { if (row < 0) break; if (con->current - row >= con_totallines) break; // past scrollback wrap point text = con->text + (row % con_totallines)*con_linewidth; for (x = 0; x < con_linewidth; x++) Draw_Character ( (x+1)<<3, y, text[x]); } // draw the input prompt, user text, and cursor if desired Con_DrawInput (); } /* ================== Con_NotifyBox ================== */ void Con_NotifyBox (const char *text) { double t1, t2; // during startup for sound / cd warnings Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n"); Con_Printf ("%s", text); Con_Printf ("Press a key.\n"); Con_Printf("\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n"); key_count = -2; // wait for a key down and up Key_SetDest (key_console); do { t1 = Sys_DoubleTime (); SCR_UpdateScreen (); Sys_SendKeyEvents (); t2 = Sys_DoubleTime (); realtime += t2-t1; // make the cursor blink } while (key_count < 0); Con_Printf ("\n"); Key_SetDest (key_game); realtime = 0; // put the cursor back to invisible } engine/hexen2/effects.h000066400000000000000000000122301444734033100153050ustar00rootroot00000000000000/* effects.h -- effect types and defs for Hexen II * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __EFFECTS_H #define __EFFECTS_H #define MAX_EFFECTS 256 /* Types for various chunks */ #define THINGTYPE_GREYSTONE 1 #define THINGTYPE_WOOD 2 #define THINGTYPE_METAL 3 #define THINGTYPE_FLESH 4 #define THINGTYPE_FIRE 5 #define THINGTYPE_CLAY 6 #define THINGTYPE_LEAVES 7 #define THINGTYPE_HAY 8 #define THINGTYPE_BROWNSTONE 9 #define THINGTYPE_CLOTH 10 #define THINGTYPE_WOOD_LEAF 11 #define THINGTYPE_WOOD_METAL 12 #define THINGTYPE_WOOD_STONE 13 #define THINGTYPE_METAL_STONE 14 #define THINGTYPE_METAL_CLOTH 15 #define THINGTYPE_WEBS 16 #define THINGTYPE_GLASS 17 #define THINGTYPE_ICE 18 #define THINGTYPE_CLEARGLASS 19 #define THINGTYPE_REDGLASS 20 #define THINGTYPE_ACID 21 #define THINGTYPE_METEOR 22 #define THINGTYPE_GREENFLESH 23 #define THINGTYPE_BONE 24 #define CE_NONE 0 #define CE_RAIN 1 #define CE_FOUNTAIN 2 #define CE_QUAKE 3 #define CE_WHITE_SMOKE 4 /* whtsmk1.spr */ #define CE_BLUESPARK 5 /* bspark.spr */ #define CE_YELLOWSPARK 6 /* spark.spr */ #define CE_SM_CIRCLE_EXP 7 /* fcircle.spr */ #define CE_BG_CIRCLE_EXP 8 /* fcircle.spr */ #define CE_SM_WHITE_FLASH 9 /* sm_white.spr */ #define CE_WHITE_FLASH 10 /* gryspt.spr */ #define CE_YELLOWRED_FLASH 11 /* yr_flash.spr */ #define CE_BLUE_FLASH 12 /* bluflash.spr */ #define CE_SM_BLUE_FLASH 13 /* bluflash.spr */ #define CE_RED_FLASH 14 /* redspt.spr */ #define CE_SM_EXPLOSION 15 /* sm_expld.spr */ #define CE_LG_EXPLOSION 16 /* bg_expld.spr */ #define CE_FLOOR_EXPLOSION 17 /* fl_expld.spr */ #define CE_RIDER_DEATH 18 #define CE_BLUE_EXPLOSION 19 /* xpspblue.spr */ #define CE_GREEN_SMOKE 20 /* grnsmk1.spr */ #define CE_GREY_SMOKE 21 /* grysmk1.spr */ #define CE_RED_SMOKE 22 /* redsmk1.spr */ #define CE_SLOW_WHITE_SMOKE 23 /* whtsmk1.spr */ #define CE_REDSPARK 24 /* rspark.spr */ #define CE_GREENSPARK 25 /* gspark.spr */ #define CE_TELESMK1 26 /* telesmk1.spr */ #define CE_TELESMK2 27 /* telesmk2.spr */ #define CE_ICEHIT 28 /* icehit.spr */ #define CE_MEDUSA_HIT 29 /* medhit.spr */ #define CE_MEZZO_REFLECT 30 /* mezzoref.spr */ #define CE_FLOOR_EXPLOSION2 31 /* flrexpl2.spr */ #define CE_XBOW_EXPLOSION 32 /* xbowexpl.spr */ #define CE_NEW_EXPLOSION 33 /* gen_expl.spr */ #define CE_MAGIC_MISSILE_EXPLOSION 34 /* mm_expld.spr */ #define CE_GHOST 35 /* ghost.spr */ #define CE_BONE_EXPLOSION 36 #define CE_REDCLOUD 37 #define CE_TELEPORTERPUFFS 38 #define CE_TELEPORTERBODY 39 #define CE_BONESHARD 40 #define CE_BONESHRAPNEL 41 /* New for Mission Pack... */ #define CE_FLAMESTREAM 42 /* Flamethrower */ #define CE_SNOW 43 #define CE_GRAVITYWELL 44 #define CE_BLDRN_EXPL 45 #define CE_ACID_MUZZFL 46 #define CE_ACID_HIT 47 #define CE_FIREWALL_SMALL 48 #define CE_FIREWALL_MEDIUM 49 #define CE_FIREWALL_LARGE 50 #define CE_LBALL_EXPL 51 #define CE_ACID_SPLAT 52 #define CE_ACID_EXPL 53 #define CE_FBOOM 54 #define CE_CHUNK 55 #define CE_BOMB 56 #define CE_BRN_BOUNCE 57 #define CE_LSHOCK 58 #define CE_FLAMEWALL 59 #define CE_FLAMEWALL2 60 #define CE_FLOOR_EXPLOSION3 61 #define CE_ONFIRE 62 struct EffectT { int type; float expire_time; union { struct { vec3_t e_size, dir, min_org, max_org; float next_time, wait; int color, count, veer, flags; } Rain; struct { vec3_t pos, angle, movedir; vec3_t vforward, vup, vright; int color, cnt; } Fountain; struct { vec3_t origin; float radius; } Quake; struct { vec3_t origin; vec3_t velocity; int entity_index; float time_amount, framelength, frame; } Smoke; struct { vec3_t origin; int entity_index; float time_amount; int reverse; /* Forward animation has been run, now go backwards */ } Flash; struct { vec3_t origin; int entity_index[13]; float time_amount; int stage; int color; float lifetime; } RD; /* Rider Death */ struct { int entity_index[16]; vec3_t origin; vec3_t velocity[16]; float time_amount, framelength; float skinnum; } Teleporter; struct { vec3_t angle; vec3_t origin; vec3_t avelocity; vec3_t velocity; int entity_index; float time_amount; } Missile; struct { int entity_index[16]; vec3_t velocity[16]; vec3_t avel[3]; vec3_t origin; unsigned char type; vec3_t srcVel; unsigned char numChunks; float time_amount; float aveScale; } Chunk; } ef; }; #endif /* __EFFECTS_H */ engine/hexen2/gl_rmain.c000066400000000000000000001346771444734033100154750ustar00rootroot00000000000000/* gl_main.c * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" entity_t r_worldentity; vec3_t modelorg, r_entorigin; int r_visframecount; // bumped when going to a new PVS int r_framecount; // used for dlight push checking mplane_t frustum[4]; int c_brush_polys, c_alias_polys; qboolean r_cache_thrash; // compatability GLuint currenttexture = GL_UNUSED_TEXTURE; // to avoid unnecessary texture sets GLuint particletexture; // little dot for particles GLuint playertextures[MAX_CLIENTS]; // up to MAX_CLIENTS color translated skins GLuint gl_extra_textures[MAX_EXTRA_TEXTURES]; // generic textures for models int mirrortexturenum; // quake texturenum, not gltexturenum qboolean mirror; mplane_t *mirror_plane; static float model_constant_alpha; static float r_time1; static float r_lasttime1 = 0; extern qmodel_t *player_models[MAX_PLAYER_CLASS]; // // view origin // vec3_t vup, vpn, vright, r_origin; float r_world_matrix[16]; // // screen size info // refdef_t r_refdef; mleaf_t *r_viewleaf, *r_oldviewleaf; texture_t *r_notexture_mip; int d_lightstylevalue[256]; // 8.8 fraction of base light value int gl_coloredstatic; // used to store what type of static light // we loaded in Mod_LoadLighting() static qboolean AlwaysDrawModel; cvar_t r_norefresh = {"r_norefresh", "0", CVAR_NONE}; cvar_t r_drawentities = {"r_drawentities", "1", CVAR_NONE}; cvar_t r_drawviewmodel = {"r_drawviewmodel", "1", CVAR_NONE}; cvar_t r_speeds = {"r_speeds", "0", CVAR_NONE}; cvar_t r_waterwarp = {"r_waterwarp", "0", CVAR_ARCHIVE}; cvar_t r_fullbright = {"r_fullbright", "0", CVAR_NONE}; cvar_t r_lightmap = {"r_lightmap", "0", CVAR_NONE}; cvar_t r_shadows = {"r_shadows", "0", CVAR_NONE}; cvar_t r_mirroralpha = {"r_mirroralpha", "1", CVAR_NONE}; cvar_t r_wateralpha = {"r_wateralpha", "0.33", CVAR_ARCHIVE}; cvar_t r_skyalpha = {"r_skyalpha", "0.67", CVAR_ARCHIVE}; cvar_t r_dynamic = {"r_dynamic", "1", CVAR_NONE}; cvar_t r_novis = {"r_novis", "0", CVAR_NONE}; cvar_t r_wholeframe = {"r_wholeframe", "1", CVAR_ARCHIVE}; cvar_t r_clearcolor = {"r_clearcolor", "0", CVAR_ARCHIVE}; cvar_t r_texture_external = {"r_texture_external", "0", CVAR_ARCHIVE}; cvar_t gl_clear = {"gl_clear", "1", CVAR_NONE}; cvar_t gl_cull = {"gl_cull", "1", CVAR_NONE}; cvar_t gl_ztrick = {"gl_ztrick", "0", CVAR_ARCHIVE}; cvar_t gl_zfix = {"gl_zfix", "1", CVAR_ARCHIVE}; cvar_t gl_smoothmodels = {"gl_smoothmodels", "1", CVAR_NONE}; cvar_t gl_affinemodels = {"gl_affinemodels", "0", CVAR_NONE}; cvar_t gl_polyblend = {"gl_polyblend", "1", CVAR_NONE}; cvar_t gl_flashblend = {"gl_flashblend", "0", CVAR_NONE}; cvar_t gl_playermip = {"gl_playermip", "0", CVAR_NONE}; cvar_t gl_nocolors = {"gl_nocolors", "0", CVAR_NONE}; cvar_t gl_keeptjunctions = {"gl_keeptjunctions", "1", CVAR_ARCHIVE}; cvar_t gl_reporttjunctions = {"gl_reporttjunctions", "0", CVAR_NONE}; cvar_t gl_waterripple = {"gl_waterripple", "2", CVAR_ARCHIVE}; cvar_t gl_glows = {"gl_glows", "0", CVAR_ARCHIVE}; // torch glows cvar_t gl_other_glows = {"gl_other_glows", "0", CVAR_ARCHIVE}; cvar_t gl_missile_glows = {"gl_missile_glows", "1", CVAR_ARCHIVE}; cvar_t gl_coloredlight = {"gl_coloredlight", "0", CVAR_ARCHIVE}; cvar_t gl_colored_dynamic_lights = {"gl_colored_dynamic_lights", "0", CVAR_ARCHIVE}; cvar_t gl_extra_dynamic_lights = {"gl_extra_dynamic_lights", "0", CVAR_ARCHIVE}; //============================================================================= /* ================= R_CullBox Returns true if the box is completely outside the frustom ================= */ qboolean R_CullBox (vec3_t mins, vec3_t maxs) { int i; for (i = 0; i < 4; i++) { if (BoxOnPlaneSide (mins, maxs, &frustum[i]) == 2) return true; } return false; } /* ================= R_RotateForEntity ================= */ void R_RotateForEntity (entity_t *e) { glTranslatef_fp (e->origin[0], e->origin[1], e->origin[2]); glRotatef_fp (e->angles[1], 0, 0, 1); glRotatef_fp (-e->angles[0], 0, 1, 0); glRotatef_fp (-e->angles[2], 1, 0, 0); } /* ================= R_RotateForEntity2 Same as R_RotateForEntity(), but checks for EF_ROTATE and modifies yaw appropriately. ================= */ static void R_RotateForEntity2 (entity_t *e) { float forward, yaw, pitch; vec3_t angles; glTranslatef_fp(e->origin[0], e->origin[1], e->origin[2]); if (e->model->flags & EF_FACE_VIEW) { VectorSubtract(e->origin,r_origin,angles); VectorSubtract(r_origin,e->origin,angles); VectorNormalize(angles); if (angles[1] == 0 && angles[0] == 0) { yaw = 0; if (angles[2] > 0) pitch = 90; else pitch = 270; } else { yaw = (int) (atan2(angles[1], angles[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; forward = sqrt (angles[0]*angles[0] + angles[1]*angles[1]); pitch = (int) (atan2(angles[2], forward) * 180 / M_PI); if (pitch < 0) pitch += 360; } angles[0] = pitch; angles[1] = yaw; angles[2] = 0; glRotatef_fp (-angles[0], 0, 1, 0); glRotatef_fp (angles[1], 0, 0, 1); // glRotatef_fp (-angles[2], 1, 0, 0); glRotatef_fp (-e->angles[2], 1, 0, 0); } else { if (e->model->flags & EF_ROTATE) { glRotatef_fp (anglemod((e->origin[0] + e->origin[1])*0.8 + (108*cl.time)), 0, 0, 1); } else { glRotatef_fp (e->angles[1], 0, 0, 1); } glRotatef_fp (-e->angles[0], 0, 1, 0); glRotatef_fp (-e->angles[2], 1, 0, 0); } } /* ============================================================= SPRITE MODELS ============================================================= */ /* ================ R_GetSpriteFrame ================ */ static mspriteframe_t *R_GetSpriteFrame (entity_t *e) { msprite_t *psprite; mspritegroup_t *pspritegroup; mspriteframe_t *pspriteframe; int i, numframes, frame; float *pintervals, fullinterval, targettime, time; psprite = (msprite_t *) e->model->cache.data; frame = e->frame; if ((frame >= psprite->numframes) || (frame < 0)) { Con_DPrintf ("%s: no such frame %d\n", __thisfunc__, frame); frame = 0; } if (psprite->frames[frame].type == SPR_SINGLE) { pspriteframe = psprite->frames[frame].frameptr; } else { pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; pintervals = pspritegroup->intervals; numframes = pspritegroup->numframes; fullinterval = pintervals[numframes-1]; time = cl.time + e->syncbase; // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values // are positive, so we don't have to worry about division by 0 targettime = time - ((int)(time / fullinterval)) * fullinterval; for (i = 0; i < (numframes-1); i++) { if (pintervals[i] > targettime) break; } pspriteframe = pspritegroup->frames[i]; } return pspriteframe; } /* ================= R_DrawSpriteModel ================= */ typedef struct { vec3_t vup, vright, vpn; // in worldspace } spritedesc_t; static void R_DrawSpriteModel (entity_t *e) { vec3_t point; mspriteframe_t *frame; msprite_t *psprite; vec3_t tvec; float dot, angle, sr, cr; spritedesc_t r_spritedesc; int i; frame = R_GetSpriteFrame (e); psprite = (msprite_t *) e->model->cache.data; if (psprite->type == SPR_FACING_UPRIGHT) { // generate the sprite's axes, with vup straight up in worldspace, and // r_spritedesc.vright perpendicular to modelorg. // This will not work if the view direction is very close to straight up or // down, because the cross product will be between two nearly parallel // vectors and starts to approach an undefined state, so we don't draw if // the two vectors are less than 1 degree apart tvec[0] = -modelorg[0]; tvec[1] = -modelorg[1]; tvec[2] = -modelorg[2]; VectorNormalize (tvec); dot = tvec[2]; // same as DotProduct (tvec, r_spritedesc.vup) // because r_spritedesc.vup is 0, 0, 1 if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848 return; r_spritedesc.vup[0] = 0; r_spritedesc.vup[1] = 0; r_spritedesc.vup[2] = 1; r_spritedesc.vright[0] = tvec[1]; // CrossProduct (r_spritedesc.vup, -modelorg, r_spritedesc.vright[1] = -tvec[0]; // r_spritedesc.vright) r_spritedesc.vright[2] = 0; VectorNormalize (r_spritedesc.vright); r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; r_spritedesc.vpn[1] = r_spritedesc.vright[0]; r_spritedesc.vpn[2] = 0; // CrossProduct (r_spritedesc.vright, r_spritedesc.vup, // r_spritedesc.vpn) } else if (psprite->type == SPR_VP_PARALLEL) { // generate the sprite's axes, completely parallel to the viewplane. There // are no problem situations, because the sprite is always in the same // position relative to the viewer for (i = 0; i < 3; i++) { r_spritedesc.vup[i] = vup[i]; r_spritedesc.vright[i] = vright[i]; r_spritedesc.vpn[i] = vpn[i]; } } else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) { // generate the sprite's axes, with vup straight up in worldspace, and // r_spritedesc.vright parallel to the viewplane. // This will not work if the view direction is very close to straight up or // down, because the cross product will be between two nearly parallel // vectors and starts to approach an undefined state, so we don't draw if // the two vectors are less than 1 degree apart dot = vpn[2]; // same as DotProduct (vpn, r_spritedesc.vup) // because r_spritedesc.vup is 0, 0, 1 if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848 return; r_spritedesc.vup[0] = 0; r_spritedesc.vup[1] = 0; r_spritedesc.vup[2] = 1; r_spritedesc.vright[0] = vpn[1]; // CrossProduct (r_spritedesc.vup, vpn, r_spritedesc.vright[1] = -vpn[0]; // r_spritedesc.vright) r_spritedesc.vright[2] = 0; VectorNormalize (r_spritedesc.vright); r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; r_spritedesc.vpn[1] = r_spritedesc.vright[0]; r_spritedesc.vpn[2] = 0; // CrossProduct (r_spritedesc.vright, r_spritedesc.vup, // r_spritedesc.vpn) } else if (psprite->type == SPR_ORIENTED) { // generate the sprite's axes, according to the sprite's world orientation AngleVectors (e->angles, r_spritedesc.vpn, r_spritedesc.vright, r_spritedesc.vup); } else if (psprite->type == SPR_VP_PARALLEL_ORIENTED) { // generate the sprite's axes, parallel to the viewplane, but rotated in // that plane around the center according to the sprite entity's roll // angle. So vpn stays the same, but vright and vup rotate angle = e->angles[ROLL] * (M_PI*2 / 360); sr = sin(angle); cr = cos(angle); for (i = 0; i < 3; i++) { r_spritedesc.vpn[i] = vpn[i]; r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr; r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr; } } else { Sys_Error ("%s: Bad sprite type %d", __thisfunc__, psprite->type); } /* Pa3PyX: new translucency code below if (e->drawflags & DRF_TRANSLUCENT) { glEnable_fp (GL_BLEND); glColor4f_fp (1,1,1,r_wateralpha.value); } else if (e->model->flags & EF_TRANSPARENT) { glEnable_fp (GL_BLEND); glColor3f_fp (1,1,1); } else { glEnable_fp (GL_BLEND); glColor3f_fp (1,1,1); } */ /* Pa3PyX: new translucency mechanism (doesn't look as good, should work with non 3Dfx MiniGL drivers */ if ((e->drawflags & DRF_TRANSLUCENT) || (e->model->flags & EF_TRANSPARENT)) { glDisable_fp (GL_ALPHA_TEST); glEnable_fp (GL_BLEND); glTexEnvf_fp (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f_fp (1.0f, 1.0f, 1.0f, r_wateralpha.value); } else { /* pa3pyx's alpha code looks rather ugly, use the original one. glDisable_fp (GL_BLEND); glEnable_fp (GL_ALPHA_TEST); glAlphaFunc_fp (GL_GREATER, 0.632); glColor4f_fp (1.0f, 1.0f, 1.0f, 1.0f); */ /* here, we use the original alpha code */ glEnable_fp (GL_BLEND); glColor3f_fp (1,1,1); } GL_Bind(frame->gl_texturenum); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glBegin_fp (GL_QUADS); glTexCoord2f_fp (0, 1); VectorMA (e->origin, frame->down, r_spritedesc.vup, point); VectorMA (point, frame->left, r_spritedesc.vright, point); glVertex3fv_fp (point); glTexCoord2f_fp (0, 0); VectorMA (e->origin, frame->up, r_spritedesc.vup, point); VectorMA (point, frame->left, r_spritedesc.vright, point); glVertex3fv_fp (point); glTexCoord2f_fp (1, 0); VectorMA (e->origin, frame->up, r_spritedesc.vup, point); VectorMA (point, frame->right, r_spritedesc.vright, point); glVertex3fv_fp (point); glTexCoord2f_fp (1, 1); VectorMA (e->origin, frame->down, r_spritedesc.vup, point); VectorMA (point, frame->right, r_spritedesc.vright, point); glVertex3fv_fp (point); glEnd_fp (); // restore tex parms glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); /* for pa3pyx's translucency code changes above glDisable_fp (GL_BLEND); */ if ((e->drawflags & DRF_TRANSLUCENT) || (e->model->flags & EF_TRANSPARENT)) { glDisable_fp (GL_BLEND); glTexEnvf_fp (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } else { /* not using pa3pyx's alpha code (see above) glDisable_fp (GL_ALPHA_TEST); */ glDisable_fp (GL_BLEND); } } /* ============================================================= ALIAS MODELS ============================================================= */ static vec3_t shadevector; static float shadelight, ambientlight; // precalculated dot products for quantized angles #define SHADEDOT_QUANT 16 static float r_avertexnormal_dots[SHADEDOT_QUANT][256] = { #include "anorm_dots.h" }; static float *shadedots = r_avertexnormal_dots[0]; static int lastposenum; /* ============= GL_DrawAliasFrame ============= */ static void GL_DrawAliasFrame (entity_t *e, aliashdr_t *paliashdr, int posenum) { float l; trivertx_t *verts; int *order; int count; float r, g, b; byte ColorShade; lastposenum = posenum; verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts += posenum * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); ColorShade = e->colorshade; if (ColorShade) { r = RTint[ColorShade]; g = GTint[ColorShade]; b = BTint[ColorShade]; } else r = g = b = 1; while (1) { // get the vertex count and primitive type count = *order++; if (!count) break; // done if (count < 0) { count = -count; glBegin_fp (GL_TRIANGLE_FAN); } else glBegin_fp (GL_TRIANGLE_STRIP); do { // texture coordinates come from the draw list glTexCoord2f_fp (((float *)order)[0], ((float *)order)[1]); order += 2; // normals and vertexes come from the frame list if (gl_lightmap_format == GL_RGBA) { l = shadedots[verts->lightnormalindex]; glColor4f_fp (l * lightcolor[0], l * lightcolor[1], l * lightcolor[2], model_constant_alpha); } else { l = shadedots[verts->lightnormalindex] * shadelight; glColor4f_fp (r*l, g*l, b*l, model_constant_alpha); } glVertex3f_fp (verts->v[0], verts->v[1], verts->v[2]); verts++; } while (--count); glEnd_fp (); } } /* ============= GL_DrawAliasShadow ============= */ static void GL_DrawAliasShadow (entity_t *e, aliashdr_t *paliashdr, int posenum) { trivertx_t *verts; int *order; vec3_t point; float height, lheight; int count; lheight = e->origin[2] - lightspot[2]; height = 0; verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts += posenum * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); height = -lheight + 1.0; if (have_stencil) { glEnable_fp(GL_STENCIL_TEST); glStencilFunc_fp(GL_EQUAL,1,2); glStencilOp_fp(GL_KEEP,GL_KEEP,GL_INCR); } while (1) { // get the vertex count and primitive type count = *order++; if (!count) break; // done if (count < 0) { count = -count; glBegin_fp (GL_TRIANGLE_FAN); } else glBegin_fp (GL_TRIANGLE_STRIP); do { // texture coordinates come from the draw list // (skipped for shadows) glTexCoord2fv_fp ((float *)order); order += 2; // normals and vertexes come from the frame list point[0] = verts->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0]; point[1] = verts->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1]; point[2] = verts->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2]; point[0] -= shadevector[0]*(point[2]+lheight); point[1] -= shadevector[1]*(point[2]+lheight); point[2] = height; // height -= 0.001; glVertex3fv_fp (point); verts++; } while (--count); glEnd_fp (); } if (have_stencil) glDisable_fp(GL_STENCIL_TEST); } /* ================= R_SetupAliasFrame ================= */ static void R_SetupAliasFrame (entity_t *e, aliashdr_t *paliashdr) { int pose, numposes, frame; float interval; frame = e->frame; if ((frame >= paliashdr->numframes) || (frame < 0)) { Con_DPrintf ("%s: no such frame %d\n", __thisfunc__, frame); frame = 0; } pose = paliashdr->frames[frame].firstpose; numposes = paliashdr->frames[frame].numposes; if (numposes > 1) { interval = paliashdr->frames[frame].interval; pose += (int)(cl.time / interval) % numposes; } GL_DrawAliasFrame (e, paliashdr, pose); } /* ================= R_DrawAliasModel ================= */ static void AliasModelGetLightInfo (entity_t *e) { vec3_t adjust_origin; VectorCopy(e->origin, adjust_origin); adjust_origin[2] += (e->model->mins[2] + e->model->maxs[2]) / 2; if (gl_lightmap_format == GL_RGBA) ambientlight = R_LightPointColor (adjust_origin); else ambientlight = shadelight = R_LightPoint (adjust_origin); } static void R_DrawAliasModel (entity_t *e) { int i; int lnum; vec3_t dist; float add; qmodel_t *clmodel; vec3_t mins, maxs; aliashdr_t *paliashdr; float an; static float tmatrix[3][4]; float entScale; float xyfact = 1.0, zfact = 1.0; // avoid compiler warning int skinnum; int mls; clmodel = e->model; VectorAdd (e->origin, clmodel->mins, mins); VectorAdd (e->origin, clmodel->maxs, maxs); if (!AlwaysDrawModel && R_CullBox (mins, maxs)) return; VectorCopy (e->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); // if shadows are enabled, get lighting information here regardless // of special cases below, because R_LightPoint[Color]() calculates // lightspot for us which is used by GL_DrawAliasShadow() if (r_shadows.integer && e != &cl.viewent) AliasModelGetLightInfo (e); mls = e->drawflags & MLS_MASKIN; if (e->model->flags & EF_ROTATE) { ambientlight = shadelight = lightcolor[0] = lightcolor[1] = lightcolor[2] = 60 + 34 + sin(e->origin[0] + e->origin[1] + (cl.time*3.8)) * 34; } else if (mls == MLS_ABSLIGHT) { lightcolor[0] = lightcolor[1] = lightcolor[2] = ambientlight = shadelight = e->abslight; } else if (mls != MLS_NONE) { // Use a model light style (25-30) lightcolor[0] = lightcolor[1] = lightcolor[2] = ambientlight = shadelight = d_lightstylevalue[24+mls]/2; } else if (e != &cl.viewent) // R_DrawViewModel() already does viewmodel lighting. { if (!r_shadows.integer) AliasModelGetLightInfo (e); for (lnum = 0; lnum < MAX_DLIGHTS; lnum++) { if (cl_dlights[lnum].die >= cl.time) { VectorSubtract (e->origin, cl_dlights[lnum].origin, dist); add = cl_dlights[lnum].radius - VectorLengthFast(dist); if (add > 0) { ambientlight += add; lightcolor[0] += (cl_dlights[lnum].color[0] * add); lightcolor[1] += (cl_dlights[lnum].color[1] * add); lightcolor[2] += (cl_dlights[lnum].color[2] * add); } } } // clamp lighting so it doesn't overbright as much if (ambientlight > 128) ambientlight = 128; if (ambientlight + shadelight > 192) shadelight = 192 - ambientlight; } shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; shadelight = shadelight / 200.0; VectorScale(lightcolor, 1.0f / 200.0f, lightcolor); an = e->angles[1] / 180 * M_PI; shadevector[0] = cos(-an); shadevector[1] = sin(-an); shadevector[2] = 1; VectorNormalize (shadevector); // // locate the proper data // paliashdr = (aliashdr_t *)Mod_Extradata (e->model); c_alias_polys += paliashdr->numtris; // // draw all the triangles // glPushMatrix_fp (); R_RotateForEntity2(e); if (e->scale != 0 && e->scale != 100) { entScale = (float)e->scale / 100.0f; switch (e->drawflags & SCALE_TYPE_MASKIN) { case SCALE_TYPE_UNIFORM: tmatrix[0][0] = paliashdr->scale[0]*entScale; tmatrix[1][1] = paliashdr->scale[1]*entScale; tmatrix[2][2] = paliashdr->scale[2]*entScale; xyfact = zfact = (entScale-1.0)*127.95; break; case SCALE_TYPE_XYONLY: tmatrix[0][0] = paliashdr->scale[0]*entScale; tmatrix[1][1] = paliashdr->scale[1]*entScale; tmatrix[2][2] = paliashdr->scale[2]; xyfact = (entScale-1.0)*127.95; zfact = 1.0; break; case SCALE_TYPE_ZONLY: tmatrix[0][0] = paliashdr->scale[0]; tmatrix[1][1] = paliashdr->scale[1]; tmatrix[2][2] = paliashdr->scale[2]*entScale; xyfact = 1.0; zfact = (entScale-1.0)*127.95; break; } switch (e->drawflags & SCALE_ORIGIN_MASKIN) { case SCALE_ORIGIN_CENTER: tmatrix[0][3] = paliashdr->scale_origin[0]-paliashdr->scale[0]*xyfact; tmatrix[1][3] = paliashdr->scale_origin[1]-paliashdr->scale[1]*xyfact; tmatrix[2][3] = paliashdr->scale_origin[2]-paliashdr->scale[2]*zfact; break; case SCALE_ORIGIN_BOTTOM: tmatrix[0][3] = paliashdr->scale_origin[0]-paliashdr->scale[0]*xyfact; tmatrix[1][3] = paliashdr->scale_origin[1]-paliashdr->scale[1]*xyfact; tmatrix[2][3] = paliashdr->scale_origin[2]; break; case SCALE_ORIGIN_TOP: tmatrix[0][3] = paliashdr->scale_origin[0]-paliashdr->scale[0]*xyfact; tmatrix[1][3] = paliashdr->scale_origin[1]-paliashdr->scale[1]*xyfact; tmatrix[2][3] = paliashdr->scale_origin[2]-paliashdr->scale[2]*zfact*2.0; break; } } else { tmatrix[0][0] = paliashdr->scale[0]; tmatrix[1][1] = paliashdr->scale[1]; tmatrix[2][2] = paliashdr->scale[2]; tmatrix[0][3] = paliashdr->scale_origin[0]; tmatrix[1][3] = paliashdr->scale_origin[1]; tmatrix[2][3] = paliashdr->scale_origin[2]; } if (clmodel->flags & EF_ROTATE) { // Floating motion tmatrix[2][3] += sin(e->origin[0] + e->origin[1] + (cl.time*3)) * 5.5; } if (e == &cl.viewent && scr_fov.integer > 90) /* compensate viewmodel distortion at fov>90 */ { float fovscale = tan(scr_fov.value * (0.5 * M_PI / 180)); glTranslatef_fp (tmatrix[0][3], tmatrix[1][3] * fovscale, tmatrix[2][3] * fovscale); // paliashdr->scale_origin[0..2] glScalef_fp (tmatrix[0][0], tmatrix[1][1] * fovscale, tmatrix[2][2] * fovscale); // paliashdr->scale[0..2] } else { glTranslatef_fp (tmatrix[0][3], tmatrix[1][3], tmatrix[2][3]); // paliashdr->scale_origin[0..2] glScalef_fp (tmatrix[0][0], tmatrix[1][1], tmatrix[2][2]); // paliashdr->scale[0..2] } if (e->model->flags & EF_SPECIAL_TRANS) { glEnable_fp (GL_BLEND); glBlendFunc_fp (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); // glColor3f_fp (1,1,1); model_constant_alpha = 1.0f; glDisable_fp (GL_CULL_FACE); } else if (e->drawflags & DRF_TRANSLUCENT) { glEnable_fp (GL_BLEND); // glColor4f_fp (1,1,1,r_wateralpha.value); model_constant_alpha = r_wateralpha.value; } else if (e->model->flags & EF_TRANSPARENT) { glEnable_fp (GL_BLEND); // glColor3f_fp (1,1,1); model_constant_alpha = 1.0f; } else if (e->model->flags & EF_HOLEY) { glEnable_fp (GL_BLEND); // glColor3f_fp (1,1,1); model_constant_alpha = 1.0f; } else { glColor3f_fp (1,1,1); model_constant_alpha = 1.0f; } skinnum = e->skinnum; if (skinnum >= 100) { if (skinnum > 255) Sys_Error ("skinnum > 255"); if (gl_extra_textures[skinnum - 100] == GL_UNUSED_TEXTURE) // Need to load it in { qpic_t *stonepic; glpic_t *gl; char temp[80]; q_snprintf (temp, sizeof(temp), "gfx/skin%d.lmp", skinnum); stonepic = Draw_CachePic(temp); gl = (glpic_t *)stonepic->data; gl_extra_textures[skinnum - 100] = gl->texnum; } GL_Bind(gl_extra_textures[skinnum - 100]); } else { int anim = (int)(cl.time*10) & 3; if ((skinnum >= paliashdr->numskins) || (skinnum < 0)) { Con_DPrintf ("%s: no such skin # %d\n", __thisfunc__, skinnum); skinnum = 0; } GL_Bind(paliashdr->gl_texturenum[skinnum][anim]); // we can't dynamically colormap textures, so they are cached // seperately for the players. Heads are just uncolored. if (e->colormap != vid.colormap && !gl_nocolors.integer) { if (e->model == player_models[0] || e->model == player_models[1] || e->model == player_models[2] || e->model == player_models[4] || e->model == player_models[3]) { i = e - cl_entities - 1; if (i >= 0 && i < cl.maxclients) { GL_Bind(playertextures[i]); } } } } if (gl_smoothmodels.integer) glShadeModel_fp (GL_SMOOTH); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); #if defined(__AMIGA__) && defined(REFGL_MINIGL) if (gl_affinemodels.integer) glDisable_fp (MGL_PERSPECTIVE_MAPPING); #else if (gl_affinemodels.integer) glHint_fp (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); #endif R_SetupAliasFrame (e, paliashdr); // restore params if ((e->drawflags & DRF_TRANSLUCENT) || (e->model->flags & EF_SPECIAL_TRANS) || (e->model->flags & EF_TRANSPARENT) || (e->model->flags & EF_HOLEY) ) { glDisable_fp (GL_BLEND); } if (e->model->flags & EF_SPECIAL_TRANS) { glBlendFunc_fp (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable_fp (GL_CULL_FACE); } glTexEnvf_fp (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glShadeModel_fp (GL_FLAT); #if defined(__AMIGA__) && defined(REFGL_MINIGL) if (gl_affinemodels.integer) glEnable_fp (MGL_PERSPECTIVE_MAPPING); #else if (gl_affinemodels.integer) glHint_fp (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); #endif glPopMatrix_fp (); if (r_shadows.integer) { glPushMatrix_fp (); R_RotateForEntity2 (e); glDisable_fp (GL_TEXTURE_2D); glEnable_fp (GL_BLEND); glColor4f_fp (0,0,0,0.5); glDepthMask_fp (0); // prevent Z fighting GL_DrawAliasShadow (e, paliashdr, lastposenum); glDepthMask_fp (1); glEnable_fp (GL_TEXTURE_2D); glDisable_fp (GL_BLEND); glColor4f_fp (1,1,1,1); glPopMatrix_fp (); } } //============================================================================= typedef struct sortedent_s { entity_t *ent; vec_t len; } sortedent_t; static sortedent_t cl_transvisedicts[MAX_VISEDICTS]; static sortedent_t cl_transwateredicts[MAX_VISEDICTS]; static int cl_numtransvisedicts; static int cl_numtranswateredicts; /* ============= R_DrawEntitiesOnList ============= */ static void R_DrawEntitiesOnList (void) { int i; qboolean item_trans; mleaf_t *pLeaf; entity_t *e; cl_numtransvisedicts = 0; cl_numtranswateredicts = 0; if (!r_drawentities.integer) return; // draw sprites seperately, because of alpha blending for (i = 0; i < cl_numvisedicts; i++) { e = cl_visedicts[i]; // chase-cam pitch adj. by FrikaC if (e == &cl_entities[cl.viewentity]) e->angles[0] *= 0.3; switch (e->model->type) { case mod_alias: item_trans = ((e->drawflags & DRF_TRANSLUCENT) || (e->model->flags & (EF_TRANSPARENT|EF_HOLEY|EF_SPECIAL_TRANS))) != 0; if (!item_trans) R_DrawAliasModel (e); break; case mod_brush: item_trans = (e->drawflags & DRF_TRANSLUCENT) != 0; if (!item_trans) R_DrawBrushModel (e,false); break; case mod_sprite: item_trans = true; break; default: item_trans = false; break; } if (item_trans) { pLeaf = Mod_PointInLeaf (e->origin, cl.worldmodel); // if (pLeaf->contents == CONTENTS_EMPTY) if (pLeaf->contents != CONTENTS_WATER) cl_transvisedicts[cl_numtransvisedicts++].ent = e; else cl_transwateredicts[cl_numtranswateredicts++].ent = e; } } } /* ================ R_DrawTransEntitiesOnList Implemented by: jack ================ */ static int transCompare (const void *arg1, const void *arg2) { const sortedent_t *a1, *a2; a1 = (sortedent_t *) arg1; a2 = (sortedent_t *) arg2; return (a2->len - a1->len); // Sorted in reverse order. Neat, huh? } static void R_DrawTransEntitiesOnList (qboolean inwater) { int i; int numents; sortedent_t *theents; entity_t *e; int depthMaskWrite = 0; vec3_t result; theents = (inwater) ? cl_transwateredicts : cl_transvisedicts; numents = (inwater) ? cl_numtranswateredicts : cl_numtransvisedicts; for (i = 0; i < numents; i++) { VectorSubtract(theents[i].ent->origin, r_origin, result); // theents[i].len = VectorLength(result); theents[i].len = (result[0] * result[0]) + (result[1] * result[1]) + (result[2] * result[2]); } qsort((void *) theents, numents, sizeof(sortedent_t), transCompare); // Add in BETTER sorting here glDepthMask_fp(0); for (i = 0; i < numents; i++) { e = theents[i].ent; switch (e->model->type) { case mod_alias: if (!depthMaskWrite) { depthMaskWrite = 1; glDepthMask_fp(1); } R_DrawAliasModel (e); break; case mod_brush: if (!depthMaskWrite) { depthMaskWrite = 1; glDepthMask_fp(1); } R_DrawBrushModel (e, true); break; case mod_sprite: if (depthMaskWrite) { depthMaskWrite = 0; glDepthMask_fp(0); } R_DrawSpriteModel (e); break; } } if (!depthMaskWrite) glDepthMask_fp(1); } //============================================================================= // Glow styles. These rely on unchanged game code! #define TORCH_STYLE 1 /* Flicker */ #define MISSILE_STYLE 6 /* Flicker */ #define PULSE_STYLE 11 /* Slow pulse */ static void R_DrawGlow (entity_t *e) { qmodel_t *clmodel; clmodel = e->model; // Torches & Flames if ((gl_glows.integer && (clmodel->ex_flags & XF_TORCH_GLOW)) || (gl_missile_glows.integer && (clmodel->ex_flags & XF_MISSILE_GLOW)) || (gl_other_glows.integer && (clmodel->ex_flags & XF_GLOW)) ) { // NOTE: It would be better if we batched these up. // All those state changes are not nice. KH vec3_t lightorigin; // Origin of torch. vec3_t glow_vect; // Vector to torch. float radius; // Radius of torch flare. float distance; // Vector distance to torch. float intensity; // Intensity of torch flare. int i, j; vec3_t vp2; // NOTE: I don't think this is centered on the model. VectorCopy(e->origin, lightorigin); radius = 20.0f; // for mana, make it bit bigger if ( !q_strncasecmp(clmodel->name, "models/i_btmana", 15)) radius += 5.0f; VectorSubtract(lightorigin, r_origin, vp2); // See if view is outside the light. distance = VectorLengthFast(vp2); if (distance > radius) { VectorNormalizeFast(vp2); glPushMatrix_fp(); // Translate the glow to coincide with the flame. KH if (clmodel->ex_flags & XF_TORCH_GLOW) { if (clmodel->ex_flags & XF_TORCH_GLOW_EGYPT) // egypt torch fix glTranslatef_fp (cos(e->angles[1]/180*M_PI)*8.0f, sin(e->angles[1]/180*M_PI)*8.0f, 16.0f); else glTranslatef_fp (0.0f, 0.0f, 8.0f); } // 'floating' movement if (clmodel->flags & EF_ROTATE) glTranslatef_fp (0, 0, sin(e->origin[0] + e->origin[1] + (cl.time*3))*5.5); glBegin_fp(GL_TRIANGLE_FAN); // Diminish torch flare inversely with distance. intensity = (1024.0f - distance) / 1024.0f; // Invert (fades as you approach). intensity = (1.0f - intensity); // Clamp, but don't let the flare disappear. if (intensity > 1.0f) intensity = 1.0f; else if (intensity < 0.0f) intensity = 0.0f; // Now modulate with flicker. j = 0; // avoid compiler warning if (clmodel->ex_flags & XF_TORCH_GLOW) { i = (int)(cl.time*10); if (!cl_lightstyle[TORCH_STYLE].length) { j = 256; } else { j = i % cl_lightstyle[TORCH_STYLE].length; j = cl_lightstyle[TORCH_STYLE].map[j] - 'a'; j = j * 22; } } else if (clmodel->ex_flags & XF_MISSILE_GLOW) { i = (int)(cl.time*10); if (!cl_lightstyle[MISSILE_STYLE].length) { j = 256; } else { j = i % cl_lightstyle[MISSILE_STYLE].length; j = cl_lightstyle[MISSILE_STYLE].map[j] - 'a'; j = j * 22; } } else if (clmodel->ex_flags & XF_GLOW) { i = (int)(cl.time*10); if (!cl_lightstyle[PULSE_STYLE].length) { j = 256; } else { j = i % cl_lightstyle[PULSE_STYLE].length; j = cl_lightstyle[PULSE_STYLE].map[j] - 'a'; j = j * 22; } } intensity *= ((float)j / 255.0f); glColor4f_fp (clmodel->glow_color[0]*intensity, clmodel->glow_color[1]*intensity, clmodel->glow_color[2]*intensity, clmodel->glow_color[3]); for (i = 0; i < 3; i++) glow_vect[i] = lightorigin[i] - vp2[i]*radius; glVertex3fv_fp(glow_vect); glColor4f_fp(0.0f, 0.0f, 0.0f, 1.0f); for (i = 16; i >= 0; i--) { float a = i / 16.0f * M_PI * 2; for (j = 0; j < 3; j++) glow_vect[j] = lightorigin[j] + vright[j]*cos(a)*radius + vup[j]*sin(a)*radius; glVertex3fv_fp(glow_vect); } glEnd_fp(); glColor4f_fp (0.0f, 0.0f, 0.0f, 1.0f); // Restore previous matrix glPopMatrix_fp(); } } } static void R_DrawAllGlows (void) { int i; entity_t *e; if (!gl_glows.integer && !gl_missile_glows.integer && !gl_other_glows.integer) return; if (!r_drawentities.integer) return; glDepthMask_fp (0); glDisable_fp (GL_TEXTURE_2D); glShadeModel_fp (GL_SMOOTH); glEnable_fp (GL_BLEND); glBlendFunc_fp (GL_ONE, GL_ONE); for (i = 0; i < cl_numvisedicts; i++) { e = cl_visedicts[i]; if (e->model->type == mod_alias) R_DrawGlow (e); } glDisable_fp (GL_BLEND); glEnable_fp (GL_TEXTURE_2D); glBlendFunc_fp (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask_fp (1); glShadeModel_fp (GL_FLAT); } //============================================================================= /* ============= R_DrawViewModel ============= */ static void R_DrawViewModel (void) { int lnum; vec3_t dist; float add; dlight_t *dl; entity_t *e; e = &cl.viewent; if (!e->model) return; if (gl_lightmap_format == GL_RGBA) { ambientlight = R_LightPointColor (e->origin); if (lightcolor[0] < 24) lightcolor[0] = 24; if (lightcolor[1] < 24) lightcolor[1] = 24; if (lightcolor[2] < 24) lightcolor[2] = 24; if (ambientlight < 24) ambientlight = 24; // always give some light on gun } else { ambientlight = shadelight = R_LightPoint (e->origin); if (ambientlight < 24) ambientlight = shadelight = 24; // always give some light on gun } // add dynamic lights for (lnum = 0; lnum < MAX_DLIGHTS; lnum++) { dl = &cl_dlights[lnum]; if (!dl->radius) continue; if (dl->die < cl.time) continue; VectorSubtract (e->origin, dl->origin, dist); add = dl->radius - VectorLengthFast(dist); if (add > 0) { if (gl_lightmap_format == GL_RGBA) { lightcolor[0] += (float) (dl->color[0] * add); lightcolor[1] += (float) (dl->color[1] * add); lightcolor[2] += (float) (dl->color[2] * add); } else { shadelight += (float) add; } ambientlight += add; } } cl.light_level = ambientlight; if ((cl.v.health <= 0) || (chase_active.integer) || //rjr (cl.items & IT_INVISIBILITY) || (!r_drawviewmodel.integer) || (!r_drawentities.integer)) { return; } // hack the depth range to prevent view model from poking into walls glDepthRange_fp (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin)); AlwaysDrawModel = true; R_DrawAliasModel (e); AlwaysDrawModel = false; glDepthRange_fp (gldepthmin, gldepthmax); } //============================================================================= /* =============== R_MarkLeaves =============== */ static void R_MarkLeaves (void) { byte *vis; mnode_t *node; int i; byte solid[4096]; if (r_oldviewleaf == r_viewleaf && !r_novis.integer) return; if (mirror) return; r_visframecount++; r_oldviewleaf = r_viewleaf; if (r_novis.integer) { vis = solid; memset (solid, 0xff, (cl.worldmodel->numleafs+7)>>3); } else vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); for (i = 0; i < cl.worldmodel->numleafs; i++) { if ( vis[i>>3] & (1<<(i&7)) ) { node = (mnode_t *)&cl.worldmodel->leafs[i+1]; do { if (node->visframe == r_visframecount) break; node->visframe = r_visframecount; node = node->parent; } while (node); } } } //============================================================================= /* ================= GL_DrawBlendPoly Renders a polygon covering the whole screen. For fullscreen color blending and approximated gamma correction. To be called from R_PolyBlend(). ================= */ static void GL_DrawBlendPoly (void) { glBegin_fp (GL_QUADS); glVertex3f_fp (10, 100, 100); glVertex3f_fp (10, -100, 100); glVertex3f_fp (10, -100, -100); glVertex3f_fp (10, 100, -100); glEnd_fp (); } /* ================= GL_DoGamma Uses GL_DrawBlendPoly() for gamma correction. Idea originally from LordHavoc. This trick is useful if normal ways of gamma adjustment fail: In case of 3dfx Voodoo1/2/Rush, we can't use 3dfx specific extensions in unix, so this can be our friend at a cost of 4-5 fps. To be called from R_PolyBlend(). ================= */ #if 0 static void GL_DoGamma (void) { if (v_gamma.value >= 1) return; glBlendFunc_fp (GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); glColor4f_fp (1, 1, 1, v_gamma.value); GL_DrawBlendPoly (); glBlendFunc_fp (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } #endif /* ============ R_PolyBlend ============ */ static void R_PolyBlend (void) { if (!gl_polyblend.integer) return; glEnable_fp (GL_BLEND); glDisable_fp (GL_DEPTH_TEST); glDisable_fp (GL_TEXTURE_2D); glLoadIdentity_fp (); glRotatef_fp (-90, 1, 0, 0); // put Z going up glRotatef_fp (90, 0, 0, 1); // put Z going up if (v_blend[3]) { glColor4fv_fp (v_blend); GL_DrawBlendPoly (); } /*GL_DoGamma ();*/ glDisable_fp (GL_BLEND); glEnable_fp (GL_TEXTURE_2D); glEnable_fp (GL_ALPHA_TEST); } //============================================================================= static int SignbitsForPlane (mplane_t *out) { int bits, j; // for fast box on planeside test bits = 0; for (j = 0; j < 3; j++) { if (out->normal[j] < 0) bits |= 1< 1) Cvar_SetQuick (&r_fullbright, "0"); R_AnimateLight (); r_framecount++; // build the transformation matrix for the given view angles VectorCopy (r_refdef.vieworg, r_origin); AngleVectors (r_refdef.viewangles, vpn, vright, vup); // current viewleaf r_oldviewleaf = r_viewleaf; r_viewleaf = Mod_PointInLeaf (r_origin, cl.worldmodel); V_SetContentsColor (r_viewleaf->contents); V_CalcBlend (); r_cache_thrash = false; c_brush_polys = 0; c_alias_polys = 0; } #define NEARCLIP 4 #define FARCLIP 4096 static void GL_SetFrustum (GLdouble fovx, GLdouble fovy) { GLdouble xmax, ymax; xmax = NEARCLIP * tan(fovx * M_PI / 360.0); ymax = NEARCLIP * tan(fovy * M_PI / 360.0); glFrustum_fp (-xmax, xmax, -ymax, ymax, NEARCLIP, FARCLIP); } /* ============= R_SetupGL ============= */ static void R_SetupGL (void) { int x, x2, y2, y, w, h; // // set up viewpoint // glMatrixMode_fp(GL_PROJECTION); glLoadIdentity_fp (); x = r_refdef.vrect.x * glwidth/vid.width; x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * glwidth/vid.width; y = (vid.height - r_refdef.vrect.y) * glheight/vid.height; y2 = (vid.height - (r_refdef.vrect.y + r_refdef.vrect.height)) * glheight/vid.height; // fudge around because of frac screen scale if (x > 0) x--; if (x2 < glwidth) x2++; if (y2 < 0) y2--; if (y < glheight) y++; w = x2 - x; h = y - y2; glViewport_fp (glx + x, gly + y2, w, h); GL_SetFrustum (r_refdef.fov_x, r_refdef.fov_y); if (mirror) { if (mirror_plane->normal[2]) glScalef_fp (1, -1, 1); else glScalef_fp (-1, 1, 1); glCullFace_fp(GL_BACK); } else glCullFace_fp(GL_FRONT); glMatrixMode_fp(GL_MODELVIEW); glLoadIdentity_fp (); glRotatef_fp (-90, 1, 0, 0); // put Z going up glRotatef_fp (90, 0, 0, 1); // put Z going up glRotatef_fp (-r_refdef.viewangles[2], 1, 0, 0); glRotatef_fp (-r_refdef.viewangles[0], 0, 1, 0); glRotatef_fp (-r_refdef.viewangles[1], 0, 0, 1); glTranslatef_fp (-r_refdef.vieworg[0], -r_refdef.vieworg[1], -r_refdef.vieworg[2]); glGetFloatv_fp (GL_MODELVIEW_MATRIX, r_world_matrix); // // set drawing parms // if (gl_cull.integer) glEnable_fp(GL_CULL_FACE); else glDisable_fp(GL_CULL_FACE); glDisable_fp(GL_BLEND); glDisable_fp(GL_ALPHA_TEST); glEnable_fp(GL_DEPTH_TEST); } /* ================ R_RenderScene r_refdef must be set before the first call ================ */ static void R_RenderScene (void) { R_SetupFrame (); R_SetFrustum (); R_SetupGL (); R_MarkLeaves (); // done here so we know if we're in water R_DrawWorld (); // adds static entities to the list S_ExtraUpdate (); // don't let sound get messed up if going slow R_DrawEntitiesOnList (); R_DrawAllGlows(); R_RenderDlights (); } /* ============= R_Clear ============= */ static void R_Clear (void) { if (r_mirroralpha.value != 1.0) { if (gl_clear.integer) glClear_fp (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); else glClear_fp (GL_DEPTH_BUFFER_BIT); gldepthmin = 0; gldepthmax = 0.5; glDepthFunc_fp (GL_LEQUAL); } else if (gl_ztrick.integer) { static int trickframe; if (gl_clear.integer) glClear_fp (GL_COLOR_BUFFER_BIT); trickframe++; if (trickframe & 1) { gldepthmin = 0; gldepthmax = 0.49999; glDepthFunc_fp (GL_LEQUAL); } else { gldepthmin = 1; gldepthmax = 0.5; glDepthFunc_fp (GL_GEQUAL); } } else { if (gl_clear.integer) glClear_fp (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); else glClear_fp (GL_DEPTH_BUFFER_BIT); gldepthmin = 0; gldepthmax = 1; glDepthFunc_fp (GL_LEQUAL); } glDepthRange_fp (gldepthmin, gldepthmax); if (have_stencil && r_shadows.integer) { glClearStencil_fp(1); glClear_fp(GL_STENCIL_BUFFER_BIT); } } /* ============= R_Mirror ============= */ static float r_base_world_matrix[16]; static void R_Mirror (void) { float d; msurface_t *s; entity_t *ent; if (!mirror) return; memcpy (r_base_world_matrix, r_world_matrix, sizeof(r_base_world_matrix)); d = DotProduct (r_refdef.vieworg, mirror_plane->normal) - mirror_plane->dist; VectorMA (r_refdef.vieworg, -2*d, mirror_plane->normal, r_refdef.vieworg); d = DotProduct (vpn, mirror_plane->normal); VectorMA (vpn, -2*d, mirror_plane->normal, vpn); r_refdef.viewangles[0] = -asin (vpn[2])/M_PI*180; r_refdef.viewangles[1] = atan2 (vpn[1], vpn[0])/M_PI*180; r_refdef.viewangles[2] = -r_refdef.viewangles[2]; ent = &cl_entities[cl.viewentity]; if (cl_numvisedicts < MAX_VISEDICTS) { cl_visedicts[cl_numvisedicts] = ent; cl_numvisedicts++; } gldepthmin = 0.5; gldepthmax = 1; glDepthRange_fp (gldepthmin, gldepthmax); glDepthFunc_fp (GL_LEQUAL); R_RenderScene (); glDepthMask_fp(0); R_DrawParticles (); // THIS IS THE F*S*D(KCING MIRROR ROUTINE! Go down!!! R_DrawTransEntitiesOnList (true); // This restores the depth mask R_DrawWaterSurfaces (); R_DrawTransEntitiesOnList (false); gldepthmin = 0; gldepthmax = 0.5; glDepthRange_fp (gldepthmin, gldepthmax); glDepthFunc_fp (GL_LEQUAL); // blend on top glEnable_fp (GL_BLEND); glMatrixMode_fp(GL_PROJECTION); if (mirror_plane->normal[2]) glScalef_fp (1,-1,1); else glScalef_fp (-1,1,1); glCullFace_fp(GL_FRONT); glMatrixMode_fp(GL_MODELVIEW); glLoadMatrixf_fp (r_base_world_matrix); glColor4f_fp (1,1,1,r_mirroralpha.value); s = cl.worldmodel->textures[mirrortexturenum]->texturechain; for ( ; s ; s = s->texturechain) R_RenderBrushPoly (&r_worldentity, s, true); cl.worldmodel->textures[mirrortexturenum]->texturechain = NULL; glDisable_fp (GL_BLEND); glColor4f_fp (1,1,1,1); } /* ============= R_PrintTimes ============= */ static void R_PrintTimes (void) { float r_time2; float ms, fps; r_lasttime1 = r_time2 = Sys_DoubleTime(); ms = 1000 * (r_time2 - r_time1); fps = 1000 / ms; Con_Printf("%3.1f fps %5.0f ms\n%4i wpoly %4i epoly\n", fps, ms, c_brush_polys, c_alias_polys); } /* ================ R_RenderView r_refdef must be set before the first call ================ */ void R_RenderView (void) { if (r_norefresh.integer) return; if (!r_worldentity.model || !cl.worldmodel) Sys_Error ("%s: NULL worldmodel", __thisfunc__); if (r_speeds.integer) { glFinish_fp (); if (r_wholeframe.integer) r_time1 = r_lasttime1; else r_time1 = Sys_DoubleTime (); c_brush_polys = 0; c_alias_polys = 0; } mirror = false; // glFinish_fp (); R_Clear (); // render normal view R_RenderScene (); glDepthMask_fp(0); R_DrawParticles (); R_DrawTransEntitiesOnList (r_viewleaf->contents == CONTENTS_EMPTY); // This restores the depth mask R_DrawWaterSurfaces (); R_DrawTransEntitiesOnList (r_viewleaf->contents != CONTENTS_EMPTY); R_DrawViewModel(); // render mirror view R_Mirror (); R_PolyBlend (); if (r_speeds.integer) R_PrintTimes (); } engine/hexen2/gl_rmisc.c000066400000000000000000000303531444734033100154660ustar00rootroot00000000000000/* r_misc.c -- * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "hashindex.h" byte *playerTranslation; const int color_offsets[MAX_PLAYER_CLASS] = { 2 * 14 * 256, 0, 1 * 14 * 256, 2 * 14 * 256, 2 * 14 * 256 #if defined(H2W) , 2 * 14 * 256 #endif }; cvar_t gl_purge_maptex = {"gl_purge_maptex", "1", CVAR_ARCHIVE}; // whether or not map-specific OGL textures // are purged on map change. default == yes qboolean flush_textures; int gl_texlevel; extern int menu_numcachepics; extern cachepic_t menu_cachepics[MAX_CACHED_PICS]; extern hashindex_t hash_gltextures; extern hashindex_t hash_cachepics; extern void R_InitBubble (void); /* ================== R_InitTextures ================== */ void R_InitTextures (void) { int x, y, m; byte *dest; // create a simple checkerboard texture for the default r_notexture_mip = (texture_t *) Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, "notexture"); r_notexture_mip->width = r_notexture_mip->height = 16; r_notexture_mip->offsets[0] = sizeof(texture_t); r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16; r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8; r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4; for (m = 0; m < 4; m++) { dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[m]; for (y = 0; y < (16 >> m); y++) { for (x = 0; x < (16 >> m); x++) { if ( (y < (8 >> m)) ^ (x < (8 >> m)) ) *dest++ = 0; else *dest++ = 0xff; } } } } #define TEXSIZE 16 /* was 8 */ /* static byte dottexture[TEXSIZE][TEXSIZE] = { {0,1,1,0,0,0,0,0}, {1,1,1,1,0,0,0,0}, {1,1,1,1,0,0,0,0}, {0,1,1,0,0,0,0,0}, {0,0,0,0,1,0,0,0}, {0,0,0,0,1,0,0,1}, {0,0,0,0,0,0,1,0}, {0,0,0,1,0,0,0,1}, }; */ static byte dottexture[TEXSIZE][TEXSIZE] = { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},//1 {0,0,0,1,0,1,0,0,0,0,0,0,1,1,0,0}, {0,0,0,0,1,0,0,0,0,0,0,1,1,1,1,0}, {0,1,0,1,1,1,0,1,0,0,0,1,1,1,1,0},//4 {0,0,1,1,1,1,1,0,0,0,0,0,1,1,0,0}, {0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0}, {0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0},//8 {0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0}, {0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,0}, {0,0,0,0,0,0,0,0,1,1,0,1,1,0,1,0}, {0,0,0,1,0,0,0,0,1,1,1,1,1,0,1,0},//12 {0,1,1,1,0,0,0,0,1,1,0,1,1,0,1,0}, {0,0,1,1,1,0,0,0,0,1,1,1,0,1,0,0}, {0,0,1,0,0,0,0,0,0,0,1,1,1,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},//16 }; void R_InitParticleTexture (void) { int x, y; byte data[TEXSIZE][TEXSIZE][4]; // // particle texture // for (x = 0; x < TEXSIZE; x++) { for (y = 0; y < TEXSIZE; y++) { data[y][x][0] = 255; data[y][x][1] = 255; data[y][x][2] = 255; data[y][x][3] = dottexture[x][y]*255; } } particletexture = GL_LoadTexture("", (byte *)data, TEXSIZE, TEXSIZE, TEX_ALPHA | TEX_RGBA | TEX_LINEAR); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } void R_InitExtraTextures (void) { int i; for (i = 0; i < MAX_EXTRA_TEXTURES; i++) gl_extra_textures[i] = GL_UNUSED_TEXTURE; // see R_TranslatePlayerSkin() below glGenTextures_fp(MAX_CLIENTS, playertextures); } /* ==================== R_TimeRefresh_f For program optimization ==================== */ static void R_TimeRefresh_f (void) { int i; float start, stop, time; if (cls.state != ca_connected) { Con_Printf("Not connected to a server\n"); return; } start = Sys_DoubleTime (); for (i = 0; i < 128; i++) { GL_BeginRendering(&glx, &gly, &glwidth, &glheight); r_refdef.viewangles[1] = i/128.0*360.0; R_RenderView (); GL_EndRendering (); } glFinish_fp (); stop = Sys_DoubleTime (); time = stop-start; Con_Printf ("%f seconds (%f fps)\n", time, 128/time); } /* ==================== R_SetClearColor_f -- johnfitz ==================== */ static void R_SetClearColor_f (cvar_t *var) { int s = var->integer & 0xFF; byte *rgb = (byte *)(d_8to24table + s); glClearColor_fp(rgb[0]/255.0, rgb[1]/255.0, rgb[2]/255.0, 0); } /* =============== R_Init =============== */ void R_Init (void) { Cmd_AddCommand ("timerefresh", R_TimeRefresh_f); Cmd_AddCommand ("pointfile", R_ReadPointFile_f); Cvar_RegisterVariable (&r_norefresh); Cvar_RegisterVariable (&r_lightmap); Cvar_RegisterVariable (&r_fullbright); Cvar_RegisterVariable (&r_waterwarp); Cvar_RegisterVariable (&r_drawentities); Cvar_RegisterVariable (&r_drawviewmodel); Cvar_RegisterVariable (&r_shadows); Cvar_RegisterVariable (&r_mirroralpha); Cvar_RegisterVariable (&r_wateralpha); Cvar_RegisterVariable (&r_skyalpha); Cvar_RegisterVariable (&r_dynamic); Cvar_RegisterVariable (&r_novis); Cvar_RegisterVariable (&r_speeds); Cvar_RegisterVariable (&r_wholeframe); Cvar_RegisterVariable (&r_clearcolor); Cvar_SetCallback (&r_clearcolor, R_SetClearColor_f); Cvar_RegisterVariable (&r_texture_external); Cvar_RegisterVariable (&gl_clear); Cvar_RegisterVariable (&gl_cull); Cvar_RegisterVariable (&gl_smoothmodels); Cvar_RegisterVariable (&gl_affinemodels); Cvar_RegisterVariable (&gl_polyblend); Cvar_RegisterVariable (&gl_flashblend); Cvar_RegisterVariable (&gl_playermip); Cvar_RegisterVariable (&gl_nocolors); Cvar_RegisterVariable (&gl_waterripple); Cvar_RegisterVariable (&gl_ztrick); Cvar_RegisterVariable (&gl_zfix); Cvar_RegisterVariable (&gl_purge_maptex); Cvar_RegisterVariable (&gl_keeptjunctions); Cvar_RegisterVariable (&gl_reporttjunctions); Cvar_RegisterVariable (&gl_glows); Cvar_RegisterVariable (&gl_missile_glows); Cvar_RegisterVariable (&gl_other_glows); Cvar_RegisterVariable (&gl_coloredlight); Cvar_RegisterVariable (&gl_colored_dynamic_lights); Cvar_RegisterVariable (&gl_extra_dynamic_lights); R_InitBubble(); R_InitParticles (); R_InitParticleTexture (); R_InitExtraTextures (); R_SetClearColor_f (&r_clearcolor); playerTranslation = (byte *)FS_LoadHunkFile ("gfx/player.lmp", NULL); if (!playerTranslation) Sys_Error ("Couldn't load gfx/player.lmp"); } /* =============== R_TranslatePlayerSkin Translates a skin texture by the per-player color lookup =============== */ extern byte player_8bit_texels[MAX_PLAYER_CLASS][620*245]; void R_TranslatePlayerSkin (int playernum) { int top, bottom; byte translate[256]; unsigned int translate32[256]; unsigned int i, j; qmodel_t *model; aliashdr_t *paliashdr; byte *original; unsigned int pixels[512*256], *out; unsigned int scaled_width, scaled_height; int inwidth, inheight; byte *inrow; unsigned int frac, fracstep; byte *sourceA, *sourceB, *colorA, *colorB; int playerclass = (int)cl.scores[playernum].playerclass; int s; // char texname[20]; for (i = 0; i < 256; i++) translate[i] = i; top = (cl.scores[playernum].colors & 0xf0) >> 4; bottom = (cl.scores[playernum].colors & 15); if (top > 10) top = 0; if (bottom > 10) bottom = 0; top -= 1; bottom -= 1; colorA = playerTranslation + 256 + color_offsets[playerclass-1]; colorB = colorA + 256; sourceA = colorB + 256 + (top * 256); sourceB = colorB + 256 + (bottom * 256); for (i = 0; i < 256; i++, colorA++, colorB++, sourceA++, sourceB++) { if (top >= 0 && (*colorA != 255)) translate[i] = *sourceA; if (bottom >= 0 && (*colorB != 255)) translate[i] = *sourceB; } // // locate the original skin pixels // model = cl_entities[1+playernum].model; if (!model) return; // player doesn't have a model yet // class limit is mission pack dependant s = (gameflags & GAME_PORTALS) ? MAX_PLAYER_CLASS : MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES; if (playerclass >= 1 && playerclass <= s) original = player_8bit_texels[playerclass-1]; else original = player_8bit_texels[0]; paliashdr = (aliashdr_t *)Mod_Extradata (model); s = paliashdr->skinwidth * paliashdr->skinheight; if (s & 3) Sys_Error ("%s: s&3", __thisfunc__); for (i = 0; i < 256; i++) translate32[i] = d_8to24table[translate[i]]; scaled_width = gl_max_size < 512 ? gl_max_size : 512; scaled_height = gl_max_size < 256 ? gl_max_size : 256; // allow users to crunch sizes down even more if they want scaled_width >>= gl_playermip.integer; scaled_height >>= gl_playermip.integer; inwidth = paliashdr->skinwidth; inheight = paliashdr->skinheight; out = pixels; fracstep = inwidth*0x10000/scaled_width; for (i = 0; i < scaled_height; i++, out += scaled_width) { inrow = original + inwidth*(i*inheight/scaled_height); frac = fracstep >> 1; for (j = 0; j < scaled_width; j += 4) { out[j] = translate32[inrow[frac>>16]]; frac += fracstep; out[j+1] = translate32[inrow[frac>>16]]; frac += fracstep; out[j+2] = translate32[inrow[frac>>16]]; frac += fracstep; out[j+3] = translate32[inrow[frac>>16]]; frac += fracstep; } } // playertextures doesn't like GL_LoadTexture() and its associated glDeleteTextures() // call, not sure why for now, so I have to do this the old way until I figure it out. // q_snprintf(texname, 19, "player%i", playernum); // playertextures[playernum] = GL_LoadTexture(texname, (byte *)pixels, scaled_width, scaled_height, TEX_RGBA|TEX_LINEAR); GL_Bind(playertextures[playernum]); glTexImage2D_fp(GL_TEXTURE_2D, 0, gl_solid_format, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } /* =============== R_NewMap =============== */ void R_NewMap (void) { int i; for (i = 0; i < 256; i++) d_lightstylevalue[i] = 264; // normal light value memset (&r_worldentity, 0, sizeof(r_worldentity)); r_worldentity.model = cl.worldmodel; // clear out efrags in case the level hasn't been reloaded // FIXME: is this one short? for (i = 0; i < cl.worldmodel->numleafs; i++) cl.worldmodel->leafs[i].efrags = NULL; r_viewleaf = NULL; R_ClearParticles (); GL_BuildLightmaps (); // identify sky texture skytexturenum = -1; mirrortexturenum = -1; for (i = 0; i < cl.worldmodel->numtextures; i++) { if (!cl.worldmodel->textures[i]) continue; if (!strncmp(cl.worldmodel->textures[i]->name,"sky",3) ) skytexturenum = i; if (!strncmp(cl.worldmodel->textures[i]->name,"window02_1",10) ) mirrortexturenum = i; } #ifdef QUAKE2 R_LoadSkys (); #endif } /* D_ClearOpenGLTexture this procedure (called by Host_ClearMemory/SV_SpawnServer in hexen2 on new map, or by CL_ClearState/CL_ParseServerData in HW on new connection) will purge all OpenGL textures beyond static ones (console, menu, etc, whatever was loaded at initialization time). This will save a lot of video memory, because the textures won't keep accumulating from map to map, thus bloating more and more the more time the client is running, which gets pretty nasty on 8-16-32M machines with OpenGL drivers like nVidia, which cache all textures in system memory. (Pa3PyX) */ void D_ClearOpenGLTextures (int last_tex) { int i, key; // Delete OpenGL textures for (i = last_tex; i < numgltextures; i++) { glDeleteTextures_fp(1, &(gltextures[i].texnum)); key = Hash_GenerateKeyString (&hash_gltextures, gltextures[i].identifier, true); Hash_Remove(&hash_gltextures, key, i); } memset(&(gltextures[last_tex]), 0, (numgltextures - last_tex) * sizeof(gltexture_t)); numgltextures = last_tex; if (currenttexture >= (GLuint)last_tex) currenttexture = GL_UNUSED_TEXTURE; // Clear menu pic cache memset(menu_cachepics, 0, menu_numcachepics * sizeof(cachepic_t)); menu_numcachepics = 0; Hash_Clear(&hash_cachepics); Con_DPrintf ("Purged OpenGL textures\n"); } void D_FlushCaches (void) { if (numgltextures - gl_texlevel > 0 && flush_textures && gl_purge_maptex.integer) D_ClearOpenGLTextures (gl_texlevel); } engine/hexen2/gl_rsurf.c000066400000000000000000001040721444734033100155120ustar00rootroot00000000000000/* * r_surf.c -- surface-related refresh code * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" int gl_lightmap_format = GL_RGBA; cvar_t gl_lightmapfmt = {"gl_lightmapfmt", "GL_RGBA", CVAR_ARCHIVE}; int lightmap_bytes = 4; // 1, 2, or 4. default is 4 for GL_RGBA GLuint lightmap_textures[MAX_LIGHTMAPS]; static unsigned int blocklights[18*18]; static unsigned int blocklightscolor[18*18*3]; // colored light support. *3 for RGB to the definitions at the top #define BLOCK_WIDTH 128 #define BLOCK_HEIGHT 128 static glpoly_t *lightmap_polys[MAX_LIGHTMAPS]; static qboolean lightmap_modified[MAX_LIGHTMAPS]; static int allocated[MAX_LIGHTMAPS][BLOCK_WIDTH]; // the lightmap texture data needs to be kept in // main memory so texsubimage can update properly static byte lightmaps[4*MAX_LIGHTMAPS*BLOCK_WIDTH*BLOCK_HEIGHT]; /* =============== R_AddDynamicLights =============== */ static void R_AddDynamicLights (msurface_t *surf) { int lnum; int sd, td; float dist, rad, minlight; vec3_t impact, local; int s, t; int i; int smax, tmax; mtexinfo_t *tex; // vars for lit support float cred, cgreen, cblue, brightness; unsigned int *bl; smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; tex = surf->texinfo; for (lnum = 0; lnum < MAX_DLIGHTS; lnum++) { if ( !(surf->dlightbits & (1<plane->normal) - surf->plane->dist; rad -= fabs(dist); minlight = cl_dlights[lnum].minlight; if (rad < minlight) continue; minlight = rad - minlight; for (i = 0; i < 3; i++) { impact[i] = cl_dlights[lnum].origin[i] - surf->plane->normal[i]*dist; } local[0] = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3]; local[1] = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3]; local[0] -= surf->texturemins[0]; local[1] -= surf->texturemins[1]; // lit support (LordHavoc) bl = blocklightscolor; cred = cl_dlights[lnum].color[0] * 256.0f; cgreen = cl_dlights[lnum].color[1] * 256.0f; cblue = cl_dlights[lnum].color[2] * 256.0f; for (t = 0; t < tmax; t++) { td = local[1] - t*16; if (td < 0) td = -td; for (s = 0; s < smax; s++) { sd = local[0] - s*16; if (sd < 0) sd = -sd; if (sd > td) dist = sd + (td>>1); else dist = td + (sd>>1); if (dist < minlight) { brightness = rad - dist; if (cl_dlights[lnum].dark) { // clamp to 0 bl[0] -= (int)(((brightness * cred) < bl[0]) ? (brightness * cred) : bl[0]); bl[1] -= (int)(((brightness * cgreen) < bl[1]) ? (brightness * cgreen) : bl[1]); bl[2] -= (int)(((brightness * cblue) < bl[2]) ? (brightness * cblue) : bl[2]); } else { bl[0] += (int)(brightness * cred); bl[1] += (int)(brightness * cgreen); bl[2] += (int)(brightness * cblue); } blocklights[t*smax + s] += (rad - dist)*256; } bl += 3; } } } } /* =============== GL_SetupLightmapFmt Used to setup the lightmap_format and lightmap_bytes during init from VID_Init() and at every level change from Mod_LoadLighting(). =============== */ void GL_SetupLightmapFmt (void) { // only GL_LUMINANCE and GL_RGBA are supported if (!q_strcasecmp(gl_lightmapfmt.string, "GL_LUMINANCE")) gl_lightmap_format = GL_LUMINANCE; else if (!q_strcasecmp(gl_lightmapfmt.string, "GL_RGBA")) gl_lightmap_format = GL_RGBA; else { gl_lightmap_format = GL_RGBA; Cvar_SetQuick (&gl_lightmapfmt, "GL_RGBA"); } if (!host_initialized) // check for cmdline overrides { if (COM_CheckParm ("-lm_1")) { gl_lightmap_format = GL_LUMINANCE; Cvar_SetQuick (&gl_lightmapfmt, "GL_LUMINANCE"); } else if (COM_CheckParm ("-lm_4")) { gl_lightmap_format = GL_RGBA; Cvar_SetQuick (&gl_lightmapfmt, "GL_RGBA"); } } switch (gl_lightmap_format) { case GL_RGBA: lightmap_bytes = 4; break; case GL_LUMINANCE: lightmap_bytes = 1; break; } } /* =============== R_BuildLightMap Combine and scale multiple lightmaps into the 8.8 format in blocklights =============== */ static void R_BuildLightMap (msurface_t *surf, byte *dest, int stride) { int smax, tmax; int t, r, s, q; int i, j, size; byte *lightmap; unsigned int scale; int maps; unsigned int *bl, *blcr, *blcg, *blcb; surf->cached_dlight = (surf->dlightframe == r_framecount); smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; size = smax*tmax; lightmap = surf->samples; // set to full bright if no light data if (r_fullbright.integer || !cl.worldmodel->lightdata) { for (i = 0; i < size; i++) { if (gl_lightmap_format == GL_RGBA) blocklightscolor[i*3+0] = blocklightscolor[i*3+1] = blocklightscolor[i*3+2] = 65280; else blocklights[i] = 255*256; } goto store; } // clear to no light for (i = 0; i < size; i++) { if (gl_lightmap_format == GL_RGBA) blocklightscolor[i*3+0] = blocklightscolor[i*3+1] = blocklightscolor[i*3+2] = 0; else blocklights[i] = 0; } // add all the lightmaps if (lightmap) { for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; maps++) { scale = d_lightstylevalue[surf->styles[maps]]; surf->cached_light[maps] = scale; // 8.8 fraction if (gl_lightmap_format == GL_RGBA) { for (i = 0, j = 0; i < size; i++) { blocklightscolor[i*3+0] += lightmap[j] * scale; blocklightscolor[i*3+1] += lightmap[++j] * scale; blocklightscolor[i*3+2] += lightmap[++j] * scale; j++; } lightmap += size * 3; } else { for (i = 0; i < size; i++) blocklights[i] += lightmap[i] * scale; lightmap += size; // skip to next lightmap } } } // add all the dynamic lights if (surf->dlightframe == r_framecount) R_AddDynamicLights (surf); // bound, invert, and shift store: switch (gl_lightmap_format) { case GL_RGBA: stride -= (smax<<2); blcr = &blocklightscolor[0]; blcg = &blocklightscolor[1]; blcb = &blocklightscolor[2]; for (i = 0; i < tmax; i++, dest += stride) { for (j = 0; j < smax; j++) { q = *blcr; q >>= 7; r = *blcg; r >>= 7; s = *blcb; s >>= 7; if (q > 255) q = 255; if (r > 255) r = 255; if (s > 255) s = 255; if (gl_coloredlight.integer) { dest[0] = q; //255 - q; dest[1] = r; //255 - r; dest[2] = s; //255 - s; dest[3] = 255; //(q+r+s)/3; } else { t = (int) ((float)q * 0.33f + (float)s * 0.33f + (float)r * 0.33f); if (t > 255) t = 255; dest[0] = t; dest[1] = t; dest[2] = t; dest[3] = 255; //t; } dest += 4; blcr += 3; blcg += 3; blcb += 3; } } break; case GL_LUMINANCE: bl = blocklights; for (i = 0; i < tmax; i++, dest += stride) { for (j = 0; j < smax; j++) { t = *bl++; t >>= 7; if (t > 255) t = 255; dest[j] = 255-t; } } break; default: Sys_Error ("Bad lightmap format"); } } /* =============== R_TextureAnimation Returns the proper texture for a given time and base texture =============== */ static texture_t *R_TextureAnimation (entity_t *e, texture_t *base) { int reletive; int count; if (e->frame) { if (base->alternate_anims) base = base->alternate_anims; } if (!base->anim_total) return base; reletive = (int)(cl.time*10) % base->anim_total; count = 0; while (base->anim_min > reletive || base->anim_max <= reletive) { base = base->anim_next; if (!base) Sys_Error ("%s: broken cycle", __thisfunc__); if (++count > 100) Sys_Error ("%s: infinite cycle", __thisfunc__); } return base; } /* ============================================================= BRUSH MODELS ============================================================= */ /* ================ DrawGLWaterPoly Warp the vertex coordinates ================ */ static void DrawGLWaterPoly (glpoly_t *p) { int i; float *v; vec3_t nv; glBegin_fp (GL_TRIANGLE_FAN); v = p->verts[0]; for (i = 0; i < p->numverts; i++, v+= VERTEXSIZE) { glTexCoord2f_fp (v[3], v[4]); if (r_waterwarp.integer) { nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime); nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime); nv[2] = v[2]; glVertex3fv_fp (nv); } else { glVertex3fv_fp (v); } } glEnd_fp (); } static void DrawGLWaterPolyLightmap (glpoly_t *p) { int i; float *v; vec3_t nv; glBegin_fp (GL_TRIANGLE_FAN); v = p->verts[0]; for (i = 0; i < p->numverts; i++, v+= VERTEXSIZE) { glTexCoord2f_fp (v[5], v[6]); if (r_waterwarp.integer) { nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime); nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime); nv[2] = v[2]; glVertex3fv_fp (nv); } else { glVertex3fv_fp (v); } } glEnd_fp (); } static void DrawGLWaterPolyMTexLM (glpoly_t *p) { int i; float *v; vec3_t nv; glBegin_fp (GL_TRIANGLE_FAN); v = p->verts[0]; for (i = 0; i < p->numverts; i++, v+= VERTEXSIZE) { glMultiTexCoord2fARB_fp (GL_TEXTURE0_ARB, v[3], v[4]); glMultiTexCoord2fARB_fp (GL_TEXTURE1_ARB, v[5], v[6]); if (r_waterwarp.integer) { nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime); nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime); nv[2] = v[2]; glVertex3fv_fp (nv); } else { glVertex3fv_fp (v); } } glEnd_fp (); } /* ================ DrawGLPoly ================ */ static void DrawGLPoly (glpoly_t *p) { int i; float *v; glBegin_fp (GL_POLYGON); v = p->verts[0]; for (i = 0; i < p->numverts; i++, v+= VERTEXSIZE) { glTexCoord2f_fp (v[3], v[4]); glVertex3fv_fp (v); } glEnd_fp (); } static void DrawGLPolyMTex (glpoly_t *p) { int i; float *v; glBegin_fp (GL_POLYGON); v = p->verts[0]; for (i = 0; i < p->numverts; i++, v+= VERTEXSIZE) { glMultiTexCoord2fARB_fp (GL_TEXTURE0_ARB, v[3], v[4]); glMultiTexCoord2fARB_fp (GL_TEXTURE1_ARB, v[5], v[6]); glVertex3fv_fp (v); } glEnd_fp (); } /* ================ R_BlendLightmaps ================ */ static void R_BlendLightmaps (qboolean Translucent) { unsigned int i; int j; glpoly_t *p; float *v; if (r_fullbright.integer) return; if (!Translucent) glDepthMask_fp (0); // don't bother writing Z if (gl_lightmap_format == GL_RGBA) { glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f_fp (1.0f,1.0f,1.0f, 1.0f); glBlendFunc_fp (GL_ZERO, GL_SRC_COLOR); } else if (gl_lightmap_format == GL_LUMINANCE) { glBlendFunc_fp (GL_ZERO, GL_ONE_MINUS_SRC_COLOR); } if (!r_lightmap.integer) { glEnable_fp (GL_BLEND); } if (! lightmap_textures[0]) { // if lightmaps were hosed in a video mode change, make // sure we allocate new slots for lightmaps, otherwise // we'll probably overwrite some other existing textures. glGenTextures_fp(MAX_LIGHTMAPS, lightmap_textures); } for (i = 0; i < MAX_LIGHTMAPS; i++) { p = lightmap_polys[i]; if (!p) continue; // skip if no lightmap GL_Bind(lightmap_textures[i]); if (lightmap_modified[i]) { // if current lightmap was changed reload it // and mark as not changed. lightmap_modified[i] = false; glTexImage2D_fp (GL_TEXTURE_2D, 0, lightmap_bytes, BLOCK_WIDTH, BLOCK_HEIGHT, 0, gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps + i*BLOCK_WIDTH*BLOCK_HEIGHT*lightmap_bytes); } for ( ; p ; p = p->chain) { if (p->flags & SURF_UNDERWATER) DrawGLWaterPolyLightmap (p); else { glBegin_fp (GL_POLYGON); v = p->verts[0]; for (j = 0; j < p->numverts; j++, v+= VERTEXSIZE) { glTexCoord2f_fp (v[5], v[6]); glVertex3fv_fp (v); } glEnd_fp (); } } } if (!r_lightmap.integer) { glDisable_fp (GL_BLEND); } if (gl_lightmap_format == GL_RGBA) { glBlendFunc_fp (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } else if (gl_lightmap_format == GL_LUMINANCE) { glBlendFunc_fp (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } if (!Translucent) glDepthMask_fp (1); // back to normal Z buffering } static void R_UpdateLightmaps (qboolean Translucent) { unsigned int i; glpoly_t *p; if (r_fullbright.integer) return; glActiveTextureARB_fp (GL_TEXTURE1_ARB); if (! lightmap_textures[0]) { // if lightmaps were hosed in a video mode change, make // sure we allocate new slots for lightmaps, otherwise // we'll probably overwrite some other existing textures. glGenTextures_fp(MAX_LIGHTMAPS, lightmap_textures); } for (i = 0; i < MAX_LIGHTMAPS; i++) { p = lightmap_polys[i]; if (!p) continue; // skip if no lightmap GL_Bind(lightmap_textures[i]); if (lightmap_modified[i]) { // if current lightmap was changed reload it // and mark as not changed. lightmap_modified[i] = false; glTexImage2D_fp (GL_TEXTURE_2D, 0, lightmap_bytes, BLOCK_WIDTH, BLOCK_HEIGHT, 0, gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps + i*BLOCK_WIDTH*BLOCK_HEIGHT*lightmap_bytes); } } glActiveTextureARB_fp (GL_TEXTURE0_ARB); } /* ================ R_RenderBrushPoly ================ */ void R_RenderBrushPoly (entity_t *e, msurface_t *fa, qboolean override) { texture_t *t; byte *base; int maps; float intensity, alpha_val; c_brush_polys++; if (gl_mtexable) glActiveTextureARB_fp(GL_TEXTURE0_ARB); intensity = 1.0f; alpha_val = 1.0f; if (e->drawflags & DRF_TRANSLUCENT) { glEnable_fp (GL_BLEND); alpha_val = r_wateralpha.value; glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } if ((e->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT) { // ent->abslight 0 - 255 glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); intensity = (float)e->abslight / 255.0f; } if (!override) glColor4f_fp(intensity, intensity, intensity, alpha_val); if (fa->flags & SURF_DRAWSKY) { // warp texture, no lightmaps EmitBothSkyLayers (fa); return; } t = R_TextureAnimation (e, fa->texinfo->texture); GL_Bind (t->gl_texturenum); if (fa->flags & SURF_DRAWTURB) { // warp texture, no lightmaps EmitWaterPolys (fa); return; } if (gl_mtexable) { if ((e->drawflags & DRF_TRANSLUCENT) || (e->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT) { if (fa->flags & SURF_UNDERWATER) DrawGLWaterPoly (fa->polys); else DrawGLPoly (fa->polys); } else { glActiveTextureARB_fp(GL_TEXTURE1_ARB); if (gl_lightmap_format == GL_LUMINANCE) glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); else glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glEnable_fp(GL_TEXTURE_2D); GL_Bind (lightmap_textures[fa->lightmaptexturenum]); //glEnable_fp (GL_BLEND); if (fa->flags & SURF_UNDERWATER) DrawGLWaterPolyMTexLM (fa->polys); else DrawGLPolyMTex (fa->polys); glDisable_fp(GL_TEXTURE_2D); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //glDisable_fp (GL_BLEND); glActiveTextureARB_fp(GL_TEXTURE0_ARB); } } else { if (fa->flags & SURF_UNDERWATER) DrawGLWaterPoly (fa->polys); else DrawGLPoly (fa->polys); } // add the poly to the proper lightmap chain fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; lightmap_polys[fa->lightmaptexturenum] = fa->polys; // check for lightmap modification for (maps = 0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++) { if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps]) goto dynamic; } if (fa->dlightframe == r_framecount // dynamic this frame || fa->cached_dlight) // dynamic previously { dynamic: if (r_dynamic.integer) { lightmap_modified[fa->lightmaptexturenum] = true; base = lightmaps + fa->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes); } } if ((e->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT || (e->drawflags & DRF_TRANSLUCENT)) { glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } if (e->drawflags & DRF_TRANSLUCENT) { glDisable_fp (GL_BLEND); } } void R_RenderBrushPolyMTex (entity_t *e, msurface_t *fa, qboolean override) { texture_t *t; byte *base; int maps; float intensity, alpha_val; c_brush_polys++; glActiveTextureARB_fp(GL_TEXTURE0_ARB); intensity = 1.0f; alpha_val = 1.0f; if (e->drawflags & DRF_TRANSLUCENT) { glEnable_fp (GL_BLEND); alpha_val = r_wateralpha.value; glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } else { /* KIERO: Seems it's enabled when we enter here. */ glDisable_fp (GL_BLEND); } if ((e->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT) { glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); intensity = (float)e->abslight / 255.0f; } if (fa->flags & SURF_DRAWTURB) { glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable_fp (GL_BLEND); glActiveTextureARB_fp(GL_TEXTURE1_ARB); glDisable_fp(GL_TEXTURE_2D); glActiveTextureARB_fp(GL_TEXTURE0_ARB); intensity = 1.0; } if (!override) glColor4f_fp(intensity, intensity, intensity, alpha_val); if (fa->flags & SURF_DRAWSKY) { // warp texture, no lightmaps EmitBothSkyLayers (fa); return; } glActiveTextureARB_fp(GL_TEXTURE0_ARB); t = R_TextureAnimation (e, fa->texinfo->texture); GL_Bind (t->gl_texturenum); if (fa->flags & SURF_DRAWTURB) { glColor4f_fp(1.0f, 1.0f, 1.0f, 1.0f); EmitWaterPolys (fa); //return; } else { if ((e->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT) { glActiveTextureARB_fp(GL_TEXTURE0_ARB); if (fa->flags & SURF_UNDERWATER) DrawGLWaterPoly (fa->polys); else DrawGLPoly (fa->polys); } else { glActiveTextureARB_fp(GL_TEXTURE1_ARB); GL_Bind (lightmap_textures[fa->lightmaptexturenum]); if (fa->flags & SURF_UNDERWATER) DrawGLWaterPolyMTexLM (fa->polys); else DrawGLPolyMTex (fa->polys); } glActiveTextureARB_fp(GL_TEXTURE1_ARB); // add the poly to the proper lightmap chain fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; lightmap_polys[fa->lightmaptexturenum] = fa->polys; // check for lightmap modification for (maps = 0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++) { if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps]) goto dynamic1; } if (fa->dlightframe == r_framecount // dynamic this frame || fa->cached_dlight) // dynamic previously { dynamic1: if (r_dynamic.integer) { lightmap_modified[fa->lightmaptexturenum] = true; base = lightmaps + fa->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes); } } } glActiveTextureARB_fp(GL_TEXTURE0_ARB); if ((e->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT || (e->drawflags & DRF_TRANSLUCENT)) { glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } if (e->drawflags & DRF_TRANSLUCENT) { glDisable_fp (GL_BLEND); } glActiveTextureARB_fp(GL_TEXTURE1_ARB); } /* ================ R_MirrorChain ================ */ static void R_MirrorChain (msurface_t *s) { if (mirror) return; mirror = true; mirror_plane = s->plane; } /* ================ R_DrawWaterSurfaces ================ */ void R_DrawWaterSurfaces (void) { int i; msurface_t *s; texture_t *t; if (r_wateralpha.value > 1) r_wateralpha.value = 1; if (r_wateralpha.value == 1.0) return; glDepthMask_fp(0); // // go back to the world matrix // glLoadMatrixf_fp (r_world_matrix); glEnable_fp (GL_BLEND); glColor4f_fp (1,1,1,r_wateralpha.value); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); for (i = 0; i < cl.worldmodel->numtextures; i++) { t = cl.worldmodel->textures[i]; if (!t) continue; s = t->texturechain; if (!s) continue; if (!(s->flags & SURF_DRAWTURB)) continue; //if ((s->flags & SURF_DRAWTURB) && (s->flags & SURF_TRANSLUCENT)) if (s->flags & SURF_TRANSLUCENT) glColor4f_fp (1,1,1,r_wateralpha.value); else glColor4f_fp (1,1,1,1); // set modulate mode explicitly GL_Bind (t->gl_texturenum); for ( ; s ; s = s->texturechain) EmitWaterPolys (s); t->texturechain = NULL; } glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor4f_fp (1,1,1,1); glDisable_fp (GL_BLEND); glDepthMask_fp (1); } /* ================ DrawTextureChains ================ */ static void DrawTextureChains (entity_t *e) { int i; msurface_t *s; texture_t *t; for (i = 0; i < cl.worldmodel->numtextures; i++) { t = cl.worldmodel->textures[i]; if (!t) continue; s = t->texturechain; if (!s) continue; if (i == skytexturenum) R_DrawSkyChain (s); else if (i == mirrortexturenum && r_mirroralpha.value != 1.0) { R_MirrorChain (s); continue; } else { if ((s->flags & SURF_DRAWTURB) && r_wateralpha.value != 1.0) continue; // draw translucent water later if (((e->drawflags & DRF_TRANSLUCENT) || (e->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT)) { for ( ; s ; s = s->texturechain) R_RenderBrushPoly (e, s, false); } else if (gl_mtexable) { glActiveTextureARB_fp(GL_TEXTURE0_ARB); glEnable_fp(GL_TEXTURE_2D); glActiveTextureARB_fp(GL_TEXTURE1_ARB); glEnable_fp(GL_TEXTURE_2D); if (gl_lightmap_format == GL_LUMINANCE) glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); else glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glEnable_fp (GL_BLEND); for ( ; s ; s = s->texturechain) R_RenderBrushPolyMTex (e, s, false); glDisable_fp(GL_TEXTURE_2D); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable_fp (GL_BLEND); glActiveTextureARB_fp(GL_TEXTURE0_ARB); } else { for ( ; s ; s = s->texturechain) R_RenderBrushPoly (e, s, false); } } t->texturechain = NULL; } } /* ================= R_DrawBrushModel ================= */ void R_DrawBrushModel (entity_t *e, qboolean Translucent) { int i, k; vec3_t mins, maxs; msurface_t *psurf; float dot; mplane_t *pplane; qmodel_t *clmodel; qboolean rotated; currenttexture = GL_UNUSED_TEXTURE; clmodel = e->model; if (e->angles[0] || e->angles[1] || e->angles[2]) { rotated = true; for (i = 0; i < 3; i++) { mins[i] = e->origin[i] - clmodel->radius; maxs[i] = e->origin[i] + clmodel->radius; } } else { rotated = false; VectorAdd (e->origin, clmodel->mins, mins); VectorAdd (e->origin, clmodel->maxs, maxs); } if (R_CullBox (mins, maxs)) return; #if 0 /* causes side effects in 16 bpp. alternative down below */ /* Get rid of Z-fighting for textures by offsetting the * drawing of entity models compared to normal polygons. * (Only works if gl_ztrick is turned off) */ if (gl_zfix.integer && !gl_ztrick.integer) { glEnable_fp(GL_POLYGON_OFFSET_FILL); glEnable_fp(GL_POLYGON_OFFSET_LINE); } #endif /* #if 0 */ glColor3f_fp (1,1,1); memset (lightmap_polys, 0, sizeof(lightmap_polys)); VectorSubtract (r_refdef.vieworg, e->origin, modelorg); if (rotated) { vec3_t temp; vec3_t forward, right, up; VectorCopy (modelorg, temp); AngleVectors (e->angles, forward, right, up); modelorg[0] = DotProduct (temp, forward); modelorg[1] = -DotProduct (temp, right); modelorg[2] = DotProduct (temp, up); } psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; // calculate dynamic lighting for bmodel if it's not an // instanced model if (clmodel->firstmodelsurface != 0 && !gl_flashblend.integer) { for (k = 0; k < MAX_DLIGHTS; k++) { if ((cl_dlights[k].die < cl.time) || (!cl_dlights[k].radius)) continue; R_MarkLights (&cl_dlights[k], 1<nodes + clmodel->hulls[0].firstclipnode); } } glPushMatrix_fp (); e->angles[0] = -e->angles[0]; // stupid quake bug e->angles[2] = -e->angles[2]; // stupid quake bug /* hack the origin to prevent bmodel z-fighting * http://forums.inside3d.com/viewtopic.php?t=1350 */ if (gl_zfix.integer) { e->origin[0] -= DIST_EPSILON; e->origin[1] -= DIST_EPSILON; e->origin[2] -= DIST_EPSILON; } R_RotateForEntity (e); /* un-hack the origin */ if (gl_zfix.integer) { e->origin[0] += DIST_EPSILON; e->origin[1] += DIST_EPSILON; e->origin[2] += DIST_EPSILON; } e->angles[0] = -e->angles[0]; // stupid quake bug e->angles[2] = -e->angles[2]; // stupid quake bug // // draw texture // for (i = 0; i < clmodel->nummodelsurfaces; i++, psurf++) { // find which side of the node we are on pplane = psurf->plane; dot = DotProduct (modelorg, pplane->normal) - pplane->dist; // draw the polygon if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { R_RenderBrushPoly (e, psurf, false); } } if (!Translucent && (e->drawflags & MLS_ABSLIGHT) != MLS_ABSLIGHT && !gl_mtexable) { R_BlendLightmaps (Translucent); } glPopMatrix_fp (); #if 0 /* see above... */ if (gl_zfix.integer && !gl_ztrick.integer) { glDisable_fp(GL_POLYGON_OFFSET_FILL); glDisable_fp(GL_POLYGON_OFFSET_LINE); } #endif /* #if 0 */ } /* ============================================================= WORLD MODEL ============================================================= */ /* ================ R_RecursiveWorldNode ================ */ static void R_RecursiveWorldNode (mnode_t *node) { int c, side; mplane_t *plane; msurface_t *surf, **mark; mleaf_t *pleaf; double dot; if (node->contents == CONTENTS_SOLID) return; // solid if (node->visframe != r_visframecount) return; if (R_CullBox (node->minmaxs, node->minmaxs+3)) return; // if a leaf node, draw stuff if (node->contents < 0) { pleaf = (mleaf_t *)node; mark = pleaf->firstmarksurface; c = pleaf->nummarksurfaces; if (c) { do { (*mark)->visframe = r_framecount; mark++; } while (--c); } // deal with model fragments in this leaf if (pleaf->efrags) R_StoreEfrags (&pleaf->efrags); return; } // node is just a decision point, so go down the apropriate sides // find which side of the node we are on plane = node->plane; switch (plane->type) { case PLANE_X: dot = modelorg[0] - plane->dist; break; case PLANE_Y: dot = modelorg[1] - plane->dist; break; case PLANE_Z: dot = modelorg[2] - plane->dist; break; default: dot = DotProduct (modelorg, plane->normal) - plane->dist; break; } if (dot >= 0) side = 0; else side = 1; // recurse down the children, front side first R_RecursiveWorldNode (node->children[side]); // draw stuff c = node->numsurfaces; if (c) { surf = cl.worldmodel->surfaces + node->firstsurface; if (dot < 0 -BACKFACE_EPSILON) side = SURF_PLANEBACK; else if (dot > BACKFACE_EPSILON) side = 0; for ( ; c ; c--, surf++) { if (surf->visframe != r_framecount) continue; // don't backface underwater surfaces, because they warp if (!(surf->flags & SURF_UNDERWATER) && ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK))) continue; // wrong side // sorting by texture, just store it out if (!mirror || surf->texinfo->texture != cl.worldmodel->textures[mirrortexturenum]) { surf->texturechain = surf->texinfo->texture->texturechain; surf->texinfo->texture->texturechain = surf; } } } // recurse down the back side R_RecursiveWorldNode (node->children[!side]); } /* ============= R_DrawWorld ============= */ void R_DrawWorld (void) { VectorCopy (r_refdef.vieworg, modelorg); currenttexture = GL_UNUSED_TEXTURE; glColor4f_fp (1.0f,1.0f,1.0f,1.0f); memset (lightmap_polys, 0, sizeof(lightmap_polys)); #ifdef QUAKE2 R_ClearSkyBox (); #endif R_RecursiveWorldNode (cl.worldmodel->nodes); DrawTextureChains (&r_worldentity); // disable multitexturing - just in case if (gl_mtexable) { glActiveTextureARB_fp (GL_TEXTURE1_ARB); glDisable_fp(GL_TEXTURE_2D); glActiveTextureARB_fp (GL_TEXTURE0_ARB); glEnable_fp(GL_TEXTURE_2D); } if (!gl_mtexable) R_BlendLightmaps (false); else R_UpdateLightmaps (false); #ifdef QUAKE2 R_DrawSkyBox (); #endif } /* ============================================================================= LIGHTMAP ALLOCATION ============================================================================= */ // returns a texture number and the position inside it static unsigned int AllocBlock (int w, int h, int *x, int *y) { int i, j; int best, best2; unsigned int texnum; for (texnum = 0; texnum < MAX_LIGHTMAPS; texnum++) { best = BLOCK_HEIGHT; for (i = 0; i < BLOCK_WIDTH - w; i++) { best2 = 0; for (j = 0; j < w; j++) { if (allocated[texnum][i+j] >= best) break; if (allocated[texnum][i+j] > best2) best2 = allocated[texnum][i+j]; } if (j == w) { // this is a valid spot *x = i; *y = best = best2; } } if (best + h > BLOCK_HEIGHT) continue; for (i = 0; i < w; i++) allocated[texnum][*x + i] = best + h; return texnum; } Sys_Error ("%s: full", __thisfunc__); return -1; // shut up the compiler } #define COLINEAR_EPSILON 0.001 static mvertex_t *r_pcurrentvertbase; static qmodel_t *currentmodel; /* ================ BuildSurfaceDisplayList ================ */ static void BuildSurfaceDisplayList (msurface_t *fa) { int i, lindex, lnumverts; medge_t *pedges, *r_pedge; float *vec; float s, t; glpoly_t *poly; // reconstruct the polygon pedges = currentmodel->edges; lnumverts = fa->numedges; // // draw texture // poly = (glpoly_t *) Hunk_AllocName (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float), "poly"); poly->next = fa->polys; poly->flags = fa->flags; fa->polys = poly; poly->numverts = lnumverts; for (i = 0; i < lnumverts; i++) { lindex = currentmodel->surfedges[fa->firstedge + i]; if (lindex > 0) { r_pedge = &pedges[lindex]; vec = r_pcurrentvertbase[r_pedge->v[0]].position; } else { r_pedge = &pedges[-lindex]; vec = r_pcurrentvertbase[r_pedge->v[1]].position; } s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; s /= fa->texinfo->texture->width; t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t /= fa->texinfo->texture->height; VectorCopy (vec, poly->verts[i]); poly->verts[i][3] = s; poly->verts[i][4] = t; // // lightmap texture coordinates // s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; s -= fa->texturemins[0]; s += fa->light_s*16; s += 8; s /= BLOCK_WIDTH*16; //fa->texinfo->texture->width; t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t -= fa->texturemins[1]; t += fa->light_t*16; t += 8; t /= BLOCK_HEIGHT*16; //fa->texinfo->texture->height; poly->verts[i][5] = s; poly->verts[i][6] = t; } // // remove co-linear points - Ed // if (!gl_keeptjunctions.integer && !(fa->flags & SURF_UNDERWATER)) { for (i = 0; i < lnumverts; ++i) { vec3_t v1, v2; float *prev, *curr, *next; prev = poly->verts[(i + lnumverts - 1) % lnumverts]; curr = poly->verts[i]; next = poly->verts[(i + 1) % lnumverts]; VectorSubtract(curr, prev, v1); VectorNormalize(v1); VectorSubtract(next, prev, v2); VectorNormalize(v2); // skip co-linear points if ((fabs(v1[0] - v2[0]) <= COLINEAR_EPSILON) && (fabs(v1[1] - v2[1]) <= COLINEAR_EPSILON) && (fabs(v1[2] - v2[2]) <= COLINEAR_EPSILON)) { int j, k; for (j = i + 1; j < lnumverts; ++j) { for (k = 0; k < VERTEXSIZE; ++k) poly->verts[j - 1][k] = poly->verts[j][k]; } --lnumverts; // retry next vertex next time, which is now current vertex --i; } } } poly->numverts = lnumverts; } /* ======================== GL_CreateSurfaceLightmap ======================== */ static void GL_CreateSurfaceLightmap (msurface_t *surf) { int smax, tmax; byte *base; if (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB)) return; smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; surf->lightmaptexturenum = AllocBlock (smax, tmax, &surf->light_s, &surf->light_t); base = lightmaps + surf->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; base += (surf->light_t * BLOCK_WIDTH + surf->light_s) * lightmap_bytes; R_BuildLightMap (surf, base, BLOCK_WIDTH*lightmap_bytes); } /* ================== GL_BuildLightmaps Builds the lightmap texture with all the surfaces from all brush models ================== */ void GL_BuildLightmaps (void) { int i, j; qmodel_t *m; memset (allocated, 0, sizeof(allocated)); r_framecount = 1; // no dlightcache if (! lightmap_textures[0]) { glGenTextures_fp(MAX_LIGHTMAPS, lightmap_textures); } for (j = 1; j < MAX_MODELS; j++) { m = cl.model_precache[j]; if (!m) break; if (m->name[0] == '*') continue; r_pcurrentvertbase = m->vertexes; currentmodel = m; for (i = 0; i < m->numsurfaces; i++) { GL_CreateSurfaceLightmap (m->surfaces + i); if (m->surfaces[i].flags & SURF_DRAWTURB) continue; #ifndef QUAKE2 if (m->surfaces[i].flags & SURF_DRAWSKY) continue; #endif if (!draw_reinit) BuildSurfaceDisplayList (m->surfaces + i); } } if (gl_mtexable) glActiveTextureARB_fp (GL_TEXTURE1_ARB); // // upload all lightmaps that were filled // for (i = 0; i < MAX_LIGHTMAPS; i++) { if (!allocated[i][0]) break; // no more used lightmap_modified[i] = false; GL_Bind(lightmap_textures[i]); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D_fp (GL_TEXTURE_2D, 0, lightmap_bytes, BLOCK_WIDTH, BLOCK_HEIGHT, 0, gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps + i*BLOCK_WIDTH*BLOCK_HEIGHT*lightmap_bytes); } if (gl_mtexable) glActiveTextureARB_fp (GL_TEXTURE0_ARB); } engine/hexen2/host.c000066400000000000000000000600331444734033100146420ustar00rootroot00000000000000/* * host.c -- coordinates spawning and killing of local servers * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2001 contributors of the Anvil of Thyrion project * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "cfgfile.h" #include "debuglog.h" #include "bgmusic.h" #include "cdaudio.h" #include /* * A server can always be started, even if the system started out as a * client to a remote system. * * A client can NOT be started if the system is started as a dedicated * server. * * Memory is cleared / released when a server or client begins, not when * they end. */ static void Host_WriteConfiguration (const char *fname); quakeparms_t *host_parms; qboolean host_initialized; // true if into command execution static jmp_buf host_abort; double host_frametime; double realtime; // without any filtering or bounding static double oldrealtime; // last frame run int host_framecount; int host_hunklevel; client_t *host_client; // current client byte *host_basepal; byte *host_colormap; cvar_t sys_ticrate = {"sys_ticrate", "0.05", CVAR_NONE}; static cvar_t sys_adaptive = {"sys_adaptive", "1", CVAR_ARCHIVE}; static cvar_t host_framerate = {"host_framerate", "0", CVAR_NONE}; // set for slow motion static cvar_t host_speeds = {"host_speeds", "0", CVAR_NONE}; // set for running times static cvar_t serverprofile = {"serverprofile", "0", CVAR_NONE}; cvar_t fraglimit = {"fraglimit", "0", CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t timelimit = {"timelimit", "0", CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t teamplay = {"teamplay", "0", CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t samelevel = {"samelevel", "0", CVAR_NONE}; cvar_t noexit = {"noexit", "0", CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t developer = {"developer", "0", CVAR_ARCHIVE}; cvar_t skill = {"skill", "1", CVAR_NONE}; // 0 - 3 cvar_t coop = {"coop", "0", CVAR_NONE}; // 0 or 1 cvar_t deathmatch = {"deathmatch", "0", CVAR_NONE}; // 0, 1, or 2 cvar_t randomclass = {"randomclass", "0", CVAR_NONE}; // 0, 1, or 2 cvar_t pausable = {"pausable", "1", CVAR_NONE}; cvar_t temp1 = {"temp1", "0", CVAR_NONE}; /* =============================================================================== SAVEGAME FILES HANDLING =============================================================================== */ void Host_RemoveGIPFiles (const char *path) { const char *name; char tempdir[MAX_OSPATH], *p; size_t len; if (path) q_strlcpy(tempdir, path, MAX_OSPATH); else q_strlcpy(tempdir, FS_GetUserdir(), MAX_OSPATH); len = strlen(tempdir); p = tempdir + len; len = sizeof(tempdir) - len; name = Sys_FindFirstFile (tempdir, "*.gip"); while (name) { q_snprintf (p, len, "/%s", name); Sys_unlink (tempdir); *p = '\0'; name = Sys_FindNextFile(); } Sys_FindClose(); } void Host_DeleteSave (const char *savepath) { char tmppath[MAX_OSPATH]; if (strstr(savepath, FS_GetUserdir()) != savepath) return; Host_RemoveGIPFiles (savepath); q_snprintf (tmppath, sizeof(tmppath), "%s/info.dat", savepath); Sys_unlink (tmppath); Sys_rmdir (savepath); } int Host_CopyFiles (const char *source, const char *pat, const char *dest) { const char *name; char tempdir[MAX_OSPATH], tempdir2[MAX_OSPATH]; int error; name = Sys_FindFirstFile(source, pat); error = 0; while (name) { if (q_snprintf(tempdir, sizeof(tempdir),"%s/%s", source, name) >= (int)sizeof(tempdir) || q_snprintf(tempdir2, sizeof(tempdir2),"%s/%s", dest, name) >= (int)sizeof(tempdir2)) { Sys_FindClose(); Host_Error("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return -1; } error = FS_CopyFile (tempdir, tempdir2); if (error) { Con_Printf ("Error copying %s to %s\n", tempdir, tempdir2); goto error_out; } name = Sys_FindNextFile(); } error_out: Sys_FindClose(); return error; } //============================================================================ /* ================ Host_EndGame Does not return either due to Sys_Error() or longjmp() ================ */ void Host_EndGame (const char *message, ...) { va_list argptr; char string[1024]; va_start (argptr,message); q_vsnprintf (string, sizeof(string), message, argptr); va_end (argptr); Con_DPrintf ("%s: %s\n", __thisfunc__, string); if (sv.active) Host_ShutdownServer (false); if (cls.state == ca_dedicated) Sys_Error ("%s: %s", __thisfunc__, string); // dedicated servers exit if (cls.demonum != -1 && !cls.timedemo) CL_NextDemo (); else CL_Disconnect (); longjmp (host_abort, 1); } /* ================ Host_Error This shuts down both the client and server Does not return either due to Sys_Error() or longjmp() ================ */ void Host_Error (const char *error, ...) { va_list argptr; char string[1024]; static qboolean inerror = false; if (inerror) Sys_Error ("%s: recursive error!", __thisfunc__); inerror = true; SCR_EndLoadingPlaque (); // reenable screen updates va_start (argptr,error); q_vsnprintf (string, sizeof(string), error, argptr); va_end (argptr); Con_Printf ("%s: %s\n", __thisfunc__, string); if (sv.active) Host_ShutdownServer (false); if (cls.state == ca_dedicated) Sys_Error ("%s: %s", __thisfunc__, string); // dedicated servers exit CL_Disconnect (); cls.demonum = -1; inerror = false; longjmp (host_abort, 1); } /* ================ Host_FindMaxClients ================ */ static void Host_FindMaxClients (void) { int i; svs.maxclients = 1; i = COM_CheckParm ("-dedicated"); if (i) { cls.state = ca_dedicated; if (i != (com_argc - 1)) { svs.maxclients = atoi (com_argv[i+1]); if (svs.maxclients < 2) svs.maxclients = 8; } else { svs.maxclients = 8; } } else cls.state = ca_disconnected; i = COM_CheckParm ("-listen"); if (i) { if (cls.state == ca_dedicated) Sys_Error ("Only one of -dedicated or -listen can be specified"); if (i != (com_argc - 1)) { svs.maxclients = atoi (com_argv[i+1]); if (svs.maxclients < 2) svs.maxclients = 8; } else { svs.maxclients = 8; } } if (svs.maxclients < 1) svs.maxclients = 8; else if (svs.maxclients > MAX_CLIENTS) svs.maxclients = MAX_CLIENTS; svs.maxclientslimit = svs.maxclients; if (svs.maxclientslimit < 4) svs.maxclientslimit = 4; svs.clients = (client_t *) Hunk_AllocName (svs.maxclientslimit*sizeof(client_t), "clients"); if (svs.maxclients > 1) Cvar_SetQuick (&deathmatch, "1"); else Cvar_SetQuick (&deathmatch, "0"); } /* =============== Host_SaveConfig_f =============== */ static void Host_SaveConfig_f (void) { const char *p; if (cmd_source != src_command) return; if (Cmd_Argc() != 2) { Con_Printf ("saveConfig : save a config file\n"); return; } p = Cmd_Argv(1); if (*p == '.' || strstr(p, "..")) { Con_Printf ("Invalid config name.\n"); return; } Host_WriteConfiguration (p); } static void Host_Version_f (void) { Con_Printf ("Version %4.2f\n", ENGINE_VERSION); Con_Printf ("Exe: " __TIME__ " " __DATE__ "\n"); } /* cvar callback functions : */ void Host_Callback_Notify (cvar_t *var) { if (sv.active) SV_BroadcastPrintf ("\"%s\" changed to \"%s\"\n", var->name, var->string); } /* ======================= Host_InitLocal ====================== */ static void Host_InitLocal (void) { Cmd_AddCommand ("saveconfig", Host_SaveConfig_f); Cmd_AddCommand ("version", Host_Version_f); Host_InitCommands (); Cvar_RegisterVariable (&developer); if (COM_CheckParm("-developer")) { Cvar_Set ("developer", "1"); Cvar_LockVar ("developer"); } Cvar_RegisterVariable (&sys_nostdout); Cvar_RegisterVariable (&sys_throttle); Cvar_RegisterVariable (&sys_ticrate); Cvar_RegisterVariable (&sys_adaptive); Cvar_RegisterVariable (&host_framerate); Cvar_RegisterVariable (&host_speeds); Cvar_RegisterVariable (&serverprofile); Cvar_RegisterVariable (&fraglimit); Cvar_RegisterVariable (&timelimit); Cvar_RegisterVariable (&teamplay); Cvar_SetCallback (&fraglimit, Host_Callback_Notify); Cvar_SetCallback (&timelimit, Host_Callback_Notify); Cvar_SetCallback (&teamplay, Host_Callback_Notify); Cvar_RegisterVariable (&samelevel); Cvar_RegisterVariable (&noexit); Cvar_SetCallback (&noexit, Host_Callback_Notify); Cvar_RegisterVariable (&skill); Cvar_RegisterVariable (&coop); Cvar_RegisterVariable (&deathmatch); Cvar_RegisterVariable (&randomclass); Cvar_RegisterVariable (&pausable); Cvar_RegisterVariable (&temp1); Host_FindMaxClients (); } /* =============== Host_WriteConfiguration Writes key bindings and archived cvars to config.cfg =============== */ static void Host_WriteConfiguration (const char *fname) { FILE *f; // dedicated servers initialize the host but don't parse and set the // config.cfg cvars if (host_initialized && !isDedicated && !host_parms->errstate) { f = fopen (FS_MakePath(FS_USERDIR,NULL,fname), "w"); if (!f) { Con_Printf ("Couldn't write %s.\n",fname); return; } Key_WriteBindings (f); Cvar_WriteVariables (f); // if mlook was down, keep it that way: if (in_mlook.state & 1) fprintf (f, "+mlook\n"); fclose (f); } } /* ================= SV_ClientPrintf Sends text across to be displayed FIXME: make this just a stuffed echo? ================= */ void SV_ClientPrintf (unsigned int unused, const char *fmt, ...) { va_list argptr; char string[1024]; va_start (argptr,fmt); q_vsnprintf (string, sizeof (string), fmt, argptr); va_end (argptr); MSG_WriteByte (&host_client->message, svc_print); MSG_WriteString (&host_client->message, string); } /* ================= SV_BroadcastPrintf Sends text to all active clients ================= */ void SV_BroadcastPrintf (const char *fmt, ...) { va_list argptr; char string[1024]; int i; va_start (argptr,fmt); q_vsnprintf (string, sizeof (string), fmt, argptr); va_end (argptr); for (i = 0; i < svs.maxclients; i++) { if (svs.clients[i].active && svs.clients[i].spawned) { MSG_WriteByte (&svs.clients[i].message, svc_print); MSG_WriteString (&svs.clients[i].message, string); } } } /* ================= Host_ClientCommands Send text over to the client to be executed ================= */ void Host_ClientCommands (const char *fmt, ...) { va_list argptr; char string[1024]; va_start (argptr,fmt); q_vsnprintf (string, sizeof (string), fmt, argptr); va_end (argptr); MSG_WriteByte (&host_client->message, svc_stufftext); MSG_WriteString (&host_client->message, string); } /* ===================== SV_DropClient Called when the player is getting totally kicked off the host if (crash = true), don't bother sending signofs ===================== */ void SV_DropClient (qboolean crash) { int saveSelf; int i; client_t *client; if (!crash) { // send any final messages (don't check for errors) if (NET_CanSendMessage (host_client->netconnection)) { MSG_WriteByte (&host_client->message, svc_disconnect); NET_SendMessage (host_client->netconnection, &host_client->message); } if (host_client->edict && host_client->spawned) { // call the prog function for removing a client // this will set the body to a dead frame, among other things saveSelf = *sv_globals.self; *sv_globals.self = EDICT_TO_PROG(host_client->edict); PR_ExecuteProgram (*sv_globals.ClientDisconnect); *sv_globals.self = saveSelf; } Sys_Printf ("Client %s removed\n",host_client->name); } // break the net connection NET_Close (host_client->netconnection); host_client->netconnection = NULL; // free the client (the body stays around) host_client->active = false; host_client->name[0] = 0; host_client->old_frags = -999999; memset(&host_client->old_v,0,sizeof(host_client->old_v)); ED_ClearEdict(host_client->edict); host_client->send_all_v = true; net_activeconnections--; // send notification to all clients for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++) { if (!client->active) continue; MSG_WriteByte (&client->message, svc_updatename); MSG_WriteByte (&client->message, host_client - svs.clients); MSG_WriteString (&client->message, ""); MSG_WriteByte (&client->message, svc_updatefrags); MSG_WriteByte (&client->message, host_client - svs.clients); MSG_WriteShort (&client->message, 0); MSG_WriteByte (&client->message, svc_updatecolors); MSG_WriteByte (&client->message, host_client - svs.clients); MSG_WriteByte (&client->message, 0); } } /* ================== Host_ShutdownServer This only happens at the end of a game, not between levels ================== */ void Host_ShutdownServer(qboolean crash) { int i; int count; sizebuf_t buf; byte message[4]; double start; if (!sv.active) return; sv.active = false; // stop all client sounds immediately if (cls.state == ca_connected) CL_Disconnect (); // flush any pending messages - like the score!!! start = Sys_DoubleTime(); do { count = 0; for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if (host_client->active && host_client->message.cursize) { if (NET_CanSendMessage (host_client->netconnection)) { NET_SendMessage(host_client->netconnection, &host_client->message); SZ_Clear (&host_client->message); } else { NET_GetMessage(host_client->netconnection); count++; } } } if ((Sys_DoubleTime() - start) > 3.0) break; } while (count); // make sure all the clients know we're disconnecting SZ_Init (&buf, message, sizeof(message)); MSG_WriteByte(&buf, svc_disconnect); count = NET_SendToAll (&buf, 5.0); if (count) Con_Printf("%s: NET_SendToAll failed for %d clients\n", __thisfunc__, count); for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if (host_client->active) SV_DropClient(crash); } // clear structures // memset (&sv, 0, sizeof(sv)); // ServerSpawn already do this by Host_ClearMemory memset (svs.clients, 0, svs.maxclientslimit*sizeof(client_t)); } /* ================ Host_ClearMemory This clears all the memory used by both the client and server, but does not reinitialize anything. ================ */ void Host_ClearMemory (void) { Con_DPrintf ("Clearing memory\n"); D_FlushCaches (); Mod_ClearAll (); /* host_hunklevel MUST be set at this point */ Hunk_FreeToLowMark (host_hunklevel); cls.signon = 0; memset (&sv, 0, sizeof(sv)); memset (&cl, 0, sizeof(cl)); } //============================================================================ /* =================== Host_FilterTime Returns false if the time is too short to run a frame =================== */ static qboolean Host_FilterTime (float time) { realtime += time; if (!cls.timedemo && realtime - oldrealtime < 1.0/72.0) return false; // framerate is too high host_frametime = realtime - oldrealtime; oldrealtime = realtime; if (host_framerate.value > 0) host_frametime = host_framerate.value; else { // don't allow really long or short frames if (host_frametime > 0.05 && !sys_adaptive.integer) host_frametime = 0.05; if (host_frametime < 0.001) host_frametime = 0.001; } return true; } /* =================== Host_GetConsoleCommands Add them exactly as if they had been typed at the console =================== */ static void Host_GetConsoleCommands (void) { const char *cmd; if (!isDedicated) return; // no stdin necessary in graphical mode while (1) { cmd = Sys_ConsoleInput (); if (!cmd) break; Cbuf_AddText (cmd); } } //#define FPS_20 /* ================== Host_ServerFrame ================== */ #ifdef FPS_20 static void _Host_ServerFrame (void) { // run the world state *sv_globals.frametime = host_frametime; // read client messages SV_RunClients (); // move things around and think // always pause in single player if in console or menus if (!sv.paused && (svs.maxclients > 1 || Key_GetDest() == key_game)) { SV_Physics (); R_UpdateParticles (); CL_UpdateEffects (); } } static void Host_ServerFrame (void) { float save_host_frametime; float temp_host_frametime; // run the world state *sv_globals.frametime = host_frametime; // set the time and clear the general datagram SV_ClearDatagram (); // check for new clients SV_CheckForNewClients (); temp_host_frametime = save_host_frametime = host_frametime; while (temp_host_frametime > (1.0/72.0)) { if (temp_host_frametime > 0.05) host_frametime = 0.05; else host_frametime = temp_host_frametime; temp_host_frametime -= host_frametime; _Host_ServerFrame (); } host_frametime = save_host_frametime; // send all messages to the clients SV_SendClientMessages (); } #else static void Host_ServerFrame (void) { // run the world state *sv_globals.frametime = host_frametime; // set the time and clear the general datagram SV_ClearDatagram (); // check for new clients SV_CheckForNewClients (); // read client messages SV_RunClients (); // move things around and think // always pause in single player if in console or menus if (!sv.paused && (svs.maxclients > 1 || Key_GetDest() == key_game)) SV_Physics (); // send all messages to the clients SV_SendClientMessages (); } #endif /* ================== Host_Frame Runs all active servers ================== */ static void _Host_Frame (float time) { static double time1 = 0; static double time2 = 0; static double time3 = 0; int pass1, pass2, pass3; #if !defined(FPS_20) double save_host_frametime,total_host_frametime; #endif if (setjmp(host_abort)) return; // something bad happened, or the server disconnected // keep the random time dependent rand (); // decide the simulation time if (!Host_FilterTime (time)) return; // don't run too fast, or packets will flood out // get new key events Sys_SendKeyEvents (); // allow mice or other external controllers to add commands IN_Commands (); // process console commands Cbuf_Execute (); NET_Poll(); // if running the server locally, make intentions now if (sv.active) CL_SendCmd (); //------------------- // // server operations // //------------------- // check for commands typed to the host Host_GetConsoleCommands (); #ifdef FPS_20 if (sv.active) Host_ServerFrame (); //------------------- // // client operations // //------------------- // if running the server remotely, send intentions now after // the incoming messages have been read if (!sv.active) CL_SendCmd (); // fetch results from server if (cls.state == ca_connected) CL_ReadFromServer (); #else save_host_frametime = total_host_frametime = host_frametime; if (sys_adaptive.integer) { if (host_frametime > 0.05) host_frametime = 0.05; } if (total_host_frametime > 1.0) total_host_frametime = 0.05; do { if (sv.active) Host_ServerFrame (); //------------------- // // client operations // //------------------- // if running the server remotely, send intentions now after // the incoming messages have been read if (!sv.active) CL_SendCmd (); // fetch results from server if (cls.state == ca_connected) CL_ReadFromServer (); R_UpdateParticles (); CL_UpdateEffects (); if (!sys_adaptive.integer) break; total_host_frametime -= 0.05; if (total_host_frametime > 0 && total_host_frametime < 0.05) { save_host_frametime -= total_host_frametime; oldrealtime -= total_host_frametime; break; } } while (total_host_frametime > 0); host_frametime = save_host_frametime; #endif // update video if (host_speeds.integer) time1 = Sys_DoubleTime (); SCR_UpdateScreen (); if (host_speeds.integer) time2 = Sys_DoubleTime (); // update audio BGM_Update(); // adds music raw samples and/or advances midi driver if (cls.signon == SIGNONS) { S_Update (r_origin, vpn, vright, vup); CL_DecayLights (); } else S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin); CDAudio_Update(); if (host_speeds.integer) { pass1 = (time1 - time3)*1000; time3 = Sys_DoubleTime (); pass2 = (time2 - time1)*1000; pass3 = (time3 - time2)*1000; Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n", pass1+pass2+pass3, pass1, pass2, pass3); } host_framecount++; } void Host_Frame (float time) { double time1, time2; static double timetotal; static int timecount; int i, c, m; if (!serverprofile.integer) { _Host_Frame (time); return; } time1 = Sys_DoubleTime (); _Host_Frame (time); time2 = Sys_DoubleTime (); timetotal += time2 - time1; timecount++; if (timecount < 1000) return; m = timetotal*1000/timecount; timecount = 0; timetotal = 0; c = 0; for (i = 0; i < svs.maxclients; i++) { if (svs.clients[i].active) c++; } Con_Printf ("serverprofile: %2i clients %2i msec\n", c, m); } //============================================================================ /* ==================== Host_Init ==================== */ void Host_Init (void) { Sys_Printf ("Host_Init\n"); Memory_Init (host_parms->membase, host_parms->memsize); Cbuf_Init (); Cmd_Init (); COM_Init (); SV_Init (); FS_Init (); CL_Cmd_Init (); Host_RemoveGIPFiles(NULL); CFG_OpenConfig ("config.cfg"); Host_InitLocal (); PR_Init (); Mod_Init (); NET_Init (); Con_Printf ("Exe: " __TIME__ " " __DATE__ "\n"); Con_Printf ("%4.1f megabyte heap\n", host_parms->memsize/(1024*1024.0)); R_InitTextures (); // needed even for dedicated servers if (cls.state != ca_dedicated) // decided in Host_InitLocal() by calling Host_FindMaxClients() { V_Init (); Chase_Init (); W_LoadWadFile ("gfx.wad"); Key_Init (); Con_Init (); M_Init (); host_basepal = (byte *)FS_LoadHunkFile ("gfx/palette.lmp", NULL); if (!host_basepal) Sys_Error ("Couldn't load gfx/palette.lmp"); host_colormap = (byte *)FS_LoadHunkFile ("gfx/colormap.lmp", NULL); if (!host_colormap) Sys_Error ("Couldn't load gfx/colormap.lmp"); VID_Init (host_basepal); Draw_Init (); SCR_Init (); R_Init (); Sbar_Init(); S_Init (); CDAudio_Init(); MIDI_Init(); BGM_Init(); CL_Init(); IN_Init(); } CFG_CloseConfig(); // move commands and cvars used by progs to the front for faster access Cmd_MoveToFront ("bf"); Cvar_MoveToFront ("teamplay"); Cvar_MoveToFront ("skill"); Cvar_MoveToFront ("registered"); #ifdef GLQUAKE /* analogous to host_hunklevel, this will mark OpenGL texture * beyond which everything will need to be purged on new map */ gl_texlevel = numgltextures; #endif Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); host_hunklevel = Hunk_LowMark (); host_initialized = true; Con_Printf("\n======== Hexen II Initialized =========\n\n"); /* execute the hexen.rc file: a valid file runs default.cfg, config.cfg * and autoexec.cfg in this order, then processes the cmdline arguments * by sending "stuffcmds". */ if (cls.state != ca_dedicated) { Cbuf_InsertText ("exec hexen.rc\n"); if (!setjmp(host_abort)) /* in case exec fails with a longjmp(), e.g. Host_Error() */ Cbuf_Execute (); } Cvar_UnlockAll (); /* unlock the early-set cvars after init */ if (cls.state == ca_dedicated) { Cmd_StuffCmds_f (); /* process command line arguments */ Cbuf_Execute (); if (!sv.active) Cbuf_AddText ("map demo1\n"); } } /* =============== Host_Shutdown FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better to run quit through here before the final handoff to the sys code. =============== */ void Host_Shutdown(void) { static qboolean isdown = false; if (isdown) { printf ("recursive shutdown\n"); return; } isdown = true; // keep Con_Printf from trying to update the screen scr_disabled_for_loading = true; Host_WriteConfiguration ("config.cfg"); NET_Shutdown (); if (cls.state != ca_dedicated) { BGM_Shutdown(); CDAudio_Shutdown (); MIDI_Cleanup(); S_Shutdown(); IN_Shutdown (); VID_Shutdown(); } LOG_Close (); } engine/hexen2/host.h000066400000000000000000000062171444734033100146530ustar00rootroot00000000000000/* * host.h -- public host structures and functions * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef HX2_HOST_H #define HX2_HOST_H // quakeparms structure specifies the base of the directory tree, the // command line parms passed to the program, and the amount of memory // available for the program to use typedef struct quakeparms_s { const char *basedir; const char *userdir; // user's directory on UNIX platforms. // if user directories are enabled, basedir // and userdir will point to different // memory locations, otherwise to the same. int argc; char **argv; void *membase; int memsize; int errstate; } quakeparms_t; extern quakeparms_t *host_parms; extern qboolean isDedicated; extern cvar_t sys_ticrate; extern cvar_t sys_throttle; extern cvar_t sys_nostdout; extern cvar_t developer; extern cvar_t pausable; extern qboolean host_initialized; // true if into command execution extern double host_frametime; extern byte *host_basepal; extern byte *host_colormap; extern int host_framecount; // incremented every frame, never reset extern double realtime; // not bounded in any way, changed at // start of every frame, never reset void Host_Init (void); void Host_InitCommands (void); void Host_Shutdown(void); void Host_Callback_Notify (cvar_t *var); /* callback function for CVAR_NOTIFY */ /* Host_Error and Host_EndGame doesn't return either due to Sys_Error() or longjmp() */ FUNC_NORETURN void Host_Error (const char *error, ...) FUNC_PRINTF(1,2); FUNC_NORETURN void Host_EndGame (const char *message, ...) FUNC_PRINTF(1,2); #ifdef __WATCOMC__ #pragma aux Host_Error aborts; #pragma aux Host_EndGame aborts; #endif void Host_Frame (float time); void Host_Quit_f (void); void Host_ClientCommands (const char *fmt, ...) FUNC_PRINTF(1,2); void Host_ShutdownServer (qboolean crash); void Host_ClearMemory (void); void Host_RemoveGIPFiles (const char *path); void Host_DeleteSave (const char *savepath); int Host_CopyFiles(const char *source, const char *pat, const char *dest); int SaveGamestate (qboolean ClientsOnly); extern int current_skill; // skill level for currently loaded level (in case // the user changes the cvar while the level is // running, this reflects the level actually in use) extern int sv_kingofhill; // mission pack king of the hill. extern unsigned int info_mask, info_mask2; // mission pack objectives #endif /* HX2_HOST_H */ engine/hexen2/host_cmd.c000066400000000000000000001413521444734033100154710ustar00rootroot00000000000000/* * host_cmd.c -- console commands * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2001 contributors of the Anvil of Thyrion project * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "q_ctype.h" static double old_svtime; /* sv.time of prev. level when changing levels, saved by changelevel2(). * used for adjusting time globalvars of progs by RestoreClients() when * travelling levels back and forth (see clients.hc::ClientReEnter(). */ static int LoadGamestate (const char *level, const char *startspot, int ClientsMode); static void RestoreClients (int ClientsMode); #define TESTSAVE /* ================== Host_Quit_f ================== */ void Host_Quit_f (void) { if (Key_GetDest() != key_console && /* quit without asking if we aren't connected -- Steve */ /* cls.state != ca_dedicated */ cls.state == ca_connected) { M_Menu_Quit_f (); return; } CL_Disconnect (); Host_ShutdownServer(false); Sys_Quit (); } /* ================== Host_Status_f ================== */ static void Host_Status_f (void) { void (*print_fn) (unsigned int, const char *, ...) FUNCP_PRINTF(2,3); client_t *client; int seconds; int minutes; int hours = 0; int j; if (cmd_source == src_command) { if (!sv.active) { Cmd_ForwardToServer (); return; } print_fn = CON_Printf; } else print_fn = SV_ClientPrintf; print_fn (_PRINT_NORMAL, "host: %s\n", Cvar_VariableString ("hostname")); print_fn (_PRINT_NORMAL, "version: %4.2f\n", ENGINE_VERSION); if (tcpipAvailable) print_fn (_PRINT_NORMAL, "tcp/ip: %s\n", my_tcpip_address); if (ipxAvailable) print_fn (_PRINT_NORMAL, "ipx: %s\n", my_ipx_address); print_fn (_PRINT_NORMAL, "map: %s\n", sv.name); print_fn (_PRINT_NORMAL, "players: %i active (%i max)\n\n", net_activeconnections, svs.maxclients); for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) { if (!client->active) continue; seconds = (int)(net_time - NET_QSocketGetTime(client->netconnection)); minutes = seconds / 60; if (minutes) { seconds -= (minutes * 60); hours = minutes / 60; if (hours) minutes -= (hours * 60); } else hours = 0; print_fn (_PRINT_NORMAL, "#%-2u %-16.16s %3i %2i:%02i:%02i\n", j + 1, client->name, (int)client->edict->v.frags, hours, minutes, seconds); print_fn (_PRINT_NORMAL, " %s\n", NET_QSocketGetAddressString(client->netconnection)); } } /* ================== Host_God_f Sets client to godmode ================== */ static void Host_God_f (void) { if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (*sv_globals.deathmatch || *sv_globals.coop || skill.integer > 2) return; sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE; if (!((int)sv_player->v.flags & FL_GODMODE) ) SV_ClientPrintf (0, "godmode OFF\n"); else SV_ClientPrintf (0, "godmode ON\n"); } static void Host_Notarget_f (void) { if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (*sv_globals.deathmatch || skill.integer > 2) return; sv_player->v.flags = (int)sv_player->v.flags ^ FL_NOTARGET; if (!((int)sv_player->v.flags & FL_NOTARGET) ) SV_ClientPrintf (0, "notarget OFF\n"); else SV_ClientPrintf (0, "notarget ON\n"); } static void Host_Noclip_f (void) { if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (*sv_globals.deathmatch || *sv_globals.coop || skill.integer > 2) return; if (sv_player->v.movetype != MOVETYPE_NOCLIP) { sv_player->v.movetype = MOVETYPE_NOCLIP; SV_ClientPrintf (0, "noclip ON\n"); } else { sv_player->v.movetype = MOVETYPE_WALK; SV_ClientPrintf (0, "noclip OFF\n"); } } /* ================== Host_Ping_f ================== */ static void Host_Ping_f (void) { int i, j; float total; client_t *client; if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } SV_ClientPrintf (0, "Client ping times:\n"); for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++) { if (!client->active) continue; total = 0; for (j = 0; j < NUM_PING_TIMES; j++) total += client->ping_times[j]; total /= NUM_PING_TIMES; SV_ClientPrintf (0, "%4i %s\n", (int)(total*1000), client->name); } } /* =============================================================================== SERVER TRANSITIONS =============================================================================== */ /* ====================== Host_Map_f handle a map command from the console. Active clients are kicked off. ====================== */ static void Host_Map_f (void) { int i; char name[MAX_QPATH]; if (Cmd_Argc() < 2) //no map name given { Con_Printf ("map : start a new server\n"); if (cls.state == ca_disconnected) return; if (cls.state == ca_connected) { Con_Printf ("Current level: %s [ %s ]\n", cl.levelname, cl.mapname); return; } // (cls.state == ca_dedicated) if (sv.active) { Con_Printf ("Current level: %s [ %s ]\n", SV_GetLevelname(), sv.name); } return; } if (cmd_source != src_command) return; cls.demonum = -1; // stop demo loop in case this fails CL_Disconnect (); Host_ShutdownServer(false); Key_SetDest (key_game); // remove console or menu SCR_BeginLoadingPlaque (); info_mask = 0; if (!coop.integer && deathmatch.integer) info_mask2 = 0x80000000; else info_mask2 = 0; svs.serverflags = 0; // haven't completed an episode yet q_strlcpy (name, Cmd_Argv(1), sizeof(name)); SV_SpawnServer (name, NULL); if (!sv.active) return; if (cls.state != ca_dedicated) { loading_stage = 2; memset (cls.spawnparms, 0, MAX_MAPSTRING); for (i = 2; i < Cmd_Argc(); i++) { q_strlcat (cls.spawnparms, Cmd_Argv(i), MAX_MAPSTRING); q_strlcat (cls.spawnparms, " ", MAX_MAPSTRING); } Cmd_ExecuteString ("connect local", src_command); } } /* ================== Host_Changelevel_f Goes to a new map, taking all clients along ================== */ static void Host_Changelevel_f (void) { char level[MAX_QPATH]; char _startspot[MAX_QPATH]; char *startspot; if (Cmd_Argc() < 2) { Con_Printf ("changelevel : continue game on a new level\n"); return; } if (!sv.active || cls.demoplayback) { Con_Printf ("Only the server may changelevel\n"); return; } q_snprintf (level, sizeof(level), "maps/%s.bsp", Cmd_Argv(1)); if (!FS_FileExists(level, NULL)) Host_Error ("%s: cannot find map %s", __thisfunc__, level); q_strlcpy (level, Cmd_Argv(1), sizeof(level)); if (Cmd_Argc() == 2) startspot = NULL; else { q_strlcpy (_startspot, Cmd_Argv(2), sizeof(_startspot)); startspot = _startspot; } SV_SaveSpawnparms (); SV_SpawnServer (level, startspot); if (!sv.active) Host_Error ("%s: cannot run map %s", __thisfunc__, level); } /* ================== Host_Changelevel2_f changing levels within a unit ================== */ static void Host_Changelevel2_f (void) { char level[MAX_QPATH]; char _startspot[MAX_QPATH]; char *startspot; if (Cmd_Argc() < 2) { Con_Printf ("changelevel2 : continue game on a new level in the unit\n"); return; } if (!sv.active || cls.demoplayback) { Con_Printf ("Only the server may changelevel\n"); return; } q_snprintf (level, sizeof(level), "maps/%s.bsp", Cmd_Argv(1)); if (!FS_FileExists(level, NULL)) Host_Error ("%s: cannot find map %s", __thisfunc__, level); q_strlcpy (level, Cmd_Argv(1), sizeof(level)); if (Cmd_Argc() == 2) startspot = NULL; else { q_strlcpy (_startspot, Cmd_Argv(2), sizeof(_startspot)); startspot = _startspot; } SV_SaveSpawnparms (); // save the current level's state old_svtime = sv.time; if (SaveGamestate(false) != 0) return; // try to restore the new level if (LoadGamestate(level, startspot, 0) != 0) { SV_SpawnServer (level, startspot); if (!sv.active) Host_Error ("%s: cannot run map %s", __thisfunc__, level); RestoreClients (0); } } /* ================== Host_Restart_f Restarts the current server for a dead player ================== */ static void Host_Restart_f (void) { char mapname[MAX_QPATH]; char startspot[MAX_QPATH]; if (cls.demoplayback || !sv.active) return; if (cmd_source != src_command) return; q_strlcpy (mapname, sv.name, sizeof(mapname)); // mapname gets cleared in spawnserver q_strlcpy (startspot, sv.startspot, sizeof(startspot)); if (Cmd_Argc() == 2 && q_strcasecmp(Cmd_Argv(1),"restore") == 0) { if (LoadGamestate(mapname, startspot, 3) != 0) { SV_SpawnServer (mapname, startspot); if (!sv.active) Host_Error ("%s: cannot restart map %s", __thisfunc__, mapname); RestoreClients (0); } } else { SV_SpawnServer (mapname, startspot); if (!sv.active) Host_Error ("%s: cannot restart map %s", __thisfunc__, mapname); } } /* ================== Host_Reconnect_f This command causes the client to wait for the signon messages again. This is sent just before a server changes levels ================== */ static void Host_Reconnect_f (void) { R_ClearParticles (); //jfm: for restarts which didn't use to clear parts. if (oem.integer && cl.intermission == 9) // Matrox m3D bundle version's ending { CL_Disconnect(); return; } if (cls.demoplayback) // cross-map demo playback fix from Baker return; SCR_BeginLoadingPlaque (); cls.signon = 0; // need new connection messages } /* ===================== Host_Connect_f User command to connect to server ===================== */ static void Host_Connect_f (void) { char name[MAX_QPATH]; cls.demonum = -1; // stop demo loop in case this fails Key_SetDest (key_game); // remove console or menu if (cls.demoplayback) { CL_StopPlayback (); CL_Disconnect (); } q_strlcpy (name, Cmd_Argv(1), sizeof(name)); CL_EstablishConnection (name); Host_Reconnect_f (); } /* =============================================================================== LOAD / SAVE GAME =============================================================================== */ static char savename[MAX_OSPATH], savedest[MAX_OSPATH]; /* =============== Host_SavegameComment Writes a SAVEGAME_COMMENT_LENGTH character comment describing the game saved =============== */ static void Host_SavegameComment (char *text) { size_t i; char temp[20]; const char *levelname; for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++) { text[i] = ' '; } /* see SAVEGAME_COMMENT_LENGTH definition in quakedef.h */ levelname = SV_GetLevelname (); i = strlen(levelname); if (i > 20) i = 20; memcpy (text, levelname, i); Sys_DateTimeString (temp); temp[16] = '\0'; // eliminate seconds i = strlen(temp); memcpy (text+21, temp, i); // convert space to _ to make stdio happy for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++) { if (text[i] == ' ') text[i] = '_'; } text[SAVEGAME_COMMENT_LENGTH] = '\0'; } /* =============== Host_Savegame_f =============== */ static void Host_Savegame_f (void) { FILE *f; int i, error_state; char comment[SAVEGAME_COMMENT_LENGTH+1]; const char *p; if (cmd_source != src_command) return; if (!sv.active) { Con_Printf ("Not playing a local game.\n"); return; } if (cl.intermission) { Con_Printf ("Can't save in intermission.\n"); return; } #ifndef TESTSAVE if (svs.maxclients != 1) { Con_Printf ("Can't save multiplayer games.\n"); return; } #endif if (Cmd_Argc() != 2) { Con_Printf ("save : save a game\n"); return; } p = Cmd_Argv(1); if (*p == '.' || strstr(p, "..")) { Con_Printf ("Invalid save name.\n"); return; } for (i = 0; i < svs.maxclients; i++) { if (svs.clients[i].active && (svs.clients[i].edict->v.health <= 0) ) { Con_Printf ("Can't savegame with a dead player\n"); return; } } error_state = SaveGamestate (false); // don't bother doing more if SaveGamestate failed if (error_state) return; FS_MakePath_BUF (FS_USERDIR, &error_state, savename, sizeof(savename), p); if (error_state) { Con_Printf ("%s: save directory name too long\n", __thisfunc__); return; } if (Sys_mkdir(savename, false) != 0) { Con_Printf ("Unable to create save directory\n"); return; } Host_RemoveGIPFiles(savename); FS_MakePath_BUF (FS_USERDIR, NULL, savename, sizeof(savename), "clients.gip"); Sys_unlink(savename); FS_MakePath_BUF (FS_USERDIR, NULL, savedest, sizeof(savedest), p); Con_Printf ("Saving game to %s...\n", savedest); error_state = Host_CopyFiles(FS_GetUserdir(), "*.gip", savedest); if (error_state) goto finish; FS_MakePath_VABUF (FS_USERDIR, &error_state, savedest, sizeof(savedest), "%s/info.dat", p); if (error_state) { Host_Error ("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return; } f = fopen (savedest, "w"); if (!f) { error_state = 1; Con_Printf ("%s: Unable to open %s for writing!\n", __thisfunc__, savedest); goto finish; } fprintf (f, "%i\n", SAVEGAME_VERSION); Host_SavegameComment (comment); fprintf (f, "%s\n", comment); for (i = 0; i < NUM_SPAWN_PARMS; i++) fprintf (f, "%f\n", svs.clients->spawn_parms[i]); fprintf (f, "%d\n", current_skill); fprintf (f, "%s\n", sv.name); fprintf (f, "%f\n", sv.time); fprintf (f, "%d\n", svs.maxclients); fprintf (f, "%f\n", deathmatch.value); fprintf (f, "%f\n", coop.value); fprintf (f, "%f\n", teamplay.value); fprintf (f, "%f\n", randomclass.value); fprintf (f, "%f\n", cl_playerclass.value); // mission pack, objectives strings fprintf (f, "%u\n", info_mask); fprintf (f, "%u\n", info_mask2); error_state = ferror(f); fclose(f); finish: if (error_state) Host_Error ("%s: The game could not be saved properly!", __thisfunc__); } /* =============== Host_DeleteSave_f =============== */ static void Host_DeleteSave_f (void) { const char *p; int err; if (cmd_source != src_command) return; if (Cmd_Argc() != 2) { Con_Printf ("deletesave : delete a saved game\n"); return; } p = Cmd_Argv(1); if (*p == '.' || strstr(p, "..")) { Con_Printf ("Invalid save name.\n"); return; } FS_MakePath_BUF (FS_USERDIR, &err, savename, sizeof(savename), p); if (!err) Host_DeleteSave (savename); } /* =============== Host_Loadgame_f =============== */ static void Host_Loadgame_f (void) { FILE *f; char mapname[MAX_QPATH]; float playtime; char str[32768]; int version; int i, error_state; int tempi; float tempf; edict_t *ent; float spawn_parms[NUM_SPAWN_PARMS]; if (cmd_source != src_command) return; if (Cmd_Argc() != 2) { Con_Printf ("load : load a game\n"); return; } cls.demonum = -1; // stop demo loop in case this fails CL_Disconnect(); Host_RemoveGIPFiles(NULL); Key_SetDest (key_game); // remove console or menu FS_MakePath_BUF (FS_USERDIR, &error_state, savename, sizeof(savename), Cmd_Argv(1)); if (error_state) { Con_Printf ("%s: save directory name too long\n", __thisfunc__); return; } Con_Printf ("Loading game from %s...\n", savename); if (q_snprintf(savedest, sizeof(savedest), "%s/info.dat", savename) >= (int)sizeof(savedest)) { Host_Error ("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return; } f = fopen (savedest, "r"); if (!f) { Host_Error ("%s: ERROR: couldn't open savefile", __thisfunc__); return; } fscanf (f, "%i\n", &version); if (version != SAVEGAME_VERSION) { fclose (f); Host_Error ("Savegame is version %i, not %i", version, SAVEGAME_VERSION); return; } fscanf (f, "%s\n", str); for (i = 0; i < NUM_SPAWN_PARMS; i++) fscanf (f, "%f\n", &spawn_parms[i]); // this silliness is so we can load 1.06 save files, which have float skill values fscanf (f, "%f\n", &tempf); current_skill = (int)(tempf + 0.1); Cvar_SetValue ("skill", current_skill); Cvar_Set ("deathmatch", "0"); Cvar_Set ("coop", "0"); Cvar_Set ("teamplay", "0"); Cvar_Set ("randomclass", "0"); fscanf (f, "%s\n", mapname); fscanf (f, "%f\n", &playtime); tempi = -1; fscanf (f, "%d\n", &tempi); if (tempi >= 1) svs.maxclients = tempi; tempf = -1; fscanf (f, "%f\n", &tempf); if (tempf >= 0) Cvar_SetValue ("deathmatch", tempf); tempf = -1; fscanf (f, "%f\n", &tempf); if (tempf >= 0) Cvar_SetValue ("coop", tempf); tempf = -1; fscanf (f, "%f\n", &tempf); if (tempf >= 0) Cvar_SetValue ("teamplay", tempf); tempf = -1; fscanf (f, "%f\n", &tempf); if (tempf >= 0) Cvar_SetValue ("randomclass", tempf); tempf = -1; fscanf (f, "%f\n", &tempf); if (tempf >= 0) Cvar_SetValue ("_cl_playerclass", tempf); // mission pack, objectives strings fscanf (f, "%u\n", &info_mask); fscanf (f, "%u\n", &info_mask2); fclose (f); Host_RemoveGIPFiles(FS_GetUserdir()); FS_MakePath_BUF (FS_USERDIR, NULL, savedest, sizeof(savedest), Cmd_Argv(1)); error_state = Host_CopyFiles(savedest, "*.gip", FS_GetUserdir()); if (error_state) { Host_Error ("%s: The game could not be loaded properly!", __thisfunc__); return; } if (LoadGamestate(mapname, NULL, 2) != 0) return; SV_SaveSpawnparms (); ent = EDICT_NUM(1); Cvar_SetValue ("_cl_playerclass", ent->v.playerclass);//this better be the same as above... // this may be rudundant with the setting in PR_LoadProgs, but not sure so its here too if (sv_globals.cl_playerclass) *sv_globals.cl_playerclass = ent->v.playerclass; svs.clients->playerclass = ent->v.playerclass; sv.paused = true; // pause until all clients connect sv.loadgame = true; if (cls.state != ca_dedicated) { CL_EstablishConnection ("local"); Host_Reconnect_f (); } } int SaveGamestate (qboolean ClientsOnly) { FILE *f; edict_t *ent; int i, error_state; int start, end; char comment[SAVEGAME_COMMENT_LENGTH+1]; if (ClientsOnly) { start = 1; end = svs.maxclients+1; FS_MakePath_BUF (FS_USERDIR, &error_state, savename, sizeof(savename), "clients.gip"); if (error_state) { Host_Error ("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return -1; } } else { start = 1; end = sv.num_edicts; FS_MakePath_VABUF (FS_USERDIR, &error_state, savename, sizeof(savename), "%s.gip", sv.name); if (error_state) { Host_Error ("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return -1; } } f = fopen (savename, "w"); if (!f) { error_state = -1; Con_Printf ("%s: Unable to open %s for writing!\n", __thisfunc__, savename); goto finish; } fprintf (f, "%i\n", SAVEGAME_VERSION); if (!ClientsOnly) { Host_SavegameComment (comment); fprintf (f, "%s\n", comment); // for (i = 0; i < NUM_SPAWN_PARMS; i++) // fprintf (f, "%f\n", svs.clients->spawn_parms[i]); fprintf (f, "%f\n", skill.value); fprintf (f, "%s\n", sv.name); fprintf (f, "%f\n", sv.time); // write the light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { if (sv.lightstyles[i]) fprintf (f, "%s\n", sv.lightstyles[i]); else fprintf (f, "m\n"); } SV_SaveEffects (f); fprintf (f, "-1\n"); ED_WriteGlobals (f); } host_client = svs.clients; // save the client states for (i = start; i < end; i++) { ent = EDICT_NUM(i); if ((int)ent->v.flags & FL_ARCHIVE_OVERRIDE) continue; if (ClientsOnly) { if (host_client->active) { fprintf (f, "%i\n", i); ED_Write (f, ent); fflush (f); } host_client++; } else { fprintf (f, "%i\n", i); ED_Write (f, ent); fflush (f); } } error_state = ferror(f); fclose (f); finish: if (error_state) Host_Error ("%s: The level could not be saved properly!", __thisfunc__); return error_state; } static void RestoreClients (int ClientsMode) { int i, j; edict_t *ent; double time_diff; if (LoadGamestate(NULL, NULL, 1) != 0) return; /* O.S. -- mode 3 is only in response to the single player game "restart restore" * command issued by progs.dat::client.hc::respawn() function. No level change, * just respawning in the same map with the same playtime from when clients.gip * was saved, therefore there _CANNOT_ be a time_diff. See uhexen2 bug #2176023: * http://sourceforge.net/tracker/?group_id=124987&atid=701006&aid=2176023&func=detail */ time_diff = (ClientsMode == 3) ? 0 : sv.time - old_svtime; for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if (host_client->active) { ent = host_client->edict; //ent->v.colormap = NUM_FOR_EDICT(ent); ent->v.team = (host_client->colors & 15) + 1; ent->v.netname = PR_SetEngineString(host_client->name); ent->v.playerclass = host_client->playerclass; // copy spawn parms out of the client_t for (j = 0; j < NUM_SPAWN_PARMS; j++) sv_globals.parm[j] = host_client->spawn_parms[j]; // call the spawn function *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(ent); G_FLOAT(OFS_PARM0) = time_diff; PR_ExecuteProgram (*sv_globals.ClientReEnter); } } SaveGamestate (true); } static int LoadGamestate (const char *level, const char *startspot, int ClientsMode) { FILE *f; char mapname[MAX_QPATH]; float playtime, sk; char str[32768]; const char *start; int i, r; edict_t *ent; int entnum; int version; // float spawn_parms[NUM_SPAWN_PARMS]; qboolean auto_correct = false; if (ClientsMode == 1) /* for RestoreClients() only: map must be active */ { if (!sv.active) { Con_Printf ("%s: server not active\n", __thisfunc__); return -1; } FS_MakePath_BUF (FS_USERDIR, &r, savename, sizeof(savename), "clients.gip"); if (r) { Host_Error ("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return -1; } } else { FS_MakePath_VABUF (FS_USERDIR, &r, savename, sizeof(savename), "%s.gip", level); if (r) { Host_Error ("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return -1; } if (ClientsMode != 2 && ClientsMode != 3) Con_Printf ("Loading game from %s...\n", savename); } f = fopen (savename, "r"); if (!f) { if (ClientsMode == 2) /* caller: Host_Loadgame_f() */ Host_Error ("%s: ERROR: couldn't open savefile", __thisfunc__); return -1; } fscanf (f, "%i\n", &version); if (version != SAVEGAME_VERSION) { fclose (f); Host_Error ("Savegame is version %i, not %i", version, SAVEGAME_VERSION); return -1; } if (ClientsMode != 1) { fscanf (f, "%s\n", str); // for (i = 0; i < NUM_SPAWN_PARMS; i++) // fscanf (f, "%f\n", &spawn_parms[i]); fscanf (f, "%f\n", &sk); Cvar_SetValue ("skill", sk); fscanf (f, "%s\n", mapname); fscanf (f, "%f\n", &playtime); SV_SpawnServer (mapname, startspot); if (!sv.active) { fclose (f); Con_Printf ("Couldn't load map\n"); SCR_EndLoadingPlaque (); return -1; } // load the light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { fscanf (f, "%s\n", str); sv.lightstyles[i] = (const char *)Hunk_Strdup (str, "lightstyles"); } SV_LoadEffects (f); } // load the edicts out of the savegame file while (!feof(f)) { fscanf (f, "%i\n", &entnum); for (i = 0; i < (int) sizeof(str) - 1; i++) { r = fgetc (f); if (r == EOF || !r) break; str[i] = r; if (r == '}') { i++; break; } } if (i == (int) sizeof(str) - 1) { fclose (f); Host_Error ("%s: Loadgame buffer overflow", __thisfunc__); } str[i] = 0; start = str; start = COM_Parse(str); if (!com_token[0]) break; // end of file if (strcmp(com_token,"{")) { fclose (f); Host_Error ("%s: First token isn't a brace", __thisfunc__); } // parse an edict if (entnum == -1) { ED_ParseGlobals (start); // Need to restore this *sv_globals.startspot = PR_SetEngineString(sv.startspot); } else { ent = EDICT_NUM(entnum); /* default to active edict: ED_ParseEdict() set it * to free if it really is free. cf. ED_Write() */ ent->free = false; /* ED_ParseEdict() will always memset ent->v to 0, * because SaveGamestate() doesn't write entnum 0 */ ED_ParseEdict (start, ent); if (ClientsMode == 1 || ClientsMode == 2 || ClientsMode == 3) ent->v.stats_restored = true; // link it into the bsp tree if (!ent->free) { if (entnum >= sv.num_edicts) { /* This is necessary to restore "generated" edicts which were * not available during the map parsing by ED_LoadFromFile(). * This includes items dropped by monsters, items "dropped" by * an item_spawner such as the "prizes" in the Temple of Mars * (romeric5), a health sphere generated by the Crusader's * Holy Strength ability, or a respawning-candidate killed * monster in the expansion pack's nightmare mode. -- THOMAS */ /* Moved this into the if (!ent->free) construct: less debug * chatter. Even if this skips a free edict in between, the * skipped free edict wasn't parsed by ED_LoadFromFile() and * it will remain as a freed edict. (There is no harm because * we are dealing with extra edicts not originally present in * the map.) -- O.S. */ Con_DPrintf("%s: entnum %d >= sv.num_edicts (%d)\n", __thisfunc__, entnum, sv.num_edicts); sv.num_edicts = entnum + 1; } SV_LinkEdict (ent, false); if (ent->v.modelindex && ent->v.model) { i = SV_ModelIndex(PR_GetString(ent->v.model)); if (i != ent->v.modelindex) { ent->v.modelindex = i; auto_correct = true; } } } } } fclose (f); if (ClientsMode == 0) { sv.time = playtime; sv.paused = true; *sv_globals.serverflags = svs.serverflags; RestoreClients (0); } else if (ClientsMode == 2) { sv.time = playtime; } else if (ClientsMode == 3) { sv.time = playtime; *sv_globals.serverflags = svs.serverflags; RestoreClients (3); } if (ClientsMode != 1 && auto_correct) { Con_DPrintf("*** Auto-corrected model indexes!\n"); } // for (i = 0; i < NUM_SPAWN_PARMS; i++) // svs.clients->spawn_parms[i] = spawn_parms[i]; return 0; } //============================================================================ /* ====================== Host_Name_f ====================== */ static void Host_Name_f (void) { char newName[32]; char *pdest; if (Cmd_Argc () == 1) { Con_Printf ("\"name\" is \"%s\"\n", cl_name.string); return; } if (Cmd_Argc () == 2) q_strlcpy (newName, Cmd_Argv(1), sizeof(newName)); else q_strlcpy (newName, Cmd_Args(), sizeof(newName)); newName[15] = 0; // client_t structure actually says name[32]. //this is for the fuckers who put braces in the name causing loadgame to crash. pdest = strchr(newName,'{'); if (pdest) { *pdest = 0; //zap the brace Con_Printf ("Illegal char in name removed!\n"); } if (cmd_source == src_command) { if (strcmp(cl_name.string, newName) == 0) return; Cvar_Set ("_cl_name", newName); if (cls.state == ca_connected) Cmd_ForwardToServer (); return; } if (host_client->name[0] && strcmp(host_client->name, "unconnected") ) if (strcmp(host_client->name, newName) != 0) Con_Printf ("%s renamed to %s\n", host_client->name, newName); strcpy (host_client->name, newName); host_client->edict->v.netname = PR_SetEngineString(host_client->name); // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_updatename); MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients); MSG_WriteString (&sv.reliable_datagram, host_client->name); } extern const char *ClassNames[MAX_PLAYER_CLASS]; //from menu.c static void Host_Class_f (void) { float newClass; if (Cmd_Argc () == 1) { Con_Printf("\"playerclass\" is %d (\"%s\")\n", cl_playerclass.integer, (cl_playerclass.integer < 1 || cl_playerclass.integer > MAX_PLAYER_CLASS) ? "unknown" : ClassNames[cl_playerclass.integer - 1]); return; } if (Cmd_Argc () == 2) newClass = atof(Cmd_Argv(1)); else newClass = atof(Cmd_Args()); if (newClass < 0 || newClass > MAX_PLAYER_CLASS) { Con_Printf("Invalid player class.\n"); return; } #if ENABLE_OLD_DEMO if (gameflags & GAME_OLD_DEMO && (newClass != CLASS_PALADIN && newClass != CLASS_THEIF)) { Con_Printf("That class is not available in this demo version.\n"); return; } #endif /* OLD_DEMO */ if (newClass == CLASS_DEMON) { if (!(gameflags & GAME_PORTALS)) { Con_Printf("That class is only available in the mission pack.\n"); return; } if (sv.active && (progs->crc != PROGS_V112_CRC)) { /* FIXME: This isn't right!!! A custom progs can actually * support 5 classes and can have v1.11 structures at the * same time. I don't know a way to detect any such thing, * hence this lame solution! -- O.S. */ Con_Printf("progs.dat in use doesn't support that class.\n"); return; } } if (cmd_source == src_command) { Cvar_SetValue ("_cl_playerclass", newClass); // when class changes after map load, update cl_playerclass, // cl_playerclass should probably only be used in worldspawn, though. if (sv.active && sv_globals.cl_playerclass) *sv_globals.cl_playerclass = newClass; if (cls.state == ca_connected) Cmd_ForwardToServer (); return; } if (sv.loadgame || host_client->playerclass) { if (host_client->edict->v.playerclass) newClass = host_client->edict->v.playerclass; else if (host_client->playerclass) newClass = host_client->playerclass; } host_client->playerclass = newClass; host_client->edict->v.playerclass = newClass; // Change the weapon model used *sv_globals.self = EDICT_TO_PROG(host_client->edict); PR_ExecuteProgram (*sv_globals.ClassChangeWeapon); // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_updateclass); MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients); MSG_WriteByte (&sv.reliable_datagram, (byte)newClass); } static void Host_Say (qboolean teamonly) { int j; client_t *client; client_t *save; const char *p; char text[64], *p2; qboolean quoted; qboolean fromServer = false; if (cmd_source == src_command) { if (cls.state != ca_dedicated) { Cmd_ForwardToServer (); return; } fromServer = true; teamonly = false; } if (Cmd_Argc () < 2) return; save = host_client; p = Cmd_Args(); // remove quotes if present quoted = false; if (*p == '\"') { p++; quoted = true; } // turn on color set 1 if (!fromServer) q_snprintf (text, sizeof(text), "\001%s: %s", save->name, p); else q_snprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p); // check length & truncate if necessary j = (int) strlen(text); if (j >= (int) sizeof(text) - 1) { text[sizeof(text) - 2] = '\n'; text[sizeof(text) - 1] = '\0'; } else { p2 = text + j; while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)) ) { if (p2[-1] == '\"' && quoted) quoted = false; p2[-1] = '\0'; p2--; } p2[0] = '\n'; p2[1] = '\0'; } for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) { if (!client || !client->active || !client->spawned) continue; if (teamplay.integer && teamonly && client->edict->v.team != save->edict->v.team) continue; host_client = client; SV_ClientPrintf (0, "%s", text); } host_client = save; if (cls.state == ca_dedicated) Sys_Printf("%s", &text[1]); } static void Host_Say_f (void) { Host_Say(false); } static void Host_Say_Team_f (void) { Host_Say(true); } static void Host_Tell_f (void) { int j; client_t *client; client_t *save; const char *p; char text[64], *p2; qboolean quoted; if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (Cmd_Argc () < 3) return; p = Cmd_Args(); // remove quotes if present quoted = false; if (*p == '\"') { p++; quoted = true; } q_snprintf (text, sizeof(text), "%s: %s", host_client->name, p); // check length & truncate if necessary j = (int) strlen(text); if (j >= (int) sizeof(text) - 1) { text[sizeof(text) - 2] = '\n'; text[sizeof(text) - 1] = '\0'; } else { p2 = text + j; while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)) ) { if (p2[-1] == '\"' && quoted) quoted = false; p2[-1] = '\0'; p2--; } p2[0] = '\n'; p2[1] = '\0'; } save = host_client; for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) { if (!client->active || !client->spawned) continue; if (q_strcasecmp(client->name, Cmd_Argv(1))) continue; host_client = client; SV_ClientPrintf (0, "%s", text); break; } host_client = save; } /* ================== Host_Color_f ================== */ static void Host_Color_f (void) { int top, bottom; int playercolor; if (Cmd_Argc() == 1) { Con_Printf ("\"color\" is \"%i %i\"\n", cl_color.integer >> 4, cl_color.integer & 0x0f); Con_Printf ("color <0-10> [0-10]\n"); return; } if (Cmd_Argc() == 2) top = bottom = atoi(Cmd_Argv(1)); else { top = atoi(Cmd_Argv(1)); bottom = atoi(Cmd_Argv(2)); } top &= 15; if (top > 13) top = 13; bottom &= 15; if (bottom > 13) bottom = 13; playercolor = top*16 + bottom; if (cmd_source == src_command) { Cvar_SetValue ("_cl_color", playercolor); if (cls.state == ca_connected) Cmd_ForwardToServer (); return; } host_client->colors = playercolor; host_client->edict->v.team = bottom + 1; // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors); MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients); MSG_WriteByte (&sv.reliable_datagram, host_client->colors); } /* ================== Host_Kill_f ================== */ static void Host_Kill_f (void) { if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (sv_player->v.health <= 0 && sv_player->v.deadflag != DEAD_NO) { SV_ClientPrintf (0, "Can't suicide -- already dead!\n"); return; } *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (*sv_globals.ClientKill); } /* ================== Host_Pause_f ================== */ static void Host_Pause_f (void) { if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (!pausable.integer) SV_ClientPrintf (0, "Pause not allowed.\n"); else { sv.paused ^= 1; if (sv.paused) { SV_BroadcastPrintf ("%s paused the game\n", PR_GetString(sv_player->v.netname)); } else { SV_BroadcastPrintf ("%s unpaused the game\n",PR_GetString(sv_player->v.netname)); } // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_setpause); MSG_WriteByte (&sv.reliable_datagram, sv.paused); } } //=========================================================================== /* ================== Host_PreSpawn_f ================== */ static void Host_PreSpawn_f (void) { if (cmd_source == src_command) { Con_Printf ("prespawn is not valid from the console\n"); return; } if (host_client->spawned) { Con_Printf ("prespawn not valid -- already spawned\n"); return; } SZ_Write (&host_client->message, sv.signon.data, sv.signon.cursize); MSG_WriteByte (&host_client->message, svc_signonnum); MSG_WriteByte (&host_client->message, 2); host_client->sendsignon = true; } /* ================== Host_Spawn_f ================== */ static void Host_Spawn_f (void) { int i; client_t *client; edict_t *ent; if (cmd_source == src_command) { Con_Printf ("spawn is not valid from the console\n"); return; } if (host_client->spawned) { Con_Printf ("Spawn not valid -- already spawned\n"); return; } // send all current names, colors, and frag counts SZ_Clear (&host_client->message); // run the entrance script if (sv.loadgame) { // loaded games are fully inited already // if this is the last client to be connected, unpause sv.paused = false; } else { // set up the edict ent = host_client->edict; sv.paused = false; if (!ent->v.stats_restored || deathmatch.integer) { memset (&ent->v, 0, progs->entityfields * 4); //ent->v.colormap = NUM_FOR_EDICT(ent); ent->v.team = (host_client->colors & 15) + 1; ent->v.netname = PR_SetEngineString(host_client->name); ent->v.playerclass = host_client->playerclass; // copy spawn parms out of the client_t for (i = 0; i < NUM_SPAWN_PARMS; i++) sv_globals.parm[i] = host_client->spawn_parms[i]; // call the spawn function *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (*sv_globals.ClientConnect); if ((Sys_DoubleTime() - NET_QSocketGetTime(host_client->netconnection)) <= sv.time) Sys_Printf ("%s entered the game\n", host_client->name); PR_ExecuteProgram (*sv_globals.PutClientInServer); } } // send time of update MSG_WriteByte (&host_client->message, svc_time); MSG_WriteFloat (&host_client->message, sv.time); for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++) { MSG_WriteByte (&host_client->message, svc_updatename); MSG_WriteByte (&host_client->message, i); MSG_WriteString (&host_client->message, client->name); MSG_WriteByte (&host_client->message, svc_updateclass); MSG_WriteByte (&host_client->message, i); MSG_WriteByte (&host_client->message, client->playerclass); MSG_WriteByte (&host_client->message, svc_updatefrags); MSG_WriteByte (&host_client->message, i); MSG_WriteShort (&host_client->message, client->old_frags); MSG_WriteByte (&host_client->message, svc_updatecolors); MSG_WriteByte (&host_client->message, i); MSG_WriteByte (&host_client->message, client->colors); } // send all current light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { MSG_WriteByte (&host_client->message, svc_lightstyle); MSG_WriteByte (&host_client->message, (char)i); MSG_WriteString (&host_client->message, sv.lightstyles[i]); } // // send some stats // MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_TOTALSECRETS); MSG_WriteLong (&host_client->message, *sv_globals.total_secrets); MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_TOTALMONSTERS); MSG_WriteLong (&host_client->message, *sv_globals.total_monsters); MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_SECRETS); MSG_WriteLong (&host_client->message, *sv_globals.found_secrets); MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_MONSTERS); MSG_WriteLong (&host_client->message, *sv_globals.killed_monsters); SV_UpdateEffects(&host_client->message); // // send a fixangle // Never send a roll angle, because savegames can catch the server // in a state where it is expecting the client to correct the angle // and it won't happen if the game was just loaded, so you wind up // with a permanent head tilt ent = EDICT_NUM( 1 + (host_client - svs.clients) ); MSG_WriteByte (&host_client->message, svc_setangle); for (i = 0; i < 2; i++) MSG_WriteAngle (&host_client->message, ent->v.angles[i] ); MSG_WriteAngle (&host_client->message, 0); SV_WriteClientdataToMessage (host_client, sv_player, &host_client->message); MSG_WriteByte (&host_client->message, svc_signonnum); MSG_WriteByte (&host_client->message, 3); host_client->sendsignon = true; } /* ================== Host_Begin_f ================== */ static void Host_Begin_f (void) { if (cmd_source == src_command) { Con_Printf ("begin is not valid from the console\n"); return; } host_client->spawned = true; } //=========================================================================== dfunction_t *ED_FindFunctioni (const char *fn_name); static int strdiff (const char *s1, const char *s2) { int L1, L2, i; L1 = strlen(s1); L2 = strlen(s2); for (i = 0; (i < L1 && i < L2); i++) { if (q_tolower(s1[i]) != q_tolower(s2[i])) break; } return i; } static void Host_Create_f (void) { const char *FindName; dfunction_t *Search, *func; edict_t *ent; int i, fLength, NumFound, Diff, NewDiff; if (!sv.active) { Con_Printf("server is not active!\n"); return; } if (svs.maxclients != 1 || skill.integer > 2) { Con_Printf("can't cheat anymore!\n"); return; } if (Cmd_Argc () == 1) { Con_Printf("create \n"); return; } FindName = Cmd_Argv(1); func = ED_FindFunctioni ( FindName ); if (!func) { fLength = strlen(FindName); NumFound = 0; Diff = 999; for (i = 0; i < progs->numfunctions; i++) { Search = &pr_functions[i]; if ( !q_strncasecmp(PR_GetString(Search->s_name), FindName, fLength) ) { if (NumFound == 1) { Con_Printf(" %s\n", PR_GetString(func->s_name)); } if (NumFound) { Con_Printf(" %s\n", PR_GetString(Search->s_name)); NewDiff = strdiff(PR_GetString(Search->s_name), PR_GetString(func->s_name)); if (NewDiff < Diff) Diff = NewDiff; } func = Search; NumFound++; } } if (!NumFound) { Con_Printf("Could not find spawn function\n"); return; } if (NumFound != 1) { q_snprintf(key_lines[edit_line], MAXCMDLINE, ">create %s", PR_GetString(func->s_name)); key_lines[edit_line][Diff+8] = 0; key_linepos = strlen(key_lines[edit_line]); return; } } Con_Printf("Executing %s...\n", PR_GetString(func->s_name)); ent = ED_Alloc (); ent->v.classname = func->s_name; VectorCopy(r_origin,ent->v.origin); ent->v.origin[0] += vpn[0] * 80; ent->v.origin[1] += vpn[1] * 80; ent->v.origin[2] += vpn[2] * 80; VectorCopy(ent->v.origin,ent->v.absmin); VectorCopy(ent->v.origin,ent->v.absmax); ent->v.absmin[0] -= 16; ent->v.absmin[1] -= 16; ent->v.absmin[2] -= 16; ent->v.absmax[0] += 16; ent->v.absmax[1] += 16; ent->v.absmax[2] += 16; *sv_globals.self = EDICT_TO_PROG(ent); ignore_precache = true; PR_ExecuteProgram (func - pr_functions); ignore_precache = false; } //=========================================================================== /* ================== Host_Kick_f Kicks a user off of the server ================== */ static void Host_Kick_f (void) { const char *who; const char *message = NULL; client_t *save; int i; qboolean byNumber = false; if (cmd_source == src_command) { if (!sv.active) { Cmd_ForwardToServer (); return; } } else if (*sv_globals.deathmatch) return; save = host_client; if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0) { i = atof(Cmd_Argv(2)) - 1; if (i < 0 || i >= svs.maxclients) return; if (!svs.clients[i].active) return; host_client = &svs.clients[i]; byNumber = true; } else { for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if (!host_client->active) continue; if (q_strcasecmp(host_client->name, Cmd_Argv(1)) == 0) break; } } if (i < svs.maxclients) { if (cmd_source == src_command) { if (cls.state == ca_dedicated) who = "Console"; else who = cl_name.string; } else who = save->name; // can't kick yourself! if (host_client == save) return; if (Cmd_Argc() > 2) { message = COM_Parse(Cmd_Args()); if (byNumber) { message++; // skip the # while (*message == ' ') // skip white space message++; message += strlen(Cmd_Argv(2)); // skip the number } while (*message && *message == ' ') message++; } if (message) SV_ClientPrintf (0, "Kicked by %s: %s\n", who, message); else SV_ClientPrintf (0, "Kicked by %s\n", who); SV_DropClient (false); } host_client = save; } /* =============================================================================== DEBUGGING TOOLS =============================================================================== */ /* ================== Host_Give_f ================== */ static void Host_Give_f (void) { const char *t; int v; if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (*sv_globals.deathmatch || skill.integer > 2) return; t = Cmd_Argv(1); v = atoi (Cmd_Argv(2)); switch (t[0]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (t[0] >= '2') sv_player->v.items = (int)sv_player->v.items | (IT_SHOTGUN << (t[0] - '2')); break; case 's': case 'n': case 'l': case 'r': case 'm': break; case 'h': sv_player->v.health = v; break; case 'c': case 'p': break; } } static edict_t *FindViewthing (void) { int i; edict_t *e; for (i = 0; i < sv.num_edicts; i++) { e = EDICT_NUM(i); if ( !strcmp (PR_GetString(e->v.classname), "viewthing") ) return e; } Con_Printf ("No viewthing on map\n"); return NULL; } /* ================== Host_Viewmodel_f ================== */ static void Host_Viewmodel_f (void) { edict_t *e; qmodel_t *m; e = FindViewthing (); if (!e) return; m = Mod_ForName (Cmd_Argv(1), false); if (!m) { Con_Printf ("Can't load %s\n", Cmd_Argv(1)); return; } e->v.frame = 0; cl.model_precache[(int)e->v.modelindex] = m; } /* ================== Host_Viewframe_f ================== */ static void Host_Viewframe_f (void) { edict_t *e; qmodel_t *m; int f; e = FindViewthing (); if (!e) return; m = cl.model_precache[(int)e->v.modelindex]; f = atoi(Cmd_Argv(1)); if (f >= m->numframes) f = m->numframes-1; e->v.frame = f; } static void PrintFrameName (qmodel_t *m, int frame) { aliashdr_t *hdr; maliasframedesc_t *pframedesc; hdr = (aliashdr_t *)Mod_Extradata (m); if (!hdr) return; pframedesc = &hdr->frames[frame]; Con_Printf ("frame %i: %s\n", frame, pframedesc->name); } /* ================== Host_Viewnext_f ================== */ static void Host_Viewnext_f (void) { edict_t *e; qmodel_t *m; e = FindViewthing (); if (!e) return; m = cl.model_precache[(int)e->v.modelindex]; e->v.frame = e->v.frame + 1; if (e->v.frame >= m->numframes) e->v.frame = m->numframes - 1; PrintFrameName (m, e->v.frame); } /* ================== Host_Viewprev_f ================== */ static void Host_Viewprev_f (void) { edict_t *e; qmodel_t *m; e = FindViewthing (); if (!e) return; m = cl.model_precache[(int)e->v.modelindex]; e->v.frame = e->v.frame - 1; if (e->v.frame < 0) e->v.frame = 0; PrintFrameName (m, e->v.frame); } /* =============================================================================== DEMO LOOP CONTROL =============================================================================== */ /* ================== Host_Startdemos_f ================== */ static void Host_Startdemos_f (void) { int i, c; if (cls.state == ca_dedicated) return; c = Cmd_Argc() - 1; if (c > MAX_DEMOS) { Con_Printf ("Max %i demos in demoloop\n", MAX_DEMOS); c = MAX_DEMOS; } Con_Printf ("%i demo(s) in loop\n", c); for (i = 1; i < c + 1; i++) q_strlcpy (cls.demos[i-1], Cmd_Argv(i), sizeof(cls.demos[0])); if (!sv.active && cls.demonum != -1 && !cls.demoplayback) { cls.demonum = 0; CL_NextDemo (); } else cls.demonum = -1; } /* ================== Host_Demos_f Return to looping demos ================== */ static void Host_Demos_f (void) { if (cls.state == ca_dedicated) return; if (cls.demonum == -1) cls.demonum = 1; CL_Disconnect_f (); CL_NextDemo (); } /* ================== Host_Stopdemo_f Return to looping demos ================== */ static void Host_Stopdemo_f (void) { if (cls.state == ca_dedicated) return; if (!cls.demoplayback) return; CL_StopPlayback (); CL_Disconnect (); } //============================================================================= /* ================== Host_InitCommands ================== */ void Host_InitCommands (void) { Cmd_AddCommand ("status", Host_Status_f); Cmd_AddCommand ("quit", Host_Quit_f); Cmd_AddCommand ("god", Host_God_f); Cmd_AddCommand ("notarget", Host_Notarget_f); Cmd_AddCommand ("map", Host_Map_f); Cmd_AddCommand ("restart", Host_Restart_f); Cmd_AddCommand ("changelevel", Host_Changelevel_f); Cmd_AddCommand ("changelevel2", Host_Changelevel2_f); Cmd_AddCommand ("connect", Host_Connect_f); Cmd_AddCommand ("reconnect", Host_Reconnect_f); Cmd_AddCommand ("name", Host_Name_f); Cmd_AddCommand ("playerclass", Host_Class_f); Cmd_AddCommand ("noclip", Host_Noclip_f); Cmd_AddCommand ("say", Host_Say_f); Cmd_AddCommand ("say_team", Host_Say_Team_f); Cmd_AddCommand ("tell", Host_Tell_f); Cmd_AddCommand ("color", Host_Color_f); Cmd_AddCommand ("kill", Host_Kill_f); Cmd_AddCommand ("pause", Host_Pause_f); Cmd_AddCommand ("spawn", Host_Spawn_f); Cmd_AddCommand ("begin", Host_Begin_f); Cmd_AddCommand ("prespawn", Host_PreSpawn_f); Cmd_AddCommand ("kick", Host_Kick_f); Cmd_AddCommand ("ping", Host_Ping_f); Cmd_AddCommand ("load", Host_Loadgame_f); Cmd_AddCommand ("save", Host_Savegame_f); Cmd_AddCommand ("deletesave", Host_DeleteSave_f); Cmd_AddCommand ("give", Host_Give_f); Cmd_AddCommand ("startdemos", Host_Startdemos_f); Cmd_AddCommand ("demos", Host_Demos_f); Cmd_AddCommand ("stopdemo", Host_Stopdemo_f); Cmd_AddCommand ("viewmodel", Host_Viewmodel_f); Cmd_AddCommand ("viewframe", Host_Viewframe_f); Cmd_AddCommand ("viewnext", Host_Viewnext_f); Cmd_AddCommand ("viewprev", Host_Viewprev_f); Cmd_AddCommand ("create", Host_Create_f); } engine/hexen2/keys.c000066400000000000000000000532731444734033100146500ustar00rootroot00000000000000/* * keys.c -- key up events are sent even if in console mode * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2006-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" char key_lines[32][MAXCMDLINE]; int key_linepos; static qboolean shift_down = false; int key_lastpress; int key_insert = 1; // insert/overwrite mode toggle int edit_line = 0; static int history_line = 0; static keydest_t key_dest; static qboolean key_gamekey, prev_gamekey; int key_count; // incremented every key event char *keybindings[256]; static qboolean consolekeys[256]; // if true, can't be rebound while in console static qboolean menubound[256]; // if true, can't be rebound while in menu static int keyshift[256]; // key to map to if shift held down in console static int key_repeats[256]; // if > 1, it is autorepeating static qboolean keyreserved[256]; // hardcoded, can't be rebound by the user static qboolean keydown[256]; typedef struct { const char *name; int keynum; } keyname_t; static keyname_t keynames[] = { {"TAB", K_TAB}, {"ENTER", K_ENTER}, {"ESCAPE", K_ESCAPE}, {"SPACE", K_SPACE}, {"BACKSPACE", K_BACKSPACE}, {"UPARROW", K_UPARROW}, {"DOWNARROW", K_DOWNARROW}, {"LEFTARROW", K_LEFTARROW}, {"RIGHTARROW", K_RIGHTARROW}, {"ALT", K_ALT}, {"CTRL", K_CTRL}, {"SHIFT", K_SHIFT}, {"F1", K_F1}, {"F2", K_F2}, {"F3", K_F3}, {"F4", K_F4}, {"F5", K_F5}, {"F6", K_F6}, {"F7", K_F7}, {"F8", K_F8}, {"F9", K_F9}, {"F10", K_F10}, {"F11", K_F11}, {"F12", K_F12}, {"INS", K_INS}, {"DEL", K_DEL}, {"PGDN", K_PGDN}, {"PGUP", K_PGUP}, {"HOME", K_HOME}, {"END", K_END}, // {"KP_NUMLOCK", K_KP_NUMLOCK}, {"KP_SLASH", K_KP_SLASH}, {"KP_STAR", K_KP_STAR}, {"KP_MINUS", K_KP_MINUS}, {"KP_HOME", K_KP_HOME}, {"KP_UPARROW", K_KP_UPARROW}, {"KP_PGUP", K_KP_PGUP}, {"KP_PLUS", K_KP_PLUS}, {"KP_LEFTARROW", K_KP_LEFTARROW}, {"KP_5", K_KP_5}, {"KP_RIGHTARROW", K_KP_RIGHTARROW}, {"KP_END", K_KP_END}, {"KP_DOWNARROW", K_KP_DOWNARROW}, {"KP_PGDN", K_KP_PGDN}, {"KP_ENTER", K_KP_ENTER}, {"KP_INS", K_KP_INS}, {"KP_DEL", K_KP_DEL}, {"COMMAND", K_COMMAND}, {"MOUSE1", K_MOUSE1}, {"MOUSE2", K_MOUSE2}, {"MOUSE3", K_MOUSE3}, {"MWHEELUP", K_MWHEELUP}, {"MWHEELDOWN", K_MWHEELDOWN}, {"MOUSE4", K_MOUSE4}, {"MOUSE5", K_MOUSE5}, {"JOY1", K_JOY1}, {"JOY2", K_JOY2}, {"JOY3", K_JOY3}, {"JOY4", K_JOY4}, {"AUX1", K_AUX1}, {"AUX2", K_AUX2}, {"AUX3", K_AUX3}, {"AUX4", K_AUX4}, {"AUX5", K_AUX5}, {"AUX6", K_AUX6}, {"AUX7", K_AUX7}, {"AUX8", K_AUX8}, {"AUX9", K_AUX9}, {"AUX10", K_AUX10}, {"AUX11", K_AUX11}, {"AUX12", K_AUX12}, {"AUX13", K_AUX13}, {"AUX14", K_AUX14}, {"AUX15", K_AUX15}, {"AUX16", K_AUX16}, {"AUX17", K_AUX17}, {"AUX18", K_AUX18}, {"AUX19", K_AUX19}, {"AUX20", K_AUX20}, {"AUX21", K_AUX21}, {"AUX22", K_AUX22}, {"AUX23", K_AUX23}, {"AUX24", K_AUX24}, {"AUX25", K_AUX25}, {"AUX26", K_AUX26}, {"AUX27", K_AUX27}, {"AUX28", K_AUX28}, {"AUX29", K_AUX29}, {"AUX30", K_AUX30}, {"AUX31", K_AUX31}, {"AUX32", K_AUX32}, {"PAUSE", K_PAUSE}, {"SEMICOLON", ';'}, // because a raw semicolon seperates commands {NULL, 0} }; /* ============================================================================== LINE TYPING INTO THE CONSOLE ============================================================================== */ static void CompleteCommand (void) { char *matches[MAX_MATCHES]; char backup[MAXCMDLINE]; char c, *prefix, *workline; qboolean editing; int count, i; size_t len1, len2; if (key_linepos < 2) return; workline = key_lines[edit_line]; c = workline[key_linepos]; editing = (c != 0); if (editing) { // make a copy of the text starting from the // cursor position (see below) q_strlcpy(backup, workline + key_linepos, sizeof(backup)); } // complete the text only up to the cursor position: // bash style. cut off the rest for now. workline[key_linepos] = 0; prefix = workline + 1; // skip the leading whitespace and command markers while (*prefix) { if (*prefix != '\\' && *prefix != '/' && *prefix > ' ') break; ++prefix; } // if the remainder line has no length or has // spaces in it, don't bother if (!*prefix || strstr(prefix," ")) { workline[key_linepos] = c; return; } // store the length of the relevant partial len1 = len2 = strlen(prefix); // start checking for matches, finally... count = 0; count += ListCommands(prefix, (const char**)matches, count); count += ListCvars (prefix, (const char**)matches, count); count += ListAlias (prefix, (const char**)matches, count); if (count) { // do not do a full auto-complete // unless there is only one match if (count == 1) { // workline[1] = '/'; // q_strlcpy (workline + 2, matches[0], MAXCMDLINE-2); // key_linepos = 2 + strlen(matches[0]); q_strlcpy (workline + 1, matches[0], MAXCMDLINE-1); key_linepos = 1 + strlen(matches[0]); workline[key_linepos] = ' '; key_linepos++; } else { // more than one match, sort and list all of them qsort (matches, count, sizeof(char *), COM_StrCompare); Con_Printf("\n"); #if 0 // plain listing for (i = 0; i < count && i < MAX_MATCHES; i++) Con_Printf ("%s\n", matches[i]); Con_Printf("\n%d matches found\n\n", count); #else // S.A.: columnize the listing. Con_Printf("%d possible completions:\n\n", count); Con_ShowList (count, (const char**) matches); Con_Printf("\n"); #endif // cycle throgh all matches and see // if there is a partial completion _search: for (i = 1; i < count && i < MAX_MATCHES; i++) { if (matches[0][len2] != matches[i][len2]) goto _check; } ++len2; goto _search; _check: if (len2 > len1) // found a partial match { // workline[1] = '/'; // strncpy (workline + 2, matches[0], len2); // key_linepos = len2 + 2; strncpy (workline + 1, matches[0], len2); key_linepos = len2 + 1; } } workline[key_linepos] = 0; } // put back the remainder of the original text // which was lost after the trimming if (editing) q_strlcpy (workline + key_linepos, backup, MAXCMDLINE-key_linepos); } static void PasteToConsole (void) { char *cbd, *p, *workline; int mvlen, inslen; if (key_linepos == MAXCMDLINE - 1) return; if ((cbd = Sys_GetClipboardData()) == NULL) return; p = cbd; while (*p) { if (*p == '\n' || *p == '\r' || *p == '\b') { *p = 0; break; } p++; } inslen = (int) (p - cbd); if (inslen + key_linepos > MAXCMDLINE - 1) inslen = MAXCMDLINE - 1 - key_linepos; if (inslen <= 0) goto done; workline = key_lines[edit_line]; workline += key_linepos; mvlen = (int) strlen(workline); if (mvlen + inslen + key_linepos > MAXCMDLINE - 1) { mvlen = MAXCMDLINE - 1 - key_linepos - inslen; if (mvlen < 0) mvlen = 0; } // insert the string if (mvlen != 0) memmove (workline + inslen, workline, mvlen); memcpy (workline, cbd, inslen); key_linepos += inslen; workline[mvlen + inslen] = '\0'; done: Z_Free(cbd); } /* ==================== Key_Console Interactive line editing and console scrollback ==================== */ static void Key_Console (int key) { int history_line_last; size_t len; char *workline = key_lines[edit_line]; switch (key) { case K_ENTER: Cbuf_AddText (workline + 1); // skip the > Cbuf_AddText ("\n"); Con_Printf ("%s\n", workline); edit_line = (edit_line + 1) & 31; history_line = edit_line; key_lines[edit_line][0] = ']'; key_lines[edit_line][1] = 0; key_linepos = 1; if (cls.state == ca_disconnected) SCR_UpdateScreen (); // force an update, because the command // may take some time return; case K_TAB: CompleteCommand (); return; case K_LEFTARROW: if (key_linepos < 2) return; if (keydown[K_CTRL]) { /* ctrl - left, word processor style: first, * move to the ending of previous word, then * move to its beginning */ char *p = workline + key_linepos - 1; while (p != workline && *p == ' ') --p; while (p != workline) { if (*--p == ' ') break; } key_linepos = (int)(p - workline) + 1; } else /* simple cursor-to-left, only. */ { --key_linepos; } return; case K_RIGHTARROW: if (!workline[key_linepos]) return; if (keydown[K_CTRL]) { /* ctrl - right, word processor style: if * we are on a text move to its end, then * move to the beginning of the next word */ char *p = workline + key_linepos; while (*p && *p != ' ') ++p; while (*p && *p == ' ') ++p; key_linepos = (int)(p - workline); } else /* simple cursor-to-right only. */ { ++key_linepos; } return; case K_BACKSPACE: if (key_linepos > 1) { workline += key_linepos - 1; if (workline[1]) { len = strlen(workline); memmove (workline, workline + 1, len); } else *workline = 0; key_linepos--; } return; case K_DEL: workline += key_linepos; if (*workline) { if (workline[1]) { len = strlen(workline); memmove (workline, workline + 1, len); } else *workline = 0; } return; case K_UPARROW: history_line_last = history_line; do { history_line = (history_line - 1) & 31; } while (history_line != edit_line && !key_lines[history_line][1]); if (history_line == edit_line) history_line = history_line_last; len = strlen(key_lines[history_line]); memmove(workline, key_lines[history_line], len+1); key_linepos = (int)len; return; case K_DOWNARROW: if (history_line == edit_line) return; do { history_line = (history_line + 1) & 31; } while (history_line != edit_line && !key_lines[history_line][1]); if (history_line == edit_line) { workline[0] = ']'; workline[1] = 0; key_linepos = 1; } else { len = strlen(key_lines[history_line]); memmove(workline, key_lines[history_line], len+1); key_linepos = (int)len; } return; case K_PGUP: case K_MWHEELUP: con->display -= 2; return; case K_PGDN: case K_MWHEELDOWN: con->display += 2; if (con->display > con->current) con->display = con->current; return; case K_HOME: if (keydown[K_CTRL]) con->display = con->current - con_totallines + 10; else key_linepos = 1; return; case K_END: if (keydown[K_CTRL]) con->display = con->current; else key_linepos = strlen(workline); return; case K_INS: if (keydown[K_SHIFT]) /* Shift+Ins paste */ PasteToConsole(); else key_insert ^= 1; return; case 'v': case 'V': #if defined(PLATFORM_OSX) || defined(PLATFORM_MAC) if (keydown[K_COMMAND]) { /* Cmd+V paste (Mac-only) */ PasteToConsole(); return; } #endif if (keydown[K_CTRL]) { /* Ctrl+v paste */ PasteToConsole(); return; } break; case 'c': case 'C': if (keydown[K_CTRL]) { /* Ctrl+C: abort the line -- S.A */ Con_Printf ("%s\n", workline); workline[0] = ']'; workline[1] = 0; key_linepos = 1; history_line= edit_line; return; } break; } if (key < 32 || key > 127) return; // non printable if (key_linepos < MAXCMDLINE - 1) { qboolean endpos = !workline[key_linepos]; // if inserting, move the text to the right if (key_insert && !endpos) { workline[MAXCMDLINE - 2] = 0; workline += key_linepos; len = strlen(workline) + 1; memmove (workline + 1, workline, len); *workline = key; } else { workline += key_linepos; *workline = key; // null terminate if at the end if (endpos) workline[1] = 0; } key_linepos++; } } //============================================================================ qboolean chat_team = false; static char chat_buffer[32]; static int chat_bufferlen = 0; const char *Key_GetChatBuffer (void) { return chat_buffer; } int Key_GetChatMsgLen (void) { return chat_bufferlen; } void Key_EndChat (void) { Key_SetDest (key_game); chat_bufferlen = 0; chat_buffer[0] = 0; } static void Key_Message (int key) { if (key == K_ENTER) { if (chat_team) Cbuf_AddText ("say_team \""); else Cbuf_AddText ("say \""); Cbuf_AddText(chat_buffer); Cbuf_AddText("\"\n"); Key_EndChat (); return; } if (key == K_ESCAPE) { Key_EndChat (); return; } if (key == K_BACKSPACE) { if (chat_bufferlen) chat_buffer[--chat_bufferlen] = 0; return; } if (key < 32 || key > 127) return; // non printable if (chat_bufferlen == sizeof(chat_buffer) - 1) return; // all full chat_buffer[chat_bufferlen++] = key; chat_buffer[chat_bufferlen] = 0; } //============================================================================ /* =================== Key_StringToKeynum Returns a key number to be used to index keybindings[] by looking at the given string. Single ascii characters return themselves, while the K_* names are matched up. =================== */ static int Key_StringToKeynum (const char *str) { keyname_t *kn; if (!str || !str[0]) return -1; if (!str[1]) return str[0]; for (kn = keynames; kn->name; kn++) { if (!q_strcasecmp(str,kn->name)) return kn->keynum; } return -1; } /* =================== Key_KeynumToString Returns a string (either a single ascii char, or a K_* name) for the given keynum. FIXME: handle quote special (general escape sequence?) =================== */ const char *Key_KeynumToString (int keynum) { static char tinystr[2]; keyname_t *kn; if (keynum == -1) return ""; if (keynum > 32 && keynum < 127) { // printable ascii tinystr[0] = keynum; tinystr[1] = 0; return tinystr; } for (kn = keynames; kn->name; kn++) { if (keynum == kn->keynum) return kn->name; } return ""; } /* =================== Key_SetBinding =================== */ void Key_SetBinding (int keynum, const char *binding) { if (keynum == -1) return; if (keyreserved[keynum]) return; // free old bindings if (keybindings[keynum]) { Z_Free (keybindings[keynum]); keybindings[keynum] = NULL; } // allocate memory for new binding if (binding) keybindings[keynum] = Z_Strdup(binding); } /* =================== Key_Unbind_f =================== */ static void Key_Unbind_f (void) { int b; if (Cmd_Argc() != 2) { Con_Printf ("unbind : remove commands from a key\n"); return; } b = Key_StringToKeynum (Cmd_Argv(1)); if (b == -1) { Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } Key_SetBinding (b, NULL); } static void Key_Unbindall_f (void) { int i; for (i = 0; i < 256; i++) Key_SetBinding(i, NULL); } /* =================== Key_Bind_f =================== */ static void Key_Bind_f (void) { int i, c, b; char cmd[1024]; c = Cmd_Argc(); if (c != 2 && c != 3) { Con_Printf ("bind [command] : attach a command to a key\n"); return; } b = Key_StringToKeynum (Cmd_Argv(1)); if (b == -1) { Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } if (c == 2) { if (keybindings[b]) Con_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b] ); else Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) ); return; } // copy the rest of the command line cmd[0] = 0; for (i = 2; i < c; i++) { q_strlcat (cmd, Cmd_Argv(i), sizeof(cmd)); if (i != (c-1)) q_strlcat (cmd, " ", sizeof(cmd)); } Key_SetBinding (b, cmd); } /* ============ Key_WriteBindings Writes lines containing "bind key value" ============ */ void Key_WriteBindings (FILE *f) { int i; // unbindall before loading stored bindings: if (cfg_unbindall.integer) fprintf (f, "unbindall\n"); for (i = 0; i < 256; i++) { if (keybindings[i] && *keybindings[i]) fprintf (f, "bind \"%s\" \"%s\"\n", Key_KeynumToString(i), keybindings[i]); } } /* =================== Key_Init =================== */ void Key_Init (void) { int i; for (i = 0; i < 32; i++) { key_lines[i][0] = ']'; key_lines[i][1] = 0; } key_linepos = 1; memset (consolekeys, 0, sizeof(consolekeys)); memset (menubound, 0, sizeof(menubound)); memset (keyreserved, 0, sizeof(keyreserved)); // init ascii characters in console mode for (i = 32; i < 128; i++) consolekeys[i] = true; consolekeys[K_ENTER] = true; consolekeys[K_TAB] = true; consolekeys[K_LEFTARROW] = true; consolekeys[K_RIGHTARROW] = true; consolekeys[K_UPARROW] = true; consolekeys[K_DOWNARROW] = true; consolekeys[K_BACKSPACE] = true; consolekeys[K_DEL] = true; consolekeys[K_INS] = true; consolekeys[K_HOME] = true; consolekeys[K_END] = true; consolekeys[K_PGUP] = true; consolekeys[K_PGDN] = true; consolekeys[K_SHIFT] = true; consolekeys[K_MWHEELUP] = true; consolekeys[K_MWHEELDOWN] = true; consolekeys['`'] = false; consolekeys['~'] = false; for (i = 0; i < 256; i++) keyshift[i] = i; for (i = 'a'; i <= 'z'; i++) keyshift[i] = i - 'a' + 'A'; keyshift['1'] = '!'; keyshift['2'] = '@'; keyshift['3'] = '#'; keyshift['4'] = '$'; keyshift['5'] = '%'; keyshift['6'] = '^'; keyshift['7'] = '&'; keyshift['8'] = '*'; keyshift['9'] = '('; keyshift['0'] = ')'; keyshift['-'] = '_'; keyshift['='] = '+'; keyshift[','] = '<'; keyshift['.'] = '>'; keyshift['/'] = '?'; keyshift[';'] = ':'; keyshift['\''] = '"'; keyshift['['] = '{'; keyshift[']'] = '}'; keyshift['`'] = '~'; keyshift['\\'] = '|'; menubound[K_ESCAPE] = true; for (i = 0; i < 12; i++) menubound[K_F1+i] = true; memset (key_repeats, 0, sizeof(key_repeats)); // bind our reserved keys Key_SetBinding ('`', "toggleconsole"); Key_SetBinding ('~', "toggleconsole"); Key_SetBinding (K_PAUSE, "pause"); keyreserved['`'] = true; keyreserved['~'] = true; keyreserved[K_KP_NUMLOCK] = true; keyreserved[K_PAUSE] = true; // register our functions Cmd_AddCommand ("bind",Key_Bind_f); Cmd_AddCommand ("unbind",Key_Unbind_f); Cmd_AddCommand ("unbindall",Key_Unbindall_f); } /* =================== Key_Event Called by the system between frames for both key up and key down events Should NOT be called during an interrupt! =================== */ void Key_Event (int key, qboolean down) { char *kb; char cmd[1024]; keydown[key] = down; if (!down) key_repeats[key] = 0; key_lastpress = key; key_count++; if (key_count <= 0) return; // just catching keys for Con_NotifyBox // update auto-repeat status if (down) { /* Pause key doesn't generate a scancode when released, * never increment its auto-repeat status. */ if (key != K_PAUSE && key != K_KP_NUMLOCK) key_repeats[key]++; #if 0 if (key != K_BACKSPACE && key != K_PGUP && key != K_PGDN && key_repeats[key] > 1) return; // ignore most autorepeats #endif if (key_repeats[key] > 1) { if (key_dest == key_game && !con_forcedup) return; // ignore autorepeats in game mode } else if (key >= 200 && !keybindings[key]) Con_Printf ("%s is unbound, hit F4 to set.\n", Key_KeynumToString(key)); } if (key == K_SHIFT) shift_down = down; // handle escape specialy, so the user can never unbind it if (key == K_ESCAPE) { if (!down) return; switch (key_dest) { case key_message: Key_Message (key); break; case key_menu: M_Keydown (key); break; case key_menubind: M_Keybind (key); break; case key_game: case key_console: M_ToggleMenu_f (); break; default: Sys_Error ("Bad key_dest"); } return; } // key up events only generate commands if the game key binding is // a button command (leading + sign). These will occur even in console mode, // to keep the character from continuing an action started before a console // switch. Button commands include the kenum as a parameter, so multiple // downs can be matched with ups if (!down) { kb = keybindings[key]; if (kb && kb[0] == '+') { sprintf (cmd, "-%s %i\n", kb+1, key); Cbuf_AddText (cmd); } if (keyshift[key] != key) { kb = keybindings[keyshift[key]]; if (kb && kb[0] == '+') { sprintf (cmd, "-%s %i\n", kb+1, key); Cbuf_AddText (cmd); } } return; } // during demo playback, most keys bring up the main menu if (cls.demoplayback && down && consolekeys[key] && key_dest == key_game) { M_ToggleMenu_f (); return; } if (cl.intermission == 12 && down) { Cbuf_AddText ("map keep1\n"); } // if not a consolekey, send to the interpreter no matter what mode is if (((key_dest & key_menu) && menubound[key]) || (key_dest == key_console && !consolekeys[key]) || (key_dest == key_game && (!con_forcedup || !consolekeys[key]))) { kb = keybindings[key]; if (kb) { if (kb[0] == '+') { // button commands add keynum as a parm sprintf (cmd, "%s %i\n", kb, key); Cbuf_AddText (cmd); } else { Cbuf_AddText (kb); Cbuf_AddText ("\n"); } } return; } if (!down) return; // other systems only care about key down events if (shift_down) key = keyshift[key]; switch (key_dest) { case key_message: Key_Message (key); break; case key_menu: M_Keydown (key); break; case key_menubind: M_Keybind (key); break; case key_game: case key_console: Key_Console (key); break; default: Sys_Error ("Bad key_dest"); } } /* =================== Key_ClearStates =================== */ void Key_ClearStates (void) { int i; for (i = 0; i < 256; i++) { if (keydown[i]) Key_Event (i, false); } } qboolean Key_IsGameKey (void) { return ((key_dest == key_game && !con_forcedup) || (key_dest == key_menubind)); } keydest_t Key_GetDest (void) { return key_dest; } void Key_SetDest (keydest_t dest) { key_dest = dest; if ((key_gamekey = Key_IsGameKey()) != prev_gamekey) { prev_gamekey = key_gamekey; Key_ClearStates(); } } engine/hexen2/menu.c000066400000000000000000003434551444734033100146450ustar00rootroot00000000000000/* * menu.c * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "bgmusic.h" #include "cdaudio.h" #include "r_shared.h" void (*vid_menudrawfn)(void); void (*vid_menukeyfn)(int key); enum m_state_e m_state; void M_Menu_Main_f (void); static void M_Menu_SinglePlayer_f (void); static void M_Menu_Load_f (void); static void M_Menu_Save_f (void); static void M_Menu_MultiPlayer_f (void); static void M_Menu_Setup_f (void); static void M_Menu_Net_f (void); void M_Menu_Options_f (void); static void M_Menu_Keys_f (void); static void M_Menu_Video_f (void); static void M_Menu_Help_f (void); void M_Menu_Quit_f (void); static void M_Menu_LanConfig_f (void); static void M_Menu_GameOptions_f (void); static void M_Menu_Search_f (void); static void M_Menu_ServerList_f (void); static void M_Main_Draw (void); static void M_SinglePlayer_Draw (void); static void M_Load_Draw (void); static void M_Save_Draw (void); static void M_MultiPlayer_Draw (void); static void M_Setup_Draw (void); static void M_Net_Draw (void); static void M_Options_Draw (void); static void M_Keys_Draw (void); static void M_Video_Draw (void); static void M_Help_Draw (void); static void M_Quit_Draw (void); static void M_LanConfig_Draw (void); static void M_GameOptions_Draw (void); static void M_Search_Draw (void); static void M_ServerList_Draw (void); static void M_Main_Key (int key); static void M_SinglePlayer_Key (int key); static void M_Load_Key (int key); static void M_Save_Key (int key); static void M_MultiPlayer_Key (int key); static void M_Setup_Key (int key); static void M_Net_Key (int key); static void M_Options_Key (int key); static void M_Keys_Key (int key); static void M_Video_Key (int key); static void M_Help_Key (int key); static void M_Quit_Key (int key); static void M_LanConfig_Key (int key); static void M_GameOptions_Key (int key); static void M_Search_Key (int key); static void M_ServerList_Key (int key); #if defined(NET_USE_SERIAL) static void M_Menu_SerialConfig_f (void); static void M_Menu_ModemConfig_f (void); static void M_SerialConfig_Draw (void); static void M_ModemConfig_Draw (void); static void M_SerialConfig_Key (int key); static void M_ModemConfig_Key (int key); #endif /* NET_USE_SERIAL */ static qboolean m_entersound; // play after drawing a frame, so caching // won't disrupt the sound static qboolean m_recursiveDraw; enum m_state_e m_return_state; qboolean m_return_onerror; char m_return_reason [32]; qboolean menu_disabled_mouse = false; static float TitlePercent = 0; static float TitleTargetPercent = 1; static float LogoPercent = 0; static float LogoTargetPercent = 1; static int setup_class; static const char *msave_message, *msave_message2; static double message_time; static void M_ConfigureNetSubsystem(void); #define StartingGame (m_multiplayer_cursor == 1) #define JoiningGame (m_multiplayer_cursor == 0) #if !defined(NET_USE_SERIAL) #define _nums_serial 0 #define _ser_draw_offset 8 /* incr. the Y offset this much pixels */ #else #define _item_net_ser 0 /* order of serial menu entry */ #define _item_net_dc 1 /* order of direct connect menu entry */ #define _nums_serial 2 #define _ser_draw_offset 0 #define SerialConfig (m_net_cursor == _item_net_ser) #define DirectConfig (m_net_cursor == _item_net_dc) #endif #define _item_net_ipx (0 + _nums_serial) /* order of IPX menu entry */ #define _item_net_tcp (1 + _nums_serial) /* order of TCP menu entry */ #define IPXConfig (m_net_cursor == _item_net_ipx) #define TCPIPConfig (m_net_cursor == _item_net_tcp) static void M_Menu_Class_f (void); const char *ClassNames[MAX_PLAYER_CLASS] = { "Paladin", "Crusader", "Necromancer", "Assassin", "Demoness" }; static const char *ClassNamesU[MAX_PLAYER_CLASS] = { "PALADIN", "CRUSADER", "NECROMANCER", "ASSASSIN", "DEMONESS" }; #define NUM_DIFFLEVELS 4 static const char *DiffNames[MAX_PLAYER_CLASS][NUM_DIFFLEVELS] = { { // Paladin "APPRENTICE", "SQUIRE", "ADEPT", "LORD" }, { // Crusader "GALLANT", "HOLY AVENGER", "DIVINE HERO", "LEGEND" }, { // Necromancer "SORCERER", "DARK SERVANT", "WARLOCK", "LICH KING" }, { // Assassin "ROGUE", "CUTTHROAT", "EXECUTIONER", "WIDOW MAKER" }, { // Demoness "LARVA", "SPAWN", "FIEND", "SHE BITCH" } }; //============================================================================= /* Support Routines */ /* ================ M_DrawCharacter Draws one solid graphics character, centered, on line ================ */ void M_DrawCharacter (int cx, int line, int num) { Draw_Character ( cx + ((vid.width - 320)>>1), line, num); } void M_Print (int cx, int cy, const char *str) { while (*str) { M_DrawCharacter (cx, cy, ((unsigned char)(*str))+256); str++; cx += 8; } } void M_PrintWhite (int cx, int cy, const char *str) { while (*str) { M_DrawCharacter (cx, cy, (unsigned char)*str); str++; cx += 8; } } void M_DrawTransPic (int x, int y, qpic_t *pic) { Draw_TransPic (x + ((vid.width - 320)>>1), y, pic); } void M_DrawPic (int x, int y, qpic_t *pic) { Draw_Pic (x + ((vid.width - 320)>>1), y, pic); } static void M_DrawTransPicCropped (int x, int y, qpic_t *pic) { Draw_TransPicCropped (x + ((vid.width - 320)>>1), y, pic); } static byte identityTable[256]; static byte translationTable[256]; static void M_BuildTranslationTable(int top, int bottom) { int j; byte *dest, *source, *sourceA, *sourceB, *colorA, *colorB; for (j = 0; j < 256; j++) identityTable[j] = j; dest = translationTable; source = identityTable; memcpy (dest, source, 256); if (top > 10) top = 0; if (bottom > 10) bottom = 0; top -= 1; bottom -= 1; colorA = playerTranslation + 256 + color_offsets[(int)setup_class-1]; colorB = colorA + 256; sourceA = colorB + 256 + (top * 256); sourceB = colorB + 256 + (bottom * 256); for (j = 0; j < 256; j++, colorA++, colorB++, sourceA++, sourceB++) { if (top >= 0 && (*colorA != 255)) dest[j] = source[*sourceA]; if (bottom >= 0 && (*colorB != 255)) dest[j] = source[*sourceB]; } } void M_DrawTextBox (int x, int y, int width, int lines) { qpic_t *p, *tm, *bm; int cx, cy; int n; // draw left side cx = x; cy = y; p = Draw_CachePic ("gfx/box_tl.lmp"); M_DrawTransPic (cx, cy, p); p = Draw_CachePic ("gfx/box_ml.lmp"); for (n = 0; n < lines; n++) { cy += 8; M_DrawTransPic (cx, cy, p); } p = Draw_CachePic ("gfx/box_bl.lmp"); M_DrawTransPic (cx, cy+8, p); // draw middle cx += 8; tm = Draw_CachePic ("gfx/box_tm.lmp"); bm = Draw_CachePic ("gfx/box_bm.lmp"); while (width > 0) { cy = y; M_DrawTransPic (cx, cy, tm); p = Draw_CachePic ("gfx/box_mm.lmp"); for (n = 0; n < lines; n++) { cy += 8; if (n == 1) p = Draw_CachePic ("gfx/box_mm2.lmp"); M_DrawTransPic (cx, cy, p); } M_DrawTransPic (cx, cy+8, bm); width -= 2; cx += 16; } // draw right side cy = y; p = Draw_CachePic ("gfx/box_tr.lmp"); M_DrawTransPic (cx, cy, p); p = Draw_CachePic ("gfx/box_mr.lmp"); for (n = 0; n < lines; n++) { cy += 8; M_DrawTransPic (cx, cy, p); } p = Draw_CachePic ("gfx/box_br.lmp"); M_DrawTransPic (cx, cy+8, p); } //============================================================================= static int m_save_demonum; /* ================ M_ToggleMenu_f ================ */ void M_ToggleMenu_f (void) { keydest_t dest = Key_GetDest(); m_entersound = true; if (dest & key_menu) { if (m_state != m_main) { LogoTargetPercent = TitleTargetPercent = 1; LogoPercent = TitlePercent = 0; M_Menu_Main_f (); return; } Key_SetDest (key_game); m_state = m_none; Sbar_Changed (); return; } if (dest == key_console) { Con_ToggleConsole_f (); } else { LogoTargetPercent = TitleTargetPercent = 1; LogoPercent = TitlePercent = 0; M_Menu_Main_f (); } } // Note: old version of demo has bigfont.lmp, not bigfont2.lmp #define BIGCHAR_FONT_FILE0 "gfx/menu/bigfont.lmp" #define BIGCHAR_FONT_FILE "gfx/menu/bigfont2.lmp" #define BIGCHAR_WIDTH_FILE "gfx/menu/fontsize.lmp" static char BigCharWidth[27][27]; static void M_BuildBigCharWidth (void) { qpic_t *p; byte *source; int ypos, xpos; int numA, numB; int biggestX, adjustment; char After[20], Before[20]; p = (qpic_t *)FS_LoadTempFile (BIGCHAR_FONT_FILE, NULL); if (!p) p = (qpic_t *)FS_LoadTempFile (BIGCHAR_FONT_FILE0, NULL); if (!p) Sys_Error ("Failed to load %s", BIGCHAR_FONT_FILE); SwapPic(p); for (numA = 0; numA < 27; numA++) { memset (After, 20, sizeof(After)); source = p->data + ((numA % 8) * 20) + (numA / 8 * p->width * 20); biggestX = 0; for (ypos = 0; ypos < 19; ypos++) { for (xpos = 0; xpos < 19; xpos++, source++) { if (*source) { After[ypos] = xpos; if (xpos > biggestX) biggestX = xpos; } } source += (p->width - 19); } biggestX++; for (numB = 0; numB < 27; numB++) { memset (Before, 0, sizeof(Before)); source = p->data + ((numB % 8) * 20) + (numB / 8 * p->width * 20); adjustment = 0; for (ypos = 0; ypos < 19; ypos++) { for (xpos = 0; xpos < 19; xpos++, source++) { if (!(*source)) { Before[ypos]++; } else break; } source += (p->width - xpos); } while (1) { for (ypos = 0; ypos < 19; ypos++) { if (After[ypos] - Before[ypos] >= 15) break; Before[ypos]--; } if (ypos < 19) break; adjustment--; } BigCharWidth[numA][numB] = adjustment + biggestX; } } FS_CreatePath(FS_MakePath(FS_USERDIR, NULL, BIGCHAR_WIDTH_FILE)); FS_WriteFile (BIGCHAR_WIDTH_FILE, BigCharWidth, sizeof(BigCharWidth)); } static int M_DrawBigCharacter (int x, int y, int num, int numNext) { int add; if (num == ' ') return 32; if (num == '/') num = 26; else num -= 65; if (num < 0 || num >= 27) // only a-z and / return 0; if (numNext == '/') numNext = 26; else numNext -= 65; Draw_BigCharacter (x, y, num); if (numNext < 0 || numNext >= 27) return 0; add = 0; if (num == (int)'C'-65 && numNext == (int)'P'-65) add = 3; return BigCharWidth[num][numNext] + add; } static void M_DrawBigString(int x, int y, const char *string) { x += ((vid.width - 320)>>1); while (*string) { x += M_DrawBigCharacter(x, y, string[0], string[1]); ++string; } } void ScrollTitle (const char *name) { qpic_t *p; float delta; int finaly; static const char *LastName = ""; static qboolean CanSwitch = true; if (TitlePercent < TitleTargetPercent) { delta = ((TitleTargetPercent-TitlePercent)/0.5)*host_frametime; if (delta < 0.004) delta = 0.004; TitlePercent += delta; if (TitlePercent > TitleTargetPercent) { TitlePercent = TitleTargetPercent; } } else if (TitlePercent > TitleTargetPercent) { delta = ((TitlePercent-TitleTargetPercent)/0.15)*host_frametime; if (delta < 0.02) delta = 0.02; TitlePercent -= delta; if (TitlePercent <= TitleTargetPercent) { TitlePercent = TitleTargetPercent; CanSwitch = true; } } if (LogoPercent < LogoTargetPercent) { /* delta = ((LogoTargetPercent-LogoPercent)/1.1)*host_frametime; if (delta < 0.0015) delta = 0.0015; */ delta = ((LogoTargetPercent-LogoPercent)/.15)*host_frametime; if (delta < 0.02) delta = 0.02; LogoPercent += delta; if (LogoPercent > LogoTargetPercent) LogoPercent = LogoTargetPercent; } if (q_strcasecmp(LastName,name) != 0 && TitleTargetPercent != 0) TitleTargetPercent = 0; if (CanSwitch) { LastName = name; CanSwitch = false; TitleTargetPercent = 1; } p = Draw_CachePic(LastName); finaly = ((float)p->height * TitlePercent) - p->height; M_DrawTransPicCropped( (320-p->width)/2, finaly , p); if (m_state != m_keys) { p = Draw_CachePic("gfx/menu/hplaque.lmp"); finaly = ((float)p->height * LogoPercent) - p->height; M_DrawTransPicCropped(10, finaly, p); } } //============================================================================= /* MAIN MENU */ static int m_main_cursor; #define MAIN_ITEMS 5 static void BGM_RestartMusic(void); static char old_bgmtype[20]; // S.A void M_Menu_Main_f (void) { // Deactivate the mouse when the menus are drawn - S.A. menu_disabled_mouse = true; if (modestate == MS_WINDOWED) IN_DeactivateMouse (); if (!(Key_GetDest() & key_menu)) { m_save_demonum = cls.demonum; cls.demonum = -1; } Key_SetDest (key_menu); m_state = m_main; m_entersound = true; } static void M_Main_Draw (void) { int f; ScrollTitle("gfx/menu/title0.lmp"); M_DrawBigString (72, 60 + (0 * 20), "SINGLE PLAYER"); M_DrawBigString (72, 60 + (1 * 20), "MULTIPLAYER"); M_DrawBigString (72, 60 + (2 * 20), "OPTIONS"); M_DrawBigString (72, 60 + (3 * 20), "HELP"); M_DrawBigString (72, 60 + (4 * 20), "QUIT"); f = (int)(realtime * 10)%8; M_DrawTransPic (43, 54 + m_main_cursor * 20,Draw_CachePic( va("gfx/menu/menudot%i.lmp", f+1 ) ) ); } static void M_Main_Key (int key) { switch (key) { case K_ESCAPE: // leaving the main menu, reactivate mouse - S.A. menu_disabled_mouse = false; IN_ActivateMouse (); // and check we haven't changed the music type if (old_bgmtype[0] != 0 && strcmp(old_bgmtype,bgmtype.string) != 0) BGM_RestartMusic (); old_bgmtype[0] = 0; Key_SetDest (key_game); m_state = m_none; Sbar_Changed (); cls.demonum = m_save_demonum; if (cls.demonum != -1 && !cls.demoplayback && cls.state != ca_connected) CL_NextDemo (); break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); if (++m_main_cursor >= MAIN_ITEMS) m_main_cursor = 0; break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); if (--m_main_cursor < 0) m_main_cursor = MAIN_ITEMS - 1; break; case K_ENTER: m_entersound = true; switch (m_main_cursor) { case 0: M_Menu_SinglePlayer_f (); break; case 1: M_Menu_MultiPlayer_f (); break; case 2: M_Menu_Options_f (); break; case 3: M_Menu_Help_f (); break; case 4: M_Menu_Quit_f (); break; } } } //============================================================================= /* DIFFICULTY MENU */ static void M_Menu_Difficulty_f (void) { Key_SetDest (key_menu); m_state = m_difficulty; } static int m_diff_cursor; static int m_enter_portals = 0; #define DIFF_ITEMS NUM_DIFFLEVELS static void M_Difficulty_Draw (void) { int f, i; ScrollTitle("gfx/menu/title5.lmp"); setup_class = cl_playerclass.integer; if (setup_class < 1 || setup_class > MAX_PLAYER_CLASS) setup_class = MAX_PLAYER_CLASS; if (setup_class > MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES && !(gameflags & GAME_PORTALS)) setup_class = MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES; setup_class--; for (i = 0; i < NUM_DIFFLEVELS; ++i) M_DrawBigString (72, 60 + (i * 20), DiffNames[setup_class][i]); f = (int)(realtime * 10)%8; M_DrawTransPic (43, 54 + m_diff_cursor * 20, Draw_CachePic(va("gfx/menu/menudot%i.lmp", f+1)) ); } static void M_NewMissionPackGame (void) { /* running a new single player mission pack game through * the menu system starts intermission screen #12, first. * when the user hits a key, Key_Event () gets us out of * the intermission by running the keep1 map. */ Key_SetDest (key_game); cls.demonum = m_save_demonum; CL_SetupIntermission (12); /* make sure the mouse is active, so that pressing a mouse * button can be captured by Key_Event (see above.) */ menu_disabled_mouse = false; IN_ActivateMouse (); } static void M_Difficulty_Key (int key) { switch (key) { case K_LEFTARROW: case K_RIGHTARROW: break; case K_ESCAPE: M_Menu_Class_f (); break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); if (++m_diff_cursor >= DIFF_ITEMS) m_diff_cursor = 0; break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); if (--m_diff_cursor < 0) m_diff_cursor = DIFF_ITEMS - 1; break; case K_ENTER: Cvar_SetValue ("skill", m_diff_cursor); m_entersound = true; m_state = m_none; if (m_enter_portals) { M_NewMissionPackGame (); return; } Cbuf_AddText ("wait\n"); /* make m_none to really work */ Cbuf_AddText ("map demo1\n"); break; default: Key_SetDest (key_game); m_state = m_none; break; } } //============================================================================= /* CLASS CHOICE MENU */ static int class_flag; static void M_Menu_Class_f (void) { class_flag = 0; Key_SetDest (key_menu); m_state = m_class; } static void M_Menu_Class2_f (void) { Key_SetDest (key_menu); m_state = m_class; class_flag = 1; } static int m_class_cursor; #define CLASS_ITEMS MAX_PLAYER_CLASS static void M_Class_Draw (void) { int i, f = MAX_PLAYER_CLASS; if (! (gameflags & GAME_PORTALS)) f = MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES; #if DISALLOW_DEMONESS_IN_OLD_GAME else if (!m_enter_portals) f = MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES; #endif if (m_class_cursor >= f) m_class_cursor = 0; ScrollTitle("gfx/menu/title2.lmp"); for (i = 0; i < f; ++i) M_DrawBigString (72, 60 + (i * 20), ClassNamesU[i]); f = (int)(realtime * 10)%8; M_DrawTransPic (43, 54 + m_class_cursor * 20, Draw_CachePic(va("gfx/menu/menudot%i.lmp", f+1)) ); M_DrawPic (251, 54 + 21, Draw_CachePic (va("gfx/cport%d.lmp", m_class_cursor + 1))); M_DrawTransPic (242, 54, Draw_CachePic ("gfx/menu/frame.lmp")); } static void M_Class_Key (int key) { int f = MAX_PLAYER_CLASS; if (! (gameflags & GAME_PORTALS)) f = MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES; #if DISALLOW_DEMONESS_IN_OLD_GAME else if (!m_enter_portals) f = MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES; #endif switch (key) { case K_LEFTARROW: case K_RIGHTARROW: break; case K_ESCAPE: M_Menu_SinglePlayer_f (); break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); #if ENABLE_OLD_DEMO if (gameflags & GAME_OLD_DEMO) m_class_cursor = (m_class_cursor == CLASS_PALADIN-1) ? CLASS_THEIF-1 : CLASS_PALADIN-1; else #endif /* OLD_DEMO */ if (++m_class_cursor >= f) m_class_cursor = 0; break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); #if ENABLE_OLD_DEMO if (gameflags & GAME_OLD_DEMO) m_class_cursor = (m_class_cursor == CLASS_PALADIN-1) ? CLASS_THEIF-1 : CLASS_PALADIN-1; else #endif /* OLD_DEMO */ if (--m_class_cursor < 0) m_class_cursor = f - 1; break; case K_ENTER: Cbuf_AddText ( va ("playerclass %d\n", m_class_cursor+1) ); m_entersound = true; if (!class_flag) { M_Menu_Difficulty_f(); } else { Key_SetDest (key_game); m_state = m_none; } break; default: Key_SetDest (key_game); m_state = m_none; break; } } //============================================================================= /* SINGLE PLAYER MENU */ #define SINGLEPLAYER_ITEMS 3 #define SP_PORTALS_ITEMS 2 static int m_singleplayer_cursor; static void M_Menu_SinglePlayer_f (void) { Key_SetDest (key_menu); m_state = m_singleplayer; m_entersound = true; Cvar_Set ("timelimit", "0"); //put this here to help play single after dm } static void M_SinglePlayer_Draw (void) { int f; ScrollTitle("gfx/menu/title1.lmp"); if (gameflags & GAME_PORTALS) M_DrawBigString (72, 60 + (0 * 20), "NEW MISSION"); else M_DrawBigString (72, 60 + (0 * 20), "NEW GAME"); M_DrawBigString (72, 60 + (1 * 20), "LOAD"); M_DrawBigString (72, 60 + (2 * 20), "SAVE"); if (gameflags & GAME_PORTALS) { M_DrawBigString (72, 60 + (3 * 20), "OLD MISSION"); M_DrawBigString (72, 60 + (4 * 20), "VIEW INTRO"); } f = (int)(realtime * 10)%8; M_DrawTransPic (43, 54 + m_singleplayer_cursor * 20, Draw_CachePic(va("gfx/menu/menudot%i.lmp", f+1)) ); } static void M_SinglePlayer_Key (int key) { switch (key) { case K_ESCAPE: M_Menu_Main_f (); break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); m_singleplayer_cursor++; if (gameflags & GAME_PORTALS) { if (m_singleplayer_cursor >= SINGLEPLAYER_ITEMS + SP_PORTALS_ITEMS) m_singleplayer_cursor = 0; } else { if (m_singleplayer_cursor >= SINGLEPLAYER_ITEMS) m_singleplayer_cursor = 0; } break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); if (--m_singleplayer_cursor < 0) { if (gameflags & GAME_PORTALS) m_singleplayer_cursor = SINGLEPLAYER_ITEMS + SP_PORTALS_ITEMS - 1; else m_singleplayer_cursor = SINGLEPLAYER_ITEMS - 1; } break; case K_ENTER: m_entersound = true; m_enter_portals = 0; switch (m_singleplayer_cursor) { case 0: if (gameflags & GAME_PORTALS) m_enter_portals = 1; case 3: if (sv.active) if (!SCR_ModalMessage("Are you sure you want to\nstart a new game?\n")) break; Key_SetDest (key_game); if (sv.active) Cbuf_AddText ("disconnect\n"); Host_RemoveGIPFiles(NULL); Cbuf_AddText ("maxplayers 1\n"); Cbuf_AddText ("coop 0\n"); Cbuf_AddText ("deathmatch 0\n"); M_Menu_Class_f (); break; case 1: M_Menu_Load_f (); break; case 2: M_Menu_Save_f (); break; case 4: if (gameflags & GAME_PORTALS) { Key_SetDest (key_game); Cbuf_AddText("playdemo t9\n"); } break; } } } //============================================================================= /* LOAD/SAVE MENU */ static int load_cursor; // 0 < load_cursor < MAX_SAVEGAMES static char m_filenames[MAX_SAVEGAMES][SAVEGAME_COMMENT_LENGTH+1]; static char savefile[MAX_OSPATH]; static int loadable[MAX_SAVEGAMES]; static void M_ScanSaves (void) { int i, j, version; char name[MAX_OSPATH]; FILE *f; for (i = 0; i < MAX_SAVEGAMES; i++) { q_strlcpy (m_filenames[i], "--- UNUSED SLOT ---", SAVEGAME_COMMENT_LENGTH+1); loadable[i] = false; FS_MakePath_VABUF (FS_USERDIR, NULL, name, sizeof(name), "s%i/info.dat", i); f = fopen (name, "r"); if (!f) continue; fscanf (f, "%i\n", &version); if (version != SAVEGAME_VERSION) { fclose (f); continue; } fscanf (f, "%79s\n", name); q_strlcpy (m_filenames[i], name, SAVEGAME_COMMENT_LENGTH+1); // change _ back to space for (j = 0; j < SAVEGAME_COMMENT_LENGTH; j++) { if (m_filenames[i][j] == '_') m_filenames[i][j] = ' '; } loadable[i] = true; fclose (f); } } static void M_Menu_Load_f (void) { m_entersound = true; m_state = m_load; Key_SetDest (key_menu); M_ScanSaves (); } static void M_Menu_Save_f (void) { if (!sv.active) return; if (cl.intermission) return; if (svs.maxclients != 1) return; m_entersound = true; m_state = m_save; Key_SetDest (key_menu); M_ScanSaves (); } static void M_Load_Draw (void) { int i; ScrollTitle("gfx/menu/load.lmp"); for (i = 0; i < MAX_SAVEGAMES; i++) M_Print (16, 60 + 8*i, m_filenames[i]); // line cursor M_DrawCharacter (8, 60 + load_cursor*8, 12+((int)(realtime*4)&1)); } static void M_Save_Draw (void) { int i; ScrollTitle("gfx/menu/save.lmp"); for (i = 0; i < MAX_SAVEGAMES; i++) M_Print (16, 60 + 8*i, m_filenames[i]); // line cursor M_DrawCharacter (8, 60 + load_cursor*8, 12+((int)(realtime*4)&1)); } static void M_Load_Key (int k) { switch (k) { case K_ESCAPE: M_Menu_SinglePlayer_f (); break; case K_DEL: S_LocalSound ("raven/menu2.wav"); if (!loadable[load_cursor]) return; if (!SCR_ModalMessage("Are you sure you want to\ndelete this saved game?\n")) return; FS_MakePath_VABUF (FS_USERDIR, NULL, savefile, sizeof(savefile), "s%i", load_cursor); Host_DeleteSave (savefile); M_ScanSaves (); break; case K_ENTER: S_LocalSound ("raven/menu2.wav"); if (!loadable[load_cursor]) return; m_state = m_none; Sbar_Changed (); Key_SetDest (key_game); // Host_Loadgame_f can't bring up the loading plaque because too much // stack space has been used, so do it now SCR_BeginLoadingPlaque (); // issue the load command Cbuf_AddText (va ("load s%i\n", load_cursor) ); return; case K_UPARROW: case K_LEFTARROW: S_LocalSound ("raven/menu1.wav"); load_cursor--; if (load_cursor < 0) load_cursor = MAX_SAVEGAMES-1; break; case K_DOWNARROW: case K_RIGHTARROW: S_LocalSound ("raven/menu1.wav"); load_cursor++; if (load_cursor >= MAX_SAVEGAMES) load_cursor = 0; break; } } static void M_Save_Key (int k) { switch (k) { case K_ESCAPE: M_Menu_SinglePlayer_f (); break; case K_DEL: S_LocalSound ("raven/menu2.wav"); if (!loadable[load_cursor]) return; if (!SCR_ModalMessage("Are you sure you want to\ndelete this saved game?\n")) return; FS_MakePath_VABUF (FS_USERDIR, NULL, savefile, sizeof(savefile), "s%i", load_cursor); Host_DeleteSave (savefile); M_ScanSaves (); break; case K_ENTER: m_state = m_none; Sbar_Changed (); Key_SetDest (key_game); Cbuf_AddText (va("save s%i\n", load_cursor)); menu_disabled_mouse = false; IN_ActivateMouse (); return; case K_UPARROW: case K_LEFTARROW: S_LocalSound ("raven/menu1.wav"); load_cursor--; if (load_cursor < 0) load_cursor = MAX_SAVEGAMES-1; break; case K_DOWNARROW: case K_RIGHTARROW: S_LocalSound ("raven/menu1.wav"); load_cursor++; if (load_cursor >= MAX_SAVEGAMES) load_cursor = 0; break; } } //============================================================================= /* MULTIPLAYER LOAD/SAVE MENU */ static void M_ScanMSaves (void) { int i, j, version; char name[MAX_OSPATH]; FILE *f; for (i = 0; i < MAX_SAVEGAMES; i++) { q_strlcpy (m_filenames[i], "--- UNUSED SLOT ---", SAVEGAME_COMMENT_LENGTH+1); loadable[i] = false; FS_MakePath_VABUF (FS_USERDIR, NULL, name, sizeof(name), "ms%i/info.dat", i); f = fopen (name, "r"); if (!f) continue; fscanf (f, "%i\n", &version); if (version != SAVEGAME_VERSION) { fclose (f); continue; } fscanf (f, "%79s\n", name); q_strlcpy (m_filenames[i], name, SAVEGAME_COMMENT_LENGTH+1); // change _ back to space for (j = 0; j < SAVEGAME_COMMENT_LENGTH; j++) { if (m_filenames[i][j] == '_') m_filenames[i][j] = ' '; } loadable[i] = true; fclose (f); } } static void M_Menu_MLoad_f (void) { m_entersound = true; m_state = m_mload; Key_SetDest (key_menu); M_ScanMSaves (); } static void M_Menu_MSave_f (void) { if (!sv.active || cl.intermission || svs.maxclients == 1) { msave_message = "Only a network server"; msave_message2 = "can save a multiplayer game"; message_time = realtime; return; } m_entersound = true; m_state = m_msave; Key_SetDest (key_menu); M_ScanMSaves (); } static void M_MLoad_Key (int k) { switch (k) { case K_ESCAPE: M_Menu_MultiPlayer_f (); break; case K_DEL: S_LocalSound ("raven/menu2.wav"); if (!loadable[load_cursor]) return; if (!SCR_ModalMessage("Are you sure you want to\ndelete this saved game?\n")) return; FS_MakePath_VABUF (FS_USERDIR, NULL, savefile, sizeof(savefile), "ms%i", load_cursor); Host_DeleteSave (savefile); M_ScanMSaves (); break; case K_ENTER: S_LocalSound ("raven/menu2.wav"); if (!loadable[load_cursor]) return; m_state = m_none; Sbar_Changed (); Key_SetDest (key_game); if (sv.active) Cbuf_AddText ("disconnect\n"); Cbuf_AddText ("listen 1\n"); // so host_netport will be re-examined // Host_Loadgame_f can't bring up the loading plaque because too much // stack space has been used, so do it now SCR_BeginLoadingPlaque (); // issue the load command Cbuf_AddText (va ("load ms%i\n", load_cursor) ); return; case K_UPARROW: case K_LEFTARROW: S_LocalSound ("raven/menu1.wav"); load_cursor--; if (load_cursor < 0) load_cursor = MAX_SAVEGAMES-1; break; case K_DOWNARROW: case K_RIGHTARROW: S_LocalSound ("raven/menu1.wav"); load_cursor++; if (load_cursor >= MAX_SAVEGAMES) load_cursor = 0; break; } } static void M_MSave_Key (int k) { switch (k) { case K_ESCAPE: M_Menu_MultiPlayer_f (); break; case K_DEL: S_LocalSound ("raven/menu2.wav"); if (!loadable[load_cursor]) return; if (!SCR_ModalMessage("Are you sure you want to\ndelete this saved game?\n")) return; FS_MakePath_VABUF (FS_USERDIR, NULL, savefile, sizeof(savefile), "ms%i", load_cursor); Host_DeleteSave (savefile); M_ScanMSaves (); break; case K_ENTER: m_state = m_none; Sbar_Changed (); Key_SetDest (key_game); Cbuf_AddText (va("save ms%i\n", load_cursor)); menu_disabled_mouse = false; IN_ActivateMouse (); return; case K_UPARROW: case K_LEFTARROW: S_LocalSound ("raven/menu1.wav"); load_cursor--; if (load_cursor < 0) load_cursor = MAX_SAVEGAMES-1; break; case K_DOWNARROW: case K_RIGHTARROW: S_LocalSound ("raven/menu1.wav"); load_cursor++; if (load_cursor >= MAX_SAVEGAMES) load_cursor = 0; break; } } //============================================================================= /* MULTIPLAYER MENU */ static int m_multiplayer_cursor; #define MULTIPLAYER_ITEMS 5 static void M_Menu_MultiPlayer_f (void) { Key_SetDest (key_menu); m_state = m_multiplayer; m_entersound = true; msave_message = NULL; } static void M_MultiPlayer_Draw (void) { int f; ScrollTitle("gfx/menu/title4.lmp"); // M_DrawTransPic (72, 32, Draw_CachePic ("gfx/mp_menu.lmp") ); M_DrawBigString (72, 60 + (0 * 20), "JOIN A GAME"); M_DrawBigString (72, 60 + (1 * 20), "NEW GAME"); M_DrawBigString (72, 60 + (2 * 20), "SETUP"); M_DrawBigString (72, 60 + (3 * 20), "LOAD"); M_DrawBigString (72, 60 + (4 * 20), "SAVE"); f = (int)(realtime * 10)%8; M_DrawTransPic (43, 54 + m_multiplayer_cursor * 20,Draw_CachePic( va("gfx/menu/menudot%i.lmp", f+1 ) ) ); if (msave_message) { M_PrintWhite ((320/2) - ((27*8)/2), 168, msave_message); M_PrintWhite ((320/2) - ((27*8)/2), 176, msave_message2); if (realtime - 5 > message_time) msave_message = NULL; } if (serialAvailable || ipxAvailable || tcpipAvailable) return; M_PrintWhite ((320/2) - ((27*8)/2), 160, "No Communications Available"); } static void M_MultiPlayer_Key (int key) { switch (key) { case K_ESCAPE: M_Menu_Main_f (); break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); if (++m_multiplayer_cursor >= MULTIPLAYER_ITEMS) m_multiplayer_cursor = 0; break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); if (--m_multiplayer_cursor < 0) m_multiplayer_cursor = MULTIPLAYER_ITEMS - 1; break; case K_ENTER: m_entersound = true; switch (m_multiplayer_cursor) { case 0: if (serialAvailable || ipxAvailable || tcpipAvailable) M_Menu_Net_f (); break; case 1: if (serialAvailable || ipxAvailable || tcpipAvailable) M_Menu_Net_f (); break; case 2: M_Menu_Setup_f (); break; case 3: M_Menu_MLoad_f (); break; case 4: M_Menu_MSave_f (); break; } } } //============================================================================= /* SETUP MENU */ static int setup_cursor = 5; static const int setup_cursor_table[] = {40, 56, 80, 104, 128, 156}; static char setup_hostname[16]; static char setup_myname[16]; static int setup_oldtop; static int setup_oldbottom; static int setup_top; static int setup_bottom; #define NUM_SETUP_CMDS 6 static void M_Menu_Setup_f (void) { Key_SetDest (key_menu); m_state = m_setup; m_entersound = true; q_strlcpy(setup_myname, cl_name.string, sizeof(setup_myname)); q_strlcpy(setup_hostname, hostname.string, sizeof(setup_hostname)); setup_top = setup_oldtop = (cl_color.integer >> 4) & 15; setup_bottom = setup_oldbottom = cl_color.integer & 15; setup_class = cl_playerclass.integer; if (setup_class < 1 || setup_class > MAX_PLAYER_CLASS) setup_class = MAX_PLAYER_CLASS; #if ENABLE_OLD_DEMO if (gameflags & GAME_OLD_DEMO) { if (setup_class != CLASS_PALADIN && setup_class != CLASS_THEIF) setup_class = CLASS_PALADIN; } else #endif /* OLD_DEMO */ if (!(gameflags & GAME_PORTALS)) { if (setup_class > MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES) setup_class = MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES; } } static void M_DrawTransPicTranslate (int x, int y, qpic_t *pic, int p_class) { Draw_TransPicTranslate (x + ((vid.width - 320)>>1), y, pic, translationTable, p_class); } static void M_Setup_Draw (void) { qpic_t *p; ScrollTitle("gfx/menu/title4.lmp"); M_Print (64, 40, "Hostname"); M_DrawTextBox (160, 32, 16, 1); M_Print (168, 40, setup_hostname); M_Print (64, 56, "Your name"); M_DrawTextBox (160, 48, 16, 1); M_Print (168, 56, setup_myname); M_Print (64, 80, "Current Class: "); M_Print (88, 88, ClassNames[setup_class-1]); M_Print (64, 104, "First color patch"); M_Print (64, 128, "Second color patch"); M_DrawTextBox (64, 156-8, 14, 1); M_Print (72, 156, "Accept Changes"); p = Draw_CachePic (va("gfx/menu/netp%i.lmp",setup_class)); M_BuildTranslationTable(setup_top, setup_bottom); /* garymct */ M_DrawTransPicTranslate (220, 72, p, setup_class); M_DrawCharacter (56, setup_cursor_table [setup_cursor], 12+((int)(realtime*4)&1)); if (setup_cursor == 0) M_DrawCharacter (168 + 8*strlen(setup_hostname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1)); if (setup_cursor == 1) M_DrawCharacter (168 + 8*strlen(setup_myname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1)); } static void M_Setup_Key (int k) { int l; switch (k) { case K_ESCAPE: M_Menu_MultiPlayer_f (); break; case K_ENTER: if (setup_cursor == 0 || setup_cursor == 1) return; if (setup_cursor == 2 || setup_cursor == 3 || setup_cursor == 4) goto forward; if (strcmp(cl_name.string, setup_myname) != 0) Cbuf_AddText ( va ("name \"%s\"\n", setup_myname) ); if (strcmp(hostname.string, setup_hostname) != 0) Cvar_Set("hostname", setup_hostname); if (setup_top != setup_oldtop || setup_bottom != setup_oldbottom) Cbuf_AddText( va ("color %i %i\n", setup_top, setup_bottom) ); Cbuf_AddText ( va ("playerclass %d\n", setup_class) ); m_entersound = true; M_Menu_MultiPlayer_f (); break; case K_BACKSPACE: if (setup_cursor == 0) { if (strlen(setup_hostname)) setup_hostname[strlen(setup_hostname)-1] = 0; } if (setup_cursor == 1) { if (strlen(setup_myname)) setup_myname[strlen(setup_myname)-1] = 0; } break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); setup_cursor--; if (setup_cursor < 0) setup_cursor = NUM_SETUP_CMDS-1; break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); setup_cursor++; if (setup_cursor >= NUM_SETUP_CMDS) setup_cursor = 0; break; case K_LEFTARROW: if (setup_cursor < 2) return; S_LocalSound ("raven/menu3.wav"); if (setup_cursor == 2) { #if ENABLE_OLD_DEMO if (gameflags & GAME_OLD_DEMO) { setup_class = (setup_class == CLASS_PALADIN) ? CLASS_THEIF : CLASS_PALADIN; break; } #endif /* OLD_DEMO */ setup_class--; if (setup_class < 1) setup_class = MAX_PLAYER_CLASS; if (setup_class > MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES && !(gameflags & GAME_PORTALS)) setup_class = MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES; } else if (setup_cursor == 3) setup_top = setup_top - 1; else if (setup_cursor == 4) setup_bottom = setup_bottom - 1; break; case K_RIGHTARROW: if (setup_cursor < 2) return; forward: S_LocalSound ("raven/menu3.wav"); if (setup_cursor == 2) { #if ENABLE_OLD_DEMO if (gameflags & GAME_OLD_DEMO) { setup_class = (setup_class == CLASS_PALADIN) ? CLASS_THEIF : CLASS_PALADIN; break; } #endif /* OLD_DEMO */ setup_class++; if (setup_class > MAX_PLAYER_CLASS) setup_class = 1; if (setup_class > MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES && !(gameflags & GAME_PORTALS)) setup_class = 1; } else if (setup_cursor == 3) setup_top = setup_top + 1; else if (setup_cursor == 4) setup_bottom = setup_bottom + 1; break; default: if (k < 32 || k > 127) break; if (setup_cursor == 0) { l = strlen(setup_hostname); if (l < 15) { setup_hostname[l+1] = 0; setup_hostname[l] = k; } } if (setup_cursor == 1) { l = strlen(setup_myname); if (l < 15) { setup_myname[l+1] = 0; setup_myname[l] = k; } } } if (setup_top > 10) setup_top = 0; else if (setup_top < 0) setup_top = 10; if (setup_bottom > 10) setup_bottom = 0; else if (setup_bottom < 0) setup_bottom = 10; } //============================================================================= /* NET MENU */ #define NET_ITEMS (2 + _nums_serial) static int m_net_cursor = 0; static const char *net_helpMessage[] = { /* .........1.........2.... */ #if defined(NET_USE_SERIAL) " ", " Two computers connected", " through two modems. ", " ", " ", " Two computers connected", " by a null-modem cable. ", " ", #endif /* NET_USE_SERIAL */ " Novell network LANs ", " or Windows 95 DOS-box. ", " ", "(LAN=Local Area Network)", " Commonly used to play ", " over the Internet, but ", " also used on a Local ", " Area Network. " }; static void M_Menu_Net_f (void) { Key_SetDest (key_menu); m_state = m_net; m_entersound = true; if (m_net_cursor >= NET_ITEMS) m_net_cursor = 0; m_net_cursor--; M_Net_Key (K_DOWNARROW); } static void M_Net_Draw (void) { int f; ScrollTitle("gfx/menu/title4.lmp"); #if defined(NET_USE_SERIAL) M_DrawBigString (72, (64 + _ser_draw_offset) + (_item_net_ser * 20), "MODEM"); M_DrawBigString (72, (64 + _ser_draw_offset) + (_item_net_dc * 20), "DIRECT CONNECT"); #endif M_DrawBigString (72, (64 + _ser_draw_offset) + (_item_net_ipx * 20), "IPX"); M_DrawBigString (72, (64 + _ser_draw_offset) + (_item_net_tcp * 20), "TCP/IP"); f = (320 - 26*8) / 2; M_DrawTextBox (f, 142, 24, 4); f += 8; M_Print (f, (142 + 1*8), net_helpMessage[m_net_cursor*4 + 0]); M_Print (f, (142 + 2*8), net_helpMessage[m_net_cursor*4 + 1]); M_Print (f, (142 + 3*8), net_helpMessage[m_net_cursor*4 + 2]); M_Print (f, (142 + 4*8), net_helpMessage[m_net_cursor*4 + 3]); f = (int)(realtime * 10)%8; M_DrawTransPic (43, (56 + _ser_draw_offset) + m_net_cursor * 20, Draw_CachePic(va("gfx/menu/menudot%i.lmp", f+1)) ); } static void M_Net_Key (int k) { again: switch (k) { case K_ESCAPE: M_Menu_MultiPlayer_f (); break; case K_DOWNARROW: // Tries to re-draw the menu here, and m_net_cursor could be set to -1 // S_LocalSound ("raven/menu1.wav"); if (++m_net_cursor >= NET_ITEMS) m_net_cursor = 0; break; case K_UPARROW: // S_LocalSound ("raven/menu1.wav"); if (--m_net_cursor < 0) m_net_cursor = NET_ITEMS - 1; break; case K_ENTER: m_entersound = true; switch (m_net_cursor) { #if defined(NET_USE_SERIAL) case _item_net_ser: M_Menu_SerialConfig_f (); break; case _item_net_dc : M_Menu_SerialConfig_f (); break; #endif /* NET_USE_SERIAL*/ case _item_net_ipx: M_Menu_LanConfig_f (); break; case _item_net_tcp: M_Menu_LanConfig_f (); break; default: // multiprotocol break; } break; } #if defined(NET_USE_SERIAL) if (SerialConfig && !serialAvailable) goto again; if (DirectConfig && !serialAvailable) goto again; #endif /* NET_USE_SERIAL*/ if (IPXConfig && !ipxAvailable) goto again; if (TCPIPConfig && !tcpipAvailable) goto again; switch (k) { case K_DOWNARROW: case K_UPARROW: S_LocalSound ("raven/menu1.wav"); break; } } //============================================================================= /* OPTIONS MENU */ #define SLIDER_RANGE 10 enum { OPT_CUSTOMIZE = 0, OPT_CONSOLE, OPT_DEFAULTS, #ifdef GLQUAKE OPT_SCALE, #endif OPT_SCRSIZE, OPT_GAMMA, OPT_MOUSESPEED, OPT_MUSICTYPE, OPT_MUSICVOL, OPT_SNDVOL, OPT_ALWAYRUN, OPT_INVMOUSE, OPT_ALWAYSMLOOK, OPT_USEMOUSE, OPT_CROSSHAIR, OPT_CHASE_ACTIVE, #ifdef GLQUAKE OPT_OPENGL, #endif OPT_VIDEO, OPTIONS_ITEMS }; #ifdef GLQUAKE // prototypes for the opengl menu static void M_Menu_OpenGL_f (void); static void M_OpenGL_Draw (void); static void M_OpenGL_Key (int k); #endif static int options_cursor; void M_Menu_Options_f (void) { Key_SetDest (key_menu); m_state = m_options; m_entersound = true; // get the current music type if (old_bgmtype[0] == 0) q_strlcpy(old_bgmtype, bgmtype.string, sizeof(old_bgmtype)); #if 0 /* change to 1 if dont want to disable mouse in fullscreen */ if ((options_cursor == OPT_USEMOUSE) && (modestate != MS_WINDOWED)) options_cursor = 0; #endif } static void M_AdjustSliders (int dir) { float f; S_LocalSound ("raven/menu3.wav"); switch (options_cursor) { #ifdef GLQUAKE case OPT_SCALE: // scale slider S.A. VID_ChangeConsize(dir); break; #endif case OPT_SCRSIZE: // screen size Cvar_SetValue ("viewsize", scr_viewsize.integer + dir * 10); break; case OPT_GAMMA: // gamma f = v_gamma.value - dir * 0.05; if (f < 0.5) f = 0.5; else if (f > 1) f = 1; Cvar_SetValue ("gamma", f); break; case OPT_MOUSESPEED: // mouse speed f = sensitivity.value + dir * 0.5; if (f > 11) f = 11; else if (f < 1) f = 1; Cvar_SetValue ("sensitivity", f); break; case OPT_MUSICTYPE: // bgm type if (q_strcasecmp(bgmtype.string,"midi") == 0) { if (bgm_extmusic.integer) { if (dir < 0) Cvar_Set("bgm_extmusic","0"); else Cvar_Set("bgmtype","cd"); } else { if (dir < 0) Cvar_Set("bgmtype","none"); else Cvar_Set("bgm_extmusic","1"); } } else if (q_strcasecmp(bgmtype.string,"cd") == 0) { if (dir < 0) { Cvar_Set("bgmtype","midi"); Cvar_Set("bgm_extmusic","1"); } else { Cvar_Set("bgmtype","none"); } } else { if (dir < 0) { Cvar_Set("bgmtype","cd"); } else { Cvar_Set("bgm_extmusic","0"); Cvar_Set("bgmtype","midi"); } } break; case OPT_MUSICVOL: // music volume f = bgmvolume.value + dir * 0.1; if (f < 0) f = 0; else if (f > 1) f = 1; Cvar_SetValue ("bgmvolume", f); break; case OPT_SNDVOL: // sfx volume f = sfxvolume.value + dir * 0.1; if (f < 0) f = 0; else if (f > 1) f = 1; Cvar_SetValue ("volume", f); break; case OPT_ALWAYRUN: // always run if (cl_forwardspeed.value > 200) { Cvar_Set ("cl_forwardspeed", "200"); Cvar_Set ("cl_backspeed", "200"); } else { Cvar_Set ("cl_forwardspeed", "400"); Cvar_Set ("cl_backspeed", "400"); } break; case OPT_INVMOUSE: // invert mouse Cvar_SetValue ("m_pitch", -m_pitch.value); break; case OPT_ALWAYSMLOOK: if (in_mlook.state & 1) Cbuf_AddText("-mlook"); else Cbuf_AddText("+mlook"); break; case OPT_CROSSHAIR: Cvar_Set ("crosshair", crosshair.integer ? "0" : "1"); break; case OPT_CHASE_ACTIVE: // chase_active Cvar_Set ("chase_active", chase_active.integer ? "0" : "1"); break; case OPT_USEMOUSE: // _enable_mouse Cvar_Set ("_enable_mouse", _enable_mouse.integer ? "0" : "1"); break; default: break; } } static void M_DrawSlider (int x, int y, float range) { int i; if (range < 0) range = 0; else if (range > 1) range = 1; M_DrawCharacter (x-8, y, 256); for (i = 0; i < SLIDER_RANGE; i++) M_DrawCharacter (x + i*8, y, 257); M_DrawCharacter (x + i*8, y, 258); M_DrawCharacter (x + (SLIDER_RANGE-1)*8 * range, y, 259); } void M_DrawCheckbox (int x, int y, int on) { if (on) M_Print (x, y, "on"); else M_Print (x, y, "off"); } static void M_Options_Draw (void) { float r; menu_disabled_mouse = false; IN_ActivateMouse (); // we entered the customization menu ScrollTitle("gfx/menu/title3.lmp"); // we use 22 character option titles. the increment to // the x offset is: (22 - strlen(option_title)) * 8 // r goes from 0 (left) to 1 (right) M_Print (16 + (4 * 8), 60 + 8*OPT_CUSTOMIZE, "Customize controls"); M_Print (16 + (9 * 8), 60 + 8*OPT_CONSOLE, "Go to console"); M_Print (16 + (5 * 8), 60 + 8*OPT_DEFAULTS, "Reset to defaults"); #ifdef GLQUAKE M_Print (16 + (17 * 8), 60 + 8*OPT_SCALE, "Scale"); r = VID_ReportConsize(); M_DrawSlider (220, 60 + 8*OPT_SCALE, (r-1)/2); #endif M_Print (16 + (11 * 8), 60 + 8*OPT_SCRSIZE, "Screen size"); r = (scr_viewsize.value - 30.0) / (130 - 30); M_DrawSlider (220, 60 + 8*OPT_SCRSIZE, r); M_Print (16 + (12 * 8), 60 + 8*OPT_GAMMA, "Brightness"); r = (1.0 - v_gamma.value) / 0.5; M_DrawSlider (220, 60 + 8*OPT_GAMMA, r); M_Print (16 + (11 * 8), 60 + 8*OPT_MOUSESPEED, "Mouse Speed"); r = (sensitivity.value - 1) / 10; M_DrawSlider (220, 60 + 8*OPT_MOUSESPEED, r); M_Print (16 + (12 * 8), 60 + 8*OPT_MUSICTYPE, "Music Type"); if (q_strcasecmp(bgmtype.string, "midi") == 0) { if (bgm_extmusic.integer) M_Print (220, 60 + 8*OPT_MUSICTYPE, "ALL CODECS"); else M_Print (220, 60 + 8*OPT_MUSICTYPE, "MIDI ONLY"); } else if (q_strcasecmp(bgmtype.string, "cd") == 0) M_Print (220, 60 + 8*OPT_MUSICTYPE, "CD"); else M_Print (220, 60 + 8*OPT_MUSICTYPE, "None"); M_Print (16 + (10 * 8), 60 + 8*OPT_MUSICVOL, "Music Volume"); r = bgmvolume.value; M_DrawSlider (220, 60 + 8*OPT_MUSICVOL, r); M_Print (16 + (10 * 8), 60 + 8*OPT_SNDVOL, "Sound Volume"); r = sfxvolume.value; M_DrawSlider (220, 60 + 8*OPT_SNDVOL, r); M_Print (16 + (12 * 8), 60 + 8*OPT_ALWAYRUN, "Always Run"); M_DrawCheckbox (220, 60 + 8*OPT_ALWAYRUN, (cl_forwardspeed.value > 200)); M_Print (16 + (10 * 8), 60 + 8*OPT_INVMOUSE, "Invert Mouse"); M_DrawCheckbox (220, 60 + 8*OPT_INVMOUSE, m_pitch.value < 0); M_Print (16 + (12 * 8), 60 + 8*OPT_ALWAYSMLOOK, "Mouse Look"); M_DrawCheckbox (220, 60 + 8*OPT_ALWAYSMLOOK, in_mlook.state & 1); M_Print (16 + (13 * 8), 60 + 8*OPT_USEMOUSE, "Use Mouse"); M_DrawCheckbox (220, 60 + 8*OPT_USEMOUSE, _enable_mouse.integer); M_Print (16 + (8 * 8), 60 + 8*OPT_CROSSHAIR, "Show Crosshair"); M_DrawCheckbox (220, 60 + 8*OPT_CROSSHAIR, crosshair.integer); #ifdef GLQUAKE M_Print (16 + (7 * 8), 60 + 8*OPT_OPENGL, "OpenGL Features"); #endif M_Print (16 + (12 * 8), 60 + 8*OPT_CHASE_ACTIVE, "Chase Mode"); M_DrawCheckbox (220, 60 + 8*OPT_CHASE_ACTIVE, chase_active.integer); if (vid_menudrawfn) M_Print (16 + (11 * 8), 60 + 8*OPT_VIDEO, "Video Modes"); // cursor M_DrawCharacter (200, 60 + 8*options_cursor, 12 + ((int)(realtime*4) & 1)); } static void M_Options_Key (int k) { switch (k) { case K_ESCAPE: M_Menu_Main_f (); break; case K_ENTER: m_entersound = true; switch (options_cursor) { case OPT_CUSTOMIZE: M_Menu_Keys_f (); break; case OPT_CONSOLE: m_state = m_none; Con_ToggleConsole_f (); break; case OPT_DEFAULTS: Cbuf_AddText ("exec default.cfg\n"); break; #ifdef GLQUAKE case OPT_OPENGL: M_Menu_OpenGL_f (); break; #endif case OPT_VIDEO: M_Menu_Video_f (); break; default: M_AdjustSliders (1); break; } return; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); options_cursor--; if (options_cursor < 0) options_cursor = OPTIONS_ITEMS-1; break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); options_cursor++; if (options_cursor >= OPTIONS_ITEMS) options_cursor = 0; break; case K_LEFTARROW: M_AdjustSliders (-1); break; case K_RIGHTARROW: M_AdjustSliders (1); break; } #if 0 /* change to 1 if dont want to disable mouse in fullscreen */ if ((options_cursor == OPT_USEMOUSE) && (modestate != MS_WINDOWED)) { if (k == K_UPARROW) options_cursor = OPT_USEMOUSE - 1; else { options_cursor = OPT_USEMOUSE + 1; if (options_cursor == OPTIONS_ITEMS) options_cursor = 0; } } #endif if (options_cursor == OPT_VIDEO && vid_menudrawfn == NULL) { if (k == K_UPARROW) options_cursor = OPT_VIDEO - 1; else options_cursor = 0; } } //============================================================================= /* OPENGL FEATURES MENU */ #ifdef GLQUAKE enum { OGL_PURGETEX, OGL_GLOW1, OGL_GLOW2, OGL_GLOW3, OGL_LIGHTMAPFMT, OGL_COLOREDLIGHT, OGL_COLOREDSTATIC, OGL_COLOREDDYNAMIC, OGL_COLOREDEXTRA, OGL_TEXFILTER, OGL_ANISOTROPY, OGL_ITEMS }; static const struct { const char *name; // legible string value const char *desc; // description for user int glenum; // opengl enum } lm_formats[] = { { "gl_luminance", "gl_luminance (8 bit)", GL_LUMINANCE }, { "gl_rgba", "gl_rgba (32 bit)", GL_RGBA }, { " ", "Unknown value (?)", 0 } }; #define MAX_LMFORMATS (sizeof(lm_formats) / sizeof(lm_formats[0])) static int tex_mode; static int lm_format; static int opengl_cursor; static void M_Menu_OpenGL_f (void) { Key_SetDest (key_menu); m_state = m_opengl; m_entersound = true; } static void M_OpenGL_Draw (void) { int i; ScrollTitle("gfx/menu/title3.lmp"); M_PrintWhite (96, 72, "OpenGL Features:"); // we use 22 character option titles. the increment to // the x offset is: (22 - strlen(option_title)) * 8 M_Print (32 + (4 * 8), 90 + 8*OGL_PURGETEX, "Purge map textures"); M_DrawCheckbox (232, 90 + 8*OGL_PURGETEX, gl_purge_maptex.integer); M_Print (32 + (9 * 8), 90 + 8*OGL_GLOW1, "missile glows"); M_DrawCheckbox (232, 90 + 8*OGL_GLOW1, gl_missile_glows.integer); M_Print (32 + (11 * 8), 90 + 8*OGL_GLOW2, "torch glows"); M_DrawCheckbox (232, 90 + 8*OGL_GLOW2, gl_glows.integer); M_Print (32 + (11 * 8), 90 + 8*OGL_GLOW3, "other glows"); M_DrawCheckbox (232, 90 + 8*OGL_GLOW3, gl_other_glows.integer); M_Print (32 + (6 * 8), 90 + 8*OGL_LIGHTMAPFMT, "Lightmap Format:"); for (i = 0; i < (int)MAX_LMFORMATS; i++) { if (!q_strcasecmp(gl_lightmapfmt.string, lm_formats[i].name)) break; } lm_format = i; M_Print (232, 90 + 8*OGL_LIGHTMAPFMT, lm_formats[lm_format].desc); M_Print (32 + (6 * 8), 90 + 8*OGL_COLOREDLIGHT, "Colored lights :"); M_Print (32 + (9 * 8), 90 + 8*OGL_COLOREDSTATIC, "static lights"); M_Print (32 + (8 * 8), 90 + 8*OGL_COLOREDDYNAMIC, "dynamic lights"); M_Print (32 + (10 * 8), 90 + 8*OGL_COLOREDEXTRA, "extra lights"); // bound the gl_coloredlight value if (gl_coloredlight.integer < 0) Cvar_Set ("gl_coloredlight", "0"); switch (gl_coloredlight.integer) { case 0: M_Print (232, 90 + 8*OGL_COLOREDSTATIC, "none (white)"); break; case 1: M_Print (232, 90 + 8*OGL_COLOREDSTATIC, "colored"); break; default: M_Print (232, 90 + 8*OGL_COLOREDSTATIC, "blend"); break; } M_DrawCheckbox (232, 90 + 8*OGL_COLOREDDYNAMIC, gl_colored_dynamic_lights.integer); M_DrawCheckbox (232, 90 + 8*OGL_COLOREDEXTRA, gl_extra_dynamic_lights.integer); M_Print (32 + (5 * 8), 90 + 8*OGL_TEXFILTER, "Texture filtering"); M_Print (232, 90 + 8*OGL_TEXFILTER, gl_texmodes[gl_filter_idx].name); M_Print (32 + (5 * 8), 90 + 8*OGL_ANISOTROPY, "Anisotropy level:"); M_Print (232, 90 + 8*OGL_ANISOTROPY, (gl_max_anisotropy < 2) ? "N/A" : Cvar_VariableString("gl_texture_anisotropy")); // cursor M_DrawCharacter (216, 90 + opengl_cursor*8, 12+((int)(realtime*4)&1)); if (opengl_cursor == OGL_LIGHTMAPFMT && gl_lightmap_format != lm_formats[lm_format].glenum) { int x = (320-25*8)/2; for (i = 0; i < (int)MAX_LMFORMATS-1; i++) { if (gl_lightmap_format == lm_formats[i].glenum) break; } M_DrawTextBox (x, 94 + 8*(OGL_LIGHTMAPFMT), 25, 5); M_Print (x+16, 98 + 8*(OGL_LIGHTMAPFMT+1), "currently running with:"); M_Print (x+16, 98 + 8*(OGL_LIGHTMAPFMT+2), lm_formats[i].desc); M_Print (x+16, 98 + 8*(OGL_LIGHTMAPFMT+3), "your selection will take"); M_Print (x+16, 98 + 8*(OGL_LIGHTMAPFMT+4), "effect upon level reload"); } else if (opengl_cursor == OGL_COLOREDSTATIC) { int x = (320-25*8)/2; if (gl_lightmap_format != GL_RGBA) { if (gl_coloredlight.integer) { M_DrawTextBox (x, 94 + 8*(OGL_COLOREDSTATIC), 25, 3); M_Print (x+16, 98 + 8*(OGL_COLOREDSTATIC+1), " rgba lightmap format "); M_Print (x+16, 98 + 8*(OGL_COLOREDSTATIC+2), " is required"); } } else if (gl_coloredlight.integer != gl_coloredstatic) { M_DrawTextBox (x, 94 + 8*(OGL_COLOREDSTATIC), 25, 3); M_Print (x+16, 98 + 8*(OGL_COLOREDSTATIC+1), "your selection will take"); M_Print (x+16, 98 + 8*(OGL_COLOREDSTATIC+2), "effect upon level reload"); } } } static void M_OpenGL_Key (int k) { switch (k) { case K_ESCAPE: M_Menu_Options_f (); break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); if (--opengl_cursor == OGL_COLOREDLIGHT) --opengl_cursor; if (opengl_cursor < 0) opengl_cursor = OGL_ITEMS-1; break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); if (++opengl_cursor == OGL_COLOREDLIGHT) ++opengl_cursor; if (opengl_cursor >= OGL_ITEMS) opengl_cursor = 0; break; case K_ENTER: case K_LEFTARROW: case K_RIGHTARROW: m_entersound = true; switch (opengl_cursor) { case OGL_PURGETEX: // purge gl textures on map change Cvar_Set ("gl_purge_maptex", gl_purge_maptex.integer ? "0" : "1"); break; case OGL_GLOW1: // glow effects, missiles Cvar_Set ("gl_missile_glows", gl_missile_glows.integer ? "0" : "1"); break; case OGL_GLOW2: // glow effects, torches Cvar_Set ("gl_glows", gl_glows.integer ? "0" : "1"); break; case OGL_GLOW3: // glow effects, other: mana, etc. Cvar_Set ("gl_other_glows", gl_other_glows.integer ? "0" : "1"); break; case OGL_LIGHTMAPFMT: // lightmap format switch (k) { case K_RIGHTARROW: if (++lm_format >= (int)MAX_LMFORMATS - 1) lm_format = MAX_LMFORMATS - 2; Cvar_Set ("gl_lightmapfmt", lm_formats[lm_format].name); break; case K_LEFTARROW: if (--lm_format < 0) lm_format = 0; Cvar_Set ("gl_lightmapfmt", lm_formats[lm_format].name); break; default: break; } break; case OGL_COLOREDSTATIC: // static colored lights switch (k) { case K_RIGHTARROW: Cvar_Set ("gl_coloredlight", (gl_coloredlight.integer >= 1) ? "2" : "1"); break; case K_LEFTARROW: Cvar_Set ("gl_coloredlight", (gl_coloredlight.integer <= 1) ? "0" : "1"); break; default: break; } break; case OGL_COLOREDDYNAMIC: // dynamic colored lights Cvar_Set ("gl_colored_dynamic_lights", gl_colored_dynamic_lights.integer ? "0" : "1"); break; case OGL_COLOREDEXTRA: // extra dynamic colored lights Cvar_Set ("gl_extra_dynamic_lights", gl_extra_dynamic_lights.integer ? "0" : "1"); break; case OGL_TEXFILTER: // texture filter tex_mode = gl_filter_idx; switch (k) { case K_LEFTARROW: if (--tex_mode < 0) tex_mode = 0; break; case K_RIGHTARROW: if (++tex_mode >= NUM_GL_FILTERS) tex_mode = NUM_GL_FILTERS-1; break; default: return; } Cvar_Set ("gl_texturemode", gl_texmodes[tex_mode].name); break; case OGL_ANISOTROPY: // anisotropic filter level if (gl_max_anisotropy < 2) return; tex_mode = Cvar_VariableValue("gl_texture_anisotropy"); switch (k) { case K_LEFTARROW: if (--tex_mode < 1) tex_mode = 1; break; case K_RIGHTARROW: if (++tex_mode > gl_max_anisotropy) tex_mode = gl_max_anisotropy; break; default: return; } Cvar_SetValue ("gl_texture_anisotropy", tex_mode); break; default: break; } default: break; } } #endif //============================================================================= /* KEYS MENU */ static const char *bindnames[][2] = { {"+attack", "attack"}, {"impulse 10", "next weapon"}, {"impulse 12", "prev.weapon"}, {"+jump", "jump / swim up"}, {"+forward", "walk forward"}, {"+back", "backpedal"}, {"+left", "turn left"}, {"+right", "turn right"}, {"+speed", "run"}, {"+moveleft", "step left"}, {"+moveright", "step right"}, {"+strafe", "sidestep"}, {"+crouch", "crouch"}, {"+lookup", "look up"}, {"+lookdown", "look down"}, {"centerview", "center view"}, {"+moveup", "swim up"}, {"+movedown", "swim down"}, {"impulse 13", "lift object"}, {"invuse", "use inv item"}, {"impulse 44", "drop inv item"}, {"+showinfo", "full inventory"}, {"+showdm", "info / frags"}, {"toggle_dm", "toggle frags"}, {"+infoplaque", "objectives"}, /* command to display the mission pack's objectives */ {"invleft", "inv move left"}, {"invright", "inv move right"}, {"impulse 100", "inv:torch"}, {"impulse 101", "inv:qrtz flask"}, {"impulse 102", "inv:mystic urn"}, {"impulse 103", "inv:krater"}, {"impulse 104", "inv:chaos devc"}, {"impulse 105", "inv:tome power"}, {"impulse 106", "inv:summon stn"}, {"impulse 107", "inv:invisiblty"}, {"impulse 108", "inv:glyph"}, {"impulse 109", "inv:boots"}, {"impulse 110", "inv:repulsion"}, {"impulse 111", "inv:bo peep"}, {"impulse 112", "inv:flight"}, {"impulse 113", "inv:force cube"}, {"impulse 114", "inv:icon defn"} }; #define NUMCOMMANDS (sizeof(bindnames)/sizeof(bindnames[0])) #define KEYS_SIZE 14 static int keys_cursor; static int keys_top = 0; static void M_Menu_Keys_f (void) { Key_SetDest (key_menu); m_state = m_keys; m_entersound = true; } static void M_FindKeysForCommand (const char *command, int *twokeys) { int count; int j; int l,l2; char *b; twokeys[0] = twokeys[1] = -1; l = strlen(command); count = 0; for (j = 0; j < 256; j++) { b = keybindings[j]; if (!b) continue; if (!strncmp (b, command, l)) { l2 = strlen(b); if (l == l2) { twokeys[count] = j; count++; if (count == 2) break; } } } } static void M_UnbindCommand (const char *command) { int j; int l; char *b; l = strlen(command); for (j = 0; j < 256; j++) { b = keybindings[j]; if (!b) continue; if (!strncmp (b, command, l) ) Key_SetBinding (j, NULL); } } static void M_Keys_Draw (void) { int i, x, y; int keys[2]; const char *name; ScrollTitle("gfx/menu/title6.lmp"); if (keys_top) M_DrawCharacter (6, 80, 128); if (keys_top + KEYS_SIZE < (int)NUMCOMMANDS) M_DrawCharacter (6, 80 + ((KEYS_SIZE-1)*8), 129); // search for known bindings for (i = 0; i < KEYS_SIZE; i++) { y = 80 + 8*i; M_Print (16, y, bindnames[i+keys_top][1]); M_FindKeysForCommand (bindnames[i+keys_top][0], keys); if (keys[0] == -1) { M_Print (140, y, "???"); } else { name = Key_KeynumToString (keys[0]); M_Print (140, y, name); x = strlen(name) * 8; if (keys[1] != -1) { M_Print (140 + x + 8, y, "or"); M_Print (140 + x + 32, y, Key_KeynumToString (keys[1])); } } } if (Key_GetDest() & key_bindbit) { M_Print (12, 64, "Press a key or button for this action"); M_DrawCharacter (130, 80 + (keys_cursor-keys_top)*8, '='); } else { M_Print (18, 64, "Enter to change, backspace to clear"); M_DrawCharacter (130, 80 + (keys_cursor-keys_top)*8, 12+((int)(realtime*4)&1)); } } static void M_Keys_Key (int k) { int keys[2]; switch (k) { case K_ESCAPE: M_Menu_Options_f (); break; case K_LEFTARROW: case K_UPARROW: S_LocalSound ("raven/menu1.wav"); keys_cursor--; if (keys_cursor < 0) keys_cursor = NUMCOMMANDS-1; break; case K_DOWNARROW: case K_RIGHTARROW: S_LocalSound ("raven/menu1.wav"); keys_cursor++; if (keys_cursor >= (int)NUMCOMMANDS) keys_cursor = 0; break; case K_ENTER: // go into bind mode M_FindKeysForCommand (bindnames[keys_cursor][0], keys); S_LocalSound ("raven/menu2.wav"); if (keys[1] != -1) M_UnbindCommand (bindnames[keys_cursor][0]); Key_SetDest (key_menubind); break; case K_BACKSPACE: // delete bindings case K_DEL: // delete bindings S_LocalSound ("raven/menu2.wav"); M_UnbindCommand (bindnames[keys_cursor][0]); break; } if (keys_cursor < keys_top) keys_top = keys_cursor; else if (keys_cursor >= keys_top+KEYS_SIZE) keys_top = keys_cursor - KEYS_SIZE + 1; } //============================================================================= /* VIDEO MENU */ static void M_Menu_Video_f (void) { Key_SetDest (key_menu); m_state = m_video; m_entersound = true; } static void M_Video_Draw (void) { (*vid_menudrawfn) (); } static void M_Video_Key (int key) { (*vid_menukeyfn) (key); } //============================================================================= /* HELP MENU */ static int help_page; #define NUM_HELP_PAGES 5 static void M_Menu_Help_f (void) { Key_SetDest (key_menu); m_state = m_help; m_entersound = true; help_page = 0; } #if FULLSCREEN_INTERMISSIONS # ifdef GLQUAKE # define Load_HelpPic_FN(X,Y,Z) Draw_CachePicNoTrans((X)) # define Draw_HelpPic_FN(X,Y,Z) Draw_IntermissionPic((Z)) # else # define Load_HelpPic_FN(X,Y,Z) Draw_CachePicResize((X),(Y),(Z)) # define Draw_HelpPic_FN(X,Y,Z) Draw_Pic(0,0,(Z)) # endif #else # ifdef GLQUAKE # define Load_HelpPic_FN(X,Y,Z) Draw_CachePic((X)) # define Draw_HelpPic_FN(X,Y,Z) Draw_Pic((X),(Y),(Z)) # else # define Load_HelpPic_FN(X,Y,Z) Draw_CachePic((X)) # define Draw_HelpPic_FN(X,Y,Z) Draw_Pic((X),(Y),(Z)) # endif #endif static void M_Help_Draw (void) { Draw_HelpPic_FN ((vid.width - 320)>>1, 0, Load_HelpPic_FN(va("gfx/menu/help%02i.lmp", help_page+1), vid.width, vid.height)); } static void M_Help_Key (int key) { switch (key) { case K_ESCAPE: M_Menu_Main_f (); break; case K_UPARROW: case K_RIGHTARROW: m_entersound = true; if (++help_page >= NUM_HELP_PAGES) help_page = 0; break; case K_DOWNARROW: case K_LEFTARROW: m_entersound = true; if (--help_page < 0) help_page = NUM_HELP_PAGES-1; break; } } //============================================================================= /* QUIT MENU */ //static int msgNumber; static enum m_state_e m_quit_prevstate; static qboolean wasInMenus; #if 0 static const char *quitMessage [] = { /* .........1.........2.... */ " Look! Behind you! ", " There's a big nasty ", " thing - shoot it! ", " ", " You can't go now, I ", " was just getting ", " warmed up. ", " ", " One more game. ", " C'mon... ", " Who's gonna know? ", " ", " What's the matter? ", " Palms too sweaty to ", " keep playing? ", " ", " Watch your local store", " for Hexen 2 ", " plush toys and ", " greeting cards! ", " Hexen 2... ", " ", " Too much is never ", " enough. ", " Sure go ahead and ", " leave. But I know ", " you'll be back. ", " ", " ", " Insert cute phrase ", " here ", " " }; #endif static float LinePos; static int LineTimes; static int MaxLines; static const char **LineText; static qboolean LineTxt2; static qboolean SoundPlayed; #define MAX_LINES 145 static const char *CreditText[MAX_LINES] = { "Project Director: Brian Raffel", "", "Lead Programmer: Rick Johnson", "", "Programming:", " Ben Gokey", " Bob Love", " Mike Gummelt", "", "Additional Programming:", " Josh Weier", "", "Lead Design: Eric Biessman", "", "Design:", " Brian Raffel", " Brian Frank", " Tom Odell", "", "Art Director: Brian Pelletier", "", "Art:", " Shane Gurno", " Jim Sumwalt", " Mark Morgan", " Kim Lathrop", " Ted Halsted", " Rebecca Rettenmund", " Les Dorscheid", "", "Animation:", " Chaos (Mike Werckle)", " Brian Shubat", "", "Cinematics:", " Jeff Dewitt", " Jeffrey P. Lampo", "", "Music:", " Kevin Schilder", "", "Sound:", " Kevin Schilder", " Chia Chin Lee", "", "", "Activision", "", "Producer:", " Steve Stringer", "", "Localization Producer:", " Sandi Isaacs", "", "Marketing Product Manager:", " Henk Hartong", "", "European Marketing", "Product Director:", " Janine Johnson", "", "Marketing Associate:", " Kevin Kraff", "", "Senior Quality", "Assurance Lead:", " Tim Vanlaw", "", "Quality Assurance Lead:", " John Tam", "", "Quality Assurance Team:", " Steve Rosenthal, Mike Spann,", " Steve Elwell, Kelly Wand,", " Kip Stolberg, Igor Krinitskiy,", " Ian Stevens, Marilena Wahmann,", " David Baker, Winnie Lee", "", "Documentation:", " Mike Rivera, Sylvia Orzel,", " Belinda Vansickle", "", "Chronicle of Deeds written by:", " Joe Grant Bell", "", "Localization:", " Nathalie Dove, Lucy Morgan,", " Alex Wylde, Nicky Kerth", "", "Installer by:", " Steve Stringer, Adam Goldberg,", " Tanya Martino, Eric Schmidt,", " Ronnie Lane", "", "Art Assistance by:", " Carey Chico and Franz Boehm", "", "BizDev Babe:", " Jamie Bafus", "", "And...", "", "Deal Guru:", " Mitch Lasky", "", "", "Thanks to Id software:", " John Carmack", " Adrian Carmack", " Kevin Cloud", " Barrett 'Bear' Alexander", " American McGee", "", "", "Published by Id Software, Inc.", "Distributed by Activision, Inc.", "", "The Id Software Technology used", "under license in Hexen II (tm)", "(c) 1996, 1997 Id Software, Inc.", "All Rights Reserved.", "", "Hexen(r) is a registered trademark", "of Raven Software Corp.", "Hexen II (tm) and the Raven logo", "are trademarks of Raven Software", "Corp. The Id Software name and", "id logo are trademarks of", "Id Software, Inc. Activision(r)", "is a registered trademark of", "Activision, Inc. All other", "trademarks are the property of", "their respective owners.", "", "", "", "Send bug descriptions to:", " h2bugs@mail.ravensoft.com", "", "Special thanks to Gary McTaggart", "at 3dfx for his help with", "the gl version!", "", "No snails were harmed in the", #ifdef DEMOBUILD "making of this demo!" #else "making of this game!" #endif }; #define MAX_LINES2 158 static const char *Credit2Text[MAX_LINES2] = { "Map Master: ", " 'Caffeine Buzz' Raffel", "", "Code Warrior:", " Rick 'Superfly' Johnson", "", "Grunt Boys:", " 'Judah' Ben Gokey", " Bob 'Back In Action' Love", " Mike 'Jethro' Gummelt", "", "Additional Grunting:", " Josh 'Intern' Weier", "", "Whippin' Boy:", " Eric 'Baby' Biessman", "", "Crazy Levelers:", " 'Big Daddy' Brian Raffel", " Brian 'Red' Frank", " Tom 'Texture Alignment' Odell", "", "Art Lord:", " Brian 'Mr. Calm' Pelletier", "", "Pixel Pushers:", " Shane 'Duh' Gurno", " 'Slim' Jim Sumwalt", " Mark 'Dad Gummit' Morgan", " Kim 'Toy Master' Lathrop", " 'Drop Dead' Ted Halsted", " Rebecca 'Zombie' Rettenmund", " Les 'Be Friends' Dorscheid", "", "Salad Shooters:", " Mike 'Chaos' Werckle", " Brian 'Mutton Chops' Shubat", "", "Last Minute Addition:", " Jeff 'Spanky' Dewitt", " Jeffrey 'Misspalld' Lampo", "", "Random Notes:", " Kevin 'I Already Paid' Schilder", "", "Grunts, Groans, and Moans:", " Kevin 'I Already Paid' Schilder", " Chia 'Pet' Chin Lee", "", "", "Activision", "", "Producer:", " Steve 'Ferris' Stringer", "", "Localization Producer:", " Sandi 'Boneduster' Isaacs", "", "Marketing Product Manager:", " Henk 'A-10' Hartong", "", "European Marketing", "Product Director:", " Janine Johnson", "", "Marketing Associate:", " Kevin 'Savage' Kraff", "", "Senior Quality", "Assurance Lead:", " Tim 'Outlaw' Vanlaw", "", "Quality Assurance Lead:", " John 'Armadillo' Tam", "", "Quality Assurance Team:", " Steve 'Rhinochoadicus'", " Rosenthal,", " Mike 'Dragonhawk' Spann,", " Steve 'Zendog' Elwell,", " Kelly 'Li'l Bastard' Wand,", " Kip 'Angus' Stolberg,", " Igor 'Russo' Krinitskiy,", " Ian 'Cracker' Stevens,", " Marilena 'Raveness-X' Wahmann,", " David 'Spicegirl' Baker,", " Winnie 'Mew' Lee", "", "Documentation:", " Mike Rivera, Sylvia Orzel,", " Belinda Vansickle", "", "Chronicle of Deeds written by:", " Joe Grant Bell", "", "Localization:", " Nathalie Dove, Lucy Morgan,", " Alex Wylde, Nicky Kerth", "", "Installer by:", " Steve 'Bahh' Stringer,", " Adam Goldberg, Tanya Martino,", " Eric Schmidt, Ronnie Lane", "", "Art Assistance by:", " Carey 'Damien' Chico and", " Franz Boehm", "", "BizDev Babe:", " Jamie Bafus", "", "And...", "", "Deal Guru:", " Mitch Lasky", "", "", "Thanks to Id software:", " John Carmack", " Adrian Carmack", " Kevin Cloud", " Barrett 'Bear' Alexander", " American McGee", "", "", "Published by Id Software, Inc.", "Distributed by Activision, Inc.", "", "The Id Software Technology used", "under license in Hexen II (tm)", "(c) 1996, 1997 Id Software, Inc.", "All Rights Reserved.", "", "Hexen(r) is a registered trademark", "of Raven Software Corp.", "Hexen II (tm) and the Raven logo", "are trademarks of Raven Software", "Corp. The Id Software name and", "id logo are trademarks of", "Id Software, Inc. Activision(r)", "is a registered trademark of", "Activision, Inc. All other", "trademarks are the property of", "their respective owners.", "", "", "", "Send bug descriptions to:", " h2bugs@mail.ravensoft.com", "", "Special thanks to Bob for", "remembering 'P' is for Polymorph", "", "", "See the next movie in the long", "awaited sequel, starring", "Bobby Love in,", " Out of Traction, Back in Action!" }; #define MAX_LINES_MP 138 static const char *CreditTextMP[MAX_LINES_MP] = { "Project Director: James Monroe", "Creative Director: Brian Raffel", "Project Coordinator: Kevin Schilder", "", "Lead Programmer: James Monroe", "", "Programming:", " Mike Gummelt", " Josh Weier", "", "Additional Programming:", " Josh Heitzman", " Nathan Albury", " Rick Johnson", "", "Assembly Consultant:", " Mr. John Scott", "", "Lead Design: Jon Zuk", "", "Design:", " Tom Odell", " Jeremy Statz", " Mike Renner", " Eric Biessman", " Kenn Hoekstra", " Matt Pinkston", " Bobby Duncanson", " Brian Raffel", "", "Art Director: Les Dorscheid", "", "Art:", " Kim Lathrop", " Gina Garren", " Joe Koberstein", " Kevin Long", " Jeff Butler", " Scott Rice", " John Payne", " Steve Raffel", "", "Animation:", " Eric Turman", " Chaos (Mike Werckle)", "", "Music:", " Kevin Schilder", "", "Sound:", " Chia Chin Lee", "", "Activision", "", "Producer:", " Steve Stringer", "", "Marketing Product Manager:", " Henk Hartong", "", "Marketing Associate:", " Kevin Kraff", "", "Senior Quality", "Assurance Lead:", " Tim Vanlaw", "", "Quality Assurance Lead:", " Doug Jacobs", "", "Quality Assurance Team:", " Steve Rosenthal, Steve Elwell,", " Chad Bordwell, David Baker,", " Aaron Casillas, Damien Fischer,", " Winnie Lee, Igor Krinitskiy,", " Samantha Lee, John Park", " Ian Stevens, Chris Toft", "", "Production Testers:", " Steve Rosenthal and", " Chad Bordwell", "", "Additional QA and Support:", " Tony Villalobos", " Jason Sullivan", "", "Installer by:", " Steve Stringer, Adam Goldberg,", " Tanya Martino, Eric Schmidt,", " Ronnie Lane", "", "Art Assistance by:", " Carey Chico and Franz Boehm", "", "BizDev Babe:", " Jamie Bafus", "", "And...", "", "Our Big Toe:", " Mitch Lasky", "", "", "Special Thanks to:", " Id software", " The original Hexen2 crew", " We couldn't have done it", " without you guys!", "", "", "Published by Id Software, Inc.", "Distributed by Activision, Inc.", "", "The Id Software Technology used", "under license in Hexen II (tm)", "(c) 1996, 1997 Id Software, Inc.", "All Rights Reserved.", "", "Hexen(r) is a registered trademark", "of Raven Software Corp.", "Hexen II (tm) and the Raven logo", "are trademarks of Raven Software", "Corp. The Id Software name and", "id logo are trademarks of", "Id Software, Inc. Activision(r)", "is a registered trademark of", "Activision, Inc. All other", "trademarks are the property of", "their respective owners.", "", "", "", "Send bug descriptions to:", " h2bugs@mail.ravensoft.com", "", "", "No yaks were harmed in the", "making of this game!" }; #define MAX_LINES2_MP 151 static const char *Credit2TextMP[MAX_LINES2_MP] = { "PowerTrip: James (emorog) Monroe", "Cartoons: Brian Raffel", " (use more puzzles)", "Doc Keeper: Kevin Schilder", "", "Whip cracker: James Monroe", "", "Whipees:", " Mike (i didn't break it) Gummelt", " Josh (extern) Weier", "", "We don't deserve whipping:", " Josh (I'm not on this project)", " Heitzman", " Nathan (deer hunter) Albury", " Rick (model crusher) Johnson", "", "Bit Packer:", " Mr. John (Slaine) Scott", "", "Lead Slacker: Jon (devil boy) Zuk", "", "Other Slackers:", " Tom (can i have an office) Odell", " Jeremy (nt crashed again) Statz", " Mike (i should be doing my ", " homework) Renner", " Eric (the nose) Biessman", " Kenn (.plan) Hoekstra", " Matt (big elbow) Pinkston", " Bobby (needs haircut) Duncanson", " Brian (they're in my town) Raffel", "", "Use the mouse: Les Dorscheid", "", "What's a mouse?:", " Kim (where's my desk) Lathrop", " Gina (i can do your laundry)", " Garren", " Joe (broken axle) Koberstein", " Kevin (titanic) Long", " Jeff (norbert) Butler", " Scott (what's the DEL key for?)", " Rice", " John (Shpluuurt!) Payne", " Steve (crash) Raffel", "", "Boners:", " Eric (terminator) Turman", " Chaos Device", "", "Drum beater:", " Kevin Schilder", "", "Whistle blower:", " Chia Chin (bruce) Lee", "", "", "Activision", "", "Producer:", " Steve 'Ferris' Stringer", "", "Marketing Product Manager:", " Henk 'GODMODE' Hartong", "", "Marketing Associate:", " Kevin 'Kraffinator' Kraff", "", "Senior Quality", "Assurance Lead:", " Tim 'Outlaw' Vanlaw", "", "Quality Assurance Lead:", " Doug Jacobs", "", "Shadow Finders:", " Steve Rosenthal, Steve Elwell,", " Chad Bordwell,", " David 'Spice Girl' Baker,", " Error Casillas, Damien Fischer,", " Winnie Lee,", " Ygor Krynytyskyy,", " Samantha (Crusher) Lee, John Park", " Ian Stevens, Chris Toft", "", "Production Testers:", " Steve 'Damn It's Cold!'", " Rosenthal and", " Chad 'What Hotel Receipt?'", " Bordwell", "", "Additional QA and Support:", " Tony Villalobos", " Jason Sullivan", "", "Installer by:", " Steve 'Bahh' Stringer,", " Adam Goldberg, Tanya Martino,", " Eric Schmidt, Ronnie Lane", "", "Art Assistance by:", " Carey 'Damien' Chico and", " Franz Boehm", "", "BizDev Babe:", " Jamie Bafus", "", "And...", "", "Our Big Toe:", " Mitch Lasky", "", "", "Special Thanks to:", " Id software", " Anyone who ever worked for Raven,", " (except for Alex)", "", "", "Published by Id Software, Inc.", "Distributed by Activision, Inc.", "", "The Id Software Technology used", "under license in Hexen II (tm)", "(c) 1996, 1997 Id Software, Inc.", "All Rights Reserved.", "", "Hexen(r) is a registered trademark", "of Raven Software Corp.", "Hexen II (tm) and the Raven logo", "are trademarks of Raven Software", "Corp. The Id Software name and", "id logo are trademarks of", "Id Software, Inc. Activision(r)", "is a registered trademark of", "Activision, Inc. All other", "trademarks are the property of", "their respective owners.", "", "", "", "Send bug descriptions to:", " h2bugs@mail.ravensoft.com", "", "Special Thanks To:", " E.H.S., The Osmonds,", " B.B.V.D., Daisy The Lovin' Lamb,", " 'You Killed' Kenny,", " and Baby Biessman.", "" }; #define QUIT_SIZE 16 /* was 18. stole 2 for the two uHexen2 lines. */ void M_Menu_Quit_f (void) { if (m_state == m_quit) return; wasInMenus = !!(Key_GetDest () & key_menu); Key_SetDest (key_menu); m_quit_prevstate = m_state; m_state = m_quit; m_entersound = true; // msgNumber = rand()&7; LinePos = 0; LineTimes = 0; if (gameflags & GAME_PORTALS) { LineText = CreditTextMP; MaxLines = MAX_LINES_MP; } else { LineText = CreditText; MaxLines = MAX_LINES; } LineTxt2 = false; SoundPlayed = false; } static void M_Quit_Key (int key) { switch (key) { case K_ESCAPE: case 'n': case 'N': if (wasInMenus) { m_state = m_quit_prevstate; m_entersound = true; } else { Key_SetDest (key_game); m_state = m_none; Sbar_Changed (); } break; case 'Y': case 'y': Key_SetDest (key_console); Host_Quit_f (); break; default: break; } } static void M_Quit_Draw (void) { int i, x, y, place, topy; qpic_t *p; if (wasInMenus) { m_state = m_quit_prevstate; m_recursiveDraw = true; M_Draw (); m_state = m_quit; } LinePos += host_frametime*1.75; if (LinePos > MaxLines + QUIT_SIZE + 2) { LinePos = 0; SoundPlayed = false; LineTimes++; if (LineTimes >= 2) { if (gameflags & GAME_PORTALS) { MaxLines = MAX_LINES2_MP; LineText = Credit2TextMP; CDAudio_Play (12, false); } else { MaxLines = MAX_LINES2; LineText = Credit2Text; } LineTxt2 = true; } } y = 12; M_DrawTextBox (0, 0, 38, 23); // the increment to the x offset is for properly centering the line M_Print (16 + (8 * 8), y, "Hexen II version " STRINGIFY(ENGINE_VERSION)); M_Print (16 + (9 * 8), y + 8, "by Raven Software"); M_PrintWhite (16 + (7 * 8), y + 16, "Hammer of Thyrion " HOT_VERSION_STR); M_PrintWhite (16 +(13 * 8), y + 24, "Source Port"); y += 40; if (LinePos > 55 && !SoundPlayed && LineTxt2) { S_LocalSound ("rj/steve.wav"); SoundPlayed = true; } topy = y; place = floor(LinePos); y -= floor((LinePos - place) * 8); for (i = 0; i < QUIT_SIZE; i++, y += 8) { if (i + place - QUIT_SIZE >= MaxLines) break; if (i + place < QUIT_SIZE) continue; if (LineText[i + place - QUIT_SIZE][0] == ' ') M_PrintWhite(24, y, LineText[i + place - QUIT_SIZE]); else M_Print(24, y, LineText[i + place - QUIT_SIZE]); } p = Draw_CachePic ("gfx/box_mm2.lmp"); x = 24; y = topy - 8; for (i = 4; i < 38; i++, x += 8) { M_DrawPic(x, y, p); // background at top for smooth scroll out M_DrawPic(x, y + (QUIT_SIZE*8), p); // draw at bottom for smooth scroll in } y += (QUIT_SIZE * 8) + 8; M_PrintWhite (16 + (10 * 8), y, "Press y to exit"); } //============================================================================= /* SERIAL CONFIG MENU */ #if defined(NET_USE_SERIAL) static int serialConfig_cursor; static const int serialConfig_cursor_table[] = { 80, 96, 112, 128, 144, 164 }; // { 48, 64, 80, 96, 112, 132 } #define NUM_SERIALCONFIG_CMDS 6 static const int ISA_uarts[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8}; static const int ISA_IRQs[4] = { 4, 3, 4, 3 }; static const int serialConfig_baudrate[6] = { 9600, 14400, 19200, 28800, 38400, 57600 }; static int serialConfig_comport; static int serialConfig_irq ; static int serialConfig_baud; static char serialConfig_phone[16]; static void M_Menu_SerialConfig_f (void) { int n; int port; int baudrate; qboolean useModem; Key_SetDest (key_menu); m_state = m_serialconfig; m_entersound = true; if (JoiningGame && SerialConfig) serialConfig_cursor = 4; else serialConfig_cursor = 5; (*GetComPortConfig) (0, &port, &serialConfig_irq, &baudrate, &useModem); // map uart's port to COMx for (n = 0; n < 4; n++) { if (ISA_uarts[n] == port) break; } if (n == 4) { n = 0; serialConfig_irq = 4; } serialConfig_comport = n + 1; // map baudrate to index for (n = 0; n < 6; n++) { if (serialConfig_baudrate[n] == baudrate) break; } if (n == 6) n = 5; serialConfig_baud = n; m_return_onerror = false; m_return_reason[0] = 0; } static void M_SerialConfig_Draw (void) { qpic_t *p; int basex; const char *startJoin; const char *directModem; p = Draw_CachePic ("gfx/menu/title4.lmp"); basex = (320 - p->width) / 2; ScrollTitle("gfx/menu/title4.lmp"); if (StartingGame) startJoin = "New Game"; else startJoin = "Join Game"; if (SerialConfig) directModem = "Modem"; else directModem = "Direct Connect"; M_Print (basex, serialConfig_cursor_table[0]-16, va ("%s - %s", startJoin, directModem)); basex += 8; M_Print (basex, serialConfig_cursor_table[0], "Port"); M_DrawTextBox (168, serialConfig_cursor_table[0]-8, 4, 1); M_Print (176, serialConfig_cursor_table[0], va("COM%u", serialConfig_comport)); M_Print (basex, serialConfig_cursor_table[1], "IRQ"); M_DrawTextBox (168, serialConfig_cursor_table[1]-8, 1, 1); M_Print (176, serialConfig_cursor_table[1], va("%u", serialConfig_irq)); M_Print (basex, serialConfig_cursor_table[2], "Baud"); M_DrawTextBox (168, serialConfig_cursor_table[2]-8, 5, 1); M_Print (176, serialConfig_cursor_table[2], va("%u", serialConfig_baudrate[serialConfig_baud])); if (SerialConfig) { M_Print (basex, serialConfig_cursor_table[3], "Modem Setup..."); if (JoiningGame) { M_Print (basex, serialConfig_cursor_table[4], "Phone number"); M_DrawTextBox (168, serialConfig_cursor_table[4]-8, 16, 1); M_Print (176, serialConfig_cursor_table[4], serialConfig_phone); } } if (JoiningGame) { M_DrawTextBox (basex, serialConfig_cursor_table[5]-8, 7, 1); M_Print (basex+8, serialConfig_cursor_table[5], "Connect"); } else { M_DrawTextBox (basex, serialConfig_cursor_table[5]-8, 2, 1); M_Print (basex+8, serialConfig_cursor_table[5], "OK"); } M_DrawCharacter (basex-8, serialConfig_cursor_table [serialConfig_cursor], 12+((int)(realtime*4)&1)); if (serialConfig_cursor == 4) M_DrawCharacter (176 + 8*strlen(serialConfig_phone), serialConfig_cursor_table [serialConfig_cursor], 10+((int)(realtime*4)&1)); if (*m_return_reason) M_PrintWhite (basex, 148, m_return_reason); } static void M_SerialConfig_Key (int key) { int l; switch (key) { case K_ESCAPE: M_Menu_Net_f (); break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); serialConfig_cursor--; if (serialConfig_cursor < 0) serialConfig_cursor = NUM_SERIALCONFIG_CMDS-1; break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); serialConfig_cursor++; if (serialConfig_cursor >= NUM_SERIALCONFIG_CMDS) serialConfig_cursor = 0; break; case K_LEFTARROW: if (serialConfig_cursor > 2) break; S_LocalSound ("raven/menu3.wav"); if (serialConfig_cursor == 0) { serialConfig_comport--; if (serialConfig_comport == 0) serialConfig_comport = 4; serialConfig_irq = ISA_IRQs[serialConfig_comport-1]; } else if (serialConfig_cursor == 1) { serialConfig_irq--; if (serialConfig_irq == 6) serialConfig_irq = 5; if (serialConfig_irq == 1) serialConfig_irq = 7; } else if (serialConfig_cursor == 2) { serialConfig_baud--; if (serialConfig_baud < 0) serialConfig_baud = 5; } break; case K_RIGHTARROW: if (serialConfig_cursor > 2) break; forward: S_LocalSound ("raven/menu3.wav"); if (serialConfig_cursor == 0) { serialConfig_comport++; if (serialConfig_comport > 4) serialConfig_comport = 1; serialConfig_irq = ISA_IRQs[serialConfig_comport-1]; } else if (serialConfig_cursor == 1) { serialConfig_irq++; if (serialConfig_irq == 6) serialConfig_irq = 7; if (serialConfig_irq == 8) serialConfig_irq = 2; } else if (serialConfig_cursor == 2) { serialConfig_baud++; if (serialConfig_baud > 5) serialConfig_baud = 0; } break; case K_ENTER: if (serialConfig_cursor < 3) goto forward; m_entersound = true; if (serialConfig_cursor == 3) { (*SetComPortConfig) (0, ISA_uarts[serialConfig_comport-1], serialConfig_irq, serialConfig_baudrate[serialConfig_baud], SerialConfig); M_Menu_ModemConfig_f (); break; } if (serialConfig_cursor == 4) { serialConfig_cursor = 5; break; } // serialConfig_cursor == 5 (OK/CONNECT) (*SetComPortConfig) (0, ISA_uarts[serialConfig_comport-1], serialConfig_irq, serialConfig_baudrate[serialConfig_baud], SerialConfig); M_ConfigureNetSubsystem (); if (StartingGame) { M_Menu_GameOptions_f (); break; } m_return_state = m_state; m_return_onerror = true; Key_SetDest (key_game); m_state = m_none; if (SerialConfig) Cbuf_AddText (va ("connect \"%s\"\n", serialConfig_phone)); else Cbuf_AddText ("connect\n"); break; case K_BACKSPACE: if (serialConfig_cursor == 4) { if (strlen(serialConfig_phone)) serialConfig_phone[strlen(serialConfig_phone)-1] = 0; } break; default: if (key < 32 || key > 127) break; if (serialConfig_cursor == 4) { l = strlen(serialConfig_phone); if (l < 15) { serialConfig_phone[l+1] = 0; serialConfig_phone[l] = key; } } } if (DirectConfig && (serialConfig_cursor == 3 || serialConfig_cursor == 4)) { if (key == K_UPARROW) serialConfig_cursor = 2; else serialConfig_cursor = 5; } if (SerialConfig && StartingGame && serialConfig_cursor == 4) { if (key == K_UPARROW) serialConfig_cursor = 3; else serialConfig_cursor = 5; } } //============================================================================= /* MODEM CONFIG MENU */ static int modemConfig_cursor; static const int modemConfig_cursor_table[] = { 64, 78, 108, 138, 172 }; // { 40, 56, 88, 120, 156 } #define NUM_MODEMCONFIG_CMDS 5 static char modemConfig_dialing; static char modemConfig_clear[16]; static char modemConfig_init[32]; static char modemConfig_hangup[16]; static void M_Menu_ModemConfig_f (void) { Key_SetDest (key_menu); m_state = m_modemconfig; m_entersound = true; (*GetModemConfig) (0, &modemConfig_dialing, modemConfig_clear, modemConfig_init, modemConfig_hangup); } static void M_ModemConfig_Draw (void) { qpic_t *p; int basex; p = Draw_CachePic ("gfx/menu/title4.lmp"); /* our p->width == 185: if we don't do the -8 here, drawing of * the init string textbox with 30 chars width shall fail with * a 'bad coordinates' message in Draw_TransPic() at 320x200. */ basex = (320 - p->width) / 2 - 8; ScrollTitle("gfx/menu/title4.lmp"); if (modemConfig_dialing == 'P') M_Print (basex, modemConfig_cursor_table[0], "Pulse Dialing"); else M_Print (basex, modemConfig_cursor_table[0], "Touch Tone Dialing"); M_Print (basex, modemConfig_cursor_table[1], "Clear"); M_DrawTextBox (basex, modemConfig_cursor_table[1]+4, 16, 1); M_Print (basex+8, modemConfig_cursor_table[1]+12, modemConfig_clear); if (modemConfig_cursor == 1) M_DrawCharacter (basex+8 + 8*strlen(modemConfig_clear), modemConfig_cursor_table[1]+12, 10+((int)(realtime*4)&1)); M_Print (basex, modemConfig_cursor_table[2], "Init"); M_DrawTextBox (basex, modemConfig_cursor_table[2]+4, 30, 1); M_Print (basex+8, modemConfig_cursor_table[2]+12, modemConfig_init); if (modemConfig_cursor == 2) M_DrawCharacter (basex+8 + 8*strlen(modemConfig_init), modemConfig_cursor_table[2]+12, 10+((int)(realtime*4)&1)); M_Print (basex, modemConfig_cursor_table[3], "Hangup"); M_DrawTextBox (basex, modemConfig_cursor_table[3]+4, 16, 1); M_Print (basex+8, modemConfig_cursor_table[3]+12, modemConfig_hangup); if (modemConfig_cursor == 3) M_DrawCharacter (basex+8 + 8*strlen(modemConfig_hangup), modemConfig_cursor_table[3]+12, 10+((int)(realtime*4)&1)); M_DrawTextBox (basex, modemConfig_cursor_table[4]-8, 2, 1); M_Print (basex+8, modemConfig_cursor_table[4], "OK"); M_DrawCharacter (basex-8, modemConfig_cursor_table [modemConfig_cursor], 12+((int)(realtime*4)&1)); } static void M_ModemConfig_Key (int key) { int l; switch (key) { case K_ESCAPE: M_Menu_SerialConfig_f (); break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); modemConfig_cursor--; if (modemConfig_cursor < 0) modemConfig_cursor = NUM_MODEMCONFIG_CMDS-1; break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); modemConfig_cursor++; if (modemConfig_cursor >= NUM_MODEMCONFIG_CMDS) modemConfig_cursor = 0; break; case K_LEFTARROW: case K_RIGHTARROW: if (modemConfig_cursor == 0) { if (modemConfig_dialing == 'P') modemConfig_dialing = 'T'; else modemConfig_dialing = 'P'; S_LocalSound ("raven/menu1.wav"); } break; case K_ENTER: if (modemConfig_cursor == 0) { if (modemConfig_dialing == 'P') modemConfig_dialing = 'T'; else modemConfig_dialing = 'P'; m_entersound = true; } else if (modemConfig_cursor == 4) { (*SetModemConfig) (0, va ("%c", modemConfig_dialing), modemConfig_clear, modemConfig_init, modemConfig_hangup); m_entersound = true; M_Menu_SerialConfig_f (); } break; case K_BACKSPACE: if (modemConfig_cursor == 1) { l = strlen(modemConfig_clear); if (l) modemConfig_clear[l-1] = 0; } else if (modemConfig_cursor == 2) { l = strlen(modemConfig_init); if (l) modemConfig_init[l-1] = 0; } else if (modemConfig_cursor == 3) { l = strlen(modemConfig_hangup); if (l) modemConfig_hangup[l-1] = 0; } break; default: if (key < 32 || key > 127) break; if (modemConfig_cursor == 1) { l = strlen(modemConfig_clear); if (l < 15) { modemConfig_clear[l+1] = 0; modemConfig_clear[l] = key; } } else if (modemConfig_cursor == 2) { l = strlen(modemConfig_init); if (l < 29) { modemConfig_init[l+1] = 0; modemConfig_init[l] = key; } } else if (modemConfig_cursor == 3) { l = strlen(modemConfig_hangup); if (l < 15) { modemConfig_hangup[l+1] = 0; modemConfig_hangup[l] = key; } } } } #endif /* NET_USE_SERIAL */ //============================================================================= /* LAN CONFIG MENU */ static int lanConfig_cursor = -1; static const int lanConfig_cursor_table[] = {100, 120, 140, 172}; #define NUM_LANCONFIG_CMDS 4 static int lanConfig_port; static char lanConfig_portname[6]; static char lanConfig_joinname[30]; static void M_Menu_LanConfig_f (void) { Key_SetDest (key_menu); m_state = m_lanconfig; m_entersound = true; if (lanConfig_cursor == -1) { if (JoiningGame && TCPIPConfig) lanConfig_cursor = 2; else lanConfig_cursor = 1; } if (StartingGame && lanConfig_cursor >= 2) lanConfig_cursor = 1; lanConfig_port = DEFAULTnet_hostport; q_snprintf(lanConfig_portname, sizeof(lanConfig_portname), "%d", lanConfig_port); m_return_onerror = false; m_return_reason[0] = 0; setup_class = cl_playerclass.integer; if (setup_class < 1 || setup_class > MAX_PLAYER_CLASS) setup_class = MAX_PLAYER_CLASS; #if ENABLE_OLD_DEMO if (gameflags & GAME_OLD_DEMO) { if (setup_class != CLASS_PALADIN && setup_class != CLASS_THEIF) setup_class = CLASS_PALADIN; } else #endif /* OLD_DEMO */ if (!(gameflags & GAME_PORTALS)) { if (setup_class > MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES) setup_class = MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES; } setup_class--; } static void M_LanConfig_Draw (void) { int basex; const char *startJoin; const char *protocol; ScrollTitle("gfx/menu/title4.lmp"); basex = 48; if (StartingGame) startJoin = "New Game"; else startJoin = "Join Game"; if (IPXConfig) protocol = "IPX"; else protocol = "TCP/IP"; M_Print (basex, 60, va ("%s - %s", startJoin, protocol)); basex += 8; M_Print (basex, 80, "Address:"); if (IPXConfig) M_Print (basex+9*8, 80, my_ipx_address); else M_Print (basex+9*8, 80, my_tcpip_address); M_Print (basex, lanConfig_cursor_table[0], "Port"); M_DrawTextBox (basex+8*8, lanConfig_cursor_table[0]-8, 6, 1); M_Print (basex+9*8, lanConfig_cursor_table[0], lanConfig_portname); if (JoiningGame) { M_Print (basex, lanConfig_cursor_table[1], "Class:"); M_Print (basex+8*7, lanConfig_cursor_table[1], ClassNames[setup_class]); M_Print (basex, lanConfig_cursor_table[2], "Search for local games..."); M_Print (basex, 156, "Join game at:"); M_DrawTextBox (basex, lanConfig_cursor_table[3]-8, 30, 1); M_Print (basex+8, lanConfig_cursor_table[3], lanConfig_joinname); } else { M_DrawTextBox (basex, lanConfig_cursor_table[1]-8, 2, 1); M_Print (basex+8, lanConfig_cursor_table[1], "OK"); } M_DrawCharacter (basex-8, lanConfig_cursor_table [lanConfig_cursor], 12+((int)(realtime*4)&1)); if (lanConfig_cursor == 0) M_DrawCharacter (basex+9*8 + 8*strlen(lanConfig_portname), lanConfig_cursor_table [0], 10+((int)(realtime*4)&1)); if (lanConfig_cursor == 3) M_DrawCharacter (basex+8 + 8*strlen(lanConfig_joinname), lanConfig_cursor_table [3], 10+((int)(realtime*4)&1)); if (*m_return_reason) M_PrintWhite (basex, 192, m_return_reason); } static void M_LanConfig_Key (int key) { int l; switch (key) { case K_ESCAPE: M_Menu_Net_f (); break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); lanConfig_cursor--; if (JoiningGame) { if (lanConfig_cursor < 0) lanConfig_cursor = NUM_LANCONFIG_CMDS-1; } else { if (lanConfig_cursor < 0) lanConfig_cursor = NUM_LANCONFIG_CMDS-2; } break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); lanConfig_cursor++; if (lanConfig_cursor >= NUM_LANCONFIG_CMDS) lanConfig_cursor = 0; break; case K_ENTER: if ((JoiningGame && lanConfig_cursor <= 1) || (!JoiningGame && lanConfig_cursor == 0)) break; m_entersound = true; if (JoiningGame) Cbuf_AddText ( va ("playerclass %d\n", setup_class+1) ); M_ConfigureNetSubsystem (); if ((JoiningGame && lanConfig_cursor == 2) || (!JoiningGame && lanConfig_cursor == 1)) { if (StartingGame) { M_Menu_GameOptions_f (); break; } M_Menu_Search_f(); break; } if (lanConfig_cursor == 3) { m_return_state = m_state; m_return_onerror = true; Key_SetDest (key_game); m_state = m_none; Cbuf_AddText ( va ("connect \"%s\"\n", lanConfig_joinname) ); break; } break; case K_BACKSPACE: if (lanConfig_cursor == 0) { l = strlen(lanConfig_portname); if (l) lanConfig_portname[l-1] = 0; } else if (lanConfig_cursor == 3) { l = strlen(lanConfig_joinname); if (l) lanConfig_joinname[l-1] = 0; } break; case K_LEFTARROW: if (lanConfig_cursor != 1 || !JoiningGame) break; S_LocalSound ("raven/menu3.wav"); #if ENABLE_OLD_DEMO if (gameflags & GAME_OLD_DEMO) { setup_class = (setup_class == CLASS_PALADIN-1) ? CLASS_THEIF-1 : CLASS_PALADIN-1; break; } #endif /* OLD_DEMO */ setup_class--; if (setup_class < 0) setup_class = MAX_PLAYER_CLASS -1; if (setup_class > MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES - 1 && !(gameflags & GAME_PORTALS)) setup_class = MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES -1; break; case K_RIGHTARROW: if (lanConfig_cursor != 1 || !JoiningGame) break; S_LocalSound ("raven/menu3.wav"); #if ENABLE_OLD_DEMO if (gameflags & GAME_OLD_DEMO) { setup_class = (setup_class == CLASS_PALADIN-1) ? CLASS_THEIF-1 : CLASS_PALADIN-1; break; } #endif /* OLD_DEMO */ setup_class++; if (setup_class > MAX_PLAYER_CLASS - 1) setup_class = 0; if (setup_class > MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES - 1 && !(gameflags & GAME_PORTALS)) setup_class = 0; break; default: if (key < 32 || key > 127) break; if (lanConfig_cursor == 3) { l = strlen(lanConfig_joinname); if (l < 29) { lanConfig_joinname[l+1] = 0; lanConfig_joinname[l] = key; } } if (key < '0' || key > '9') break; if (lanConfig_cursor == 0) { l = strlen(lanConfig_portname); if (l < 5) { lanConfig_portname[l+1] = 0; lanConfig_portname[l] = key; } } } if (StartingGame && lanConfig_cursor == 2) { if (key == K_UPARROW) lanConfig_cursor = 1; else lanConfig_cursor = 0; } l = atoi(lanConfig_portname); if (l > 65535) l = lanConfig_port; else lanConfig_port = l; q_snprintf(lanConfig_portname, sizeof(lanConfig_portname), "%d", lanConfig_port); } //============================================================================= /* GAME OPTIONS MENU */ static const struct { const char *name; const char *description; } levels[] = { {"demo1", "Blackmarsh"}, // 0 {"demo2", "Barbican"}, // 1 {"ravdm1", "Deathmatch 1"}, // 2 {"demo1","Blackmarsh"}, // 3 {"demo2","Barbican"}, // 4 {"demo3","The Mill"}, // 5 {"village1","King's Court"}, // 6 {"village2","Inner Courtyard"}, // 7 {"village3","Stables"}, // 8 {"village4","Palace Entrance"}, // 9 {"village5","The Forgotten Chapel"}, // 10 {"rider1a","Famine's Domain"}, // 11 {"meso2","Plaza of the Sun"}, // 12 {"meso1","The Palace of Columns"}, // 13 {"meso3","Square of the Stream"}, // 14 {"meso4","Tomb of the High Priest"}, // 15 {"meso5","Obelisk of the Moon"}, // 16 {"meso6","Court of 1000 Warriors"}, // 17 {"meso8","Bridge of Stars"}, // 18 {"meso9","Well of Souls"}, // 19 {"egypt1","Temple of Horus"}, // 20 {"egypt2","Ancient Temple of Nefertum"}, // 21 {"egypt3","Temple of Nefertum"}, // 22 {"egypt4","Palace of the Pharaoh"}, // 23 {"egypt5","Pyramid of Anubis"}, // 24 {"egypt6","Temple of Light"}, // 25 {"egypt7","Shrine of Naos"}, // 26 {"rider2c","Pestilence's Lair"}, // 27 {"romeric1","The Hall of Heroes"}, // 28 {"romeric2","Gardens of Athena"}, // 29 {"romeric3","Forum of Zeus"}, // 30 {"romeric4","Baths of Demetrius"}, // 31 {"romeric5","Temple of Mars"}, // 32 {"romeric6","Coliseum of War"}, // 33 {"romeric7","Reflecting Pool"}, // 34 {"cath","Cathedral"}, // 35 {"tower","Tower of the Dark Mage"}, // 36 {"castle4","The Underhalls"}, // 37 {"castle5","Eidolon's Ordeal"}, // 38 {"eidolon","Eidolon's Lair"}, // 39 {"ravdm1","Atrium of Immolation"}, // 40 {"ravdm2","Total Carnage"}, // 41 {"ravdm3","Reckless Abandon"}, // 42 {"ravdm4","Temple of RA"}, // 43 {"ravdm5","Tom Foolery"}, // 44 {"ravdm1", "Deathmatch 1"}, // 45 //OEM {"demo1","Blackmarsh"}, // 46 {"demo2","Barbican"}, // 47 {"demo3","The Mill"}, // 48 {"village1","King's Court"}, // 49 {"village2","Inner Courtyard"}, // 50 {"village3","Stables"}, // 51 {"village4","Palace Entrance"}, // 52 {"village5","The Forgotten Chapel"}, // 53 {"rider1a","Famine's Domain"}, // 54 //Mission Pack {"keep1", "Eidolon's Lair"}, // 55 {"keep2", "Village of Turnabel"}, // 56 {"keep3", "Duke's Keep"}, // 57 {"keep4", "The Catacombs"}, // 58 {"keep5", "Hall of the Dead"}, // 59 {"tibet1", "Tulku"}, // 60 {"tibet2", "Ice Caverns"}, // 61 {"tibet3", "The False Temple"}, // 62 {"tibet4", "Courtyards of Tsok"}, // 63 {"tibet5", "Temple of Kalachakra"}, // 64 {"tibet6", "Temple of Bardo"}, // 65 {"tibet7", "Temple of Phurbu"}, // 66 {"tibet8", "Palace of Emperor Egg Chen"}, // 67 {"tibet9", "Palace Inner Chambers"}, // 68 {"tibet10", "The Inner Sanctum of Praevus"},// 69 }; static const struct { const char *description; int firstLevel; int levels; } episodes[] = { // Demo {"Demo", 0, 2}, {"Demo Deathmatch", 2, 1}, // Registered {"Village", 3, 9}, {"Meso", 12, 8}, {"Egypt", 20, 8}, {"Romeric", 28, 7}, {"Cathedral", 35, 5}, {"MISSION PACK", 55, 15}, {"Deathmatch", 40, 5}, // OEM {"Village", 46, 9}, {"Deathmatch", 45, 1}, }; #define OEM_START 9 #define REG_START 2 #define MP_START 7 #define DM_START 8 static int startepisode; static int startlevel; static int maxplayers; //static qboolean m_serverInfoMessage = false; //static double m_serverInfoMessageTime; static const int gameoptions_cursor_table[] = {40, 56, 64, 72, 80, 88, 96, 104, 112, 128, 136}; #define NUM_GAMEOPTIONS 11 static int gameoptions_cursor; static void M_Menu_GameOptions_f (void) { Key_SetDest (key_menu); m_state = m_gameoptions; m_entersound = true; if (maxplayers == 0) maxplayers = svs.maxclients; if (maxplayers < 2) maxplayers = svs.maxclientslimit; setup_class = cl_playerclass.integer; if (setup_class < 1 || setup_class > MAX_PLAYER_CLASS) setup_class = MAX_PLAYER_CLASS; #if ENABLE_OLD_DEMO if (gameflags & GAME_OLD_DEMO) { if (setup_class != CLASS_PALADIN && setup_class != CLASS_THEIF) setup_class = CLASS_PALADIN; } else #endif /* OLD_DEMO */ if (!(gameflags & GAME_PORTALS)) { if (setup_class > MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES) setup_class = MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES; } setup_class--; if (oem.integer) { if (startepisode < OEM_START || startepisode > OEM_START+1) startepisode = OEM_START; if (coop.integer) startepisode = OEM_START; } else if (registered.integer) { if (startepisode < REG_START || startepisode >= OEM_START) startepisode = REG_START; else if (startepisode == MP_START && !(gameflags & GAME_PORTALS)) startepisode = REG_START; if (coop.integer && startepisode == DM_START) startepisode = REG_START; } else // demo { if (startepisode < 0 || startepisode > 1) startepisode = 0; if (coop.integer) startepisode = 0; } if (coop.integer) { startlevel = 0; if (gameoptions_cursor >= NUM_GAMEOPTIONS-1) gameoptions_cursor = 0; } } static void M_GameOptions_Draw (void) { ScrollTitle("gfx/menu/title4.lmp"); M_DrawTextBox (152+8, 60, 10, 1); M_Print (160+8, 68, "begin game"); // we use 17 character option titles. the second increment // to the x offset is: (17 - strlen(option_title)) * 8 M_Print (0+8 + 6*8, 84, "Max players"); M_Print (160+8, 84, va("%i", maxplayers) ); M_Print (0+8 + 8*8, 92, "Game Type"); if (coop.integer) M_Print (160+8, 92, "Cooperative"); else M_Print (160+8, 92, "Deathmatch"); M_Print (0+8 + 9*8, 100, "Teamplay"); { const char *msg; switch (teamplay.integer) { case 1: msg = "No Friendly Fire"; break; case 2: msg = "Friendly Fire"; break; default: msg = "Off"; break; } M_Print (160+8, 100, msg); } M_Print (0+8 + 12*8, 108, "Class"); M_Print (160+8, 108, ClassNames[setup_class]); M_Print (0+8 + 7*8, 116, "Difficulty"); M_Print (160+8, 116, DiffNames[setup_class][skill.integer]); M_Print (0+8 + 7*8, 124, "Frag Limit"); if (fraglimit.integer == 0) M_Print (160+8, 124, "none"); else M_Print (160+8, 124, va("%i frags", fraglimit.integer)); M_Print (0+8 + 7*8, 132, "Time Limit"); if (timelimit.integer == 0) M_Print (160+8, 132, "none"); else M_Print (160+8, 132, va("%i minutes", timelimit.integer)); M_Print (0+8 + 5*8, 140, "Random Class"); if (randomclass.integer) M_Print (160+8, 140, "on"); else M_Print (160+8, 140, "off"); M_Print (0+8 + 10*8, 156, "Episode"); M_Print (160+8, 156, episodes[startepisode].description); M_Print (0+8 + 12*8, 164, "Level"); M_Print (160+8, 164, levels[episodes[startepisode].firstLevel + startlevel].name); M_Print (96, 180, levels[episodes[startepisode].firstLevel + startlevel].description); // line cursor M_DrawCharacter (172-16, gameoptions_cursor_table[gameoptions_cursor]+28, 12+((int)(realtime*4)&1)); /* rjr if (m_serverInfoMessage) { if ((realtime - m_serverInfoMessageTime) < 5.0) { x = (320-26*8)/2; M_DrawTextBox (x, 138, 24, 4); x += 8; M_Print (x, 146, " More than 4 players "); M_Print (x, 154, " requires using command "); M_Print (x, 162, "line parameters; please "); M_Print (x, 170, " see techinfo.txt. "); } else { m_serverInfoMessage = false; } }*/ } static void M_NetStart_Change (int dir) { int val; switch (gameoptions_cursor) { case 1: maxplayers += dir; if (maxplayers > svs.maxclientslimit) { maxplayers = svs.maxclientslimit; // m_serverInfoMessage = true; // m_serverInfoMessageTime = realtime; } if (maxplayers < 2) maxplayers = 2; break; case 2: if (coop.integer) { Cvar_Set ("coop", "0"); break; } Cvar_Set ("coop", "1"); startlevel = 0; if (startepisode == 1) startepisode = 0; else if (startepisode == DM_START) startepisode = REG_START; else if (startepisode == OEM_START+1) startepisode = OEM_START; break; case 3: val = teamplay.integer + dir; if (val > 2) val = 0; else if (val < 0) val = 2; Cvar_SetValue ("teamplay", val); break; case 4: #if ENABLE_OLD_DEMO if (gameflags & GAME_OLD_DEMO) { setup_class = (setup_class == CLASS_PALADIN-1) ? CLASS_THEIF-1 : CLASS_PALADIN-1; break; } #endif /* OLD_DEMO */ setup_class += dir; if (setup_class < 0) setup_class = MAX_PLAYER_CLASS - 1; else if(setup_class > MAX_PLAYER_CLASS - 1) setup_class = 0; if (setup_class > MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES - 1 && !(gameflags & GAME_PORTALS)) setup_class = (dir > 0)? 0 : MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES - 1; break; case 5: val = skill.integer + dir; if (val > 3) val = 0; else if (val < 0) val = 3; Cvar_SetValue ("skill", val); break; case 6: val = fraglimit.integer + dir*10; if (val > 100) val = 0; else if (val < 0) val = 100; Cvar_SetValue ("fraglimit", val); break; case 7: val = timelimit.integer + dir*5; if (val > 60) val = 0; else if (val < 0) val = 60; Cvar_SetValue ("timelimit", val); break; case 8: Cvar_Set ("randomclass", randomclass.integer ? "0" : "1"); break; case 9: if (registered.integer) { startepisode += dir; startlevel = 0; if (startepisode > DM_START) startepisode = REG_START; else { if (startepisode == MP_START && !(gameflags & GAME_PORTALS)) startepisode += dir; if (coop.integer && startepisode == DM_START) startepisode = (dir > 0) ? REG_START : ((gameflags & GAME_PORTALS) ? MP_START : MP_START-1); if (startepisode < REG_START) startepisode = (coop.integer) ? ((gameflags & GAME_PORTALS) ? MP_START : MP_START-1) : DM_START; } } else if (oem.integer) { if (!coop.integer) { startepisode = (startepisode != OEM_START) ? OEM_START : OEM_START+1; startlevel = 0; } } else // demo version { if (!coop.integer) { startepisode = (startepisode != 0) ? 0 : 1; startlevel = 0; } } break; case 10: if (coop.integer) { startlevel = 0; break; } startlevel += dir; if (startlevel < 0) startlevel = episodes[startepisode].levels - 1; else if (startlevel >= episodes[startepisode].levels) startlevel = 0; break; } } static void M_GameOptions_Key (int key) { switch (key) { case K_ESCAPE: M_Menu_Net_f (); break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); gameoptions_cursor--; if (gameoptions_cursor < 0) { gameoptions_cursor = NUM_GAMEOPTIONS-1; if (coop.integer) gameoptions_cursor--; } break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); gameoptions_cursor++; if (coop.integer) { if (gameoptions_cursor >= NUM_GAMEOPTIONS-1) gameoptions_cursor = 0; } else { if (gameoptions_cursor >= NUM_GAMEOPTIONS) gameoptions_cursor = 0; } break; case K_LEFTARROW: if (gameoptions_cursor == 0) break; S_LocalSound ("raven/menu3.wav"); M_NetStart_Change (-1); break; case K_RIGHTARROW: if (gameoptions_cursor == 0) break; S_LocalSound ("raven/menu3.wav"); M_NetStart_Change (1); break; case K_ENTER: S_LocalSound ("raven/menu2.wav"); if (gameoptions_cursor == 0) { if (sv.active) Cbuf_AddText ("disconnect\n"); Cbuf_AddText ( va ("playerclass %d\n", setup_class+1) ); Cbuf_AddText ("listen 0\n"); // so host_netport will be re-examined Cbuf_AddText ( va ("maxplayers %d\n", maxplayers) ); SCR_BeginLoadingPlaque (); Cbuf_AddText ( va ("map %s\n", levels[episodes[startepisode].firstLevel + startlevel].name) ); return; } M_NetStart_Change (1); break; } } //============================================================================= /* SEARCH MENU */ static qboolean searchComplete = false; static double searchCompleteTime; static void M_Menu_Search_f (void) { Key_SetDest (key_menu); m_state = m_search; m_entersound = false; slistSilent = true; slistLocal = false; searchComplete = false; NET_Slist_f(); } static void M_Search_Draw (void) { int x; ScrollTitle("gfx/menu/title4.lmp"); x = (320/2) - ((12*8)/2) + 4; M_DrawTextBox (x-8, 60, 12, 1); M_Print (x, 68, "Searching..."); if (slistInProgress) { NET_Poll(); return; } if (! searchComplete) { searchComplete = true; searchCompleteTime = realtime; } if (hostCacheCount) { M_Menu_ServerList_f (); return; } M_PrintWhite ((320/2) - ((22*8)/2), 92, "No Hexen II servers found"); if ((realtime - searchCompleteTime) < 3.0) return; M_Menu_LanConfig_f (); } static void M_Search_Key (int key) { } //============================================================================= /* SLIST MENU */ static int slist_cursor; static qboolean slist_sorted; static void M_Menu_ServerList_f (void) { Key_SetDest (key_menu); m_state = m_slist; m_entersound = true; slist_cursor = 0; m_return_onerror = false; m_return_reason[0] = 0; slist_sorted = false; } static void M_ServerList_Draw (void) { int n; if (!slist_sorted) { slist_sorted = true; NET_SlistSort (); } ScrollTitle("gfx/menu/title4.lmp"); for (n = 0; n < hostCacheCount; n++) M_Print (16, 60 + 8*n, NET_SlistPrintServer (n)); M_DrawCharacter (0, 60 + slist_cursor*8, 12+((int)(realtime*4)&1)); if (*m_return_reason) M_PrintWhite (16, 176, m_return_reason); } static void M_ServerList_Key (int k) { switch (k) { case K_ESCAPE: M_Menu_LanConfig_f (); break; case K_SPACE: M_Menu_Search_f (); break; case K_UPARROW: case K_LEFTARROW: S_LocalSound ("raven/menu1.wav"); slist_cursor--; if (slist_cursor < 0) slist_cursor = hostCacheCount - 1; break; case K_DOWNARROW: case K_RIGHTARROW: S_LocalSound ("raven/menu1.wav"); slist_cursor++; if (slist_cursor >= hostCacheCount) slist_cursor = 0; break; case K_ENTER: S_LocalSound ("raven/menu2.wav"); m_return_state = m_state; m_return_onerror = true; slist_sorted = false; Key_SetDest (key_game); m_state = m_none; Cbuf_AddText ( va ("connect \"%s\"\n", NET_SlistPrintServerName(slist_cursor)) ); break; default: break; } } //============================================================================= /* Menu Subsystem */ void M_Init (void) { char *ptr; ptr = (char *) FS_LoadTempFile (BIGCHAR_WIDTH_FILE, NULL); if (ptr == NULL) M_BuildBigCharWidth(); else { if (fs_filesize == (long) sizeof(BigCharWidth)) memcpy (BigCharWidth, ptr, sizeof(BigCharWidth)); else { Con_Printf ("Unexpected file size (%ld) for %s\n", fs_filesize, BIGCHAR_WIDTH_FILE); M_BuildBigCharWidth(); } } Cmd_AddCommand ("togglemenu", M_ToggleMenu_f); Cmd_AddCommand ("menu_main", M_Menu_Main_f); Cmd_AddCommand ("menu_singleplayer", M_Menu_SinglePlayer_f); Cmd_AddCommand ("menu_load", M_Menu_Load_f); Cmd_AddCommand ("menu_save", M_Menu_Save_f); Cmd_AddCommand ("menu_multiplayer", M_Menu_MultiPlayer_f); Cmd_AddCommand ("menu_setup", M_Menu_Setup_f); Cmd_AddCommand ("menu_options", M_Menu_Options_f); Cmd_AddCommand ("menu_keys", M_Menu_Keys_f); Cmd_AddCommand ("menu_video", M_Menu_Video_f); Cmd_AddCommand ("help", M_Menu_Help_f); Cmd_AddCommand ("menu_quit", M_Menu_Quit_f); Cmd_AddCommand ("menu_class", M_Menu_Class2_f); memset (old_bgmtype, 0, sizeof(old_bgmtype)); } void M_Draw (void) { if (m_state == m_none || !(Key_GetDest() & key_menu)) return; if (!m_recursiveDraw) { scr_copyeverything = 1; if (scr_con_current) { Draw_ConsoleBackground (vid.height); VID_UnlockBuffer (); S_ExtraUpdate (); VID_LockBuffer (); } else { Draw_FadeScreen (); if (scr_viewsize.integer < 110) scr_fullupdate = 0; } } else { m_recursiveDraw = false; } switch (m_state) { case m_none: break; case m_main: M_Main_Draw (); break; case m_singleplayer: M_SinglePlayer_Draw (); break; case m_difficulty: M_Difficulty_Draw (); break; case m_class: M_Class_Draw (); break; case m_load: case m_mload: M_Load_Draw (); break; case m_save: case m_msave: M_Save_Draw (); break; case m_multiplayer: M_MultiPlayer_Draw (); break; case m_setup: M_Setup_Draw (); break; case m_net: M_Net_Draw (); break; case m_options: M_Options_Draw (); break; #ifdef GLQUAKE case m_opengl: M_OpenGL_Draw (); break; #endif case m_keys: M_Keys_Draw (); break; case m_video: M_Video_Draw (); break; case m_help: M_Help_Draw (); break; case m_quit: M_Quit_Draw (); break; #if defined(NET_USE_SERIAL) case m_serialconfig: M_SerialConfig_Draw (); break; case m_modemconfig: M_ModemConfig_Draw (); break; #endif case m_lanconfig: M_LanConfig_Draw (); break; case m_gameoptions: M_GameOptions_Draw (); break; case m_search: M_Search_Draw (); break; case m_slist: M_ServerList_Draw (); break; } if (m_entersound) { S_LocalSound ("raven/menu2.wav"); m_entersound = false; } VID_UnlockBuffer (); S_ExtraUpdate (); VID_LockBuffer (); } void M_Keybind (int key) { char cmd[80]; S_LocalSound ("raven/menu1.wav"); if (key != K_ESCAPE && key != '`') { q_snprintf (cmd, sizeof(cmd), "bind \"%s\" \"%s\"\n", Key_KeynumToString (key), bindnames[keys_cursor][0]); Cbuf_InsertText (cmd); } Key_SetDest (key_menu); } void M_Keydown (int key) { switch (m_state) { case m_none: return; case m_main: M_Main_Key (key); return; case m_singleplayer: M_SinglePlayer_Key (key); return; case m_difficulty: M_Difficulty_Key (key); return; case m_class: M_Class_Key (key); return; case m_load: M_Load_Key (key); return; case m_save: M_Save_Key (key); return; case m_mload: M_MLoad_Key (key); return; case m_msave: M_MSave_Key (key); return; case m_multiplayer: M_MultiPlayer_Key (key); return; case m_setup: M_Setup_Key (key); return; case m_net: M_Net_Key (key); return; case m_options: M_Options_Key (key); return; #ifdef GLQUAKE case m_opengl: M_OpenGL_Key (key); break; #endif case m_keys: M_Keys_Key (key); return; case m_video: M_Video_Key (key); return; case m_help: M_Help_Key (key); return; case m_quit: M_Quit_Key (key); return; #if defined(NET_USE_SERIAL) case m_serialconfig: M_SerialConfig_Key (key); return; case m_modemconfig: M_ModemConfig_Key (key); return; #endif case m_lanconfig: M_LanConfig_Key (key); return; case m_gameoptions: M_GameOptions_Key (key); return; case m_search: M_Search_Key (key); break; case m_slist: M_ServerList_Key (key); return; } } static void M_ConfigureNetSubsystem(void) { // enable/disable net systems to match desired config Cbuf_AddText ("stopdemo\n"); #if defined(NET_USE_SERIAL) if (SerialConfig || DirectConfig) { Cbuf_AddText ("com1 enable\n"); } #endif if (IPXConfig || TCPIPConfig) net_hostport = lanConfig_port; } static void BGM_RestartMusic (void) { // called after exitting the menus and changing the music type // this is pretty crude, but doen't seem to break anything S.A if (q_strcasecmp(bgmtype.string,"midi") == 0) { CDAudio_Stop(); BGM_PlayMIDIorMusic(cl.midi_name); } else if (q_strcasecmp(bgmtype.string,"cd") == 0) { BGM_Stop(); CDAudio_Play ((byte)cl.cdtrack, true); } else { CDAudio_Stop(); BGM_Stop(); } } engine/hexen2/menu.h000066400000000000000000000036571444734033100146470ustar00rootroot00000000000000/* * menu.h * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_MENU_H #define __HX2_MENU_H enum m_state_e { m_none = 0, m_main, m_singleplayer, m_load, m_save, m_multiplayer, m_setup, m_net, m_options, #ifdef GLQUAKE m_opengl, #endif m_video, m_keys, m_help, m_quit, /* need net.h included before menu.h to see this definition's existance */ #if defined(NET_USE_SERIAL) m_serialconfig, m_modemconfig, #endif m_lanconfig, m_gameoptions, m_search, m_slist, m_class, m_difficulty, m_mload, m_msave }; extern enum m_state_e m_state; extern enum m_state_e m_return_state; /* menus */ void M_Init (void); void M_Keydown (int key); void M_Keybind (int key); void M_ToggleMenu_f (void); void M_Menu_Main_f (void); void M_Menu_Options_f (void); void M_Menu_Quit_f (void); void M_Print (int cx, int cy, const char *str); void M_PrintWhite (int cx, int cy, const char *str); void M_Draw (void); void M_DrawCharacter (int cx, int line, int num); void M_DrawPic (int x, int y, qpic_t *pic); void M_DrawTransPic (int x, int y, qpic_t *pic); void M_DrawTextBox (int x, int y, int width, int lines); void M_DrawCheckbox (int x, int y, int on); void ScrollTitle (const char *name); #endif /* __HX2_MENU_H */ engine/hexen2/net.h000066400000000000000000000102571444734033100144630ustar00rootroot00000000000000/* * net.h -- quake's interface to the networking layer * network functions and data, common to the whole engine * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_NET_H #define __HX2_NET_H #define NET_NAMELEN 64 #define NET_MAXMESSAGE 16384 extern int DEFAULTnet_hostport; extern int net_hostport; extern cvar_t hostname; extern double net_time; extern sizebuf_t net_message; extern int net_activeconnections; void NET_Init (void); void NET_Shutdown (void); struct qsocket_s *NET_CheckNewConnections (void); // returns a new connection number if there is one pending, else -1 struct qsocket_s *NET_Connect (const char *host); // called by client to connect to a host. Returns -1 if not able to double NET_QSocketGetTime (const struct qsocket_s *sock); const char *NET_QSocketGetAddressString (const struct qsocket_s *sock); qboolean NET_CanSendMessage (struct qsocket_s *sock); // Returns true or false if the given qsocket can currently accept a // message to be transmitted. int NET_GetMessage (struct qsocket_s *sock); // returns data in net_message sizebuf // returns 0 if no data is waiting // returns 1 if a message was received // returns 2 if an unreliable message was received // returns -1 if the connection died int NET_SendMessage (struct qsocket_s *sock, sizebuf_t *data); int NET_SendUnreliableMessage (struct qsocket_s *sock, sizebuf_t *data); // returns 0 if the message connot be delivered reliably, but the connection // is still considered valid // returns 1 if the message was sent properly // returns -1 if the connection died int NET_SendToAll(sizebuf_t *data, double blocktime); // This is a reliable *blocking* send to all attached clients. void NET_Close (struct qsocket_s *sock); // if a dead connection is returned by a get or send function, this function // should be called when it is convenient // Server calls when a client is kicked off for a game related misbehavior // like an illegal protocal conversation. Client calls when disconnecting // from a server. // A netcon_t number will not be reused until this function is called for it void NET_Poll (void); // Server list related globals: extern qboolean slistInProgress; extern qboolean slistSilent; extern qboolean slistLocal; extern int hostCacheCount; void NET_Slist_f (void); void NET_SlistSort (void); const char *NET_SlistPrintServer (int n); const char *NET_SlistPrintServerName (int n); /* FIXME: driver related, but public: */ extern qboolean serialAvailable; extern qboolean ipxAvailable; extern qboolean tcpipAvailable; extern char my_ipx_address[NET_NAMELEN]; extern char my_tcpip_address[NET_NAMELEN]; #undef NET_USE_SERIAL /* allow the serial driver only for DOS builds * and only when USE_SERIAL is defined: */ #if defined(PLATFORM_DOS) && defined(USE_SERIAL) #define NET_USE_SERIAL 1 #endif #undef NO_LOOP_DRIVER /* The dedicated server application (h2ded) * does not need the Loopback driver: */ #if defined(SERVERONLY) #define NO_LOOP_DRIVER 1 #endif /* SERVERONLY */ #if defined(NET_USE_SERIAL) extern void (*GetComPortConfig) (int portNumber, int *port, int *irq, int *baud, qboolean *useModem); extern void (*SetComPortConfig) (int portNumber, int port, int irq, int baud, qboolean useModem); extern void (*GetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup); extern void (*SetModemConfig) (int portNumber, const char *dialType, const char *clear, const char *init, const char *hangup); #endif /* NET_USE_SERIAL */ #endif /* __HX2_NET_H */ engine/hexen2/net_bsd.c000066400000000000000000000043621444734033100153060ustar00rootroot00000000000000/* net_bsd.c * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include "arch_def.h" #include "net_sys.h" #include "quakedef.h" #include "net_defs.h" #include "net_dgrm.h" #include "net_loop.h" net_driver_t net_drivers[] = { #if !defined(NO_LOOP_DRIVER) { "Loopback", false, Loop_Init, Loop_Listen, #if !defined(SERVERONLY) Loop_SearchForHosts, Loop_Connect, #endif Loop_CheckNewConnections, Loop_GetMessage, Loop_SendMessage, Loop_SendUnreliableMessage, Loop_CanSendMessage, Loop_CanSendUnreliableMessage, Loop_Close, Loop_Shutdown }, #endif /* NO_LOOP_DRIVER */ { "Datagram", false, Datagram_Init, Datagram_Listen, #if !defined(SERVERONLY) Datagram_SearchForHosts, Datagram_Connect, #endif Datagram_CheckNewConnections, Datagram_GetMessage, Datagram_SendMessage, Datagram_SendUnreliableMessage, Datagram_CanSendMessage, Datagram_CanSendUnreliableMessage, Datagram_Close, Datagram_Shutdown } }; const int net_numdrivers = (sizeof(net_drivers) / sizeof(net_drivers[0])); #include "net_udp.h" net_landriver_t net_landrivers[] = { { "UDP", false, 0, UDP_Init, UDP_Shutdown, UDP_Listen, UDP_OpenSocket, UDP_CloseSocket, UDP_Connect, UDP_CheckNewConnections, UDP_Read, UDP_Write, UDP_Broadcast, UDP_AddrToString, UDP_StringToAddr, UDP_GetSocketAddr, UDP_GetNameFromAddr, UDP_GetAddrFromName, UDP_AddrCompare, UDP_GetSocketPort, UDP_SetSocketPort } }; const int net_numlandrivers = (sizeof(net_landrivers) / sizeof(net_landrivers[0])); engine/hexen2/net_defs.h000066400000000000000000000157751444734033100154760ustar00rootroot00000000000000/* net_defs.h -- functions and data private to the network layer * net_sys.h and its dependencies must be included before net_defs.h. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __NET_DEFS_H #define __NET_DEFS_H struct qsockaddr { #if defined(HAVE_SA_LEN) unsigned char qsa_len; unsigned char qsa_family; #else short qsa_family; #endif /* BSD, sockaddr */ unsigned char qsa_data[14]; }; #define NET_NAME_ID "HEXENII" #define NET_HEADERSIZE (2 * sizeof(unsigned int)) #define NET_DATAGRAMSIZE (MAX_DATAGRAM + NET_HEADERSIZE) // NetHeader flags #define NETFLAG_LENGTH_MASK 0x0000ffff #define NETFLAG_DATA 0x00010000 #define NETFLAG_ACK 0x00020000 #define NETFLAG_NAK 0x00040000 #define NETFLAG_EOM 0x00080000 #define NETFLAG_UNRELIABLE 0x00100000 #define NETFLAG_CTL 0x80000000 #define NET_PROTOCOL_VERSION 5 /** This is the network info/connection protocol. It is used to find Quake servers, get info about them, and connect to them. Once connected, the Quake game protocol (documented elsewhere) is used. General notes: game_name is currently always "QUAKE", but is there so this same protocol can be used for future games as well; can you say Quake2? CCREQ_CONNECT string game_name "QUAKE" byte net_protocol_version NET_PROTOCOL_VERSION CCREQ_SERVER_INFO string game_name "QUAKE" byte net_protocol_version NET_PROTOCOL_VERSION CCREQ_PLAYER_INFO byte player_number CCREQ_RULE_INFO string rule CCREP_ACCEPT long port CCREP_REJECT string reason CCREP_SERVER_INFO string server_address string host_name string level_name byte current_players byte max_players byte protocol_version NET_PROTOCOL_VERSION CCREP_PLAYER_INFO byte player_number string name long colors long frags long connect_time string address CCREP_RULE_INFO string rule string value note: There are two address forms used above. The short form is just a port number. The address that goes along with the port is defined as "whatever address you receive this reponse from". This lets us use the host OS to solve the problem of multiple host addresses (possibly with no routing between them); the host will use the right address when we reply to the inbound connection request. The long from is a full address and port in a string. It is used for returning the address of a server that is not running locally. **/ #define CCREQ_CONNECT 0x01 #define CCREQ_SERVER_INFO 0x02 #define CCREQ_PLAYER_INFO 0x03 #define CCREQ_RULE_INFO 0x04 #define CCREP_ACCEPT 0x81 #define CCREP_REJECT 0x82 #define CCREP_SERVER_INFO 0x83 #define CCREP_PLAYER_INFO 0x84 #define CCREP_RULE_INFO 0x85 typedef struct qsocket_s { struct qsocket_s *next; double connecttime; double lastMessageTime; double lastSendTime; qboolean disconnected; qboolean canSend; qboolean sendNext; int driver; int landriver; sys_socket_t socket; void *driverdata; unsigned int ackSequence; unsigned int sendSequence; unsigned int unreliableSendSequence; int sendMessageLength; byte sendMessage [NET_MAXMESSAGE]; unsigned int receiveSequence; unsigned int unreliableReceiveSequence; int receiveMessageLength; byte receiveMessage [NET_MAXMESSAGE]; struct qsockaddr addr; char address[NET_NAMELEN]; } qsocket_t; extern qsocket_t *net_activeSockets; extern qsocket_t *net_freeSockets; extern int net_numsockets; typedef struct { const char *name; qboolean initialized; sys_socket_t controlSock; sys_socket_t (*Init) (void); void (*Shutdown) (void); void (*Listen) (qboolean state); sys_socket_t (*Open_Socket) (int port); int (*Close_Socket) (sys_socket_t socketid); int (*Connect) (sys_socket_t socketid, struct qsockaddr *addr); sys_socket_t (*CheckNewConnections) (void); int (*Read) (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr); int (*Write) (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr); int (*Broadcast) (sys_socket_t socketid, byte *buf, int len); const char * (*AddrToString) (struct qsockaddr *addr); int (*StringToAddr) (const char *string, struct qsockaddr *addr); int (*GetSocketAddr) (sys_socket_t socketid, struct qsockaddr *addr); int (*GetNameFromAddr) (struct qsockaddr *addr, char *name); int (*GetAddrFromName) (const char *name, struct qsockaddr *addr); int (*AddrCompare) (struct qsockaddr *addr1, struct qsockaddr *addr2); int (*GetSocketPort) (struct qsockaddr *addr); int (*SetSocketPort) (struct qsockaddr *addr, int port); } net_landriver_t; #define MAX_NET_DRIVERS 8 extern net_landriver_t net_landrivers[]; extern const int net_numlandrivers; typedef struct { const char *name; qboolean initialized; int (*Init) (void); void (*Listen) (qboolean state); #if !defined(SERVERONLY) void (*SearchForHosts) (qboolean xmit); qsocket_t *(*Connect) (const char *host); #endif /* SERVERONLY */ qsocket_t *(*CheckNewConnections) (void); int (*QGetMessage) (qsocket_t *sock); int (*QSendMessage) (qsocket_t *sock, sizebuf_t *data); int (*SendUnreliableMessage) (qsocket_t *sock, sizebuf_t *data); qboolean (*CanSendMessage) (qsocket_t *sock); qboolean (*CanSendUnreliableMessage) (qsocket_t *sock); void (*Close) (qsocket_t *sock); void (*Shutdown) (void); } net_driver_t; extern net_driver_t net_drivers[]; extern const int net_numdrivers; /* Loop driver must always be registered the first */ #if defined(NO_LOOP_DRIVER) #define IS_LOOP_DRIVER(p) 0 #else #define IS_LOOP_DRIVER(p) ((p) == 0) #endif /* NO_LOOP_DRIVER */ extern int net_driverlevel; extern cvar_t net_allowmultiple; extern int messagesSent; extern int messagesReceived; extern int unreliableMessagesSent; extern int unreliableMessagesReceived; qsocket_t *NET_NewQSocket (void); void NET_FreeQSocket(qsocket_t *); double SetNetTime(void); #define HOSTCACHESIZE 8 typedef struct { char name[16]; char map[16]; char cname[32]; int users; int maxusers; int driver; int ldriver; struct qsockaddr addr; } hostcache_t; extern int hostCacheCount; extern hostcache_t hostcache[HOSTCACHESIZE]; typedef struct _PollProcedure { struct _PollProcedure *next; double nextTime; void (*procedure)(void *); void *arg; } PollProcedure; void SchedulePollProcedure(PollProcedure *pp, double timeOffset); #endif /* __NET_DEFS_H */ engine/hexen2/net_dgrm.c000066400000000000000000001002731444734033100154650ustar00rootroot00000000000000/* net_dgrm.c -- This is enables a simple IP banning mechanism * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include "arch_def.h" #include "net_sys.h" #include "quakedef.h" #include "net_defs.h" #include "net_dgrm.h" #define BAN_TEST #if defined(PLATFORM_DOS) && !defined(USE_WATT32) #undef BAN_TEST #endif // these two macros are to make the code more readable #define sfunc net_landrivers[sock->landriver] #define dfunc net_landrivers[net_landriverlevel] static int net_landriverlevel; /* statistic counters */ static int packetsSent = 0; static int packetsReSent = 0; static int packetsReceived = 0; static int receivedDuplicateCount = 0; static int shortPacketCount = 0; static int droppedDatagrams; static struct { unsigned int length; unsigned int sequence; byte data[MAX_DATAGRAM]; } packetBuffer; #if !defined(SERVERONLY) static int myDriverLevel; extern qboolean m_return_onerror; extern char m_return_reason[32]; #endif /* SERVERONLY */ static char *StrAddr (struct qsockaddr *addr) { static char buf[34]; byte *p = (byte *)addr; int n; for (n = 0; n < 16; n++) sprintf (buf + n * 2, "%02x", *p++); return buf; } #ifdef BAN_TEST static struct in_addr banAddr; static struct in_addr banMask; static void NET_Ban_f (void) { char addrStr [32]; char maskStr [32]; void (*print_fn) (unsigned int, const char *, ...) FUNCP_PRINTF(2,3); if (cmd_source == src_command) { if (!sv.active) { #if !defined(SERVERONLY) Cmd_ForwardToServer (); #else Con_Printf("Server not active\n"); #endif /* SERVERONLY */ return; } print_fn = CON_Printf; } else { if (*sv_globals.deathmatch) return; print_fn = SV_ClientPrintf; } switch (Cmd_Argc ()) { case 1: if (banAddr.s_addr != INADDR_ANY) { strcpy(addrStr, inet_ntoa(banAddr)); strcpy(maskStr, inet_ntoa(banMask)); print_fn(_PRINT_NORMAL, "Banning %s [%s]\n", addrStr, maskStr); } else print_fn(_PRINT_NORMAL, "Banning not active\n"); break; case 2: if (q_strcasecmp(Cmd_Argv(1), "off") == 0) banAddr.s_addr = INADDR_ANY; else banAddr.s_addr = inet_addr(Cmd_Argv(1)); banMask.s_addr = INADDR_NONE; break; case 3: banAddr.s_addr = inet_addr(Cmd_Argv(1)); banMask.s_addr = inet_addr(Cmd_Argv(2)); break; default: print_fn(_PRINT_NORMAL, "BAN ip_address [mask]\n"); break; } } #endif // BAN_TEST int Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data) { unsigned int packetLen; unsigned int dataLen; unsigned int eom; #ifdef DEBUG_BUILD if (data->cursize == 0) Sys_Error("%s: zero length message", __thisfunc__); if (data->cursize > NET_MAXMESSAGE) Sys_Error("%s: message too big: %u", __thisfunc__, data->cursize); if (sock->canSend == false) Sys_Error("%s: called with canSend == false", __thisfunc__); #endif memcpy(sock->sendMessage, data->data, data->cursize); sock->sendMessageLength = data->cursize; if (data->cursize <= MAX_DATAGRAM) { dataLen = data->cursize; eom = NETFLAG_EOM; } else { dataLen = MAX_DATAGRAM; eom = 0; } packetLen = NET_HEADERSIZE + dataLen; packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom)); packetBuffer.sequence = BigLong(sock->sendSequence++); memcpy (packetBuffer.data, sock->sendMessage, dataLen); sock->canSend = false; if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) return -1; sock->lastSendTime = net_time; packetsSent++; return 1; } static int SendMessageNext (qsocket_t *sock) { unsigned int packetLen; unsigned int dataLen; unsigned int eom; if (sock->sendMessageLength <= MAX_DATAGRAM) { dataLen = sock->sendMessageLength; eom = NETFLAG_EOM; } else { dataLen = MAX_DATAGRAM; eom = 0; } packetLen = NET_HEADERSIZE + dataLen; packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom)); packetBuffer.sequence = BigLong(sock->sendSequence++); memcpy (packetBuffer.data, sock->sendMessage, dataLen); sock->sendNext = false; if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) return -1; sock->lastSendTime = net_time; packetsSent++; return 1; } static int ReSendMessage (qsocket_t *sock) { unsigned int packetLen; unsigned int dataLen; unsigned int eom; if (sock->sendMessageLength <= MAX_DATAGRAM) { dataLen = sock->sendMessageLength; eom = NETFLAG_EOM; } else { dataLen = MAX_DATAGRAM; eom = 0; } packetLen = NET_HEADERSIZE + dataLen; packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom)); packetBuffer.sequence = BigLong(sock->sendSequence - 1); memcpy (packetBuffer.data, sock->sendMessage, dataLen); sock->sendNext = false; if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) return -1; sock->lastSendTime = net_time; packetsReSent++; return 1; } qboolean Datagram_CanSendMessage (qsocket_t *sock) { if (sock->sendNext) SendMessageNext (sock); return sock->canSend; } qboolean Datagram_CanSendUnreliableMessage (qsocket_t *sock) { return true; } int Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data) { int packetLen; #ifdef DEBUG_BUILD if (data->cursize == 0) Sys_Error("%s: zero length message", __thisfunc__); if (data->cursize > MAX_DATAGRAM) Sys_Error("%s: message too big: %u", __thisfunc__, data->cursize); #endif packetLen = NET_HEADERSIZE + data->cursize; packetBuffer.length = BigLong(packetLen | NETFLAG_UNRELIABLE); packetBuffer.sequence = BigLong(sock->unreliableSendSequence++); memcpy (packetBuffer.data, data->data, data->cursize); if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) return -1; packetsSent++; return 1; } int Datagram_GetMessage (qsocket_t *sock) { unsigned int length; unsigned int flags; int ret = 0; struct qsockaddr readaddr; unsigned int sequence; unsigned int count; if (!sock->canSend) if ((net_time - sock->lastSendTime) > 1.0) ReSendMessage (sock); while (1) { length = (unsigned int) sfunc.Read(sock->socket, (byte *)&packetBuffer, NET_DATAGRAMSIZE, &readaddr); // if ((rand() & 255) > 220) // continue; if (length == 0) break; if (length == (unsigned int)-1) { Con_Printf("Read error\n"); return -1; } if (sfunc.AddrCompare(&readaddr, &sock->addr) != 0) { Con_Printf("Forged packet received\n"); Con_Printf("Expected: %s\n", StrAddr (&sock->addr)); Con_Printf("Received: %s\n", StrAddr (&readaddr)); continue; } if (length < NET_HEADERSIZE) { shortPacketCount++; continue; } length = BigLong(packetBuffer.length); flags = length & (~NETFLAG_LENGTH_MASK); length &= NETFLAG_LENGTH_MASK; if (flags & NETFLAG_CTL) continue; sequence = BigLong(packetBuffer.sequence); packetsReceived++; if (flags & NETFLAG_UNRELIABLE) { if (sequence < sock->unreliableReceiveSequence) { Con_DPrintf("Got a stale datagram\n"); ret = 0; break; } if (sequence != sock->unreliableReceiveSequence) { count = sequence - sock->unreliableReceiveSequence; droppedDatagrams += count; Con_DPrintf("Dropped %u datagram(s)\n", count); } sock->unreliableReceiveSequence = sequence + 1; length -= NET_HEADERSIZE; SZ_Clear (&net_message); SZ_Write (&net_message, packetBuffer.data, length); ret = 2; break; } if (flags & NETFLAG_ACK) { if (sequence != (sock->sendSequence - 1)) { Con_DPrintf("Stale ACK received\n"); continue; } if (sequence == sock->ackSequence) { sock->ackSequence++; if (sock->ackSequence != sock->sendSequence) Con_DPrintf("ack sequencing error\n"); } else { Con_DPrintf("Duplicate ACK received\n"); continue; } sock->sendMessageLength -= MAX_DATAGRAM; if (sock->sendMessageLength > 0) { memmove (sock->sendMessage, sock->sendMessage + MAX_DATAGRAM, sock->sendMessageLength); sock->sendNext = true; } else { sock->sendMessageLength = 0; sock->canSend = true; } continue; } if (flags & NETFLAG_DATA) { packetBuffer.length = BigLong(NET_HEADERSIZE | NETFLAG_ACK); packetBuffer.sequence = BigLong(sequence); sfunc.Write (sock->socket, (byte *)&packetBuffer, NET_HEADERSIZE, &readaddr); if (sequence != sock->receiveSequence) { receivedDuplicateCount++; continue; } sock->receiveSequence++; length -= NET_HEADERSIZE; if (flags & NETFLAG_EOM) { SZ_Clear(&net_message); SZ_Write(&net_message, sock->receiveMessage, sock->receiveMessageLength); SZ_Write(&net_message, packetBuffer.data, length); sock->receiveMessageLength = 0; ret = 1; break; } memcpy(sock->receiveMessage + sock->receiveMessageLength, packetBuffer.data, length); sock->receiveMessageLength += length; continue; } } if (sock->sendNext) SendMessageNext (sock); return ret; } static void PrintStats(qsocket_t *s) { Con_Printf("canSend = %4u \n", s->canSend); Con_Printf("sendSeq = %4u ", s->sendSequence); Con_Printf("recvSeq = %4u \n", s->receiveSequence); Con_Printf("\n"); } static void NET_Stats_f (void) { qsocket_t *s; if (Cmd_Argc () == 1) { Con_Printf("unreliable messages sent = %i\n", unreliableMessagesSent); Con_Printf("unreliable messages recv = %i\n", unreliableMessagesReceived); Con_Printf("reliable messages sent = %i\n", messagesSent); Con_Printf("reliable messages received = %i\n", messagesReceived); Con_Printf("packetsSent = %i\n", packetsSent); Con_Printf("packetsReSent = %i\n", packetsReSent); Con_Printf("packetsReceived = %i\n", packetsReceived); Con_Printf("receivedDuplicateCount = %i\n", receivedDuplicateCount); Con_Printf("shortPacketCount = %i\n", shortPacketCount); Con_Printf("droppedDatagrams = %i\n", droppedDatagrams); } else if (strcmp(Cmd_Argv(1), "*") == 0) { for (s = net_activeSockets; s; s = s->next) PrintStats(s); for (s = net_freeSockets; s; s = s->next) PrintStats(s); } else { for (s = net_activeSockets; s; s = s->next) { if (q_strcasecmp(Cmd_Argv(1), s->address) == 0) break; } if (s == NULL) { for (s = net_freeSockets; s; s = s->next) { if (q_strcasecmp(Cmd_Argv(1), s->address) == 0) break; } } if (s == NULL) return; PrintStats(s); } } #if !defined(SERVERONLY) static qboolean testInProgress = false; static int testPollCount; static int testDriver; static sys_socket_t testSocket; static void Test_Poll (void *); static PollProcedure testPollProcedure = {NULL, 0.0, Test_Poll}; static void Test_Poll (void *unused) { struct qsockaddr clientaddr; int control; int len; char name[32]; char address[64]; int colors; int frags; int connectTime; net_landriverlevel = testDriver; while (1) { len = dfunc.Read (testSocket, net_message.data, net_message.maxsize, &clientaddr); if (len < (int) sizeof(int)) break; net_message.cursize = len; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) break; if ((control & (~NETFLAG_LENGTH_MASK)) != (int)NETFLAG_CTL) break; if ((control & NETFLAG_LENGTH_MASK) != len) break; if (MSG_ReadByte() != CCREP_PLAYER_INFO) Sys_Error("Unexpected response to Player Info request\n"); MSG_ReadByte(); /* playerNumber */ strcpy(name, MSG_ReadString()); colors = MSG_ReadLong(); frags = MSG_ReadLong(); connectTime = MSG_ReadLong(); strcpy(address, MSG_ReadString()); Con_Printf("%s\n frags:%3i colors:%d %d time:%d\n %s\n", name, frags, colors >> 4, colors & 0x0f, connectTime / 60, address); } testPollCount--; if (testPollCount) { SchedulePollProcedure(&testPollProcedure, 0.1); } else { dfunc.Close_Socket(testSocket); testInProgress = false; } } static void Test_f (void) { const char *host; int n; int maxusers = MAX_CLIENTS; struct qsockaddr sendaddr; if (testInProgress) return; host = Cmd_Argv (1); if (host && hostCacheCount) { for (n = 0; n < hostCacheCount; n++) { if (q_strcasecmp (host, hostcache[n].name) == 0) { if (hostcache[n].driver != myDriverLevel) continue; net_landriverlevel = hostcache[n].ldriver; maxusers = hostcache[n].maxusers; memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr)); break; } } if (n < hostCacheCount) goto JustDoIt; } for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) { if (!net_landrivers[net_landriverlevel].initialized) continue; // see if we can resolve the host name if (dfunc.GetAddrFromName(host, &sendaddr) != -1) break; } if (net_landriverlevel == net_numlandrivers) return; JustDoIt: testSocket = dfunc.Open_Socket(0); if (testSocket == INVALID_SOCKET) return; testInProgress = true; testPollCount = 20; testDriver = net_landriverlevel; for (n = 0; n < maxusers; n++) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_PLAYER_INFO); MSG_WriteByte(&net_message, n); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (testSocket, net_message.data, net_message.cursize, &sendaddr); } SZ_Clear(&net_message); SchedulePollProcedure(&testPollProcedure, 0.1); } static qboolean test2InProgress = false; static int test2Driver; static sys_socket_t test2Socket; static void Test2_Poll (void *); static PollProcedure test2PollProcedure = {NULL, 0.0, Test2_Poll}; static void Test2_Poll (void *unused) { struct qsockaddr clientaddr; int control; int len; char name[256]; char value[256]; net_landriverlevel = test2Driver; name[0] = 0; len = dfunc.Read (test2Socket, net_message.data, net_message.maxsize, &clientaddr); if (len < (int) sizeof(int)) goto Reschedule; net_message.cursize = len; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) goto Error; if ((control & (~NETFLAG_LENGTH_MASK)) != (int)NETFLAG_CTL) goto Error; if ((control & NETFLAG_LENGTH_MASK) != len) goto Error; if (MSG_ReadByte() != CCREP_RULE_INFO) goto Error; strcpy(name, MSG_ReadString()); if (name[0] == 0) goto Done; strcpy(value, MSG_ReadString()); Con_Printf("%-16.16s %-16.16s\n", name, value); SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_RULE_INFO); MSG_WriteString(&net_message, name); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (test2Socket, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); Reschedule: SchedulePollProcedure(&test2PollProcedure, 0.05); return; Error: Con_Printf("Unexpected response to Rule Info request\n"); Done: dfunc.Close_Socket(test2Socket); test2InProgress = false; return; } static void Test2_f (void) { const char *host; int n; struct qsockaddr sendaddr; if (test2InProgress) return; host = Cmd_Argv (1); if (host && hostCacheCount) { for (n = 0; n < hostCacheCount; n++) { if (q_strcasecmp (host, hostcache[n].name) == 0) { if (hostcache[n].driver != myDriverLevel) continue; net_landriverlevel = hostcache[n].ldriver; memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr)); break; } } if (n < hostCacheCount) goto JustDoIt; } for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) { if (!net_landrivers[net_landriverlevel].initialized) continue; // see if we can resolve the host name if (dfunc.GetAddrFromName(host, &sendaddr) != -1) break; } if (net_landriverlevel == net_numlandrivers) return; JustDoIt: test2Socket = dfunc.Open_Socket(0); if (test2Socket == INVALID_SOCKET) return; test2InProgress = true; test2Driver = net_landriverlevel; SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_RULE_INFO); MSG_WriteString(&net_message, ""); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (test2Socket, net_message.data, net_message.cursize, &sendaddr); SZ_Clear(&net_message); SchedulePollProcedure(&test2PollProcedure, 0.05); } #endif /* SERVERONLY */ int Datagram_Init (void) { int i, num_inited; sys_socket_t csock; #ifdef BAN_TEST banAddr.s_addr = INADDR_ANY; banMask.s_addr = INADDR_NONE; #endif #if !defined(SERVERONLY) myDriverLevel = net_driverlevel; #endif /* SERVERONLY */ Cmd_AddCommand ("net_stats", NET_Stats_f); if (safemode || COM_CheckParm("-nolan")) return -1; num_inited = 0; for (i = 0; i < net_numlandrivers; i++) { csock = net_landrivers[i].Init (); if (csock == INVALID_SOCKET) continue; net_landrivers[i].initialized = true; net_landrivers[i].controlSock = csock; num_inited++; } if (num_inited == 0) return -1; #ifdef BAN_TEST Cmd_AddCommand ("ban", NET_Ban_f); #endif #if !defined(SERVERONLY) Cmd_AddCommand ("test", Test_f); Cmd_AddCommand ("test2", Test2_f); #endif /* SERVERONLY */ return 0; } void Datagram_Shutdown (void) { int i; // // shutdown the lan drivers // for (i = 0; i < net_numlandrivers; i++) { if (net_landrivers[i].initialized) { net_landrivers[i].Shutdown (); net_landrivers[i].initialized = false; } } } void Datagram_Close (qsocket_t *sock) { sfunc.Close_Socket(sock->socket); } void Datagram_Listen (qboolean state) { int i; for (i = 0; i < net_numlandrivers; i++) { if (net_landrivers[i].initialized) net_landrivers[i].Listen (state); } } static qsocket_t *Datagram_Reject (const char *message, sys_socket_t acceptsocket, struct qsockaddr *addr) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_REJECT); MSG_WriteString(&net_message, message); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsocket, net_message.data, net_message.cursize, addr); SZ_Clear(&net_message); return NULL; } static qsocket_t *_Datagram_CheckNewConnections (void) { struct qsockaddr clientaddr; struct qsockaddr newaddr; sys_socket_t newsock; sys_socket_t acceptsock; qsocket_t *sock; qsocket_t *s; int len; int command; int control; int ret; acceptsock = dfunc.CheckNewConnections(); if (acceptsock == INVALID_SOCKET) return NULL; SZ_Clear(&net_message); len = dfunc.Read (acceptsock, net_message.data, net_message.maxsize, &clientaddr); if (len < (int) sizeof(int)) return NULL; net_message.cursize = len; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) return NULL; if ((control & (~NETFLAG_LENGTH_MASK)) != (int)NETFLAG_CTL) return NULL; if ((control & NETFLAG_LENGTH_MASK) != len) return NULL; command = MSG_ReadByte(); if (command == CCREQ_SERVER_INFO) { if (strcmp(MSG_ReadString(), NET_NAME_ID) != 0) return NULL; SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_SERVER_INFO); dfunc.GetSocketAddr(acceptsock, &newaddr); MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr)); MSG_WriteString(&net_message, hostname.string); MSG_WriteString(&net_message, sv.name); MSG_WriteByte(&net_message, net_activeconnections); MSG_WriteByte(&net_message, svs.maxclients); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command == CCREQ_PLAYER_INFO) { int playerNumber; int activeNumber; int clientNumber; client_t *client; playerNumber = MSG_ReadByte(); activeNumber = -1; for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++) { if (client->active) { activeNumber++; if (activeNumber == playerNumber) break; } } if (clientNumber == svs.maxclients) return NULL; SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_PLAYER_INFO); MSG_WriteByte(&net_message, playerNumber); MSG_WriteString(&net_message, client->name); MSG_WriteLong(&net_message, client->colors); MSG_WriteLong(&net_message, (int)client->edict->v.frags); MSG_WriteLong(&net_message, (int)(net_time - client->netconnection->connecttime)); MSG_WriteString(&net_message, client->netconnection->address); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command == CCREQ_RULE_INFO) { const char *prevCvarName; cvar_t *var; // find the search start location prevCvarName = MSG_ReadString(); var = Cvar_FindVarAfter (prevCvarName, CVAR_SERVERINFO); // send the response SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_RULE_INFO); if (var) { MSG_WriteString(&net_message, var->name); MSG_WriteString(&net_message, var->string); } *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command != CCREQ_CONNECT) return NULL; if (strcmp(MSG_ReadString(), NET_NAME_ID) != 0) return NULL; if (MSG_ReadByte() != NET_PROTOCOL_VERSION) return Datagram_Reject("Incompatible version.\n", acceptsock, &clientaddr); #ifdef BAN_TEST // check for a ban if (clientaddr.qsa_family == AF_INET) { in_addr_t testAddr; testAddr = ((struct sockaddr_in *)&clientaddr)->sin_addr.s_addr; if ((testAddr & banMask.s_addr) == banAddr.s_addr) return Datagram_Reject("You have been banned.\n", acceptsock, &clientaddr); } #endif // see if this guy is already connected for (s = net_activeSockets; s; s = s->next) { if (s->driver != net_driverlevel) continue; ret = dfunc.AddrCompare(&clientaddr, &s->addr); if (ret >= 0) { // is this a duplicate connection reqeust? if (ret == 0 && net_time - s->connecttime < 2.0) { // yes, so send a duplicate reply SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_ACCEPT); dfunc.GetSocketAddr(s->socket, &newaddr); MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr)); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } // it's somebody coming back in from a crash/disconnect // so close the old qsocket and let their retry get them back in NET_Close(s); return NULL; } } // allocate a QSocket sock = NET_NewQSocket (); if (sock == NULL) // no room; try to let him know return Datagram_Reject("Server is full.\n", acceptsock, &clientaddr); // allocate a network socket newsock = dfunc.Open_Socket(0); if (newsock == INVALID_SOCKET) { NET_FreeQSocket(sock); return NULL; } // connect to the client if (dfunc.Connect (newsock, &clientaddr) == -1) { dfunc.Close_Socket(newsock); NET_FreeQSocket(sock); return NULL; } // everything is allocated, just fill in the details sock->socket = newsock; sock->landriver = net_landriverlevel; sock->addr = clientaddr; strcpy(sock->address, dfunc.AddrToString(&clientaddr)); // send him back the info about the server connection he has been allocated SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_ACCEPT); dfunc.GetSocketAddr(newsock, &newaddr); MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr)); // MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr)); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return sock; } qsocket_t *Datagram_CheckNewConnections (void) { qsocket_t *ret = NULL; for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) { if (net_landrivers[net_landriverlevel].initialized) { if ((ret = _Datagram_CheckNewConnections ()) != NULL) break; } } return ret; } #if !defined(SERVERONLY) static void _Datagram_SearchForHosts (qboolean xmit) { int ret; int n; int i; struct qsockaddr readaddr; struct qsockaddr myaddr; int control; dfunc.GetSocketAddr (dfunc.controlSock, &myaddr); if (xmit) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_SERVER_INFO); MSG_WriteString(&net_message, NET_NAME_ID); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Broadcast(dfunc.controlSock, net_message.data, net_message.cursize); SZ_Clear(&net_message); } while ((ret = dfunc.Read (dfunc.controlSock, net_message.data, net_message.maxsize, &readaddr)) > 0) { if (ret < (int) sizeof(int)) continue; net_message.cursize = ret; // don't answer our own query if (dfunc.AddrCompare(&readaddr, &myaddr) >= 0) continue; // is the cache full? if (hostCacheCount == HOSTCACHESIZE) continue; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) continue; if ((control & (~NETFLAG_LENGTH_MASK)) != (int)NETFLAG_CTL) continue; if ((control & NETFLAG_LENGTH_MASK) != ret) continue; if (MSG_ReadByte() != CCREP_SERVER_INFO) continue; dfunc.GetAddrFromName(MSG_ReadString(), &readaddr); // search the cache for this server for (n = 0; n < hostCacheCount; n++) { if (dfunc.AddrCompare(&readaddr, &hostcache[n].addr) == 0) break; } // is it already there? if (n < hostCacheCount) continue; // add it hostCacheCount++; strcpy(hostcache[n].name, MSG_ReadString()); strcpy(hostcache[n].map, MSG_ReadString()); hostcache[n].users = MSG_ReadByte(); hostcache[n].maxusers = MSG_ReadByte(); if (MSG_ReadByte() != NET_PROTOCOL_VERSION) { strcpy(hostcache[n].cname, hostcache[n].name); hostcache[n].cname[14] = 0; strcpy(hostcache[n].name, "*"); strcat(hostcache[n].name, hostcache[n].cname); } memcpy(&hostcache[n].addr, &readaddr, sizeof(struct qsockaddr)); hostcache[n].driver = net_driverlevel; hostcache[n].ldriver = net_landriverlevel; strcpy(hostcache[n].cname, dfunc.AddrToString(&readaddr)); // check for a name conflict for (i = 0; i < hostCacheCount; i++) { if (i == n) continue; if (q_strcasecmp (hostcache[n].name, hostcache[i].name) == 0) { i = strlen(hostcache[n].name); if (i < 15 && hostcache[n].name[i-1] > '8') { hostcache[n].name[i] = '0'; hostcache[n].name[i+1] = 0; } else hostcache[n].name[i-1]++; i = -1; } } } } void Datagram_SearchForHosts (qboolean xmit) { for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) { if (hostCacheCount == HOSTCACHESIZE) break; if (net_landrivers[net_landriverlevel].initialized) _Datagram_SearchForHosts (xmit); } } static qsocket_t *_Datagram_Connect (const char *host) { struct qsockaddr sendaddr; struct qsockaddr readaddr; qsocket_t *sock; sys_socket_t newsock; int ret; int reps; double start_time; int control; const char *reason; // see if we can resolve the host name if (dfunc.GetAddrFromName(host, &sendaddr) == -1) return NULL; newsock = dfunc.Open_Socket (0); if (newsock == INVALID_SOCKET) return NULL; sock = NET_NewQSocket (); if (sock == NULL) goto ErrorReturn2; sock->socket = newsock; sock->landriver = net_landriverlevel; // connect to the host if (dfunc.Connect (newsock, &sendaddr) == -1) goto ErrorReturn; // send the connection request Con_Printf("trying...\n"); SCR_UpdateScreen (); start_time = net_time; for (reps = 0; reps < 3; reps++) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_CONNECT); MSG_WriteString(&net_message, NET_NAME_ID); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (newsock, net_message.data, net_message.cursize, &sendaddr); SZ_Clear(&net_message); do { ret = dfunc.Read (newsock, net_message.data, net_message.maxsize, &readaddr); // if we got something, validate it if (ret > 0) { // is it from the right place? if (sfunc.AddrCompare(&readaddr, &sendaddr) != 0) { Con_Printf("wrong reply address\n"); Con_Printf("Expected: %s\n", StrAddr (&sendaddr)); Con_Printf("Received: %s\n", StrAddr (&readaddr)); SCR_UpdateScreen (); ret = 0; continue; } if (ret < (int) sizeof(int)) { ret = 0; continue; } net_message.cursize = ret; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) { ret = 0; continue; } if ((control & (~NETFLAG_LENGTH_MASK)) != (int)NETFLAG_CTL) { ret = 0; continue; } if ((control & NETFLAG_LENGTH_MASK) != ret) { ret = 0; continue; } } } while (ret == 0 && (SetNetTime() - start_time) < 2.5); if (ret) break; Con_Printf("still trying...\n"); SCR_UpdateScreen (); start_time = SetNetTime(); } if (ret == 0) { reason = "No Response"; Con_Printf("%s\n", reason); strcpy(m_return_reason, reason); goto ErrorReturn; } if (ret == -1) { reason = "Network Error"; Con_Printf("%s\n", reason); strcpy(m_return_reason, reason); goto ErrorReturn; } ret = MSG_ReadByte(); if (ret == CCREP_REJECT) { reason = MSG_ReadString(); Con_Printf("%s\n", reason); q_strlcpy(m_return_reason, reason, sizeof(m_return_reason)); goto ErrorReturn; } if (ret == CCREP_ACCEPT) { memcpy(&sock->addr, &sendaddr, sizeof(struct qsockaddr)); dfunc.SetSocketPort (&sock->addr, MSG_ReadLong()); } else { reason = "Bad Response"; Con_Printf("%s\n", reason); strcpy(m_return_reason, reason); goto ErrorReturn; } dfunc.GetNameFromAddr (&sendaddr, sock->address); Con_Printf ("Connection accepted\n"); sock->lastMessageTime = SetNetTime(); // switch the connection to the specified address if (dfunc.Connect (newsock, &sock->addr) == -1) { reason = "Connect to Game failed"; Con_Printf("%s\n", reason); strcpy(m_return_reason, reason); goto ErrorReturn; } m_return_onerror = false; return sock; ErrorReturn: NET_FreeQSocket(sock); ErrorReturn2: dfunc.Close_Socket(newsock); if (m_return_onerror) { Key_SetDest (key_menu); m_state = m_return_state; m_return_onerror = false; } return NULL; } qsocket_t *Datagram_Connect (const char *host) { qsocket_t *ret = NULL; for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) { if (net_landrivers[net_landriverlevel].initialized) { if ((ret = _Datagram_Connect (host)) != NULL) break; } } return ret; } #endif /* SERVERONLY */ engine/hexen2/net_dgrm.h000066400000000000000000000026731444734033100154770ustar00rootroot00000000000000/* net_dgrm.h -- net-datagram header * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __NET_DATAGRAM_H #define __NET_DATAGRAM_H int Datagram_Init (void); void Datagram_Listen (qboolean state); void Datagram_SearchForHosts (qboolean xmit); qsocket_t *Datagram_Connect (const char *host); qsocket_t *Datagram_CheckNewConnections (void); int Datagram_GetMessage (qsocket_t *sock); int Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data); int Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data); qboolean Datagram_CanSendMessage (qsocket_t *sock); qboolean Datagram_CanSendUnreliableMessage (qsocket_t *sock); void Datagram_Close (qsocket_t *sock); void Datagram_Shutdown (void); #endif /* __NET_DATAGRAM_H */ engine/hexen2/net_loop.c000066400000000000000000000124351444734033100155070ustar00rootroot00000000000000/* net_loop.c -- network loop driver * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include "arch_def.h" #include "net_sys.h" #include "quakedef.h" #include "net_defs.h" #include "net_loop.h" static qboolean localconnectpending = false; static qsocket_t *loop_client = NULL; static qsocket_t *loop_server = NULL; int Loop_Init (void) { if (cls.state == ca_dedicated) return -1; return 0; } void Loop_Shutdown (void) { } void Loop_Listen (qboolean state) { } void Loop_SearchForHosts (qboolean xmit) { if (!sv.active) return; hostCacheCount = 1; if (strcmp(hostname.string, "UNNAMED") == 0) strcpy(hostcache[0].name, "local"); else strcpy(hostcache[0].name, hostname.string); strcpy(hostcache[0].map, sv.name); hostcache[0].users = net_activeconnections; hostcache[0].maxusers = svs.maxclients; hostcache[0].driver = net_driverlevel; strcpy(hostcache[0].cname, "local"); } qsocket_t *Loop_Connect (const char *host) { if (strcmp(host,"local") != 0) return NULL; localconnectpending = true; if (!loop_client) { if ((loop_client = NET_NewQSocket ()) == NULL) { Con_Printf("%s: no qsocket available\n", __thisfunc__); return NULL; } strcpy (loop_client->address, "localhost"); } loop_client->receiveMessageLength = 0; loop_client->sendMessageLength = 0; loop_client->canSend = true; if (!loop_server) { if ((loop_server = NET_NewQSocket ()) == NULL) { Con_Printf("%s: no qsocket available\n", __thisfunc__); return NULL; } strcpy (loop_server->address, "LOCAL"); } loop_server->receiveMessageLength = 0; loop_server->sendMessageLength = 0; loop_server->canSend = true; loop_client->driverdata = (void *)loop_server; loop_server->driverdata = (void *)loop_client; return loop_client; } qsocket_t *Loop_CheckNewConnections (void) { if (!localconnectpending) return NULL; localconnectpending = false; loop_server->sendMessageLength = 0; loop_server->receiveMessageLength = 0; loop_server->canSend = true; loop_client->sendMessageLength = 0; loop_client->receiveMessageLength = 0; loop_client->canSend = true; return loop_server; } static int IntAlign(int value) { return (value + (sizeof(int) - 1)) & (~(sizeof(int) - 1)); } int Loop_GetMessage (qsocket_t *sock) { int ret; int length; if (sock->receiveMessageLength == 0) return 0; ret = sock->receiveMessage[0]; length = sock->receiveMessage[1] + (sock->receiveMessage[2] << 8); // alignment byte skipped here SZ_Clear (&net_message); SZ_Write (&net_message, &sock->receiveMessage[4], length); length = IntAlign(length + 4); sock->receiveMessageLength -= length; if (sock->receiveMessageLength) memmove (sock->receiveMessage, &sock->receiveMessage[length], sock->receiveMessageLength); if (sock->driverdata && ret == 1) ((qsocket_t *)sock->driverdata)->canSend = true; return ret; } int Loop_SendMessage (qsocket_t *sock, sizebuf_t *data) { byte *buffer; int *bufferLength; if (!sock->driverdata) return -1; bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength; if ((*bufferLength + data->cursize + 4) > NET_MAXMESSAGE) Sys_Error("%s: overflow", __thisfunc__); buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength; // message type *buffer++ = 1; // length *buffer++ = data->cursize & 0xff; *buffer++ = data->cursize >> 8; // align buffer++; // message memcpy(buffer, data->data, data->cursize); *bufferLength = IntAlign(*bufferLength + data->cursize + 4); sock->canSend = false; return 1; } int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data) { byte *buffer; int *bufferLength; if (!sock->driverdata) return -1; bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength; if ((*bufferLength + data->cursize + sizeof(byte) + sizeof(short)) > NET_MAXMESSAGE) return 0; buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength; // message type *buffer++ = 2; // length *buffer++ = data->cursize & 0xff; *buffer++ = data->cursize >> 8; // align buffer++; // message memcpy(buffer, data->data, data->cursize); *bufferLength = IntAlign(*bufferLength + data->cursize + 4); return 1; } qboolean Loop_CanSendMessage (qsocket_t *sock) { if (!sock->driverdata) return false; return sock->canSend; } qboolean Loop_CanSendUnreliableMessage (qsocket_t *sock) { return true; } void Loop_Close (qsocket_t *sock) { if (sock->driverdata) ((qsocket_t *)sock->driverdata)->driverdata = NULL; sock->receiveMessageLength = 0; sock->sendMessageLength = 0; sock->canSend = true; if (sock == loop_client) loop_client = NULL; else loop_server = NULL; } engine/hexen2/net_loop.h000066400000000000000000000025721444734033100155150ustar00rootroot00000000000000/* net_loop.h -- network loop driver * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef NET_LOOP_H #define NET_LOOP_H int Loop_Init (void); void Loop_Listen (qboolean state); void Loop_SearchForHosts (qboolean xmit); qsocket_t *Loop_Connect (const char *host); qsocket_t *Loop_CheckNewConnections (void); int Loop_GetMessage (qsocket_t *sock); int Loop_SendMessage (qsocket_t *sock, sizebuf_t *data); int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data); qboolean Loop_CanSendMessage (qsocket_t *sock); qboolean Loop_CanSendUnreliableMessage (qsocket_t *sock); void Loop_Close (qsocket_t *sock); void Loop_Shutdown (void); #endif /* NET_LOOP_H */ engine/hexen2/net_main.c000066400000000000000000000472221444734033100154640ustar00rootroot00000000000000/* net_main.c -- main networking module * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include "arch_def.h" #include "net_sys.h" #include "quakedef.h" #include "net_defs.h" qsocket_t *net_activeSockets = NULL; qsocket_t *net_freeSockets = NULL; int net_numsockets = 0; qboolean serialAvailable = false; qboolean ipxAvailable = false; qboolean tcpipAvailable = false; int net_hostport; int DEFAULTnet_hostport = 26900; char my_ipx_address[NET_NAMELEN]; char my_tcpip_address[NET_NAMELEN]; #if defined(SERVERONLY) #define listening true /* h2ded is always listening */ #else static qboolean listening = false; qboolean slistInProgress = false; qboolean slistSilent = false; qboolean slistLocal = true; static double slistStartTime; static int slistLastShown; static void Slist_Send (void *); static void Slist_Poll (void *); static PollProcedure slistSendProcedure = {NULL, 0.0, Slist_Send}; static PollProcedure slistPollProcedure = {NULL, 0.0, Slist_Poll}; #endif /* SERVERONLY */ sizebuf_t net_message; //static byte net_message_buffer[NET_MAXMESSAGE]; int net_activeconnections = 0; int messagesSent = 0; int messagesReceived = 0; int unreliableMessagesSent = 0; int unreliableMessagesReceived = 0; static cvar_t net_messagetimeout = {"net_messagetimeout", "300", CVAR_NONE}; cvar_t hostname = {"hostname", "UNNAMED", CVAR_NONE}; cvar_t net_allowmultiple = {"net_allowmultiple", "0", CVAR_ARCHIVE}; #if defined(NET_USE_SERIAL) static qboolean configRestored = false; void (*GetComPortConfig) (int portNumber, int *port, int *irq, int *baud, qboolean *useModem); void (*SetComPortConfig) (int portNumber, int port, int irq, int baud, qboolean useModem); void (*GetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup); void (*SetModemConfig) (int portNumber, const char *dialType, const char *clear, const char *init, const char *hangup); cvar_t config_com_port = {"_config_com_port", "0x3f8", CVAR_ARCHIVE}; cvar_t config_com_irq = {"_config_com_irq", "4", CVAR_ARCHIVE}; cvar_t config_com_baud = {"_config_com_baud", "57600", CVAR_ARCHIVE}; cvar_t config_com_modem = {"_config_com_modem", "1", CVAR_ARCHIVE}; cvar_t config_modem_dialtype = {"_config_modem_dialtype", "T", CVAR_ARCHIVE}; cvar_t config_modem_clear = {"_config_modem_clear", "ATZ", CVAR_ARCHIVE}; cvar_t config_modem_init = {"_config_modem_init", "", CVAR_ARCHIVE}; cvar_t config_modem_hangup = {"_config_modem_hangup", "AT H", CVAR_ARCHIVE}; #endif /* NET_USE_SERIAL */ // these two macros are to make the code more readable #define sfunc net_drivers[sock->driver] #define dfunc net_drivers[net_driverlevel] int net_driverlevel; double net_time; double SetNetTime (void) { net_time = Sys_DoubleTime(); return net_time; } /* =================== NET_NewQSocket Called by drivers when a new communications endpoint is required The sequence and buffer fields will be filled in properly =================== */ qsocket_t *NET_NewQSocket (void) { qsocket_t *sock; if (net_freeSockets == NULL) return NULL; if (net_activeconnections >= svs.maxclients) return NULL; // get one from free list sock = net_freeSockets; net_freeSockets = sock->next; // add it to active list sock->next = net_activeSockets; net_activeSockets = sock; sock->disconnected = false; sock->connecttime = net_time; strcpy (sock->address,"UNSET ADDRESS"); sock->driver = net_driverlevel; sock->socket = 0; sock->driverdata = NULL; sock->canSend = true; sock->sendNext = false; sock->lastMessageTime = net_time; sock->ackSequence = 0; sock->sendSequence = 0; sock->unreliableSendSequence = 0; sock->sendMessageLength = 0; sock->receiveSequence = 0; sock->unreliableReceiveSequence = 0; sock->receiveMessageLength = 0; return sock; } void NET_FreeQSocket(qsocket_t *sock) { qsocket_t *s; // remove it from active list if (sock == net_activeSockets) net_activeSockets = net_activeSockets->next; else { for (s = net_activeSockets; s; s = s->next) { if (s->next == sock) { s->next = sock->next; break; } } if (!s) Sys_Error ("%s: not active", __thisfunc__); } // add it to free list sock->next = net_freeSockets; net_freeSockets = sock; sock->disconnected = true; } double NET_QSocketGetTime (const qsocket_t *s) { return s->connecttime; } const char *NET_QSocketGetAddressString (const qsocket_t *s) { return s->address; } #if !defined(SERVERONLY) static void NET_Listen_f (void) { if (Cmd_Argc () != 2) { Con_Printf ("\"listen\" is \"%d\"\n", listening ? 1 : 0); return; } listening = atoi(Cmd_Argv(1)) ? true : false; for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++) { if (net_drivers[net_driverlevel].initialized == false) continue; dfunc.Listen (listening); } } static void MaxPlayers_f (void) { int n; if (Cmd_Argc () != 2) { Con_Printf ("\"maxplayers\" is \"%d\"\n", svs.maxclients); return; } if (sv.active) { Con_Printf ("maxplayers can not be changed while a server is running.\n"); return; } n = atoi(Cmd_Argv(1)); if (n < 1) n = 1; if (n > svs.maxclientslimit) { n = svs.maxclientslimit; Con_Printf ("\"maxplayers\" set to \"%d\"\n", n); } if ((n == 1) && listening) Cbuf_AddText ("listen 0\n"); if ((n > 1) && (!listening)) Cbuf_AddText ("listen 1\n"); svs.maxclients = n; if (n == 1) Cvar_Set ("deathmatch", "0"); else Cvar_Set ("deathmatch", "1"); } static void NET_Port_f (void) { int n; if (Cmd_Argc () != 2) { Con_Printf ("\"port\" is \"%d\"\n", net_hostport); return; } n = atoi(Cmd_Argv(1)); if (n < 1 || n > 65534) { Con_Printf ("Bad value, must be between 1 and 65534\n"); return; } DEFAULTnet_hostport = n; net_hostport = n; if (listening) { // force a change to the new port Cbuf_AddText ("listen 0\n"); Cbuf_AddText ("listen 1\n"); } } static void PrintSlistHeader(void) { Con_Printf("Server Connect Map Users\n"); Con_Printf("----------- -------- ----------- -----\n"); slistLastShown = 0; } static void PrintSlist(void) { int n; const char *name; for (n = slistLastShown; n < hostCacheCount; n++) { if (hostcache[n].driver == 0) name = net_drivers[hostcache[n].driver].name; else name = net_landrivers[hostcache[n].ldriver].name; if (hostcache[n].maxusers) Con_Printf("%-11.11s %-8.8s %-10.10s %2d/%2d\n", hostcache[n].name, name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers); else Con_Printf("%-11.11s %-8.8s %-10.10s\n", hostcache[n].name, name, hostcache[n].map); } slistLastShown = n; } static void PrintSlistTrailer(void) { if (hostCacheCount) Con_Printf("== end list ==\n\n"); else Con_Printf("No Hexen II servers found.\n\n"); } void NET_Slist_f (void) { if (slistInProgress) return; if (! slistSilent) { Con_Printf("Looking for Hexen II servers...\n"); PrintSlistHeader(); } slistInProgress = true; slistStartTime = Sys_DoubleTime(); SchedulePollProcedure(&slistSendProcedure, 0.0); SchedulePollProcedure(&slistPollProcedure, 0.1); hostCacheCount = 0; } void NET_SlistSort (void) { if (hostCacheCount > 1) { int i, j; hostcache_t temp; for (i = 0; i < hostCacheCount; i++) { for (j = i + 1; j < hostCacheCount; j++) { if (strcmp(hostcache[j].name, hostcache[i].name) < 0) { memcpy(&temp, &hostcache[j], sizeof(hostcache_t)); memcpy(&hostcache[j], &hostcache[i], sizeof(hostcache_t)); memcpy(&hostcache[i], &temp, sizeof(hostcache_t)); } } } } } const char *NET_SlistPrintServer (int idx) { static char string[64]; const char *name; if (idx < 0 || idx >= hostCacheCount) return ""; if (hostcache[idx].driver == 0) name = net_drivers[hostcache[idx].driver].name; else name = net_landrivers[hostcache[idx].ldriver].name; if (hostcache[idx].maxusers) { q_snprintf(string, sizeof(string), "%-11.11s %-8.8s %-10.10s %2d/%2d\n", hostcache[idx].name, name, hostcache[idx].map, hostcache[idx].users, hostcache[idx].maxusers); } else { q_snprintf(string, sizeof(string), "%-11.11s %-8.8s %-10.10s\n", hostcache[idx].name, name, hostcache[idx].map); } return string; } const char *NET_SlistPrintServerName (int idx) { if (idx < 0 || idx >= hostCacheCount) return ""; return hostcache[idx].cname; } static void Slist_Send (void *unused) { for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++) { if (!slistLocal && IS_LOOP_DRIVER(net_driverlevel)) continue; if (net_drivers[net_driverlevel].initialized == false) continue; dfunc.SearchForHosts (true); } if ((Sys_DoubleTime() - slistStartTime) < 0.5) SchedulePollProcedure(&slistSendProcedure, 0.75); } static void Slist_Poll (void *unused) { for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++) { if (!slistLocal && IS_LOOP_DRIVER(net_driverlevel)) continue; if (net_drivers[net_driverlevel].initialized == false) continue; dfunc.SearchForHosts (false); } if (! slistSilent) PrintSlist(); if ((Sys_DoubleTime() - slistStartTime) < 1.5) { SchedulePollProcedure(&slistPollProcedure, 0.1); return; } if (! slistSilent) PrintSlistTrailer(); slistInProgress = false; slistSilent = false; slistLocal = true; } /* =================== NET_Connect =================== */ int hostCacheCount = 0; hostcache_t hostcache[HOSTCACHESIZE]; qsocket_t *NET_Connect (const char *host) { qsocket_t *ret; int n; SetNetTime(); if (host && *host == 0) host = NULL; if (host && hostCacheCount) { for (n = 0; n < hostCacheCount; n++) { if (q_strcasecmp (host, hostcache[n].name) == 0) { host = hostcache[n].cname; break; } } if (n < hostCacheCount) goto JustDoIt; } slistSilent = host ? true : false; NET_Slist_f (); while (slistInProgress) NET_Poll(); if (host == NULL) { if (hostCacheCount != 1) return NULL; host = hostcache[0].cname; Con_Printf("Connecting to...\n%s @ %s\n\n", hostcache[0].name, host); } if (hostCacheCount) { for (n = 0; n < hostCacheCount; n++) { if (q_strcasecmp (host, hostcache[n].name) == 0) { host = hostcache[n].cname; break; } } } JustDoIt: for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++) { if (net_drivers[net_driverlevel].initialized == false) continue; ret = dfunc.Connect (host); if (ret) return ret; } if (host) { Con_Printf("\n"); PrintSlistHeader(); PrintSlist(); PrintSlistTrailer(); } return NULL; } #endif /* SERVERONLY */ /* =================== NET_CheckNewConnections =================== */ qsocket_t *NET_CheckNewConnections (void) { qsocket_t *ret; SetNetTime(); for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++) { if (net_drivers[net_driverlevel].initialized == false) continue; if (!IS_LOOP_DRIVER(net_driverlevel) && listening == false) continue; ret = dfunc.CheckNewConnections (); if (ret) { return ret; } } return NULL; } /* =================== NET_Close =================== */ void NET_Close (qsocket_t *sock) { if (!sock) return; if (sock->disconnected) return; SetNetTime(); // call the driver_Close function sfunc.Close (sock); NET_FreeQSocket(sock); } /* ================= NET_GetMessage If there is a complete message, return it in net_message returns 0 if no data is waiting returns 1 if a message was received returns -1 if connection is invalid ================= */ int NET_GetMessage (qsocket_t *sock) { int ret; if (!sock) return -1; if (sock->disconnected) { Con_Printf("%s: disconnected socket\n", __thisfunc__); return -1; } SetNetTime(); ret = sfunc.QGetMessage(sock); // see if this connection has timed out if (ret == 0 && !IS_LOOP_DRIVER(sock->driver)) { if (net_time - sock->lastMessageTime > net_messagetimeout.value) { NET_Close(sock); return -1; } } if (ret > 0) { if (!IS_LOOP_DRIVER(sock->driver)) { sock->lastMessageTime = net_time; if (ret == 1) messagesReceived++; else if (ret == 2) unreliableMessagesReceived++; } } return ret; } /* ================== NET_SendMessage Try to send a complete length+message unit over the reliable stream. returns 0 if the message cannot be delivered reliably, but the connection is still considered valid returns 1 if the message was sent properly returns -1 if the connection died ================== */ int NET_SendMessage (qsocket_t *sock, sizebuf_t *data) { int r; if (!sock) return -1; if (sock->disconnected) { Con_Printf("%s: disconnected socket\n", __thisfunc__); return -1; } SetNetTime(); r = sfunc.QSendMessage(sock, data); if (r == 1 && !IS_LOOP_DRIVER(sock->driver)) messagesSent++; return r; } int NET_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data) { int r; if (!sock) return -1; if (sock->disconnected) { Con_Printf("%s: disconnected socket\n", __thisfunc__); return -1; } SetNetTime(); r = sfunc.SendUnreliableMessage(sock, data); if (r == 1 && !IS_LOOP_DRIVER(sock->driver)) unreliableMessagesSent++; return r; } /* ================== NET_CanSendMessage Returns true or false if the given qsocket can currently accept a message to be transmitted. ================== */ qboolean NET_CanSendMessage (qsocket_t *sock) { if (!sock) return false; if (sock->disconnected) return false; SetNetTime(); return sfunc.CanSendMessage(sock); } int NET_SendToAll (sizebuf_t *data, double blocktime) { double start; int i; int count = 0; qboolean msg_init[MAX_CLIENTS]; /* did we write the message to the client's connection */ qboolean msg_sent[MAX_CLIENTS]; /* did the msg arrive its destination (canSend state). */ for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if (host_client->netconnection && host_client->active) { #if !defined(NO_LOOP_DRIVER) if (IS_LOOP_DRIVER(host_client->netconnection->driver)) { NET_SendMessage(host_client->netconnection, data); msg_init[i] = true; msg_sent[i] = true; continue; } #endif count++; msg_init[i] = false; msg_sent[i] = false; } else { msg_init[i] = true; msg_sent[i] = true; } } start = Sys_DoubleTime(); while (count) { count = 0; for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if (! msg_init[i]) { if (NET_CanSendMessage (host_client->netconnection)) { msg_init[i] = true; NET_SendMessage(host_client->netconnection, data); } else { NET_GetMessage (host_client->netconnection); } count++; continue; } if (! msg_sent[i]) { if (NET_CanSendMessage (host_client->netconnection)) { msg_sent[i] = true; } else { NET_GetMessage (host_client->netconnection); } count++; continue; } } if ((Sys_DoubleTime() - start) > blocktime) break; } return count; } //============================================================================= /* ==================== NET_Init ==================== */ void NET_Init (void) { int i; qsocket_t *s; i = COM_CheckParm ("-port"); if (!i) i = COM_CheckParm ("-udpport"); if (!i) i = COM_CheckParm ("-ipxport"); if (i) { if (i < com_argc-1) DEFAULTnet_hostport = atoi (com_argv[i+1]); else Con_SafePrintf("%s: ignoring -port argument\n", __thisfunc__); } net_hostport = DEFAULTnet_hostport; net_numsockets = svs.maxclientslimit; #if !defined(SERVERONLY) if (cls.state != ca_dedicated) net_numsockets++; if (COM_CheckParm("-listen") || cls.state == ca_dedicated) listening = true; #endif /* SERVERONLY */ SetNetTime(); for (i = 0; i < net_numsockets; i++) { s = (qsocket_t *)Hunk_AllocName(sizeof(qsocket_t), "qsocket"); s->next = net_freeSockets; net_freeSockets = s; s->disconnected = true; } // allocate space for network message buffer // SZ_Init (&net_message, net_message_buffer, sizeof(net_message_buffer)); SZ_Init (&net_message, NULL, NET_MAXMESSAGE); Cvar_RegisterVariable (&net_messagetimeout); Cvar_RegisterVariable (&hostname); Cvar_RegisterVariable (&net_allowmultiple); #if defined(NET_USE_SERIAL) Cvar_RegisterVariable (&config_com_port); Cvar_RegisterVariable (&config_com_irq); Cvar_RegisterVariable (&config_com_baud); Cvar_RegisterVariable (&config_com_modem); Cvar_RegisterVariable (&config_modem_dialtype); Cvar_RegisterVariable (&config_modem_clear); Cvar_RegisterVariable (&config_modem_init); Cvar_RegisterVariable (&config_modem_hangup); #endif /* NET_USE_SERIAL */ #if !defined(SERVERONLY) Cmd_AddCommand ("slist", NET_Slist_f); Cmd_AddCommand ("listen", NET_Listen_f); Cmd_AddCommand ("maxplayers", MaxPlayers_f); Cmd_AddCommand ("port", NET_Port_f); #endif /* SERVERONLY */ // initialize all the drivers for (i = net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++) { if (net_drivers[net_driverlevel].Init() == -1) continue; i++; net_drivers[net_driverlevel].initialized = true; if (listening) net_drivers[net_driverlevel].Listen (true); } /* Loop_Init() returns -1 for dedicated server case, * therefore the i == 0 check is correct */ if (i == 0 #if !defined(SERVERONLY) && cls.state == ca_dedicated #endif /* SERVERONLY */ ) { Sys_Error("Network not available!"); } if (*my_ipx_address) { Con_DPrintf("IPX address %s\n", my_ipx_address); } if (*my_tcpip_address) { Con_DPrintf("TCP/IP address %s\n", my_tcpip_address); } } /* ==================== NET_Shutdown ==================== */ void NET_Shutdown (void) { qsocket_t *sock; SetNetTime(); for (sock = net_activeSockets; sock; sock = sock->next) NET_Close(sock); // // shutdown the drivers // for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++) { if (net_drivers[net_driverlevel].initialized == true) { net_drivers[net_driverlevel].Shutdown (); net_drivers[net_driverlevel].initialized = false; } } } static PollProcedure *pollProcedureList = NULL; void NET_Poll(void) { PollProcedure *pp; #if defined(NET_USE_SERIAL) if (!configRestored) { if (serialAvailable) { qboolean useModem; if (config_com_modem.value == 1.0) useModem = true; else useModem = false; SetComPortConfig (0, config_com_port.integer, config_com_irq.integer, config_com_baud.integer, useModem); SetModemConfig (0, config_modem_dialtype.string, config_modem_clear.string, config_modem_init.string, config_modem_hangup.string); } configRestored = true; } #endif /* NET_USE_SERIAL */ SetNetTime(); for (pp = pollProcedureList; pp; pp = pp->next) { if (pp->nextTime > net_time) break; pollProcedureList = pp->next; pp->procedure(pp->arg); } } void SchedulePollProcedure(PollProcedure *proc, double timeOffset) { PollProcedure *pp, *prev; proc->nextTime = Sys_DoubleTime() + timeOffset; for (pp = pollProcedureList, prev = NULL; pp; pp = pp->next) { if (pp->nextTime >= proc->nextTime) break; prev = pp; } if (prev == NULL) { proc->next = pollProcedureList; pollProcedureList = proc; return; } proc->next = pp; prev->next = proc; } engine/hexen2/net_udp.c000066400000000000000000000406061444734033100153270ustar00rootroot00000000000000/* net_udp.c * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include "arch_def.h" #include "net_sys.h" #include #include "quakedef.h" #include "net_defs.h" static sys_socket_t net_acceptsocket = INVALID_SOCKET; // socket for fielding new connections static sys_socket_t net_controlsocket; static sys_socket_t net_broadcastsocket = 0; static struct sockaddr_in broadcastaddr; static char ifname[IFNAMSIZ]; static struct in_addr myAddr, // the local address returned by the OS. localAddr, // address to advertise by embedding in // CCREP_SERVER_INFO and CCREP_ACCEPT // response packets instead of the default // returned by the OS. from command line // argument -localip , used // by GetSocketAddr() bindAddr; // the address that we bind to instead of // INADDR_ANY. from the command line args // -ip #include "net_udp.h" #if defined(PLATFORM_AMIGA) struct Library *SocketBase; #endif //============================================================================= static int udp_scan_iface (sys_socket_t socketfd) { #if defined(SIOCGIFCONF) && defined(SIOCGIFADDR) struct ifconf ifc; struct ifreq *ifr; char buf[8192]; int i, n; struct sockaddr_in *iaddr; struct in_addr addr; if (COM_CheckParm("-noifscan")) return -1; ifc.ifc_len = (int) sizeof(buf); ifc.ifc_buf = (caddr_t) buf; if (ioctlsocket(socketfd, SIOCGIFCONF, IOCTLARG_P(&ifc)) == -1) { n = SOCKETERRNO; Con_SafePrintf("%s: SIOCGIFCONF failed (%s)\n", __thisfunc__, socketerror(n)); return -1; } ifr = ifc.ifc_req; n = ifc.ifc_len / sizeof(struct ifreq); for (i = 0; i < n; i++) { if (ioctlsocket(socketfd, SIOCGIFADDR, IOCTLARG_P(&ifr[i])) == -1) continue; iaddr = (struct sockaddr_in *) &ifr[i].ifr_addr; Con_SafeDPrintf("%s: %s\n", ifr[i].ifr_name, inet_ntoa(iaddr->sin_addr)); addr.s_addr = iaddr->sin_addr.s_addr; if (addr.s_addr != htonl(INADDR_LOOPBACK)) { myAddr.s_addr = addr.s_addr; strcpy (ifname, ifr[i].ifr_name); return 0; } } #endif /**/ return -1; } sys_socket_t UDP_Init (void) { int i, err; char *tst; char buff[MAXHOSTNAMELEN]; struct hostent *local; struct qsockaddr addr; if (COM_CheckParm ("-noudp")) return INVALID_SOCKET; #if defined(PLATFORM_AMIGA) SocketBase = OpenLibrary("bsdsocket.library", 0); if (!SocketBase) { Con_SafePrintf("%s: Can't open bsdsocket.library\n", __thisfunc__); return INVALID_SOCKET; } #endif /* PLATFORM_AMIGA */ #if defined(PLATFORM_OS2) && !defined(__EMX__) if (sock_init() < 0) { Con_SafePrintf("%s: Can't initialize IBM OS/2 sockets\n", __thisfunc__); return INVALID_SOCKET; } #endif /* PLATFORM_OS2 */ #if defined(PLATFORM_DOS) #if defined(USE_WATT32) if (ipxAvailable) /* IPX + PktDrvr don't get along */ { Con_Printf("Skipping WATTCP (IPX present)\n"); return INVALID_SOCKET; } /* dbug_init();*/ i = _watt_do_exit; _watt_do_exit = 0; err = sock_init(); _watt_do_exit = i; if (err != 0) { Con_Printf("WATTCP initialization failed (%s)\n", sock_init_err(err)); return INVALID_SOCKET; } #endif #endif /* PLATFORM_DOS */ // determine my name & address myAddr.s_addr = htonl(INADDR_LOOPBACK); if (gethostname(buff, MAXHOSTNAMELEN) != 0) { err = SOCKETERRNO; Con_SafePrintf("%s: WARNING: gethostname failed (%s)\n", __thisfunc__, socketerror(err)); } else { buff[MAXHOSTNAMELEN - 1] = 0; #ifdef PLATFORM_OSX // ericw -- if our hostname ends in ".local" (a macOS thing), // don't bother calling gethostbyname(), because it blocks for a few seconds // and then fails (on my system anyway.) tst = strstr(buff, ".local"); if (tst && tst[6] == '\0') { Con_SafePrintf("%s: skipping gethostbyname for %s\n", __thisfunc__, buff); } else #endif if (!(local = gethostbyname(buff))) { Con_SafePrintf("%s: WARNING: gethostbyname failed (%s)\n", __thisfunc__, hstrerror(h_errno)); } else if (local->h_addrtype != AF_INET) { Con_SafePrintf("%s: address from gethostbyname not IPv4\n", __thisfunc__); } else { myAddr = *(struct in_addr *)local->h_addr_list[0]; } } // check for interface binding option i = COM_CheckParm("-ip"); if (i == 0) i = COM_CheckParm("-bindip"); if (i && i < com_argc - 1) { bindAddr.s_addr = inet_addr(com_argv[i + 1]); if (bindAddr.s_addr == INADDR_NONE) { Sys_Error("%s: %s is not a valid IP address", __thisfunc__, com_argv[i + 1]); } Con_SafePrintf("Binding to IP Interface Address of %s\n", com_argv[i + 1]); } else { bindAddr.s_addr = INADDR_NONE; } // check for ip advertise option i = COM_CheckParm("-localip"); if (i && i < com_argc - 1) { localAddr.s_addr = inet_addr(com_argv[i + 1]); if (localAddr.s_addr == INADDR_NONE) { Sys_Error("%s: %s is not a valid IP address", __thisfunc__, com_argv[i + 1]); } Con_SafePrintf("Advertising %s as the local IP in response packets\n", com_argv[i + 1]); } else { localAddr.s_addr = INADDR_NONE; } if ((net_controlsocket = UDP_OpenSocket(0)) == INVALID_SOCKET) { Con_SafePrintf("%s: Unable to open control socket, UDP disabled\n", __thisfunc__); return INVALID_SOCKET; } // myAddr may resolve to 127.0.0.1, see if we can do any better memset (ifname, 0, sizeof(ifname)); if (myAddr.s_addr == htonl(INADDR_LOOPBACK)) { if (udp_scan_iface(net_controlsocket) == 0) { Con_SafePrintf ("UDP, Local address: %s (%s)\n", inet_ntoa(myAddr), ifname); } } if (ifname[0] == 0) { Con_SafePrintf("UDP, Local address: %s\n", inet_ntoa(myAddr)); } broadcastaddr.sin_family = AF_INET; broadcastaddr.sin_addr.s_addr = INADDR_BROADCAST; broadcastaddr.sin_port = htons((unsigned short)net_hostport); UDP_GetSocketAddr (net_controlsocket, &addr); strcpy(my_tcpip_address, UDP_AddrToString (&addr)); tst = strrchr(my_tcpip_address, ':'); if (tst) *tst = 0; Con_SafePrintf("UDP Initialized\n"); tcpipAvailable = true; return net_controlsocket; } //============================================================================= void UDP_Shutdown (void) { UDP_Listen (false); UDP_CloseSocket (net_controlsocket); #if defined(PLATFORM_AMIGA) if (SocketBase) { CloseLibrary(SocketBase); SocketBase = NULL; } #endif /* PLATFORM_AMIGA */ } //============================================================================= void UDP_Listen (qboolean state) { // enable listening if (state) { if (net_acceptsocket != INVALID_SOCKET) return; if ((net_acceptsocket = UDP_OpenSocket (net_hostport)) == INVALID_SOCKET) Sys_Error ("%s: Unable to open accept socket", __thisfunc__); return; } // disable listening if (net_acceptsocket == INVALID_SOCKET) return; UDP_CloseSocket (net_acceptsocket); net_acceptsocket = INVALID_SOCKET; } //============================================================================= sys_socket_t UDP_OpenSocket (int port) { sys_socket_t newsocket; struct sockaddr_in address; #if defined(PLATFORM_WINDOWS) || defined(PLATFORM_DOS) u_long _true = 1; #else int _true = 1; #endif int err; if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) { err = SOCKETERRNO; Con_SafePrintf("%s: %s\n", __thisfunc__, socketerror(err)); return INVALID_SOCKET; } if (ioctlsocket (newsocket, FIONBIO, IOCTLARG_P(&_true)) == SOCKET_ERROR) goto ErrorReturn; memset(&address, 0, sizeof(struct sockaddr_in)); address.sin_family = AF_INET; if (bindAddr.s_addr != INADDR_NONE) address.sin_addr.s_addr = bindAddr.s_addr; else address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons((unsigned short)port); if (bind (newsocket, (struct sockaddr *)&address, sizeof(address)) == 0) return newsocket; ErrorReturn: err = SOCKETERRNO; Con_SafePrintf("%s: %s\n", __thisfunc__, socketerror(err)); UDP_CloseSocket (newsocket); return INVALID_SOCKET; } //============================================================================= int UDP_CloseSocket (sys_socket_t socketid) { if (socketid == net_broadcastsocket) net_broadcastsocket = 0; return closesocket (socketid); } //============================================================================= /* ============ PartialIPAddress this lets you type only as much of the net address as required, using the local network components to fill in the rest ============ */ static int PartialIPAddress (const char *in, struct qsockaddr *hostaddr) { char buff[256]; char *b; int addr, mask, num, port, run; buff[0] = '.'; b = buff; strcpy(buff+1, in); if (buff[1] == '.') b++; addr = 0; mask = -1; while (*b == '.') { b++; num = 0; run = 0; while (!( *b < '0' || *b > '9')) { num = num*10 + *b++ - '0'; if (++run > 3) return -1; } if ((*b < '0' || *b > '9') && *b != '.' && *b != ':' && *b != 0) return -1; if (num < 0 || num > 255) return -1; mask <<= 8; addr = (addr<<8) + num; } if (*b++ == ':') port = atoi(b); else port = net_hostport; hostaddr->qsa_family = AF_INET; ((struct sockaddr_in *)hostaddr)->sin_port = htons((unsigned short)port); ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (myAddr.s_addr & htonl(mask)) | htonl(addr); return 0; } //============================================================================= int UDP_Connect (sys_socket_t socketid, struct qsockaddr *addr) { return 0; } //============================================================================= sys_socket_t UDP_CheckNewConnections (void) { #if defined(PLATFORM_AMIGA) char buf[4096]; if (net_acceptsocket == INVALID_SOCKET) return INVALID_SOCKET; if (recvfrom (net_acceptsocket, buf, sizeof(buf), MSG_PEEK, NULL, NULL) != SOCKET_ERROR) { return net_acceptsocket; } return INVALID_SOCKET; #else #if defined(PLATFORM_WINDOWS) || defined(PLATFORM_DOS) u_long available; #else int available; #endif struct sockaddr_in from; socklen_t fromlen; char buff[1]; if (net_acceptsocket == INVALID_SOCKET) return INVALID_SOCKET; if (ioctlsocket (net_acceptsocket, FIONREAD, IOCTLARG_P(&available)) == -1) { int err = SOCKETERRNO; Sys_Error ("UDP: ioctlsocket (FIONREAD) failed (%s)", socketerror(err)); } if (available) return net_acceptsocket; // quietly absorb empty packets recvfrom (net_acceptsocket, buff, 0, 0, (struct sockaddr *) &from, &fromlen); return INVALID_SOCKET; #endif } //============================================================================= int UDP_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr) { socklen_t addrlen = sizeof(struct qsockaddr); int ret; ret = recvfrom (socketid, buf, len, 0, (struct sockaddr *)addr, &addrlen); if (ret == SOCKET_ERROR) { int err = SOCKETERRNO; if (err == NET_EWOULDBLOCK || err == NET_ECONNREFUSED) return 0; Con_SafeDPrintf ("%s, recvfrom: %s\n", __thisfunc__, socketerror(err)); } return ret; } //============================================================================= static int UDP_MakeSocketBroadcastCapable (sys_socket_t socketid) { int i = 1; // make this socket broadcast capable if (setsockopt(socketid, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) == SOCKET_ERROR) { int err = SOCKETERRNO; Con_SafePrintf ("%s, setsockopt: %s\n", __thisfunc__, socketerror(err)); return -1; } net_broadcastsocket = socketid; return 0; } //============================================================================= int UDP_Broadcast (sys_socket_t socketid, byte *buf, int len) { int ret; if (socketid != net_broadcastsocket) { if (net_broadcastsocket != 0) Sys_Error("Attempted to use multiple broadcasts sockets"); ret = UDP_MakeSocketBroadcastCapable (socketid); if (ret == -1) { Con_Printf("Unable to make socket broadcast capable\n"); return ret; } } return UDP_Write (socketid, buf, len, (struct qsockaddr *)&broadcastaddr); } //============================================================================= int UDP_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr) { int ret; ret = sendto (socketid, buf, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr)); if (ret == SOCKET_ERROR) { int err = SOCKETERRNO; if (err == NET_EWOULDBLOCK) return 0; Con_SafeDPrintf ("%s, sendto: %s\n", __thisfunc__, socketerror(err)); } return ret; } //============================================================================= const char *UDP_AddrToString (struct qsockaddr *addr) { static char buffer[22]; int haddr; haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); q_snprintf (buffer, sizeof(buffer), "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff, (haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff, ntohs(((struct sockaddr_in *)addr)->sin_port)); return buffer; } //============================================================================= int UDP_StringToAddr (const char *string, struct qsockaddr *addr) { int ha1, ha2, ha3, ha4, hp, ipaddr; sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp); ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4; addr->qsa_family = AF_INET; ((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr); ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)hp); return 0; } //============================================================================= int UDP_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr) { socklen_t addrlen = sizeof(struct qsockaddr); struct sockaddr_in *address = (struct sockaddr_in *)addr; struct in_addr a; memset(addr, 0, sizeof(struct qsockaddr)); getsockname(socketid, (struct sockaddr *)addr, &addrlen); /* * The returned IP is embedded in our repsonse to a broadcast * request for server info from clients. If the server admin * wishes to advertise a specific IP, then allow the "default" * address returned by the OS to be overridden. */ if (localAddr.s_addr != INADDR_NONE) address->sin_addr.s_addr = localAddr.s_addr; else { a = address->sin_addr; if (a.s_addr == 0 || a.s_addr == htonl(INADDR_LOOPBACK)) address->sin_addr.s_addr = myAddr.s_addr; } return 0; } //============================================================================= int UDP_GetNameFromAddr (struct qsockaddr *addr, char *name) { struct hostent *hostentry; hostentry = gethostbyaddr ((char *)&((struct sockaddr_in *)addr)->sin_addr, sizeof(struct in_addr), AF_INET); if (hostentry) { strncpy (name, (char *)hostentry->h_name, NET_NAMELEN - 1); return 0; } strcpy (name, UDP_AddrToString (addr)); return 0; } //============================================================================= int UDP_GetAddrFromName (const char *name, struct qsockaddr *addr) { struct hostent *hostentry; if (name[0] >= '0' && name[0] <= '9') return PartialIPAddress (name, addr); hostentry = gethostbyname (name); if (!hostentry) return -1; addr->qsa_family = AF_INET; ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)net_hostport); ((struct sockaddr_in *)addr)->sin_addr.s_addr = *(in_addr_t *)hostentry->h_addr_list[0]; return 0; } //============================================================================= int UDP_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2) { if (addr1->qsa_family != addr2->qsa_family) return -1; if (((struct sockaddr_in *)addr1)->sin_addr.s_addr != ((struct sockaddr_in *)addr2)->sin_addr.s_addr) return -1; if (((struct sockaddr_in *)addr1)->sin_port != ((struct sockaddr_in *)addr2)->sin_port) return 1; return 0; } //============================================================================= int UDP_GetSocketPort (struct qsockaddr *addr) { return ntohs(((struct sockaddr_in *)addr)->sin_port); } int UDP_SetSocketPort (struct qsockaddr *addr, int port) { ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)port); return 0; } //============================================================================= engine/hexen2/net_udp.h000066400000000000000000000035331444734033100153320ustar00rootroot00000000000000/* net_udp.h * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __net_udp_h #define __net_udp_h sys_socket_t UDP_Init (void); void UDP_Shutdown (void); void UDP_Listen (qboolean state); sys_socket_t UDP_OpenSocket (int port); int UDP_CloseSocket (sys_socket_t socketid); int UDP_Connect (sys_socket_t socketid, struct qsockaddr *addr); sys_socket_t UDP_CheckNewConnections (void); int UDP_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr); int UDP_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr); int UDP_Broadcast (sys_socket_t socketid, byte *buf, int len); const char *UDP_AddrToString (struct qsockaddr *addr); int UDP_StringToAddr (const char *string, struct qsockaddr *addr); int UDP_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr); int UDP_GetNameFromAddr (struct qsockaddr *addr, char *name); int UDP_GetAddrFromName (const char *name, struct qsockaddr *addr); int UDP_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2); int UDP_GetSocketPort (struct qsockaddr *addr); int UDP_SetSocketPort (struct qsockaddr *addr, int port); #endif /* __net_udp_h */ engine/hexen2/net_win.c000066400000000000000000000052271444734033100153340ustar00rootroot00000000000000/* net_win.c * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include "arch_def.h" #include "net_sys.h" #include "quakedef.h" #include "net_defs.h" #include "net_dgrm.h" #include "net_loop.h" net_driver_t net_drivers[] = { #if !defined(NO_LOOP_DRIVER) { "Loopback", false, Loop_Init, Loop_Listen, #if !defined(SERVERONLY) Loop_SearchForHosts, Loop_Connect, #endif Loop_CheckNewConnections, Loop_GetMessage, Loop_SendMessage, Loop_SendUnreliableMessage, Loop_CanSendMessage, Loop_CanSendUnreliableMessage, Loop_Close, Loop_Shutdown }, #endif /* NO_LOOP_DRIVER */ { "Datagram", false, Datagram_Init, Datagram_Listen, #if !defined(SERVERONLY) Datagram_SearchForHosts, Datagram_Connect, #endif Datagram_CheckNewConnections, Datagram_GetMessage, Datagram_SendMessage, Datagram_SendUnreliableMessage, Datagram_CanSendMessage, Datagram_CanSendUnreliableMessage, Datagram_Close, Datagram_Shutdown } }; const int net_numdrivers = (sizeof(net_drivers) / sizeof(net_drivers[0])); #include "net_wins.h" #include "net_wipx.h" net_landriver_t net_landrivers[] = { { "TCPIP", false, 0, WINS_Init, WINS_Shutdown, WINS_Listen, WINS_OpenSocket, WINS_CloseSocket, WINS_Connect, WINS_CheckNewConnections, WINS_Read, WINS_Write, WINS_Broadcast, WINS_AddrToString, WINS_StringToAddr, WINS_GetSocketAddr, WINS_GetNameFromAddr, WINS_GetAddrFromName, WINS_AddrCompare, WINS_GetSocketPort, WINS_SetSocketPort }, { "IPX", false, 0, WIPX_Init, WIPX_Shutdown, WIPX_Listen, WIPX_OpenSocket, WIPX_CloseSocket, WIPX_Connect, WIPX_CheckNewConnections, WIPX_Read, WIPX_Write, WIPX_Broadcast, WIPX_AddrToString, WIPX_StringToAddr, WIPX_GetSocketAddr, WIPX_GetNameFromAddr, WIPX_GetAddrFromName, WIPX_AddrCompare, WIPX_GetSocketPort, WIPX_SetSocketPort } }; const int net_numlandrivers = (sizeof(net_landrivers) / sizeof(net_landrivers[0])); engine/hexen2/net_wins.c000066400000000000000000000347701444734033100155240ustar00rootroot00000000000000/* net_wins.c -- winsock udp driver * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include "arch_def.h" #include "net_sys.h" #include "quakedef.h" #include "net_defs.h" static sys_socket_t net_acceptsocket = INVALID_SOCKET; // socket for fielding new connections static sys_socket_t net_controlsocket; static sys_socket_t net_broadcastsocket = 0; static struct sockaddr_in broadcastaddr; static struct in_addr myAddr, // the local address returned by the OS. localAddr, // address to advertise by embedding in // CCREP_SERVER_INFO and CCREP_ACCEPT // response packets instead of the default // returned by the OS. from command line // argument -localip , used // by GetSocketAddr() bindAddr; // the address that we bind to instead of // INADDR_ANY. from the command line args // -ip #include "net_wins.h" int winsock_initialized = 0; WSADATA winsockdata; #define __wsaerr_static /* not static: used by net_wipx.c too */ #include "wsaerror.h" //============================================================================= #if !defined(_USE_WINSOCK2) static double blocktime; static INT_PTR PASCAL FAR BlockingHook (void) { MSG msg; BOOL ret; if ((Sys_DoubleTime() - blocktime) > 2.0) { WSACancelBlockingCall(); return FALSE; } /* get the next message, if any */ ret = (BOOL) PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); /* if we got one, process it */ if (ret) { TranslateMessage(&msg); DispatchMessage(&msg); } /* TRUE if we got a message */ return ret; } #endif /* ! _USE_WINSOCK2 */ sys_socket_t WINS_Init (void) { int i, err; char *colon; char buff[MAXHOSTNAMELEN]; struct hostent *local; struct qsockaddr addr; if (COM_CheckParm ("-noudp") || (winsock_initialized == -1)) return INVALID_SOCKET; if (winsock_initialized == 0) { err = WSAStartup(MAKEWORD(1,1), &winsockdata); if (err != 0) { winsock_initialized = -1; Con_SafePrintf("Winsock initialization failed (%s)\n", socketerror(err)); return INVALID_SOCKET; } } winsock_initialized++; // determine my name & address myAddr.s_addr = htonl(INADDR_LOOPBACK); if (gethostname(buff, MAXHOSTNAMELEN) != 0) { err = SOCKETERRNO; Con_SafePrintf("%s: WARNING: gethostname failed (%s)\n", __thisfunc__, socketerror(err)); } else { buff[MAXHOSTNAMELEN - 1] = 0; #if !defined(_USE_WINSOCK2) blocktime = Sys_DoubleTime(); WSASetBlockingHook(BlockingHook); #endif /* ! _USE_WINSOCK2 */ local = gethostbyname(buff); err = WSAGetLastError(); #if !defined(_USE_WINSOCK2) WSAUnhookBlockingHook(); #endif /* ! _USE_WINSOCK2 */ if (local == NULL) { Con_SafePrintf("%s: WARNING: gethostbyname failed (%s)\n", __thisfunc__, __WSAE_StrError(err)); } else if (local->h_addrtype != AF_INET) { Con_SafePrintf("%s: address from gethostbyname not IPv4\n", __thisfunc__); } else { myAddr = *(struct in_addr *)local->h_addr_list[0]; } } Con_SafePrintf("UDP, Local address: %s\n", inet_ntoa(myAddr)); // check for interface binding option i = COM_CheckParm("-ip"); if (i == 0) i = COM_CheckParm("-bindip"); if (i && i < com_argc - 1) { bindAddr.s_addr = inet_addr(com_argv[i + 1]); if (bindAddr.s_addr == INADDR_NONE) { Sys_Error("%s: %s is not a valid IP address", __thisfunc__, com_argv[i + 1]); } Con_SafePrintf("Binding to IP Interface Address of %s\n", com_argv[i + 1]); } else { bindAddr.s_addr = INADDR_NONE; } // check for ip advertise option i = COM_CheckParm("-localip"); if (i && i < com_argc - 1) { localAddr.s_addr = inet_addr(com_argv[i + 1]); if (localAddr.s_addr == INADDR_NONE) { Sys_Error("%s: %s is not a valid IP address", __thisfunc__, com_argv[i + 1]); } Con_SafePrintf("Advertising %s as the local IP in response packets\n", com_argv[i + 1]); } else { localAddr.s_addr = INADDR_NONE; } if ((net_controlsocket = WINS_OpenSocket(0)) == INVALID_SOCKET) { Con_SafePrintf("%s: Unable to open control socket, UDP disabled\n", __thisfunc__); if (--winsock_initialized == 0) WSACleanup (); return INVALID_SOCKET; } broadcastaddr.sin_family = AF_INET; broadcastaddr.sin_addr.s_addr = INADDR_BROADCAST; broadcastaddr.sin_port = htons((unsigned short)net_hostport); WINS_GetSocketAddr (net_controlsocket, &addr); strcpy(my_tcpip_address, WINS_AddrToString (&addr)); colon = strrchr (my_tcpip_address, ':'); if (colon) *colon = 0; Con_SafePrintf("UDP Initialized\n"); tcpipAvailable = true; return net_controlsocket; } //============================================================================= void WINS_Shutdown (void) { WINS_Listen (false); WINS_CloseSocket (net_controlsocket); if (--winsock_initialized == 0) WSACleanup (); } //============================================================================= void WINS_Listen (qboolean state) { // enable listening if (state) { if (net_acceptsocket != INVALID_SOCKET) return; if ((net_acceptsocket = WINS_OpenSocket (net_hostport)) == INVALID_SOCKET) Sys_Error ("%s: Unable to open accept socket", __thisfunc__); return; } // disable listening if (net_acceptsocket == INVALID_SOCKET) return; WINS_CloseSocket (net_acceptsocket); net_acceptsocket = INVALID_SOCKET; } //============================================================================= sys_socket_t WINS_OpenSocket (int port) { sys_socket_t newsocket; struct sockaddr_in address; u_long _true = 1; int err; if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) { err = SOCKETERRNO; Con_SafePrintf("%s: %s\n", __thisfunc__, socketerror(err)); return INVALID_SOCKET; } if (ioctlsocket (newsocket, FIONBIO, &_true) == SOCKET_ERROR) goto ErrorReturn; memset(&address, 0, sizeof(struct sockaddr_in)); address.sin_family = AF_INET; if (bindAddr.s_addr != INADDR_NONE) address.sin_addr.s_addr = bindAddr.s_addr; else address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons((unsigned short)port); if (bind (newsocket, (struct sockaddr *)&address, sizeof(address)) == 0) return newsocket; if (tcpipAvailable) { err = SOCKETERRNO; Sys_Error ("Unable to bind to %s (%s)", WINS_AddrToString ((struct qsockaddr *) &address), socketerror(err)); return INVALID_SOCKET; /* not reached */ } /* else: we are still in init phase, no need to error */ ErrorReturn: err = SOCKETERRNO; Con_SafePrintf("%s: %s\n", __thisfunc__, socketerror(err)); closesocket (newsocket); return INVALID_SOCKET; } //============================================================================= int WINS_CloseSocket (sys_socket_t socketid) { if (socketid == net_broadcastsocket) net_broadcastsocket = 0; return closesocket (socketid); } //============================================================================= /* ============ PartialIPAddress this lets you type only as much of the net address as required, using the local network components to fill in the rest ============ */ static int PartialIPAddress (const char *in, struct qsockaddr *hostaddr) { char buff[256]; char *b; int addr, mask, num, port, run; buff[0] = '.'; b = buff; strcpy(buff+1, in); if (buff[1] == '.') b++; addr = 0; mask = -1; while (*b == '.') { b++; num = 0; run = 0; while (!( *b < '0' || *b > '9')) { num = num*10 + *b++ - '0'; if (++run > 3) return -1; } if ((*b < '0' || *b > '9') && *b != '.' && *b != ':' && *b != 0) return -1; if (num < 0 || num > 255) return -1; mask <<= 8; addr = (addr<<8) + num; } if (*b++ == ':') port = atoi(b); else port = net_hostport; hostaddr->qsa_family = AF_INET; ((struct sockaddr_in *)hostaddr)->sin_port = htons((unsigned short)port); ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (myAddr.s_addr & htonl(mask)) | htonl(addr); return 0; } //============================================================================= int WINS_Connect (sys_socket_t socketid, struct qsockaddr *addr) { return 0; } //============================================================================= sys_socket_t WINS_CheckNewConnections (void) { char buf[4096]; if (net_acceptsocket == INVALID_SOCKET) return INVALID_SOCKET; if (recvfrom (net_acceptsocket, buf, sizeof(buf), MSG_PEEK, NULL, NULL) != SOCKET_ERROR) { return net_acceptsocket; } return INVALID_SOCKET; } //============================================================================= int WINS_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr) { socklen_t addrlen = sizeof(struct qsockaddr); int ret; ret = recvfrom (socketid, (char *)buf, len, 0, (struct sockaddr *)addr, &addrlen); if (ret == SOCKET_ERROR) { int err = SOCKETERRNO; if (err == NET_EWOULDBLOCK || err == NET_ECONNREFUSED) return 0; Con_SafeDPrintf ("%s, recvfrom: %s\n", __thisfunc__, socketerror(err)); } return ret; } //============================================================================= static int WINS_MakeSocketBroadcastCapable (sys_socket_t socketid) { int i = 1; // make this socket broadcast capable if (setsockopt(socketid, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) == SOCKET_ERROR) { int err = SOCKETERRNO; Con_SafePrintf ("%s, setsockopt: %s\n", __thisfunc__, socketerror(err)); return -1; } net_broadcastsocket = socketid; return 0; } //============================================================================= int WINS_Broadcast (sys_socket_t socketid, byte *buf, int len) { int ret; if (socketid != net_broadcastsocket) { if (net_broadcastsocket != 0) Sys_Error("Attempted to use multiple broadcasts sockets"); ret = WINS_MakeSocketBroadcastCapable (socketid); if (ret == -1) { Con_Printf("Unable to make socket broadcast capable\n"); return ret; } } return WINS_Write (socketid, buf, len, (struct qsockaddr *)&broadcastaddr); } //============================================================================= int WINS_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr) { int ret; ret = sendto (socketid, (char *)buf, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr)); if (ret == SOCKET_ERROR) { int err = SOCKETERRNO; if (err == NET_EWOULDBLOCK) return 0; Con_SafeDPrintf ("%s, sendto: %s\n", __thisfunc__, socketerror(err)); } return ret; } //============================================================================= const char *WINS_AddrToString (struct qsockaddr *addr) { static char buffer[22]; int haddr; haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); q_snprintf (buffer, sizeof(buffer), "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff, (haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff, ntohs(((struct sockaddr_in *)addr)->sin_port)); return buffer; } //============================================================================= int WINS_StringToAddr (const char *string, struct qsockaddr *addr) { int ha1, ha2, ha3, ha4, hp, ipaddr; sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp); ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4; addr->qsa_family = AF_INET; ((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr); ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)hp); return 0; } //============================================================================= int WINS_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr) { socklen_t addrlen = sizeof(struct qsockaddr); struct sockaddr_in *address = (struct sockaddr_in *)addr; struct in_addr a; memset(addr, 0, sizeof(struct qsockaddr)); getsockname(socketid, (struct sockaddr *)addr, &addrlen); /* * The returned IP is embedded in our repsonse to a broadcast * request for server info from clients. If the server admin * wishes to advertise a specific IP, then allow the "default" * address returned by the OS to be overridden. */ if (localAddr.s_addr != INADDR_NONE) address->sin_addr.s_addr = localAddr.s_addr; else { a = address->sin_addr; if (a.s_addr == 0 || a.s_addr == htonl(INADDR_LOOPBACK)) address->sin_addr.s_addr = myAddr.s_addr; } return 0; } //============================================================================= int WINS_GetNameFromAddr (struct qsockaddr *addr, char *name) { struct hostent *hostentry; hostentry = gethostbyaddr ((char *)&((struct sockaddr_in *)addr)->sin_addr, sizeof(struct in_addr), AF_INET); if (hostentry) { strncpy (name, (char *)hostentry->h_name, NET_NAMELEN - 1); return 0; } strcpy (name, WINS_AddrToString (addr)); return 0; } //============================================================================= int WINS_GetAddrFromName (const char *name, struct qsockaddr *addr) { struct hostent *hostentry; if (name[0] >= '0' && name[0] <= '9') return PartialIPAddress (name, addr); hostentry = gethostbyname (name); if (!hostentry) return -1; addr->qsa_family = AF_INET; ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)net_hostport); ((struct sockaddr_in *)addr)->sin_addr.s_addr = *(in_addr_t *)hostentry->h_addr_list[0]; return 0; } //============================================================================= int WINS_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2) { if (addr1->qsa_family != addr2->qsa_family) return -1; if (((struct sockaddr_in *)addr1)->sin_addr.s_addr != ((struct sockaddr_in *)addr2)->sin_addr.s_addr) return -1; if (((struct sockaddr_in *)addr1)->sin_port != ((struct sockaddr_in *)addr2)->sin_port) return 1; return 0; } //============================================================================= int WINS_GetSocketPort (struct qsockaddr *addr) { return ntohs(((struct sockaddr_in *)addr)->sin_port); } int WINS_SetSocketPort (struct qsockaddr *addr, int port) { ((struct sockaddr_in *)addr)->sin_port = htons((unsigned short)port); return 0; } //============================================================================= engine/hexen2/net_wins.h000066400000000000000000000036201444734033100155170ustar00rootroot00000000000000/* net_wins.h -- winsock udp driver * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __NET_WINSOCK_H #define __NET_WINSOCK_H sys_socket_t WINS_Init (void); void WINS_Shutdown (void); void WINS_Listen (qboolean state); sys_socket_t WINS_OpenSocket (int port); int WINS_CloseSocket (sys_socket_t socketid); int WINS_Connect (sys_socket_t socketid, struct qsockaddr *addr); sys_socket_t WINS_CheckNewConnections (void); int WINS_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr); int WINS_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr); int WINS_Broadcast (sys_socket_t socketid, byte *buf, int len); const char *WINS_AddrToString (struct qsockaddr *addr); int WINS_StringToAddr (const char *string, struct qsockaddr *addr); int WINS_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr); int WINS_GetNameFromAddr (struct qsockaddr *addr, char *name); int WINS_GetAddrFromName (const char *name, struct qsockaddr *addr); int WINS_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2); int WINS_GetSocketPort (struct qsockaddr *addr); int WINS_SetSocketPort (struct qsockaddr *addr, int port); #endif /* __NET_WINSOCK_H */ engine/hexen2/net_wipx.c000066400000000000000000000276671444734033100155420ustar00rootroot00000000000000/* net_wipx.c -- winsock ipx driver * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include "arch_def.h" #include "net_sys.h" #ifndef __LCC__ #include #else #define NSPROTO_IPX 1000 #define NSPROTO_SPX 1256 #define NSPROTO_SPXII 1257 typedef struct sockaddr_ipx { short sa_family; char sa_netnum[4]; char sa_nodenum[6]; unsigned short sa_socket; } SOCKADDR_IPX, *PSOCKADDR_IPX, *LPSOCKADDR_IPX; #endif #include "quakedef.h" #include "net_defs.h" #include "net_wipx.h" extern cvar_t hostname; static sys_socket_t net_acceptsocket = INVALID_SOCKET; // socket for fielding new connections static sys_socket_t net_controlsocket; static struct sockaddr_ipx broadcastaddr; /* externs from net_wins.c: */ extern int winsock_initialized; extern WSADATA winsockdata; extern const char *__WSAE_StrError (int); #define IPXSOCKETS 18 static sys_socket_t ipxsocket[IPXSOCKETS]; static int sequence[IPXSOCKETS]; //============================================================================= sys_socket_t WIPX_Init (void) { int i, err; char *colon; char buff[MAXHOSTNAMELEN]; struct qsockaddr addr; if (COM_CheckParm ("-noipx") || (winsock_initialized == -1)) return INVALID_SOCKET; if (winsock_initialized == 0) { err = WSAStartup(MAKEWORD(1,1), &winsockdata); if (err != 0) { winsock_initialized = -1; Con_SafePrintf("Winsock initialization failed (%s)\n", socketerror(err)); return INVALID_SOCKET; } } winsock_initialized++; for (i = 0; i < IPXSOCKETS; i++) ipxsocket[i] = 0; // determine my name & address if (gethostname(buff, MAXHOSTNAMELEN) != 0) { err = SOCKETERRNO; Con_SafePrintf("%s: WARNING: gethostname failed (%s)\n", __thisfunc__, socketerror(err)); } else { buff[MAXHOSTNAMELEN - 1] = 0; } if ((net_controlsocket = WIPX_OpenSocket(0)) == INVALID_SOCKET) { Con_SafePrintf("%s: Unable to open control socket, IPX disabled\n", __thisfunc__); if (--winsock_initialized == 0) WSACleanup (); return INVALID_SOCKET; } broadcastaddr.sa_family = AF_IPX; memset(broadcastaddr.sa_netnum, 0, 4); memset(broadcastaddr.sa_nodenum, 0xff, 6); broadcastaddr.sa_socket = htons((unsigned short)net_hostport); WIPX_GetSocketAddr (net_controlsocket, &addr); strcpy(my_ipx_address, WIPX_AddrToString (&addr)); colon = strrchr (my_ipx_address, ':'); if (colon) *colon = 0; Con_SafePrintf("IPX Initialized\n"); ipxAvailable = true; return net_controlsocket; } //============================================================================= void WIPX_Shutdown (void) { WIPX_Listen (false); WIPX_CloseSocket (net_controlsocket); if (--winsock_initialized == 0) WSACleanup (); } //============================================================================= void WIPX_Listen (qboolean state) { // enable listening if (state) { if (net_acceptsocket != INVALID_SOCKET) return; if ((net_acceptsocket = WIPX_OpenSocket (net_hostport)) == INVALID_SOCKET) Sys_Error ("%s: Unable to open accept socket", __thisfunc__); return; } // disable listening if (net_acceptsocket == INVALID_SOCKET) return; WIPX_CloseSocket (net_acceptsocket); net_acceptsocket = INVALID_SOCKET; } //============================================================================= sys_socket_t WIPX_OpenSocket (int port) { int err; sys_socket_t handle, newsocket; struct sockaddr_ipx address; u_long _true = 1; for (handle = 0; handle < IPXSOCKETS; handle++) { if (ipxsocket[handle] == 0) break; } if (handle == IPXSOCKETS) { Con_SafePrintf("%s: Out of free IPX handles.\n", __thisfunc__); return INVALID_SOCKET; } if ((newsocket = socket (AF_IPX, SOCK_DGRAM, NSPROTO_IPX)) == INVALID_SOCKET) { err = SOCKETERRNO; Con_SafePrintf("%s: %s\n", __thisfunc__, socketerror(err)); return INVALID_SOCKET; } if (ioctlsocket (newsocket, FIONBIO, &_true) == SOCKET_ERROR) goto ErrorReturn; if (setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof(_true)) == SOCKET_ERROR) goto ErrorReturn; memset(&address, 0, sizeof(struct sockaddr_ipx)); address.sa_family = AF_IPX; address.sa_socket = htons((unsigned short)port); if (bind (newsocket, (struct sockaddr *)&address, sizeof(address)) == 0) { ipxsocket[handle] = newsocket; sequence[handle] = 0; return handle; } if (ipxAvailable) { err = SOCKETERRNO; Sys_Error ("IPX bind failed (%s)", socketerror(err)); return INVALID_SOCKET; /* not reached */ } /* else: we are still in init phase, no need to error */ ErrorReturn: err = SOCKETERRNO; Con_SafePrintf("%s: %s\n", __thisfunc__, socketerror(err)); closesocket (newsocket); return INVALID_SOCKET; } //============================================================================= int WIPX_CloseSocket (sys_socket_t handle) { sys_socket_t socketid = ipxsocket[handle]; int ret; ret = closesocket (socketid); ipxsocket[handle] = 0; return ret; } //============================================================================= int WIPX_Connect (sys_socket_t handle, struct qsockaddr *addr) { return 0; } //============================================================================= sys_socket_t WIPX_CheckNewConnections (void) { u_long available; if (net_acceptsocket == INVALID_SOCKET) return INVALID_SOCKET; if (ioctlsocket (ipxsocket[net_acceptsocket], FIONREAD, &available) == SOCKET_ERROR) { int err = SOCKETERRNO; Sys_Error ("WIPX: ioctlsocket (FIONREAD) failed (%s)", socketerror(err)); } if (available) return net_acceptsocket; return INVALID_SOCKET; } //============================================================================= //static byte netpacketBuffer[NET_DATAGRAMSIZE + 4]; static byte netpacketBuffer[NET_MAXMESSAGE + 4]; //_Datagram_SearchForHosts calls this with net_message.maxsize as len! int WIPX_Read (sys_socket_t handle, byte *buf, int len, struct qsockaddr *addr) { socklen_t addrlen = sizeof(struct qsockaddr); sys_socket_t socketid = ipxsocket[handle]; int ret; ret = recvfrom (socketid, (char *)netpacketBuffer, len+4, 0, (struct sockaddr *)addr, &addrlen); if (ret == SOCKET_ERROR) { int err = SOCKETERRNO; if (err == NET_EWOULDBLOCK || err == NET_ECONNREFUSED) return 0; Con_SafeDPrintf ("%s, recvfrom: %s\n", __thisfunc__, socketerror(err)); } if (ret < 4) return 0; // remove sequence number, it's only needed for DOS IPX ret -= 4; memcpy(buf, netpacketBuffer+4, ret); return ret; } //============================================================================= int WIPX_Broadcast (sys_socket_t handle, byte *buf, int len) { return WIPX_Write (handle, buf, len, (struct qsockaddr *)&broadcastaddr); } //============================================================================= int WIPX_Write (sys_socket_t handle, byte *buf, int len, struct qsockaddr *addr) { sys_socket_t socketid = ipxsocket[handle]; int ret; // build packet with sequence number memcpy(&netpacketBuffer[0], &sequence[handle], 4); sequence[handle]++; memcpy(&netpacketBuffer[4], buf, len); len += 4; ret = sendto (socketid, (char *)netpacketBuffer, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr)); if (ret == SOCKET_ERROR) { int err = SOCKETERRNO; if (err == NET_EWOULDBLOCK) return 0; Con_SafeDPrintf ("%s, sendto: %s\n", __thisfunc__, socketerror(err)); } return ret; } //============================================================================= const char *WIPX_AddrToString (struct qsockaddr *addr) { static char buf[28]; sprintf(buf, "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%u", ((struct sockaddr_ipx *)addr)->sa_netnum[0] & 0xff, ((struct sockaddr_ipx *)addr)->sa_netnum[1] & 0xff, ((struct sockaddr_ipx *)addr)->sa_netnum[2] & 0xff, ((struct sockaddr_ipx *)addr)->sa_netnum[3] & 0xff, ((struct sockaddr_ipx *)addr)->sa_nodenum[0] & 0xff, ((struct sockaddr_ipx *)addr)->sa_nodenum[1] & 0xff, ((struct sockaddr_ipx *)addr)->sa_nodenum[2] & 0xff, ((struct sockaddr_ipx *)addr)->sa_nodenum[3] & 0xff, ((struct sockaddr_ipx *)addr)->sa_nodenum[4] & 0xff, ((struct sockaddr_ipx *)addr)->sa_nodenum[5] & 0xff, ntohs(((struct sockaddr_ipx *)addr)->sa_socket) ); return buf; } //============================================================================= int WIPX_StringToAddr (const char *string, struct qsockaddr *addr) { int val; char buf[3]; buf[2] = 0; memset(addr, 0, sizeof(struct qsockaddr)); addr->qsa_family = AF_IPX; #define DO(src,dest) do { \ buf[0] = string[src]; \ buf[1] = string[src + 1]; \ if (sscanf (buf, "%x", &val) != 1) \ return -1; \ ((struct sockaddr_ipx *)addr)->dest = val; \ } while (0) DO(0, sa_netnum[0]); DO(2, sa_netnum[1]); DO(4, sa_netnum[2]); DO(6, sa_netnum[3]); DO(9, sa_nodenum[0]); DO(11, sa_nodenum[1]); DO(13, sa_nodenum[2]); DO(15, sa_nodenum[3]); DO(17, sa_nodenum[4]); DO(19, sa_nodenum[5]); #undef DO sscanf (&string[22], "%u", &val); ((struct sockaddr_ipx *)addr)->sa_socket = htons((unsigned short)val); return 0; } //============================================================================= int WIPX_GetSocketAddr (sys_socket_t handle, struct qsockaddr *addr) { sys_socket_t socketid = ipxsocket[handle]; socklen_t addrlen = sizeof(struct qsockaddr); memset(addr, 0, sizeof(struct qsockaddr)); if (getsockname(socketid, (struct sockaddr *)addr, &addrlen) != 0) { int err = SOCKETERRNO; /* FIXME: what action should be taken?... */ Con_SafePrintf ("%s, getsockname: %s\n", __thisfunc__, socketerror(err)); } return 0; } //============================================================================= int WIPX_GetNameFromAddr (struct qsockaddr *addr, char *name) { strcpy(name, WIPX_AddrToString(addr)); return 0; } //============================================================================= int WIPX_GetAddrFromName (const char *name, struct qsockaddr *addr) { int n; char buf[32]; n = strlen(name); if (n == 12) { sprintf(buf, "00000000:%s:%u", name, net_hostport); return WIPX_StringToAddr (buf, addr); } if (n == 21) { sprintf(buf, "%s:%u", name, net_hostport); return WIPX_StringToAddr (buf, addr); } if (n > 21 && n <= 27) return WIPX_StringToAddr (name, addr); return -1; } //============================================================================= int WIPX_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2) { if (addr1->qsa_family != addr2->qsa_family) return -1; if (*((struct sockaddr_ipx *)addr1)->sa_netnum && *((struct sockaddr_ipx *)addr2)->sa_netnum) { if (memcmp(((struct sockaddr_ipx *)addr1)->sa_netnum, ((struct sockaddr_ipx *)addr2)->sa_netnum, 4) != 0) return -1; } if (memcmp(((struct sockaddr_ipx *)addr1)->sa_nodenum, ((struct sockaddr_ipx *)addr2)->sa_nodenum, 6) != 0) return -1; if (((struct sockaddr_ipx *)addr1)->sa_socket != ((struct sockaddr_ipx *)addr2)->sa_socket) { if (net_allowmultiple.integer) return -1; else return 1; } return 0; } //============================================================================= int WIPX_GetSocketPort (struct qsockaddr *addr) { return ntohs(((struct sockaddr_ipx *)addr)->sa_socket); } int WIPX_SetSocketPort (struct qsockaddr *addr, int port) { ((struct sockaddr_ipx *)addr)->sa_socket = htons((unsigned short)port); return 0; } //============================================================================= engine/hexen2/net_wipx.h000066400000000000000000000036151444734033100155320ustar00rootroot00000000000000/* net_wipx.h -- winsock ipx driver * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __NET_WINIPX_H #define __NET_WINIPX_H sys_socket_t WIPX_Init (void); void WIPX_Shutdown (void); void WIPX_Listen (qboolean state); sys_socket_t WIPX_OpenSocket (int port); int WIPX_CloseSocket (sys_socket_t socketid); int WIPX_Connect (sys_socket_t socketid, struct qsockaddr *addr); sys_socket_t WIPX_CheckNewConnections (void); int WIPX_Read (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr); int WIPX_Write (sys_socket_t socketid, byte *buf, int len, struct qsockaddr *addr); int WIPX_Broadcast (sys_socket_t socketid, byte *buf, int len); const char *WIPX_AddrToString (struct qsockaddr *addr); int WIPX_StringToAddr (const char *string, struct qsockaddr *addr); int WIPX_GetSocketAddr (sys_socket_t socketid, struct qsockaddr *addr); int WIPX_GetNameFromAddr (struct qsockaddr *addr, char *name); int WIPX_GetAddrFromName (const char *name, struct qsockaddr *addr); int WIPX_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2); int WIPX_GetSocketPort (struct qsockaddr *addr); int WIPX_SetSocketPort (struct qsockaddr *addr, int port); #endif /* __NET_WINIPX_H */ engine/hexen2/particle.h000066400000000000000000000037331444734033100155010ustar00rootroot00000000000000/* * particle.h -- particle enums and types: note that hexen2 and * hexenworld versions of these are different !!! * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __PARTICLE_H #define __PARTICLE_H #define PARTICLE_Z_CLIP 8.0 typedef enum { pt_static, pt_grav, pt_fastgrav, pt_slowgrav, pt_fire, pt_explode, pt_explode2, pt_blob, pt_blob2, pt_rain, pt_c_explode, pt_c_explode2, pt_spit, pt_fireball, pt_ice, pt_spell, pt_test, pt_quake, pt_rd, /* rider's death */ pt_vorpal, pt_setstaff, pt_magicmissile, pt_boneshard, pt_scarab, pt_acidball, pt_darken, pt_snow, pt_gravwell, pt_redfire } ptype_t; typedef enum { rt_rocket_trail = 0, rt_smoke, rt_blood, rt_tracer, rt_slight_blood, rt_tracer2, rt_voor_trail, rt_fireball, rt_ice, rt_spit, rt_spell, rt_vorpal, rt_setstaff, rt_magicmissile, rt_boneshard, rt_scarab, rt_acidball, rt_bloodshot } rt_type_t; typedef struct particle_s { /* driver-usable fields */ vec3_t org; float color; /* drivers never touch the following fields */ struct particle_s *next; vec3_t vel; vec3_t min_org; vec3_t max_org; float ramp; float die; byte type; byte flags; byte count; } particle_t; #endif /* __PARTICLE_H */ engine/hexen2/progdefs.h000066400000000000000000000240051444734033100155020ustar00rootroot00000000000000/* * progdefs.h -- PROGS structures: Generated by HCC. Do not * modify unless you know what you are doing. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __PROGDEFS_H #define __PROGDEFS_H /* glabals structure for Hexen II v1.03 progs: */ /* found in the cdrom version and the old demo: * randomclass NOT in globalvars_t, hence the CRC * change. the newclass impulses 171 to 174 not * supported. the effect stuff CE_TELEPORTERPUFFS, * CE_TELEPORTERBODY,CE_BONESHARD,CE_BONESHRAPNEL * not supported. */ typedef struct { int pad[28]; int self; int other; int world; float time; float frametime; float force_retouch; string_t mapname; string_t startspot; float deathmatch; /* no randomclass in v1.03 */ float coop; float teamplay; /* no cl_playerclass in v1.03-1.11 */ float serverflags; float total_secrets; float total_monsters; float found_secrets; float killed_monsters; float chunk_cnt; float done_precache; float parm1; float parm2; float parm4; float parm5; float parm6; float parm7; float parm8; float parm9; float parm10; float parm11; float parm12; float parm13; float parm14; float parm15; float parm16; string_t parm3; vec3_t v_forward; vec3_t v_up; vec3_t v_right; float trace_allsolid; float trace_startsolid; float trace_fraction; vec3_t trace_endpos; vec3_t trace_plane_normal; float trace_plane_dist; int trace_ent; float trace_inopen; float trace_inwater; int msg_entity; float cycle_wrapped; float crouch_cnt; /* modelindex_assassin ... _necromancer * only in Hexen II v1.03 to 1.11 progs */ float modelindex_assassin; float modelindex_crusader; float modelindex_paladin; float modelindex_necromancer; float modelindex_sheep; float num_players; float exp_mult; func_t main; func_t StartFrame; func_t PlayerPreThink; func_t PlayerPostThink; func_t ClientKill; func_t ClientConnect; func_t PutClientInServer; func_t ClientReEnter; func_t ClientDisconnect; func_t ClassChangeWeapon; } globalvars_v103_t; /* glabals structure for Hexen II v1.11 progs: */ typedef struct { int pad[28]; int self; int other; int world; float time; float frametime; float force_retouch; string_t mapname; string_t startspot; float deathmatch; float randomclass; /* since v1.09 */ float coop; float teamplay; /* no cl_playerclass in v1.03-1.11 */ float serverflags; float total_secrets; float total_monsters; float found_secrets; float killed_monsters; float chunk_cnt; float done_precache; float parm1; float parm2; float parm4; float parm5; float parm6; float parm7; float parm8; float parm9; float parm10; float parm11; float parm12; float parm13; float parm14; float parm15; float parm16; string_t parm3; vec3_t v_forward; vec3_t v_up; vec3_t v_right; float trace_allsolid; float trace_startsolid; float trace_fraction; vec3_t trace_endpos; vec3_t trace_plane_normal; float trace_plane_dist; int trace_ent; float trace_inopen; float trace_inwater; int msg_entity; float cycle_wrapped; float crouch_cnt; /* modelindex_assassin ... _necromancer * only in Hexen II v1.03 to 1.11 progs */ float modelindex_assassin; float modelindex_crusader; float modelindex_paladin; float modelindex_necromancer; float modelindex_sheep; float num_players; float exp_mult; func_t main; func_t StartFrame; func_t PlayerPreThink; func_t PlayerPostThink; func_t ClientKill; func_t ClientConnect; func_t PutClientInServer; func_t ClientReEnter; func_t ClientDisconnect; func_t ClassChangeWeapon; } globalvars_v111_t; /* glabals structure for Mission Pack v1.12 progs: */ typedef struct { int pad[28]; int self; int other; int world; float time; float frametime; float force_retouch; string_t mapname; string_t startspot; float deathmatch; float randomclass; /* since v1.09 */ float coop; float teamplay; float cl_playerclass; /* only in mission pack (v1.12) */ float serverflags; float total_secrets; float total_monsters; float found_secrets; float killed_monsters; float chunk_cnt; float done_precache; float parm1; float parm2; float parm4; float parm5; float parm6; float parm7; float parm8; float parm9; float parm10; float parm11; float parm12; float parm13; float parm14; float parm15; float parm16; string_t parm3; vec3_t v_forward; vec3_t v_up; vec3_t v_right; float trace_allsolid; float trace_startsolid; float trace_fraction; vec3_t trace_endpos; vec3_t trace_plane_normal; float trace_plane_dist; int trace_ent; float trace_inopen; float trace_inwater; int msg_entity; float cycle_wrapped; float crouch_cnt; /* modelindex_assassin ... _necromancer * not in Portal of Praevus v1.12 progs */ float modelindex_sheep; float num_players; float exp_mult; func_t main; func_t StartFrame; func_t PlayerPreThink; func_t PlayerPostThink; func_t ClientKill; func_t ClientConnect; func_t PutClientInServer; func_t ClientReEnter; func_t ClientDisconnect; func_t ClassChangeWeapon; } globalvars_v112_t; typedef struct { int *self; /* @ offset 28. */ int *other; int *world; float *time; float *frametime; float *force_retouch; string_t *mapname; string_t *startspot; float *deathmatch; float *randomclass; /* only since v1.09 */ float *coop; float *teamplay; float *cl_playerclass; /* only in v1.12 */ float *serverflags; float *total_secrets; float *total_monsters; float *found_secrets; float *killed_monsters; float *chunk_cnt; float *done_precache; /* parm1-16 form an array. Unlike Quake, Hexen II progs has them * in order parms1..2 parms4..16 as float and parm3 as string_t */ float *parm; vec3_t *v_forward; vec3_t *v_up; vec3_t *v_right; float *trace_allsolid; float *trace_startsolid; float *trace_fraction; vec3_t *trace_endpos; vec3_t *trace_plane_normal; float *trace_plane_dist; int *trace_ent; float *trace_inopen; float *trace_inwater; int *msg_entity; float *cycle_wrapped; float *crouch_cnt; /* modelindex_assassin ... _necromancer NOT in v1.12 */ float *modelindex_assassin; float *modelindex_crusader; float *modelindex_paladin; float *modelindex_necromancer; float *modelindex_sheep; float *num_players; float *exp_mult; /* functions */ func_t *main; func_t *StartFrame; func_t *PlayerPreThink; func_t *PlayerPostThink; func_t *ClientKill; func_t *ClientConnect; func_t *PutClientInServer; func_t *ClientReEnter; func_t *ClientDisconnect; func_t *ClassChangeWeapon; } sv_globals_t; typedef struct { float modelindex; vec3_t absmin; vec3_t absmax; float ltime; float movetype; float solid; vec3_t origin; vec3_t oldorigin; vec3_t velocity; vec3_t angles; vec3_t avelocity; vec3_t punchangle; string_t classname; string_t model; float frame; float skin; float effects; float scale; float drawflags; float abslight; vec3_t mins; vec3_t maxs; vec3_t size; float hull; func_t touch; func_t use; func_t think; func_t blocked; float nextthink; int groundentity; float stats_restored; float frags; float weapon; string_t weaponmodel; float weaponframe; float health; float max_health; float playerclass; float bluemana; float greenmana; float max_mana; float armor_amulet; float armor_bracer; float armor_breastplate; float armor_helmet; float level; float intelligence; float wisdom; float dexterity; float strength; float experience; float ring_flight; float ring_water; float ring_turning; float ring_regeneration; float haste_time; float tome_time; string_t puzzle_inv1; string_t puzzle_inv2; string_t puzzle_inv3; string_t puzzle_inv4; string_t puzzle_inv5; string_t puzzle_inv6; string_t puzzle_inv7; string_t puzzle_inv8; float experience_value; float items; float takedamage; int chain; float deadflag; vec3_t view_ofs; float button0; float button1; float button2; float impulse; float fixangle; vec3_t v_angle; float idealpitch; float idealroll; float hoverz; string_t netname; int enemy; float flags; float flags2; float artifact_flags; float colormap; float team; float light_level; float teleport_time; float armortype; float armorvalue; float waterlevel; float watertype; float friction; float ideal_yaw; float yaw_speed; int goalentity; float spawnflags; string_t target; string_t targetname; float dmg_take; float dmg_save; int dmg_inflictor; int owner; vec3_t movedir; float message; float soundtype; string_t noise; string_t noise1; string_t noise2; string_t noise3; float rings; float rings_active; float rings_low; float artifacts; float artifact_active; float artifact_low; float hasted; float inventory; float cnt_torch; float cnt_h_boost; float cnt_sh_boost; float cnt_mana_boost; float cnt_teleport; float cnt_tome; float cnt_summon; float cnt_invisibility; float cnt_glyph; float cnt_haste; float cnt_blast; float cnt_polymorph; float cnt_flight; float cnt_cubeofforce; float cnt_invincibility; int cameramode; int movechain; func_t chainmoved; float string_index; } entvars_t; /* crc for Hexen II v1.03 progs.dat headers */ #define PROGS_V103_CRC 14046 /* crc for Hexen II v1.11 progs.dat headers */ #define PROGS_V111_CRC 38488 #define PROGS_V109_CRC (PROGS_V111_CRC) /* crc for Mission Pack v1.12 progs.dat headers */ #define PROGS_V112_CRC 26905 /* the default valid crc: */ #define PROGHEADER_CRC (PROGS_V112_CRC) #endif /* __PROGDEFS_H */ engine/hexen2/protocol.h000066400000000000000000000242041444734033100155330ustar00rootroot00000000000000/* protocol.h -- communications protocols * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_PROTOCOL_H #define __HX2_PROTOCOL_H #define PROTOCOL_RAVEN_107 15 /* cd version, aka 1.03 (not supported) */ #define PROTOCOL_RAVEN_109 17 /* official 1.09 update (not supported) */ #define PROTOCOL_RAVEN_111 18 /* official 1.11 update */ #define PROTOCOL_RAVEN_112 19 /* 1.12, mission pack */ #define PROTOCOL_UQE_113 20 /* Korax UQE patch 1.13 */ /* the default protocol: */ #define PROTOCOL_VERSION (PROTOCOL_RAVEN_112) //========================================= //================== // note that constant.hc may mirror to some of these numbers // also related to svc_strings[] in cl_parse //================== // // server to client // #define svc_bad 0 #define svc_nop 1 #define svc_disconnect 2 #define svc_updatestat 3 // [byte] [long] #define svc_version 4 // [long] server version #define svc_setview 5 // [short] entity number #define svc_sound 6 // #define svc_time 7 // [float] server time #define svc_print 8 // [string] null terminated string #define svc_stufftext 9 // [string] stuffed into client's console buffer // the string should be \n terminated #define svc_setangle 10 // [angle3] set the view angle to this absolute value #define svc_serverinfo 11 // [long] version // [string] signon string // [string]..[0]model cache // [string]...[0]sounds cache #define svc_lightstyle 12 // [byte] [string] #define svc_updatename 13 // [byte] [string] #define svc_updatefrags 14 // [byte] [short] #define svc_clientdata 15 // #define svc_stopsound 16 // #define svc_updatecolors 17 // [byte] [byte] #define svc_particle 18 // [vec3] #define svc_damage 19 #define svc_spawnstatic 20 #define svc_raineffect 21 #define svc_spawnbaseline 22 #define svc_temp_entity 23 #define svc_setpause 24 // [byte] on / off #define svc_signonnum 25 // [byte] used for the signon sequence #define svc_centerprint 26 // [string] to put in center of the screen #define svc_killedmonster 27 #define svc_foundsecret 28 #define svc_spawnstaticsound 29 // [coord3] [byte] samp [byte] vol [byte] aten #define svc_intermission 30 // [string] music #define svc_finale 31 // [string] music [string] text #define svc_cdtrack 32 // [byte] track [byte] looptrack #define svc_sellscreen 33 #define svc_particle2 34 // [vec3] #define svc_cutscene 35 #define svc_midi_name 36 // [string] name #define svc_updateclass 37 // [byte] [byte] #define svc_particle3 38 #define svc_particle4 39 #define svc_set_view_flags 40 #define svc_clear_view_flags 41 #define svc_start_effect 42 #define svc_end_effect 43 #define svc_plaque 44 #define svc_particle_explosion 45 #define svc_set_view_tint 46 #define svc_reference 47 #define svc_clear_edicts 48 #define svc_update_inv 49 #define svc_setangle_interpolate 50 #define svc_update_kingofhill 51 #define svc_toggle_statbar 52 #define svc_sound_update_pos 53 // [short] ent+channel [coord3] pos #define svc_mod_name 54 // [string] name (UQE v1.13 by Korax, music file name) #define svc_skybox 55 // [string] name (UQE v1.13 by Korax, skybox name) //============================================== // // client to server // #define clc_bad 0 #define clc_nop 1 #define clc_disconnect 2 #define clc_move 3 // [usercmd_t] #define clc_stringcmd 4 // [string] message #define clc_inv_select 5 #define clc_frame 6 //============================================== // if the high bit of the servercmd is set, the low bits are fast update flags: #define U_MOREBITS (1<<0) #define U_ORIGIN1 (1<<1) #define U_ORIGIN2 (1<<2) #define U_ORIGIN3 (1<<3) #define U_ANGLE2 (1<<4) #define U_NOLERP (1<<5) // don't interpolate movement #define U_FRAME (1<<6) #define U_SIGNAL (1<<7) // just differentiates from other updates // svc_update can pass all of the fast update bits, plus more #define U_ANGLE1 (1<<8) #define U_ANGLE3 (1<<9) #define U_MODEL (1<<10) #define U_CLEAR_ENT (1<<11) #define U_ENT_OFF (1<<13) #define U_LONGENTITY (1<<14) #define U_MOREBITS2 (1<<15) #define U_SKIN (1<<16) #define U_EFFECTS (1<<17) #define U_SCALE (1<<18) #define U_COLORMAP (1<<19) #define BE_ON (1<<0) #define SU_VIEWHEIGHT (1<<0) #define SU_IDEALPITCH (1<<1) #define SU_PUNCH1 (1<<2) #define SU_PUNCH2 (1<<3) #define SU_PUNCH3 (1<<4) #define SU_VELOCITY1 (1<<5) #define SU_VELOCITY2 (1<<6) #define SU_VELOCITY3 (1<<7) //define SU_AIMENT (1<<8) AVAILABLE BIT #define SU_IDEALROLL (1<<8) // I'll take that available bit #define SU_SC1 (1<<9) #define SU_ONGROUND (1<<10) // no data follows, the bit is it #define SU_INWATER (1<<11) // no data follows, the bit is it #define SU_WEAPONFRAME (1<<12) #define SU_ARMOR (1<<13) #define SU_WEAPON (1<<14) #define SU_SC2 (1<<15) // Bits to help send server info about the client's edict variables #define SC1_HEALTH (1<<0) // changes stat bar #define SC1_LEVEL (1<<1) // changes stat bar #define SC1_INTELLIGENCE (1<<2) // changes stat bar #define SC1_WISDOM (1<<3) // changes stat bar #define SC1_STRENGTH (1<<4) // changes stat bar #define SC1_DEXTERITY (1<<5) // changes stat bar #define SC1_WEAPON (1<<6) // changes stat bar #define SC1_BLUEMANA (1<<7) // changes stat bar #define SC1_GREENMANA (1<<8) // changes stat bar #define SC1_EXPERIENCE (1<<9) // changes stat bar #define SC1_CNT_TORCH (1<<10) // changes stat bar #define SC1_CNT_H_BOOST (1<<11) // changes stat bar #define SC1_CNT_SH_BOOST (1<<12) // changes stat bar #define SC1_CNT_MANA_BOOST (1<<13) // changes stat bar #define SC1_CNT_TELEPORT (1<<14) // changes stat bar #define SC1_CNT_TOME (1<<15) // changes stat bar #define SC1_CNT_SUMMON (1<<16) // changes stat bar #define SC1_CNT_INVISIBILITY (1<<17) // changes stat bar #define SC1_CNT_GLYPH (1<<18) // changes stat bar #define SC1_CNT_HASTE (1<<19) // changes stat bar #define SC1_CNT_BLAST (1<<20) // changes stat bar #define SC1_CNT_POLYMORPH (1<<21) // changes stat bar #define SC1_CNT_FLIGHT (1<<22) // changes stat bar #define SC1_CNT_CUBEOFFORCE (1<<23) // changes stat bar #define SC1_CNT_INVINCIBILITY (1<<24) // changes stat bar #define SC1_ARTIFACT_ACTIVE (1<<25) #define SC1_ARTIFACT_LOW (1<<26) #define SC1_MOVETYPE (1<<27) #define SC1_CAMERAMODE (1<<28) #define SC1_HASTED (1<<29) #define SC1_INVENTORY (1<<30) #define SC1_RINGS_ACTIVE (1<<31) #define SC2_RINGS_LOW (1<<0) #define SC2_AMULET (1<<1) #define SC2_BRACER (1<<2) #define SC2_BREASTPLATE (1<<3) #define SC2_HELMET (1<<4) #define SC2_FLIGHT_T (1<<5) #define SC2_WATER_T (1<<6) #define SC2_TURNING_T (1<<7) #define SC2_REGEN_T (1<<8) #define SC2_HASTE_T (1<<9) #define SC2_TOME_T (1<<10) #define SC2_PUZZLE1 (1<<11) #define SC2_PUZZLE2 (1<<12) #define SC2_PUZZLE3 (1<<13) #define SC2_PUZZLE4 (1<<14) #define SC2_PUZZLE5 (1<<15) #define SC2_PUZZLE6 (1<<16) #define SC2_PUZZLE7 (1<<17) #define SC2_PUZZLE8 (1<<18) #define SC2_MAXHEALTH (1<<19) #define SC2_MAXMANA (1<<20) #define SC2_FLAGS (1<<21) #define SC2_OBJ (1<<22) #define SC2_OBJ2 (1<<23) // This is to mask out those items that need to generate a stat bar change #define SC1_STAT_BAR 0x01ffffff #define SC2_STAT_BAR 0x0000001e // This is to mask out those items in the inventory (for inventory changes) #define SC1_INV 0x01fffc00 #define SC2_INV 0x00000000 //============================================== // a sound with no channel is a local only sound #define SND_VOLUME (1<<0) /* a byte */ #define SND_ATTENUATION (1<<1) /* a byte */ #define SND_OVERFLOW (1<<2) /* add 256 to snd num */ #define SND_OVERFLOW2 (1<<3) /* add 512 to snd num: a UQE/1.13 thing */ // gonna use the rest of the bits to pack the ent+channel #define DEFAULT_SOUND_PACKET_VOLUME 255 #define DEFAULT_SOUND_PACKET_ATTENUATION 1.0 // defaults for clientinfo messages #define DEFAULT_VIEWHEIGHT 22 #define DEFAULT_ITEMS 16385 // game types sent by serverinfo // these determine which intermission screen plays #define GAME_COOP 0 #define GAME_DEATHMATCH 1 /* ========================================================== ELEMENTS COMMUNICATED ACROSS THE NET ========================================================== */ // // entity states and client frames // typedef struct { vec3_t origin; vec3_t angles; short modelindex; byte frame; byte colormap; byte skin; byte effects; byte scale; byte drawflags; byte abslight; byte ClearCount[32]; } entity_state_t; typedef struct { byte flags; short index; vec3_t origin; vec3_t angles; short modelindex; byte frame; byte colormap; byte skin; byte effects; byte scale; byte drawflags; byte abslight; } entity_state2_t; typedef struct { byte flags; vec3_t origin; vec3_t angles; short modelindex; byte frame; byte colormap; byte skin; byte effects; byte scale; byte drawflags; byte abslight; } entity_state3_t; #define MAX_CLIENT_STATES 150 #define MAX_FRAMES 5 #define CLEAR_LIMIT 2 #define ENT_STATE_ON 1 #define ENT_CLEARED 2 typedef struct { entity_state2_t states[MAX_CLIENT_STATES]; // unsigned long frame; // unsigned long flags; int count; } client_frames_t; typedef struct { entity_state2_t states[MAX_CLIENT_STATES*2]; int count; } client_frames2_t; typedef struct { client_frames_t frames[MAX_FRAMES+2]; // 0 = base, 1-max = proposed, max+1 = too late } client_state2_t; typedef struct usercmd_s { vec3_t viewangles; // intended velocities float forwardmove; float sidemove; float upmove; byte lightlevel; } usercmd_t; #endif /* __HX2_PROTOCOL_H */ engine/hexen2/quakedef.h000066400000000000000000000155451444734033100154670ustar00rootroot00000000000000/* quakedef.h -- common definitions for client and server. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __QUAKEDEFS_H #define __QUAKEDEFS_H #define __STRINGIFY(x) #x #define STRINGIFY(x) __STRINGIFY(x) #define HOT_VERSION_MAJ 1 #define HOT_VERSION_MID 5 #define HOT_VERSION_MIN 10 #define HOT_VERSION_REL_DATE "2022-10-25" #define HOT_VERSION_STR STRINGIFY(HOT_VERSION_MAJ) "." STRINGIFY(HOT_VERSION_MID) "." STRINGIFY(HOT_VERSION_MIN) #define GLQUAKE_VERSION 1.00 #define ENGINE_VERSION 1.29 #define ENGINE_NAME "Hexen2" #define MAX_QPATH 64 // max length of a quake game pathname #define QUAKE_GAME // as opposed to utilities // !!! if this is changed, it must be changed in d_ifacea.h too !!! #define CACHE_SIZE 32 // used to align key data structures #define Q_UNUSED(x) (x = x) // for pesky compiler / lint warnings #define MINIMUM_MEMORY 0x550000 #define MINIMUM_MEMORY_LEVELPAK (MINIMUM_MEMORY + 0x100000) #define MAX_NUM_ARGVS 50 // up / down #define PITCH 0 // left / right #define YAW 1 // fall over #define ROLL 2 // // Timing macros // #define HX_FRAME_TIME 0.05 #define HX_FPS 20 //#ifdef DEMOBUILD //#define MAX_CLIENTS 8 //#endif #define MAX_CLIENTS 16 #define ON_EPSILON 0.1 // point on plane side epsilon #define DIST_EPSILON (0.03125) // 1/32 epsilon to keep floating point happy (moved from world.h) //#define MAX_MSGLEN 8000 // max length of a reliable message //#define MAX_MSGLEN 16000 // max length of a reliable message #define MAX_MSGLEN 20000 // for mission pack tibet2 //#define MAX_DATAGRAM 2048 // max length of unreliable message TEMP: This only for E3 #define MAX_DATAGRAM 1024 // max length of unreliable message #define MAX_PRINTMSG 4096 // maximum allowed print message length // // per-level limits // #define MAX_EDICTS 600 // FIXME: ouch! ouch! ouch! #define MAX_LIGHTSTYLES 64 #define MAX_MODELS 512 /* Sent over the net as a word */ #define MAX_SOUNDS_OLD 256 /* Hexen2 v1.11 (protocol 18) and older: sent as a byte */ #define MAX_SOUNDS_H2MP 512 /* Mission Pack (protocol 19), messy thing: */ /* SV_StartSound sends it as a byte, but PF_ambientsound sends it as a word. */ #define MAX_SOUNDS (MAX_SOUNDS_H2MP) #define MAX_STYLESTRING 64 // // stats are integers communicated to the client by the server // #define MAX_CL_STATS 32 //#define STAT_HEALTH 0 #define STAT_FRAGS 1 #define STAT_WEAPON 2 //#define STAT_AMMO 3 #define STAT_ARMOR 4 #define STAT_WEAPONFRAME 5 //#define STAT_SHELLS 6 //#define STAT_NAILS 7 //#define STAT_ROCKETS 8 //#define STAT_CELLS 9 //#define STAT_ACTIVEWEAPON 10 #define STAT_TOTALSECRETS 11 #define STAT_TOTALMONSTERS 12 #define STAT_SECRETS 13 // bumped on client side by svc_foundsecret #define STAT_MONSTERS 14 // bumped by svc_killedmonster //#define STAT_BLUEMANA 15 //#define STAT_GREENMANA 16 //#define STAT_EXPERIENCE 17 #define MAX_INVENTORY 15 /* Max inventory array size */ /* the number of cnt_ members in the entvars_t struct: from cnt_torch to cnt_invincibility: 15 total (see in progdefs.h). */ #define SAVEGAME_VERSION 5 #define SAVEGAME_COMMENT_LENGTH 39 // 0-19: level name, 21-rest: save time #define MAX_SAVEGAMES 12 // Max number of savegames for the menu listing // // quake item flags // #define IT_SHOTGUN (1 << 0 ) #define IT_SUPER_SHOTGUN (1 << 1 ) #define IT_NAILGUN (1 << 2 ) #define IT_SUPER_NAILGUN (1 << 3 ) #define IT_GRENADE_LAUNCHER (1 << 4 ) #define IT_ROCKET_LAUNCHER (1 << 5 ) #define IT_LIGHTNING (1 << 6 ) #define IT_SUPER_LIGHTNING (1 << 7 ) #define IT_SHELLS (1 << 8 ) #define IT_NAILS (1 << 9 ) #define IT_ROCKETS (1 << 10) #define IT_CELLS (1 << 11) #define IT_AXE (1 << 12) #define IT_ARMOR1 (1 << 13) #define IT_ARMOR2 (1 << 14) #define IT_ARMOR3 (1 << 15) #define IT_SUPERHEALTH (1 << 16) #define IT_KEY1 (1 << 17) #define IT_KEY2 (1 << 18) #define IT_INVISIBILITY (1 << 19) #define IT_INVULNERABILITY (1 << 20) #define IT_SUIT (1 << 21) #define IT_QUAD (1 << 22) #define IT_SIGIL1 (1 << 28) #define IT_SIGIL2 (1 << 29) #define IT_SIGIL3 (1 << 30) #define IT_SIGIL4 (1 << 31) // // hexen2 artifact flags // #define ART_HASTE (1 << 0) #define ART_INVINCIBILITY (1 << 1) #define ART_TOMEOFPOWER (1 << 2) #define ART_INVISIBILITY (1 << 3) /* hexen2 and hexenworld versions of these flags are different !!! */ #define ARTFLAG_FROZEN (1 << 7) #define ARTFLAG_STONED (1 << 8) #define ARTFLAG_DIVINE_INTERVENTION (1 << 9) // // edict->drawflags // #define MLS_MASKIN 7 // MLS: Model Light Style #define MLS_MASKOUT 248 #define MLS_NONE 0 #define MLS_FULLBRIGHT 1 #define MLS_POWERMODE 2 #define MLS_TORCH 3 #define MLS_TOTALDARK 4 #define MLS_ABSLIGHT 7 #define SCALE_TYPE_MASKIN 24 #define SCALE_TYPE_MASKOUT 231 #define SCALE_TYPE_UNIFORM 0 // Scale X, Y, and Z #define SCALE_TYPE_XYONLY 8 // Scale X and Y #define SCALE_TYPE_ZONLY 16 // Scale Z #define SCALE_ORIGIN_MASKIN 96 #define SCALE_ORIGIN_MASKOUT 159 #define SCALE_ORIGIN_CENTER 0 // Scaling origin at object center #define SCALE_ORIGIN_BOTTOM 32 // Scaling origin at object bottom #define SCALE_ORIGIN_TOP 64 // Scaling origin at object top #define DRF_TRANSLUCENT 128 #define DRF_ANIMATEONCE 256 // // Player Classes // #define MAX_PLAYER_CLASS 5 /* total number of available player classes */ #define PORTALS_EXTRA_CLASSES 1 /* number of player classes only available in the mission pack */ #define ABILITIES_STR_INDEX 400 /* starting number of class ability lines in strings.txt - 1 */ #define CLASS_PALADIN 1 #define CLASS_CLERIC 2 #define CLASS_CRUSADER CLASS_CLERIC /* alias, the progs actually use this one */ #define CLASS_NECROMANCER 3 #define CLASS_THEIF 4 #define CLASS_THIEF CLASS_THEIF /* for those who type correctly ;) */ #define CLASS_ASSASSIN CLASS_THEIF /* another alias, progs actually use this one */ #define CLASS_DEMON 5 #define CLASS_SUCCUBUS CLASS_DEMON /* alias, the h2w progs actually use this one */ //#define BASE_ENT_ON 1 //#define BASE_ENT_SENT 2 /* include our common header file */ /* FIXME: kill this in the future and make each C file include only the necessary headers. */ #include "quakeinc.h" #endif /* __QUAKEDEFS_H */ engine/hexen2/quakeinc.h000066400000000000000000000044301444734033100154710ustar00rootroot00000000000000/* * quakeinc.h -- primary header for client and server * FIXME: kill this in the future and make each C * file include only the necessary headers. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __QUAKEINC_H #define __QUAKEINC_H /* include the system stdc headers: */ #include "q_stdinc.h" /* include the compiler specific stuff */ #include "compiler.h" /* include the OS/arch definitions, etc */ #include "arch_def.h" /* make sure to include our compile time options first */ #include "h2config.h" /* include the quake headers */ #include "q_endian.h" #include "sys.h" #include "qsnprint.h" #include "strl_fn.h" #include "link_ops.h" #include "sizebuf.h" #include "msg_io.h" #include "printsys.h" #include "common.h" #include "quakefs.h" #include "bspfile.h" #include "zone.h" #include "mathlib.h" #include "cvar.h" #include "protocol.h" #include "net.h" #include "cmd.h" #include "crc.h" #include "host.h" #include "host_string.h" #include "progs.h" #include "effects.h" #include "server.h" #if defined(SERVERONLY) #include "sv_model.h" #include "world.h" #else /* client */ #include "console.h" #include "wad.h" #include "vid.h" #include "screen.h" #include "draw.h" #include "render.h" #include "view.h" #include "sbar.h" #include "q_sound.h" #include "client.h" #if defined(GLQUAKE) #include "glheader.h" #include "gl_model.h" #include "glquake.h" #else /* sw client */ #include "model.h" #include "d_iface.h" #endif #include "world.h" #include "r_part.h" #include "input.h" #include "keys.h" #include "menu.h" #endif /* !SERVERONLY */ #endif /* __QUAKEINC_H */ engine/hexen2/r_alias.c000066400000000000000000000640631444734033100153060ustar00rootroot00000000000000/* r_alias.c -- routines for setting up to draw alias models * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" #include "d_local.h" // FIXME: shouldn't be needed (is needed for patch // right now, but that should move) #define LIGHT_MIN 5 // lowest light value we'll allow, to avoid the // need for inner-loop light clamping mtriangle_t *ptriangles; affinetridesc_t r_affinetridesc; void *acolormap; // FIXME: should go away ASM_LINKAGE_BEGIN trivertx_t *r_apverts; // TODO: these probably will go away with optimized rasterization vec3_t r_plightvec; int r_ambientlight; float r_shadelight; ASM_LINKAGE_END aliashdr_t *paliashdr; finalvert_t *pfinalverts; auxvert_t *pauxverts; newmdl_t *pmdl; //JFM: new model fmt static qmodel_t *pmodel; static float ziscale; static vec3_t alias_forward, alias_right, alias_up; static maliasskindesc_t *pskindesc; int r_amodels_drawn; ASM_LINKAGE_BEGIN int r_anumverts; float aliastransform[3][4]; float r_avertexnormals[NUMVERTEXNORMALS][3] = { #include "anorms.h" }; ASM_LINKAGE_END typedef struct { int index0; int index1; } aedge_t; static aedge_t aedges[12] = { {0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6}, {6, 7}, {7, 4}, {0, 5}, {1, 4}, {2, 7}, {3, 6} }; #if !id386 && !id68k static void R_AliasTransformAndProjectFinalVerts (finalvert_t *fv, stvert_t *pstverts); #endif static void R_AliasSetUpTransform (int trivial_accept); #if !id68k static void R_AliasTransformVector (vec3_t in, vec3_t out); static void R_AliasTransformFinalVert (finalvert_t *fv, auxvert_t *av, trivertx_t *pverts); #endif /* ================ R_AliasCheckBBox ================ */ qboolean R_AliasCheckBBox (void) { int i, flags, frame, numv; aliashdr_t *pahdr; float zi, basepts[8][3], v0, v1, frac; finalvert_t *pv0, *pv1, viewpts[16]; auxvert_t *pa0, *pa1, viewaux[16]; maliasframedesc_t *pframedesc; qboolean zclipped, zfullyclipped; unsigned int anyclip, allclip; int minz; // expand, rotate, and translate points into worldspace currententity->trivial_accept = 0; pmodel = currententity->model; pahdr = (aliashdr_t *) Mod_Extradata (pmodel); pmdl = (newmdl_t*)((byte *)pahdr + pahdr->model); R_AliasSetUpTransform (0); // construct the base bounding box for this frame frame = currententity->frame; // TODO: don't repeat this check when drawing? if ((frame >= pmdl->numframes) || (frame < 0)) { Con_DPrintf ("No such frame %d %s\n", frame, pmodel->name); frame = 0; } pframedesc = &pahdr->frames[frame]; // x worldspace coordinates basepts[0][0] = basepts[1][0] = basepts[2][0] = basepts[3][0] = (float)pframedesc->bboxmin.v[0]; basepts[4][0] = basepts[5][0] = basepts[6][0] = basepts[7][0] = (float)pframedesc->bboxmax.v[0]; // y worldspace coordinates basepts[0][1] = basepts[3][1] = basepts[5][1] = basepts[6][1] = (float)pframedesc->bboxmin.v[1]; basepts[1][1] = basepts[2][1] = basepts[4][1] = basepts[7][1] = (float)pframedesc->bboxmax.v[1]; // z worldspace coordinates basepts[0][2] = basepts[1][2] = basepts[4][2] = basepts[5][2] = (float)pframedesc->bboxmin.v[2]; basepts[2][2] = basepts[3][2] = basepts[6][2] = basepts[7][2] = (float)pframedesc->bboxmax.v[2]; zclipped = false; zfullyclipped = true; minz = 9999; for (i = 0 ; i < 8 ; i++) { R_AliasTransformVector (&basepts[i][0], &viewaux[i].fv[0]); if (viewaux[i].fv[2] < ALIAS_Z_CLIP_PLANE) { // we must clip points that are closer than the near clip plane viewpts[i].flags = ALIAS_Z_CLIP; zclipped = true; } else { if (viewaux[i].fv[2] < minz) minz = viewaux[i].fv[2]; viewpts[i].flags = 0; zfullyclipped = false; } } if (minz < r_aliasmip.integer || zclipped) pmodel->flags &= ~EF_MIP_MAP_FAR; else pmodel->flags |= EF_MIP_MAP_FAR; if (zfullyclipped) { return false; // everything was near-z-clipped } numv = 8; if (zclipped) { // organize points by edges, use edges to get new points // (possible trivial reject) for (i = 0 ; i < 12 ; i++) { // edge endpoints pv0 = &viewpts[aedges[i].index0]; pv1 = &viewpts[aedges[i].index1]; pa0 = &viewaux[aedges[i].index0]; pa1 = &viewaux[aedges[i].index1]; // if one end is clipped and the other isn't, make a new point if (pv0->flags ^ pv1->flags) { frac = (ALIAS_Z_CLIP_PLANE - pa0->fv[2]) / (pa1->fv[2] - pa0->fv[2]); viewaux[numv].fv[0] = pa0->fv[0] + (pa1->fv[0] - pa0->fv[0]) * frac; viewaux[numv].fv[1] = pa0->fv[1] + (pa1->fv[1] - pa0->fv[1]) * frac; viewaux[numv].fv[2] = ALIAS_Z_CLIP_PLANE; viewpts[numv].flags = 0; numv++; } } } // project the vertices that remain after clipping anyclip = 0; allclip = ALIAS_XY_CLIP_MASK; // TODO: probably should do this loop in ASM, especially if we use floats for (i = 0 ; i < numv ; i++) { // we don't need to bother with vertices that were z-clipped if (viewpts[i].flags & ALIAS_Z_CLIP) continue; zi = 1.0 / viewaux[i].fv[2]; // FIXME: do with chop mode in ASM, or convert to float v0 = (viewaux[i].fv[0] * xscale * zi) + xcenter; v1 = (viewaux[i].fv[1] * yscale * zi) + ycenter; flags = 0; if (v0 < r_refdef.fvrectx) flags |= ALIAS_LEFT_CLIP; if (v1 < r_refdef.fvrecty) flags |= ALIAS_TOP_CLIP; if (v0 > r_refdef.fvrectright) flags |= ALIAS_RIGHT_CLIP; if (v1 > r_refdef.fvrectbottom) flags |= ALIAS_BOTTOM_CLIP; anyclip |= flags; allclip &= flags; } if (allclip) return false; // trivial reject off one side currententity->trivial_accept = !anyclip & !zclipped; if (currententity->trivial_accept) { if (minz > (r_aliastransition + (pmdl->size * r_resfudge))) { currententity->trivial_accept |= 2; } } return true; } #if !id68k /* ================ R_AliasTransformVector ================ */ static void R_AliasTransformVector (vec3_t in, vec3_t out) { out[0] = DotProduct(in, aliastransform[0]) + aliastransform[0][3]; out[1] = DotProduct(in, aliastransform[1]) + aliastransform[1][3]; out[2] = DotProduct(in, aliastransform[2]) + aliastransform[2][3]; } #endif /* ================ R_AliasPreparePoints General clipped case ================ */ static void R_AliasPreparePoints (void) { int i; stvert_t *pstverts; finalvert_t *fv; auxvert_t *av; mtriangle_t *ptri; finalvert_t *pfv[3]; pstverts = (stvert_t *)((byte *)paliashdr + paliashdr->stverts); r_anumverts = pmdl->numverts; fv = pfinalverts; av = pauxverts; for (i = 0 ; i < r_anumverts ; i++, fv++, av++, r_apverts++) { // fv->v[2] = pstverts->s; // fv->v[3] = pstverts->t; fv->flags = 0; R_AliasTransformFinalVert (fv, av, r_apverts); if (av->fv[2] < ALIAS_Z_CLIP_PLANE) fv->flags |= ALIAS_Z_CLIP; else { R_AliasProjectFinalVert (fv, av); if (fv->v[0] < r_refdef.aliasvrect.x) fv->flags |= ALIAS_LEFT_CLIP; if (fv->v[1] < r_refdef.aliasvrect.y) fv->flags |= ALIAS_TOP_CLIP; if (fv->v[0] > r_refdef.aliasvrectright) fv->flags |= ALIAS_RIGHT_CLIP; if (fv->v[1] > r_refdef.aliasvrectbottom) fv->flags |= ALIAS_BOTTOM_CLIP; } } // // clip and draw all triangles // r_affinetridesc.numtriangles = 1; ptri = (mtriangle_t *)((byte *)paliashdr + paliashdr->triangles); for (i = 0 ; i < pmdl->numtris ; i++, ptri++) { pfv[0] = &pfinalverts[ptri->vertindex[0]]; pfv[1] = &pfinalverts[ptri->vertindex[1]]; pfv[2] = &pfinalverts[ptri->vertindex[2]]; if ( pfv[0]->flags & pfv[1]->flags & pfv[2]->flags & (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP) ) continue; // completely clipped //jfm: fill in the triangles s and t into the finalvert pfv[0]->v[2] = pstverts[ptri->stindex[0]].s; pfv[0]->v[3] = pstverts[ptri->stindex[0]].t; pfv[0]->flags |= pstverts[ptri->stindex[0]].onseam; pfv[1]->v[2] = pstverts[ptri->stindex[1]].s; pfv[1]->v[3] = pstverts[ptri->stindex[1]].t; pfv[1]->flags |= pstverts[ptri->stindex[1]].onseam; pfv[2]->v[2] = pstverts[ptri->stindex[2]].s; pfv[2]->v[3] = pstverts[ptri->stindex[2]].t; pfv[2]->flags |= pstverts[ptri->stindex[2]].onseam; if ( ! ( (pfv[0]->flags | pfv[1]->flags | pfv[2]->flags) & (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP) ) ) { // totally unclipped r_affinetridesc.pfinalverts = pfinalverts; r_affinetridesc.ptriangles = ptri; if (currententity->model->flags & EF_SPECIAL_TRANS) D_PolysetDrawT5 (); else if (currententity->drawflags & DRF_TRANSLUCENT) D_PolysetDrawT (); else if (currententity->model->flags & EF_TRANSPARENT) D_PolysetDrawT2 (); else if (currententity->model->flags & EF_HOLEY) D_PolysetDrawT3 (); else D_PolysetDraw (); } else { // partially clipped R_AliasClipTriangle (ptri); } } } /* ================ R_AliasSetUpTransform ================ */ static void R_AliasSetUpTransform (int trivial_accept) { int i; float rotationmatrix[3][4], t2matrix[3][4]; static float tmatrix[3][4]; static float viewmatrix[3][4]; vec3_t angles; float entScale; float xyfact = 1.0, zfact = 1.0; // avoid compiler warning float forward; float yaw, pitch; // TODO: should really be stored with the entity instead of being reconstructed // TODO: should use a look-up table // TODO: could cache lazily, stored in the entity if (currententity->model->flags & EF_FACE_VIEW) { VectorSubtract(currententity->origin,r_origin,angles); VectorSubtract(r_origin,currententity->origin,angles); VectorNormalizeFast(angles); if (angles[1] == 0 && angles[0] == 0) { yaw = 0; if (angles[2] > 0) pitch = 90; else pitch = 270; } else { yaw = (int) (atan2(angles[1], angles[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; forward = Q_sqrt (angles[0]*angles[0] + angles[1]*angles[1]); pitch = (int) (atan2(angles[2], forward) * 180 / M_PI); if (pitch < 0) pitch += 360; } angles[0] = -pitch; angles[1] = yaw; // angles[2] = 0; angles[2] = currententity->angles[ROLL]; } else { angles[ROLL] = currententity->angles[ROLL]; angles[PITCH] = -currententity->angles[PITCH]; if (currententity->model->flags & EF_ROTATE) { angles[YAW] = anglemod( (currententity->origin[0] + currententity->origin[1])*0.8 + (108*cl.time) ); } else { angles[YAW] = currententity->angles[YAW]; } } AngleVectors (angles, alias_forward, alias_right, alias_up); if (currententity->scale != 0 && currententity->scale != 100) { entScale = (float)currententity->scale/100.0; switch (currententity->drawflags & SCALE_TYPE_MASKIN) { case SCALE_TYPE_UNIFORM: tmatrix[0][0] = pmdl->scale[0]*entScale; tmatrix[1][1] = pmdl->scale[1]*entScale; tmatrix[2][2] = pmdl->scale[2]*entScale; xyfact = zfact = (entScale-1.0)*127.95; break; case SCALE_TYPE_XYONLY: tmatrix[0][0] = pmdl->scale[0]*entScale; tmatrix[1][1] = pmdl->scale[1]*entScale; tmatrix[2][2] = pmdl->scale[2]; xyfact = (entScale-1.0)*127.95; zfact = 1.0; break; case SCALE_TYPE_ZONLY: tmatrix[0][0] = pmdl->scale[0]; tmatrix[1][1] = pmdl->scale[1]; tmatrix[2][2] = pmdl->scale[2]*entScale; xyfact = 1.0; zfact = (entScale-1.0)*127.95; break; } switch (currententity->drawflags & SCALE_ORIGIN_MASKIN) { case SCALE_ORIGIN_CENTER: tmatrix[0][3] = pmdl->scale_origin[0]-pmdl->scale[0]*xyfact; tmatrix[1][3] = pmdl->scale_origin[1]-pmdl->scale[1]*xyfact; tmatrix[2][3] = pmdl->scale_origin[2]-pmdl->scale[2]*zfact; break; case SCALE_ORIGIN_BOTTOM: tmatrix[0][3] = pmdl->scale_origin[0]-pmdl->scale[0]*xyfact; tmatrix[1][3] = pmdl->scale_origin[1]-pmdl->scale[1]*xyfact; tmatrix[2][3] = pmdl->scale_origin[2]; break; case SCALE_ORIGIN_TOP: tmatrix[0][3] = pmdl->scale_origin[0]-pmdl->scale[0]*xyfact; tmatrix[1][3] = pmdl->scale_origin[1]-pmdl->scale[1]*xyfact; tmatrix[2][3] = pmdl->scale_origin[2]-pmdl->scale[2]*zfact*2.0; break; } } else { tmatrix[0][0] = pmdl->scale[0]; tmatrix[1][1] = pmdl->scale[1]; tmatrix[2][2] = pmdl->scale[2]; tmatrix[0][3] = pmdl->scale_origin[0]; tmatrix[1][3] = pmdl->scale_origin[1]; tmatrix[2][3] = pmdl->scale_origin[2]; } if (currententity->model->flags & EF_ROTATE) { // Floating motion tmatrix[2][3] += q_sinrad(currententity->origin[0] + currententity->origin[1] + (cl.time*3)) * 5.5; } // TODO: can do this with simple matrix rearrangement for (i = 0 ; i < 3 ; i++) { t2matrix[i][0] = alias_forward[i]; t2matrix[i][1] = -alias_right[i]; t2matrix[i][2] = alias_up[i]; } t2matrix[0][3] = -modelorg[0]; t2matrix[1][3] = -modelorg[1]; t2matrix[2][3] = -modelorg[2]; // FIXME: can do more efficiently than full concatenation R_ConcatTransforms (t2matrix, tmatrix, rotationmatrix); // TODO: should be global, set when vright, etc., set VectorCopy (vright, viewmatrix[0]); VectorCopy (vup, viewmatrix[1]); VectorInverse (viewmatrix[1]); VectorCopy (vpn, viewmatrix[2]); // viewmatrix[0][3] = 0; // viewmatrix[1][3] = 0; // viewmatrix[2][3] = 0; R_ConcatTransforms (viewmatrix, rotationmatrix, aliastransform); // do the scaling up of x and y to screen coordinates as part of the transform // for the unclipped case (it would mess up clipping in the clipped case). // Also scale down z, so 1/z is scaled 31 bits for free, and scale down x and y // correspondingly so the projected x and y come out right // FIXME: make this work for clipped case too? if (trivial_accept) { for (i = 0 ; i < 4 ; i++) { aliastransform[0][i] *= aliasxscale * (1.0 / ((float)0x8000 * 0x10000)); aliastransform[1][i] *= aliasyscale * (1.0 / ((float)0x8000 * 0x10000)); aliastransform[2][i] *= 1.0 / ((float)0x8000 * 0x10000); } } } #if !id68k /* ================ R_AliasTransformFinalVert ================ */ static void R_AliasTransformFinalVert (finalvert_t *fv, auxvert_t *av, trivertx_t *pverts) { int temp; float lightcos, *plightnormal; av->fv[0] = DotProduct(pverts->v, aliastransform[0]) + aliastransform[0][3]; av->fv[1] = DotProduct(pverts->v, aliastransform[1]) + aliastransform[1][3]; av->fv[2] = DotProduct(pverts->v, aliastransform[2]) + aliastransform[2][3]; // lighting plightnormal = r_avertexnormals[pverts->lightnormalindex]; lightcos = DotProduct (plightnormal, r_plightvec); temp = r_ambientlight; if (lightcos < 0) { temp += (int)(r_shadelight * lightcos); // clamp; because we limited the minimum ambient and shading light, we // don't have to clamp low light, just bright if (temp < 0) temp = 0; } fv->v[4] = temp; } #endif #if !id386 && !id68k /* ================ R_AliasTransformAndProjectFinalVerts ================ */ static void R_AliasTransformAndProjectFinalVerts (finalvert_t *fv, stvert_t *pstverts) { int i, temp; float lightcos, *plightnormal, zi; trivertx_t *pverts; pverts = r_apverts; for (i = 0 ; i < r_anumverts ; i++, fv++, pverts++, pstverts++) { // transform and project zi = 1.0 / (DotProduct(pverts->v, aliastransform[2]) + aliastransform[2][3]); // x, y, and z are scaled down by 1/2**31 in the transform, so 1/z is // scaled up by 1/2**31, and the scaling cancels out for x and y in the // projection fv->v[5] = zi; fv->v[0] = ((DotProduct(pverts->v, aliastransform[0]) + aliastransform[0][3]) * zi) + aliasxcenter; fv->v[1] = ((DotProduct(pverts->v, aliastransform[1]) + aliastransform[1][3]) * zi) + aliasycenter; fv->v[2] = pstverts->s; fv->v[3] = pstverts->t; fv->flags = pstverts->onseam; // lighting plightnormal = r_avertexnormals[pverts->lightnormalindex]; lightcos = DotProduct (plightnormal, r_plightvec); temp = r_ambientlight; if (lightcos < 0) { temp += (int)(r_shadelight * lightcos); // clamp; because we limited the minimum ambient and shading // light, we don't have to clamp low light, just bright if (temp < 0) temp = 0; } fv->v[4] = temp; } } #endif /* ================ R_AliasProjectFinalVert ================ */ void R_AliasProjectFinalVert (finalvert_t *fv, auxvert_t *av) { float zi; // project points zi = 1.0 / av->fv[2]; fv->v[5] = zi * ziscale; fv->v[0] = (av->fv[0] * aliasxscale * zi) + aliasxcenter; fv->v[1] = (av->fv[1] * aliasyscale * zi) + aliasycenter; } /* ================ R_AliasPrepareUnclippedPoints ================ */ void R_AliasPrepareUnclippedPoints (void) { stvert_t *pstverts; finalvert_t *fv; mtriangle_t *ptri; int i; pstverts = (stvert_t *)((byte *)paliashdr + paliashdr->stverts); r_anumverts = pmdl->numverts; // FIXME: just use pfinalverts directly? fv = pfinalverts; R_AliasTransformAndProjectFinalVerts (fv, pstverts); r_affinetridesc.pfinalverts = pfinalverts; r_affinetridesc.ptriangles = (mtriangle_t *) ((byte *)paliashdr + paliashdr->triangles); r_affinetridesc.numtriangles = 1;//pmdl->numtris; ptri = (mtriangle_t *)((byte *)paliashdr + paliashdr->triangles); for (i = 0 ; i < pmdl->numtris ; i++, ptri++) { finalvert_t *pfv[3]; pfv[0] = &pfinalverts[ptri->vertindex[0]]; pfv[1] = &pfinalverts[ptri->vertindex[1]]; pfv[2] = &pfinalverts[ptri->vertindex[2]]; //jfm: fill in the triangles s and t into the finalvert pfv[0]->v[2] = pstverts[ptri->stindex[0]].s; pfv[0]->v[3] = pstverts[ptri->stindex[0]].t; pfv[0]->flags = pstverts[ptri->stindex[0]].onseam; pfv[1]->v[2] = pstverts[ptri->stindex[1]].s; pfv[1]->v[3] = pstverts[ptri->stindex[1]].t; pfv[1]->flags = pstverts[ptri->stindex[1]].onseam; pfv[2]->v[2] = pstverts[ptri->stindex[2]].s; pfv[2]->v[3] = pstverts[ptri->stindex[2]].t; pfv[2]->flags = pstverts[ptri->stindex[2]].onseam; r_affinetridesc.ptriangles = ptri; if (r_affinetridesc.drawtype) { if (currententity->model->flags & EF_SPECIAL_TRANS) { D_PolysetDrawFinalVertsT5 (pfv[0],pfv[1],pfv[2]); D_PolysetDrawT5 (); } else if (currententity->drawflags & DRF_TRANSLUCENT) { D_PolysetDrawFinalVertsT (pfv[0],pfv[1],pfv[2]); D_PolysetDrawT (); } else if (currententity->model->flags & EF_TRANSPARENT) { D_PolysetDrawFinalVertsT2 (pfv[0],pfv[1],pfv[2]); D_PolysetDrawT2 (); } else if (currententity->model->flags & EF_HOLEY) { D_PolysetDrawFinalVertsT3 (pfv[0],pfv[1],pfv[2]); D_PolysetDrawT3 (); } else { D_PolysetDrawFinalVerts (pfv[0],pfv[1],pfv[2]); D_PolysetDraw (); } } else { if (currententity->model->flags & EF_SPECIAL_TRANS) D_PolysetDrawT5 (); else if (currententity->drawflags & DRF_TRANSLUCENT) D_PolysetDrawT (); else if (currententity->model->flags & EF_TRANSPARENT) D_PolysetDrawT2 (); else if (currententity->model->flags & EF_HOLEY) D_PolysetDrawT3 (); else D_PolysetDraw (); } } } /* =============== R_AliasSetupSkin =============== */ static void R_AliasSetupSkin (void) { int i, numskins, skinnum; int a_skinwidth; maliasskingroup_t *paliasskingroup; float *pskinintervals, fullskininterval; float skintargettime, skintime; qpic_t *stonepic; char temp[40]; skinnum = currententity->skinnum; if (skinnum >= 100) skinnum = 0; if ((skinnum >= pmdl->numskins) || (skinnum < 0)) { Con_DPrintf ("%s: no such skin # %d\n", __thisfunc__, skinnum); skinnum = 0; } if ( (currententity->model->flags & EF_MIP_MAP) && (currententity->model->flags & EF_MIP_MAP_FAR) && skinnum+1 < pmdl->numskins ) skinnum++; pskindesc = ((maliasskindesc_t *)((byte *)paliashdr + paliashdr->skindesc)) + skinnum; a_skinwidth = pmdl->skinwidth; if (pskindesc->type == ALIAS_SKIN_GROUP) { paliasskingroup = (maliasskingroup_t *)((byte *)paliashdr + pskindesc->skin); pskinintervals = (float *)((byte *)paliashdr + paliasskingroup->intervals); numskins = paliasskingroup->numskins; fullskininterval = pskinintervals[numskins-1]; skintime = cl.time + currententity->syncbase; // when loading in Mod_LoadAliasSkinGroup, we guaranteed all interval // values are positive, so we don't have to worry about division by 0 skintargettime = skintime - ((int)(skintime / fullskininterval)) * fullskininterval; for (i = 0 ; i < (numskins-1) ; i++) { if (pskinintervals[i] > skintargettime) break; } pskindesc = &paliasskingroup->skindescs[i]; } if (currententity->skinnum >= 100) { sprintf(temp,"gfx/skin%d.lmp",currententity->skinnum); stonepic = Draw_CachePic(temp); r_affinetridesc.pskindesc = pskindesc; r_affinetridesc.pskin = stonepic->data; r_affinetridesc.skinwidth = stonepic->width; r_affinetridesc.seamfixupX16 = (stonepic->width >> 1) << 16; r_affinetridesc.skinheight = stonepic->height; } else { r_affinetridesc.pskindesc = pskindesc; r_affinetridesc.pskin = (void *)((byte *)paliashdr + pskindesc->skin); r_affinetridesc.skinwidth = a_skinwidth; r_affinetridesc.seamfixupX16 = (a_skinwidth >> 1) << 16; r_affinetridesc.skinheight = pmdl->skinheight; } } /* ================ R_AliasSetupLighting ================ */ static void R_AliasSetupLighting (alight_t *plighting) { // guarantee that no vertex will ever be lit below LIGHT_MIN, so we don't have // to clamp off the bottom r_ambientlight = plighting->ambientlight; if (r_ambientlight < LIGHT_MIN) r_ambientlight = LIGHT_MIN; r_ambientlight = (255 - r_ambientlight) << VID_CBITS; if (r_ambientlight < LIGHT_MIN) r_ambientlight = LIGHT_MIN; r_shadelight = plighting->shadelight; if (r_shadelight < 0) r_shadelight = 0; r_shadelight *= VID_GRADES; // rotate the lighting vector into the model's frame of reference r_plightvec[0] = DotProduct (plighting->plightvec, alias_forward); r_plightvec[1] = -DotProduct (plighting->plightvec, alias_right); r_plightvec[2] = DotProduct (plighting->plightvec, alias_up); } /* ================= R_AliasSetupFrame set r_apverts ================= */ static void R_AliasSetupFrame (void) { int i, frame, numframes; maliasgroup_t *paliasgroup; float *pintervals, fullinterval, targettime, time; frame = currententity->frame; if ((frame >= pmdl->numframes) || (frame < 0)) { Con_DPrintf ("%s: no such frame %d\n", __thisfunc__, frame); frame = 0; } if (paliashdr->frames[frame].type == ALIAS_SINGLE) { r_apverts = (trivertx_t *) ((byte *)paliashdr + paliashdr->frames[frame].frame); return; } paliasgroup = (maliasgroup_t *) ((byte *)paliashdr + paliashdr->frames[frame].frame); pintervals = (float *)((byte *)paliashdr + paliasgroup->intervals); numframes = paliasgroup->numframes; fullinterval = pintervals[numframes-1]; time = cl.time + currententity->syncbase; // // when loading in Mod_LoadAliasGroup, we guaranteed all interval values // are positive, so we don't have to worry about division by 0 // targettime = time - ((int)(time / fullinterval)) * fullinterval; for (i = 0 ; i < (numframes-1) ; i++) { if (pintervals[i] > targettime) break; } r_apverts = (trivertx_t *) ((byte *)paliashdr + paliasgroup->frames[i].frame); } /* ================ R_AliasDrawModel ================ */ void R_AliasDrawModel (alight_t *plighting) { int mls; int i, j; byte *dest, *source, *sourceA; auxvert_t auxverts[MAXALIASVERTS]; finalvert_t finalverts[MAXALIASVERTS + ((CACHE_SIZE - 1) / sizeof(finalvert_t)) + 1]; r_amodels_drawn++; // cache align pfinalverts = (finalvert_t *) (((intptr_t)&finalverts[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); pauxverts = &auxverts[0]; paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model); pmdl = (newmdl_t *)((byte *)paliashdr + paliashdr->model); R_AliasSetupSkin (); R_AliasSetUpTransform (currententity->trivial_accept); mls = currententity->drawflags & MLS_MASKIN; if (currententity->model->flags & EF_ROTATE) { plighting->ambientlight = plighting->shadelight = 60 + 34 + q_sinrad(currententity->origin[0] + currententity->origin[1] + (cl.time*3.8))*34; R_AliasSetupLighting (plighting); r_plightvec[0] = r_plightvec[1] = r_plightvec[2] = 0; } else if (mls == MLS_ABSLIGHT) { plighting->ambientlight = plighting->shadelight = currententity->abslight; R_AliasSetupLighting (plighting); r_plightvec[0] = r_plightvec[1] = r_plightvec[2] = 0; } else if (mls != MLS_NONE) { // Use a model light style (25-30) plighting->ambientlight = plighting->shadelight = d_lightstylevalue[24+mls]/2; R_AliasSetupLighting (plighting); } else R_AliasSetupLighting (plighting); R_AliasSetupFrame (); if (!currententity->colormap) Sys_Error ("%s: !currententity->colormap", __thisfunc__); r_affinetridesc.drawtype = (currententity->trivial_accept == 3) && r_recursiveaffinetriangles; if (r_affinetridesc.drawtype) { D_PolysetUpdateTables (); // FIXME: precalc... } else { #if id386 if (currententity->model->flags & EF_SPECIAL_TRANS) D_Aff8PatchT5 (currententity->colormap); else if (currententity->drawflags & DRF_TRANSLUCENT) D_Aff8PatchT (currententity->colormap); else if (currententity->model->flags & EF_TRANSPARENT) D_Aff8PatchT2 (currententity->colormap); else if (currententity->model->flags & EF_HOLEY) D_Aff8PatchT3 (currententity->colormap); else D_Aff8Patch (currententity->colormap); #endif } acolormap = currententity->colormap; if ( currententity->colorshade && (currententity->colorshade != lastglobalcolor || currententity->sourcecolormap != lastsourcecolormap) ) { lastglobalcolor = currententity->colorshade; lastsourcecolormap = currententity->sourcecolormap; dest = globalcolormap; source = currententity->sourcecolormap; sourceA = transTable + (((int)lastglobalcolor)*256); for (i = 0 ; i < VID_GRADES ; i++) { for (j = 0 ; j < 256 ; j++, dest++, source++) { *dest = sourceA[*source]; } } } if (currententity != &cl.viewent) ziscale = (float)0x8000 * (float)0x10000; else ziscale = (float)0x8000 * (float)0x10000 * 3.0; if (currententity->trivial_accept) R_AliasPrepareUnclippedPoints (); else R_AliasPreparePoints (); } engine/hexen2/r_main.c000066400000000000000000001012211444734033100151250ustar00rootroot00000000000000/* r_main.c * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" #include "d_local.h" //#define PASSAGES static vec3_t viewlightvec; static alight_t r_viewlighting = {128, 192, viewlightvec}; ASM_LINKAGE_BEGIN #if id386 void *colormap; #endif ASM_LINKAGE_END float r_time1; float r_lasttime1 = 0; int r_numallocatededges; #if 0 qboolean r_drawpolys; qboolean r_drawculledpolys; qboolean r_worldpolysbacktofront; #endif qboolean r_recursiveaffinetriangles = true; int r_pixbytes = 1; float r_aliasuvscale = 1.0; int r_outofsurfaces; int r_outofedges; byte *mainTransTable; byte *transTable; /* the particle table */ byte *playerTranslation; const int color_offsets[MAX_PLAYER_CLASS] = { 2 * 14 * 256, 0, 1 * 14 * 256, 2 * 14 * 256, 2 * 14 * 256 #if defined(H2W) , 2 * 14 * 256 #endif }; qboolean r_dowarp, r_dowarpold, r_viewchanged; int numbtofpolys; btofpoly_t *pbtofpolys; mvertex_t *r_pcurrentvertbase; int c_surf; int r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs; qboolean r_surfsonstack; int r_clipflags; byte *r_warpbuffer; static byte *r_stack_start; qboolean r_fov_greater_than_90; entity_t r_worldentity; // // view origin // vec3_t vup, base_vup; vec3_t vpn, base_vpn; vec3_t vright, base_vright; vec3_t r_origin; // // screen size info // refdef_t r_refdef; float xcenter, ycenter; float xscale, yscale; float xscaleinv, yscaleinv; float xscaleshrink, yscaleshrink; float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; int screenwidth; float pixelAspect; float screenAspect; float verticalFieldOfView; float xOrigin, yOrigin; mplane_t screenedge[4]; // // refresh flags // int r_framecount = 1; // so frame counts initialized to 0 don't match int r_visframecount; int d_spanpixcount; int r_polycount; int r_drawnpolycount; int r_wholepolycount; int *pfrustum_indexes[4]; int r_frustum_indexes[4*6]; mleaf_t *r_viewleaf, *r_oldviewleaf; texture_t *r_notexture_mip; float r_aliastransition, r_resfudge; int d_lightstylevalue[256]; // 8.8 fraction of base light value float dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2; float se_time1, se_time2, de_time1, de_time2, dv_time1, dv_time2; static edge_t *SaveEdges; static surf_t *SaveSurfaces; static int SaveEdgesCount, SaveSurfacesCount, SaveEdgesSize, SaveSurfacesSize; static qboolean AllowTranslucency; cvar_t r_draworder = {"r_draworder", "0", CVAR_NONE}; cvar_t r_speeds = {"r_speeds", "0", CVAR_NONE}; cvar_t r_timegraph = {"r_timegraph", "0", CVAR_NONE}; cvar_t r_graphheight = {"r_graphheight", "10", CVAR_NONE}; cvar_t r_clearcolor = {"r_clearcolor", "0", CVAR_NONE}; cvar_t r_waterwarp = {"r_waterwarp", "1", CVAR_ARCHIVE}; cvar_t r_fullbright = {"r_fullbright", "0", CVAR_NONE}; cvar_t r_drawentities = {"r_drawentities", "1", CVAR_NONE}; cvar_t r_drawviewmodel = {"r_drawviewmodel", "1", CVAR_NONE}; cvar_t r_aliasstats = {"r_polymodelstats", "0", CVAR_NONE}; cvar_t r_dspeeds = {"r_dspeeds", "0", CVAR_NONE}; cvar_t r_drawflat = {"r_drawflat", "0", CVAR_NONE}; cvar_t r_ambient = {"r_ambient", "0", CVAR_NONE}; cvar_t r_reportsurfout = {"r_reportsurfout", "0", CVAR_NONE}; cvar_t r_maxsurfs = {"r_maxsurfs", "0", CVAR_NONE}; cvar_t r_numsurfs = {"r_numsurfs", "0", CVAR_NONE}; cvar_t r_reportedgeout = {"r_reportedgeout", "0", CVAR_NONE}; cvar_t r_maxedges = {"r_maxedges", "0", CVAR_NONE}; cvar_t r_numedges = {"r_numedges", "0", CVAR_NONE}; cvar_t r_aliastransbase = {"r_aliastransbase", "200", CVAR_NONE}; cvar_t r_aliastransadj = {"r_aliastransadj", "100", CVAR_NONE}; cvar_t r_aliasmip = {"r_aliasmip", "80", CVAR_NONE}; cvar_t r_wholeframe = {"r_wholeframe", "1", CVAR_ARCHIVE}; cvar_t r_transwater = {"r_transwater", "1", CVAR_ARCHIVE}; cvar_t r_texture_external = {"r_texture_external", "0", CVAR_ARCHIVE}; cvar_t r_dynamic = {"r_dynamic", "1", CVAR_NONE}; //void CreatePassages (void); //void SetVisibilityByPassages (void); //============================================================================= /* ================== R_InitTextures ================== */ void R_InitTextures (void) { int x, y, m; byte *dest; // create a simple checkerboard texture for the default r_notexture_mip = (texture_t *) Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, "notexture"); r_notexture_mip->width = r_notexture_mip->height = 16; r_notexture_mip->offsets[0] = sizeof(texture_t); r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16; r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8; r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4; for (m = 0; m < 4; m++) { dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[m]; for (y = 0; y < (16>>m); y++) { for (x = 0; x < (16>>m); x++) { if ( (y < (8>>m)) ^ (x < (8>>m)) ) *dest++ = 0; else *dest++ = 0xff; } } } } /* ================ R_InitTurb ================ */ static void R_InitTurb (void) { int i; for (i = 0 ; i < (SIN_BUFFER_SIZE) ; i++) { sintable[i] = AMP + sin(i * 3.14159 * 2 / CYCLE) * AMP; intsintable[i] = AMP2 + sin(i * 3.14159 * 2 / CYCLE) * AMP2; // AMP2, not 20 } } /* =============== R_Init =============== */ void R_Init (void) { int dummy; // get stack position so we can guess if we are going to overflow r_stack_start = (byte *)&dummy; R_InitTurb (); Cmd_AddCommand ("timerefresh", R_TimeRefresh_f); Cmd_AddCommand ("pointfile", R_ReadPointFile_f); Cvar_RegisterVariable (&r_draworder); Cvar_RegisterVariable (&r_speeds); Cvar_RegisterVariable (&r_timegraph); Cvar_RegisterVariable (&r_graphheight); Cvar_RegisterVariable (&r_drawflat); Cvar_RegisterVariable (&r_ambient); Cvar_RegisterVariable (&r_clearcolor); Cvar_RegisterVariable (&r_waterwarp); Cvar_RegisterVariable (&r_fullbright); Cvar_RegisterVariable (&r_drawentities); Cvar_RegisterVariable (&r_drawviewmodel); Cvar_RegisterVariable (&r_aliasstats); Cvar_RegisterVariable (&r_dspeeds); Cvar_RegisterVariable (&r_reportsurfout); Cvar_RegisterVariable (&r_maxsurfs); Cvar_RegisterVariable (&r_numsurfs); Cvar_RegisterVariable (&r_reportedgeout); Cvar_RegisterVariable (&r_maxedges); Cvar_RegisterVariable (&r_numedges); Cvar_RegisterVariable (&r_aliastransbase); Cvar_RegisterVariable (&r_aliastransadj); Cvar_RegisterVariable (&r_aliasmip); Cvar_RegisterVariable (&r_wholeframe); Cvar_RegisterVariable (&r_transwater); Cvar_RegisterVariable (&r_texture_external); Cvar_RegisterVariable (&r_dynamic); Cvar_SetValueQuick (&r_maxedges, (float)NUMSTACKEDGES); Cvar_SetValueQuick (&r_maxsurfs, (float)NUMSTACKSURFACES); view_clipplanes[0].leftedge = true; view_clipplanes[1].rightedge = true; view_clipplanes[1].leftedge = view_clipplanes[2].leftedge = view_clipplanes[3].leftedge = false; view_clipplanes[0].rightedge = view_clipplanes[2].rightedge = view_clipplanes[3].rightedge = false; r_refdef.xOrigin = XCENTERING; r_refdef.yOrigin = YCENTERING; transTable = (byte *)FS_LoadHunkFile ("gfx/tinttab.lmp", NULL); if (!transTable) Sys_Error ("Couldn't load gfx/tinttab.lmp"); if (fs_filesize != 65536) Sys_Error ("Unexpected file size (%ld) for %s\n", fs_filesize, "gfx/tinttab.lmp"); mainTransTable = (byte *)FS_LoadHunkFile ("gfx/tinttab2.lmp", NULL); if (!mainTransTable) Sys_Error ("Couldn't load gfx/tinttab2.lmp"); if (fs_filesize != 65536) Sys_Error ("Unexpected file size (%ld) for %s\n", fs_filesize, "gfx/tinttab2.lmp"); playerTranslation = (byte *)FS_LoadHunkFile ("gfx/player.lmp", NULL); if (!playerTranslation) Sys_Error ("Couldn't load gfx/player.lmp"); R_InitParticles (); D_Init (); #if id386 Sys_MakeCodeWriteable ((long)R_EdgeCodeStart, (long)R_EdgeCodeEnd - (long)R_EdgeCodeStart); Sys_MakeCodeWriteable ((long)R_EdgeCodeStartT, (long)R_EdgeCodeEndT - (long)R_EdgeCodeStartT); D_Patch(); R_TranPatch1(); R_TranPatch2(); R_TranPatch6(); Sys_MakeCodeWriteable ((int)D_Draw16StartT, (int)D_Draw16EndT - (int)D_Draw16StartT); R_TranPatch3(); Sys_MakeCodeWriteable ((int)D_SpriteSpansStartT, (int)D_SpriteSpansEndT - (int)D_SpriteSpansStartT); R_TranPatch4(); Sys_MakeCodeWriteable ((int)D_SpriteSpansStartT2, (int)D_SpriteSpansEndT2 - (int)D_SpriteSpansStartT2); R_TranPatch5(); Sys_MakeCodeWriteable ((int)D_DrawTurbulent8TSpan, (int)D_DrawTurbulent8TSpanEnd - (int)D_DrawTurbulent8TSpan); R_TranPatch7(); #endif } /* =============== R_NewMap =============== */ void R_NewMap (void) { int i; memset (&r_worldentity, 0, sizeof(r_worldentity)); r_worldentity.model = cl.worldmodel; // clear out efrags in case the level hasn't been reloaded // FIXME: is this one short? for (i = 0; i < cl.worldmodel->numleafs; i++) cl.worldmodel->leafs[i].efrags = NULL; r_viewleaf = NULL; R_ClearParticles (); r_cnumsurfs = r_maxsurfs.integer; if (r_cnumsurfs <= MINSURFACES) r_cnumsurfs = MINSURFACES; if (r_cnumsurfs > NUMSTACKSURFACES) { surfaces = (surf_t *) Hunk_AllocName (r_cnumsurfs * sizeof(surf_t), "surfaces"); surface_p = surfaces; surf_max = &surfaces[r_cnumsurfs]; r_surfsonstack = false; // surface 0 doesn't really exist; it's just a dummy because index 0 // is used to indicate no edge attached to surface surfaces--; #if id386 R_SurfacePatch (); #endif } else { r_surfsonstack = true; } SaveSurfacesSize = r_cnumsurfs * sizeof(surf_t); SaveSurfaces = (surf_t *) Hunk_AllocName (SaveSurfacesSize, "surfback"); r_maxedgesseen = 0; r_maxsurfsseen = 0; r_numallocatededges = r_maxedges.integer; if (r_numallocatededges < MINEDGES) r_numallocatededges = MINEDGES; if (r_numallocatededges <= NUMSTACKEDGES) { auxedges = NULL; } else { auxedges = (edge_t *) Hunk_AllocName (r_numallocatededges * sizeof(edge_t), "edges"); } SaveEdgesSize = r_numallocatededges * sizeof(edge_t); SaveEdges = (edge_t *) Hunk_AllocName (SaveEdgesSize, "edgeback"); r_dowarpold = false; r_viewchanged = false; #ifdef PASSAGES CreatePassages (); #endif } /* =============== R_SetVrect =============== */ void R_SetVrect (vrect_t *pvrectin, vrect_t *pvrect, int lineadj) { float size; int h; size = scr_viewsize.integer > 100 ? 100.0 : scr_viewsize.integer; if (cl.intermission) { size = 100.0; // intermission is always full screen lineadj = 0; } size /= 100.0; h = pvrectin->height - lineadj; pvrect->width = pvrectin->width * size; if (pvrect->width < 96) { size = 96.0 / pvrectin->width; pvrect->width = 96; // min for icons } pvrect->width &= ~7; pvrect->height = pvrectin->height * size; if (pvrect->height > pvrectin->height - lineadj) pvrect->height = pvrectin->height - lineadj; pvrect->height &= ~1; pvrect->x = (pvrectin->width - pvrect->width)/2; pvrect->y = (h - pvrect->height)/2; } /* =============== R_ViewChanged Called every time the vid structure or r_refdef changes. Guaranteed to be called before the first refresh =============== */ #if defined(PLATFORM_DOS) || defined(SVGAQUAKE) #define NOT_VGA_MODE (vid.aspect <= 1.10f) /* no Hor+ for weirdass VGA modes */ #elif defined(PLATFORM_AMIGAOS3) #define NOT_VGA_MODE (!vid.noadapt) /* no Hor+ for Amiga native chipset modes */ #else #define NOT_VGA_MODE true #endif #define FOV_ADAPTING (scr_fov_adapt.integer && NOT_VGA_MODE) void R_ViewChanged (float aspect) { int i; float res_scale; r_viewchanged = true; r_refdef.horizontalFieldOfView = 2.0 * tan (r_refdef.fov_x/360*M_PI); r_refdef.fvrectx = (float)r_refdef.vrect.x; r_refdef.fvrectx_adj = (float)r_refdef.vrect.x - 0.5; r_refdef.vrect_x_adj_shift20 = (r_refdef.vrect.x<<20) + (1<<19) - 1; r_refdef.fvrecty = (float)r_refdef.vrect.y; r_refdef.fvrecty_adj = (float)r_refdef.vrect.y - 0.5; r_refdef.vrectright = r_refdef.vrect.x + r_refdef.vrect.width; r_refdef.vrectright_adj_shift20 = (r_refdef.vrectright<<20) + (1<<19) - 1; r_refdef.fvrectright = (float)r_refdef.vrectright; r_refdef.fvrectright_adj = (float)r_refdef.vrectright - 0.5; r_refdef.vrectrightedge = (float)r_refdef.vrectright - 0.99; r_refdef.vrectbottom = r_refdef.vrect.y + r_refdef.vrect.height; r_refdef.fvrectbottom = (float)r_refdef.vrectbottom; r_refdef.fvrectbottom_adj = (float)r_refdef.vrectbottom - 0.5; r_refdef.aliasvrect.x = (int)(r_refdef.vrect.x * r_aliasuvscale); r_refdef.aliasvrect.y = (int)(r_refdef.vrect.y * r_aliasuvscale); r_refdef.aliasvrect.width = (int)(r_refdef.vrect.width * r_aliasuvscale); r_refdef.aliasvrect.height = (int)(r_refdef.vrect.height * r_aliasuvscale); r_refdef.aliasvrectright = r_refdef.aliasvrect.x + r_refdef.aliasvrect.width; r_refdef.aliasvrectbottom = r_refdef.aliasvrect.y + r_refdef.aliasvrect.height; xOrigin = r_refdef.xOrigin; yOrigin = r_refdef.yOrigin; // 320*200 1.0 pixelAspect = 1.6 screenAspect // 320*240 1.0 pixelAspect = 1.3333 screenAspect // proper 320*200 pixelAspect = 0.8333333 if (FOV_ADAPTING) { pixelAspect = 1; verticalFieldOfView = 2.0 * tan (r_refdef.fov_y/360*M_PI); } else { pixelAspect = aspect; screenAspect = r_refdef.vrect.width*pixelAspect / r_refdef.vrect.height; verticalFieldOfView = r_refdef.horizontalFieldOfView / screenAspect; } // values for perspective projection // if math were exact, the values would range from 0.5 to to range+0.5 // hopefully they wll be in the 0.000001 to range+.999999 and truncate // the polygon rasterization will never render in the first row or column // but will definately render in the [range] row and column, so adjust the // buffer origin to get an exact edge to edge fill xcenter = ((float)r_refdef.vrect.width * XCENTERING) + r_refdef.vrect.x - 0.5; aliasxcenter = xcenter * r_aliasuvscale; ycenter = ((float)r_refdef.vrect.height * YCENTERING) + r_refdef.vrect.y - 0.5; aliasycenter = ycenter * r_aliasuvscale; xscale = r_refdef.vrect.width / r_refdef.horizontalFieldOfView; aliasxscale = xscale * r_aliasuvscale; xscaleinv = 1.0 / xscale; yscale = (FOV_ADAPTING) ? r_refdef.vrect.height / verticalFieldOfView : xscale * pixelAspect; aliasyscale = yscale * r_aliasuvscale; yscaleinv = 1.0 / yscale; xscaleshrink = (r_refdef.vrect.width-6)/r_refdef.horizontalFieldOfView; yscaleshrink = xscaleshrink*pixelAspect; // left side clip screenedge[0].normal[0] = -1.0 / (xOrigin*r_refdef.horizontalFieldOfView); screenedge[0].normal[1] = 0; screenedge[0].normal[2] = 1; screenedge[0].type = PLANE_ANYZ; // right side clip screenedge[1].normal[0] = 1.0 / ((1.0-xOrigin)*r_refdef.horizontalFieldOfView); screenedge[1].normal[1] = 0; screenedge[1].normal[2] = 1; screenedge[1].type = PLANE_ANYZ; // top side clip screenedge[2].normal[0] = 0; screenedge[2].normal[1] = -1.0 / (yOrigin*verticalFieldOfView); screenedge[2].normal[2] = 1; screenedge[2].type = PLANE_ANYZ; // bottom side clip screenedge[3].normal[0] = 0; screenedge[3].normal[1] = 1.0 / ((1.0-yOrigin)*verticalFieldOfView); screenedge[3].normal[2] = 1; screenedge[3].type = PLANE_ANYZ; for (i = 0; i < 4; i++) VectorNormalize (screenedge[i].normal); res_scale = sqrt((double)(r_refdef.vrect.width * r_refdef.vrect.height) / (320.0 * 152.0)) * (2.0 / r_refdef.horizontalFieldOfView); r_aliastransition = r_aliastransbase.value * res_scale; r_resfudge = r_aliastransadj.value * res_scale; if (scr_fov.integer <= 90.0) r_fov_greater_than_90 = false; else r_fov_greater_than_90 = true; // TODO: collect 386-specific code in one place #if id386 if (r_pixbytes == 1) { Sys_MakeCodeWriteable ((long)R_Surf8Start, (long)R_Surf8End - (long)R_Surf8Start); colormap = vid.colormap; R_Surf8Patch (); } else { Sys_MakeCodeWriteable ((long)R_Surf16Start, (long)R_Surf16End - (long)R_Surf16Start); colormap = vid.colormap16; R_Surf16Patch (); } #endif /* id386 */ D_ViewChanged (); } #undef NOT_VGA_MODE #undef FOV_ADAPTING /* =============== R_MarkLeaves =============== */ static void R_MarkLeaves (void) { byte *vis; mnode_t *node; int i; if (r_oldviewleaf == r_viewleaf) return; r_visframecount++; r_oldviewleaf = r_viewleaf; vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); for (i = 0; i < cl.worldmodel->numleafs; i++) { if (vis[i>>3] & (1<<(i&7))) { node = (mnode_t *)&cl.worldmodel->leafs[i+1]; do { if (node->visframe == r_visframecount) break; node->visframe = r_visframecount; node = node->parent; } while (node); } } } static void R_PrepareAlias (void) { int j; int lnum; alight_t lighting; // FIXME: remove and do real lighting float lightvec[3] = {-1, 0, 0}; vec3_t dist; float add; vec3_t adjust_origin; VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); // see if the bounding box lets us trivially reject, also sets // trivial accept status if (R_AliasCheckBBox ()) { VectorCopy(currententity->origin, adjust_origin); adjust_origin[2] += (currententity->model->mins[2] + currententity->model->maxs[2]) / 2; j = R_LightPoint (adjust_origin); lighting.ambientlight = j; lighting.shadelight = j; lighting.plightvec = lightvec; if (r_dynamic.integer) { for (lnum = 0; lnum < MAX_DLIGHTS; lnum++) { if (cl_dlights[lnum].die >= cl.time) { VectorSubtract (currententity->origin, cl_dlights[lnum].origin, dist); if (cl_dlights[lnum].radius> 0) { add = cl_dlights[lnum].radius - VectorLengthFast(dist); if (add > 0) lighting.ambientlight += add; } else { add = VectorLengthFast(dist) + cl_dlights[lnum].radius; if (add < 0) lighting.ambientlight += add; } } } } // clamp lighting so it doesn't overbright as much if (lighting.ambientlight > 128) lighting.ambientlight = 128; if (lighting.ambientlight < 0) lighting.ambientlight = 0; if (lighting.ambientlight + lighting.shadelight > 192) lighting.shadelight = 192 - lighting.ambientlight; R_AliasDrawModel (&lighting); } } /* ============= R_DrawEntitiesOnList ============= */ static void R_DrawEntitiesOnList (void) { int i; if (!r_drawentities.integer) return; for (i = 0; i < cl_numvisedicts; i++) { currententity = cl_visedicts[i]; if (currententity == &cl_entities[cl.viewentity]) { if (chase_active.integer == 0) continue; // don't draw the player else // chase-cam pitch adj. by FrikaC currententity->angles[0] *= 0.3; } switch (currententity->model->type) { case mod_sprite: if ((currententity->model->flags & EF_TRANSPARENT) || (currententity->drawflags & DRF_TRANSLUCENT)) { break; } VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); R_DrawSprite (); break; case mod_alias: if ((currententity->model->flags & (EF_TRANSPARENT|EF_HOLEY|EF_SPECIAL_TRANS)) || (currententity->drawflags & DRF_TRANSLUCENT)) { break; } R_PrepareAlias (); break; default: break; } } // Draw the transparent stuff for (i = 0; i < cl_numvisedicts; i++) { currententity = cl_visedicts[i]; if (currententity == &cl_entities[cl.viewentity] && chase_active.integer == 0) continue; // don't draw the player switch (currententity->model->type) { case mod_sprite: if ((currententity->model->flags & EF_TRANSPARENT) || (currententity->drawflags & DRF_TRANSLUCENT)) { VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); R_DrawSprite (); } break; case mod_alias: if ((currententity->model->flags & (EF_TRANSPARENT|EF_HOLEY|EF_SPECIAL_TRANS)) || (currententity->drawflags & DRF_TRANSLUCENT)) { R_PrepareAlias (); } break; default: break; } } } /* ============= R_DrawViewModel ============= */ static void R_DrawViewModel (void) { // FIXME: remove and do real lighting float lightvec[3] = {-1, 0, 0}; int j; int lnum; vec3_t dist; float add; dlight_t *dl; currententity = &cl.viewent; if (!currententity->model) return; VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); VectorCopy (vup, viewlightvec); VectorInverse (viewlightvec); j = R_LightPoint (currententity->origin); if (j < 24) j = 24; // always give some light on gun r_viewlighting.ambientlight = j; r_viewlighting.shadelight = j; // add dynamic lights if (r_dynamic.integer) { for (lnum = 0; lnum < MAX_DLIGHTS; lnum++) { dl = &cl_dlights[lnum]; if (!dl->radius) continue; if (dl->die < cl.time) continue; VectorSubtract (currententity->origin, dl->origin, dist); if (dl->radius > 0) { add = dl->radius - VectorLengthFast(dist); if (add > 0) r_viewlighting.ambientlight += add; } else { add = VectorLengthFast(dist) + dl->radius; if (add < 0) r_viewlighting.ambientlight += add; } } } // clamp lighting so it doesn't overbright as much if (r_viewlighting.ambientlight > 128) r_viewlighting.ambientlight = 128; if (r_viewlighting.ambientlight < 0) r_viewlighting.ambientlight = 0; if (r_viewlighting.ambientlight + r_viewlighting.shadelight > 192) r_viewlighting.shadelight = 192 - r_viewlighting.ambientlight; r_viewlighting.plightvec = lightvec; cl.light_level = r_viewlighting.ambientlight; if ((cl.v.health <= 0) || (chase_active.integer) || //rjr (cl.items & IT_INVISIBILITY) || //O.S. (r_fov_greater_than_90) || (!r_drawviewmodel.integer)) { return; } R_AliasDrawModel (&r_viewlighting); } /* ============= R_BmodelCheckBBox ============= */ static int R_BmodelCheckBBox (qmodel_t *clmodel, float *minmaxs) { int i, *pindex, clipflags; vec3_t acceptpt, rejectpt; double d; clipflags = 0; if (currententity->angles[0] || currententity->angles[1] || currententity->angles[2]) { for (i = 0; i < 4; i++) { d = DotProduct (currententity->origin, view_clipplanes[i].normal); d -= view_clipplanes[i].dist; if (d <= -clmodel->radius) return BMODEL_FULLY_CLIPPED; if (d <= clmodel->radius) clipflags |= (1< tmaxs[j]) tmaxs[j] = v[j]; } } } /* ============= R_DrawBEntitiesOnList ============= */ static void R_DrawBEntitiesOnList (void) { int i, j, k, clipflags; vec3_t oldorigin; qmodel_t *clmodel; float minmaxs[6]; vec3_t mins, maxs; if (!r_drawentities.integer) return; VectorCopy (modelorg, oldorigin); insubmodel = true; r_dlightframecount = r_framecount; for (i = 0; i < cl_numvisedicts; i++) { currententity = cl_visedicts[i]; switch (currententity->model->type) { case mod_brush: clmodel = currententity->model; // see if the bounding box lets us trivially reject, // also sets trivial accept status RotatedBBox (clmodel->mins, clmodel->maxs, currententity->angles, mins, maxs); VectorAdd (mins, currententity->origin, minmaxs); VectorAdd (maxs, currententity->origin, (minmaxs+3)); clipflags = R_BmodelCheckBBox (clmodel, minmaxs); if (clipflags != BMODEL_FULLY_CLIPPED) { VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); r_pcurrentvertbase = clmodel->vertexes; // FIXME: stop transforming twice R_RotateBmodel (); // calculate dynamic lighting for bmodel // if it's not an instanced model if (r_dynamic.integer) { if (clmodel->firstmodelsurface != 0) { for (k = 0; k < MAX_DLIGHTS; k++) { if ((cl_dlights[k].die < cl.time) || (!cl_dlights[k].radius)) { continue; } R_MarkLights (&cl_dlights[k], 1<nodes + clmodel->hulls[0].firstclipnode); } } } #if 0 // if the driver wants polygons, deliver those. Z-buffering is on // at this point, so no clipping to the world tree is needed, just // frustum clipping if (r_drawpolys | r_drawculledpolys) { R_ZDrawSubmodelPolys (clmodel); } else #endif { r_pefragtopnode = NULL; for (j = 0; j < 3; j++) { r_emins[j] = minmaxs[j]; r_emaxs[j] = minmaxs[3+j]; } R_SplitEntityOnNode2 (cl.worldmodel->nodes); if (r_pefragtopnode) { currententity->topnode = r_pefragtopnode; if (r_pefragtopnode->contents >= 0) { // not a leaf; has to be clipped to the world BSP r_clipflags = clipflags; R_DrawSolidClippedSubmodelPolygons (clmodel); } else { // falls entirely in one leaf, so we just put all the // edges in the edge list and let 1/z sorting handle // drawing order R_DrawSubmodelPolygons (clmodel, clipflags); } currententity->topnode = NULL; } } // put back world rotation and frustum clipping // FIXME: R_RotateBmodel should just work off base_vxx VectorCopy (base_vpn, vpn); VectorCopy (base_vup, vup); VectorCopy (base_vright, vright); VectorCopy (base_modelorg, modelorg); VectorCopy (oldorigin, modelorg); R_TransformFrustum (); } break; default: break; } } insubmodel = false; } /* ================ R_EdgeDrawing ================ */ static void R_EdgeDrawing (qboolean Translucent) { #if id386 # define static_in_C_ /* nothing */ #else # define static_in_C_ static #endif /* R_EdgeDrawing() is called twice by R_RenderView_(): * First with Translucent as false, where the three pointers r_edges, * surfaces and surf_max are set to the ledges[] and lsurfs[] arrays, * and then with Translucent as true in which case R_EdgeDrawing() * does *not* set the pointers and the code assumes them to be still * valid, but they pointed to variables which went out of scope by * that time. * The x86 assembler code actually handles that (R_SurfacePatch() -> * R_SurfacePatchT(), I think) and if you make those arrays static, * then you may as well get a segmentation fault. * For C-only code, however, the ledges[] and lsurfs[] arrays *must* * be static in order to keep r_edges, surfaces and surf_max pointers * valid during the second call of R_EdgeDrawing(true). */ static_in_C_ edge_t ledges[NUMSTACKEDGES + ((CACHE_SIZE - 1) / sizeof(edge_t)) + 1]; static_in_C_ surf_t lsurfs[NUMSTACKSURFACES + ((CACHE_SIZE - 1) / sizeof(surf_t)) + 1]; int EdgesSize, SurfacesSize; if (!Translucent) { if (auxedges) { r_edges = auxedges; } else { r_edges = (edge_t *) (((intptr_t)&ledges[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); } if (r_surfsonstack) { surfaces = (surf_t *) (((intptr_t)&lsurfs[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); surf_max = &surfaces[r_cnumsurfs]; // surface 0 doesn't really exist; it's just a dummy because // index 0 is used to indicate no edge attached to surface surfaces--; #if id386 R_SurfacePatch (); #endif } R_BeginEdgeFrame (); if (r_dspeeds.integer) { rw_time1 = Sys_DoubleTime (); } R_RenderWorld (); } else { memcpy(r_edges,SaveEdges,SaveEdgesCount * sizeof(edge_t)); memcpy(surfaces,SaveSurfaces,SaveSurfacesCount * sizeof(surf_t)); } #if 0 if (r_drawculledpolys) R_ScanEdges (Translucent); #endif // only the world can be drawn back to front with no z reads or compares, just // z writes, so have the driver turn z compares on now if (!Translucent) { D_TurnZOn (); if (r_dspeeds.integer) { rw_time2 = Sys_DoubleTime (); db_time1 = rw_time2; } R_DrawBEntitiesOnList (); SaveSurfacesCount = surface_p - surfaces; SurfacesSize = SaveSurfacesCount * sizeof(surf_t); SaveEdgesCount = edge_p - r_edges; EdgesSize = SaveEdgesCount * sizeof(edge_t); if (SurfacesSize > SaveSurfacesSize || EdgesSize > SaveEdgesSize) { Con_Printf("WARNING: Translucency disabled: %d/%d %d/%d\n",SurfacesSize,SaveSurfacesSize,EdgesSize,SaveEdgesSize); AllowTranslucency = false; } else { AllowTranslucency = true; memcpy(SaveEdges,r_edges,EdgesSize); memcpy(SaveSurfaces,surfaces,SurfacesSize); } if (r_dspeeds.integer) { db_time2 = Sys_DoubleTime (); se_time1 = db_time2; } if (!r_dspeeds.integer) { VID_UnlockBuffer (); S_ExtraUpdate (); // don't let sound get messed up if going slow VID_LockBuffer (); } } #if 0 if (!(r_drawpolys | r_drawculledpolys)) #endif R_ScanEdges (Translucent); } /* ================ R_RenderView r_refdef must be set before the first call ================ */ static void R_RenderView_ (void) { byte warpbuffer[WARP_WIDTH * WARP_HEIGHT]; r_warpbuffer = warpbuffer; if (r_timegraph.integer || r_speeds.integer || r_dspeeds.integer) { if (r_wholeframe.integer) r_time1 = r_lasttime1; else r_time1 = Sys_DoubleTime (); } R_SetupFrame (); #ifdef PASSAGES SetVisibilityByPassages (); #else R_MarkLeaves (); // done here so we know if we're in water #endif // make FDIV fast. This reduces timing precision after we've been running for a // while, so we don't do it globally. This also sets chop mode, and we do it // here so that setup stuff like the refresh area calculations match what's // done in screen.c Sys_LowFPPrecision (); if (!r_worldentity.model || !cl.worldmodel) Sys_Error ("%s: NULL worldmodel", __thisfunc__); if (!r_dspeeds.integer) { VID_UnlockBuffer (); S_ExtraUpdate (); // don't let sound get messed up if going slow VID_LockBuffer (); } R_EdgeDrawing (false); if (!r_dspeeds.integer) { VID_UnlockBuffer (); S_ExtraUpdate (); // don't let sound get messed up if going slow VID_LockBuffer (); } if (r_dspeeds.integer) { se_time2 = Sys_DoubleTime (); de_time1 = se_time2; } R_DrawEntitiesOnList (); if (TransCount && AllowTranslucency) R_EdgeDrawing (true); if (r_dspeeds.integer) { de_time2 = Sys_DoubleTime (); dv_time1 = de_time2; } R_DrawViewModel (); if (r_dspeeds.integer) { dv_time2 = Sys_DoubleTime (); dp_time1 = Sys_DoubleTime (); } R_DrawParticles (); if (r_dspeeds.integer) dp_time2 = Sys_DoubleTime (); if (r_dowarp) D_WarpScreen (); V_SetContentsColor (r_viewleaf->contents); if (r_timegraph.integer) R_TimeGraph (); if (r_aliasstats.integer) R_PrintAliasStats (); if (r_speeds.integer) R_PrintTimes (); if (r_dspeeds.integer) R_PrintDSpeeds (); if (r_reportsurfout.integer && r_outofsurfaces) Con_Printf ("Short %d surfaces\n", r_outofsurfaces); if (r_reportedgeout.integer && r_outofedges) Con_Printf ("Short roughly %d edges\n", r_outofedges * 2 / 3); // back to high floating-point precision Sys_HighFPPrecision (); } void R_RenderView (void) { /* int dummy; int delta; delta = (byte *)&dummy - r_stack_start; if (delta < -10000 || delta > 10000) Sys_Error ("%s: called without enough stack", __thisfunc__); */ if ( Hunk_LowMark() & 3 ) Sys_Error ("Hunk is missaligned"); /* if ( (intptr_t)(&dummy) & 3 ) Sys_Error ("Stack is missaligned"); */ if ( (intptr_t)(&r_warpbuffer) & 3 ) Sys_Error ("Globals are missaligned"); R_RenderView_ (); } engine/hexen2/r_misc.c000066400000000000000000000242131444734033100151410ustar00rootroot00000000000000/* r_misc.c -- * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" /* =============== R_CheckVariables =============== */ static void R_CheckVariables (void) { static int oldbright; if (r_fullbright.integer != oldbright) { oldbright = r_fullbright.integer; D_FlushCaches (); // so all lighting changes } } /* ==================== R_TimeRefresh_f For program optimization ==================== */ void R_TimeRefresh_f (void) { int i; float start, stop, time; int startangle; vrect_t vr; if (cls.state != ca_connected) { Con_Printf("Not connected to a server\n"); return; } startangle = r_refdef.viewangles[1]; start = Sys_DoubleTime (); for (i = 0; i < 128; i++) { r_refdef.viewangles[1] = i/128.0*360.0; VID_LockBuffer (); R_RenderView (); VID_UnlockBuffer (); vr.x = r_refdef.vrect.x; vr.y = r_refdef.vrect.y; vr.width = r_refdef.vrect.width; vr.height = r_refdef.vrect.height; vr.pnext = NULL; VID_Update (&vr); } stop = Sys_DoubleTime (); time = stop-start; Con_Printf ("%f seconds (%f fps)\n", time, 128/time); r_refdef.viewangles[1] = startangle; } /* ================ R_LineGraph ================ */ static void R_LineGraph (int x, int y, int h, int drawType, int marker) { int i; byte *dest; int s; // FIXME: should be disabled on no-buffer adapters, or should be in the driver // drawType // 0-000: expanded / solid / background // 1-001: condensed / background // 2-010: expanded / holey / background // 3-011: condensed / background // 4-100: expanded / solid / no background // 5-101: condensed / no background // 6-110: expanded / holey / no background // 7-111: condensed / no background x += r_refdef.vrect.x; y += r_refdef.vrect.y; dest = vid.buffer + vid.rowbytes*y + x; s = r_graphheight.integer; if (s > r_refdef.vrect.height) { s = r_refdef.vrect.height; } if (h > s) { h = s; } for (i = 0; i < h; i++, dest -= vid.rowbytes) { dest[0] = ((i+1) % marker == 0) ? 143 : 255; if (!(drawType & 1)) { // Expanded if (!(drawType & 2)) { // Solid *(dest-vid.rowbytes) = 53; } dest -= vid.rowbytes; } } if (drawType & 4) { // No background return; } for ( ; i < s; i++, dest -= vid.rowbytes) { dest[0] = 53; if (!(drawType & 1)) { // Expanded if (!(drawType & 2)) { // Solid *(dest-vid.rowbytes) = 53; } dest -= vid.rowbytes; } } } /* ============== R_TimeGraph Performance monitoring tool ============== */ #define MAX_TIMINGS 100 #define GRAPH_TYPE_COUNT 2 extern int LastServerMessageSize; void R_TimeGraph (void) { int a, x; int drawType, graphType; static int timex; static byte r_timings[MAX_TIMINGS]; static int graphMarkers[GRAPH_TYPE_COUNT] = { 10000, 10 }; graphType = r_timegraph.integer; if (graphType < 1 || graphType > GRAPH_TYPE_COUNT) { return; } drawType = (int)((r_timegraph.value-floor(r_timegraph.value)+1E-3)*10); if (graphType == 1) { // Frame times a = (Sys_DoubleTime() - r_time1) / 0.01; r_timings[timex] = a; } else { // Packet sizes a = LastServerMessageSize/10; LastServerMessageSize = 0; r_timings[timex] = a; } a = timex; if (r_refdef.vrect.width <= MAX_TIMINGS) { x = r_refdef.vrect.width-1; } else { x = r_refdef.vrect.width - (r_refdef.vrect.width-MAX_TIMINGS)/2; } do { R_LineGraph(x, r_refdef.vrect.height-2, r_timings[a], drawType, graphMarkers[graphType-1]); if (x == 0) { break; // screen too small to hold entire thing } x--; a--; if (a == -1) { a = MAX_TIMINGS-1; } } while (a != timex); timex = (timex+1) % MAX_TIMINGS; } /* ============= R_PrintTimes ============= */ void R_PrintTimes(void) { float r_time2; float ms, fps; r_lasttime1 = r_time2 = Sys_DoubleTime(); ms = 1000 * (r_time2 - r_time1); fps = 1000 / ms; Con_Printf("%3.1f fps %5.0f ms\n%3i/%3i/%3i poly %3i surf\n", fps, ms, c_faceclip, r_polycount, r_drawnpolycount, c_surf); c_surf = 0; } /* ============= R_PrintDSpeeds ============= */ void R_PrintDSpeeds (void) { float ms, dp_time, r_time2, rw_time, db_time, se_time, de_time, dv_time; r_time2 = Sys_DoubleTime (); dp_time = (dp_time2 - dp_time1) * 1000; rw_time = (rw_time2 - rw_time1) * 1000; db_time = (db_time2 - db_time1) * 1000; se_time = (se_time2 - se_time1) * 1000; de_time = (de_time2 - de_time1) * 1000; dv_time = (dv_time2 - dv_time1) * 1000; ms = (r_time2 - r_time1) * 1000; Con_Printf ("%3i %4.1fp %3iw %4.1fb %3is %4.1fe %4.1fv\n", (int)ms, dp_time, (int)rw_time, db_time, (int)se_time, de_time, dv_time); } /* ============= R_PrintAliasStats ============= */ void R_PrintAliasStats (void) { Con_Printf ("%3i polygon model drawn\n", r_amodels_drawn); } #if !id68k /* =================== R_TransformFrustum =================== */ void R_TransformFrustum (void) { int i; vec3_t v, v2; for (i = 0 ; i < 4 ; i++) { v[0] = screenedge[i].normal[2]; v[1] = -screenedge[i].normal[0]; v[2] = screenedge[i].normal[1]; v2[0] = v[1]*vright[0] + v[2]*vup[0] + v[0]*vpn[0]; v2[1] = v[1]*vright[1] + v[2]*vup[1] + v[0]*vpn[1]; v2[2] = v[1]*vright[2] + v[2]*vup[2] + v[0]*vpn[2]; VectorCopy (v2, view_clipplanes[i].normal); view_clipplanes[i].dist = DotProduct (modelorg, v2); } } #endif #if !id386 && !id68k /* ================ TransformVector ================ */ void TransformVector (vec3_t in, vec3_t out) { out[0] = DotProduct(in,vright); out[1] = DotProduct(in,vup); out[2] = DotProduct(in,vpn); } #endif /* ================ R_TransformPlane ================ */ void R_TransformPlane (mplane_t *p, float *normal, float *dist) { float d; d = DotProduct (r_origin, p->normal); *dist = p->dist - d; // TODO: when we have rotating entities, this will need to use the view matrix TransformVector (p->normal, normal); } /* =============== R_SetUpFrustumIndexes =============== */ static void R_SetUpFrustumIndexes (void) { int i, j, *pindex; pindex = r_frustum_indexes; for (i = 0 ; i < 4 ; i++) { for (j = 0 ; j < 3 ; j++) { if (view_clipplanes[i].normal[j] < 0) { pindex[j] = j; pindex[j+3] = j+3; } else { pindex[j] = j+3; pindex[j+3] = j; } } // FIXME: do just once at start pfrustum_indexes[i] = pindex; pindex += 6; } } /* =============== R_SetupFrame =============== */ void R_SetupFrame (void) { int edgecount; vrect_t vrect; float w, h; // don't allow cheats in multiplayer if (cl.maxclients > 1) { r_draworder.integer = 0; r_fullbright.integer = 0; r_ambient.integer = 0; r_drawflat.integer = 0; } if (r_numsurfs.integer) { if ((surface_p - surfaces) > r_maxsurfsseen) r_maxsurfsseen = surface_p - surfaces; Con_Printf ("Used %ld of %ld surfs; %d max\n", (long)(surface_p - surfaces), (long)(surf_max - surfaces), r_maxsurfsseen); } if (r_numedges.integer) { edgecount = edge_p - r_edges; if (edgecount > r_maxedgesseen) r_maxedgesseen = edgecount; Con_Printf ("Used %d of %d edges; %d max\n", edgecount, r_numallocatededges, r_maxedgesseen); } r_refdef.ambientlight = r_ambient.integer; if (r_refdef.ambientlight < 0) r_refdef.ambientlight = 0; if (!sv.active) r_draworder.integer = 0; // don't let cheaters look behind walls R_CheckVariables (); R_AnimateLight (); r_framecount++; numbtofpolys = 0; #if 0 // debugging r_refdef.vieworg[0] = 80; r_refdef.vieworg[1] = 64; r_refdef.vieworg[2] = 40; r_refdef.viewangles[0] = 0; r_refdef.viewangles[1] = 46.763641357; r_refdef.viewangles[2] = 0; #endif // build the transformation matrix for the given view angles VectorCopy (r_refdef.vieworg, modelorg); VectorCopy (r_refdef.vieworg, r_origin); AngleVectors (r_refdef.viewangles, vpn, vright, vup); // current viewleaf r_oldviewleaf = r_viewleaf; r_viewleaf = Mod_PointInLeaf (r_origin, cl.worldmodel); r_dowarpold = r_dowarp; r_dowarp = r_waterwarp.integer && (r_viewleaf->contents <= CONTENTS_WATER); if ((r_dowarp != r_dowarpold) || r_viewchanged) { if (r_dowarp) { if ((vid.width <= vid.maxwarpwidth) && (vid.height <= vid.maxwarpheight)) { vrect.x = 0; vrect.y = 0; vrect.width = vid.width; vrect.height = vid.height; R_SetVrect (&vrect, &r_refdef.vrect, sb_lines); R_ViewChanged (vid.aspect); } else { w = vid.width; h = vid.height; if (w > vid.maxwarpwidth) { h *= (float)vid.maxwarpwidth / w; w = vid.maxwarpwidth; } if (h > vid.maxwarpheight) { h = vid.maxwarpheight; w *= (float)vid.maxwarpheight / h; } vrect.x = 0; vrect.y = 0; vrect.width = (int)w; vrect.height = (int)h; R_SetVrect (&vrect, &r_refdef.vrect, (int)((float)sb_lines * (h/(float)vid.height))); R_ViewChanged (vid.aspect * (h / w) * ((float)vid.width / (float)vid.height)); } } else { // scr_vrect alredy holds the original data, // therefore no need for extra R_SetVrect() r_refdef.vrect = scr_vrect; R_ViewChanged (vid.aspect); } r_viewchanged = false; } // start off with just the four screen edge clip planes R_TransformFrustum (); // save base values VectorCopy (vpn, base_vpn); VectorCopy (vright, base_vright); VectorCopy (vup, base_vup); VectorCopy (modelorg, base_modelorg); R_SetSkyFrame (); R_SetUpFrustumIndexes (); r_cache_thrash = false; // clear frame counts c_faceclip = 0; d_spanpixcount = 0; r_polycount = 0; r_drawnpolycount = 0; r_wholepolycount = 0; r_amodels_drawn = 0; r_outofsurfaces = 0; r_outofedges = 0; D_SetupFrame (); } engine/hexen2/r_part.c000066400000000000000000001221711444734033100151560ustar00rootroot00000000000000/* r_part.c -- particles rendering * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" #define SFL_FLUFFY 1 // All largish flakes #define SFL_MIXED 2 // Mixed flakes #define SFL_HALF_BRIGHT 4 // All flakes start darker #define SFL_NO_MELT 8 // Flakes don't melt when his surface, just go away #define SFL_IN_BOUNDS 16 // Flakes cannot leave the bounds of their box #define SFL_NO_TRANS 32 // All flakes start non-translucent #define SFL_64 64 #define SFL_128 128 #define MAX_PARTICLES 7000 // default max # of particles at one time #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's // on the command line //============================================================================= static int ramp1[8] = { 416, 416+2, 416+4, 416+6, 416+8, 416+10, 416+12, 416+14 }; static int ramp2[8] = { 384+4, 384+6, 384+8, 384+10, 384+12, 384+13, 384+14, 384+15 }; static int ramp3[8] = { 0x6d, 0x6b, 6, 5, 4, 3 }; static int ramp4[16] = { 416, 416+1, 416+2, 416+3, 416+4, 416+5, 416+6, 416+7, 416+8, 416+9, 416+10, 416+11, 416+12, 416+13, 416+14, 416+15 }; static int ramp5[16] = { 400, 400+1, 400+2, 400+3, 400+4, 400+5, 400+6, 400+7, 400+8, 400+9, 400+10, 400+11, 400+12, 400+13, 400+14, 400+15 }; static int ramp6[16] = { 256, 256+1, 256+2, 256+3, 256+4, 256+5, 256+6, 256+7, 256+8, 256+9, 256+10, 256+11, 256+12, 256+13, 256+14, 256+15 }; static int ramp7[16] = { 384, 384+1, 384+2, 384+3, 384+4, 384+5, 384+6, 384+7, 384+8, 384+9, 384+10, 384+11, 384+12, 384+13, 384+14, 384+15 }; static int ramp8[16] = { 175, 174, 173, 172, 171, 170, 169, 168, 167, 166, 13, 14, 15, 16, 17, 18 }; //static int ramp9[16] ={272, 272+1, 272+2, 272+3, 272+4, 272+5, 272+6, 272+7, 272+8, 272+9, 272+10, 272+11, 272+12, 272+13, 272+14, 272+15 }; static int ramp9[16] = { 416, 416+1, 416+2, 416+3, 416+4, 416+5, 416+6, 416+7, 416+8, 416+9, 416+10, 416+11, 416+12, 416+13, 416+14, 416+15 }; /* MISSION PACK */ static int ramp10[16] ={ 432, 432+1, 432+2, 432+3, 432+4, 432+5, 432+6, 432+7, 432+8, 432+9, 432+10, 432+11, 432+12, 432+13, 432+14, 432+15 }; static int ramp11[8] = { 424, 424+1, 424+2, 424+3, 424+4, 424+5, 424+6, 424+7 }; static int ramp12[8] = { 136, 137, 138, 139, 140, 141, 142, 143 }; //============================================================================= particle_t *active_particles, *free_particles; particle_t *particles; static int r_numparticles; vec3_t r_pright, r_pup, r_ppn; static vec3_t rider_origin; static cvar_t leak_color = {"leak_color", "251", CVAR_ARCHIVE}; static cvar_t snow_flurry= {"snow_flurry", "1", CVAR_ARCHIVE}; static cvar_t snow_active= {"snow_active", "1", CVAR_ARCHIVE}; static particle_t *AllocParticle (void); void R_RunParticleEffect2 (vec3_t org, vec3_t dmin, vec3_t dmax, int color, ptype_t effect, int count); void R_RunParticleEffect3 (vec3_t org, vec3_t box, int color, ptype_t effect, int count); void R_RunParticleEffect4 (vec3_t org, float radius, int color, ptype_t effect, int count); //============================================================================= /* =============== R_InitParticles =============== */ void R_InitParticles (void) { int i; i = COM_CheckParm ("-particles"); if (i && i < com_argc-1) { r_numparticles = atoi(com_argv[i+1]); if (r_numparticles < ABSOLUTE_MIN_PARTICLES) r_numparticles = ABSOLUTE_MIN_PARTICLES; } else { r_numparticles = MAX_PARTICLES; } particles = (particle_t *) Hunk_AllocName (r_numparticles * sizeof(particle_t), "particles"); Cvar_RegisterVariable (&leak_color); //JFM: snow test Cvar_RegisterVariable (&snow_flurry); Cvar_RegisterVariable (&snow_active); } void R_DarkFieldParticles (entity_t *ent) { int i, j, k; particle_t *p; float vel; vec3_t dir; vec3_t org; org[0] = ent->origin[0]; org[1] = ent->origin[1]; org[2] = ent->origin[2]; for (i = -16; i < 16; i += 8) { for (j = -16; j < 16; j += 8) { for (k = 0; k < 32; k += 8) { p = AllocParticle(); if (!p) return; p->die = cl.time + 0.2 + (rand() & 7) * 0.02; p->color = 150 + (rand() % 6); p->type = pt_slowgrav; dir[0] = j * 8; dir[1] = i * 8; dir[2] = k * 8; p->org[0] = org[0] + i + (rand() & 3); p->org[1] = org[1] + j + (rand() & 3); p->org[2] = org[2] + k + (rand() & 3); VectorNormalizeFast (dir); vel = 50 + (rand() & 63); VectorScale (dir, vel, p->vel); } } } } /* =============== AllocParticle =============== */ static particle_t *AllocParticle (void) { particle_t *p; if (!free_particles) return NULL; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; return p; } /* =============== R_ClearParticles =============== */ void R_ClearParticles (void) { int i; if (!r_numparticles) return; free_particles = &particles[0]; active_particles = NULL; for (i = 0; i < r_numparticles-1; i++) particles[i].next = &particles[i+1]; particles[r_numparticles-1].next = NULL; } void R_ReadPointFile_f (void) { FILE *f; vec3_t org; int r; int c; particle_t *p; char name[MAX_QPATH]; byte color; if (cls.state != ca_connected) return; // need an active map. color = (byte)leak_color.integer; q_snprintf (name, sizeof(name), "maps/%s.pts", cl.mapname); FS_OpenFile (name, &f, NULL); if (!f) { Con_Printf ("couldn't open %s\n", name); return; } Con_Printf ("Reading %s...\n", name); c = 0; VectorClear (org); // silence pesky compiler warnings for ( ;; ) { r = fscanf (f,"%f %f %f\n", &org[0], &org[1], &org[2]); if (r != 3) break; c++; p = AllocParticle(); if (!p) { Con_Printf ("Not enough free particles\n"); break; } p->die = 99999; p->color = color; // (-c)&15; p->type = pt_static; VectorClear (p->vel); VectorCopy (org, p->org); } fclose (f); Con_Printf ("%i points read\n", c); } #if 0 /* EF_BRIGHTFIELD used this */ /* =============== R_EntityParticles =============== */ #if defined(GLQUAKE) /* otherwise from r_alias.c (r_shared.h) */ #define NUMVERTEXNORMALS 162 static const float r_avertexnormals[NUMVERTEXNORMALS][3] = { #include "anorms.h" }; #endif static vec3_t avelocities[NUMVERTEXNORMALS]; static float beamlength = 16; void R_EntityParticles (entity_t *ent) { int i; particle_t *p; float angle, dist; float sp, sy, cp, cy; vec3_t forward; dist = 64; if (!avelocities[0][0]) { for (i = 0; i < NUMVERTEXNORMALS; i++) { avelocities[i][0] = (rand() & 255) * 0.01; avelocities[i][1] = (rand() & 255) * 0.01; avelocities[i][2] = (rand() & 255) * 0.01; } } for (i = 0; i < NUMVERTEXNORMALS; i++) { angle = cl.time * avelocities[i][0]; sy = sin(angle); cy = cos(angle); angle = cl.time * avelocities[i][1]; sp = sin(angle); cp = cos(angle); angle = cl.time * avelocities[i][2]; forward[0] = cp*cy; forward[1] = cp*sy; forward[2] = -sp; p = AllocParticle(); if (!p) return; p->die = cl.time + 0.01; p->color = 0x6f; p->type = pt_fireball; //pt_explode; p->org[0] = ent->origin[0] + r_avertexnormals[i][0]*dist + forward[0]*beamlength; p->org[1] = ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*beamlength; p->org[2] = ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*beamlength; } } #endif /* =============== R_ParseParticleEffect Parse an effect out of the server message =============== */ void R_ParseParticleEffect (void) { vec3_t org, dir; int i, count, msgcount, color; for (i = 0; i < 3; i++) org[i] = MSG_ReadCoord (); for (i = 0; i < 3; i++) dir[i] = MSG_ReadChar () * (1.0/16); msgcount = MSG_ReadByte (); color = MSG_ReadByte (); if (msgcount == 255) count = 1024; else count = msgcount; R_RunParticleEffect (org, dir, color, count); } /* =============== R_ParseParticleEffect2 Parse an effect out of the server message =============== */ void R_ParseParticleEffect2 (void) { vec3_t org, dmin, dmax; int i, msgcount, color; ptype_t effect; for (i = 0; i < 3; i++) org[i] = MSG_ReadCoord (); for (i = 0; i < 3; i++) dmin[i] = MSG_ReadFloat (); for (i = 0; i < 3; i++) dmax[i] = MSG_ReadFloat (); color = MSG_ReadShort (); msgcount = MSG_ReadByte (); effect = (ptype_t) MSG_ReadByte (); R_RunParticleEffect2 (org, dmin, dmax, color, effect, msgcount); } /* =============== R_ParseParticleEffect3 Parse an effect out of the server message =============== */ void R_ParseParticleEffect3 (void) { vec3_t org, box; int i, msgcount, color; ptype_t effect; for (i = 0; i < 3; i++) org[i] = MSG_ReadCoord (); for (i = 0; i < 3; i++) box[i] = MSG_ReadByte (); color = MSG_ReadShort (); msgcount = MSG_ReadByte (); effect = (ptype_t) MSG_ReadByte (); R_RunParticleEffect3 (org, box, color, effect, msgcount); } /* =============== R_ParseParticleEffect4 Parse an effect out of the server message =============== */ void R_ParseParticleEffect4 (void) { vec3_t org; int i, msgcount, color; ptype_t effect; float radius; for (i = 0; i < 3; i++) org[i] = MSG_ReadCoord (); radius = MSG_ReadByte(); color = MSG_ReadShort (); msgcount = MSG_ReadByte (); effect = (ptype_t) MSG_ReadByte (); R_RunParticleEffect4 (org, radius, color, effect, msgcount); } /* =============== R_ParticleExplosion =============== */ void R_ParticleExplosion (vec3_t org) { int i, j; particle_t *p; for (i = 0; i < 1024; i++) { p = AllocParticle(); if (!p) return; p->die = cl.time + 5; p->color = ramp1[0]; p->ramp = rand() & 3; if (i & 1) { p->type = pt_explode; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() & 31) - 16); p->vel[j] = (rand() & 511) - 256; } } else { p->type = pt_explode2; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() & 31) - 16); p->vel[j] = (rand() & 511) - 256; } } } } /* =============== R_ParticleExplosion2 color mapped explosion =============== */ void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength) { int i, j; particle_t *p; int colorMod = 0; for (i = 0; i < 512; i++) { p = AllocParticle(); if (!p) return; p->die = cl.time + 0.3; p->color = colorStart + (colorMod % colorLength); colorMod++; p->type = pt_blob; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() & 31) - 16); p->vel[j] = (rand() & 511) - 256; } } } /* =============== R_BlobExplosion tarbaby explosion =============== */ void R_BlobExplosion (vec3_t org) { int i, j; particle_t *p; for (i = 0; i < 1024; i++) { p = AllocParticle(); if (!p) return; p->die = cl.time + 1 + (rand() & 8) * 0.05; if (i & 1) { p->type = pt_blob; p->color = 66 + (rand() % 6); for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() & 31) - 16); p->vel[j] = (rand() & 511) - 256; } } else { p->type = pt_blob2; p->color = 150 + (rand() % 6); for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() & 31) - 16); p->vel[j] = (rand() & 511) - 256; } } } } /* =============== R_RunParticleEffect =============== */ void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count) { int i, j; particle_t *p; for (i = 0; i < count; i++) { p = AllocParticle(); if (!p) return; if (count == 1024) { // rocket explosion p->die = cl.time + 5; p->color = ramp1[0]; p->ramp = rand() & 3; if (i & 1) { p->type = pt_explode; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() & 31) - 16); p->vel[j] = (rand() & 511) - 256; } } else { p->type = pt_explode2; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() & 31) - 16); p->vel[j] = (rand() & 511) - 256; } } } else { p->die = cl.time + 0.1 * (rand() % 5); // p->color = (color & ~7) + (rand() & 7); // p->color = 265 + (rand() % 9); p->color = 256 + 16 + 12 + (rand() & 3); p->type = pt_slowgrav; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() & 15) - 8); p->vel[j] = dir[j] * 15;// + (rand() % 300) - 150; } } } } /* =============== R_RunParticleEffect2 =============== */ void R_RunParticleEffect2 (vec3_t org, vec3_t dmin, vec3_t dmax, int color, ptype_t effect, int count) { int i, j; particle_t *p; float num; for (i = 0; i < count; i++) { p = AllocParticle(); if (!p) return; // p->die = cl.time + 0.1 * (rand() % 5); p->die = cl.time + 2; p->color = color; p->type = effect; p->ramp = 0; for (j = 0; j < 3; j++) { num = rand() * (1.0 / RAND_MAX); p->org[j] = org[j] + ((rand() & 8) - 4); //added randomness to org p->vel[j] = dmin[j] + ((dmax[j] - dmin[j]) * num); } } } /* =============== R_RunParticleEffect3 =============== */ void R_RunParticleEffect3 (vec3_t org, vec3_t box, int color, ptype_t effect, int count) { int i, j; particle_t *p; float num; for (i = 0; i < count; i++) { p = AllocParticle(); if (!p) return; // p->die = cl.time + 0.1 * (rand() % 5); p->die = cl.time + 2; p->color = color; p->type = effect; p->ramp = 0; for (j = 0; j < 3; j++) { num = rand() * (1.0 / RAND_MAX); p->org[j] = org[j] + ((rand() & 15) - 8); p->vel[j] = (box[j] * num * 2) - box[j]; } } } /* =============== R_RunParticleEffect4 =============== */ void R_RunParticleEffect4 (vec3_t org, float radius, int color, ptype_t effect, int count) { int i, j; particle_t *p; float num; for (i = 0; i < count; i++) { p = AllocParticle(); if (!p) return; // p->die = cl.time + 0.1 * (rand() % 5); p->die = cl.time + 2; p->color = color; p->type = effect; p->ramp = 0; for (j = 0; j < 3; j++) { num = rand() * (1.0 / RAND_MAX); p->org[j] = org[j] + ((rand() & 15) - 8); p->vel[j] = (radius * num * 2) - radius; } } } /* =============== R_LavaSplash =============== */ void R_LavaSplash (vec3_t org) { int i, j, k; particle_t *p; float vel; vec3_t dir; for (i = -16; i < 16; i++) { for (j = -16; j < 16; j++) { for (k = 0; k < 1; k++) { p = AllocParticle(); if (!p) return; p->die = cl.time + 2 + (rand() & 31) * 0.02; p->color = 224 + (rand() & 7); p->type = pt_slowgrav; dir[0] = j*8 + (rand() & 7); dir[1] = i*8 + (rand() & 7); dir[2] = 256; p->org[0] = org[0] + dir[0]; p->org[1] = org[1] + dir[1]; p->org[2] = org[2] + (rand() & 63); VectorNormalizeFast (dir); vel = 50 + (rand() & 63); VectorScale (dir, vel, p->vel); } } } } /* =============== R_TeleportSplash =============== */ void R_TeleportSplash (vec3_t org) { int i, j, k; particle_t *p; float vel; vec3_t dir; for (i = -16; i < 16; i += 4) { for (j = -16; j < 16; j += 4) { for (k = -24; k < 32; k += 4) { p = AllocParticle(); if (!p) return; p->die = cl.time + 0.2 + (rand() & 7) * 0.02; p->color = 7 + (rand() & 7); p->type = pt_slowgrav; dir[0] = j * 8; dir[1] = i * 8; dir[2] = k * 8; p->org[0] = org[0] + i + (rand() & 3); p->org[1] = org[1] + j + (rand() & 3); p->org[2] = org[2] + k + (rand() & 3); VectorNormalizeFast (dir); vel = 50 + (rand() & 63); VectorScale (dir, vel, p->vel); } } } } /* =============== R_RunQuakeEffect =============== */ void R_RunQuakeEffect (vec3_t org, float distance) { int i; particle_t *p; float num, num2; for (i = 0; i < 100; i++) { p = AllocParticle(); if (!p) return; p->die = cl.time + 0.3 * (rand() % 5); p->color = (rand() & 3) + ((rand() % 3) * 16) + (13 * 16) + 256 + 11; p->type = pt_quake; p->ramp = 0; num = rand() * (1.0 / RAND_MAX); num2 = distance * num; num = rand() * (1.0 / RAND_MAX); q_sincosrad(num * 2 * M_PI, &p->org[0], &p->org[1]); p->org[0] *= num2; p->org[1] *= num2; p->org[0] += org[0]; p->org[1] += org[1]; num = rand() * (1.0 / RAND_MAX); p->org[2] = org[2] + 15*num; p->org[2] = org[2]; num = rand() * (1.0 / RAND_MAX); p->vel[0] = (num * 40) - 20; num = rand() * (1.0 / RAND_MAX); p->vel[1] = (num * 40) - 20; num = rand() * (1.0 / RAND_MAX); p->vel[2] = 65*num + 80; } } /* =============== R_SunStaffTrail =============== */ void R_SunStaffTrail(vec3_t source, vec3_t dest) { int i; particle_t *p; vec3_t vec, dist; float length, size; VectorSubtract(dest, source, vec); length = VectorNormalizeFast(vec); dist[0] = vec[0]; dist[1] = vec[1]; dist[2] = vec[2]; size = 10; while (length > 0) { length -= size; if ((p = AllocParticle()) == NULL) return; p->die = cl.time + 2; p->ramp = rand() & 3; p->color = ramp6[(int)(p->ramp)]; p->type = pt_spit; for (i = 0; i < 3; i++) { p->org[i] = source[i] + ((rand() & 3) - 2); } p->vel[0] = (rand() % 10) - 5; p->vel[1] = (rand() % 10) - 5; p->vel[2] = (rand() % 10); VectorAdd(source, dist, source); } } void RiderParticle (int count, vec3_t origin) { int i; particle_t *p; float radius, angle; VectorCopy(origin, rider_origin); for (i = 0; i < count; i++) { p = AllocParticle(); if (!p) return; p->die = cl.time + 4; p->color = 256 + 16 + 15; p->type = pt_rd; p->ramp = 0; angle = (rand() % 360) / (2 * M_PI); radius = 300 + (rand() & 255); q_sincosrad(angle, &p->org[0], &p->org[1]); p->org[0] *= radius; p->org[1] *= radius; p->org[0] += origin[0]; p->org[1] += origin[1]; p->org[2] = origin[2]; p->org[2] += (rand() & 255) - 30; p->vel[0] = (rand() & 255) - 127; p->vel[1] = (rand() & 255) - 127; p->vel[2] = (rand() & 255) - 127; } } void GravityWellParticle (int count, vec3_t origin, int color) { int i; particle_t *p; float radius,angle; VectorCopy(origin, rider_origin); for (i = 0; i < count; i++) { p = AllocParticle(); if (!p) return; p->die = cl.time + 4; p->color = color + (rand() & 15); p->type = pt_gravwell; p->ramp = 0; angle = (rand() % 360) / (2 * M_PI); radius = 300 + (rand() & 255); q_sincosrad(angle, &p->org[0], &p->org[1]); p->org[0] *= radius; p->org[1] *= radius; p->org[0] += origin[0]; p->org[1] += origin[1]; p->org[2] = origin[2]; p->org[2] += (rand() & 255) - 30; p->vel[0] = (rand() & 255) - 127; p->vel[1] = (rand() & 255) - 127; p->vel[2] = (rand() & 255) - 127; } } /* =============== R_RocketTrail =============== */ void R_RocketTrail (vec3_t start, vec3_t end, int type) { vec3_t vec, dist; float len, size, lifetime; int j; particle_t *p; static int tracercount; VectorSubtract (end, start, vec); len = VectorNormalizeFast (vec); dist[0] = vec[0]; dist[1] = vec[1]; dist[2] = vec[2]; size = 1; lifetime = 2; switch (type) { case 9: // Spit break; case 8: // Ice size *= 5 * 3; dist[0] *= 5 * 3; dist[1] *= 5 * 3; dist[2] *= 5 * 3; break; case rt_acidball: // Ice size = 5; lifetime = .8; break; default: size = 3; dist[0] *= 3; dist[1] *= 3; dist[2] *= 3; break; } while (len > 0) { len -= size; p = AllocParticle(); if (!p) return; VectorClear (p->vel); p->die = cl.time + lifetime; switch (type) { case rt_rocket_trail: // rocket trail p->ramp = rand() & 3; p->color = ramp3[(int)p->ramp]; p->type = pt_fire; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() % 6) - 3); break; case rt_smoke: // smoke smoke p->ramp = (rand() & 3) + 2; p->color = ramp3[(int)p->ramp]; p->type = pt_fire; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() % 6) - 3); break; case rt_blood: // blood p->type = pt_slowgrav; p->color = 134 + (rand() & 7); for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() % 6) - 3); break; case rt_tracer:; case rt_tracer2:; // tracer p->die = cl.time + 0.5; p->type = pt_static; if (type == 3) p->color = 130 + (rand() & 6); // 243 + (rand() & 3); else p->color = 230 + ((tracercount & 4) << 1); tracercount++; VectorCopy (start, p->org); if (tracercount & 1) { p->vel[0] = 30 * vec[1]; p->vel[1] = 30 * -vec[0]; } else { p->vel[0] = 30 * -vec[1]; p->vel[1] = 30 * vec[0]; } break; case rt_slight_blood: // slight blood p->type = pt_slowgrav; p->color = 134 + (rand() & 7); for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() % 6) - 3); len -= size; break; case rt_bloodshot: // bloodshot trail p->type = pt_darken; p->color = 136 + (rand() & 5); for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 3) - 2); len -= size; break; case rt_voor_trail: // voor trail p->color = 9*16 + 8 + (rand() & 3); p->type = pt_static; p->die = cl.time + 0.3; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 15) - 8); break; case rt_fireball: // Fireball p->ramp = rand() & 3; p->color = ramp4[(int)(p->ramp)]; p->type = pt_fireball; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 3) - 2); p->org[2] += 2; // compensate for model p->vel[0] = (rand() % 200) - 100; p->vel[1] = (rand() % 200) - 100; p->vel[2] = (rand() % 200) - 100; break; case rt_acidball: // Acid ball p->ramp = rand() & 3; p->color = ramp10[(int)(p->ramp)]; p->type = pt_acidball; p->die = cl.time + 0.5; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 3) - 2); p->org[2] += 2; // compensate for model p->vel[0] = (rand() % 40) - 20; p->vel[1] = (rand() % 40) - 20; p->vel[2] = (rand() % 40) - 20; break; case rt_ice: // Ice p->ramp = rand() & 3; p->color = ramp5[(int)(p->ramp)]; p->type = pt_ice; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 3) - 2); p->org[2] += 2; // compensate for model p->vel[0] = (rand() % 16) - 8; p->vel[1] = (rand() % 16) - 8; p->vel[2] = (rand() % 20) - 40; break; case rt_spit: // Spit p->ramp = rand() & 3; p->color = ramp6[(int)(p->ramp)]; p->type = pt_spit; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 3) - 2); p->org[2] += 2; // compensate for model p->vel[0] = (rand() % 10) - 5; p->vel[1] = (rand() % 10) - 5; p->vel[2] = (rand() % 10); break; case rt_spell: // Spell p->ramp = rand() & 3; p->color = ramp6[(int)(p->ramp)]; p->type = pt_spell; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 3) - 2); p->vel[0] = (rand() % 10) - 5; p->vel[1] = (rand() % 10) - 5; p->vel[2] = (rand() % 10); p->vel[0] = vec[0] * -10; p->vel[1] = vec[1] * -10; p->vel[2] = vec[2] * -10; break; case rt_vorpal: // vorpal missile p->type = pt_vorpal; p->color = 44 + (rand() & 3) + 256; for (j = 0; j < 2; j++) p->org[j] = start[j] + ((rand() % 48) - 24); p->org[2] = start[2] + ((rand() & 15) - 8); break; case rt_setstaff: // set staff p->type = pt_setstaff; p->color = ramp9[0]; p->ramp = rand() & 3; for (j = 0; j < 2; j++) p->org[j] = start[j] + ((rand() % 6) - 3); p->org[2] = start[2] + ((rand() % 10) - 5); p->vel[0] = (rand() & 7) - 4; p->vel[1] = (rand() & 7) - 4; break; case rt_magicmissile: // magic missile p->type = pt_magicmissile; p->color = 148 + (rand() & 11); p->ramp = rand() & 3; for (j = 0; j < 2; j++) p->org[j] = start[j] + ((rand() % 48) - 24); p->org[2] = start[2] + ((rand() % 48) - 24); p->vel[2] = -((rand() & 15) + 8); break; case rt_boneshard: // bone shard p->type = pt_boneshard; p->color = 368 + (rand() & 16); for (j = 0; j < 2; j++) p->org[j] = start[j] + ((rand() % 48) - 24); p->org[2] = start[2] + ((rand() % 48) - 24); p->vel[2] = -((rand() & 15) + 8); break; case rt_scarab: // scarab staff p->type = pt_scarab; p->color = 250 + (rand() & 3); for (j = 0; j < 3; j++) p->org[j] = start[j] + (rand() & 7); p->vel[2] = -(rand() & 7); break; } VectorAdd (start, dist, start); } } /* =============== R_RainEffect =============== */ void R_RainEffect (vec3_t org, vec3_t e_size, int x_dir, int y_dir, int color, int count) { int i, holdint; particle_t *p; float z_time; for (i = 0; i < count; i++) { p = AllocParticle(); if (!p) return; p->vel[0] = x_dir; // X and Y motion p->vel[1] = y_dir; p->vel[2] = -(rand() % 956); if (p->vel[2] > -256) { p->vel[2] += -256; } z_time = -(e_size[2]/p->vel[2]); p->die = cl.time + z_time; p->color = color; p->ramp = rand() & 3; //p->veer = veer; p->type = pt_rain; holdint = e_size[0]; p->org[0] = org[0] + (rand() % holdint); holdint = e_size[1]; p->org[1] = org[1] + (rand() % holdint); p->org[2] = org[2]; } } /* =============== R_SnowEffect MG =============== */ void R_SnowEffect (vec3_t org1, vec3_t org2, int flags, vec3_t alldir, int count) { int i, j, holdint; particle_t *p; mleaf_t *l; count *= snow_active.integer; for (i = 0; i < count; i++) { p = AllocParticle(); if (!p) return; p->vel[0] = alldir[0]; // X and Y motion p->vel[1] = alldir[1]; p->vel[2] = alldir[2] * ((rand() & 15) + 7)/10; p->flags = flags; #ifdef GLQUAKE // have a console variable 'happy_snow' that makes all snowflakes happy snow! if ((rand() & 0x7f) <= 1) p->count = 69; // happy snow! else if (flags & SFL_FLUFFY || (flags & SFL_MIXED && (rand() & 3))) p->count = (rand() & 31) + 10; // From 10 to 41 scale, will be divided else p->count = 10; #else if (flags & SFL_FLUFFY || (flags & SFL_MIXED && (rand() & 3))) p->count = (rand() & 3) + 2; // From 2 to 5 extra else p->count = 1; // Only one particle #endif if (flags & SFL_HALF_BRIGHT) // Start darker p->color = 26 + (rand() % 5); else p->color = 18 + (rand() % 12); if (!(flags & SFL_NO_TRANS)) // Start translucent p->color += 256; p->die = cl.time + 7; p->ramp = rand() & 3; //p->veer = veer; p->type = pt_snow; holdint = org2[0] - org1[0]; p->org[0] = org1[0] + (rand() % holdint); holdint = org2[1] - org1[1]; p->org[1] = org1[1] + (rand() % holdint); p->org[2] = org2[2]; j = 50; l = Mod_PointInLeaf (p->org, cl.worldmodel); // while (SV_PointContents(p->org) != CONTENTS_EMPTY && j < 50) while (l->contents != CONTENTS_EMPTY && j) { // Make sure it doesn't start in a solid holdint = org2[0] - org1[0]; p->org[0] = org1[0] + (rand() % holdint); holdint = org2[1] - org1[1]; p->org[1] = org1[1] + (rand() % holdint); j--; // No infinite loops l = Mod_PointInLeaf (p->org, cl.worldmodel); } if (l->contents != CONTENTS_EMPTY) Sys_Error ("Snow entity top plane is not in an empty area (sorry!)"); VectorCopy(org1, p->min_org); VectorCopy(org2, p->max_org); } } /* =============== R_ColoredParticleExplosion =============== */ void R_ColoredParticleExplosion (vec3_t org, int color, int radius, int counter) { int i, j; particle_t *p; for (i = 0; i < counter; i++) { p = AllocParticle(); if (!p) return; p->die = cl.time + 3; p->color = color; p->ramp = rand() & 3; if (i & 1) { p->type = pt_c_explode; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() % (radius * 2)) - radius); p->vel[j] = (rand() & 511) - 256; } } else { p->type = pt_c_explode2; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() % (radius * 2)) - radius); p->vel[j] = (rand() & 511) - 256; } } } } /* =============== R_DrawParticles =============== */ extern cvar_t sv_gravity; #if defined(GLQUAKE) static const float ptex_coord[4][3][2] = { { {1.000, 0.000}, {1.000, 0.500}, {0.500, 0.000} }, // any, or snow count < 30 { {0.000, 1.000}, {0.500, 1.000}, {0.000, 0.500} }, // snow count >= 30 { {0.000, 0.000}, {0.815, 0.000}, {0.000, 0.815} }, // snow count >= 40 { {1.000, 1.000}, {1.000, 0.180}, {0.180, 1.000} } // snow count >= 69 : happy snow! }; void R_DrawParticles (void) { int i, color; particle_t *p; float scale; #define SCALE_BASE ((p->type == pt_snow) ? p->count/10 : 1) GL_Bind(particletexture); glEnable_fp (GL_BLEND); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glBegin_fp (GL_TRIANGLES); VectorScale (vup, 1.5, r_pup); VectorScale (vright, 1.5, r_pright); for (p = active_particles ; p ; p = p->next) { // hack a scale up to keep particles from disapearing scale = (p->org[0] - r_origin[0])*vpn[0] + (p->org[1] - r_origin[1])*vpn[1] + (p->org[2] - r_origin[2])*vpn[2]; if (scale < 20) scale = SCALE_BASE; else scale = SCALE_BASE + scale * 0.004; /* clamp color to 0-511: particle->type 10 and 11 (pt_c_explode * and pt_c_explode2, e.g. Crusader's ice particles hitting a * wall) lead to negative values, because R_UpdateParticles () * decrements their color against time. */ color = ((int)p->color) & 0x01ff; if (color < 256) glColor3ubv_fp ((byte *)&d_8to24table[color]); else glColor4ubv_fp ((byte *)&d_8to24TranslucentTable[color-256]); // setup texture coordinates i = 0; if (p->type == pt_snow) { if (p->count >= 69) i = 3; // happy snow! else if (p->count >= 40) i = 2; else if (p->count >= 30) i = 1; } glTexCoord2fv_fp (ptex_coord[i][0]); glVertex3fv_fp (p->org); glTexCoord2fv_fp (ptex_coord[i][1]); glVertex3f_fp (p->org[0] + r_pup[0]*scale, p->org[1] + r_pup[1]*scale, p->org[2] + r_pup[2]*scale); glTexCoord2fv_fp (ptex_coord[i][2]); glVertex3f_fp (p->org[0] + r_pright[0]*scale, p->org[1] + r_pright[1]*scale, p->org[2] + r_pright[2]*scale); } glEnd_fp (); glDisable_fp (GL_BLEND); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } #else /* !GLQUAKE */ void R_DrawParticles (void) { particle_t *p; int i; float vel0, vel1, vel2; vec3_t save_org; D_StartParticles (); VectorScale (vright, xscaleshrink, r_pright); VectorScale (vup, yscaleshrink, r_pup); VectorCopy (vpn, r_ppn); for (p = active_particles ; p ; p = p->next) { switch (p->type) { case pt_snow: VectorCopy(p->org, save_org); D_DrawParticle (p); for (i = 1; i < p->count; i++) { switch (i) { // FIXME: More translucency // on outside particles? // case 0: // // original // break; case 1: // One to right p->org[0] = save_org[0] + vright[0]; p->org[1] = save_org[1] + vright[1]; p->org[2] = save_org[2] + vright[2]; break; case 2: // One above p->org[0] = save_org[0] + vup[0]; p->org[1] = save_org[1] + vup[1]; p->org[2] = save_org[2] + vup[2]; break; case 3: // One to left p->org[0] = save_org[0] - vright[0]; p->org[1] = save_org[1] - vright[1]; p->org[2] = save_org[2] - vright[2]; break; case 4: // One below p->org[0] = save_org[0] - vup[0]; p->org[1] = save_org[1] - vup[1]; p->org[2] = save_org[2] - vup[2]; break; default: Con_Printf ("count too big!\n"); break; } D_DrawParticle (p); } VectorCopy(save_org, p->org); // Restore origin break; case pt_rain: VectorCopy(p->org, save_org); vel0 = p->vel[0]*.001; vel1 = p->vel[1]*.001; vel2 = p->vel[2]*.001; for (i = 0; i < 4; i++) { D_DrawParticle(p); p->org[0] += vel0; p->org[1] += vel1; p->org[2] += vel2; } D_DrawParticle(p); VectorCopy(save_org, p->org); // Restore origin break; default: D_DrawParticle (p); break; } } D_EndParticles (); } #endif /* R_DrawParticles */ void R_UpdateParticles (void) { particle_t *p, *kill; float grav, grav2, percent; int i; float time2, time3, time4; float time1; float dvel; float frametime; float vel0, vel1, vel2; float colindex; vec3_t diff; // vec3_t save_org; // float speed; // qboolean in_solid; if (cls.state == ca_disconnected) return; frametime = cl.time - cl.oldtime; // Con_Printf("%10.5f\n", frametime); time4 = frametime * 20; time3 = frametime * 15; time2 = frametime * 10; time1 = frametime * 5; grav = frametime * sv_gravity.value * 0.05; grav2 = frametime * sv_gravity.value * 0.025; dvel = 4 * frametime; percent = (frametime / HX_FRAME_TIME); for ( ;; ) { kill = active_particles; if (kill && kill->die < cl.time) { active_particles = kill->next; kill->next = free_particles; free_particles = kill; continue; } break; } for (p = active_particles ; p ; p = p->next) { for ( ;; ) { kill = p->next; if (kill && kill->die < cl.time) { p->next = kill->next; kill->next = free_particles; free_particles = kill; continue; } break; } if (p->type == pt_rain) { vel0 = p->vel[0]*.001; vel1 = p->vel[1]*.001; vel2 = p->vel[2]*.001; for (i = 0; i < 4; i++) { p->org[0] += vel0; p->org[1] += vel1; p->org[2] += vel2; } p->org[0] += p->vel[0] * (frametime - .004); p->org[1] += p->vel[1] * (frametime - .004); p->org[2] += p->vel[2] * (frametime - .004); } else if (p->type == pt_snow) { if (p->vel[0] == 0 && p->vel[1] == 0 && p->vel[2] == 0) { // Stopped moving if (p->color == 256 + 31) // Most translucent white { // Go away p->die = -1; } else { // Count fifty and fade in translucency // once each time p->ramp += 1; if (p->ramp >= 7) { p->color += 1; //Get more translucent p->ramp = 0; } } } else { // FIXME: If flake going fast enough, can go through, // do a check in increments ot 10, max? // if not in_bounds Get length of diff, add in // increments of 4 & check solid mleaf_t *l; if (snow_flurry.integer == 1) { if (rand() & 31) { // Add flurry movement float snow_speed; vec3_t save_vel; snow_speed = p->vel[0] * p->vel[0] + p->vel[1] * p->vel[1] + p->vel[2] * p->vel[2]; snow_speed = Q_sqrt(snow_speed); VectorCopy(p->vel, save_vel); save_vel[0] += ( (rand() * (2.0 / RAND_MAX)) - 1 ) * 30; save_vel[1] += ( (rand() * (2.0 / RAND_MAX)) - 1 ) * 30; if ((rand() & 7) || p->vel[2] > 10) save_vel[2] += ( (rand() * (2.0 / RAND_MAX)) - 1 ) * 30; VectorNormalizeFast(save_vel); VectorScale(save_vel, snow_speed, p->vel); // retain speed but use new dir } } /* VectorScale(p->vel, frametime, diff); speed = VectorNormalize(diff); in_solid = false; if (!(p->flags & SFL_IN_BOUNDS)) { // Not cut off by bounds if (speed >= 8) { // Moving more than 8 pixels this turn for (i = 4; i < speed; i += 4) { // Check for solid in increments of 4 VectorScale(diff, i, save_org); VectorAdd(p->org, save_org, save_org); // if (SV_PointContents(save_org) != CONTENTS_EMPTY) l = Mod_PointInLeaf (save_org, cl.worldmodel); if (l->contents != CONTENTS_EMPTY) { in_solid = true; VectorCopy(save_org, p->org); break; } } } } if (!in_solid) */ { VectorScale(p->vel, frametime, diff); VectorAdd(p->org, diff, p->org); } if (p->flags & SFL_IN_BOUNDS) { // Always stay inside the boundry! if ( p->org[0] < p->min_org[0] || p->org[0] > p->max_org[0] || p->org[1] < p->min_org[1] || p->org[1] > p->max_org[1] || p->org[2] < p->min_org[2] || p->org[2] > p->max_org[2] ) { p->die = -1; } } else { // if hit solid, go to last position, // no velocity, fade out. l = Mod_PointInLeaf (p->org, cl.worldmodel); if (l->contents != CONTENTS_EMPTY) // || in_solid == true { if (p->flags & SFL_NO_MELT) { // Don't melt, just die p->die = -1; } else { // still have small prob of snow melting on emitter VectorScale(diff, 0.2, p->vel); i = 6; while (l->contents != CONTENTS_EMPTY) { p->org[0] -= p->vel[0]; p->org[1] -= p->vel[1]; p->org[2] -= p->vel[2]; i--; //no infinite loops if (!i) { p->die = -1; //should never happen now! break; } l = Mod_PointInLeaf (p->org, cl.worldmodel); } p->vel[0] = p->vel[1] = p->vel[2] = 0; p->ramp = 0; } } } } } else { p->org[0] += p->vel[0] * frametime; p->org[1] += p->vel[1] * frametime; p->org[2] += p->vel[2] * frametime; } switch (p->type) { case pt_static: break; case pt_fire: p->ramp += time1; if ((int)p->ramp >= 6) p->die = -1; else p->color = ramp3[(int)p->ramp]; p->vel[2] += grav; break; case pt_explode: p->ramp += time2; if ((int)p->ramp >= 8) p->die = -1; else p->color = ramp1[(int)p->ramp]; for (i = 0; i < 3; i++) p->vel[i] += p->vel[i]*dvel; p->vel[2] -= grav; break; case pt_explode2: p->ramp += time3; if ((int)p->ramp >= 8) p->die = -1; else p->color = ramp2[(int)p->ramp]; for (i = 0; i < 3; i++) p->vel[i] -= p->vel[i] * frametime; p->vel[2] -= grav; break; case pt_c_explode: p->ramp += time2; if ((int)p->ramp >= 8) p->die = -1; else if (time2) p->color--; for (i = 0; i < 3; i++) p->vel[i] += p->vel[i]*dvel; p->vel[2] -= grav; break; case pt_c_explode2: p->ramp += time3; if ((int)p->ramp >= 8) p->die = -1; else if (time3) p->color -= 2; for (i = 0; i < 3; i++) p->vel[i] -= p->vel[i] * frametime; p->vel[2] -= grav; break; case pt_grav: #ifdef QUAKE2 p->vel[2] -= grav * 20; break; #endif case pt_slowgrav: p->vel[2] -= grav; break; case pt_fastgrav: p->vel[2] -= grav * 4; break; case pt_rain: break; case pt_snow: break; case pt_fireball: p->ramp += time3; if ((int)p->ramp >= 16) p->die = -1; else p->color = ramp4[(int)p->ramp]; break; case pt_acidball: p->ramp += time4 * 1.4; if ((int)p->ramp >= 23) p->die = -1; else if ((int)p->ramp >= 15) p->color = ramp11[(int)p->ramp - 15]; else p->color = ramp10[(int)p->ramp]; p->vel[2] -= grav; break; case pt_spit: p->ramp += time3; if ((int)p->ramp >= 16) p->die = -1; else p->color = ramp6[(int)p->ramp]; // p->vel[2] += grav * 2; break; case pt_ice: p->ramp += time4; if ((int)p->ramp >= 16) p->die = -1; else p->color = ramp5[(int)p->ramp]; p->vel[2] -= grav; break; case pt_spell: p->ramp += time2; if ((int)p->ramp >= 16) p->die = -1; else p->color = ramp7[(int)p->ramp]; // p->vel[2] += grav * 2; break; case pt_test: p->vel[2] += 1.3; p->ramp += time3; if ((int)p->ramp >= 13 || ((int)p->ramp > 10 && (int)p->vel[2] < 20) ) p->die = -1; else p->color = ramp8[(int)p->ramp]; break; case pt_quake: p->vel[0] *= 1.05; p->vel[1] *= 1.05; p->vel[2] -= grav * 4; break; case pt_rd: if (!frametime) break; p->ramp += percent; if ((int)p->ramp > 50) { p->ramp = 50; p->die = -1; } p->color = 256 + 16 + 16 - (p->ramp / (50/16)); VectorSubtract(rider_origin, p->org, diff); /* p->org[0] += diff[0] * p->ramp / 80; p->org[1] += diff[1] * p->ramp / 80; p->org[2] += diff[2] * p->ramp / 80; */ vel0 = 1 / (51 - p->ramp); p->org[0] += diff[0] * vel0; p->org[1] += diff[1] * vel0; p->org[2] += diff[2] * vel0; break; case pt_gravwell: if (!frametime) break; p->ramp += percent; if ((int)p->ramp > 35) { p->ramp = 35; p->die = -1; } VectorSubtract(rider_origin, p->org, diff); /* p->org[0] += diff[0] * p->ramp / 80; p->org[1] += diff[1] * p->ramp / 80; p->org[2] += diff[2] * p->ramp / 80; */ vel0 = 1 / (36 - p->ramp); p->org[0] += diff[0] * vel0; p->org[1] += diff[1] * vel0; p->org[2] += diff[2] * vel0; break; case pt_vorpal: --p->color; if ((int)p->color <= 37 + 256) p->die = -1; break; case pt_setstaff: p->ramp += time1; if ((int)p->ramp >= 16) p->die = -1; else p->color = ramp9[(int)p->ramp]; p->vel[0] *= 1.08 * percent; p->vel[1] *= 1.08 * percent; p->vel[2] -= grav2; break; case pt_redfire: p->ramp += frametime * 3; if ((int)p->ramp >= 8) p->die = -1; else p->color = ramp12[(int)p->ramp] + 256; p->vel[0] *= .9; p->vel[1] *= .9; p->vel[2] += grav/2; break; case pt_magicmissile: --p->color; if ((int)p->color < 149) p->color = 149; p->ramp += time1; if ((int)p->ramp > 16) p->die = -1; break; case pt_boneshard: --p->color; if ((int)p->color < 368) p->die = -1; break; case pt_scarab: --p->color; if ((int)p->color < 250) p->die = -1; break; case pt_darken: p->vel[2] -= grav; //Also gravity --p->color; colindex = 0; while (colindex < 224) { if (colindex == 192 || colindex == 200) colindex += 8; else colindex += 16; if (p->color == colindex) p->die = -1; } break; } } } engine/hexen2/r_part.h000066400000000000000000000045241444734033100151640ustar00rootroot00000000000000/* r_part.h -- exported functions from r_part.c * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __R_PART_H #define __R_PART_H void R_DrawParticles (void); void R_InitParticles (void); void R_ClearParticles (void); void R_UpdateParticles (void); void R_ParseParticleEffect (void); void R_ParseParticleEffect2 (void); void R_ParseParticleEffect3 (void); void R_ParseParticleEffect4 (void); void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count); void R_RunParticleEffect2 (vec3_t org, vec3_t dmin, vec3_t dmax, int color, ptype_t effect, int count); /* for ptype_t, d_iface.h or glquake.h must be included before. */ void R_ParticleExplosion (vec3_t org); void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength); void R_ColoredParticleExplosion (vec3_t org, int color, int radius, int counter); void R_BlobExplosion (vec3_t org); void R_RocketTrail (vec3_t start, vec3_t end, int type); void R_SunStaffTrail (vec3_t source, vec3_t dest); void R_LavaSplash (vec3_t org); void R_TeleportSplash (vec3_t org); void R_RainEffect (vec3_t org, vec3_t e_size, int x_dir, int y_dir, int color, int count); void R_SnowEffect (vec3_t org1, vec3_t org2, int flags, vec3_t alldir, int count); void R_RunQuakeEffect (vec3_t org, float distance); void RiderParticle (int count, vec3_t origin); void GravityWellParticle (int count, vec3_t origin, int color); void R_DarkFieldParticles (entity_t *ent); void R_EntityParticles (entity_t *ent); /* * NOTES: R_EntityParticles, R_ParticleExplosion2 * and R_BlobExplosion actually are not used. */ #endif /* __R_PART_H */ engine/hexen2/render.h000066400000000000000000000106641444734033100151560ustar00rootroot00000000000000/* * refresh.h -- public interface to refresh functions * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RENDER_H_ #define RENDER_H_ #define MAXCLIPPLANES 11 #define TOP_RANGE 16 // soldier uniform colors #define BOTTOM_RANGE 96 //============================================================================= typedef struct efrag_s { struct mleaf_s *leaf; struct efrag_s *leafnext; struct entity_s *entity; struct efrag_s *entnext; } efrag_t; typedef struct entity_s { qboolean forcelink; // model changed int update_type; entity_state3_t baseline; // to fill in defaults in updates double msgtime; // time of last update vec3_t msg_origins[2]; // last two updates (0 is newest) vec3_t origin; vec3_t msg_angles[2]; // last two updates (0 is newest) vec3_t angles; struct qmodel_s *model; // NULL = no model struct efrag_s *efrag; // linked list of efrags int frame; float syncbase; // for client-side animations byte *colormap, *sourcecolormap; byte colorshade; int effects; // light, particals, etc int skinnum; // for Alias models int scale; // for Alias models int drawflags; // for Alias models int abslight; // for Alias models int visframe; // last frame this entity was // found in an active leaf int dlightframe; // dynamic lighting int dlightbits; // FIXME: could turn these into a union int trivial_accept; struct mnode_s *topnode; // for bmodels, first world node // that splits bmodel, or NULL if // not split } entity_t; // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct { vrect_t vrect; // subwindow in video for refresh // FIXME: not need vrect next field here? vrect_t aliasvrect; // scaled Alias version int vrectright, vrectbottom; // right & bottom screen coords int aliasvrectright, aliasvrectbottom; // scaled Alias versions float vrectrightedge; // rightmost right edge we care about, // for use in edge list float fvrectx, fvrecty; // for floating-point compares float fvrectx_adj, fvrecty_adj; // left and top edges, for clamping int vrect_x_adj_shift20; // (vrect.x + 0.5 - epsilon) << 20 int vrectright_adj_shift20; // (vrectright + 0.5 - epsilon) << 20 float fvrectright_adj, fvrectbottom_adj; // right and bottom edges, for clamping float fvrectright; // rightmost edge, for Alias clamping float fvrectbottom; // bottommost edge, for Alias clamping float horizontalFieldOfView; // at Z = 1.0, this many X is visible // 2.0 = 90 degrees float xOrigin; // should probably always be 0.5 float yOrigin; // between be around 0.3 to 0.5 vec3_t vieworg; vec3_t viewangles; float fov_x, fov_y; int ambientlight; } refdef_t; // // refresh // ASM_LINKAGE_BEGIN extern refdef_t r_refdef; extern vec3_t r_origin, vpn, vright, vup; ASM_LINKAGE_END extern struct texture_s *r_notexture_mip; extern entity_t r_worldentity; void R_Init (void); void R_InitTextures (void); void R_InitEfrags (void); void R_RenderView (void); // must set r_refdef first void R_ViewChanged (float aspect); // called whenever r_refdef or vid change void R_NewMap (void); void R_InitSky (struct texture_s *mt); // called at level load void R_PushDlights (void); void R_AddEfrags (entity_t *ent); void R_RemoveEfrags (entity_t *ent); // // surface cache related // extern qboolean r_cache_thrash; // set if thrashing the surface cache int D_SurfaceCacheForRes (int width, int height); void D_FlushCaches (void); void D_DeleteSurfaceCache (void); void D_InitCaches (void *buffer, int size); void R_SetVrect (vrect_t *pvrect, vrect_t *pvrectin, int lineadj); #endif /* RENDER_H_ */ engine/hexen2/sbar.c000066400000000000000000001105631444734033100146200ustar00rootroot00000000000000/* sbar.c -- Hexen II status bar * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_shared.h" // MACROS ------------------------------------------------------------------ #define STAT_MINUS 10 /* num frame for '-' stats digit */ #define BAR_TOP_HEIGHT 46.0 #define BAR_BOTTOM_HEIGHT 98.0 #define BAR_TOTAL_HEIGHT (BAR_TOP_HEIGHT+BAR_BOTTOM_HEIGHT) #define BAR_BUMP_HEIGHT 23.0 #define INVENTORY_DISPLAY_TIME 4 #define RING_FLIGHT 1 #define RING_WATER 2 #define RING_REGENERATION 4 #define RING_TURNING 8 /* Max number of inventory icons to display at once */ #define INV_MAX_ICON 6 /* artifact counts are relative to the cnt_torch in entvars_t struct: */ #define Inv_GetCount(artifact_num) (int)(&cl.v.cnt_torch)[(artifact_num)] // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- void Sbar_DeathmatchOverlay(void); // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void Sbar_PuzzlePieceOverlay(void); static void Sbar_SmallDeathmatchOverlay(void); static void Sbar_DrawPic(int x, int y, qpic_t *pic); static void Sbar_DrawTransPic(int x, int y, qpic_t *pic); static int Sbar_itoa(int num, char *buf); static void Sbar_DrawNum(int x, int y, int number, int digits); static void Sbar_SortFrags(void); #if 0 /* all uses are commented out */ static void Sbar_DrawCharacter(int x, int y, int num); static void Sbar_DrawSmallCharacter(int x, int y, int num); static void Sbar_DrawString(int x, int y, const char *str); #endif static void Sbar_DrawSmallString(int x, int y, const char *str); static void DrawBarArtifactNumber(int x, int y, int number); static void DrawFullScreenInfo(void); static void DrawLowerBar(void); static void DrawActiveRings(void); static void DrawActiveArtifacts(void); static int CalcAC(void); static void DrawBarArtifactIcon(int x, int y, int artifact); static qboolean SetChainPosition(float health, float maxHealth, qboolean immediate); static void ShowInfoDown_f(void); static void ShowInfoUp_f(void); static void ShowDMDown_f(void); static void ShowDMUp_f(void); static void ToggleDM_f(void); static void InvLeft_f(void); static void InvRight_f(void); static void InvUse_f(void); static void InvOff_f(void); static void DrawArtifactInventory(void); // PUBLIC DATA DEFINITIONS ------------------------------------------------- int sb_lines; // scan lines to draw // PRIVATE DATA DEFINITIONS ------------------------------------------------ static int sb_updates; // if >= vid.numpages, no update needed static float BarHeight; static float BarTargetHeight; static cvar_t BarSpeed = {"barspeed", "5", CVAR_NONE}; static cvar_t DMMode = {"dm_mode", "1", CVAR_ARCHIVE}; static cvar_t sbtrans = {"sbtrans", "0", CVAR_ARCHIVE}; static cvar_t dmtrans = {"dmtrans", "0", CVAR_ARCHIVE}; static qpic_t *sb_nums[11]; static qpic_t *sb_colon, *sb_slash; static int fragsort[MAX_CLIENTS]; static int scoreboardlines; static float ChainPosition = 0; static qboolean sb_ShowInfo; static qboolean sb_ShowDM; static qboolean inv_flg; // true - show inventory interface static float InventoryHideTime; extern const char *ClassNames[MAX_PLAYER_CLASS]; //from menu.c static const int AmuletAC[MAX_PLAYER_CLASS] = { 8, // Paladin 4, // Crusader 2, // Necromancer 6, // Assassin 6 // Demoness }; static const int BracerAC[MAX_PLAYER_CLASS] = { 6, // Paladin 8, // Crusader 4, // Necromancer 2, // Assassin 2 // Demoness }; static const int BreastplateAC[MAX_PLAYER_CLASS] = { 2, // Paladin 6, // Crusader 8, // Necromancer 4, // Assassin 4 // Demoness }; static const int HelmetAC[MAX_PLAYER_CLASS] = { 4, // Paladin 2, // Crusader 6, // Necromancer 8, // Assassin 8 // Demoness }; // CODE -------------------------------------------------------------------- //========================================================================== // // Sbar_Init // //========================================================================== void Sbar_Init(void) { int i; for (i = 0; i < 10; i++) { sb_nums[i] = Draw_PicFromWad(va("num_%i",i)); } sb_nums[10] = Draw_PicFromWad("num_minus"); sb_colon = Draw_PicFromWad("num_colon"); sb_slash = Draw_PicFromWad("num_slash"); if (draw_reinit) return; Cmd_AddCommand("+showinfo", ShowInfoDown_f); Cmd_AddCommand("-showinfo", ShowInfoUp_f); Cmd_AddCommand("+showdm", ShowDMDown_f); Cmd_AddCommand("-showdm", ShowDMUp_f); Cmd_AddCommand("invleft", InvLeft_f); Cmd_AddCommand("invright", InvRight_f); Cmd_AddCommand("invuse", InvUse_f); Cmd_AddCommand("invoff", InvOff_f); Cmd_AddCommand("toggle_dm", ToggleDM_f); Cvar_RegisterVariable(&DMMode); Cvar_RegisterVariable(&sbtrans); Cvar_RegisterVariable(&dmtrans); Cvar_RegisterVariable(&BarSpeed); BarHeight = BarTargetHeight = BAR_TOP_HEIGHT; } //========================================================================== // // Sbar_Draw // //========================================================================== void Sbar_Draw(void) { float delta; char tempStr[80]; int mana, maxMana; if (intro_playing) // the mission pack intro is active { scr_fullupdate = 0; scr_copyeverything = 1; return; } if (scr_con_current == vid.height) // console is full screen return; DrawArtifactInventory(); DrawActiveRings(); DrawActiveArtifacts(); trans_level = 0; if (sb_ShowDM) { if (cl.gametype == GAME_DEATHMATCH) Sbar_DeathmatchOverlay(); else Sbar_PuzzlePieceOverlay(); } else if (cl.gametype == GAME_DEATHMATCH && DMMode.integer) { Sbar_SmallDeathmatchOverlay(); } trans_level = sbtrans.integer; if (trans_level < 0 || trans_level > 2) trans_level = 0; if (BarHeight < BarTargetHeight) { delta = ((BarTargetHeight-BarHeight)*BarSpeed.value) * host_frametime; if (delta < 0.5) delta = 0.5; BarHeight += delta; if (BarHeight > BarTargetHeight) BarHeight = BarTargetHeight; if (scr_viewsize.integer < 100) scr_fullupdate = 0; } else if (BarHeight > BarTargetHeight) { delta = ((BarHeight-BarTargetHeight)*BarSpeed.value) * host_frametime; if (delta < 0.5) delta = 0.5; BarHeight -= delta; if (BarHeight < BarTargetHeight) BarHeight = BarTargetHeight; if (scr_viewsize.integer < 100) scr_fullupdate = 0; } Sbar_DrawTransPic(0, -23, Draw_CachePic("gfx/topbumpl.lmp")); Sbar_DrawTransPic(138, -8, Draw_CachePic("gfx/topbumpm.lmp")); Sbar_DrawTransPic(269, -23, Draw_CachePic("gfx/topbumpr.lmp")); if (BarHeight > BAR_TOP_HEIGHT || m_state != m_none || scr_viewsize.integer > 100) sb_updates = 0; #ifndef GLQUAKE if (sb_updates >= vid.numpages) return; #endif // if (BarHeight == BarTargetHeight) // return; scr_copyeverything = 1; sb_updates++; if (BarHeight < 0 && scr_viewsize.integer <= 120) { SetChainPosition(cl.v.health, cl.v.max_health, true); DrawFullScreenInfo(); return; } Sbar_DrawPic(0, 0, Draw_CachePic("gfx/topbar1.lmp")); Sbar_DrawPic(160, 0, Draw_CachePic("gfx/topbar2.lmp")); //Sbar_DrawTransPic(0, -23, Draw_CachePic("gfx/topbumpl.lmp")); //Sbar_DrawTransPic(138, -8, Draw_CachePic("gfx/topbumpm.lmp")); //Sbar_DrawTransPic(269, -23, Draw_CachePic("gfx/topbumpr.lmp")); maxMana = (int)cl.v.max_mana; // Blue mana mana = (int)cl.v.bluemana; if (mana < 0) mana = 0; else if (mana > maxMana) mana = maxMana; sprintf(tempStr, "%03d", mana); Sbar_DrawSmallString(201, 22, tempStr); if (mana) { Sbar_DrawPic(190, 26-(int)((mana*18.0)/(float)maxMana+0.5), Draw_CachePic("gfx/bmana.lmp")); Sbar_DrawPic(190, 27, Draw_CachePic("gfx/bmanacov.lmp")); } // Green mana mana = (int)cl.v.greenmana; if (mana < 0) mana = 0; else if (mana > maxMana) mana = maxMana; sprintf(tempStr, "%03d", mana); Sbar_DrawSmallString(243, 22, tempStr); if (mana) { Sbar_DrawPic(232, 26-(int)((mana*18.0)/(float)maxMana+0.5), Draw_CachePic("gfx/gmana.lmp")); Sbar_DrawPic(232, 27, Draw_CachePic("gfx/gmanacov.lmp")); } // HP if (cl.v.health < -99) Sbar_DrawNum(58, 14, -99, 3); else Sbar_DrawNum(58, 14, cl.v.health, 3); if (SetChainPosition(cl.v.health, cl.v.max_health, false)) sb_updates = 0; Sbar_DrawTransPic(45+((int)ChainPosition&7), 38, Draw_CachePic("gfx/hpchain.lmp")); Sbar_DrawTransPic(45+(int)ChainPosition, 36, Draw_CachePic("gfx/hpgem.lmp")); Sbar_DrawPic(43, 36, Draw_CachePic("gfx/chnlcov.lmp")); Sbar_DrawPic(267, 36, Draw_CachePic("gfx/chnrcov.lmp")); // AC Sbar_DrawNum(105, 14, CalcAC(), 2); if (BarHeight > BAR_TOP_HEIGHT) { DrawLowerBar(); } // Current inventory item if (cl.inv_selected >= 0) { DrawBarArtifactIcon(144, 3, cl.inv_order[cl.inv_selected]); // Sbar_DrawTransPic(144, 3, Draw_CachePic(va("gfx/arti%02d.lmp", cl.inv_order[cl.inv_selected]))); } } //========================================================================== // // SetChainPosition // //========================================================================== static qboolean SetChainPosition(float health, float maxHealth, qboolean immediate) { float delta; float chainTargetPosition; if (health < 0) health = 0; else if (health > maxHealth) health = maxHealth; chainTargetPosition = (health*195)/maxHealth; if (fabs(ChainPosition-chainTargetPosition) < 0.1) return false; if (immediate) ChainPosition = chainTargetPosition; if (ChainPosition < chainTargetPosition) { delta = ((chainTargetPosition-ChainPosition)*5)*host_frametime; if (delta < 0.5) delta = 0.5; ChainPosition += delta; if (ChainPosition > chainTargetPosition) ChainPosition = chainTargetPosition; } else if (ChainPosition > chainTargetPosition) { delta = ((ChainPosition-chainTargetPosition)*5)*host_frametime; if (delta < 0.5) delta = 0.5; ChainPosition -= delta; if (ChainPosition < chainTargetPosition) ChainPosition = chainTargetPosition; } return true; } //========================================================================== // // DrawFullScreenInfo // //========================================================================== static void DrawFullScreenInfo(void) { int y; int mana, maxMana; char tempStr[80]; y = BarHeight-37; Sbar_DrawPic(3, y, Draw_CachePic("gfx/bmmana.lmp")); Sbar_DrawPic(3, y+18, Draw_CachePic("gfx/gmmana.lmp")); maxMana = (int)cl.v.max_mana; // Blue mana mana = (int)cl.v.bluemana; if (mana < 0) mana = 0; else if (mana > maxMana) mana = maxMana; sprintf(tempStr, "%03d", mana); Sbar_DrawSmallString(10, y+6, tempStr); // Green mana mana = (int)cl.v.greenmana; if (mana < 0) mana = 0; else if (mana > maxMana) mana = maxMana; sprintf(tempStr, "%03d", mana); Sbar_DrawSmallString(10, y+18+6, tempStr); // HP Sbar_DrawNum(38, y+18, cl.v.health, 3); // Current inventory item if (cl.inv_selected >= 0) { DrawBarArtifactIcon(288, y+7, cl.inv_order[cl.inv_selected]); } } //========================================================================== // // DrawLowerBar // //========================================================================== static void DrawLowerBar(void) { int i; // int minutes, seconds, tens, units; char tempStr[80]; int playerClass; int piece; int ringhealth; //playerClass = cl.v.playerclass; playerClass = cl_playerclass.integer; if (playerClass < 1 || playerClass > MAX_PLAYER_CLASS) playerClass = 1; // Default to paladin if (!(gameflags & GAME_PORTALS) && playerClass > MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES) playerClass = 1; // Default to paladin // Backdrop Sbar_DrawPic(0, 46, Draw_CachePic("gfx/btmbar1.lmp")); Sbar_DrawPic(160, 46, Draw_CachePic("gfx/btmbar2.lmp")); /* // Game time minutes = cl.time / 60; seconds = cl.time - 60*minutes; tens = seconds / 10; units = seconds - 10*tens; sprintf(tempStr, "Time :%3i:%i%i", minutes, tens, units); Sbar_DrawSmallString(116, 114, tempStr); // Map name Sbar_DrawSmallString(10, 114, cl.levelname); */ // Stats Sbar_DrawSmallString(11, 48, ClassNames[playerClass-1]); Sbar_DrawSmallString(11, 58, "int"); sprintf(tempStr, "%02d", (int)cl.v.intelligence); Sbar_DrawSmallString(33, 58, tempStr); Sbar_DrawSmallString(11, 64, "wis"); sprintf(tempStr, "%02d", (int)cl.v.wisdom); Sbar_DrawSmallString(33, 64, tempStr); Sbar_DrawSmallString(11, 70, "dex"); sprintf(tempStr, "%02d", (int)cl.v.dexterity); Sbar_DrawSmallString(33, 70, tempStr); Sbar_DrawSmallString(58, 58, "str"); sprintf(tempStr, "%02d", (int)cl.v.strength); Sbar_DrawSmallString(80, 58, tempStr); Sbar_DrawSmallString(58, 64, "lvl"); sprintf(tempStr, "%02d", (int)cl.v.level); Sbar_DrawSmallString(80, 64, tempStr); Sbar_DrawSmallString(58, 70, "exp"); sprintf(tempStr, "%06d", (int)cl.v.experience); Sbar_DrawSmallString(80, 70, tempStr); // Abilities Sbar_DrawSmallString(11, 79, "abilities"); i = ABILITIES_STR_INDEX + (playerClass - 1)*2; if (i + 1 < host_string_count) { if (((int)cl.v.flags) & FL_SPECIAL_ABILITY1) { Sbar_DrawSmallString(8, 89, Host_GetString(i)); } if (((int)cl.v.flags) & FL_SPECIAL_ABILITY2) { Sbar_DrawSmallString(8, 96, Host_GetString(i + 1)); } } // Portrait sprintf(tempStr, "gfx/cport%d.lmp", playerClass); Sbar_DrawPic(134, 50, Draw_CachePic(tempStr)); // Armor if (cl.v.armor_helmet > 0) { Sbar_DrawPic(164, 115, Draw_CachePic("gfx/armor1.lmp")); sprintf(tempStr, "+%d", (int)cl.v.armor_helmet); Sbar_DrawSmallString(168, 136, tempStr); } if (cl.v.armor_amulet > 0) { Sbar_DrawPic(205, 115, Draw_CachePic("gfx/armor2.lmp")); sprintf(tempStr, "+%d", (int)cl.v.armor_amulet); Sbar_DrawSmallString(208, 136, tempStr); } if (cl.v.armor_breastplate > 0) { Sbar_DrawPic(246, 115, Draw_CachePic("gfx/armor3.lmp")); sprintf(tempStr, "+%d", (int)cl.v.armor_breastplate); Sbar_DrawSmallString(249, 136, tempStr); } if (cl.v.armor_bracer > 0) { Sbar_DrawPic(285, 115, Draw_CachePic("gfx/armor4.lmp")); sprintf(tempStr, "+%d", (int)cl.v.armor_bracer); Sbar_DrawSmallString(288, 136, tempStr); } // Rings if (cl.v.ring_flight > 0) { Sbar_DrawTransPic(6, 119, Draw_CachePic("gfx/ring_f.lmp")); ringhealth = (int)cl.v.ring_flight; if (ringhealth > 100) ringhealth = 100; Sbar_DrawPic(35 - (int)(26 * (ringhealth/(float)100)), 142, Draw_CachePic("gfx/ringhlth.lmp")); Sbar_DrawPic(35, 142, Draw_CachePic("gfx/rhlthcvr.lmp")); } if (cl.v.ring_water > 0) { Sbar_DrawTransPic(44, 119, Draw_CachePic("gfx/ring_w.lmp")); ringhealth = (int)cl.v.ring_water; if (ringhealth > 100) ringhealth = 100; Sbar_DrawPic(73 - (int)(26 * (ringhealth/(float)100)), 142, Draw_CachePic("gfx/ringhlth.lmp")); Sbar_DrawPic(73, 142, Draw_CachePic("gfx/rhlthcvr.lmp")); } if (cl.v.ring_turning > 0) { Sbar_DrawTransPic(81, 119, Draw_CachePic("gfx/ring_t.lmp")); ringhealth = (int)cl.v.ring_turning; if (ringhealth > 100) ringhealth = 100; Sbar_DrawPic(110 - (int)(26 * (ringhealth/(float)100)), 142, Draw_CachePic("gfx/ringhlth.lmp")); Sbar_DrawPic(110, 142, Draw_CachePic("gfx/rhlthcvr.lmp")); } if (cl.v.ring_regeneration > 0) { Sbar_DrawTransPic(119, 119, Draw_CachePic("gfx/ring_r.lmp")); ringhealth = (int)cl.v.ring_regeneration; if (ringhealth > 100) ringhealth = 100; Sbar_DrawPic(148 -(int)(26 * (ringhealth/(float)100)), 142, Draw_CachePic("gfx/ringhlth.lmp")); Sbar_DrawPic(148, 142, Draw_CachePic("gfx/rhlthcv2.lmp")); } // Puzzle pieces piece = 0; for (i = 0; i < 8; i++) { if (cl.puzzle_pieces[i][0] == 0) continue; Sbar_DrawPic(194+(piece%4)*31, piece < 4 ? 51 : 82, Draw_CachePic(va("gfx/puzzle/%s.lmp", cl.puzzle_pieces[i]))); piece++; } } //========================================================================== // // CalcAC // //========================================================================== static int CalcAC(void) { int a; int playerClass; //playerClass = cl.v.playerclass; playerClass = cl_playerclass.integer -1; if (playerClass < 0 || playerClass >= MAX_PLAYER_CLASS) playerClass = 1; // Default to paladin if (!(gameflags & GAME_PORTALS) && playerClass > MAX_PLAYER_CLASS - PORTALS_EXTRA_CLASSES) playerClass = 1; // Default to paladin a = 0; if (cl.v.armor_amulet > 0) { a += AmuletAC[playerClass]; a += cl.v.armor_amulet/5; } if (cl.v.armor_bracer > 0) { a += BracerAC[playerClass]; a += cl.v.armor_bracer/5; } if (cl.v.armor_breastplate > 0) { a += BreastplateAC[playerClass]; a += cl.v.armor_breastplate/5; } if (cl.v.armor_helmet > 0) { a += HelmetAC[playerClass]; a += cl.v.armor_helmet/5; } return a; } //========================================================================== // // Sbar_Changed // //========================================================================== void Sbar_Changed(void) { sb_updates = 0; // Update next frame } //========================================================================== // // Sbar_itoa // //========================================================================== static int Sbar_itoa(int num, char *buf) { char *str; int pow10; int dig; str = buf; if (num < 0) { *str++ = '-'; num = -num; } for (pow10 = 10; num >= pow10; pow10 *= 10) ; do { pow10 /= 10; dig = num / pow10; *str++ = '0' + dig; num -= dig * pow10; } while (pow10 != 1); *str = 0; return str-buf; } //========================================================================== // // Sbar_DrawNum // //========================================================================== static void Sbar_DrawNum(int x, int y, int number, int digits) { char str[12]; char *ptr; int l, frame; l = Sbar_itoa(number, str); ptr = str; if (l > digits) { ptr += (l-digits); } if (l < digits) { x += ((digits-l)*13)/2; } while (*ptr) { if (*ptr == '-') frame = STAT_MINUS; else frame = *ptr -'0'; Sbar_DrawTransPic(x, y, sb_nums[frame]); x += 13; ptr++; } } //========================================================================== // // Sbar_SortFrags // //========================================================================== static void Sbar_SortFrags(void) { int i, j, k; // sort by frags scoreboardlines = 0; for (i = 0; i < cl.maxclients; i++) { if (cl.scores[i].name[0]) { fragsort[scoreboardlines] = i; scoreboardlines++; } } for (i = 0; i < scoreboardlines; i++) { for (j = 0; j < scoreboardlines-1-i; j++) { if (cl.scores[fragsort[j]].frags < cl.scores[fragsort[j+1]].frags) { k = fragsort[j]; fragsort[j] = fragsort[j+1]; fragsort[j+1] = k; } } } } static void FindColor (int slot, int *color1, int *color2) { int j; int top, bottom, done; byte *sourceA, *sourceB, *colorA, *colorB; if (slot > cl.maxclients) Sys_Error ("%s: slot > cl.maxclients", __thisfunc__); if (!cl.scores[slot].playerclass) { *color1 = *color2 = 0; return; } top = (cl.scores[slot].colors & 0xf0) >> 4; bottom = (cl.scores[slot].colors & 15); if (top > 10) top = 0; if (bottom > 10) bottom = 0; top -= 1; bottom -= 1; colorA = playerTranslation + 256 + color_offsets[(int)cl.scores[slot].playerclass-1]; colorB = colorA + 256; sourceA = colorB + 256 + (top * 256); sourceB = colorB + 256 + (bottom * 256); done = 0; for (j = 0; j < 256; j++, colorA++, colorB++, sourceA++, sourceB++) { if ((*colorA != 255) && !(done & 1)) { if (top >= 0) *color1 = *sourceA; else *color1 = *colorA; // done |= 1; } if ((*colorB != 255) && !(done & 2)) { if (bottom >= 0) *color2 = *sourceB; else *color2 = *colorB; // done |= 2; } } } //========================================================================== // // Sbar_DeathmatchOverlay // //========================================================================== void Sbar_DeathmatchOverlay(void) { qpic_t *pic; int i, k, l; int top, bottom; int x, y, f; char num[12]; scoreboard_t *s; scr_copyeverything = 1; if (scr_viewsize.integer < 100) scr_fullupdate = 0; pic = Draw_CachePic ("gfx/menu/title8.lmp"); M_DrawTransPic ((320-pic->width)/2, 0, pic); // scores Sbar_SortFrags (); // draw the text l = scoreboardlines; x = 80 + ((vid.width - 320)>>1); y = 62; top = bottom = 0; for (i = 0; i < l; i++) { if (y+10 >= vid.height) break; k = fragsort[i]; s = &cl.scores[k]; if (!s->name[0]) continue; // draw background FindColor (k, &top, &bottom); Draw_Fill (x, y, 40, 4, top); Draw_Fill (x, y+4, 40, 4, bottom); // draw number f = s->frags; sprintf (num, "%3i", f); if (k == sv_kingofhill) Draw_Character (x-12 , y-1, 130); Draw_Character (x+8 , y-1, num[0]); Draw_Character (x+16 , y-1, num[1]); Draw_Character (x+24 , y-1, num[2]); if (k == cl.viewentity - 1) Draw_Character (x - 8, y-1, 12); // draw name Draw_String (x+64, y, s->name); y += 10; } } //========================================================================== // // Sbar_PuzzlePieceOverlay // //========================================================================== static void FindPuzzlePieceName(const char *which, char *name) { const char *str = CL_FindPuzzleString (which); if (!str) str = "Unknown"; q_strlcpy (name, str, 40); /* 40 == array size in caller */ } static void Sbar_PuzzlePieceOverlay(void) { int i, y, piece; char Name[40]; scr_copyeverything = 1; if (scr_viewsize.integer < 100) scr_fullupdate = 0; piece = 0; y = 40; for (i = 0; i < 8; i++) { if (cl.puzzle_pieces[i][0] == 0) continue; if (piece == 4) y = 40; FindPuzzlePieceName(cl.puzzle_pieces[i], Name); if (piece < 4) { M_DrawPic(10, y, Draw_CachePic(va("gfx/puzzle/%s.lmp", cl.puzzle_pieces[i]))); M_PrintWhite (45, y, Name); } else { M_DrawPic(310-32, y, Draw_CachePic(va("gfx/puzzle/%s.lmp", cl.puzzle_pieces[i]))); M_PrintWhite (310-32-3-(strlen(Name)*8), 18+y, Name); } y += 32; piece++; } } //========================================================================== // // Sbar_SmallDeathmatchOverlay // //========================================================================== static void Sbar_SmallDeathmatchOverlay(void) { int i, k, l; int top, bottom; int x, y, f; char num[12]; scoreboard_t *s; if (!DMMode.integer) return; if (DMMode.integer > 2) DMMode.integer = 2; if (DMMode.integer == 2 && BarHeight != BAR_TOP_HEIGHT) return; trans_level = dmtrans.integer; if (trans_level < 0 || trans_level > 2) trans_level = 0; scr_copyeverything = 1; if (scr_viewsize.integer < 100) scr_fullupdate = 0; // scores Sbar_SortFrags (); // draw the text l = scoreboardlines; if (DMMode.integer == 1) { if (l > 8) l = 8; y = 46; } //else if (DMMode.integer == 2) else { if (l > 4) l = 4; y = vid.height - BAR_TOP_HEIGHT; } x = 10; top = bottom = 0; for (i = 0; i < l; i++) { k = fragsort[i]; s = &cl.scores[k]; if (!s->name[0]) continue; // draw background FindColor (k, &top, &bottom); Draw_Fill (x, y, 28, 4, top); Draw_Fill (x, y+4, 28, 4, bottom); // draw number f = s->frags; sprintf (num, "%3i", f); if (k != cl.viewentity - 1) { Draw_Character (x+2 , y-1, num[0]); Draw_Character (x+10 , y-1, num[1]); Draw_Character (x+18 , y-1, num[2]); if (k == sv_kingofhill) Draw_Character (x+30 , y-1, 130); } else { Draw_Character (x+2 , y-1, num[0] + 256); Draw_Character (x+10 , y-1, num[1] + 256); Draw_Character (x+18 , y-1, num[2] + 256); if (k == sv_kingofhill) Draw_Character ( x+30 , y-1, 130); } y += 10; } trans_level = 0; } //========================================================================== // // DrawActiveRings // //========================================================================== static int art_col; static int ring_row; static void DrawActiveRings(void) { int flag; int frame; char tempStr[24]; if (scr_con_current == vid.height) return; // console is full screen ring_row = 1; flag = (int)cl.v.rings_active; if (flag & RING_TURNING) { frame = 1 + ((int)(cl.time * 16) & 15); sprintf(tempStr, "gfx/rngtrn%d.lmp", frame); Draw_TransPic(vid.width - 50, ring_row, Draw_CachePic(tempStr)); ring_row += 33; } /* if (flag & RING_REGENERATION) { frame = 1 + ((int)(cl.time * 16) & 15); sprintf(tempStr, "gfx/rngreg%d.lmp", frame); Draw_TransPic(vid.width - 50, ring_row, Draw_CachePic(tempStr)); ring_row += 33; } */ if (flag & RING_WATER) { frame = 1 + ((int)(cl.time * 16) & 15); sprintf(tempStr, "gfx/rngwtr%d.lmp", frame); Draw_TransPic(vid.width - 50, ring_row, Draw_CachePic(tempStr)); ring_row += 33; } if (flag & RING_FLIGHT) { frame = 1 + ((int)(cl.time * 16) & 15); sprintf(tempStr, "gfx/rngfly%d.lmp", frame); Draw_TransPic(vid.width - 50, ring_row, Draw_CachePic(tempStr)); ring_row += 33; } } //========================================================================== // // DrawActiveArtifacts // //========================================================================== static void DrawActiveArtifacts(void) { int flag; static int oldflags = 0; int frame; char tempStr[24]; if (scr_con_current == vid.height) return; art_col = 50; if (ring_row != 1) art_col += 50; flag = (int)cl.v.artifact_active; if (flag & ART_TOMEOFPOWER) { frame = 1 + ((int)(cl.time * 16) & 15); sprintf(tempStr, "gfx/pwrbook%d.lmp", frame); Draw_TransPic(vid.width-art_col, 1, Draw_CachePic(tempStr)); art_col += 50; scr_topupdate = 0; } else if (oldflags & ART_TOMEOFPOWER) { scr_topupdate = 0; } if (flag & ART_HASTE) { frame = 1 + ((int)(cl.time * 16) & 15); sprintf(tempStr, "gfx/durhst%d.lmp", frame); Draw_TransPic(vid.width-art_col, 1, Draw_CachePic(tempStr)); art_col += 50; scr_topupdate = 0; } else if (oldflags & ART_HASTE) { scr_topupdate = 0; } if (flag & ART_INVINCIBILITY) { frame = 1 + ((int)(cl.time * 16) & 15); sprintf(tempStr, "gfx/durshd%d.lmp", frame); Draw_TransPic(vid.width-art_col, 1, Draw_CachePic(tempStr)); art_col += 50; scr_topupdate = 0; } else if (oldflags & ART_INVINCIBILITY) { scr_topupdate = 0; } if (scr_viewsize.integer >= 100) scr_topupdate = vid.numpages; oldflags = flag; } //============================================================================ void Inv_Update(qboolean force) { if (inv_flg || force) { // Just to be safe if (cl.inv_selected >= 0 && cl.inv_count > 0) cl.v.inventory = cl.inv_order[cl.inv_selected] + 1; else cl.v.inventory = 0; if (!force) { if (scr_viewsize.integer < 100) scr_fullupdate = 0; inv_flg = false; // Toggle menu off } // This will cause the server to set the client's edict's inventory value MSG_WriteByte (&cls.message, clc_inv_select); MSG_WriteByte (&cls.message, cl.v.inventory); } } //========================================================================== // // DrawBarArtifactIcon // //========================================================================== static void DrawBarArtifactIcon(int x, int y, int artifact) { int count; if (artifact < 0 || artifact >= MAX_INVENTORY) return; Sbar_DrawTransPic(x, y, Draw_CachePic(va("gfx/arti%02d.lmp", artifact))); if ((count = Inv_GetCount(artifact)) > 0) { DrawBarArtifactNumber(x+20, y+21, count); } } //========================================================================== // // DrawArtifactInventory // //========================================================================== static void DrawArtifactInventory(void) { int i; int x, y; if (InventoryHideTime < cl.time) { Inv_Update(false); return; } if (!inv_flg) return; if (!cl.inv_count) { Inv_Update(false); return; } if (BarHeight < 0) y = BarHeight-34; else y = -37; // InvLeft_f and InvRight_f scrolls the inventory as needed - S.A. for (i = 0, x = 64; i < INV_MAX_ICON; i++, x += 33) { if (i >= cl.inv_count) break; if ((cl.inv_startpos + i) % cl.inv_count == cl.inv_selected) { // Highlight icon Sbar_DrawTransPic(x+9, y-12, Draw_CachePic("gfx/artisel.lmp")); } DrawBarArtifactIcon(x, y, cl.inv_order[(cl.inv_startpos + i) % cl.inv_count]); } /* //Inv_DrawArrows (x, y); // Left arrow showing there are icons to the left if (cl.inv_startpos) Draw_Fill ( x , y - 5, 3, 1, 30); // Right arrow showing there are icons to the right if (cl.inv_startpos + INV_MAX_ICON < cl.inv_count) Draw_Fill ( x + 200, y - 5 , 3, 1, 30); */ } //========================================================================== // // ShowDMDown_f // //========================================================================== static void ShowDMDown_f(void) { if (sb_ShowDM) return; sb_ShowDM = true; } //========================================================================== // // ShowDMUp_f // //========================================================================== static void ShowDMUp_f(void) { sb_ShowDM = false; } //========================================================================== // // ShowInfoDown_f // //========================================================================== static void ShowInfoDown_f(void) { if (sb_ShowInfo || cl.intermission || cls.state < ca_connected) return; // if (cls.demoplayback) return; // demoplay check not needed -- O.S. S_LocalSound("misc/barmovup.wav"); BarTargetHeight = BAR_TOTAL_HEIGHT; sb_ShowInfo = true; sb_updates = 0; } //========================================================================== // // ShowInfoUp_f // //========================================================================== static void ShowInfoUp_f(void) { if (cl.intermission || (scr_viewsize.integer > 110/* && !sbtrans.integer*/)) BarTargetHeight = 0.0-BAR_BUMP_HEIGHT; else BarTargetHeight = BAR_TOP_HEIGHT; S_LocalSound("misc/barmovdn.wav"); sb_ShowInfo = false; sb_updates = 0; } //========================================================================== // // InvLeft_f // //========================================================================== static void InvLeft_f(void) { if (!cl.inv_count || cl.intermission) return; if (inv_flg) { // scroll inventory icons if we're at the left-most already if (cl.inv_selected == cl.inv_startpos) { cl.inv_startpos = (cl.inv_startpos - 1 + cl.inv_count) % cl.inv_count; cl.inv_selected = cl.inv_startpos; } else { cl.inv_selected = (cl.inv_selected - 1 + cl.inv_count) % cl.inv_count; } if (scr_viewsize.integer < 100) scr_fullupdate = 0; } else { inv_flg = true; } S_LocalSound("misc/invmove.wav"); InventoryHideTime = cl.time+INVENTORY_DISPLAY_TIME; } //========================================================================== // // InvRight_f // //========================================================================== static void InvRight_f(void) { int right_icon; if (!cl.inv_count || cl.intermission) return; if (inv_flg) { if (cl.inv_count >= INV_MAX_ICON) right_icon = (cl.inv_startpos + INV_MAX_ICON - 1) % cl.inv_count; else right_icon = (cl.inv_startpos + cl.inv_count - 1) % cl.inv_count; // scroll inventory icons if we're at the right most already if (cl.inv_selected == right_icon) cl.inv_startpos = (cl.inv_startpos + 1) % cl.inv_count; cl.inv_selected = (cl.inv_selected + 1) % cl.inv_count; if (scr_viewsize.integer < 100) scr_fullupdate = 0; } else { inv_flg = true; } S_LocalSound("misc/invmove.wav"); InventoryHideTime = cl.time+INVENTORY_DISPLAY_TIME; } //========================================================================== // // InvUse_f // //========================================================================== static void InvUse_f(void) { if (!cl.inv_count || cl.intermission) return; S_LocalSound("misc/invuse.wav"); //Inv_Update(false); Inv_Update(true); inv_flg = false; if (scr_viewsize.integer < 100) scr_fullupdate = 0; in_impulse = 23; } //========================================================================== // // InvOff_f // //========================================================================== static void InvOff_f(void) { inv_flg = false; if (scr_viewsize.integer < 100) scr_fullupdate = 0; } //========================================================================== // // ToggleDM_f // //========================================================================== static void ToggleDM_f(void) { DMMode.integer += 1; if (DMMode.integer > 2) DMMode.integer = 0; } //========================================================================== // // SB_InvChanged // //========================================================================== void SB_InvChanged(void) { int counter, position; qboolean examined[MAX_INVENTORY]; qboolean ForceUpdate = false; memset (examined, 0, sizeof(examined)); if (cl.inv_selected >= 0 && Inv_GetCount(cl.inv_order[cl.inv_selected]) == 0) ForceUpdate = true; // removed items we no longer have from the order for (counter = position = 0; counter < cl.inv_count; counter++) { if (Inv_GetCount(cl.inv_order[counter]) > 0) { cl.inv_order[position] = cl.inv_order[counter]; examined[cl.inv_order[position]] = true; position++; } } // add in the new items for (counter = 0; counter < MAX_INVENTORY; counter++) { if (!examined[counter]) { if (Inv_GetCount(counter) > 0) { cl.inv_order[position] = counter; position++; } } } cl.inv_count = position; if (cl.inv_selected >= cl.inv_count) { cl.inv_selected = cl.inv_count-1; ForceUpdate = true; } if (cl.inv_count && cl.inv_selected < 0) { cl.inv_selected = 0; ForceUpdate = true; } if (ForceUpdate) { Inv_Update(true); } /* make sure that startpos isn't borked */ if (cl.inv_count <= 1) cl.inv_startpos = 0; else if (cl.inv_startpos >= cl.inv_count) cl.inv_startpos = cl.inv_selected; else { position = cl.inv_selected - cl.inv_startpos; if (position < 0) position += cl.inv_count; if (position >= INV_MAX_ICON/* || position < 0*/) cl.inv_startpos = cl.inv_selected; } } //========================================================================== // // SB_InvReset // //========================================================================== void SB_InvReset(void) { cl.inv_count = cl.inv_startpos = 0; cl.inv_selected = -1; inv_flg = false; if (scr_viewsize.integer < 100) scr_fullupdate = 0; } //========================================================================== // // SB_ViewSizeChanged // //========================================================================== void SB_ViewSizeChanged(void) { if (cl.intermission || (scr_viewsize.integer > 110/* && !sbtrans.integer*/)) BarHeight = BarTargetHeight = 0.0-BAR_BUMP_HEIGHT; else BarHeight = BarTargetHeight = BAR_TOP_HEIGHT; } // DRAWING FUNCTIONS ******************************************************* //========================================================================== // // Sbar_Draw**Pic // Relative to the current status bar location. // //========================================================================== static void Sbar_DrawPic(int x, int y, qpic_t *pic) { Draw_PicCropped (x+((vid.width-320)>>1), y+(vid.height-(int)BarHeight), pic); } static void Sbar_DrawTransPic(int x, int y, qpic_t *pic) { Draw_TransPicCropped (x+((vid.width-320)>>1), y+(vid.height-(int)BarHeight), pic); } #if 0 /* no callers */ static void Sbar_DrawCharacter(int x, int y, int num) { Draw_Character (x+((vid.width-320)>>1)+4, y+vid.height-(int)BarHeight, num); } static void Sbar_DrawString(int x, int y, const char *str) { Draw_String (x+((vid.width-320)>>1), y+vid.height-(int)BarHeight, str); } static void Sbar_DrawSmallCharacter(int x, int y, int num) { Draw_SmallCharacter (x+((vid.width-320)>>1)+4, y+vid.height-(int)BarHeight, num); } #endif static void Sbar_DrawSmallString(int x, int y, const char *str) { Draw_SmallString (x+((vid.width-320)>>1), y+vid.height-(int)BarHeight, str); } static void DrawBarArtifactNumber(int x, int y, int number) { static char artiNumName[18] = "gfx/artinum0.lmp"; if (number >= 10) { artiNumName[11] = '0'+(number%100)/10; Sbar_DrawTransPic(x -4, y, Draw_CachePic(artiNumName)); } artiNumName[11] = '0'+number%10; Sbar_DrawTransPic(x, y, Draw_CachePic(artiNumName)); } engine/hexen2/server.h000066400000000000000000000222011444734033100151730ustar00rootroot00000000000000/* hexen2/server.h * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_SERVER_H #define __HX2_SERVER_H typedef struct { int maxclients; int maxclientslimit; struct client_s *clients; // [maxclients] int serverflags; // episode completion information qboolean changelevel_issued; // cleared when at SV_SpawnServer } server_static_t; //============================================================================= typedef enum { ss_loading, ss_active } server_state_t; typedef struct { qboolean active; // false if only a net client qboolean paused; qboolean loadgame; // handle connections specially double time; int lastcheck; // used by PF_checkclient double lastchecktime; char name[64]; // map name char midi_name[128]; // midi file name byte cd_track; // cd track number char startspot[64]; char modelname[MAX_QPATH]; // maps/.bsp, for model_precache[0] struct qmodel_s *worldmodel; const char *model_precache[MAX_MODELS]; // NULL terminated struct qmodel_s *models[MAX_MODELS]; const char *sound_precache[MAX_SOUNDS]; // NULL terminated const char *lightstyles[MAX_LIGHTSTYLES]; struct EffectT Effects[MAX_EFFECTS]; client_state2_t *states; int num_edicts; edict_t *edicts; // can NOT be array indexed, because // edict_t is variable sized, but can // be used to reference the world ent server_state_t state; // some actions are only valid during load sizebuf_t datagram; byte datagram_buf[NET_MAXMESSAGE]; sizebuf_t reliable_datagram; // copied to all clients at end of frame byte reliable_datagram_buf[NET_MAXMESSAGE]; sizebuf_t signon; byte signon_buf[NET_MAXMESSAGE]; } server_t; #define NUM_PING_TIMES 16 #define NUM_SPAWN_PARMS 16 typedef struct client_s { qboolean active; // false = client is free qboolean spawned; // false = don't send datagrams qboolean dropasap; // has been told to go to another level qboolean sendsignon; // only valid before spawned double last_message; // reliable messages must be sent // periodically struct qsocket_s *netconnection; // communications handle usercmd_t cmd; // movement vec3_t wishdir; // intended motion calced from cmd sizebuf_t message; // can be added to at any time, // copied and clear once per frame byte msgbuf[MAX_MSGLEN]; sizebuf_t datagram; byte datagram_buf[NET_MAXMESSAGE]; edict_t *edict; // EDICT_NUM(clientnum+1) char name[32]; // for printing to other people int colors; float playerclass; float ping_times[NUM_PING_TIMES]; int num_pings; // ping_times[num_pings%NUM_PING_TIMES] // spawn parms are carried from level to level float spawn_parms[NUM_SPAWN_PARMS]; // client known data for deltas int old_frags; entvars_t old_v; qboolean send_all_v; byte current_frame, last_frame; byte current_sequence, last_sequence; // mission pack, objectives strings unsigned int info_mask, info_mask2; } client_t; //============================================================================= // // edict->movetype values // #define MOVETYPE_NONE 0 // never moves #define MOVETYPE_ANGLENOCLIP 1 #define MOVETYPE_ANGLECLIP 2 #define MOVETYPE_WALK 3 // gravity #define MOVETYPE_STEP 4 // gravity, special edge handling #define MOVETYPE_FLY 5 #define MOVETYPE_TOSS 6 // gravity #define MOVETYPE_PUSH 7 // no clip to world, push and crush #define MOVETYPE_NOCLIP 8 #define MOVETYPE_FLYMISSILE 9 // extra size to monsters #define MOVETYPE_BOUNCE 10 //#ifdef QUAKE2 #define MOVETYPE_BOUNCEMISSILE 11 // bounce w/o gravity #define MOVETYPE_FOLLOW 12 // track movement of aiment //#endif #define MOVETYPE_PUSHPULL 13 // pushable/pullable object #define MOVETYPE_SWIM 14 // should keep the object in water // // edict->solid values // #define SOLID_NOT 0 // no interaction with other objects #define SOLID_TRIGGER 1 // touch on edge, but not blocking #define SOLID_BBOX 2 // touch on edge, block #define SOLID_SLIDEBOX 3 // touch on edge, but not an onground #define SOLID_BSP 4 // bsp clip, touch on edge, block #define SOLID_PHASE 5 // won't slow down when hitting entities flagged as FL_MONSTER // // edict->deadflag values // #define DEAD_NO 0 #define DEAD_DYING 1 #define DEAD_DEAD 2 #define DAMAGE_NO 0 // Cannot be damaged #define DAMAGE_YES 1 // Can be damaged #define DAMAGE_NO_GRENADE 2 // Will not set off grenades // // edict->flags // #define FL_FLY 1 #define FL_SWIM 2 #define FL_CONVEYOR 4 #define FL_CLIENT 8 #define FL_INWATER 16 #define FL_MONSTER 32 #define FL_GODMODE 64 #define FL_NOTARGET 128 #define FL_ITEM 256 #define FL_ONGROUND 512 #define FL_PARTIALGROUND 1024 // not all corners are valid #define FL_WATERJUMP 2048 // player jumping out of water #define FL_JUMPRELEASED 4096 // for jump debouncing #define FL_FLASHLIGHT 8192 #define FL_ARCHIVE_OVERRIDE 1048576 #define FL_ARTIFACTUSED 16384 #define FL_MOVECHAIN_ANGLE 32768 // when in a move chain, will update the angle /* the following three are for monster_pentacles of the mission pack */ #define FL_HUNTFACE 65536 // Makes monster go for enemy view_ofs thwn moving #define FL_NOZ 131072 // Monster will not automove on Z if flying or swimming #define FL_SET_TRACE 262144 // Trace will always be set for this monster (pentacles) #define FL_CLASS_DEPENDENT 2097152 // model will appear different to each player #define FL_SPECIAL_ABILITY1 4194304 // has 1st special ability #define FL_SPECIAL_ABILITY2 8388608 // has 2nd special ability #define FL2_CROUCHED 4096 // // Built-in Spawn Flags // #define SPAWNFLAG_NOT_PALADIN 0x00000100 #define SPAWNFLAG_NOT_CLERIC 0x00000200 #define SPAWNFLAG_NOT_NECROMANCER 0x00000400 #define SPAWNFLAG_NOT_THEIF 0x00000800 #define SPAWNFLAG_NOT_EASY 0x00001000 #define SPAWNFLAG_NOT_MEDIUM 0x00002000 #define SPAWNFLAG_NOT_HARD 0x00004000 #define SPAWNFLAG_NOT_DEATHMATCH 0x00008000 #define SPAWNFLAG_NOT_COOP 0x00010000 #define SPAWNFLAG_NOT_SINGLE 0x00020000 /* SPAWNFLAG_NOT_DEMON is NOT used: ED_LoadFromFile * checks SPAWNFLAG_NOT_NECROMANCER for CLASS_DEMON !! */ #define SPAWNFLAG_NOT_DEMON 0x00040000 // // server flags // #define SFL_EPISODE_1 1 #define SFL_EPISODE_2 2 #define SFL_EPISODE_3 4 #define SFL_EPISODE_4 8 #define SFL_NEW_UNIT 16 #define SFL_NEW_EPISODE 32 #define SFL_CROSS_TRIGGERS 65280 //============================================================================ extern cvar_t teamplay; extern cvar_t skill; extern cvar_t deathmatch; extern cvar_t coop; extern cvar_t randomclass; extern cvar_t fraglimit; extern cvar_t timelimit; extern server_static_t svs; // persistant server info extern server_t sv; // local server extern int sv_protocol; // protocol version to use extern client_t *host_client; extern edict_t *sv_player; //=========================================================== void SV_Init (void); void SV_UserInit (void); void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count); void SV_StartParticle2 (vec3_t org, vec3_t dmin, vec3_t dmax, int color, int effect, int count); void SV_StartParticle3 (vec3_t org, vec3_t box, int color, int effect, int count); void SV_StartParticle4 (vec3_t org, float radius, int color, int effect, int count); void SV_StartSound (edict_t *entity, int channel, const char *sample, int volume, float attenuation); void SV_StopSound (edict_t *entity, int channel); void SV_UpdateSoundPos (edict_t *entity, int channel); void SV_DropClient (qboolean crash); void SV_Edicts (const char *Name); void SV_SendClientMessages (void); void SV_ClearDatagram (void); int SV_ModelIndex (const char *name); void SV_SetIdealPitch (void); void SV_AddUpdates (void); void SV_ClientThink (void); void SV_AddClientToServer (struct qsocket_s *ret); void SV_ClientPrintf (unsigned int unused, const char *fmt, ...) FUNC_PRINTF(2,3); void SV_BroadcastPrintf (const char *fmt, ...) FUNC_PRINTF(1,2); void SV_Physics (void); qboolean SV_CheckBottom (edict_t *ent); qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink, qboolean noenemy, qboolean set_trace); void SV_WriteClientdataToMessage (client_t *client, edict_t *ent, sizebuf_t *msg); void SV_MoveToGoal (void); void SV_CheckForNewClients (void); void SV_RunClients (void); void SV_SaveSpawnparms (void); void SV_SpawnServer (const char *server, const char *startspot); const char *SV_GetLevelname (void); void SV_ParseEffect (sizebuf_t *sb); void SV_UpdateEffects (sizebuf_t *sb); void SV_SaveEffects (FILE *FH); void SV_LoadEffects (FILE *FH); #endif /* __HX2_SERVER_H */ engine/hexen2/server/000077500000000000000000000000001444734033100150255ustar00rootroot00000000000000engine/hexen2/server/Makefile000066400000000000000000000240511444734033100164670ustar00rootroot00000000000000# GNU Makefile for Hexen II dedicated server (h2ded) using GCC. # # Remember to "make clean" between different types of builds or targets. # # To cross-compile for Win32 on Unix: either pass the W32BUILD=1 # argument to make, or export it. Also see build_cross_win32.sh. # Requires: a mingw or mingw-w64 compiler toolchain. # # To cross-compile for Win64 on Unix: either pass the W64BUILD=1 # argument to make, or export it. Also see build_cross_win64.sh. # Requires: a mingw-w64 compiler toolchain. # # To cross-compile for MacOSX on Unix: either pass the OSXBUILD=1 # argument to make, or export it. You would also need to pass a # suitable MACH_TYPE=xxx (ppc, x86, x86_64, or ppc64) argument to # make. Also see build_cross_osx.sh. # # To (cross-)compile for DOS: either pass the DOSBUILD=1 argument # to make, or export it. Also see build_cross_dos.sh. Requires: a # djgpp compiler toolchain. # # To use a compiler other than gcc: make CC=compiler_name [other stuff] # # To build for the demo version: make DEMO=1 [other stuff] # # To build a debug version: make DEBUG=1 [other stuff] # # PATH SETTINGS: UHEXEN2_TOP:=../../.. ENGINE_TOP:=../.. COMMONDIR:=$(ENGINE_TOP)/h2shared UHEXEN2_SHARED:=$(UHEXEN2_TOP)/common LIBS_DIR:=$(UHEXEN2_TOP)/libs OSLIBS:=$(UHEXEN2_TOP)/oslibs # GENERAL OPTIONS (customize as required) # use WinSock2 instead of WinSock-1.1? (disabled for w32 for compat. # with old Win95 machines.) (enabled for Win64 in the win64 section.) USE_WINSOCK2=no # DOS Serial driver is useless in h2ded USE_SERIAL=no # use WatTCP (WATT-32) for DOS UDP networking? USE_WATT32=yes # include the common dirty stuff include $(UHEXEN2_TOP)/scripts/makefile.inc # Names of the binaries BINARY:=h2ded$(exe_ext) ############################################################# # Compiler flags ############################################################# ifeq ($(MACH_TYPE),x86) CPU_X86=-march=i586 endif # Overrides for the default CPUFLAGS CPUFLAGS=$(CPU_X86) CFLAGS += -Wall CFLAGS += $(CPUFLAGS) ifdef DEBUG CFLAGS += -g else # optimization flags CFLAGS += -O2 -DNDEBUG=1 -ffast-math # NOTE: -fomit-frame-pointer is broken with ancient gcc versions!! CFLAGS += -fomit-frame-pointer endif CPPFLAGS= LDFLAGS = # linkage may be sensitive to order: add SYSLIBS after all others. SYSLIBS = # compiler includes INCLUDES= -I. -I.. -I$(COMMONDIR) -I$(UHEXEN2_SHARED) # end of compiler flags ############################################################# ############################################################# # Other build flags ############################################################# CPPFLAGS+= -DSERVERONLY ifdef DEMO CPPFLAGS+= -DDEMOBUILD endif ifdef DEBUG # This activates some extra code in hexen2/hexenworld C source CPPFLAGS+= -DDEBUG=1 -DDEBUG_BUILD=1 endif ############################################################# # DOS flags/settings ############################################################# ifeq ($(TARGET_OS),dos) INCLUDES += -I$(OSLIBS)/dos ifeq ($(USE_SERIAL),yes) CPPFLAGS+= -DUSE_SERIAL endif ifeq ($(USE_WATT32),yes) CPPFLAGS+= -DUSE_WATT32 -DWATT32_NO_OLDIES INCLUDES+= -I$(OSLIBS)/dos/watt32/inc LDFLAGS += -L$(OSLIBS)/dos/watt32/lib -lwatt endif SYSLIBS += $(OSLIBS)/dos/djtime/djtime.a -lc -lgcc #SYSLIBS += -lm endif # End of DOS settings ############################################################# ############################################################# # OS/2 flags/settings ############################################################# ifeq ($(TARGET_OS),os2) INCLUDES+= -I$(OSLIBS)/os2/emx/include CFLAGS += -Zmt LDFLAGS += -Zmt ifndef DEBUG LDFLAGS += -s # -fomit-frame-pointer/-ffast-math seems to cause trouble # with EMX at least with the old compilers I tried. CFLAGS += -fno-omit-frame-pointer endif SYSLIBS += -lsocket endif # End of OS/2 settings ############################################################# ############################################################# # Win32 flags/settings ############################################################# ifeq ($(TARGET_OS),win32) CPPFLAGS+= -DWIN32_LEAN_AND_MEAN ifeq ($(USE_WINSOCK2),yes) CPPFLAGS+=-D_USE_WINSOCK2 LIBWINSOCK=ws2_32 else LIBWINSOCK=wsock32 endif INCLUDES+= -I$(OSLIBS)/windows/misc/include CFLAGS += -m32 LDFLAGS += -m32 -mconsole LDFLAGS += -l$(LIBWINSOCK) -lwinmm endif # End of Win32 settings ############################################################# ############################################################# # Win64 flags/settings ############################################################# ifeq ($(TARGET_OS),win64) CPPFLAGS+= -DWIN32_LEAN_AND_MEAN # use winsock2 for win64 USE_WINSOCK2=yes ifeq ($(USE_WINSOCK2),yes) CPPFLAGS+=-D_USE_WINSOCK2 LIBWINSOCK=ws2_32 else LIBWINSOCK=wsock32 endif INCLUDES+= -I$(OSLIBS)/windows/misc/include CFLAGS += -m64 LDFLAGS += -m64 -mconsole LDFLAGS += -l$(LIBWINSOCK) -lwinmm endif # End of Win64 settings ############################################################# ############################################################# # Mac OS X flags/settings ############################################################# ifeq ($(TARGET_OS),darwin) CPUFLAGS= # require 10.5 for 64 bit builds ifeq ($(MACH_TYPE),x86_64) CFLAGS +=-mmacosx-version-min=10.5 LDFLAGS +=-mmacosx-version-min=10.5 endif ifeq ($(MACH_TYPE),ppc64) CFLAGS +=-mmacosx-version-min=10.5 LDFLAGS +=-mmacosx-version-min=10.5 endif endif # End of Mac OS X settings ############################################################# ############################################################# # Unix flags/settings ############################################################# ifeq ($(TARGET_OS),unix) # common unix: ifeq ($(HOST_OS),qnx) SYSLIBS += -lsocket endif ifeq ($(HOST_OS),haiku) SYSLIBS += -lnetwork endif ifeq ($(HOST_OS),sunos) SYSLIBS += -lsocket -lnsl -lresolv endif SYSLIBS += -lm endif # End of Unix settings ############################################################# ############################################################# # MorphOS flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),morphos) CFLAGS += -noixemul LDFLAGS += -noixemul SYSLIBS += -lm endif # End of MorphOS settings ############################################################# ############################################################# # AROS flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),aros) CFLAGS += -fno-common INCLUDES+= -I$(OSLIBS)/aros-$(MACH_TYPE)/misc/include endif # End of AROS settings ############################################################# ############################################################# # AmigaOS flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),amigaos) # use Bebbo's GCC6 toolchain BEBBO_TOOLCHAIN=yes # crt: libnix or clib2: USE_CLIB2=yes ifeq ($(BEBBO_TOOLCHAIN),yes) USE_CLIB2=no endif ifndef DEBUG ifneq ($(BEBBO_TOOLCHAIN),yes) # -fomit-frame-pointer / -ffast-math causes trouble with gcc2.95 CFLAGS += -fno-omit-frame-pointer else # these break the game with GCC6 CFLAGS += -fbbb=- -fno-tree-forwprop -fno-tree-ter -fno-move-loop-invariants endif endif ifeq ($(USE_CLIB2),yes) CRT_FLAGS=-mcrt=clib2 else CRT_FLAGS=-noixemul endif CFLAGS += $(CRT_FLAGS) -m68060 -m68881 LDFLAGS += $(CRT_FLAGS) -m68060 -m68881 SYSLIBS += -lm # for extra missing headers INCLUDES += -I$(OSLIBS)/amigaos/include ifneq ($(BEBBO_TOOLCHAIN),yes) # Roadshow SDK NET_INC = -I$(OSLIBS)/amigaos/netinclude endif endif # End of AmigaOS settings ############################################################# # Rules for turning source files into .o files %.o: %.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: ../%.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< dos/%.o: ../dos/%.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(COMMONDIR)/%.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(UHEXEN2_SHARED)/%.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< # Objects # Platform specific object settings ifeq ($(TARGET_OS),win32) SYSOBJ_NET := net_win.o net_wins.o net_wipx.o SYSOBJ_SYS := sys_win.o endif ifeq ($(TARGET_OS),win64) SYSOBJ_NET := net_win.o net_wins.o net_wipx.o SYSOBJ_SYS := sys_win.o endif ifeq ($(TARGET_OS),dos) DOSTCP := SYSOBJ_NET := dos/net_dos.o dos/net_ipx.o ifeq ($(USE_WATT32),yes) SYSOBJ_NET += net_udp.o endif ifeq ($(USE_SERIAL),yes) SYSOBJ_NET += dos/net_ser.o endif SYSOBJ_SYS := dos_v2.o sys_dos.o endif ifeq ($(TARGET_OS),os2) SYSOBJ_NET := net_bsd.o net_udp.o SYSOBJ_SYS := sys_os2.o endif ifeq ($(TARGET_OS),unix) SYSOBJ_NET := net_bsd.o net_udp.o SYSOBJ_SYS := sys_unix.o endif ifeq ($(TARGET_OS),darwin) SYSOBJ_NET := net_bsd.o net_udp.o SYSOBJ_SYS := sys_unix.o endif ifeq ($(TARGET_OS),aros) SYSOBJ_NET := net_bsd.o net_udp.o SYSOBJ_SYS := sys_amiga.o endif ifeq ($(TARGET_OS),morphos) SYSOBJ_NET := net_bsd.o net_udp.o SYSOBJ_SYS := sys_amiga.o endif ifeq ($(TARGET_OS),amigaos) SYSOBJ_NET := net_bsd.o net_udp.o SYSOBJ_SYS := sys_amiga.o endif # Final list of objects OBJECTS = \ q_endian.o \ link_ops.o \ sizebuf.o \ strlcat.o \ strlcpy.o \ qsnprint.o \ msg_io.o \ common.o \ debuglog.o \ quakefs.o \ cmd.o \ crc.o \ cvar.o \ mathlib.o \ zone.o \ hashindex.o \ $(SYSOBJ_NET) \ net_dgrm.o \ net_main.o \ sv_model.o \ host.o \ host_cmd.o \ pr_cmds.o \ pr_edict.o \ pr_exec.o \ host_string.o \ sv_effect.o \ sv_main.o \ sv_move.o \ sv_phys.o \ sv_user.o \ world.o \ $(SYSOBJ_SYS) # Targets .PHONY: clean distclean report default: $(BINARY) all: default $(BINARY): $(OBJECTS) $(LINKER) $(OBJECTS) $(LDFLAGS) $(SYSLIBS) -o $@ ifeq ($(TARGET_OS),amigaos) # workaround stupid AmiTCP SDK mess for old aos3 net_bsd.o: INCLUDES+= $(NET_INC) net_udp.o: INCLUDES+= $(NET_INC) net_dgrm.o: INCLUDES+= $(NET_INC) net_loop.o: INCLUDES+= $(NET_INC) net_main.o: INCLUDES+= $(NET_INC) endif # deps for *.c including another *.c dos/net_ser.o: ../dos/net_comx.c clean: rm -f *.o *.res dos/*.o core distclean: clean rm -f $(BINARY) report: @echo "Host OS :" $(HOST_OS) @echo "Target OS:" $(TARGET_OS) @echo "Machine :" $(MACH_TYPE) engine/hexen2/server/Makefile.os2000066400000000000000000000047371444734033100172020ustar00rootroot00000000000000# makefile to build h2ded.exe for OS/2 using Open Watcom: # wmake -f Makefile.os2 # PATH SETTINGS: !ifndef __UNIX__ PATH_SEP=\ UHEXEN2_TOP=..\..\.. ENGINE_TOP=..\.. COMMONDIR=$(ENGINE_TOP)\h2shared UHEXEN2_SHARED=$(UHEXEN2_TOP)\common LIBS_DIR=$(UHEXEN2_TOP)\libs OSLIBS=$(UHEXEN2_TOP)\oslibs !else PATH_SEP=/ UHEXEN2_TOP=../../.. ENGINE_TOP=../.. COMMONDIR=$(ENGINE_TOP)/h2shared UHEXEN2_SHARED=$(UHEXEN2_TOP)/common LIBS_DIR=$(UHEXEN2_TOP)/libs OSLIBS=$(UHEXEN2_TOP)/oslibs !endif # GENERAL OPTIONS (customize as required) # Names of the binaries BINARY=h2ded.exe ############################################################# # Compiler flags ############################################################# CFLAGS = -zq -wx -bm -bt=os2 -5s -sg -otexan -fp5 -fpi87 -ei -j -zp8 # newer OpenWatcom versions enable W303 by default CFLAGS+= -wcd=303 # compiler includes INCLUDES= -I. -I.. -I$(COMMONDIR) -I$(UHEXEN2_SHARED) # end of compiler flags ############################################################# ############################################################# # Other build flags ############################################################# CPPFLAGS+= -DSERVERONLY !ifdef DEMO CPPFLAGS+= -DDEMOBUILD !endif !ifdef DEBUG CFLAGS += -d2 # This activates some extra code in hexen2/hexenworld C source CPPFLAGS+= -DDEBUG=1 -DDEBUG_BUILD=1 !endif ############################################################# # OS/2 flags/settings and overrides: ############################################################# ############################################################# .c: ..;$(COMMONDIR);$(UHEXEN2_SHARED) .c.obj: wcc386 $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -fo=$^@ $< # Objects SYSOBJ_NET = net_bsd.obj net_udp.obj SYSOBJ_SYS = sys_os2.obj # Final list of objects OBJECTS = & q_endian.obj & link_ops.obj & sizebuf.obj & strlcat.obj & strlcpy.obj & qsnprint.obj & msg_io.obj & common.obj & debuglog.obj & quakefs.obj & cmd.obj & crc.obj & cvar.obj & mathlib.obj & zone.obj & hashindex.obj & $(SYSOBJ_NET) & net_dgrm.obj & net_main.obj & sv_model.obj & host.obj & host_cmd.obj & pr_cmds.obj & pr_edict.obj & pr_exec.obj & host_string.obj & sv_effect.obj & sv_main.obj & sv_move.obj & sv_phys.obj & sv_user.obj & world.obj & $(SYSOBJ_SYS) all: $(BINARY) h2ded: $(BINARY) # 512 KB stack size. $(BINARY): $(OBJECTS) wlink N $@ SYS OS2V2 OPTION q OPTION STACK=0x80000 F {$(OBJECTS)} clean: .symbolic rm -f *.obj *.res *.err distclean: clean .symbolic rm -f $(BINARY) engine/hexen2/server/Makefile.wat000066400000000000000000000056651444734033100172730ustar00rootroot00000000000000# makefile to build h2ded for Windows using Open Watcom: # wmake -f Makefile.wat # PATH SETTINGS: !ifndef __UNIX__ PATH_SEP=\ UHEXEN2_TOP=..\..\.. ENGINE_TOP=..\.. COMMONDIR=$(ENGINE_TOP)\h2shared UHEXEN2_SHARED=$(UHEXEN2_TOP)\common LIBS_DIR=$(UHEXEN2_TOP)\libs OSLIBS=$(UHEXEN2_TOP)\oslibs !else PATH_SEP=/ UHEXEN2_TOP=../../.. ENGINE_TOP=../.. COMMONDIR=$(ENGINE_TOP)/h2shared UHEXEN2_SHARED=$(UHEXEN2_TOP)/common LIBS_DIR=$(UHEXEN2_TOP)/libs OSLIBS=$(UHEXEN2_TOP)/oslibs !endif # GENERAL OPTIONS (customize as required) # use WinSock2 instead of WinSock-1.1? (disabled for w32 for compat. # with old Win95 machines.) (enabled for Win64 in the win64 section.) USE_WINSOCK2=no # Names of the binaries BINARY=h2ded.exe ############################################################# # Compiler flags ############################################################# CFLAGS = -zq -wx -bm -bt=nt -5s -sg -otexan -fp5 -fpi87 -ei -j -zp8 # newer OpenWatcom versions enable W303 by default CFLAGS+= -wcd=303 # compiler includes INCLUDES= -I. -I.. -I$(COMMONDIR) -I$(UHEXEN2_SHARED) # end of compiler flags ############################################################# ############################################################# # Other build flags ############################################################# CPPFLAGS+= -DSERVERONLY !ifdef DEMO CPPFLAGS+= -DDEMOBUILD !endif !ifdef DEBUG CFLAGS += -d2 # This activates some extra code in hexen2/hexenworld C source CPPFLAGS+= -DDEBUG=1 -DDEBUG_BUILD=1 !endif ############################################################# # Win32 flags/settings and overrides: ############################################################# !ifndef __UNIX__ INCLUDES+= -I$(OSLIBS)\windows\misc\include !else INCLUDES+= -I$(OSLIBS)/windows/misc/include !endif !ifeq USE_WINSOCK2 yes CPPFLAGS+=-D_USE_WINSOCK2 LIBWINSOCK=ws2_32.lib !else LIBWINSOCK=wsock32.lib !endif CPPFLAGS+= -DWIN32_LEAN_AND_MEAN LIBS += $(LIBWINSOCK) winmm.lib ############################################################# .c: ..;$(COMMONDIR);$(UHEXEN2_SHARED) .c.obj: wcc386 $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -fo=$^@ $< # Objects SYSOBJ_NET = net_win.obj net_wins.obj net_wipx.obj SYSOBJ_SYS = sys_win.obj # Final list of objects OBJECTS = & q_endian.obj & link_ops.obj & sizebuf.obj & strlcat.obj & strlcpy.obj & qsnprint.obj & msg_io.obj & common.obj & debuglog.obj & quakefs.obj & cmd.obj & crc.obj & cvar.obj & mathlib.obj & zone.obj & hashindex.obj & $(SYSOBJ_NET) & net_dgrm.obj & net_main.obj & sv_model.obj & host.obj & host_cmd.obj & pr_cmds.obj & pr_edict.obj & pr_exec.obj & host_string.obj & sv_effect.obj & sv_main.obj & sv_move.obj & sv_phys.obj & sv_user.obj & world.obj & $(SYSOBJ_SYS) all: $(BINARY) h2ded: $(BINARY) # 512 KB stack. $(BINARY): $(OBJECTS) wlink N $@ SYS NT OPTION q OPTION STACK=0x80000 LIBR {$(LIBS)} F {$(OBJECTS)} clean: .symbolic rm -f *.obj *.res *.err distclean: clean .symbolic rm -f $(BINARY) engine/hexen2/server/build_cross_amigaos.sh000077500000000000000000000005051444734033100213740ustar00rootroot00000000000000#!/bin/sh UHEXEN2_TOP=../../.. . $UHEXEN2_TOP/scripts/cross_defs.amigaos if test "$1" = "strip"; then $STRIPPER -S h2ded exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac exec $MAKE_CMD $* engine/hexen2/server/build_cross_aros.sh000077500000000000000000000005371444734033100207250ustar00rootroot00000000000000#!/bin/sh # for x86-AROS cross-builds UHEXEN2_TOP=../../.. . $UHEXEN2_TOP/scripts/cross_defs.aros if test "$1" = "strip"; then $STRIPPER -S h2ded exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac exec $MAKE_CMD $* engine/hexen2/server/build_cross_aros64.sh000077500000000000000000000005441444734033100210750ustar00rootroot00000000000000#!/bin/sh # for x86_64-AROS cross-builds UHEXEN2_TOP=../../.. . $UHEXEN2_TOP/scripts/cross_defs.aros64 if test "$1" = "strip"; then $STRIPPER -S h2ded exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac exec $MAKE_CMD $* engine/hexen2/server/build_cross_morphos.sh000077500000000000000000000005051444734033100214430ustar00rootroot00000000000000#!/bin/sh UHEXEN2_TOP=../../.. . $UHEXEN2_TOP/scripts/cross_defs.morphos if test "$1" = "strip"; then $STRIPPER -S h2ded exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac exec $MAKE_CMD $* engine/hexen2/server/dos/000077500000000000000000000000001444734033100156125ustar00rootroot00000000000000engine/hexen2/server/dos/.cvsignore000066400000000000000000000000101444734033100176010ustar00rootroot00000000000000*.o *.a engine/hexen2/server/host.c000066400000000000000000000370201444734033100161500ustar00rootroot00000000000000/* * host.c -- coordinates spawning and killing of local servers * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2001 contributors of the Anvil of Thyrion project * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "debuglog.h" /* * Memory is cleared / released when a server or client begins, not when * they end. */ quakeparms_t *host_parms; qboolean host_initialized; // true if into command execution double host_frametime; double realtime; // without any filtering or bounding int host_hunklevel; client_t *host_client; // current client cvar_t sys_ticrate = {"sys_ticrate", "0.05", CVAR_NONE}; static cvar_t host_framerate = {"host_framerate", "0", CVAR_NONE}; // set for slow motion static cvar_t serverprofile = {"serverprofile", "0", CVAR_NONE}; cvar_t fraglimit = {"fraglimit", "0", CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t timelimit = {"timelimit", "0", CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t teamplay = {"teamplay", "0", CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t samelevel = {"samelevel", "0", CVAR_NONE}; cvar_t noexit = {"noexit", "0", CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t developer = {"developer", "0", CVAR_ARCHIVE}; cvar_t skill = {"skill", "1", CVAR_NONE}; // 0 - 3 cvar_t coop = {"coop", "0", CVAR_NONE}; // 0 or 1 cvar_t deathmatch = {"deathmatch", "0", CVAR_NONE}; // 0, 1, or 2 cvar_t randomclass = {"randomclass", "0", CVAR_NONE}; // 0, 1, or 2 cvar_t pausable = {"pausable", "1", CVAR_NONE}; cvar_t temp1 = {"temp1", "0", CVAR_NONE}; /* =============================================================================== SAVEGAME FILES HANDLING =============================================================================== */ void Host_RemoveGIPFiles (const char *path) { const char *name; char tempdir[MAX_OSPATH], *p; size_t len; if (path) q_strlcpy(tempdir, path, MAX_OSPATH); else q_strlcpy(tempdir, FS_GetUserdir(), MAX_OSPATH); len = strlen(tempdir); p = tempdir + len; len = sizeof(tempdir) - len; name = Sys_FindFirstFile (tempdir, "*.gip"); while (name) { q_snprintf (p, len, "/%s", name); Sys_unlink (tempdir); *p = '\0'; name = Sys_FindNextFile(); } Sys_FindClose(); } void Host_DeleteSave (const char *savepath) { char tmppath[MAX_OSPATH]; if (strstr(savepath, FS_GetUserdir()) != savepath) return; Host_RemoveGIPFiles (savepath); q_snprintf (tmppath, sizeof(tmppath), "%s/info.dat", savepath); Sys_unlink (tmppath); Sys_rmdir (savepath); } int Host_CopyFiles (const char *source, const char *pat, const char *dest) { const char *name; char tempdir[MAX_OSPATH], tempdir2[MAX_OSPATH]; int error; name = Sys_FindFirstFile(source, pat); error = 0; while (name) { if (q_snprintf(tempdir, sizeof(tempdir),"%s/%s", source, name) >= (int)sizeof(tempdir) || q_snprintf(tempdir2, sizeof(tempdir2),"%s/%s", dest, name) >= (int)sizeof(tempdir2)) { Sys_FindClose(); Host_Error("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return -1; } error = FS_CopyFile (tempdir, tempdir2); if (error) { Con_Printf ("Error copying %s to %s\n", tempdir, tempdir2); goto error_out; } name = Sys_FindNextFile(); } error_out: Sys_FindClose(); return error; } //============================================================================ /* ================ Host_Error This shuts down both the client and server ================ */ void Host_Error (const char *error, ...) { va_list argptr; char string[1024]; static qboolean inerror = false; if (inerror) Sys_Error ("%s: recursive error!", __thisfunc__); inerror = true; va_start (argptr,error); q_vsnprintf (string, sizeof(string), error, argptr); va_end (argptr); Con_Printf ("%s: %s\n", __thisfunc__, string); Host_ShutdownServer (false); Sys_Error ("%s: %s", __thisfunc__, string); // dedicated servers exit } /* ================ Host_FindMaxClients ================ */ static void Host_FindMaxClients (void) { int i; svs.maxclients = 8; i = COM_CheckParm ("-dedicated"); if (i && i < com_argc-1) svs.maxclients = atoi (com_argv[i+1]); if (svs.maxclients < 2) svs.maxclients = 8; else if (svs.maxclients > MAX_CLIENTS) svs.maxclients = MAX_CLIENTS; svs.maxclientslimit = svs.maxclients; if (svs.maxclientslimit < 4) svs.maxclientslimit = 4; svs.clients = (client_t *) Hunk_AllocName (svs.maxclientslimit*sizeof(client_t), "clients"); Cvar_Set ("deathmatch", "1"); } static void Host_Version_f (void) { Con_Printf ("Version %4.2f\n", ENGINE_VERSION); Con_Printf ("Exe: " __TIME__ " " __DATE__ "\n"); } /* cvar callback functions : */ void Host_Callback_Notify (cvar_t *var) { if (sv.active) SV_BroadcastPrintf ("\"%s\" changed to \"%s\"\n", var->name, var->string); } /* ======================= Host_InitLocal ====================== */ static void Host_InitLocal (void) { Cmd_AddCommand ("version", Host_Version_f); Host_InitCommands (); Cvar_RegisterVariable (&developer); if (COM_CheckParm("-developer")) { Cvar_Set ("developer", "1"); Cvar_LockVar ("developer"); } Cvar_RegisterVariable (&sys_nostdout); Cvar_RegisterVariable (&sys_ticrate); Cvar_RegisterVariable (&host_framerate); Cvar_RegisterVariable (&serverprofile); Cvar_RegisterVariable (&fraglimit); Cvar_RegisterVariable (&timelimit); Cvar_RegisterVariable (&teamplay); Cvar_SetCallback (&fraglimit, Host_Callback_Notify); Cvar_SetCallback (&timelimit, Host_Callback_Notify); Cvar_SetCallback (&teamplay, Host_Callback_Notify); Cvar_RegisterVariable (&samelevel); Cvar_RegisterVariable (&noexit); Cvar_SetCallback (&noexit, Host_Callback_Notify); Cvar_RegisterVariable (&skill); Cvar_RegisterVariable (&coop); Cvar_RegisterVariable (&deathmatch); Cvar_RegisterVariable (&randomclass); Cvar_RegisterVariable (&pausable); Cvar_RegisterVariable (&temp1); Host_FindMaxClients (); } /* ================ CON_Printf ================ */ void CON_Printf (unsigned int flags, const char *fmt, ...) { va_list argptr; char msg[MAX_PRINTMSG]; if (flags & _PRINT_DEVEL && !developer.integer) { if (con_debuglog & LOG_DEVEL) /* full logging */ { va_start (argptr, fmt); q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); LOG_Print (msg); } return; } va_start (argptr, fmt); q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); Sys_PrintTerm (msg); // echo to the terminal if (con_debuglog) LOG_Print (msg); } /* ================= SV_ClientPrintf Sends text across to be displayed FIXME: make this just a stuffed echo? ================= */ void SV_ClientPrintf (unsigned int unused, const char *fmt, ...) { va_list argptr; char string[1024]; va_start (argptr,fmt); q_vsnprintf (string, sizeof (string), fmt, argptr); va_end (argptr); MSG_WriteByte (&host_client->message, svc_print); MSG_WriteString (&host_client->message, string); } /* ================= SV_BroadcastPrintf Sends text to all active clients ================= */ void SV_BroadcastPrintf (const char *fmt, ...) { va_list argptr; char string[1024]; int i; va_start (argptr,fmt); q_vsnprintf (string, sizeof (string), fmt, argptr); va_end (argptr); for (i = 0; i < svs.maxclients; i++) { if (svs.clients[i].active && svs.clients[i].spawned) { MSG_WriteByte (&svs.clients[i].message, svc_print); MSG_WriteString (&svs.clients[i].message, string); } } } /* ================= Host_ClientCommands Send text over to the client to be executed ================= */ void Host_ClientCommands (const char *fmt, ...) { va_list argptr; char string[1024]; va_start (argptr,fmt); q_vsnprintf (string, sizeof (string), fmt, argptr); va_end (argptr); MSG_WriteByte (&host_client->message, svc_stufftext); MSG_WriteString (&host_client->message, string); } /* ===================== SV_DropClient Called when the player is getting totally kicked off the host if (crash = true), don't bother sending signofs ===================== */ void SV_DropClient (qboolean crash) { int saveSelf; int i; client_t *client; if (!crash) { // send any final messages (don't check for errors) if (NET_CanSendMessage (host_client->netconnection)) { MSG_WriteByte (&host_client->message, svc_disconnect); NET_SendMessage (host_client->netconnection, &host_client->message); } if (sv.active && host_client->edict && host_client->spawned) { // call the prog function for removing a client // this will set the body to a dead frame, among other things saveSelf = *sv_globals.self; *sv_globals.self = EDICT_TO_PROG(host_client->edict); PR_ExecuteProgram (*sv_globals.ClientDisconnect); *sv_globals.self = saveSelf; } Sys_Printf ("Client %s removed\n",host_client->name); } // break the net connection NET_Close (host_client->netconnection); host_client->netconnection = NULL; // free the client (the body stays around) host_client->active = false; host_client->name[0] = 0; host_client->old_frags = -999999; memset(&host_client->old_v,0,sizeof(host_client->old_v)); ED_ClearEdict(host_client->edict); host_client->send_all_v = true; net_activeconnections--; // send notification to all clients for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++) { if (!client->active) continue; MSG_WriteByte (&client->message, svc_updatename); MSG_WriteByte (&client->message, host_client - svs.clients); MSG_WriteString (&client->message, ""); MSG_WriteByte (&client->message, svc_updatefrags); MSG_WriteByte (&client->message, host_client - svs.clients); MSG_WriteShort (&client->message, 0); MSG_WriteByte (&client->message, svc_updatecolors); MSG_WriteByte (&client->message, host_client - svs.clients); MSG_WriteByte (&client->message, 0); } } /* ================== Host_ShutdownServer This only happens at the end of a game, not between levels ================== */ void Host_ShutdownServer(qboolean crash) { int i; int count; sizebuf_t buf; byte message[4]; double start; if (!sv.active) return; sv.active = false; // flush any pending messages - like the score!!! start = Sys_DoubleTime(); do { count = 0; for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if (host_client->active && host_client->message.cursize) { if (NET_CanSendMessage (host_client->netconnection)) { NET_SendMessage(host_client->netconnection, &host_client->message); SZ_Clear (&host_client->message); } else { NET_GetMessage(host_client->netconnection); count++; } } } if ((Sys_DoubleTime() - start) > 3.0) break; } while (count); // make sure all the clients know we're disconnecting SZ_Init (&buf, message, sizeof(message)); MSG_WriteByte(&buf, svc_disconnect); count = NET_SendToAll (&buf, 5.0); if (count) Con_Printf("%s: NET_SendToAll failed for %d clients\n", __thisfunc__, count); for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if (host_client->active) SV_DropClient(crash); } // clear structures // memset (&sv, 0, sizeof(sv)); // ServerSpawn already do this by Host_ClearMemory memset (svs.clients, 0, svs.maxclientslimit*sizeof(client_t)); } /* ================ Host_ClearMemory This clears all the memory used by both the client and server, but does not reinitialize anything. ================ */ void Host_ClearMemory (void) { Con_DPrintf ("Clearing memory\n"); Mod_ClearAll (); /* host_hunklevel MUST be set at this point */ Hunk_FreeToLowMark (host_hunklevel); memset (&sv, 0, sizeof(sv)); } //============================================================================ /* =================== Host_GetConsoleCommands Add them exactly as if they had been typed at the console =================== */ static void Host_GetConsoleCommands (void) { const char *cmd; while (1) { cmd = Sys_ConsoleInput (); if (!cmd) break; Cbuf_AddText (cmd); } } /* ================== Host_ServerFrame ================== */ static void Host_ServerFrame (void) { // run the world state *sv_globals.frametime = host_frametime; // set the time and clear the general datagram SV_ClearDatagram (); // check for new clients SV_CheckForNewClients (); // read client messages SV_RunClients (); // move things around and think // always pause in single player if in console or menus if (!sv.paused) SV_Physics (); // send all messages to the clients SV_SendClientMessages (); } /* ================== Host_Frame Runs all active servers ================== */ static void _Host_Frame (float time) { // keep the random time dependent rand (); // decide the simulation time realtime += time; if (host_framerate.value > 0) host_frametime = host_framerate.value; else { host_frametime = time; // don't allow really long or short frames if (host_frametime > 0.05) host_frametime = 0.05; else if (host_frametime < 0.001) host_frametime = 0.001; } // process console commands Cbuf_Execute (); NET_Poll(); // check for commands typed to the host Host_GetConsoleCommands (); if (sv.active) Host_ServerFrame (); } void Host_Frame (float time) { double time1, time2; static double timetotal; static int timecount; int i, c, m; if (!serverprofile.integer) { _Host_Frame (time); return; } time1 = Sys_DoubleTime (); _Host_Frame (time); time2 = Sys_DoubleTime (); timetotal += time2 - time1; timecount++; if (timecount < 1000) return; m = timetotal*1000/timecount; timecount = 0; timetotal = 0; c = 0; for (i = 0; i < svs.maxclients; i++) { if (svs.clients[i].active) c++; } Con_Printf ("serverprofile: %2i clients %2i msec\n", c, m); } //============================================================================ /* ==================== Host_Init ==================== */ void Host_Init (void) { Sys_Printf ("Host_Init\n"); Memory_Init (host_parms->membase, host_parms->memsize); Cbuf_Init (); Cmd_Init (); COM_Init (); SV_Init (); FS_Init (); Host_RemoveGIPFiles(NULL); Host_InitLocal (); PR_Init (); Mod_Init (); NET_Init (); Con_Printf ("Exe: " __TIME__ " " __DATE__ "\n"); Con_Printf ("%4.1f megabyte heap\n", host_parms->memsize/(1024*1024.0)); Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); host_hunklevel = Hunk_LowMark (); host_initialized = true; Con_Printf("\n===== Hexen II dedicated server initialized ======\n\n"); Cvar_UnlockAll (); /* unlock the early-set cvars after init */ Cbuf_InsertText ("exec server.cfg\n"); Cbuf_Execute (); Cmd_StuffCmds_f (); /* process command line arguments */ Cbuf_Execute (); if (!sv.active) /* no map specified on the command line: spawn demo1.map */ Cmd_ExecuteString ("map demo1", src_command); } /* =============== Host_Shutdown FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better to run quit through here before the final handoff to the sys code. =============== */ void Host_Shutdown(void) { static qboolean isdown = false; if (isdown) { printf ("recursive shutdown\n"); return; } isdown = true; NET_Shutdown (); LOG_Close (); } engine/hexen2/server/host_cmd.c000066400000000000000000001157361444734033100170060ustar00rootroot00000000000000/* * host_cmd.c -- console commands * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2001 contributors of the Anvil of Thyrion project * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "q_ctype.h" static double old_svtime; /* sv.time of prev. level when changing levels, saved by changelevel2(). * used for adjusting time globalvars of progs by RestoreClients() when * travelling levels back and forth (see clients.hc::ClientReEnter(). */ static int LoadGamestate (const char *level, const char *startspot, int ClientsMode); static void RestoreClients (int ClientsMode); #define TESTSAVE /* ================== Host_Quit_f ================== */ void Host_Quit_f (void) { Host_ShutdownServer(false); Sys_Quit (); } /* ================== Host_Status_f ================== */ static void Host_Status_f (void) { void (*print_fn) (unsigned int, const char *, ...) FUNCP_PRINTF(2,3); client_t *client; int seconds; int minutes; int hours = 0; int j; if (cmd_source == src_command) { if (!sv.active) { Con_Printf("Server not active\n"); return; } print_fn = CON_Printf; } else print_fn = SV_ClientPrintf; print_fn (_PRINT_NORMAL, "host: %s\n", Cvar_VariableString ("hostname")); print_fn (_PRINT_NORMAL, "version: %4.2f\n", ENGINE_VERSION); if (tcpipAvailable) print_fn (_PRINT_NORMAL, "tcp/ip: %s\n", my_tcpip_address); if (ipxAvailable) print_fn (_PRINT_NORMAL, "ipx: %s\n", my_ipx_address); print_fn (_PRINT_NORMAL, "map: %s\n", sv.name); print_fn (_PRINT_NORMAL, "players: %i active (%i max)\n\n", net_activeconnections, svs.maxclients); for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) { if (!client->active) continue; seconds = (int)(net_time - NET_QSocketGetTime(client->netconnection)); minutes = seconds / 60; if (minutes) { seconds -= (minutes * 60); hours = minutes / 60; if (hours) minutes -= (hours * 60); } else hours = 0; print_fn (_PRINT_NORMAL, "#%-2u %-16.16s %3i %2i:%02i:%02i\n", j + 1, client->name, (int)client->edict->v.frags, hours, minutes, seconds); print_fn (_PRINT_NORMAL, " %s\n", NET_QSocketGetAddressString(client->netconnection)); } } /* ================== Host_God_f Sets client to godmode ================== */ static void Host_God_f (void) { if (cmd_source == src_command) return; if (*sv_globals.deathmatch || *sv_globals.coop || skill.integer > 2) return; sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE; if (!((int)sv_player->v.flags & FL_GODMODE) ) SV_ClientPrintf (0, "godmode OFF\n"); else SV_ClientPrintf (0, "godmode ON\n"); } static void Host_Notarget_f (void) { if (cmd_source == src_command) return; if (*sv_globals.deathmatch || skill.integer > 2) return; sv_player->v.flags = (int)sv_player->v.flags ^ FL_NOTARGET; if (!((int)sv_player->v.flags & FL_NOTARGET) ) SV_ClientPrintf (0, "notarget OFF\n"); else SV_ClientPrintf (0, "notarget ON\n"); } static void Host_Noclip_f (void) { if (cmd_source == src_command) return; if (*sv_globals.deathmatch || *sv_globals.coop || skill.integer > 2) return; if (sv_player->v.movetype != MOVETYPE_NOCLIP) { sv_player->v.movetype = MOVETYPE_NOCLIP; SV_ClientPrintf (0, "noclip ON\n"); } else { sv_player->v.movetype = MOVETYPE_WALK; SV_ClientPrintf (0, "noclip OFF\n"); } } /* ================== Host_Ping_f ================== */ static void Host_Ping_f (void) { int i, j; float total; client_t *client; if (cmd_source == src_command) return; SV_ClientPrintf (0, "Client ping times:\n"); for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++) { if (!client->active) continue; total = 0; for (j = 0; j < NUM_PING_TIMES; j++) total += client->ping_times[j]; total /= NUM_PING_TIMES; SV_ClientPrintf (0, "%4i %s\n", (int)(total*1000), client->name); } } /* =============================================================================== SERVER TRANSITIONS =============================================================================== */ /* ====================== Host_Map_f handle a map command from the console. Active clients are kicked off. ====================== */ static void Host_Map_f (void) { char name[MAX_QPATH]; if (Cmd_Argc() < 2) //no map name given { Con_Printf ("map : start a new server\n"); if (sv.active) { Con_Printf ("Current level: %s [ %s ]\n", SV_GetLevelname(), sv.name); } return; } if (cmd_source != src_command) return; Host_ShutdownServer(false); info_mask = 0; if (!coop.integer && deathmatch.integer) info_mask2 = 0x80000000; else info_mask2 = 0; svs.serverflags = 0; // haven't completed an episode yet q_strlcpy (name, Cmd_Argv(1), sizeof(name)); SV_SpawnServer (name, NULL); } /* ================== Host_Changelevel_f Goes to a new map, taking all clients along ================== */ static void Host_Changelevel_f (void) { char level[MAX_QPATH]; char _startspot[MAX_QPATH]; char *startspot; if (Cmd_Argc() < 2) { Con_Printf ("changelevel : continue game on a new level\n"); return; } if (!sv.active) { Con_Printf ("Server not active\n"); return; } q_snprintf (level, sizeof(level), "maps/%s.bsp", Cmd_Argv(1)); if (!FS_FileExists(level, NULL)) Host_Error ("%s: cannot find map %s", __thisfunc__, level); q_strlcpy (level, Cmd_Argv(1), sizeof(level)); if (Cmd_Argc() == 2) startspot = NULL; else { q_strlcpy (_startspot, Cmd_Argv(2), sizeof(_startspot)); startspot = _startspot; } SV_SaveSpawnparms (); SV_SpawnServer (level, startspot); if (!sv.active) Host_Error ("%s: cannot run map %s", __thisfunc__, level); } /* ================== Host_Changelevel2_f changing levels within a unit ================== */ static void Host_Changelevel2_f (void) { char level[MAX_QPATH]; char _startspot[MAX_QPATH]; char *startspot; if (Cmd_Argc() < 2) { Con_Printf ("changelevel2 : continue game on a new level in the unit\n"); return; } if (!sv.active) { Con_Printf ("Server not active\n"); return; } q_snprintf (level, sizeof(level), "maps/%s.bsp", Cmd_Argv(1)); if (!FS_FileExists(level, NULL)) Host_Error ("%s: cannot find map %s", __thisfunc__, level); q_strlcpy (level, Cmd_Argv(1), sizeof(level)); if (Cmd_Argc() == 2) startspot = NULL; else { q_strlcpy (_startspot, Cmd_Argv(2), sizeof(_startspot)); startspot = _startspot; } SV_SaveSpawnparms (); // save the current level's state old_svtime = sv.time; if (SaveGamestate(false) != 0) return; // try to restore the new level if (LoadGamestate(level, startspot, 0) != 0) { SV_SpawnServer (level, startspot); if (!sv.active) Host_Error ("%s: cannot run map %s", __thisfunc__, level); RestoreClients (0); } } /* ================== Host_Restart_f Restarts the current server for a dead player ================== */ static void Host_Restart_f (void) { char mapname[MAX_QPATH]; char startspot[MAX_QPATH]; if (cmd_source != src_command) return; if (!sv.active) { Con_Printf ("Server not active\n"); return; } q_strlcpy (mapname, sv.name, sizeof(mapname)); // mapname gets cleared in spawnserver q_strlcpy (startspot, sv.startspot, sizeof(startspot)); if (Cmd_Argc() == 2 && q_strcasecmp(Cmd_Argv(1),"restore") == 0) { if (LoadGamestate(mapname, startspot, 3) != 0) { SV_SpawnServer (mapname, startspot); if (!sv.active) Host_Error ("%s: cannot restart map %s", __thisfunc__, mapname); RestoreClients (0); } } else { SV_SpawnServer (mapname, startspot); if (!sv.active) Host_Error ("%s: cannot restart map %s", __thisfunc__, mapname); } } /* =============================================================================== LOAD / SAVE GAME =============================================================================== */ static char savename[MAX_OSPATH], savedest[MAX_OSPATH]; /* =============== Host_SavegameComment Writes a SAVEGAME_COMMENT_LENGTH character comment describing the game saved =============== */ static void Host_SavegameComment (char *text) { size_t i; char temp[20]; const char *levelname; for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++) { text[i] = ' '; } /* see SAVEGAME_COMMENT_LENGTH definition in quakedef.h */ levelname = SV_GetLevelname (); i = strlen(levelname); if (i > 20) i = 20; memcpy (text, levelname, i); Sys_DateTimeString (temp); temp[16] = '\0'; // eliminate seconds i = strlen(temp); memcpy (text+21, temp, i); // convert space to _ to make stdio happy for (i = 0; i < SAVEGAME_COMMENT_LENGTH; i++) { if (text[i] == ' ') text[i] = '_'; } text[SAVEGAME_COMMENT_LENGTH] = '\0'; } /* =============== Host_Savegame_f =============== */ static void Host_Savegame_f (void) { FILE *f; int i, error_state; char comment[SAVEGAME_COMMENT_LENGTH+1]; const char *p; if (cmd_source != src_command) return; if (!sv.active) { Con_Printf ("Server not active\n"); return; } #ifndef TESTSAVE if (svs.maxclients != 1) { Con_Printf ("Can't save multiplayer games.\n"); return; } #endif if (Cmd_Argc() != 2) { Con_Printf ("save : save a game\n"); return; } p = Cmd_Argv(1); if (*p == '.' || strstr(p, "..")) { Con_Printf ("Invalid save name.\n"); return; } for (i = 0; i < svs.maxclients; i++) { if (svs.clients[i].active && (svs.clients[i].edict->v.health <= 0) ) { Con_Printf ("Can't savegame with a dead player\n"); return; } } error_state = SaveGamestate (false); // don't bother doing more if SaveGamestate failed if (error_state) return; FS_MakePath_BUF (FS_USERDIR, &error_state, savename, sizeof(savename), p); if (error_state) { Con_Printf ("%s: save directory name too long\n", __thisfunc__); return; } if (Sys_mkdir(savename, false) != 0) { Con_Printf ("Unable to create save directory\n"); return; } Host_RemoveGIPFiles(savename); FS_MakePath_BUF (FS_USERDIR, NULL, savename, sizeof(savename), "clients.gip"); Sys_unlink(savename); FS_MakePath_BUF (FS_USERDIR, NULL, savedest, sizeof(savedest), p); Con_Printf ("Saving game to %s...\n", savedest); error_state = Host_CopyFiles(FS_GetUserdir(), "*.gip", savedest); if (error_state) goto finish; FS_MakePath_VABUF (FS_USERDIR, &error_state, savedest, sizeof(savedest), "%s/info.dat", p); if (error_state) { Host_Error ("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return; } f = fopen (savedest, "w"); if (!f) { error_state = 1; Con_Printf ("%s: Unable to open %s for writing!\n", __thisfunc__, savedest); goto finish; } fprintf (f, "%i\n", SAVEGAME_VERSION); Host_SavegameComment (comment); fprintf (f, "%s\n", comment); for (i = 0; i < NUM_SPAWN_PARMS; i++) fprintf (f, "%f\n", svs.clients->spawn_parms[i]); fprintf (f, "%d\n", current_skill); fprintf (f, "%s\n", sv.name); fprintf (f, "%f\n", sv.time); fprintf (f, "%d\n", svs.maxclients); fprintf (f, "%f\n", deathmatch.value); fprintf (f, "%f\n", coop.value); fprintf (f, "%f\n", teamplay.value); fprintf (f, "%f\n", randomclass.value); fprintf (f, "%f\n", 1.0); // dummy cl_playerclass.value // mission pack, objectives strings fprintf (f, "%u\n", info_mask); fprintf (f, "%u\n", info_mask2); error_state = ferror(f); fclose(f); finish: if (error_state) Host_Error ("%s: The game could not be saved properly!", __thisfunc__); } /* =============== Host_DeleteSave_f =============== */ static void Host_DeleteSave_f (void) { const char *p; int err; if (cmd_source != src_command) return; if (Cmd_Argc() != 2) { Con_Printf ("deletesave : delete a saved game\n"); return; } p = Cmd_Argv(1); if (*p == '.' || strstr(p, "..")) { Con_Printf ("Invalid save name.\n"); return; } FS_MakePath_BUF (FS_USERDIR, &err, savename, sizeof(savename), p); if (!err) Host_DeleteSave (savename); } /* =============== Host_Loadgame_f =============== */ static void Host_Loadgame_f (void) { FILE *f; char mapname[MAX_QPATH]; float playtime; char str[32768]; int version; int i, error_state; int tempi; float tempf; edict_t *ent; float spawn_parms[NUM_SPAWN_PARMS]; if (cmd_source != src_command) return; if (Cmd_Argc() != 2) { Con_Printf ("load : load a game\n"); return; } Host_RemoveGIPFiles(NULL); FS_MakePath_BUF (FS_USERDIR, &error_state, savename, sizeof(savename), Cmd_Argv(1)); if (error_state) { Con_Printf ("%s: save directory name too long\n", __thisfunc__); return; } Con_Printf ("Loading game from %s...\n", savename); if (q_snprintf(savedest, sizeof(savedest), "%s/info.dat", savename) >= (int)sizeof(savedest)) { Host_Error ("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return; } f = fopen (savedest, "r"); if (!f) { Host_Error ("%s: ERROR: couldn't open savefile", __thisfunc__); return; } fscanf (f, "%i\n", &version); if (version != SAVEGAME_VERSION) { fclose (f); Host_Error ("Savegame is version %i, not %i", version, SAVEGAME_VERSION); return; } fscanf (f, "%s\n", str); for (i = 0; i < NUM_SPAWN_PARMS; i++) fscanf (f, "%f\n", &spawn_parms[i]); // this silliness is so we can load 1.06 save files, which have float skill values fscanf (f, "%f\n", &tempf); current_skill = (int)(tempf + 0.1); Cvar_SetValue ("skill", current_skill); Cvar_Set ("deathmatch", "0"); Cvar_Set ("coop", "0"); Cvar_Set ("teamplay", "0"); Cvar_Set ("randomclass", "0"); fscanf (f, "%s\n", mapname); fscanf (f, "%f\n", &playtime); tempi = -1; fscanf (f, "%d\n", &tempi); if (tempi >= 1) svs.maxclients = tempi; tempf = -1; fscanf (f, "%f\n", &tempf); if (tempf >= 0) Cvar_SetValue ("deathmatch", tempf); tempf = -1; fscanf (f, "%f\n", &tempf); if (tempf >= 0) Cvar_SetValue ("coop", tempf); tempf = -1; fscanf (f, "%f\n", &tempf); if (tempf >= 0) Cvar_SetValue ("teamplay", tempf); tempf = -1; fscanf (f, "%f\n", &tempf); if (tempf >= 0) Cvar_SetValue ("randomclass", tempf); tempf = -1; fscanf (f, "%f\n", &tempf); // mission pack, objectives strings fscanf (f, "%u\n", &info_mask); fscanf (f, "%u\n", &info_mask2); fclose (f); Host_RemoveGIPFiles(FS_GetUserdir()); FS_MakePath_BUF (FS_USERDIR, NULL, savedest, sizeof(savedest), Cmd_Argv(1)); error_state = Host_CopyFiles(savedest, "*.gip", FS_GetUserdir()); if (error_state) { Host_Error ("%s: The game could not be loaded properly!", __thisfunc__); return; } if (LoadGamestate(mapname, NULL, 2) != 0) return; SV_SaveSpawnparms (); ent = EDICT_NUM(1); // this may be rudundant with the setting in PR_LoadProgs, but not sure so its here too if (sv_globals.cl_playerclass) *sv_globals.cl_playerclass = ent->v.playerclass; svs.clients->playerclass = ent->v.playerclass; sv.paused = true; // pause until all clients connect sv.loadgame = true; } int SaveGamestate (qboolean ClientsOnly) { FILE *f; edict_t *ent; int i, error_state; int start, end; char comment[SAVEGAME_COMMENT_LENGTH+1]; if (ClientsOnly) { start = 1; end = svs.maxclients+1; FS_MakePath_BUF (FS_USERDIR, &error_state, savename, sizeof(savename), "clients.gip"); if (error_state) { Host_Error ("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return -1; } } else { start = 1; end = sv.num_edicts; FS_MakePath_VABUF (FS_USERDIR, &error_state, savename, sizeof(savename), "%s.gip", sv.name); if (error_state) { Host_Error ("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return -1; } } f = fopen (savename, "w"); if (!f) { error_state = -1; Con_Printf ("%s: Unable to open %s for writing!\n", __thisfunc__, savename); goto finish; } fprintf (f, "%i\n", SAVEGAME_VERSION); if (!ClientsOnly) { Host_SavegameComment (comment); fprintf (f, "%s\n", comment); // for (i = 0; i < NUM_SPAWN_PARMS; i++) // fprintf (f, "%f\n", svs.clients->spawn_parms[i]); fprintf (f, "%f\n", skill.value); fprintf (f, "%s\n", sv.name); fprintf (f, "%f\n", sv.time); // write the light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { if (sv.lightstyles[i]) fprintf (f, "%s\n", sv.lightstyles[i]); else fprintf (f, "m\n"); } SV_SaveEffects (f); fprintf (f, "-1\n"); ED_WriteGlobals (f); } host_client = svs.clients; // save the client states for (i = start; i < end; i++) { ent = EDICT_NUM(i); if ((int)ent->v.flags & FL_ARCHIVE_OVERRIDE) continue; if (ClientsOnly) { if (host_client->active) { fprintf (f, "%i\n", i); ED_Write (f, ent); fflush (f); } host_client++; } else { fprintf (f, "%i\n", i); ED_Write (f, ent); fflush (f); } } error_state = ferror(f); fclose (f); finish: if (error_state) Host_Error ("%s: The level could not be saved properly!", __thisfunc__); return error_state; } static void RestoreClients (int ClientsMode) { int i, j; edict_t *ent; double time_diff; if (LoadGamestate(NULL, NULL, 1) != 0) return; /* O.S. -- mode 3 is only in response to the single player game "restart restore" * command issued by progs.dat::client.hc::respawn() function. No level change, * just respawning in the same map with the same playtime from when clients.gip * was saved, therefore there _CANNOT_ be a time_diff. See uhexen2 bug #2176023: * http://sourceforge.net/tracker/?group_id=124987&atid=701006&aid=2176023&func=detail */ time_diff = (ClientsMode == 3) ? 0 : sv.time - old_svtime; for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if (host_client->active) { ent = host_client->edict; //ent->v.colormap = NUM_FOR_EDICT(ent); ent->v.team = (host_client->colors & 15) + 1; ent->v.netname = PR_SetEngineString(host_client->name); ent->v.playerclass = host_client->playerclass; // copy spawn parms out of the client_t for (j = 0; j < NUM_SPAWN_PARMS; j++) sv_globals.parm[j] = host_client->spawn_parms[j]; // call the spawn function *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(ent); G_FLOAT(OFS_PARM0) = time_diff; PR_ExecuteProgram (*sv_globals.ClientReEnter); } } SaveGamestate (true); } static int LoadGamestate (const char *level, const char *startspot, int ClientsMode) { FILE *f; char mapname[MAX_QPATH]; float playtime, sk; char str[32768]; const char *start; int i, r; edict_t *ent; int entnum; int version; // float spawn_parms[NUM_SPAWN_PARMS]; qboolean auto_correct = false; if (ClientsMode == 1) /* for RestoreClients() only: map must be active */ { if (!sv.active) { Con_Printf ("%s: server not active\n", __thisfunc__); return -1; } FS_MakePath_BUF (FS_USERDIR, &r, savename, sizeof(savename), "clients.gip"); if (r) { Host_Error ("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return -1; } } else { FS_MakePath_VABUF (FS_USERDIR, &r, savename, sizeof(savename), "%s.gip", level); if (r) { Host_Error ("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return -1; } if (ClientsMode != 2 && ClientsMode != 3) Con_Printf ("Loading game from %s...\n", savename); } f = fopen (savename, "r"); if (!f) { if (ClientsMode == 2) /* caller: Host_Loadgame_f() */ Host_Error ("%s: ERROR: couldn't open savefile", __thisfunc__); return -1; } fscanf (f, "%i\n", &version); if (version != SAVEGAME_VERSION) { fclose (f); Host_Error ("Savegame is version %i, not %i", version, SAVEGAME_VERSION); return -1; } if (ClientsMode != 1) { fscanf (f, "%s\n", str); // for (i = 0; i < NUM_SPAWN_PARMS; i++) // fscanf (f, "%f\n", &spawn_parms[i]); fscanf (f, "%f\n", &sk); Cvar_SetValue ("skill", sk); fscanf (f, "%s\n", mapname); fscanf (f, "%f\n", &playtime); SV_SpawnServer (mapname, startspot); if (!sv.active) { fclose (f); Con_Printf ("Couldn't load map\n"); return -1; } // load the light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { fscanf (f, "%s\n", str); sv.lightstyles[i] = (const char *)Hunk_Strdup (str, "lightstyles"); } SV_LoadEffects (f); } // load the edicts out of the savegame file while (!feof(f)) { fscanf (f, "%i\n", &entnum); for (i = 0; i < (int) sizeof(str) - 1; i++) { r = fgetc (f); if (r == EOF || !r) break; str[i] = r; if (r == '}') { i++; break; } } if (i == (int) sizeof(str) - 1) { fclose (f); Host_Error ("%s: Loadgame buffer overflow", __thisfunc__); } str[i] = 0; start = str; start = COM_Parse(str); if (!com_token[0]) break; // end of file if (strcmp(com_token,"{")) { fclose (f); Host_Error ("%s: First token isn't a brace", __thisfunc__); } // parse an edict if (entnum == -1) { ED_ParseGlobals (start); // Need to restore this *sv_globals.startspot = PR_SetEngineString(sv.startspot); } else { ent = EDICT_NUM(entnum); /* default to active edict: ED_ParseEdict() set it * to free if it really is free. cf. ED_Write() */ ent->free = false; /* ED_ParseEdict() will always memset ent->v to 0, * because SaveGamestate() doesn't write entnum 0 */ ED_ParseEdict (start, ent); if (ClientsMode == 1 || ClientsMode == 2 || ClientsMode == 3) ent->v.stats_restored = true; // link it into the bsp tree if (!ent->free) { if (entnum >= sv.num_edicts) { /* This is necessary to restore "generated" edicts which were * not available during the map parsing by ED_LoadFromFile(). * This includes items dropped by monsters, items "dropped" by * an item_spawner such as the "prizes" in the Temple of Mars * (romeric5), a health sphere generated by the Crusader's * Holy Strength ability, or a respawning-candidate killed * monster in the expansion pack's nightmare mode. -- THOMAS */ /* Moved this into the if (!ent->free) construct: less debug * chatter. Even if this skips a free edict in between, the * skipped free edict wasn't parsed by ED_LoadFromFile() and * it will remain as a freed edict. (There is no harm because * we are dealing with extra edicts not originally present in * the map.) -- O.S. */ Con_DPrintf("%s: entnum %d >= sv.num_edicts (%d)\n", __thisfunc__, entnum, sv.num_edicts); sv.num_edicts = entnum + 1; } SV_LinkEdict (ent, false); if (ent->v.modelindex && ent->v.model) { i = SV_ModelIndex(PR_GetString(ent->v.model)); if (i != ent->v.modelindex) { ent->v.modelindex = i; auto_correct = true; } } } } } fclose (f); if (ClientsMode == 0) { sv.time = playtime; sv.paused = true; *sv_globals.serverflags = svs.serverflags; RestoreClients (0); } else if (ClientsMode == 2) { sv.time = playtime; } else if (ClientsMode == 3) { sv.time = playtime; *sv_globals.serverflags = svs.serverflags; RestoreClients (3); } if (ClientsMode != 1 && auto_correct) { Con_DPrintf("*** Auto-corrected model indexes!\n"); } // for (i = 0; i < NUM_SPAWN_PARMS; i++) // svs.clients->spawn_parms[i] = spawn_parms[i]; return 0; } //============================================================================ /* ====================== Host_Name_f ====================== */ static void Host_Name_f (void) { char newName[32]; char *pdest; if (cmd_source == src_command) return; if (Cmd_Argc () == 2) q_strlcpy (newName, Cmd_Argv(1), sizeof(newName)); else q_strlcpy (newName, Cmd_Args(), sizeof(newName)); newName[15] = 0; // client_t structure actually says name[32]. //this is for the fuckers who put braces in the name causing loadgame to crash. pdest = strchr(newName,'{'); if (pdest) { *pdest = 0; //zap the brace Con_Printf ("Illegal char in name removed!\n"); } if (host_client->name[0] && strcmp(host_client->name, "unconnected") ) if (strcmp(host_client->name, newName) != 0) Con_Printf ("%s renamed to %s\n", host_client->name, newName); strcpy (host_client->name, newName); host_client->edict->v.netname = PR_SetEngineString(host_client->name); // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_updatename); MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients); MSG_WriteString (&sv.reliable_datagram, host_client->name); } static void Host_Class_f (void) { float newClass; if (cmd_source == src_command) return; if (Cmd_Argc () == 2) newClass = atof(Cmd_Argv(1)); else newClass = atof(Cmd_Args()); if (newClass < 0 || newClass > MAX_PLAYER_CLASS) { Con_Printf("Invalid player class.\n"); return; } #if ENABLE_OLD_DEMO if (gameflags & GAME_OLD_DEMO && (newClass != CLASS_PALADIN && newClass != CLASS_THEIF)) { Con_Printf("That class is not available in this demo version.\n"); return; } #endif /* OLD_DEMO */ if (newClass == CLASS_DEMON) { if (!(gameflags & GAME_PORTALS)) { Con_Printf("That class is only available in the mission pack.\n"); return; } if (sv.active && (progs->crc != PROGS_V112_CRC)) { /* FIXME: This isn't right!!! A custom progs can actually * support 5 classes and can have v1.11 structures at the * same time. I don't know a way to detect any such thing, * hence this lame solution! -- O.S. */ Con_Printf("progs.dat in use doesn't support that class.\n"); return; } } if (sv.loadgame || host_client->playerclass) { if (host_client->edict->v.playerclass) newClass = host_client->edict->v.playerclass; else if (host_client->playerclass) newClass = host_client->playerclass; } host_client->playerclass = newClass; host_client->edict->v.playerclass = newClass; // Change the weapon model used *sv_globals.self = EDICT_TO_PROG(host_client->edict); PR_ExecuteProgram (*sv_globals.ClassChangeWeapon); // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_updateclass); MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients); MSG_WriteByte (&sv.reliable_datagram, (byte)newClass); } static void Host_Say (qboolean teamonly) { int j; client_t *client; client_t *save; const char *p; char text[64], *p2; qboolean quoted; qboolean fromServer = false; if (cmd_source == src_command) { fromServer = true; teamonly = false; } if (Cmd_Argc () < 2) return; save = host_client; p = Cmd_Args(); // remove quotes if present quoted = false; if (*p == '\"') { p++; quoted = true; } // turn on color set 1 if (!fromServer) q_snprintf (text, sizeof(text), "\001%s: %s", save->name, p); else q_snprintf (text, sizeof(text), "\001<%s> %s", hostname.string, p); // check length & truncate if necessary j = (int) strlen(text); if (j >= (int) sizeof(text) - 1) { text[sizeof(text) - 2] = '\n'; text[sizeof(text) - 1] = '\0'; } else { p2 = text + j; while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)) ) { if (p2[-1] == '\"' && quoted) quoted = false; p2[-1] = '\0'; p2--; } p2[0] = '\n'; p2[1] = '\0'; } for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) { if (!client || !client->active || !client->spawned) continue; if (teamplay.integer && teamonly && client->edict->v.team != save->edict->v.team) continue; host_client = client; SV_ClientPrintf (0, "%s", text); } host_client = save; Sys_Printf("%s", &text[1]); } static void Host_Say_f (void) { Host_Say(false); } static void Host_Say_Team_f (void) { Host_Say(true); } static void Host_Tell_f (void) { int j; client_t *client; client_t *save; const char *p; char text[64], *p2; qboolean quoted; if (cmd_source == src_command) return; if (Cmd_Argc () < 3) return; p = Cmd_Args(); // remove quotes if present quoted = false; if (*p == '\"') { p++; quoted = true; } q_snprintf (text, sizeof(text), "%s: %s", host_client->name, p); // check length & truncate if necessary j = (int) strlen(text); if (j >= (int) sizeof(text) - 1) { text[sizeof(text) - 2] = '\n'; text[sizeof(text) - 1] = '\0'; } else { p2 = text + j; while ((const char *)p2 > (const char *)text && (p2[-1] == '\r' || p2[-1] == '\n' || (p2[-1] == '\"' && quoted)) ) { if (p2[-1] == '\"' && quoted) quoted = false; p2[-1] = '\0'; p2--; } p2[0] = '\n'; p2[1] = '\0'; } save = host_client; for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) { if (!client->active || !client->spawned) continue; if (q_strcasecmp(client->name, Cmd_Argv(1))) continue; host_client = client; SV_ClientPrintf (0, "%s", text); break; } host_client = save; } /* ================== Host_Color_f ================== */ static void Host_Color_f (void) { int top, bottom; int playercolor; if (cmd_source == src_command) return; if (Cmd_Argc() == 2) top = bottom = atoi(Cmd_Argv(1)); else { top = atoi(Cmd_Argv(1)); bottom = atoi(Cmd_Argv(2)); } top &= 15; if (top > 13) top = 13; bottom &= 15; if (bottom > 13) bottom = 13; playercolor = top*16 + bottom; host_client->colors = playercolor; host_client->edict->v.team = bottom + 1; // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors); MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients); MSG_WriteByte (&sv.reliable_datagram, host_client->colors); } /* ================== Host_Kill_f ================== */ static void Host_Kill_f (void) { if (cmd_source == src_command) return; if (sv_player->v.health <= 0 && sv_player->v.deadflag != DEAD_NO) { SV_ClientPrintf (0, "Can't suicide -- already dead!\n"); return; } *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (*sv_globals.ClientKill); } /* ================== Host_Pause_f ================== */ static void Host_Pause_f (void) { if (cmd_source == src_command) return; if (!pausable.integer) SV_ClientPrintf (0, "Pause not allowed.\n"); else { sv.paused ^= 1; if (sv.paused) { SV_BroadcastPrintf ("%s paused the game\n", PR_GetString(sv_player->v.netname)); } else { SV_BroadcastPrintf ("%s unpaused the game\n",PR_GetString(sv_player->v.netname)); } // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_setpause); MSG_WriteByte (&sv.reliable_datagram, sv.paused); } } //=========================================================================== /* ================== Host_PreSpawn_f ================== */ static void Host_PreSpawn_f (void) { if (cmd_source == src_command) { Con_Printf ("prespawn is not valid from the console\n"); return; } if (host_client->spawned) { Con_Printf ("prespawn not valid -- already spawned\n"); return; } SZ_Write (&host_client->message, sv.signon.data, sv.signon.cursize); MSG_WriteByte (&host_client->message, svc_signonnum); MSG_WriteByte (&host_client->message, 2); host_client->sendsignon = true; } /* ================== Host_Spawn_f ================== */ static void Host_Spawn_f (void) { int i; client_t *client; edict_t *ent; if (cmd_source == src_command) { Con_Printf ("spawn is not valid from the console\n"); return; } if (host_client->spawned) { Con_Printf ("Spawn not valid -- already spawned\n"); return; } // send all current names, colors, and frag counts SZ_Clear (&host_client->message); // run the entrance script if (sv.loadgame) { // loaded games are fully inited already // if this is the last client to be connected, unpause sv.paused = false; } else { // set up the edict ent = host_client->edict; sv.paused = false; if (!ent->v.stats_restored || deathmatch.integer) { memset (&ent->v, 0, progs->entityfields * 4); //ent->v.colormap = NUM_FOR_EDICT(ent); ent->v.team = (host_client->colors & 15) + 1; ent->v.netname = PR_SetEngineString(host_client->name); ent->v.playerclass = host_client->playerclass; // copy spawn parms out of the client_t for (i = 0; i < NUM_SPAWN_PARMS; i++) sv_globals.parm[i] = host_client->spawn_parms[i]; // call the spawn function *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (*sv_globals.ClientConnect); if ((Sys_DoubleTime() - NET_QSocketGetTime(host_client->netconnection)) <= sv.time) Sys_Printf ("%s entered the game\n", host_client->name); PR_ExecuteProgram (*sv_globals.PutClientInServer); } } // send time of update MSG_WriteByte (&host_client->message, svc_time); MSG_WriteFloat (&host_client->message, sv.time); for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++) { MSG_WriteByte (&host_client->message, svc_updatename); MSG_WriteByte (&host_client->message, i); MSG_WriteString (&host_client->message, client->name); MSG_WriteByte (&host_client->message, svc_updateclass); MSG_WriteByte (&host_client->message, i); MSG_WriteByte (&host_client->message, client->playerclass); MSG_WriteByte (&host_client->message, svc_updatefrags); MSG_WriteByte (&host_client->message, i); MSG_WriteShort (&host_client->message, client->old_frags); MSG_WriteByte (&host_client->message, svc_updatecolors); MSG_WriteByte (&host_client->message, i); MSG_WriteByte (&host_client->message, client->colors); } // send all current light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { MSG_WriteByte (&host_client->message, svc_lightstyle); MSG_WriteByte (&host_client->message, (char)i); MSG_WriteString (&host_client->message, sv.lightstyles[i]); } // // send some stats // MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_TOTALSECRETS); MSG_WriteLong (&host_client->message, *sv_globals.total_secrets); MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_TOTALMONSTERS); MSG_WriteLong (&host_client->message, *sv_globals.total_monsters); MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_SECRETS); MSG_WriteLong (&host_client->message, *sv_globals.found_secrets); MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_MONSTERS); MSG_WriteLong (&host_client->message, *sv_globals.killed_monsters); SV_UpdateEffects(&host_client->message); // // send a fixangle // Never send a roll angle, because savegames can catch the server // in a state where it is expecting the client to correct the angle // and it won't happen if the game was just loaded, so you wind up // with a permanent head tilt ent = EDICT_NUM( 1 + (host_client - svs.clients) ); MSG_WriteByte (&host_client->message, svc_setangle); for (i = 0; i < 2; i++) MSG_WriteAngle (&host_client->message, ent->v.angles[i] ); MSG_WriteAngle (&host_client->message, 0); SV_WriteClientdataToMessage (host_client, sv_player, &host_client->message); MSG_WriteByte (&host_client->message, svc_signonnum); MSG_WriteByte (&host_client->message, 3); host_client->sendsignon = true; } /* ================== Host_Begin_f ================== */ static void Host_Begin_f (void) { if (cmd_source == src_command) { Con_Printf ("begin is not valid from the console\n"); return; } host_client->spawned = true; } //=========================================================================== /* ================== Host_Kick_f Kicks a user off of the server ================== */ static void Host_Kick_f (void) { const char *who; const char *message = NULL; client_t *save; int i; qboolean byNumber = false; if (cmd_source == src_command) { if (!sv.active) { Con_Printf("Server not active\n"); return; } } else if (*sv_globals.deathmatch) return; save = host_client; if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0) { i = atof(Cmd_Argv(2)) - 1; if (i < 0 || i >= svs.maxclients) return; if (!svs.clients[i].active) return; host_client = &svs.clients[i]; byNumber = true; } else { for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if (!host_client->active) continue; if (q_strcasecmp(host_client->name, Cmd_Argv(1)) == 0) break; } } if (i < svs.maxclients) { if (cmd_source == src_command) who = "Console"; else who = save->name; // can't kick yourself! if (host_client == save) return; if (Cmd_Argc() > 2) { message = COM_Parse(Cmd_Args()); if (byNumber) { message++; // skip the # while (*message == ' ') // skip white space message++; message += strlen(Cmd_Argv(2)); // skip the number } while (*message && *message == ' ') message++; } if (message) SV_ClientPrintf (0, "Kicked by %s: %s\n", who, message); else SV_ClientPrintf (0, "Kicked by %s\n", who); SV_DropClient (false); } host_client = save; } /* =============================================================================== DEBUGGING TOOLS =============================================================================== */ /* ================== Host_Give_f ================== */ static void Host_Give_f (void) { const char *t; int v; if (cmd_source == src_command) return; if (*sv_globals.deathmatch || skill.integer > 2) return; t = Cmd_Argv(1); v = atoi (Cmd_Argv(2)); switch (t[0]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (t[0] >= '2') sv_player->v.items = (int)sv_player->v.items | (IT_SHOTGUN << (t[0] - '2')); break; case 's': case 'n': case 'l': case 'r': case 'm': break; case 'h': sv_player->v.health = v; break; case 'c': case 'p': break; } } //============================================================================= /* ================== Host_InitCommands ================== */ void Host_InitCommands (void) { Cmd_AddCommand ("status", Host_Status_f); Cmd_AddCommand ("quit", Host_Quit_f); Cmd_AddCommand ("god", Host_God_f); Cmd_AddCommand ("notarget", Host_Notarget_f); Cmd_AddCommand ("map", Host_Map_f); Cmd_AddCommand ("restart", Host_Restart_f); Cmd_AddCommand ("changelevel", Host_Changelevel_f); Cmd_AddCommand ("changelevel2", Host_Changelevel2_f); Cmd_AddCommand ("name", Host_Name_f); Cmd_AddCommand ("playerclass", Host_Class_f); Cmd_AddCommand ("noclip", Host_Noclip_f); Cmd_AddCommand ("say", Host_Say_f); Cmd_AddCommand ("say_team", Host_Say_Team_f); Cmd_AddCommand ("tell", Host_Tell_f); Cmd_AddCommand ("color", Host_Color_f); Cmd_AddCommand ("kill", Host_Kill_f); Cmd_AddCommand ("pause", Host_Pause_f); Cmd_AddCommand ("spawn", Host_Spawn_f); Cmd_AddCommand ("begin", Host_Begin_f); Cmd_AddCommand ("prespawn", Host_PreSpawn_f); Cmd_AddCommand ("kick", Host_Kick_f); Cmd_AddCommand ("ping", Host_Ping_f); Cmd_AddCommand ("load", Host_Loadgame_f); Cmd_AddCommand ("save", Host_Savegame_f); Cmd_AddCommand ("deletesave", Host_DeleteSave_f); Cmd_AddCommand ("give", Host_Give_f); } engine/hexen2/server/sys_amiga.c000066400000000000000000000360071444734033100171530ustar00rootroot00000000000000/* sys_amiga.c -- Amiga system interface code * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2012 Szilard Biro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "debuglog.h" #include #include #include #include #if defined(__LP64__) #define MIN_STACK_SIZE 0x200000 /* 2 MB stack */ #elif defined(PLATFORM_AMIGAOS3) #define MIN_STACK_SIZE 0x40000 /* 256 KB stack */ #else #define MIN_STACK_SIZE 0x100000 /* 1 MB stack */ #endif #ifdef __CLIB2__ int __stack_size = MIN_STACK_SIZE; #else int __stack = MIN_STACK_SIZE; #if defined(PLATFORM_AMIGAOS3) && defined(__libnix__) /* this pulls in swapstack.o */ /* NOTE: swapstack.o was a stray object in old versions * of libnix, need manually adding to libnix20.a */ extern void __stkinit(void); void * __x = __stkinit; #endif #endif #ifdef __AROS__ #include "incstack.h" /* The problem here is that our real main never returns: the exit * point of program is either Sys_Quit() or Sys_Error(). One way * of making the real main return to the incstack.h main wrapper * is setjmp()'ing in real main and longjmp()'ing from the actual * exit points, avoiding exit(). */ #include static jmp_buf exit_buf; static int my_rc = 0; #define HAVE_AROS_MAIN_WRAPPER #endif #define MIN_MEM_ALLOC 0x0800000 #define STD_MEM_ALLOC 0x1000000 cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; qboolean isDedicated = true; /* compatibility */ static double starttime; static qboolean first = true; static BPTR amiga_stdin, amiga_stdout; #define MODE_RAW 1 #define MODE_NORMAL 0 struct timerequest *timerio; struct MsgPort *timerport; #if defined(__MORPHOS__) || defined(__VBCC__) struct Library *TimerBase; #else struct Device *TimerBase; #endif /* =============================================================================== FILE IO =============================================================================== */ int Sys_mkdir(const char *path, qboolean crash) { BPTR lock = CreateDir((const STRPTR) path); if (lock) { UnLock(lock); return 0; } if (IoErr() == ERROR_OBJECT_EXISTS) return 0; if (crash) Sys_Error("Unable to create directory %s", path); return -1; } int Sys_rmdir (const char *path) { if (DeleteFile((const STRPTR) path) != 0) return 0; return -1; } int Sys_unlink (const char *path) { if (DeleteFile((const STRPTR) path) != 0) return 0; return -1; } int Sys_rename (const char *oldp, const char *newp) { if (Rename((const STRPTR) oldp, (const STRPTR) newp) != 0) return 0; return -1; } long Sys_filesize (const char *path) { long size = -1; BPTR lock = Lock((const STRPTR) path, ACCESS_READ); if (lock) { struct FileInfoBlock *fib = (struct FileInfoBlock*) AllocDosObject(DOS_FIB, NULL); if (fib != NULL) { if (Examine(lock, fib)) { size = fib->fib_Size; } FreeDosObject(DOS_FIB, fib); } UnLock(lock); } return size; } int Sys_FileType (const char *path) { int type = FS_ENT_NONE; BPTR lock = Lock((const STRPTR) path, ACCESS_READ); if (lock) { struct FileInfoBlock *fib = (struct FileInfoBlock*) AllocDosObject(DOS_FIB, NULL); if (fib != NULL) { if (Examine(lock, fib)) { if (fib->fib_DirEntryType >= 0) type = FS_ENT_DIRECTORY; else type = FS_ENT_FILE; } FreeDosObject(DOS_FIB, fib); } UnLock(lock); } return type; } #define COPY_READ_BUFSIZE 8192 /* BUFSIZ */ int Sys_CopyFile (const char *frompath, const char *topath) { char buf[COPY_READ_BUFSIZE]; BPTR in, out; struct FileInfoBlock *fib; struct DateStamp stamp; LONG remaining, count; in = Open((const STRPTR) frompath, MODE_OLDFILE); if (!in) { Con_Printf ("%s: unable to open %s\n", __thisfunc__, frompath); return 1; } fib = (struct FileInfoBlock*) AllocDosObject(DOS_FIB, NULL); if (fib != NULL) { if (ExamineFH(in, fib) == 0) remaining = -1; else { remaining = fib->fib_Size; stamp = fib->fib_Date; } FreeDosObject(DOS_FIB, fib); if (remaining < 0) { Con_Printf ("%s: can't determine size for %s\n", __thisfunc__, frompath); Close(in); return 1; } } else { Con_Printf ("%s: can't allocate FileInfoBlock for %s\n", __thisfunc__, frompath); Close(in); return 1; } out = Open((const STRPTR) topath, MODE_NEWFILE); if (!out) { Con_Printf ("%s: unable to open %s\n", __thisfunc__, topath); Close(in); return 1; } while (remaining) { if (remaining < sizeof(buf)) count = remaining; else count = sizeof(buf); if (Read(in, buf, count) == -1) break; if (Write(out, buf, count) == -1) break; remaining -= count; } Close(in); Close(out); if (remaining != 0) return 1; SetFileDate(topath, &stamp); return 0; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only, not a dirent struct. this is what we presently need in this engine. ================================================= */ static struct AnchorPath apath; static BPTR oldcurrentdir; static STRPTR pattern_str; static STRPTR pattern_helper (const char *pat) { char *pdup; const char *p; int n; for (n = 0, p = pat; *p != '\0'; ++p, ++n) { if ((p = strchr (p, '*')) == NULL) break; } if (n == 0) pdup = Z_Strdup(pat); else { /* replace each "*" by "#?" */ n += (int) strlen(pat) + 1; pdup = (char *) Z_Malloc(n, Z_MAINZONE); for (n = 0, p = pat; *p != '\0'; ++p, ++n) { if (*p != '*') pdup[n] = *p; else { pdup[n] = '#'; ++n; pdup[n] = '?'; } } pdup[n] = '\0'; } return (STRPTR) pdup; } const char *Sys_FindFirstFile (const char *path, const char *pattern) { BPTR newdir; if (pattern_str) Sys_Error ("Sys_FindFirst without FindClose"); memset(&apath, 0, sizeof(apath)); newdir = Lock((const STRPTR) path, SHARED_LOCK); if (newdir) oldcurrentdir = CurrentDir(newdir); else return NULL; pattern_str = pattern_helper (pattern); if (MatchFirst((const STRPTR) pattern_str, &apath) == 0) { if (apath.ap_Info.fib_DirEntryType < 0) return (const char *) (apath.ap_Info.fib_FileName); } return Sys_FindNextFile(); } const char *Sys_FindNextFile (void) { if (!pattern_str) return NULL; while (MatchNext(&apath) == 0) { if (apath.ap_Info.fib_DirEntryType < 0) return (const char *) (apath.ap_Info.fib_FileName); } return NULL; } void Sys_FindClose (void) { if (!pattern_str) return; MatchEnd(&apath); UnLock(CurrentDir(oldcurrentdir)); oldcurrentdir = 0; Z_Free(pattern_str); pattern_str = NULL; } /* =============================================================================== SYSTEM IO =============================================================================== */ /* ================ Sys_Init ================ */ static void Sys_Init (void) { if ((timerport = CreateMsgPort())) { if ((timerio = (struct timerequest *)CreateIORequest(timerport, sizeof(struct timerequest)))) { if (OpenDevice((STRPTR) TIMERNAME, UNIT_MICROHZ, (struct IORequest *) timerio, 0) == 0) { #if defined(__MORPHOS__) || defined(__VBCC__) TimerBase = (struct Library *)timerio->tr_node.io_Device; #else TimerBase = timerio->tr_node.io_Device; #endif } else { DeleteIORequest((struct IORequest *)timerio); DeleteMsgPort(timerport); } } else { DeleteMsgPort(timerport); } } if (!TimerBase) Sys_Error("Can't open timer.device"); /* 1us wait, for timer cleanup success */ timerio->tr_node.io_Command = TR_ADDREQUEST; timerio->tr_time.tv_secs = 0; timerio->tr_time.tv_micro = 1; SendIO((struct IORequest *) timerio); WaitIO((struct IORequest *) timerio); amiga_stdout = Output(); amiga_stdin = Input(); SetMode(amiga_stdin, MODE_RAW); } static void Sys_AtExit (void) { if (amiga_stdin) SetMode(amiga_stdin, MODE_NORMAL); if (TimerBase) { /* if (!CheckIO((struct IORequest *) timerio) { AbortIO((struct IORequest *) timerio); WaitIO((struct IORequest *) timerio); } */ WaitIO((struct IORequest *) timerio); CloseDevice((struct IORequest *) timerio); DeleteIORequest((struct IORequest *) timerio); DeleteMsgPort(timerport); TimerBase = NULL; } } #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const unsigned char *p; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (con_debuglog) { LOG_Print (ERROR_PREFIX); LOG_Print (text); LOG_Print ("\n\n"); } Host_Shutdown (); for (p = (const unsigned char *) text2; *p; p++) putc (*p, stderr); for (p = (const unsigned char *) text ; *p; p++) putc (*p, stderr); putc ('\n', stderr); putc ('\n', stderr); #ifdef HAVE_AROS_MAIN_WRAPPER Sys_AtExit(); my_rc = 1; longjmp(exit_buf, 1); #else exit (1); #endif } void Sys_PrintTerm (const char *msgtxt) { const unsigned char *p; if (sys_nostdout.integer) return; for (p = (const unsigned char *) msgtxt; *p; p++) putc (*p, stdout); } void Sys_Quit (void) { Host_Shutdown(); #ifdef HAVE_AROS_MAIN_WRAPPER Sys_AtExit(); longjmp(exit_buf, 1); #else exit (0); #endif } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { struct timeval tp; double now; GetSysTime(&tp); now = tp.tv_secs + tp.tv_micro / 1e6; if (first) { first = false; starttime = now; return 0.0; } return now - starttime; } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; time_t t; struct tm *l; int val; if (!buf) buf = strbuf; t = time(NULL); l = localtime(&t); val = l->tm_mon + 1; /* tm_mon: months since January [0,11] */ buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = l->tm_mday; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = l->tm_year / 100 + 19; /* tm_year: #years since 1900. */ buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = l->tm_year % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = l->tm_hour; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = l->tm_min; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = l->tm_sec; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } /* ================ Sys_ConsoleInput ================ */ const char *Sys_ConsoleInput (void) { static char con_text[256]; static int textlen; char c; while (WaitForChar(amiga_stdin,10)) { Read (amiga_stdin, &c, 1); if (c == '\n' || c == '\r') { Write(amiga_stdout, "\n", 1); con_text[textlen] = '\0'; textlen = 0; return con_text; } else if (c == 8) { if (textlen) { Write(amiga_stdout, "\b \b", 3); textlen--; con_text[textlen] = '\0'; } continue; } con_text[textlen] = c; textlen++; if (textlen < (int) sizeof(con_text)) { Write(amiga_stdout, &c, 1); con_text[textlen] = '\0'; } else { // buffer is full textlen = 0; con_text[0] = '\0'; Sys_PrintTerm("\nConsole input too long!\n"); break; } } return NULL; } void Sys_Sleep (unsigned long msecs) { timerio->tr_node.io_Command = TR_ADDREQUEST; timerio->tr_time.tv_secs = msecs / 1000; timerio->tr_time.tv_micro = (msecs * 1000) % 1000000; SendIO((struct IORequest *) timerio); WaitIO((struct IORequest *) timerio); } static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { #if 1 int len = q_strlcpy(dst, "PROGDIR:", dstsize); if (len < (int)dstsize) return 0; return -1; #else if (NameFromLock(GetProgramDir(), (STRPTR) dst, dstsize) != 0) return 0; return -1; #endif } static void PrintVersion (void) { Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); Sys_Printf ("Hexen II dedicated server %4.2f (%s)\n", ENGINE_VERSION, PLATFORM_STRING); Sys_Printf ("More info / sending bug reports: http://uhexen2.sourceforge.net\n"); } static const char *help_strings[] = { " [-v | --version] Display version information", #ifndef DEMOBUILD # if defined(H2MP) " [-noportals] Disable the mission pack support", # else " [-portals | -h2mp ] Run the Portal of Praevus mission pack", # endif #endif " [-heapsize Bytes] Heapsize (memory to allocate)", NULL }; static void PrintHelp (const char *name) { int i = 0; Sys_Printf ("Usage: %s [options]\n", name); while (help_strings[i]) { Sys_PrintTerm (help_strings[i]); Sys_PrintTerm ("\n"); i++; } Sys_PrintTerm ("\n"); } /* =============================================================================== MAIN =============================================================================== */ static quakeparms_t parms; static char cwd[MAX_OSPATH]; int main (int argc, char **argv) { int i; double time, oldtime; ULONG availMem; #ifdef HAVE_AROS_MAIN_WRAPPER if (setjmp(exit_buf)) return my_rc; #endif PrintVersion(); if (argc > 1) { for (i = 1; i < argc; i++) { if ( !(strcmp(argv[i], "-v")) || !(strcmp(argv[i], "-version" )) || !(strcmp(argv[i], "--version")) ) { return 0; } else if ( !(strcmp(argv[i], "-h")) || !(strcmp(argv[i], "-help" )) || !(strcmp(argv[i], "-?")) || !(strcmp(argv[i], "--help")) ) { PrintHelp(argv[0]); return 0; } } } /* initialize the host params */ memset (&parms, 0, sizeof(parms)); parms.basedir = cwd; parms.userdir = cwd; parms.argc = argc; parms.argv = argv; parms.errstate = 0; host_parms = &parms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); LOG_Init (&parms); Sys_Printf("basedir is: %s\n", parms.basedir); Sys_Printf("userdir is: %s\n", parms.userdir); COM_ValidateByteorder (); availMem = AvailMem(MEMF_ANY|MEMF_LARGEST); parms.memsize = (availMem < STD_MEM_ALLOC)? MIN_MEM_ALLOC : STD_MEM_ALLOC; i = COM_CheckParm ("-heapsize"); if (i && i < com_argc-1) parms.memsize = atoi (com_argv[i+1]) * 1024; parms.membase = malloc (parms.memsize); if (!parms.membase) Sys_Error ("Insufficient memory."); #ifndef HAVE_AROS_MAIN_WRAPPER atexit (Sys_AtExit); #endif Sys_Init (); Host_Init(); oldtime = Sys_DoubleTime (); /* main window message loop */ while (1) { time = Sys_DoubleTime (); if (time - oldtime < sys_ticrate.value ) { Sys_Sleep(1); continue; } Host_Frame (time - oldtime); oldtime = time; } return 0; } engine/hexen2/server/sys_dos.c000066400000000000000000000505151444734033100166620ustar00rootroot00000000000000/* sys_dos.c -- DOS system interface code. * from quake1 source with adaptations for uhexen2. * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include #include #include #include #include #include #include #include #include #include #include /* for _crt0_startup_flags */ #include #include #include #include "quakedef.h" #include "dosisms.h" #include "debuglog.h" #define MIN_MEM_ALLOC 0x0800000 /* minimum 8 mb */ #define STD_MEM_ALLOC 0x1000000 /* standart 16 mb */ /* 2000-07-16, DOSQuake/DJGPP mem detection fix by * Norberto Alfredo Bensa */ int _crt0_startup_flags = _CRT0_FLAG_UNIX_SBRK; int end_of_memory; static qboolean lockmem, lockunlockmem, unlockmem; static int win95; static quakeparms_t quakeparms; static int sys_checksum; /* 2000-07-28, DOSQuake "time running too fast" fix * by Norberto Alfredo Bensa. Set USE_UCLOCK_TIME * to 0 if you want to use the old original code. * See Sys_DoubleTime() for information on uclock() */ #define USE_UCLOCK_TIME 1 static void Sys_InitTime (void); #if !USE_UCLOCK_TIME static double curtime = 0.0; static double lastcurtime = 0.0; static double oldtime = 0.0; #endif /* ! USE_UCLOCK_TIME */ cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; qboolean isDedicated = true; /* compatibility */ static int minmem; float fptest_temp; extern char start_of_memory __asm__("start"); //============================================================================= // this is totally dependent on cwsdpmi putting the stack right after tge // global data // This does evil things in a Win95 DOS box!!! #if 0 extern byte end; #define CHECKBYTE 0xed static void Sys_InitStackCheck (void) { int i; for (i = 0; i < 128*1024; i++) (&end)[i] = CHECKBYTE; } void Sys_StackCheck (void) { int i; for (i = 0; i < 128*1024; i++) { if ( (&end)[i] != CHECKBYTE ) break; } Con_Printf ("%i undisturbed stack bytes\n", i); if (end != CHECKBYTE) Sys_Error ("System stack overflow!"); } #endif //============================================================================= static void Sys_DetectWin95 (void) { __dpmi_regs r; r.x.ax = 0x160a; /* Get Windows Version */ __dpmi_int(0x2f, &r); if (r.x.ax || r.h.bh < 4) /* Not windows or earlier than Win95 */ { win95 = 0; lockmem = true; lockunlockmem = false; unlockmem = true; } else { win95 = 1; lockunlockmem = COM_CheckParm ("-winlockunlock"); if (lockunlockmem) lockmem = true; else lockmem = COM_CheckParm ("-winlock"); unlockmem = lockmem && !lockunlockmem; } } static void *dos_getmaxlockedmem (int *size) { __dpmi_free_mem_info meminfo; __dpmi_meminfo info; int working_size; void *working_memory; int last_locked; int i, j, extra, allocsize; static const char msg[] = "Locking data..."; byte *x; unsigned long ul; // first lock all the current executing image so the locked count will // be accurate. It doesn't hurt to lock the memory multiple times last_locked = __djgpp_selector_limit + 1; info.size = last_locked - 4096; info.address = __djgpp_base_address + 4096; if (lockmem) { if (__dpmi_lock_linear_region(&info)) { Sys_Error ("Lock of current memory at 0x%lx for %ldKb failed!\n", info.address, info.size / 1024); } } __dpmi_get_free_memory_information(&meminfo); if (!win95) /* Not windows or earlier than Win95 */ { ul = meminfo.maximum_locked_page_allocation_in_pages * 4096; } else { ul = meminfo.largest_available_free_block_in_bytes - LEAVE_FOR_CACHE; } if (ul > 0x7fffffff) ul = 0x7fffffff; /* limit to 2GB */ working_size = (int) ul; working_size &= ~0xffff; /* Round down to 64K */ working_size += 0x10000; do { working_size -= 0x10000; /* Decrease 64K and try again */ working_memory = sbrk(working_size); } while (working_memory == (void *)-1); extra = 0xfffc - ((unsigned)sbrk(0) & 0xffff); if (extra > 0) { sbrk(extra); working_size += extra; } // now grab the memory info.address = last_locked + __djgpp_base_address; if (!win95) { info.size = __djgpp_selector_limit + 1 - last_locked; while (info.size > 0 && __dpmi_lock_linear_region(&info)) { info.size -= 0x1000; working_size -= 0x1000; sbrk(-0x1000); } } else { /* Win95 section */ j = COM_CheckParm("-winmem"); // minmem = MIN_MEM_ALLOC; minmem = STD_MEM_ALLOC; if (j && j < com_argc - 1) { allocsize = ((int)(atoi(com_argv[j + 1]))) * 0x100000 + LOCKED_FOR_MALLOC; if (allocsize < (minmem + LOCKED_FOR_MALLOC)) allocsize = minmem + LOCKED_FOR_MALLOC; } else { allocsize = minmem + LOCKED_FOR_MALLOC; } if (!lockmem) { // we won't lock, just sbrk the memory info.size = allocsize; goto UpdateSbrk; } // lock the memory down write (STDOUT_FILENO, msg, strlen (msg)); for (j = allocsize; j > (minmem + LOCKED_FOR_MALLOC); j -= 0x100000) { info.size = j; if (!__dpmi_lock_linear_region(&info)) goto Locked; write (STDOUT_FILENO, ".", 1); } // finally, try with the absolute minimum amount for (i = 0; i < 10; i++) { info.size = minmem + LOCKED_FOR_MALLOC; if (!__dpmi_lock_linear_region(&info)) goto Locked; } Sys_Error ("Can't lock memory; %lu Mb lockable RAM required. " "Try shrinking smartdrv.", info.size / 0x100000); Locked: UpdateSbrk: info.address += info.size; info.address -= __djgpp_base_address + 4; // ending point, malloc align working_size = info.address - (int)working_memory; sbrk(info.address - (int)sbrk(0)); // negative adjustment } if (lockunlockmem) { __dpmi_unlock_linear_region (&info); printf ("Locked and unlocked %d Mb data\n", working_size / 0x100000); } else if (lockmem) { printf ("Locked %d Mb data\n", working_size / 0x100000); } else { printf ("Allocated %d Mb data\n", working_size / 0x100000); } // touch all the memory to make sure it's there. The 16-page skip is to // keep Win 95 from thinking we're trying to page ourselves in (we are // doing that, of course, but there's no reason we shouldn't) x = (byte *)working_memory; for (j = 0; j < 4; j++) { for (i = 0; i < (working_size - 16 * 0x1000); i += 4) { sys_checksum += *(int *)&x[i]; sys_checksum += *(int *)&x[i + 16 * 0x1000]; } } // give some of what we locked back for malloc before returning. Done // by cheating and passing a negative value to sbrk working_size -= LOCKED_FOR_MALLOC; sbrk( -(LOCKED_FOR_MALLOC)); *size = working_size; return working_memory; } int Sys_mkdir (const char *path, qboolean crash) { int rc = mkdir (path, 0777); if (rc != 0 && errno == EEXIST) rc = 0; if (rc != 0 && crash) Sys_Error("Unable to create directory %s", path); return rc; } int Sys_rmdir (const char *path) { return rmdir(path); } int Sys_unlink (const char *path) { return remove(path); } int Sys_rename (const char *oldp, const char *newp) { return rename(oldp, newp); } long Sys_filesize (const char *path) { struct ffblk f; if (findfirst(path, &f, FA_ARCH | FA_RDONLY) != 0) return -1; return (long) f.ff_fsize; } int Sys_FileType (const char *path) { int attr = _chmod(path, 0); /* Root directories on some non-local drives (e.g. CD-ROM) as well as devices may fail _chmod, but we are not interested in such cases. */ if (attr == -1) return FS_ENT_NONE; if (attr & _A_SUBDIR) return FS_ENT_DIRECTORY; if (attr & _A_VOLID) /* we shouldn't hit this! */ return FS_ENT_DIRECTORY; return FS_ENT_FILE; } #define COPY_READ_BUFSIZE 8192 /* BUFSIZ */ int Sys_CopyFile (const char *frompath, const char *topath) { char buf[COPY_READ_BUFSIZE]; int in, out; long remaining, count; struct ftime ft; in = open (frompath, O_RDONLY | O_BINARY); if (in < 0) { Con_Printf ("%s: unable to open %s\n", __thisfunc__, frompath); return 1; } remaining = filelength (in); if (remaining < 0) { Con_Printf ("%s: %s failed filelength()\n", __thisfunc__, frompath); close (in); return 1; } out = open (topath, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, 0666); if (out < 0) { Con_Printf ("%s: unable to create %s\n", __thisfunc__, topath); close (in); return 1; } while (remaining) { if (remaining < sizeof(buf)) count = remaining; else count = sizeof(buf); if (read(in, buf, count) < 0) break; if (write(out, buf, count) < 0) break; remaining -= count; } if (remaining == 0) { /* restore the file's timestamp */ if (getftime(in, &ft) == 0) setftime(out, &ft); } close (in); close (out); return remaining; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only. ================================================= */ static struct ffblk finddata; static int findhandle = -1; static char findstr[MAX_OSPATH]; const char *Sys_FindFirstFile (const char *path, const char *pattern) { if (findhandle == 0) Sys_Error ("Sys_FindFirst without FindClose"); q_snprintf (findstr, sizeof(findstr), "%s/%s", path, pattern); memset (&finddata, 0, sizeof(finddata)); findhandle = findfirst(findstr, &finddata, FA_ARCH | FA_RDONLY); if (findhandle == 0) return finddata.ff_name; return NULL; } const char *Sys_FindNextFile (void) { if (findhandle != 0) return NULL; if (findnext(&finddata) == 0) return finddata.ff_name; return NULL; } void Sys_FindClose (void) { findhandle = -1; } const char *Sys_ConsoleInput (void) { static char con_text[256]; static int textlen = 0; char ch; if (! kbhit()) return NULL; ch = getche(); switch (ch) { case '\r': putch('\n'); if (textlen) { con_text[textlen] = '\0'; textlen = 0; return con_text; } break; case '\b': putch(' '); if (textlen) { textlen--; putch('\b'); } break; default: con_text[textlen] = ch; textlen = (textlen + 1) & 0xff; break; } return NULL; } void Sys_Sleep (unsigned long msecs) { usleep (msecs * 1000); } static void Sys_Init (void) { MaskExceptions (); Sys_SetFPCW (); #if !USE_UCLOCK_TIME dos_outportb(0x43, 0x34); // set system timer to mode 2 dos_outportb(0x40, 0); // for Sys_DoubleTime() dos_outportb(0x40, 0); #endif /* ! USE_UCLOCK_TIME */ Sys_InitTime (); _go32_interrupt_stack_size = 4 * 1024; _go32_rmcb_stack_size = 4 * 1024; } void Sys_Shutdown (void) { if (unlockmem) { dos_unlockmem (&start_of_memory, end_of_memory - (int)&start_of_memory); dos_unlockmem (quakeparms.membase, quakeparms.memsize); } } // ======================================================================= // General routines // ======================================================================= void Sys_PrintTerm (const char *msgtxt) { const unsigned char *p; if (sys_nostdout.integer) return; for (p = (const unsigned char *) msgtxt; *p; p++) putc (*p, stdout); } static void Sys_AtExit (void) { // shutdown only once (so Sys_Error can call this function to shutdown, then // print the error message, then call exit without exit calling this function // again) Sys_Shutdown(); } void Sys_Quit (void) { Host_Shutdown (); exit (0); } #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const unsigned char *p; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (con_debuglog) { LOG_Print (ERROR_PREFIX); LOG_Print (text); LOG_Print ("\n\n"); } Host_Shutdown (); for (p = (const unsigned char *) text2; *p; p++) putc (*p, stderr); for (p = (const unsigned char *) text ; *p; p++) putc (*p, stderr); putc ('\n', stderr); putc ('\n', stderr); // Sys_AtExit is called by exit to shutdown the system exit (1); } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { #if USE_UCLOCK_TIME /* From DJGPP uclock() man page : uclock() returns the number of uclock ticks since an arbitrary time, actually, since the first call to uclock(), which itself returns zero. The number of tics per second is UCLOCKS_PER_SEC (declared in time.h as 1193180.) uclock() is provided for very high-resulution timing. uclock_t is a 64-bit integer. It is currently accurate to better than 1 microsecond (actually about 840 nanoseconds). You cannot time across two midnights with this implementation, giving a maximum useful period of 48 hours and an effective limit of 24 hours. Casting to a 32-bit integer limits its usefulness to about an hour before 32 bits will wrap. Also note that uclock reprograms the interval timer in your PC to act as a rate generator rather than a square wave generator. I've had no problems running in this mode all the time, but if you notice strange things happening with the clock (losing time) after using uclock, check to see if this is the cause of the problem. Windows 3.X doesn't allow to reprogram the timer so the values returned by uclock() there are incorrect. DOS and Windows 9X don't have this problem. Windows NT, 2000 and XP attempt to use the rdtsc feature of newer CPUs instead of the interval timer because the timer tick and interval timer are not coordinated. During calibration the SIGILL signal handler is replaced to protect against systems which do not support or allow rdtsc. If rdtsc is available, uclock will keep the upper bits of the returned value consistent with the bios tick counter by re-calibration if needed. If rdtsc is not available, these systems fall back to interval timer usage, which may show an absolute error of 65536 uclock ticks in the values and not be monotonically increasing. */ return (double) uclock() / (double) UCLOCKS_PER_SEC; #else int r; unsigned t, tick; double ft, time; static int sametimecount; Sys_PushFPCW_SetHigh (); t = *(unsigned short*)real2ptr(0x46c) * 65536; dos_outportb(0x43, 0); // latch time r = dos_inportb(0x40); r |= dos_inportb(0x40) << 8; r = (r - 1) & 0xffff; tick = *(unsigned short*)real2ptr(0x46c) * 65536; if ((tick != t) && (r & 0x8000)) t = tick; ft = (double) (t + (65536 - r)) / 1193200.0; time = ft - oldtime; oldtime = ft; if (time < 0) { if (time > -3000.0) time = 0.0; else time += 3600.0; } curtime += time; if (curtime == lastcurtime) { sametimecount++; if (sametimecount > 100000) { curtime += 1.0; sametimecount = 0; } } else { sametimecount = 0; } lastcurtime = curtime; Sys_PopFPCW (); return curtime; #endif /* ! USE_UCLOCK_TIME */ } /* ================ Sys_InitTime ================ */ static void Sys_InitTime (void) { #if !USE_UCLOCK_TIME int j; Sys_DoubleTime (); oldtime = curtime; j = COM_CheckParm("-starttime"); if (j && j < com_argc - 1) { curtime = (double) (atof(com_argv[j+1])); } else { curtime = 0.0; } lastcurtime = curtime; #endif /* ! USE_UCLOCK_TIME */ } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; struct _dosdate_t d; struct _dostime_t t; unsigned int val; if (!buf) buf = strbuf; _dos_getdate(&d); _dos_gettime(&t); val = d.month; buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = d.day; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = d.year / 100; buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = d.year % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = t.hour; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = t.minute; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = t.second; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } /* ================ Sys_GetMemory ================ */ static void Sys_GetMemory (void) { int j, tsize; j = COM_CheckParm("-mem"); if (j && j < com_argc - 1) { quakeparms.memsize = (int) (atof(com_argv[j + 1]) * 1024 * 1024); quakeparms.membase = malloc (quakeparms.memsize); } else { quakeparms.membase = dos_getmaxlockedmem (&quakeparms.memsize); } printf("malloc'd: %d\n", quakeparms.memsize); j = COM_CheckParm ("-heapsize"); if (j && j < com_argc - 1) { tsize = atoi (com_argv[j + 1]) * 1024; if (tsize < quakeparms.memsize) quakeparms.memsize = tsize; } } /* ================ Sys_PageInProgram walks the text, data, and bss to make sure it's all paged in so that the actual physical memory detected by Sys_GetMemory is correct. ================ */ static void Sys_PageInProgram (void) { int i, j; end_of_memory = (int)sbrk(0); if (lockmem) { if (dos_lockmem ((void *)&start_of_memory, end_of_memory - (int)&start_of_memory)) Sys_Error ("Couldn't lock text and data"); } if (lockunlockmem) { dos_unlockmem((void *)&start_of_memory, end_of_memory - (int)&start_of_memory); printf ("Locked and unlocked %d Mb image\n", (end_of_memory - (int)&start_of_memory) / 0x100000); } else if (lockmem) { printf ("Locked %d Mb image\n", (end_of_memory - (int)&start_of_memory) / 0x100000); } else { printf ("Loaded %d Mb image\n", (end_of_memory - (int)&start_of_memory) / 0x100000); } // touch the entire image, doing the 16-page skip so Win95 doesn't think we're // trying to page ourselves in for (j = 0; j < 4; j++) { for (i = (int)&start_of_memory; i < (end_of_memory - 16 * 0x1000); i += 4) { sys_checksum += *(int *)i; sys_checksum += *(int *)(i + 16 * 0x1000); } } } /* ================ Sys_NoFPUExceptionHandler ================ */ static void Sys_NoFPUExceptionHandler (int whatever) { const char err[] = "\nError: Hexen II requires a floating-point processor\n"; const unsigned char *p; for (p = (const unsigned char *) err; *p; p++) putc (*p, stderr); exit (1); } /* ================ Sys_DefaultExceptionHandler ================ */ static void Sys_DefaultExceptionHandler (int whatever) { } static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { char *tmp; if (getcwd(dst, dstsize - 1) == NULL) return -1; tmp = dst; while (*tmp != 0) tmp++; while (*tmp == 0 && tmp != dst) { --tmp; if (tmp != dst && (*tmp == '/' || *tmp == '\\')) *tmp = 0; } return 0; } static void PrintVersion (void) { printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); printf ("Hexen II dedicated server %4.2f (%s)\n", ENGINE_VERSION, PLATFORM_STRING); printf ("More info / sending bug reports: http://uhexen2.sourceforge.net\n"); } /* ================ main ================ */ static char cwd[MAX_OSPATH]; int main (int argc, char **argv) { double time, oldtime, newtime; PrintVersion(); // make sure there's an FPU signal(SIGNOFP, Sys_NoFPUExceptionHandler); signal(SIGABRT, Sys_DefaultExceptionHandler); signal(SIGALRM, Sys_DefaultExceptionHandler); signal(SIGKILL, Sys_DefaultExceptionHandler); signal(SIGQUIT, Sys_DefaultExceptionHandler); signal(SIGINT, Sys_DefaultExceptionHandler); if (fptest_temp >= 0.0) fptest_temp += 0.1; /* initialize the host params */ memset (&quakeparms, 0, sizeof(quakeparms)); quakeparms.basedir = cwd; quakeparms.userdir = cwd; quakeparms.argc = argc; quakeparms.argv = argv; quakeparms.errstate = 0; host_parms = &quakeparms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); LOG_Init (&quakeparms); COM_ValidateByteorder (); Sys_DetectWin95 (); Sys_PageInProgram (); Sys_GetMemory (); atexit (Sys_AtExit); // in case we crash Sys_Init (); // Sys_InitStackCheck (); Host_Init(); // Sys_StackCheck (); // Con_Printf ("Top of stack: 0x%x\n", &time); oldtime = Sys_DoubleTime (); while (1) { newtime = Sys_DoubleTime (); time = newtime - oldtime; if (time < sys_ticrate.value) continue; Host_Frame (time); // Sys_StackCheck (); oldtime = newtime; } } engine/hexen2/server/sys_os2.c000066400000000000000000000237711444734033100166040ustar00rootroot00000000000000/* * sys_os2.c -- OS/2 system interface code * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2016 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "debuglog.h" #define INCL_DOS #define INCL_DOSERRORS #ifdef __EMX__ #define INCL_KBD #define INCL_VIO #endif #include #include #include #ifdef __WATCOMC__ #include #endif #include #define MIN_MEM_ALLOC 0x0800000 #define STD_MEM_ALLOC 0x1000000 cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; qboolean isDedicated = true; /* compatibility */ /* =============================================================================== FILE IO =============================================================================== */ int Sys_mkdir (const char *path, qboolean crash) { FILESTATUS3 fs; APIRET rc = DosCreateDir(path, NULL); if (rc == NO_ERROR) return 0; if ((DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)) == NO_ERROR) && (fs.attrFile & FILE_DIRECTORY)) { return 0; /* dir exists */ } if (crash) Sys_Error("Unable to create directory %s (ERR: %lu)", path, rc); return -1; } int Sys_rmdir (const char *path) { APIRET rc = DosDeleteDir(path); return (rc == NO_ERROR)? 0 : -1; } int Sys_unlink (const char *path) { APIRET rc = DosDelete(path); return (rc == NO_ERROR)? 0 : -1; } int Sys_rename (const char *oldp, const char *newp) { APIRET rc = DosMove(oldp, newp); return (rc == NO_ERROR)? 0 : -1; } long Sys_filesize (const char *path) { FILESTATUS3 fs; APIRET rc = DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)); if (rc != NO_ERROR) return -1; if (fs.attrFile & FILE_DIRECTORY) return -1; return (long)fs.cbFile; } int Sys_FileType (const char *path) { FILESTATUS3 fs; APIRET rc = DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)); if (rc != NO_ERROR) return FS_ENT_NONE; if (fs.attrFile & FILE_DIRECTORY) return FS_ENT_DIRECTORY; return FS_ENT_FILE; } int Sys_CopyFile (const char *frompath, const char *topath) { APIRET rc = DosCopy(frompath, topath, DCPY_EXISTING); return (rc == NO_ERROR)? 0 : -1; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only, not a dirent struct. this is what we presently need in this engine. ================================================= */ static HDIR findhandle = HDIR_CREATE; static FILEFINDBUF3 findbuffer; static char findstr[MAX_OSPATH]; const char *Sys_FindFirstFile (const char *path, const char *pattern) { ULONG cnt = 1; APIRET rc; if (findhandle != HDIR_CREATE) Sys_Error ("Sys_FindFirst without FindClose"); q_snprintf (findstr, sizeof(findstr), "%s/%s", path, pattern); findbuffer.oNextEntryOffset = 0; rc = DosFindFirst(findstr, &findhandle, FILE_NORMAL, &findbuffer, sizeof(findbuffer), &cnt, FIL_STANDARD); if (rc != NO_ERROR) { findhandle = HDIR_CREATE; findbuffer.oNextEntryOffset = 0; return NULL; } if (findbuffer.attrFile & FILE_DIRECTORY) return Sys_FindNextFile(); return findbuffer.achName; } const char *Sys_FindNextFile (void) { APIRET rc; ULONG cnt; if (findhandle == HDIR_CREATE) return NULL; while (1) { cnt = 1; rc = DosFindNext(findhandle, &findbuffer, sizeof(findbuffer), &cnt); if (rc != NO_ERROR) return NULL; if (!(findbuffer.attrFile & FILE_DIRECTORY)) return findbuffer.achName; } return NULL; } void Sys_FindClose (void) { if (findhandle != HDIR_CREATE) { DosFindClose(findhandle); findhandle = HDIR_CREATE; findbuffer.oNextEntryOffset = 0; } } /* =============================================================================== SYSTEM IO =============================================================================== */ #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const unsigned char *p; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (con_debuglog) { LOG_Print (ERROR_PREFIX); LOG_Print (text); LOG_Print ("\n\n"); } Host_Shutdown (); for (p = (const unsigned char *) text2; *p; p++) putc (*p, stderr); for (p = (const unsigned char *) text ; *p; p++) putc (*p, stderr); putc ('\n', stderr); putc ('\n', stderr); exit (1); } void Sys_PrintTerm (const char *msgtxt) { const unsigned char *p; if (sys_nostdout.integer) return; for (p = (const unsigned char *) msgtxt; *p; p++) putc (*p, stdout); } void Sys_Quit (void) { Host_Shutdown(); exit (0); } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { union i64 { QWORD qw; long long ll; }; static qboolean first = true; static ULONG ticks_per_sec; static union i64 start; union i64 now; if (first) { first = false; DosTmrQueryFreq(&ticks_per_sec); DosTmrQueryTime(&start.qw); return 0.0; } DosTmrQueryTime(&now.qw); return (double)(now.ll - start.ll) / (double)ticks_per_sec; } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; DATETIME dt; unsigned int val; if (!buf) buf = strbuf; DosGetDateTime (&dt); val = dt.month; buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = dt.day; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = dt.year / 100; buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = dt.year % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = dt.hours; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = dt.minutes; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = dt.seconds; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } /* ================ Sys_ConsoleInput ================ */ #ifdef __EMX__ int putch (int c) { char ch = c; VioWrtTTY(&ch, 1, 0); return c; } int kbhit (void) { KBDKEYINFO k; if (KbdPeek(&k, 0)) return 0; return (k.fbStatus & KBDTRF_FINAL_CHAR_IN); } #endif const char *Sys_ConsoleInput (void) { static char con_text[256]; static int textlen = 0; char ch; if (! kbhit()) return NULL; ch = getche(); switch (ch) { case '\r': putch('\n'); if (textlen) { con_text[textlen] = '\0'; textlen = 0; return con_text; } break; case '\b': putch(' '); if (textlen) { textlen--; putch('\b'); } break; default: con_text[textlen] = ch; textlen = (textlen + 1) & 0xff; break; } return NULL; } void Sys_Sleep (unsigned long msecs) { DosSleep (msecs); } static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { ULONG l, drv; if (dstsize < 8) return -1; l = dstsize - 3; if (DosQueryCurrentDir(0, (PBYTE) dst + 3, &l) != NO_ERROR) return -1; DosQueryCurrentDisk(&drv, &l); dst[0] = drv + 'A' - 1; dst[1] = ':'; dst[2] = '\\'; return 0; } static void PrintVersion (void) { Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); Sys_Printf ("Hexen II dedicated server %4.2f (%s)\n", ENGINE_VERSION, PLATFORM_STRING); Sys_Printf ("More info / sending bug reports: http://uhexen2.sourceforge.net\n"); } static const char *help_strings[] = { " [-v | --version] Display version information", #ifndef DEMOBUILD # if defined(H2MP) " [-noportals] Disable the mission pack support", # else " [-portals | -h2mp ] Run the Portal of Praevus mission pack", # endif #endif " [-heapsize Bytes] Heapsize (memory to allocate)", NULL }; static void PrintHelp (const char *name) { int i = 0; Sys_Printf ("Usage: %s [options]\n", name); while (help_strings[i]) { Sys_PrintTerm (help_strings[i]); Sys_PrintTerm ("\n"); i++; } Sys_PrintTerm ("\n"); } /* =============================================================================== MAIN =============================================================================== */ static quakeparms_t parms; static char cwd[MAX_OSPATH]; int main (int argc, char **argv) { int i; double time, oldtime; PrintVersion(); if (argc > 1) { for (i = 1; i < argc; i++) { if ( !(strcmp(argv[i], "-v")) || !(strcmp(argv[i], "-version" )) || !(strcmp(argv[i], "--version")) ) { exit(0); } else if ( !(strcmp(argv[i], "-h")) || !(strcmp(argv[i], "-help" )) || !(strcmp(argv[i], "-?")) || !(strcmp(argv[i], "--help")) ) { PrintHelp(argv[0]); exit (0); } } } /* initialize the host params */ memset (&parms, 0, sizeof(parms)); parms.basedir = cwd; parms.userdir = cwd; parms.argc = argc; parms.argv = argv; parms.errstate = 0; host_parms = &parms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); LOG_Init (&parms); Sys_Printf("basedir is: %s\n", parms.basedir); COM_ValidateByteorder (); parms.memsize = STD_MEM_ALLOC; i = COM_CheckParm ("-heapsize"); if (i && i < com_argc-1) parms.memsize = atoi (com_argv[i+1]) * 1024; parms.membase = malloc (parms.memsize); if (!parms.membase) Sys_Error ("Insufficient memory."); Host_Init(); oldtime = Sys_DoubleTime (); /* main window message loop */ while (1) { time = Sys_DoubleTime (); if (time - oldtime < sys_ticrate.value ) { DosSleep (1); continue; } Host_Frame (time - oldtime); oldtime = time; } return 0; } engine/hexen2/server/sys_unix.c000066400000000000000000000276171444734033100170670ustar00rootroot00000000000000/* sys_unix.c -- Unix system interface code * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2001 contributors of the Anvil of Thyrion project * Copyright (C) 2004-2005 Steven Atkinson * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "userdir.h" #include "debuglog.h" #include #include #if DO_USERDIRS #include #endif #include #include #include #include #include #include #include #include #include #define MIN_MEM_ALLOC 0x0800000 #define STD_MEM_ALLOC 0x1000000 cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; qboolean isDedicated = true; /* compatibility */ static double starttime; static qboolean first = true; /* =============================================================================== FILE IO =============================================================================== */ int Sys_mkdir (const char *path, qboolean crash) { int rc = mkdir (path, 0777); if (rc != 0 && errno == EEXIST) { struct stat st; if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) rc = 0; } if (rc != 0 && crash) { rc = errno; Sys_Error("Unable to create directory %s: %s", path, strerror(rc)); } return rc; } int Sys_rmdir (const char *path) { return rmdir(path); } int Sys_unlink (const char *path) { return unlink(path); } int Sys_rename (const char *oldp, const char *newp) { return rename(oldp, newp); } long Sys_filesize (const char *path) { struct stat st; if (stat(path, &st) != 0) return -1; if (! S_ISREG(st.st_mode)) return -1; return (long) st.st_size; } int Sys_FileType (const char *path) { /* if (access(path, R_OK) == -1) return 0; */ struct stat st; if (stat(path, &st) != 0) return FS_ENT_NONE; if (S_ISDIR(st.st_mode)) return FS_ENT_DIRECTORY; if (S_ISREG(st.st_mode)) return FS_ENT_FILE; return FS_ENT_NONE; } #define COPY_READ_BUFSIZE 8192 /* BUFSIZ */ int Sys_CopyFile (const char *frompath, const char *topath) { char buf[COPY_READ_BUFSIZE]; FILE *in, *out; struct stat st; struct utimbuf tm; /* off_t remaining, count;*/ size_t remaining, count; if (stat(frompath, &st) != 0) { Con_Printf ("%s: unable to stat %s\n", __thisfunc__, frompath); return 1; } in = fopen (frompath, "rb"); if (!in) { Con_Printf ("%s: unable to open %s\n", __thisfunc__, frompath); return 1; } out = fopen (topath, "wb"); if (!out) { Con_Printf ("%s: unable to create %s\n", __thisfunc__, topath); fclose (in); return 1; } remaining = st.st_size; while (remaining) { if (remaining < sizeof(buf)) count = remaining; else count = sizeof(buf); if (fread(buf, 1, count, in) != count) break; if (fwrite(buf, 1, count, out) != count) break; remaining -= count; } fclose (in); fclose (out); if (remaining == 0) { /* restore the file's timestamp */ tm.actime = time (NULL); tm.modtime = st.st_mtime; utime (topath, &tm); return 0; } return 1; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only, not a dirent struct. this is what we presently need in this engine. ================================================= */ static DIR *finddir; static struct dirent *finddata; static char *findpath, *findpattern; const char *Sys_FindFirstFile (const char *path, const char *pattern) { if (finddir) Sys_Error ("Sys_FindFirst without FindClose"); finddir = opendir (path); if (!finddir) return NULL; findpattern = Z_Strdup (pattern); findpath = Z_Strdup (path); return Sys_FindNextFile(); } const char *Sys_FindNextFile (void) { struct stat test; if (!finddir) return NULL; while ((finddata = readdir(finddir)) != NULL) { if (!fnmatch (findpattern, finddata->d_name, FNM_PATHNAME)) { if ( (stat(va("%s/%s", findpath, finddata->d_name), &test) == 0) && S_ISREG(test.st_mode)) return finddata->d_name; } } return NULL; } void Sys_FindClose (void) { if (finddir != NULL) { closedir(finddir); finddir = NULL; } if (findpath != NULL) { Z_Free (findpath); findpath = NULL; } if (findpattern != NULL) { Z_Free (findpattern); findpattern = NULL; } } /* =============================================================================== SYSTEM IO =============================================================================== */ #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const unsigned char *p; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (con_debuglog) { LOG_Print (ERROR_PREFIX); LOG_Print (text); LOG_Print ("\n\n"); } Host_Shutdown (); for (p = (const unsigned char *) text2; *p; p++) putc (*p, stderr); for (p = (const unsigned char *) text ; *p; p++) putc (*p, stderr); putc ('\n', stderr); putc ('\n', stderr); exit (1); } void Sys_PrintTerm (const char *msgtxt) { const unsigned char *p; if (sys_nostdout.integer) return; for (p = (const unsigned char *) msgtxt; *p; p++) putc (*p, stdout); } void Sys_Quit (void) { Host_Shutdown(); exit (0); } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { struct timeval tp; double now; gettimeofday (&tp, NULL); now = tp.tv_sec + tp.tv_usec / 1e6; if (first) { first = false; starttime = now; return 0.0; } return now - starttime; } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; time_t t; struct tm *l; int val; if (!buf) buf = strbuf; t = time(NULL); l = localtime(&t); val = l->tm_mon + 1; /* tm_mon: months since January [0,11] */ buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = l->tm_mday; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = l->tm_year / 100 + 19; /* tm_year: #years since 1900. */ buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = l->tm_year % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = l->tm_hour; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = l->tm_min; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = l->tm_sec; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } /* ================ Sys_ConsoleInput ================ */ const char *Sys_ConsoleInput (void) { static char con_text[256]; static int textlen; char c; fd_set set; struct timeval timeout; FD_ZERO (&set); FD_SET (0, &set); // stdin timeout.tv_sec = 0; timeout.tv_usec = 0; while (select (1, &set, NULL, NULL, &timeout)) { read (0, &c, 1); if (c == '\n' || c == '\r') { con_text[textlen] = '\0'; textlen = 0; return con_text; } else if (c == 8) { if (textlen) { textlen--; con_text[textlen] = '\0'; } continue; } con_text[textlen] = c; textlen++; if (textlen < (int) sizeof(con_text)) con_text[textlen] = '\0'; else { // buffer is full textlen = 0; con_text[0] = '\0'; Sys_PrintTerm("\nConsole input too long!\n"); break; } } return NULL; } void Sys_Sleep (unsigned long msecs) { usleep (msecs * 1000); } static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { char *tmp; if (getcwd(dst, dstsize - 1) == NULL) return -1; tmp = dst; while (*tmp != 0) tmp++; while (*tmp == 0 && tmp != dst) { --tmp; if (tmp != dst && *tmp == '/') *tmp = 0; } return 0; } #if DO_USERDIRS static int Sys_GetUserdir (char *dst, size_t dstsize) { size_t n; const char *home_dir = NULL; struct passwd *pwent; pwent = getpwuid(getuid()); if (pwent == NULL) perror("getpwuid"); else home_dir = pwent->pw_dir; if (home_dir == NULL) home_dir = getenv("HOME"); if (home_dir == NULL) return 1; /* what would be a maximum path for a file in the user's directory... * $HOME/AOT_USERDIR/game_dir/dirname1/dirname2/dirname3/filename.ext * still fits in the MAX_OSPATH == 256 definition, but just in case. */ n = strlen(home_dir) + strlen(AOT_USERDIR) + 50; if (n >= dstsize) { Sys_Error ("%s: Insufficient bufsize %d. Need at least %d.", __thisfunc__, (int)dstsize, (int)n); } q_snprintf (dst, dstsize, "%s/%s", home_dir, AOT_USERDIR); return 0; } #endif /* DO_USERDIRS */ static void PrintVersion (void) { Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); Sys_Printf ("Hexen II dedicated server %4.2f (%s)\n", ENGINE_VERSION, PLATFORM_STRING); Sys_Printf ("More info / sending bug reports: http://uhexen2.sourceforge.net\n"); } static const char *help_strings[] = { " [-v | --version] Display version information", #ifndef DEMOBUILD # if defined(H2MP) " [-noportals] Disable the mission pack support", # else " [-portals | -h2mp ] Run the Portal of Praevus mission pack", # endif #endif " [-heapsize Bytes] Heapsize (memory to allocate)", NULL }; static void PrintHelp (const char *name) { int i = 0; Sys_Printf ("Usage: %s [options]\n", name); while (help_strings[i]) { Sys_PrintTerm (help_strings[i]); Sys_PrintTerm ("\n"); i++; } Sys_PrintTerm ("\n"); } /* =============================================================================== MAIN =============================================================================== */ static quakeparms_t parms; static char cwd[MAX_OSPATH]; #if DO_USERDIRS static char userdir[MAX_OSPATH]; #endif int main (int argc, char **argv) { int i; double time, oldtime; PrintVersion(); if (argc > 1) { for (i = 1; i < argc; i++) { if ( !(strcmp(argv[i], "-v")) || !(strcmp(argv[i], "-version" )) || !(strcmp(argv[i], "--version")) ) { exit(0); } else if ( !(strcmp(argv[i], "-h")) || !(strcmp(argv[i], "-help" )) || !(strcmp(argv[i], "-?")) || !(strcmp(argv[i], "--help")) ) { PrintHelp(argv[0]); exit (0); } } } /* initialize the host params */ memset (&parms, 0, sizeof(parms)); parms.basedir = cwd; parms.userdir = cwd; parms.argc = argc; parms.argv = argv; parms.errstate = 0; host_parms = &parms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); #if DO_USERDIRS memset (userdir, 0, sizeof(userdir)); if (Sys_GetUserdir(userdir, sizeof(userdir)) != 0) Sys_Error ("Couldn't determine userspace directory"); Sys_mkdir(userdir, true); parms.userdir = userdir; #endif LOG_Init (&parms); Sys_Printf("basedir is: %s\n", parms.basedir); Sys_Printf("userdir is: %s\n", parms.userdir); COM_ValidateByteorder (); parms.memsize = STD_MEM_ALLOC; i = COM_CheckParm ("-heapsize"); if (i && i < com_argc-1) parms.memsize = atoi (com_argv[i+1]) * 1024; parms.membase = malloc (parms.memsize); if (!parms.membase) Sys_Error ("Insufficient memory."); Host_Init(); oldtime = Sys_DoubleTime (); /* main window message loop */ while (1) { time = Sys_DoubleTime (); if (time - oldtime < sys_ticrate.value ) { usleep (1000); continue; } Host_Frame (time - oldtime); oldtime = time; } return 0; } engine/hexen2/server/sys_win.c000066400000000000000000000246771444734033100167040ustar00rootroot00000000000000/* sys_win.c -- Windows system interface code * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "debuglog.h" #include #include #include #include #define MIN_MEM_ALLOC 0x0800000 #define STD_MEM_ALLOC 0x1000000 cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; qboolean isDedicated = true; /* compatibility */ #define TIME_WRAP_VALUE (~(DWORD)0) static DWORD starttime; static HANDLE hinput, houtput; /* =============================================================================== FILE IO =============================================================================== */ int Sys_mkdir (const char *path, qboolean crash) { if (CreateDirectory(path, NULL) != 0) return 0; if (GetLastError() == ERROR_ALREADY_EXISTS) return 0; if (crash) Sys_Error("Unable to create directory %s", path); return -1; } int Sys_rmdir (const char *path) { if (RemoveDirectory(path) != 0) return 0; return -1; } int Sys_unlink (const char *path) { if (DeleteFile(path) != 0) return 0; return -1; } int Sys_rename (const char *oldp, const char *newp) { if (MoveFile(oldp, newp) != 0) return 0; return -1; } long Sys_filesize (const char *path) { HANDLE fh; WIN32_FIND_DATA data; long size; fh = FindFirstFile(path, &data); if (fh == INVALID_HANDLE_VALUE) return -1; FindClose(fh); if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) return -1; // we're not dealing with gigabytes of files. // size should normally smaller than INT_MAX. // size = (data.nFileSizeHigh * (MAXDWORD + 1)) + data.nFileSizeLow; size = (long) data.nFileSizeLow; return size; } #ifndef INVALID_FILE_ATTRIBUTES #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #endif int Sys_FileType (const char *path) { DWORD result = GetFileAttributes(path); if (result == INVALID_FILE_ATTRIBUTES) return FS_ENT_NONE; if (result & FILE_ATTRIBUTE_DIRECTORY) return FS_ENT_DIRECTORY; return FS_ENT_FILE; } int Sys_CopyFile (const char *frompath, const char *topath) { /* 3rd param: whether to fail if 'topath' already exists */ if (CopyFile(frompath, topath, FALSE) != 0) return 0; return -1; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only, not a dirent struct. this is what we presently need in this engine. ================================================= */ static HANDLE findhandle = INVALID_HANDLE_VALUE; static WIN32_FIND_DATA finddata; static char findstr[MAX_OSPATH]; const char *Sys_FindFirstFile (const char *path, const char *pattern) { if (findhandle != INVALID_HANDLE_VALUE) Sys_Error ("Sys_FindFirst without FindClose"); q_snprintf (findstr, sizeof(findstr), "%s/%s", path, pattern); findhandle = FindFirstFile(findstr, &finddata); if (findhandle == INVALID_HANDLE_VALUE) return NULL; if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) return Sys_FindNextFile(); return finddata.cFileName; } const char *Sys_FindNextFile (void) { if (findhandle == INVALID_HANDLE_VALUE) return NULL; while (FindNextFile(findhandle, &finddata) != 0) { if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; return finddata.cFileName; } return NULL; } void Sys_FindClose (void) { if (findhandle != INVALID_HANDLE_VALUE) { FindClose(findhandle); findhandle = INVALID_HANDLE_VALUE; } } /* =============================================================================== SYSTEM IO =============================================================================== */ #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const char text3[] = "\n"; DWORD dummy; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (con_debuglog) { LOG_Print (ERROR_PREFIX); LOG_Print (text); LOG_Print ("\n\n"); } Host_Shutdown (); WriteFile(houtput, text2, strlen(text2), &dummy, NULL); WriteFile(houtput, text, strlen(text), &dummy, NULL); WriteFile(houtput, text3, strlen(text3), &dummy, NULL); exit (1); } void Sys_PrintTerm (const char *msgtxt) { DWORD dummy; if (sys_nostdout.integer) return; WriteFile(houtput, msgtxt, strlen(msgtxt), &dummy, NULL); } void Sys_Quit (void) { Host_Shutdown(); exit (0); } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { DWORD now, passed; now = timeGetTime(); if (now < starttime) /* wrapped? */ { passed = TIME_WRAP_VALUE - starttime; passed += now; } else { passed = now - starttime; } return (passed == 0) ? 0.0 : (passed / 1000.0); } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; SYSTEMTIME st; int val; if (!buf) buf = strbuf; GetLocalTime(&st); val = st.wMonth; buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = st.wDay; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = st.wYear / 100; buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = st.wYear % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = st.wHour; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = st.wMinute; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = st.wSecond; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } const char *Sys_ConsoleInput (void) { static char con_text[256]; static int textlen; INPUT_RECORD recs[1024]; int ch; DWORD dummy, numread, numevents; for ( ;; ) { if (GetNumberOfConsoleInputEvents(hinput, &numevents) == 0) Sys_Error ("Error getting # of console events"); if (! numevents) break; if (ReadConsoleInput(hinput, recs, 1, &numread) == 0) Sys_Error ("Error reading console input"); if (numread != 1) Sys_Error ("Couldn't read console input"); if (recs[0].EventType == KEY_EVENT) { if (recs[0].Event.KeyEvent.bKeyDown == FALSE) { ch = recs[0].Event.KeyEvent.uChar.AsciiChar; switch (ch) { case '\r': WriteFile(houtput, "\r\n", 2, &dummy, NULL); if (textlen != 0) { con_text[textlen] = 0; textlen = 0; return con_text; } break; case '\b': WriteFile(houtput, "\b \b", 3, &dummy, NULL); if (textlen != 0) textlen--; break; default: if (ch >= ' ') { WriteFile(houtput, &ch, 1, &dummy, NULL); con_text[textlen] = ch; textlen = (textlen + 1) & 0xff; } break; } } } } return NULL; } void Sys_Sleep (unsigned long msecs) { Sleep (msecs); } static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { char *tmp; size_t rc; rc = GetCurrentDirectory(dstsize, dst); if (rc == 0 || rc > dstsize) return -1; tmp = dst; while (*tmp != 0) tmp++; while (*tmp == 0 && tmp != dst) { --tmp; if (tmp != dst && (*tmp == '/' || *tmp == '\\')) *tmp = 0; } return 0; } static void PrintVersion (void) { Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); Sys_Printf ("Hexen II dedicated server %4.2f (%s)\n", ENGINE_VERSION, PLATFORM_STRING); Sys_Printf ("More info / sending bug reports: http://uhexen2.sourceforge.net\n"); } static const char *help_strings[] = { " [-v | --version] Display version information", #ifndef DEMOBUILD # if defined(H2MP) " [-noportals] Disable the mission pack support", # else " [-portals | -h2mp ] Run the Portal of Praevus mission pack", # endif #endif " [-heapsize Bytes] Heapsize (memory to allocate)", NULL }; static void PrintHelp (const char *name) { int i = 0; Sys_Printf ("Usage: %s [options]\n", name); while (help_strings[i]) { Sys_PrintTerm (help_strings[i]); Sys_PrintTerm ("\n"); i++; } Sys_PrintTerm ("\n"); } /* =============================================================================== MAIN =============================================================================== */ static quakeparms_t parms; static char cwd[MAX_OSPATH]; int main (int argc, char **argv) { int i; double time, oldtime; hinput = GetStdHandle (STD_INPUT_HANDLE); houtput = GetStdHandle (STD_OUTPUT_HANDLE); PrintVersion(); if (argc > 1) { for (i = 1; i < argc; i++) { if ( !(strcmp(argv[i], "-v")) || !(strcmp(argv[i], "-version" )) || !(strcmp(argv[i], "--version")) ) { exit(0); } else if ( !(strcmp(argv[i], "-h")) || !(strcmp(argv[i], "-help" )) || !(strcmp(argv[i], "--help")) || !(strcmp(argv[i], "-?")) ) { PrintHelp(argv[0]); exit (0); } } } /* initialize the host params */ memset (&parms, 0, sizeof(parms)); parms.basedir = cwd; parms.userdir = cwd; /* no userdir on win32 */ parms.argc = argc; parms.argv = argv; parms.errstate = 0; host_parms = &parms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); LOG_Init (&parms); Sys_Printf("basedir is: %s\n", parms.basedir); Sys_Printf("userdir is: %s\n", parms.userdir); COM_ValidateByteorder (); parms.memsize = STD_MEM_ALLOC; i = COM_CheckParm ("-heapsize"); if (i && i < com_argc-1) parms.memsize = atoi (com_argv[i+1]) * 1024; parms.membase = malloc (parms.memsize); if (!parms.membase) Sys_Error ("Insufficient memory."); timeBeginPeriod (1); /* 1 ms timer precision */ starttime = timeGetTime (); Host_Init(); oldtime = Sys_DoubleTime (); /* main window message loop */ while (1) { time = Sys_DoubleTime (); if (time - oldtime < sys_ticrate.value ) { Sleep (1); continue; } Host_Frame (time - oldtime); oldtime = time; } return 0; } engine/hexen2/sv_effect.c000066400000000000000000001047611444734033100156400ustar00rootroot00000000000000/* sv_effect.c -- Client side effects. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // HEADER FILES ------------------------------------------------------------ #include "quakedef.h" // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- #define MAX_EFFECT_ENTITIES 256 // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern cvar_t sv_ce_scale; extern cvar_t sv_ce_max_size; // PUBLIC DATA DEFINITIONS ------------------------------------------------- // PRIVATE DATA DEFINITIONS ------------------------------------------------ // CODE -------------------------------------------------------------------- static void SV_ClearEffects (void) { memset(sv.Effects, 0, sizeof(sv.Effects)); } // All changes need to be in SV_SendEffect(), SV_ParseEffect(), // SV_SaveEffects(), SV_LoadEffects(), CL_ParseEffect() static void SV_SendEffect (sizebuf_t *sb, int idx) { qboolean DoTest; vec3_t TestO1, Diff; float Size, TestDistance; int i, count; if (sb == &sv.reliable_datagram && sv_ce_scale.value > 0) DoTest = true; else DoTest = false; VectorClear(TestO1); TestDistance = 0; switch (sv.Effects[idx].type) { case CE_RAIN: case CE_SNOW: DoTest = false; break; case CE_FOUNTAIN: DoTest = false; break; case CE_QUAKE: VectorCopy(sv.Effects[idx].ef.Quake.origin, TestO1); TestDistance = 700; break; case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK1: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: case CE_FLAMESTREAM: case CE_ACID_MUZZFL: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: VectorCopy(sv.Effects[idx].ef.Smoke.origin, TestO1); TestDistance = 250; break; case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_ACID_HIT: case CE_LBALL_EXPL: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_FBOOM: case CE_BRN_BOUNCE: case CE_LSHOCK: case CE_BOMB: case CE_FLOOR_EXPLOSION3: VectorCopy(sv.Effects[idx].ef.Smoke.origin, TestO1); TestDistance = 250; break; case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_RED_FLASH: VectorCopy(sv.Effects[idx].ef.Smoke.origin, TestO1); TestDistance = 250; break; case CE_RIDER_DEATH: DoTest = false; break; case CE_GRAVITYWELL: DoTest = false; break; case CE_TELEPORTERPUFFS: VectorCopy(sv.Effects[idx].ef.Teleporter.origin, TestO1); TestDistance = 350; break; case CE_TELEPORTERBODY: VectorCopy(sv.Effects[idx].ef.Teleporter.origin, TestO1); TestDistance = 350; break; case CE_BONESHARD: case CE_BONESHRAPNEL: VectorCopy(sv.Effects[idx].ef.Missile.origin, TestO1); TestDistance = 600; break; case CE_CHUNK: VectorCopy(sv.Effects[idx].ef.Chunk.origin, TestO1); TestDistance = 600; break; default: PR_RunError ("%s: bad type", __thisfunc__); break; } if (!DoTest) count = 1; else { count = svs.maxclients; TestDistance = (float)TestDistance * sv_ce_scale.value; TestDistance *= TestDistance; } for (i = 0 ; i < count ; i++) { if (DoTest) { if (svs.clients[i].active) { sb = &svs.clients[i].datagram; VectorSubtract(svs.clients[i].edict->v.origin, TestO1, Diff); Size = (Diff[0]*Diff[0]) + (Diff[1]*Diff[1]) + (Diff[2]*Diff[2]); if (Size > TestDistance) continue; if (sv_ce_max_size.value > 0 && sb->cursize > sv_ce_max_size.value) continue; } else continue; } MSG_WriteByte (sb, svc_start_effect); MSG_WriteByte (sb, idx); MSG_WriteByte (sb, sv.Effects[idx].type); switch (sv.Effects[idx].type) { case CE_RAIN: MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.min_org[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.min_org[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.min_org[2]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.max_org[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.max_org[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.max_org[2]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.e_size[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.e_size[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.e_size[2]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.dir[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.dir[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.dir[2]); MSG_WriteShort(sb, sv.Effects[idx].ef.Rain.color); MSG_WriteShort(sb, sv.Effects[idx].ef.Rain.count); MSG_WriteFloat(sb, sv.Effects[idx].ef.Rain.wait); break; case CE_SNOW: MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.min_org[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.min_org[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.min_org[2]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.max_org[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.max_org[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.max_org[2]); MSG_WriteByte(sb, sv.Effects[idx].ef.Rain.flags); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.dir[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.dir[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Rain.dir[2]); MSG_WriteByte(sb, sv.Effects[idx].ef.Rain.count); //MSG_WriteShort(sb, sv.Effects[idx].ef.Rain.veer); break; case CE_FOUNTAIN: MSG_WriteCoord(sb, sv.Effects[idx].ef.Fountain.pos[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Fountain.pos[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Fountain.pos[2]); MSG_WriteAngle(sb, sv.Effects[idx].ef.Fountain.angle[0]); MSG_WriteAngle(sb, sv.Effects[idx].ef.Fountain.angle[1]); MSG_WriteAngle(sb, sv.Effects[idx].ef.Fountain.angle[2]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Fountain.movedir[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Fountain.movedir[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Fountain.movedir[2]); MSG_WriteShort(sb, sv.Effects[idx].ef.Fountain.color); MSG_WriteByte(sb, sv.Effects[idx].ef.Fountain.cnt); break; case CE_QUAKE: MSG_WriteCoord(sb, sv.Effects[idx].ef.Quake.origin[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Quake.origin[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Quake.origin[2]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Quake.radius); break; case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK1: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: case CE_FLAMESTREAM: case CE_ACID_MUZZFL: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: MSG_WriteCoord(sb, sv.Effects[idx].ef.Smoke.origin[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Smoke.origin[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Smoke.origin[2]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Smoke.velocity[0]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Smoke.velocity[1]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Smoke.velocity[2]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Smoke.framelength); /* smoke frame is a mission pack thing only. */ if (sv_protocol > PROTOCOL_RAVEN_111) MSG_WriteFloat(sb, sv.Effects[idx].ef.Smoke.frame); break; case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_FLOOR_EXPLOSION3: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_ACID_HIT: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_LBALL_EXPL: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: case CE_FBOOM: case CE_BOMB: case CE_BRN_BOUNCE: case CE_LSHOCK: MSG_WriteCoord(sb, sv.Effects[idx].ef.Smoke.origin[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Smoke.origin[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Smoke.origin[2]); break; case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_RED_FLASH: MSG_WriteCoord(sb, sv.Effects[idx].ef.Smoke.origin[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Smoke.origin[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Smoke.origin[2]); break; case CE_RIDER_DEATH: MSG_WriteCoord(sb, sv.Effects[idx].ef.RD.origin[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.RD.origin[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.RD.origin[2]); break; case CE_TELEPORTERPUFFS: MSG_WriteCoord(sb, sv.Effects[idx].ef.Teleporter.origin[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Teleporter.origin[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Teleporter.origin[2]); break; case CE_TELEPORTERBODY: MSG_WriteCoord(sb, sv.Effects[idx].ef.Teleporter.origin[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Teleporter.origin[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Teleporter.origin[2]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Teleporter.velocity[0][0]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Teleporter.velocity[0][1]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Teleporter.velocity[0][2]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Teleporter.skinnum); break; case CE_BONESHARD: case CE_BONESHRAPNEL: MSG_WriteCoord(sb, sv.Effects[idx].ef.Missile.origin[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Missile.origin[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Missile.origin[2]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Missile.velocity[0]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Missile.velocity[1]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Missile.velocity[2]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Missile.angle[0]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Missile.angle[1]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Missile.angle[2]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Missile.avelocity[0]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Missile.avelocity[1]); MSG_WriteFloat(sb, sv.Effects[idx].ef.Missile.avelocity[2]); break; case CE_GRAVITYWELL: MSG_WriteCoord(sb, sv.Effects[idx].ef.RD.origin[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.RD.origin[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.RD.origin[2]); MSG_WriteShort(sb, sv.Effects[idx].ef.RD.color); MSG_WriteFloat(sb, sv.Effects[idx].ef.RD.lifetime); break; case CE_CHUNK: MSG_WriteCoord(sb, sv.Effects[idx].ef.Chunk.origin[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Chunk.origin[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Chunk.origin[2]); MSG_WriteByte (sb, sv.Effects[idx].ef.Chunk.type); MSG_WriteCoord(sb, sv.Effects[idx].ef.Chunk.srcVel[0]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Chunk.srcVel[1]); MSG_WriteCoord(sb, sv.Effects[idx].ef.Chunk.srcVel[2]); MSG_WriteByte (sb, sv.Effects[idx].ef.Chunk.numChunks); // Con_Printf ("Adding %d chunks on server...\n", sv.Effects[idx].Chunk.numChunks); break; default: PR_RunError ("%s: bad type", __thisfunc__); break; } } } void SV_UpdateEffects (sizebuf_t *sb) { int idx; for (idx = 0 ; idx < MAX_EFFECTS ; idx++) { if (sv.Effects[idx].type) SV_SendEffect(sb, idx); } } // All changes need to be in SV_SendEffect(), SV_ParseEffect(), // SV_SaveEffects(), SV_LoadEffects(), CL_ParseEffect() void SV_ParseEffect (sizebuf_t *sb) { int idx; byte effect; effect = G_FLOAT(OFS_PARM0); for (idx = 0 ; idx < MAX_EFFECTS ; idx++) { if (!sv.Effects[idx].type || (sv.Effects[idx].expire_time && sv.Effects[idx].expire_time <= sv.time)) break; } if (idx >= MAX_EFFECTS) { PR_RunError ("MAX_EFFECTS reached"); return; } // Con_Printf("Effect #%d\n", idx); memset(&sv.Effects[idx], 0, sizeof(struct EffectT)); sv.Effects[idx].type = effect; G_FLOAT(OFS_RETURN) = idx; switch (effect) { case CE_RAIN: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Rain.min_org); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Rain.max_org); VectorCopy(G_VECTOR(OFS_PARM3), sv.Effects[idx].ef.Rain.e_size); VectorCopy(G_VECTOR(OFS_PARM4), sv.Effects[idx].ef.Rain.dir); sv.Effects[idx].ef.Rain.color = G_FLOAT(OFS_PARM5); sv.Effects[idx].ef.Rain.count = G_FLOAT(OFS_PARM6); sv.Effects[idx].ef.Rain.wait = G_FLOAT(OFS_PARM7); sv.Effects[idx].ef.Rain.next_time = 0; break; case CE_SNOW: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Rain.min_org); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Rain.max_org); sv.Effects[idx].ef.Rain.flags = G_FLOAT(OFS_PARM3); VectorCopy(G_VECTOR(OFS_PARM4), sv.Effects[idx].ef.Rain.dir); sv.Effects[idx].ef.Rain.count = G_FLOAT(OFS_PARM5); //sv.Effects[idx].Rain.veer = G_FLOAT(OFS_PARM6); //sv.Effects[idx].Rain.wait = 0.10; sv.Effects[idx].ef.Rain.next_time = 0; break; case CE_FOUNTAIN: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Fountain.pos); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Fountain.angle); VectorCopy(G_VECTOR(OFS_PARM3), sv.Effects[idx].ef.Fountain.movedir); sv.Effects[idx].ef.Fountain.color = G_FLOAT(OFS_PARM4); sv.Effects[idx].ef.Fountain.cnt = G_FLOAT(OFS_PARM5); break; case CE_QUAKE: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Quake.origin); sv.Effects[idx].ef.Quake.radius = G_FLOAT(OFS_PARM2); break; case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK1: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Smoke.origin); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Smoke.velocity); sv.Effects[idx].ef.Smoke.framelength = G_FLOAT(OFS_PARM3); sv.Effects[idx].ef.Smoke.frame = 0; sv.Effects[idx].expire_time = sv.time + 1; break; case CE_ACID_MUZZFL: case CE_FLAMESTREAM: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: /* mission pack */ VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Smoke.origin); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Smoke.velocity); sv.Effects[idx].ef.Smoke.framelength = 0.05; sv.Effects[idx].ef.Smoke.frame = G_FLOAT(OFS_PARM3); sv.Effects[idx].expire_time = sv.time + 1; break; case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_FLOOR_EXPLOSION3: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_ACID_HIT: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_LBALL_EXPL: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: case CE_FBOOM: case CE_BOMB: case CE_BRN_BOUNCE: case CE_LSHOCK: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Smoke.origin); sv.Effects[idx].expire_time = sv.time + 1; break; case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_RED_FLASH: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Flash.origin); sv.Effects[idx].expire_time = sv.time + 1; break; case CE_RIDER_DEATH: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.RD.origin); break; case CE_GRAVITYWELL: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.RD.origin); sv.Effects[idx].ef.RD.color = G_FLOAT(OFS_PARM2); sv.Effects[idx].ef.RD.lifetime = G_FLOAT(OFS_PARM3); break; case CE_TELEPORTERPUFFS: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Teleporter.origin); sv.Effects[idx].expire_time = sv.time + 1; break; case CE_TELEPORTERBODY: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Teleporter.origin); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Teleporter.velocity[0]); sv.Effects[idx].ef.Teleporter.skinnum = G_FLOAT(OFS_PARM3); sv.Effects[idx].expire_time = sv.time + 1; break; case CE_BONESHARD: case CE_BONESHRAPNEL: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Missile.origin); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Missile.velocity); VectorCopy(G_VECTOR(OFS_PARM3), sv.Effects[idx].ef.Missile.angle); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Missile.avelocity); sv.Effects[idx].expire_time = sv.time + 10; break; case CE_CHUNK: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Chunk.origin); sv.Effects[idx].ef.Chunk.type = G_FLOAT(OFS_PARM2); VectorCopy(G_VECTOR(OFS_PARM3), sv.Effects[idx].ef.Chunk.srcVel); sv.Effects[idx].ef.Chunk.numChunks = G_FLOAT(OFS_PARM4); sv.Effects[idx].expire_time = sv.time + 3; break; default: PR_RunError ("%s: bad type", __thisfunc__); } SV_SendEffect(sb, idx); } // All changes need to be in SV_SendEffect(), SV_ParseEffect(), // SV_SaveEffects(), SV_LoadEffects(), CL_ParseEffect() void SV_SaveEffects (FILE *FH) { int idx, count; unsigned int u; for (idx = count = 0 ; idx < MAX_EFFECTS ; idx++) { if (sv.Effects[idx].type) count++; } fprintf(FH, "Effects: %d\n", count); for (idx = count = 0 ; idx < MAX_EFFECTS ; idx++) { if (!sv.Effects[idx].type) continue; fprintf(FH, "Effect: %d %d %f: ", idx, sv.Effects[idx].type, sv.Effects[idx].expire_time); switch (sv.Effects[idx].type) { case CE_RAIN: fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.min_org[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.min_org[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.min_org[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.max_org[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.max_org[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.max_org[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.e_size[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.e_size[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.e_size[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.dir[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.dir[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.dir[2]); fprintf(FH, "%d ", sv.Effects[idx].ef.Rain.color); fprintf(FH, "%d ", sv.Effects[idx].ef.Rain.count); fprintf(FH, "%f\n", sv.Effects[idx].ef.Rain.wait); break; case CE_SNOW: fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.min_org[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.min_org[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.min_org[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.max_org[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.max_org[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.max_org[2]); fprintf(FH, "%d ", sv.Effects[idx].ef.Rain.flags); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.dir[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.dir[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.dir[2]); fprintf(FH, "%d ", sv.Effects[idx].ef.Rain.count); //fprintf(FH, "%d ", sv.Effects[idx].ef.Rain.veer); /* O.S: a linefeed is missing here. not adding it so as not to break existing saves. also see in: SV_LoadEffects(). */ break; case CE_FOUNTAIN: fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.pos[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.pos[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.pos[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.angle[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.angle[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.angle[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.movedir[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.movedir[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.movedir[2]); fprintf(FH, "%d ", sv.Effects[idx].ef.Fountain.color); fprintf(FH, "%d\n", sv.Effects[idx].ef.Fountain.cnt); break; case CE_QUAKE: fprintf(FH, "%f ", sv.Effects[idx].ef.Quake.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Quake.origin[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Quake.origin[2]); fprintf(FH, "%f\n", sv.Effects[idx].ef.Quake.radius); break; case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK1: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: case CE_ACID_MUZZFL: case CE_FLAMESTREAM: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: fprintf(FH, "%f ", sv.Effects[idx].ef.Smoke.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Smoke.origin[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Smoke.origin[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Smoke.velocity[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Smoke.velocity[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Smoke.velocity[2]); /* smoke frame is a mission pack thing only. */ if (sv_protocol > PROTOCOL_RAVEN_111) { fprintf(FH, "%f ", sv.Effects[idx].ef.Smoke.framelength); fprintf(FH, "%f\n", sv.Effects[idx].ef.Smoke.frame); } else { /* save it in 1.11 style */ fprintf(FH, "%f\n", sv.Effects[idx].ef.Smoke.framelength); } break; case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_FLOOR_EXPLOSION3: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_BRN_BOUNCE: case CE_LSHOCK: case CE_ACID_HIT: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_LBALL_EXPL: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: case CE_FBOOM: case CE_BOMB: fprintf(FH, "%f ", sv.Effects[idx].ef.Smoke.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Smoke.origin[1]); fprintf(FH, "%f\n", sv.Effects[idx].ef.Smoke.origin[2]); break; case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_RED_FLASH: fprintf(FH, "%f ", sv.Effects[idx].ef.Flash.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Flash.origin[1]); fprintf(FH, "%f\n", sv.Effects[idx].ef.Flash.origin[2]); break; case CE_RIDER_DEATH: fprintf(FH, "%f ", sv.Effects[idx].ef.RD.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.RD.origin[1]); fprintf(FH, "%f\n", sv.Effects[idx].ef.RD.origin[2]); break; case CE_GRAVITYWELL: fprintf(FH, "%f ", sv.Effects[idx].ef.RD.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.RD.origin[1]); fprintf(FH, "%f", sv.Effects[idx].ef.RD.origin[2]); fprintf(FH, "%d", sv.Effects[idx].ef.RD.color); fprintf(FH, "%f\n", sv.Effects[idx].ef.RD.lifetime); break; case CE_TELEPORTERPUFFS: fprintf(FH, "%f ", sv.Effects[idx].ef.Teleporter.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Teleporter.origin[1]); fprintf(FH, "%f\n", sv.Effects[idx].ef.Teleporter.origin[2]); break; case CE_TELEPORTERBODY: fprintf(FH, "%f ", sv.Effects[idx].ef.Teleporter.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Teleporter.origin[1]); fprintf(FH, "%f\n", sv.Effects[idx].ef.Teleporter.origin[2]); break; case CE_BONESHARD: case CE_BONESHRAPNEL: fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.origin[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.origin[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.velocity[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.velocity[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.velocity[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.angle[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.angle[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.angle[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.avelocity[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.avelocity[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.avelocity[2]); /* O.S: a linefeed is missing here. not adding it so as not to break existing saves. also see in: SV_LoadEffects(). */ break; case CE_CHUNK: fprintf(FH, "%f ", sv.Effects[idx].ef.Chunk.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Chunk.origin[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Chunk.origin[2]); u = sv.Effects[idx].ef.Chunk.type; fprintf(FH, "%u ", u); fprintf(FH, "%f ", sv.Effects[idx].ef.Chunk.srcVel[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Chunk.srcVel[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Chunk.srcVel[2]); u = sv.Effects[idx].ef.Chunk.numChunks; fprintf(FH, "%u ", u); /* O.S: a linefeed is missing here. not adding it so as not to break existing saves. also see in: SV_LoadEffects(). */ break; default: Host_Error ("%s: bad type", __thisfunc__); break; } } } // All changes need to be in SV_SendEffect(), SV_ParseEffect(), // SV_SaveEffects(), SV_LoadEffects(), CL_ParseEffect() void SV_LoadEffects (FILE *FH) { int idx, Total, count; int c; unsigned int u; Total = idx = -1; /* Since the map is freshly loaded, clear out any effects as a result of the loading */ SV_ClearEffects(); fscanf(FH, "Effects: %d\n", &Total); if (Total < 0 || Total > MAX_EFFECTS) Host_Error ("%s: bad numeffects", __thisfunc__); for (count = 0 ; count < Total ; idx = -1, count++) { fscanf(FH, "Effect: %d ", &idx); if (idx < 0 || idx >= MAX_EFFECTS) Host_Error ("%s: bad index", __thisfunc__); fscanf(FH, "%d %f: ", &sv.Effects[idx].type, &sv.Effects[idx].expire_time); switch (sv.Effects[idx].type) { case CE_RAIN: fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.min_org[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.min_org[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.min_org[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.max_org[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.max_org[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.max_org[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.e_size[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.e_size[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.e_size[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.dir[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.dir[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.dir[2]); fscanf(FH, "%d ", &sv.Effects[idx].ef.Rain.color); fscanf(FH, "%d ", &sv.Effects[idx].ef.Rain.count); fscanf(FH, "%f\n", &sv.Effects[idx].ef.Rain.wait); break; case CE_SNOW: fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.min_org[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.min_org[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.min_org[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.max_org[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.max_org[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.max_org[2]); fscanf(FH, "%d ", &sv.Effects[idx].ef.Rain.flags); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.dir[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.dir[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.dir[2]); fscanf(FH, "%d ", &sv.Effects[idx].ef.Rain.count); //fscanf(FH, "%d ", &sv.Effects[idx].ef.Rain.veer); /* O.S: a linefeed is missing here. not adding it so as not to break existing saves. also see in: SV_SaveEffects(). */ break; case CE_FOUNTAIN: fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.pos[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.pos[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.pos[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.angle[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.angle[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.angle[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.movedir[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.movedir[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.movedir[2]); fscanf(FH, "%d ", &sv.Effects[idx].ef.Fountain.color); fscanf(FH, "%d\n", &sv.Effects[idx].ef.Fountain.cnt); break; case CE_QUAKE: fscanf(FH, "%f ", &sv.Effects[idx].ef.Quake.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Quake.origin[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Quake.origin[2]); fscanf(FH, "%f\n", &sv.Effects[idx].ef.Quake.radius); break; case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK1: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: case CE_ACID_MUZZFL: case CE_FLAMESTREAM: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: fscanf(FH, "%f ", &sv.Effects[idx].ef.Smoke.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Smoke.origin[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Smoke.origin[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Smoke.velocity[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Smoke.velocity[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Smoke.velocity[2]); /* smoke frame is a mission pack thing only: read carefully... */ // fscanf(FH, "%f ", &sv.Effects[idx].ef.Smoke.framelength); // fscanf(FH, "%f\n", &sv.Effects[idx].ef.Smoke.frame); fscanf(FH, "%f", &sv.Effects[idx].ef.Smoke.framelength); c = fgetc (FH); /* read one char, see what it is: */ if (c == '\n' || c == '\r') { /* 1.11 style */ sv.Effects[idx].ef.Smoke.frame = 0; /* read one char until it's not an EOL char, then go one char back to the correct position. */ while (!feof(FH) && (c == '\n' || c == '\r')) c = fgetc (FH); if (!feof(FH)) ungetc (c, FH); } else { /* 1.12 mission pack style */ //if (c != ' ') // Sys_DPrintf ("broken save ??\n"); fscanf(FH, " %f\n", &sv.Effects[idx].ef.Smoke.frame); } break; case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_FLOOR_EXPLOSION3: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_BRN_BOUNCE: case CE_LSHOCK: case CE_ACID_HIT: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_LBALL_EXPL: case CE_FBOOM: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: case CE_BOMB: fscanf(FH, "%f ", &sv.Effects[idx].ef.Smoke.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Smoke.origin[1]); fscanf(FH, "%f\n", &sv.Effects[idx].ef.Smoke.origin[2]); break; case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_RED_FLASH: fscanf(FH, "%f ", &sv.Effects[idx].ef.Flash.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Flash.origin[1]); fscanf(FH, "%f\n", &sv.Effects[idx].ef.Flash.origin[2]); break; case CE_RIDER_DEATH: fscanf(FH, "%f ", &sv.Effects[idx].ef.RD.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.RD.origin[1]); fscanf(FH, "%f\n", &sv.Effects[idx].ef.RD.origin[2]); break; case CE_GRAVITYWELL: fscanf(FH, "%f ", &sv.Effects[idx].ef.RD.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.RD.origin[1]); fscanf(FH, "%f", &sv.Effects[idx].ef.RD.origin[2]); fscanf(FH, "%d", &sv.Effects[idx].ef.RD.color); fscanf(FH, "%f\n", &sv.Effects[idx].ef.RD.lifetime); break; case CE_TELEPORTERPUFFS: fscanf(FH, "%f ", &sv.Effects[idx].ef.Teleporter.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Teleporter.origin[1]); fscanf(FH, "%f\n", &sv.Effects[idx].ef.Teleporter.origin[2]); break; case CE_TELEPORTERBODY: fscanf(FH, "%f ", &sv.Effects[idx].ef.Teleporter.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Teleporter.origin[1]); fscanf(FH, "%f\n", &sv.Effects[idx].ef.Teleporter.origin[2]); break; case CE_BONESHARD: case CE_BONESHRAPNEL: fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.origin[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.origin[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.velocity[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.velocity[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.velocity[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.angle[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.angle[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.angle[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.avelocity[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.avelocity[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.avelocity[2]); /* O.S: a linefeed is missing here. not adding it so as not to break existing saves. also see in: SV_SaveEffects(). */ break; case CE_CHUNK: fscanf(FH, "%f ", &sv.Effects[idx].ef.Chunk.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Chunk.origin[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Chunk.origin[2]); fscanf(FH, "%u ", &u); sv.Effects[idx].ef.Chunk.type = u & 0xff; fscanf(FH, "%f ", &sv.Effects[idx].ef.Chunk.srcVel[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Chunk.srcVel[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Chunk.srcVel[2]); fscanf(FH, "%u ", &u); sv.Effects[idx].ef.Chunk.numChunks = u & 0xff; /* O.S: a linefeed is missing here. not adding it so as not to break existing saves. also see in: SV_SaveEffects(). */ break; default: Host_Error ("%s: bad type", __thisfunc__); break; } } } engine/hexen2/sv_main.c000066400000000000000000001546411444734033100153320ustar00rootroot00000000000000/* * sv_main.c -- server main program * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" server_t sv; server_static_t svs; static char localmodels[MAX_MODELS][8]; // inline model names for precache static cvar_t sv_sound_distance = {"sv_sound_distance", "800", CVAR_NONE}; /* doesn't seem functional, but the hcode calls it */ static cvar_t sv_update_player = {"sv_update_player", "1", CVAR_ARCHIVE}; static cvar_t sv_update_monsters = {"sv_update_monsters", "1", CVAR_ARCHIVE}; static cvar_t sv_update_missiles = {"sv_update_missiles", "1", CVAR_ARCHIVE}; static cvar_t sv_update_misc = {"sv_update_misc", "1", CVAR_ARCHIVE}; cvar_t sv_ce_scale = {"sv_ce_scale", "0", CVAR_ARCHIVE}; cvar_t sv_ce_max_size = {"sv_ce_max_size", "0", CVAR_ARCHIVE}; extern cvar_t sv_maxvelocity; extern cvar_t sv_gravity; extern cvar_t sv_nostep; extern cvar_t sv_friction; extern cvar_t sv_edgefriction; extern cvar_t sv_stopspeed; extern cvar_t sv_maxspeed; extern cvar_t sv_accelerate; extern cvar_t sv_altnoclip; extern cvar_t sv_idealpitchscale; extern cvar_t sv_idealrollscale; extern cvar_t sv_aim; extern cvar_t sv_walkpitch; extern cvar_t sv_flypitch; int current_skill; int sv_protocol = PROTOCOL_VERSION; /* protocol version to use */ int sv_kingofhill; /* mission pack, king of the hill. */ unsigned int info_mask, info_mask2; /* mission pack, objectives */ extern float scr_centertime_off; //============================================================================ static void Sv_Edicts_f(void); /* =============== SV_Init =============== */ void SV_Init (void) { int i; const char *p; Cvar_RegisterVariable (&sv_maxvelocity); Cvar_RegisterVariable (&sv_gravity); Cvar_RegisterVariable (&sv_friction); Cvar_SetCallback (&sv_gravity, Host_Callback_Notify); Cvar_SetCallback (&sv_friction, Host_Callback_Notify); Cvar_RegisterVariable (&sv_edgefriction); Cvar_RegisterVariable (&sv_stopspeed); Cvar_RegisterVariable (&sv_maxspeed); Cvar_SetCallback (&sv_maxspeed, Host_Callback_Notify); Cvar_RegisterVariable (&sv_accelerate); Cvar_RegisterVariable (&sv_altnoclip); Cvar_RegisterVariable (&sv_idealpitchscale); Cvar_RegisterVariable (&sv_idealrollscale); Cvar_RegisterVariable (&sv_aim); Cvar_RegisterVariable (&sv_nostep); Cvar_RegisterVariable (&sv_walkpitch); Cvar_RegisterVariable (&sv_flypitch); Cvar_RegisterVariable (&sv_sound_distance); Cvar_RegisterVariable (&sv_update_player); Cvar_RegisterVariable (&sv_update_monsters); Cvar_RegisterVariable (&sv_update_missiles); Cvar_RegisterVariable (&sv_update_misc); Cvar_RegisterVariable (&sv_ce_scale); Cvar_RegisterVariable (&sv_ce_max_size); SV_UserInit (); Cmd_AddCommand ("sv_edicts", Sv_Edicts_f); for (i = 0; i < MAX_MODELS; i++) sprintf (localmodels[i], "*%i", i); // initialize King of Hill to world sv_kingofhill = 0; i = COM_CheckParm ("-protocol"); if (i && i < com_argc - 1) sv_protocol = atoi (com_argv[i + 1]); switch (sv_protocol) { case PROTOCOL_RAVEN_111: p = "Raven/H2/1.11"; break; case PROTOCOL_RAVEN_112: p = "Raven/MP/1.12"; break; case PROTOCOL_UQE_113: p = "UQE/1.13"; break; default: Sys_Error ("Bad protocol version request %i. Accepted values: %i, %i, %i.", sv_protocol, PROTOCOL_RAVEN_111, PROTOCOL_RAVEN_112, PROTOCOL_UQE_113); return; /* silence compiler */ } Sys_Printf ("Server using protocol %i (%s)\n", sv_protocol, p); } void SV_Edicts (const char *Name) { FILE *FH; int i; edict_t *e; FH = fopen(FS_MakePath(FS_USERDIR,NULL,Name), "w"); if (!FH) { Con_Printf("Could not open %s\n", Name); return; } fprintf(FH, "Number of Edicts: %d\n", sv.num_edicts); fprintf(FH, "Server Time: %f\n", sv.time); fprintf(FH, "\n"); fprintf(FH, "Num. Time Class Name Model Think Touch Use\n"); fprintf(FH, "---- -------- ------------------------------ ------------------------------ ---------------------------------------- ---------------------------------------- ----------------------------------------\n"); for (i = 1; i < sv.num_edicts; i++) { e = EDICT_NUM(i); fprintf(FH, "%3d. %8.2f %-30s %-30s %-40s %-40s %-40s\n", i, e->v.nextthink, PR_GetString(e->v.classname), PR_GetString(e->v.model), PR_GetString(pr_functions[e->v.think].s_name), PR_GetString(pr_functions[e->v.touch].s_name), PR_GetString(pr_functions[e->v.use].s_name)); } fclose(FH); } static void Sv_Edicts_f (void) { const char *Name; if (!sv.active) { Con_Printf("This command can only be executed on a server running a map\n"); return; } if (Cmd_Argc() < 2) { Name = "edicts.txt"; } else { Name = Cmd_Argv(1); } SV_Edicts(Name); } /* ============================================================================= EVENT MESSAGES ============================================================================= */ /* ================== SV_StartParticle Make sure the event gets sent to all clients ================== */ void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count) { int i, v; if (sv.datagram.cursize > MAX_DATAGRAM-16) return; MSG_WriteByte (&sv.datagram, svc_particle); MSG_WriteCoord (&sv.datagram, org[0]); MSG_WriteCoord (&sv.datagram, org[1]); MSG_WriteCoord (&sv.datagram, org[2]); for (i = 0; i < 3; i++) { v = dir[i] * 16; if (v > 127) v = 127; else if (v < -128) v = -128; MSG_WriteChar (&sv.datagram, v); } MSG_WriteByte (&sv.datagram, count); MSG_WriteByte (&sv.datagram, color); } /* ================== SV_StartParticle2 Make sure the event gets sent to all clients ================== */ void SV_StartParticle2 (vec3_t org, vec3_t dmin, vec3_t dmax, int color, int effect, int count) { if (sv.datagram.cursize > MAX_DATAGRAM-36) return; MSG_WriteByte (&sv.datagram, svc_particle2); MSG_WriteCoord (&sv.datagram, org[0]); MSG_WriteCoord (&sv.datagram, org[1]); MSG_WriteCoord (&sv.datagram, org[2]); MSG_WriteFloat (&sv.datagram, dmin[0]); MSG_WriteFloat (&sv.datagram, dmin[1]); MSG_WriteFloat (&sv.datagram, dmin[2]); MSG_WriteFloat (&sv.datagram, dmax[0]); MSG_WriteFloat (&sv.datagram, dmax[1]); MSG_WriteFloat (&sv.datagram, dmax[2]); MSG_WriteShort (&sv.datagram, color); MSG_WriteByte (&sv.datagram, count); MSG_WriteByte (&sv.datagram, effect); } /* ================== SV_StartParticle3 Make sure the event gets sent to all clients ================== */ void SV_StartParticle3 (vec3_t org, vec3_t box, int color, int effect, int count) { if (sv.datagram.cursize > MAX_DATAGRAM-15) return; MSG_WriteByte (&sv.datagram, svc_particle3); MSG_WriteCoord (&sv.datagram, org[0]); MSG_WriteCoord (&sv.datagram, org[1]); MSG_WriteCoord (&sv.datagram, org[2]); MSG_WriteByte (&sv.datagram, box[0]); MSG_WriteByte (&sv.datagram, box[1]); MSG_WriteByte (&sv.datagram, box[2]); MSG_WriteShort (&sv.datagram, color); MSG_WriteByte (&sv.datagram, count); MSG_WriteByte (&sv.datagram, effect); } /* ================== SV_StartParticle4 Make sure the event gets sent to all clients ================== */ void SV_StartParticle4 (vec3_t org, float radius, int color, int effect, int count) { if (sv.datagram.cursize > MAX_DATAGRAM-13) return; MSG_WriteByte (&sv.datagram, svc_particle4); MSG_WriteCoord (&sv.datagram, org[0]); MSG_WriteCoord (&sv.datagram, org[1]); MSG_WriteCoord (&sv.datagram, org[2]); MSG_WriteByte (&sv.datagram, radius); MSG_WriteShort (&sv.datagram, color); MSG_WriteByte (&sv.datagram, count); MSG_WriteByte (&sv.datagram, effect); } /* ================== SV_StopSound ================== */ void SV_StopSound (edict_t *entity, int channel) { int ent; if (sv.datagram.cursize > MAX_DATAGRAM-4) return; ent = NUM_FOR_EDICT(entity); channel = (ent<<3) | channel; MSG_WriteByte (&sv.datagram, svc_stopsound); MSG_WriteShort (&sv.datagram, channel); } /* ================== SV_UpdateSoundPos ================== */ void SV_UpdateSoundPos (edict_t *entity, int channel) { int ent; int i; if (sv.datagram.cursize > MAX_DATAGRAM-4) return; ent = NUM_FOR_EDICT(entity); channel = (ent<<3) | channel; MSG_WriteByte (&sv.datagram, svc_sound_update_pos); MSG_WriteShort (&sv.datagram, channel); for (i = 0; i < 3; i++) MSG_WriteCoord (&sv.datagram, entity->v.origin[i] + 0.5*(entity->v.mins[i]+entity->v.maxs[i])); } /* ================== SV_StartSound Each entity can have eight independant sound sources, like voice, weapon, feet, etc. Channel 0 is an auto-allocate channel, the others override anything already running on that entity/channel pair. An attenuation of 0 will play full volume everywhere in the level. Larger attenuations will drop off. (max 4 attenuation) ================== */ void SV_StartSound (edict_t *entity, int channel, const char *sample, int volume, float attenuation) { int sound_num, ent; int i, field_mask; if (q_strcasecmp(sample,"misc/null.wav") == 0) { SV_StopSound(entity,channel); return; } if (volume < 0 || volume > 255) Host_Error ("%s: volume = %i", __thisfunc__, volume); if (attenuation < 0 || attenuation > 4) Host_Error ("%s: attenuation = %f", __thisfunc__, attenuation); if (channel < 0 || channel > 7) Host_Error ("%s: channel = %i", __thisfunc__, channel); if (sv.datagram.cursize > MAX_DATAGRAM-16) return; // find precache number for sound for (sound_num = 1; sound_num < MAX_SOUNDS && sv.sound_precache[sound_num]; sound_num++) { if (!strcmp(sample, sv.sound_precache[sound_num])) break; } if (sound_num == MAX_SOUNDS || !sv.sound_precache[sound_num]) { Con_Printf ("%s: %s not precached\n", __thisfunc__, sample); return; } ent = NUM_FOR_EDICT(entity); channel = (ent<<3) | channel; field_mask = 0; if (volume != DEFAULT_SOUND_PACKET_VOLUME) field_mask |= SND_VOLUME; if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION) field_mask |= SND_ATTENUATION; if (sound_num >= MAX_SOUNDS_OLD) { if (sv_protocol == PROTOCOL_RAVEN_111) { Con_DPrintf("%s: protocol 18 violation: %s sound_num == %i >= %i\n", __thisfunc__, sample, sound_num, MAX_SOUNDS_OLD); return; } field_mask |= SND_OVERFLOW; sound_num -= MAX_SOUNDS_OLD; } // directed messages go only to the entity the are targeted on MSG_WriteByte (&sv.datagram, svc_sound); MSG_WriteByte (&sv.datagram, field_mask); if (field_mask & SND_VOLUME) MSG_WriteByte (&sv.datagram, volume); if (field_mask & SND_ATTENUATION) MSG_WriteByte (&sv.datagram, attenuation*64); MSG_WriteShort (&sv.datagram, channel); MSG_WriteByte (&sv.datagram, sound_num); for (i = 0; i < 3; i++) MSG_WriteCoord (&sv.datagram, entity->v.origin[i] + 0.5*(entity->v.mins[i]+entity->v.maxs[i])); } /* ============================================================================== CLIENT SPAWNING ============================================================================== */ /* ================ SV_SendServerinfo Sends the first message from the server to a connected client. This will be sent on the initial connection and upon each server load. ================ */ static void SV_SendServerinfo (client_t *client) { int i; const char **s; char message[2048]; MSG_WriteByte (&client->message, svc_print); sprintf (message, "%c\nVERSION %4.2f SERVER (%i CRC)", 2, ENGINE_VERSION, pr_crc); MSG_WriteString (&client->message,message); MSG_WriteByte (&client->message, svc_serverinfo); MSG_WriteLong (&client->message, sv_protocol); MSG_WriteByte (&client->message, svs.maxclients); if (!coop.integer && deathmatch.integer) { MSG_WriteByte (&client->message, GAME_DEATHMATCH); if (sv_protocol > PROTOCOL_RAVEN_111) MSG_WriteShort (&client->message, sv_kingofhill); } else MSG_WriteByte (&client->message, GAME_COOP); // send full levelname MSG_WriteString(&client->message, SV_GetLevelname ()); for (i = 1, s = sv.model_precache + 1; i < MAX_MODELS && *s; s++) MSG_WriteString (&client->message, *s); MSG_WriteByte (&client->message, 0); for (i = 1, s = sv.sound_precache + 1; i < MAX_SOUNDS && *s; s++) MSG_WriteString (&client->message, *s); MSG_WriteByte (&client->message, 0); // send music MSG_WriteByte (&client->message, svc_cdtrack); MSG_WriteByte (&client->message, sv.cd_track); MSG_WriteByte (&client->message, sv.cd_track); MSG_WriteByte (&client->message, svc_midi_name); MSG_WriteString (&client->message, sv.midi_name); if (sv_protocol >= PROTOCOL_UQE_113) { MSG_WriteByte (&client->message, svc_mod_name); MSG_WriteString (&client->message, ""); /* uqe-hexen2 sends sv.mod_name */ MSG_WriteByte (&client->message, svc_skybox); MSG_WriteString (&client->message, ""); /* uqe-hexen2 sends "sv.skybox" */ } // set view MSG_WriteByte (&client->message, svc_setview); MSG_WriteShort (&client->message, NUM_FOR_EDICT(client->edict)); MSG_WriteByte (&client->message, svc_signonnum); MSG_WriteByte (&client->message, 1); client->sendsignon = true; client->spawned = false; // need prespawn, spawn, etc } /* ================ SV_ConnectClient Initializes a client_t for a new net connection. This will only be called once for a player each game, not once for each level change. ================ */ static void SV_ConnectClient (int clientnum) { edict_t *ent; client_t *client; int edictnum; struct qsocket_s *netconnection; // int i; float spawn_parms[NUM_SPAWN_PARMS]; client = svs.clients + clientnum; Con_DPrintf ("Client %s connected\n", NET_QSocketGetAddressString(client->netconnection)); edictnum = clientnum+1; ent = EDICT_NUM(edictnum); // set up the client_t netconnection = client->netconnection; if (sv.loadgame) memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms)); memset (client, 0, sizeof(*client)); client->send_all_v = true; client->netconnection = netconnection; strcpy (client->name, "unconnected"); client->active = true; client->spawned = false; client->edict = ent; SZ_Init (&client->message, client->msgbuf, sizeof(client->msgbuf)); client->message.allowoverflow = true; // we can catch it SZ_Init (&client->datagram, client->datagram_buf, sizeof(client->datagram_buf)); memset(&sv.states[clientnum], 0, sizeof(client_state2_t )); if (sv.loadgame) memcpy (client->spawn_parms, spawn_parms, sizeof(spawn_parms)); // else // { // call the progs to get default spawn parms for the new client // PR_ExecuteProgram (*sv_globals.SetNewParms); // for (i = 0; i < NUM_SPAWN_PARMS; i++) // client->spawn_parms[i] = sv_globals.parm[i]; // } SV_SendServerinfo (client); } /* =================== SV_CheckForNewClients =================== */ void SV_CheckForNewClients (void) { struct qsocket_s *ret; int i; // // check for new connections // while (1) { ret = NET_CheckNewConnections (); if (!ret) break; // // init a new client structure // for (i = 0; i < svs.maxclients; i++) { if (!svs.clients[i].active) break; } if (i == svs.maxclients) Sys_Error ("%s: no free clients", __thisfunc__); svs.clients[i].netconnection = ret; SV_ConnectClient (i); net_activeconnections++; } } /* =============================================================================== FRAME UPDATES =============================================================================== */ /* ================== SV_ClearDatagram ================== */ void SV_ClearDatagram (void) { SZ_Clear (&sv.datagram); } /* ============================================================================= The PVS must include a small area around the client to allow head bobbing or other small motion on the client side. Otherwise, a bob might cause an entity that should be visible to not show up, especially when the bob crosses a waterline. ============================================================================= */ static int fatbytes; static byte fatpvs[MAX_MAP_LEAFS/8]; static void SV_AddToFatPVS (vec3_t org, mnode_t *node) { int i; byte *pvs; mplane_t *plane; float d; while (1) { // if this is a leaf, accumulate the pvs bits if (node->contents < 0) { if (node->contents != CONTENTS_SOLID) { pvs = Mod_LeafPVS ( (mleaf_t *)node, sv.worldmodel); for (i = 0; i < fatbytes; i++) fatpvs[i] |= pvs[i]; } return; } plane = node->plane; d = DotProduct (org, plane->normal) - plane->dist; if (d > 8) node = node->children[0]; else if (d < -8) node = node->children[1]; else { // go down both SV_AddToFatPVS (org, node->children[0]); node = node->children[1]; } } } /* ============= SV_FatPVS Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the given point. ============= */ static byte *SV_FatPVS (vec3_t org) { fatbytes = (sv.worldmodel->numleafs+31)>>3; memset (fatpvs, 0, fatbytes); SV_AddToFatPVS (org, sv.worldmodel->nodes); return fatpvs; } #define CLIENT_FRAME_INIT 255 #define CLIENT_FRAME_RESET 254 static void SV_PrepareClientEntities (client_t *client, edict_t *clent, sizebuf_t *msg) { int e, i; int bits; byte *pvs; vec3_t org; float miss; edict_t *ent; int temp_index; char NewName[MAX_QPATH]; long flagtest; int position = 0; int client_num; client_frames_t *reference, *build; client_state2_t *state; entity_state2_t *ref_ent, *set_ent, build_ent; qboolean FoundInList,DoRemove,DoPlayer,DoMonsters,DoMissiles,DoMisc,IgnoreEnt; short RemoveList[MAX_CLIENT_STATES],NumToRemove; client_num = client-svs.clients; state = &sv.states[client_num]; reference = &state->frames[0]; if (client->last_sequence != client->current_sequence) { // Old sequence // Con_Printf("SV: Old sequence SV(%d,%d) CL(%d,%d)\n",client->current_sequence, client->current_frame, client->last_sequence, client->last_frame); client->current_frame++; if (client->current_frame > MAX_FRAMES+1) client->current_frame = MAX_FRAMES+1; } else if (client->last_frame == CLIENT_FRAME_INIT || client->last_frame == 0 || client->last_frame == MAX_FRAMES+1) { // Reference expired in current sequence // Con_Printf("SV: Expired SV(%d,%d) CL(%d,%d)\n",client->current_sequence, client->current_frame, client->last_sequence, client->last_frame); client->current_frame = 1; client->current_sequence++; } else if (client->last_frame >= 1 && client->last_frame <= client->current_frame) { // Got a valid frame // Con_Printf("SV: Valid SV(%d,%d) CL(%d,%d)\n",client->current_sequence, client->current_frame, client->last_sequence, client->last_frame); *reference = state->frames[client->last_frame]; for (i = 0; i < reference->count; i++) { if (reference->states[i].flags & ENT_CLEARED) { e = reference->states[i].index; ent = EDICT_NUM(e); if (ent->baseline.ClearCount[client_num] < CLEAR_LIMIT) { ent->baseline.ClearCount[client_num]++; } else if (ent->baseline.ClearCount[client_num] == CLEAR_LIMIT) { ent->baseline.ClearCount[client_num] = 3; reference->states[i].flags &= ~ENT_CLEARED; } } } client->current_frame = 1; client->current_sequence++; } else { // Normal frame advance // Con_Printf("SV: Normal SV(%d,%d) CL(%d,%d)\n",client->current_sequence, client->current_frame, client->last_sequence, client->last_frame); client->current_frame++; if (client->current_frame > MAX_FRAMES+1) client->current_frame = MAX_FRAMES+1; } DoPlayer = DoMonsters = DoMissiles = DoMisc = false; if (sv_update_player.integer) DoPlayer = (client->current_sequence % sv_update_player.integer) == 0; if (sv_update_monsters.integer) DoMonsters = (client->current_sequence % sv_update_monsters.integer) == 0; if (sv_update_missiles.integer) DoMissiles = (client->current_sequence % sv_update_missiles.integer) == 0; if (sv_update_misc.integer) DoMisc = (client->current_sequence % sv_update_misc.integer) == 0; build = &state->frames[client->current_frame]; memset(build, 0, sizeof(*build)); client->last_frame = CLIENT_FRAME_RESET; NumToRemove = 0; MSG_WriteByte (msg, svc_reference); MSG_WriteByte (msg, client->current_frame); MSG_WriteByte (msg, client->current_sequence); // find the client's PVS if (clent->v.cameramode) { ent = PROG_TO_EDICT(clent->v.cameramode); VectorCopy(ent->v.origin, org); } else VectorAdd (clent->v.origin, clent->v.view_ofs, org); pvs = SV_FatPVS (org); // send over all entities (except the client) that touch the pvs ent = NEXT_EDICT(sv.edicts); for (e = 1; e < sv.num_edicts; e++, ent = NEXT_EDICT(ent)) { DoRemove = false; // don't send if flagged for NODRAW and there are no lighting effects if (ent->v.effects == EF_NODRAW) { DoRemove = true; goto skipA; } // ignore if not touching a PV leaf if (ent != clent) // clent is ALWAYS sent { // ignore ents without visible models if (!ent->v.modelindex || !*PR_GetString(ent->v.model)) { DoRemove = true; goto skipA; } for (i = 0; i < ent->num_leafs; i++) { if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i] & 7)) ) break; } if (i == ent->num_leafs) { DoRemove = true; goto skipA; } } skipA: IgnoreEnt = false; flagtest = (long)ent->v.flags; if (!DoRemove) { if (flagtest & FL_CLIENT) { if (!DoPlayer) IgnoreEnt = true; } else if (flagtest & FL_MONSTER) { if (!DoMonsters) IgnoreEnt = true; } else if (ent->v.movetype == MOVETYPE_FLYMISSILE || ent->v.movetype == MOVETYPE_BOUNCEMISSILE || ent->v.movetype == MOVETYPE_BOUNCE) { if (!DoMissiles) IgnoreEnt = true; } else { if (!DoMisc) IgnoreEnt = true; } } bits = 0; while (position < reference->count && e > reference->states[position].index) position++; if (position < reference->count && reference->states[position].index == e) { FoundInList = true; if (DoRemove) { RemoveList[NumToRemove] = e; NumToRemove++; continue; } else { ref_ent = &reference->states[position]; } } else { if (DoRemove || IgnoreEnt) continue; ref_ent = &build_ent; build_ent.index = e; build_ent.origin[0] = ent->baseline.origin[0]; build_ent.origin[1] = ent->baseline.origin[1]; build_ent.origin[2] = ent->baseline.origin[2]; build_ent.angles[0] = ent->baseline.angles[0]; build_ent.angles[1] = ent->baseline.angles[1]; build_ent.angles[2] = ent->baseline.angles[2]; build_ent.modelindex = ent->baseline.modelindex; build_ent.frame = ent->baseline.frame; build_ent.colormap = ent->baseline.colormap; build_ent.skin = ent->baseline.skin; build_ent.effects = ent->baseline.effects; build_ent.scale = ent->baseline.scale; build_ent.drawflags = ent->baseline.drawflags; build_ent.abslight = ent->baseline.abslight; build_ent.flags = 0; FoundInList = false; } set_ent = &build->states[build->count]; build->count++; if (ent->baseline.ClearCount[client_num] < CLEAR_LIMIT) { memset(ref_ent, 0, sizeof(*ref_ent)); ref_ent->index = e; } *set_ent = *ref_ent; if (IgnoreEnt) continue; // send an update for (i = 0; i < 3; i++) { miss = ent->v.origin[i] - ref_ent->origin[i]; if ( miss < -0.1 || miss > 0.1 ) { bits |= U_ORIGIN1<origin[i] = ent->v.origin[i]; } } if ( ent->v.angles[0] != ref_ent->angles[0] ) { bits |= U_ANGLE1; set_ent->angles[0] = ent->v.angles[0]; } if ( ent->v.angles[1] != ref_ent->angles[1] ) { bits |= U_ANGLE2; set_ent->angles[1] = ent->v.angles[1]; } if ( ent->v.angles[2] != ref_ent->angles[2] ) { bits |= U_ANGLE3; set_ent->angles[2] = ent->v.angles[2]; } if (ent->v.movetype == MOVETYPE_STEP) bits |= U_NOLERP; // don't mess up the step animation if (ref_ent->colormap != ent->v.colormap) { bits |= U_COLORMAP; set_ent->colormap = ent->v.colormap; } if (ref_ent->skin != ent->v.skin || ref_ent->drawflags != ent->v.drawflags) { bits |= U_SKIN; set_ent->skin = ent->v.skin; set_ent->drawflags = ent->v.drawflags; } if (ref_ent->frame != ent->v.frame) { bits |= U_FRAME; set_ent->frame = ent->v.frame; } if (ref_ent->effects != ent->v.effects) { bits |= U_EFFECTS; set_ent->effects = ent->v.effects; } // flagtest = (long)ent->v.flags; if (flagtest & 0xff000000) { Host_Error("Invalid flags setting for class %s", PR_GetString(ent->v.classname)); return; } temp_index = ent->v.modelindex; if (((int)ent->v.flags & FL_CLASS_DEPENDENT) && ent->v.model) { strcpy (NewName, PR_GetString(ent->v.model)); NewName[strlen(NewName)-5] = client->playerclass + 48; temp_index = SV_ModelIndex (NewName); } if (ref_ent->modelindex != temp_index) { bits |= U_MODEL; set_ent->modelindex = temp_index; } if ( ref_ent->scale != ((int)(ent->v.scale * 100.0) & 255) || ref_ent->abslight != ((int)(ent->v.abslight * 255.0) & 255) ) { bits |= U_SCALE; set_ent->scale = ((int)(ent->v.scale * 100.0) & 255); set_ent->abslight = (int)(ent->v.abslight * 255.0) & 255; } if (ent->baseline.ClearCount[client_num] < CLEAR_LIMIT) { bits |= U_CLEAR_ENT; set_ent->flags |= ENT_CLEARED; } if (!bits && FoundInList) { if (build->count >= MAX_CLIENT_STATES) break; continue; } if (e >= 256) bits |= U_LONGENTITY; if (bits >= 256) bits |= U_MOREBITS; if (bits >= 65536) bits |= U_MOREBITS2; // // write the message // MSG_WriteByte (msg,bits | U_SIGNAL); if (bits & U_MOREBITS) MSG_WriteByte (msg, bits >> 8); if (bits & U_MOREBITS2) MSG_WriteByte (msg, bits >> 16); if (bits & U_LONGENTITY) MSG_WriteShort (msg, e); else MSG_WriteByte (msg, e); if (bits & U_MODEL) MSG_WriteShort (msg, temp_index); if (bits & U_FRAME) MSG_WriteByte (msg, ent->v.frame); if (bits & U_COLORMAP) MSG_WriteByte (msg, ent->v.colormap); if (bits & U_SKIN) { // Used for skin and drawflags MSG_WriteByte(msg, ent->v.skin); MSG_WriteByte(msg, ent->v.drawflags); } if (bits & U_EFFECTS) MSG_WriteByte (msg, ent->v.effects); if (bits & U_ORIGIN1) MSG_WriteCoord (msg, ent->v.origin[0]); if (bits & U_ANGLE1) MSG_WriteAngle (msg, ent->v.angles[0]); if (bits & U_ORIGIN2) MSG_WriteCoord (msg, ent->v.origin[1]); if (bits & U_ANGLE2) MSG_WriteAngle (msg, ent->v.angles[1]); if (bits & U_ORIGIN3) MSG_WriteCoord (msg, ent->v.origin[2]); if (bits & U_ANGLE3) MSG_WriteAngle (msg, ent->v.angles[2]); if (bits & U_SCALE) { // Used for scale and abslight MSG_WriteByte (msg, (int)(ent->v.scale * 100.0) & 255); MSG_WriteByte (msg, (int)(ent->v.abslight * 255.0) & 255); } if (build->count >= MAX_CLIENT_STATES) break; } MSG_WriteByte (msg, svc_clear_edicts); MSG_WriteByte (msg, NumToRemove); for (i = 0; i < NumToRemove; i++) MSG_WriteShort (msg, RemoveList[i]); } /* ============= SV_CleanupEnts ============= */ static void SV_CleanupEnts (void) { int e; edict_t *ent; ent = NEXT_EDICT(sv.edicts); for (e = 1; e < sv.num_edicts; e++, ent = NEXT_EDICT(ent)) { ent->v.effects = (int)ent->v.effects & ~EF_MUZZLEFLASH; } } /* ================== SV_WriteClientdataToMessage ================== */ void SV_WriteClientdataToMessage (client_t *client, edict_t *ent, sizebuf_t *msg) { int bits,sc1,sc2; byte test; int i; edict_t *other; static int next_update = 0; static int next_count = 0; // // send a damage message // if (ent->v.dmg_take || ent->v.dmg_save) { other = PROG_TO_EDICT(ent->v.dmg_inflictor); MSG_WriteByte (msg, svc_damage); MSG_WriteByte (msg, ent->v.dmg_save); MSG_WriteByte (msg, ent->v.dmg_take); for (i = 0; i < 3; i++) MSG_WriteCoord (msg, other->v.origin[i] + 0.5*(other->v.mins[i] + other->v.maxs[i])); ent->v.dmg_take = 0; ent->v.dmg_save = 0; } // // send the current viewpos offset from the view entity // SV_SetIdealPitch (); // how much to look up / down ideally // a fixangle might get lost in a dropped packet. Oh well. if (ent->v.fixangle) { MSG_WriteByte (msg, svc_setangle); for (i = 0; i < 3; i++) MSG_WriteAngle (msg, ent->v.angles[i] ); ent->v.fixangle = 0; } bits = 0; if (client->send_all_v) { bits = SU_VIEWHEIGHT | SU_IDEALPITCH | SU_IDEALROLL | SU_VELOCITY1 | (SU_VELOCITY1<<1) | (SU_VELOCITY1<<2) | SU_PUNCH1 | (SU_PUNCH1<<1) | (SU_PUNCH1<<2) | SU_WEAPONFRAME | SU_ARMOR | SU_WEAPON; } else { if (ent->v.view_ofs[2] != client->old_v.view_ofs[2]) bits |= SU_VIEWHEIGHT; if (ent->v.idealpitch != client->old_v.idealpitch) bits |= SU_IDEALPITCH; if (ent->v.idealroll != client->old_v.idealroll) bits |= SU_IDEALROLL; for (i = 0; i < 3; i++) { if (ent->v.punchangle[i] != client->old_v.punchangle[i]) bits |= (SU_PUNCH1<v.velocity[i] != client->old_v.velocity[i]) bits |= (SU_VELOCITY1<v.weaponframe != client->old_v.weaponframe) bits |= SU_WEAPONFRAME; if (ent->v.armorvalue != client->old_v.armorvalue) bits |= SU_ARMOR; if (ent->v.weaponmodel != client->old_v.weaponmodel) bits |= SU_WEAPON; } // send the data //fjm: this wasn't in here b4, and the centerview command requires it. if ( (int)ent->v.flags & FL_ONGROUND) bits |= SU_ONGROUND; next_count++; if (next_count >= 3) { next_count = 0; next_update++; if (next_update > 11) next_update = 0; switch (next_update) { case 0: bits |= SU_VIEWHEIGHT; break; case 1: bits |= SU_IDEALPITCH; break; case 2: bits |= SU_IDEALROLL; break; case 3: bits |= SU_VELOCITY1; break; case 4: bits |= (SU_VELOCITY1<<1); break; case 5: bits |= (SU_VELOCITY1<<2); break; case 6: bits |= SU_PUNCH1; break; case 7: bits |= (SU_PUNCH1<<1); break; case 8: bits |= (SU_PUNCH1<<2); break; case 9: bits |= SU_WEAPONFRAME; break; case 10: bits |= SU_ARMOR; break; case 11: bits |= SU_WEAPON; break; } } MSG_WriteByte (msg, svc_clientdata); MSG_WriteShort (msg, bits); if (bits & SU_VIEWHEIGHT) MSG_WriteChar (msg, ent->v.view_ofs[2]); if (bits & SU_IDEALPITCH) MSG_WriteChar (msg, ent->v.idealpitch); if (bits & SU_IDEALROLL) MSG_WriteChar (msg, ent->v.idealroll); for (i = 0; i < 3; i++) { if (bits & (SU_PUNCH1<v.punchangle[i]); if (bits & (SU_VELOCITY1<v.velocity[i]/16); } if (bits & SU_WEAPONFRAME) MSG_WriteByte (msg, ent->v.weaponframe); if (bits & SU_ARMOR) MSG_WriteByte (msg, ent->v.armorvalue); if (bits & SU_WEAPON) MSG_WriteShort (msg, SV_ModelIndex(PR_GetString(ent->v.weaponmodel))); if (host_client->send_all_v) { sc1 = sc2 = 0xffffffff; host_client->send_all_v = false; } else { sc1 = sc2 = 0; if (ent->v.health != host_client->old_v.health) sc1 |= SC1_HEALTH; if (ent->v.level != host_client->old_v.level) sc1 |= SC1_LEVEL; if (ent->v.intelligence != host_client->old_v.intelligence) sc1 |= SC1_INTELLIGENCE; if (ent->v.wisdom != host_client->old_v.wisdom) sc1 |= SC1_WISDOM; if (ent->v.strength != host_client->old_v.strength) sc1 |= SC1_STRENGTH; if (ent->v.dexterity != host_client->old_v.dexterity) sc1 |= SC1_DEXTERITY; if (ent->v.weapon != host_client->old_v.weapon) sc1 |= SC1_WEAPON; if (ent->v.bluemana != host_client->old_v.bluemana) sc1 |= SC1_BLUEMANA; if (ent->v.greenmana != host_client->old_v.greenmana) sc1 |= SC1_GREENMANA; if (ent->v.experience != host_client->old_v.experience) sc1 |= SC1_EXPERIENCE; if (ent->v.cnt_torch != host_client->old_v.cnt_torch) sc1 |= SC1_CNT_TORCH; if (ent->v.cnt_h_boost != host_client->old_v.cnt_h_boost) sc1 |= SC1_CNT_H_BOOST; if (ent->v.cnt_sh_boost != host_client->old_v.cnt_sh_boost) sc1 |= SC1_CNT_SH_BOOST; if (ent->v.cnt_mana_boost != host_client->old_v.cnt_mana_boost) sc1 |= SC1_CNT_MANA_BOOST; if (ent->v.cnt_teleport != host_client->old_v.cnt_teleport) sc1 |= SC1_CNT_TELEPORT; if (ent->v.cnt_tome != host_client->old_v.cnt_tome) sc1 |= SC1_CNT_TOME; if (ent->v.cnt_summon != host_client->old_v.cnt_summon) sc1 |= SC1_CNT_SUMMON; if (ent->v.cnt_invisibility != host_client->old_v.cnt_invisibility) sc1 |= SC1_CNT_INVISIBILITY; if (ent->v.cnt_glyph != host_client->old_v.cnt_glyph) sc1 |= SC1_CNT_GLYPH; if (ent->v.cnt_haste != host_client->old_v.cnt_haste) sc1 |= SC1_CNT_HASTE; if (ent->v.cnt_blast != host_client->old_v.cnt_blast) sc1 |= SC1_CNT_BLAST; if (ent->v.cnt_polymorph != host_client->old_v.cnt_polymorph) sc1 |= SC1_CNT_POLYMORPH; if (ent->v.cnt_flight != host_client->old_v.cnt_flight) sc1 |= SC1_CNT_FLIGHT; if (ent->v.cnt_cubeofforce != host_client->old_v.cnt_cubeofforce) sc1 |= SC1_CNT_CUBEOFFORCE; if (ent->v.cnt_invincibility != host_client->old_v.cnt_invincibility) sc1 |= SC1_CNT_INVINCIBILITY; if (ent->v.artifact_active != host_client->old_v.artifact_active) sc1 |= SC1_ARTIFACT_ACTIVE; if (ent->v.artifact_low != host_client->old_v.artifact_low) sc1 |= SC1_ARTIFACT_LOW; if (ent->v.movetype != host_client->old_v.movetype) sc1 |= SC1_MOVETYPE; if (ent->v.cameramode != host_client->old_v.cameramode) sc1 |= SC1_CAMERAMODE; if (ent->v.hasted != host_client->old_v.hasted) sc1 |= SC1_HASTED; if (ent->v.inventory != host_client->old_v.inventory) sc1 |= SC1_INVENTORY; if (ent->v.rings_active != host_client->old_v.rings_active) sc1 |= SC1_RINGS_ACTIVE; if (ent->v.rings_low != host_client->old_v.rings_low) sc2 |= SC2_RINGS_LOW; if (ent->v.armor_amulet != host_client->old_v.armor_amulet) sc2 |= SC2_AMULET; if (ent->v.armor_bracer != host_client->old_v.armor_bracer) sc2 |= SC2_BRACER; if (ent->v.armor_breastplate != host_client->old_v.armor_breastplate) sc2 |= SC2_BREASTPLATE; if (ent->v.armor_helmet != host_client->old_v.armor_helmet) sc2 |= SC2_HELMET; if (ent->v.ring_flight != host_client->old_v.ring_flight) sc2 |= SC2_FLIGHT_T; if (ent->v.ring_water != host_client->old_v.ring_water) sc2 |= SC2_WATER_T; if (ent->v.ring_turning != host_client->old_v.ring_turning) sc2 |= SC2_TURNING_T; if (ent->v.ring_regeneration != host_client->old_v.ring_regeneration) sc2 |= SC2_REGEN_T; if (ent->v.haste_time != host_client->old_v.haste_time) sc2 |= SC2_HASTE_T; if (ent->v.tome_time != host_client->old_v.tome_time) sc2 |= SC2_TOME_T; if (ent->v.puzzle_inv1 != host_client->old_v.puzzle_inv1) sc2 |= SC2_PUZZLE1; if (ent->v.puzzle_inv2 != host_client->old_v.puzzle_inv2) sc2 |= SC2_PUZZLE2; if (ent->v.puzzle_inv3 != host_client->old_v.puzzle_inv3) sc2 |= SC2_PUZZLE3; if (ent->v.puzzle_inv4 != host_client->old_v.puzzle_inv4) sc2 |= SC2_PUZZLE4; if (ent->v.puzzle_inv5 != host_client->old_v.puzzle_inv5) sc2 |= SC2_PUZZLE5; if (ent->v.puzzle_inv6 != host_client->old_v.puzzle_inv6) sc2 |= SC2_PUZZLE6; if (ent->v.puzzle_inv7 != host_client->old_v.puzzle_inv7) sc2 |= SC2_PUZZLE7; if (ent->v.puzzle_inv8 != host_client->old_v.puzzle_inv8) sc2 |= SC2_PUZZLE8; if (ent->v.max_health != host_client->old_v.max_health) sc2 |= SC2_MAXHEALTH; if (ent->v.max_mana != host_client->old_v.max_mana) sc2 |= SC2_MAXMANA; if (ent->v.flags != host_client->old_v.flags) sc2 |= SC2_FLAGS; // mission pack, objectives if (sv_protocol > PROTOCOL_RAVEN_111) { if (info_mask != client->info_mask) sc2 |= SC2_OBJ; if (info_mask2 != client->info_mask2) sc2 |= SC2_OBJ2; } } if (!sc1 && !sc2) goto end; MSG_WriteByte (&host_client->message, svc_update_inv); test = 0; if (sc1 & 0x000000ff) test |= 1; if (sc1 & 0x0000ff00) test |= 2; if (sc1 & 0x00ff0000) test |= 4; if (sc1 & 0xff000000) test |= 8; if (sc2 & 0x000000ff) test |= 16; if (sc2 & 0x0000ff00) test |= 32; if (sc2 & 0x00ff0000) test |= 64; if (sc2 & 0xff000000) test |= 128; MSG_WriteByte (&host_client->message, test); if (test & 1) MSG_WriteByte (&host_client->message, sc1 & 0xff); if (test & 2) MSG_WriteByte (&host_client->message, (sc1 >> 8) & 0xff); if (test & 4) MSG_WriteByte (&host_client->message, (sc1 >> 16) & 0xff); if (test & 8) MSG_WriteByte (&host_client->message, (sc1 >> 24) & 0xff); if (test & 16) MSG_WriteByte (&host_client->message, sc2 & 0xff); if (test & 32) MSG_WriteByte (&host_client->message, (sc2 >> 8) & 0xff); if (test & 64) MSG_WriteByte (&host_client->message, (sc2 >> 16) & 0xff); if (test & 128) MSG_WriteByte (&host_client->message, (sc2 >> 24) & 0xff); if (sc1 & SC1_HEALTH) MSG_WriteShort (&host_client->message, ent->v.health); if (sc1 & SC1_LEVEL) MSG_WriteByte (&host_client->message, ent->v.level); if (sc1 & SC1_INTELLIGENCE) MSG_WriteByte (&host_client->message, ent->v.intelligence); if (sc1 & SC1_WISDOM) MSG_WriteByte (&host_client->message, ent->v.wisdom); if (sc1 & SC1_STRENGTH) MSG_WriteByte (&host_client->message, ent->v.strength); if (sc1 & SC1_DEXTERITY) MSG_WriteByte (&host_client->message, ent->v.dexterity); if (sc1 & SC1_WEAPON) MSG_WriteByte (&host_client->message, ent->v.weapon); if (sc1 & SC1_BLUEMANA) MSG_WriteByte (&host_client->message, ent->v.bluemana); if (sc1 & SC1_GREENMANA) MSG_WriteByte (&host_client->message, ent->v.greenmana); if (sc1 & SC1_EXPERIENCE) MSG_WriteLong (&host_client->message, ent->v.experience); if (sc1 & SC1_CNT_TORCH) MSG_WriteByte (&host_client->message, ent->v.cnt_torch); if (sc1 & SC1_CNT_H_BOOST) MSG_WriteByte (&host_client->message, ent->v.cnt_h_boost); if (sc1 & SC1_CNT_SH_BOOST) MSG_WriteByte (&host_client->message, ent->v.cnt_sh_boost); if (sc1 & SC1_CNT_MANA_BOOST) MSG_WriteByte (&host_client->message, ent->v.cnt_mana_boost); if (sc1 & SC1_CNT_TELEPORT) MSG_WriteByte (&host_client->message, ent->v.cnt_teleport); if (sc1 & SC1_CNT_TOME) MSG_WriteByte (&host_client->message, ent->v.cnt_tome); if (sc1 & SC1_CNT_SUMMON) MSG_WriteByte (&host_client->message, ent->v.cnt_summon); if (sc1 & SC1_CNT_INVISIBILITY) MSG_WriteByte (&host_client->message, ent->v.cnt_invisibility); if (sc1 & SC1_CNT_GLYPH) MSG_WriteByte (&host_client->message, ent->v.cnt_glyph); if (sc1 & SC1_CNT_HASTE) MSG_WriteByte (&host_client->message, ent->v.cnt_haste); if (sc1 & SC1_CNT_BLAST) MSG_WriteByte (&host_client->message, ent->v.cnt_blast); if (sc1 & SC1_CNT_POLYMORPH) MSG_WriteByte (&host_client->message, ent->v.cnt_polymorph); if (sc1 & SC1_CNT_FLIGHT) MSG_WriteByte (&host_client->message, ent->v.cnt_flight); if (sc1 & SC1_CNT_CUBEOFFORCE) MSG_WriteByte (&host_client->message, ent->v.cnt_cubeofforce); if (sc1 & SC1_CNT_INVINCIBILITY) MSG_WriteByte (&host_client->message, ent->v.cnt_invincibility); if (sc1 & SC1_ARTIFACT_ACTIVE) MSG_WriteFloat (&host_client->message, ent->v.artifact_active); if (sc1 & SC1_ARTIFACT_LOW) MSG_WriteFloat (&host_client->message, ent->v.artifact_low); if (sc1 & SC1_MOVETYPE) MSG_WriteByte (&host_client->message, ent->v.movetype); if (sc1 & SC1_CAMERAMODE) MSG_WriteByte (&host_client->message, ent->v.cameramode); if (sc1 & SC1_HASTED) MSG_WriteFloat (&host_client->message, ent->v.hasted); if (sc1 & SC1_INVENTORY) MSG_WriteByte (&host_client->message, ent->v.inventory); if (sc1 & SC1_RINGS_ACTIVE) MSG_WriteFloat (&host_client->message, ent->v.rings_active); if (sc2 & SC2_RINGS_LOW) MSG_WriteFloat (&host_client->message, ent->v.rings_low); if (sc2 & SC2_AMULET) MSG_WriteByte (&host_client->message, ent->v.armor_amulet); if (sc2 & SC2_BRACER) MSG_WriteByte (&host_client->message, ent->v.armor_bracer); if (sc2 & SC2_BREASTPLATE) MSG_WriteByte (&host_client->message, ent->v.armor_breastplate); if (sc2 & SC2_HELMET) MSG_WriteByte (&host_client->message, ent->v.armor_helmet); if (sc2 & SC2_FLIGHT_T) MSG_WriteByte (&host_client->message, ent->v.ring_flight); if (sc2 & SC2_WATER_T) MSG_WriteByte (&host_client->message, ent->v.ring_water); if (sc2 & SC2_TURNING_T) MSG_WriteByte (&host_client->message, ent->v.ring_turning); if (sc2 & SC2_REGEN_T) MSG_WriteByte (&host_client->message, ent->v.ring_regeneration); if (sc2 & SC2_HASTE_T) MSG_WriteFloat (&host_client->message, ent->v.haste_time); if (sc2 & SC2_TOME_T) MSG_WriteFloat (&host_client->message, ent->v.tome_time); if (sc2 & SC2_PUZZLE1) MSG_WriteString (&host_client->message, PR_GetString(ent->v.puzzle_inv1)); if (sc2 & SC2_PUZZLE2) MSG_WriteString (&host_client->message, PR_GetString(ent->v.puzzle_inv2)); if (sc2 & SC2_PUZZLE3) MSG_WriteString (&host_client->message, PR_GetString(ent->v.puzzle_inv3)); if (sc2 & SC2_PUZZLE4) MSG_WriteString (&host_client->message, PR_GetString(ent->v.puzzle_inv4)); if (sc2 & SC2_PUZZLE5) MSG_WriteString (&host_client->message, PR_GetString(ent->v.puzzle_inv5)); if (sc2 & SC2_PUZZLE6) MSG_WriteString (&host_client->message, PR_GetString(ent->v.puzzle_inv6)); if (sc2 & SC2_PUZZLE7) MSG_WriteString (&host_client->message, PR_GetString(ent->v.puzzle_inv7)); if (sc2 & SC2_PUZZLE8) MSG_WriteString (&host_client->message, PR_GetString(ent->v.puzzle_inv8)); if (sc2 & SC2_MAXHEALTH) MSG_WriteShort (&host_client->message, ent->v.max_health); if (sc2 & SC2_MAXMANA) MSG_WriteByte (&host_client->message, ent->v.max_mana); if (sc2 & SC2_FLAGS) MSG_WriteFloat (&host_client->message, ent->v.flags); // mission pack, objectives if (sv_protocol > PROTOCOL_RAVEN_111) { if (sc2 & SC2_OBJ) { MSG_WriteLong (&host_client->message, info_mask); client->info_mask = info_mask; } if (sc2 & SC2_OBJ2) { MSG_WriteLong (&host_client->message, info_mask2); client->info_mask2 = info_mask2; } } end: memcpy (&client->old_v, &ent->v, sizeof(client->old_v)); } /* ======================= SV_SendClientDatagram ======================= */ static qboolean SV_SendClientDatagram (client_t *client) { byte buf[NET_MAXMESSAGE]; sizebuf_t msg; SZ_Init (&msg, buf, sizeof(buf)); MSG_WriteByte (&msg, svc_time); MSG_WriteFloat (&msg, sv.time); // add the client specific data to the datagram SV_WriteClientdataToMessage (client, client->edict, &msg); SV_PrepareClientEntities (client, client->edict, &msg); /* if ((rand() & 0xff) < 200) { return true; } */ // copy the server datagram if there is space if (msg.cursize + sv.datagram.cursize < msg.maxsize) SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize); if (msg.cursize + client->datagram.cursize < msg.maxsize) SZ_Write (&msg, client->datagram.data, client->datagram.cursize); SZ_Clear(&client->datagram); /* if (msg.cursize > 300) { Con_DPrintf("WARNING: packet size is %i\n",msg.cursize); } */ // send the datagram if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1) { SV_DropClient (true);// if the message couldn't send, kick off return false; } return true; } /* ======================= SV_UpdateToReliableMessages ======================= */ static void SV_UpdateToReliableMessages (void) { int i, j; client_t *client; edict_t *ent; // check for changes to be sent over the reliable streams for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { ent = host_client->edict; if (host_client->old_frags != ent->v.frags) { for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) { if (!client->active) continue; MSG_WriteByte (&client->message, svc_updatefrags); MSG_WriteByte (&client->message, i); MSG_WriteShort (&client->message, host_client->edict->v.frags); } host_client->old_frags = ent->v.frags; } } for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) { if (!client->active) continue; SZ_Write (&client->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize); } SZ_Clear (&sv.reliable_datagram); } /* ======================= SV_SendNop Send a nop message without trashing or sending the accumulated client message buffer ======================= */ static void SV_SendNop (client_t *client) { sizebuf_t msg; byte buf[4]; SZ_Init (&msg, buf, sizeof(buf)); MSG_WriteChar (&msg, svc_nop); if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1) SV_DropClient (true); // if the message couldn't send, kick off client->last_message = realtime; } /* ======================= SV_SendClientMessages ======================= */ void SV_SendClientMessages (void) { int i; // update frags, names, etc SV_UpdateToReliableMessages (); // build individual updates for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if (!host_client->active) continue; if (host_client->spawned) { if (!SV_SendClientDatagram (host_client)) continue; } else { // the player isn't totally in the game yet // send small keepalive messages if too much time has passed // send a full message when the next signon stage has been requested // some other message data (name changes, etc) may accumulate // between signon stages if (!host_client->sendsignon) { if (realtime - host_client->last_message > 5) SV_SendNop (host_client); continue; // don't send out non-signon messages } } // check for an overflowed message. Should only happen // on a very fucked up connection that backs up a lot, then // changes level if (host_client->message.overflowed) { SV_DropClient (true); host_client->message.overflowed = false; continue; } if (host_client->message.cursize || host_client->dropasap) { if (!NET_CanSendMessage (host_client->netconnection)) { // I_Printf ("can't write\n"); continue; } if (host_client->dropasap) SV_DropClient (false); // went to another level else { if (NET_SendMessage (host_client->netconnection, &host_client->message) == -1) SV_DropClient (true); // if the message couldn't send, kick off SZ_Clear (&host_client->message); host_client->last_message = realtime; host_client->sendsignon = false; } } } // clear muzzle flashes SV_CleanupEnts (); } /* ============================================================================== SERVER SPAWNING ============================================================================== */ /* ================ SV_ModelIndex ================ */ int SV_ModelIndex (const char *name) { int i; if (!name || !name[0]) return 0; for (i = 0; i < MAX_MODELS && sv.model_precache[i]; i++) { if (!strcmp(sv.model_precache[i], name)) return i; } if (i == MAX_MODELS || !sv.model_precache[i]) { Con_Printf("%s: model %s not precached\n", __thisfunc__, name); return 0; } return i; } /* ================ SV_CreateBaseline ================ */ static void SV_CreateBaseline (void) { int i; edict_t *svent; int entnum; for (entnum = 0; entnum < sv.num_edicts ; entnum++) { // get the current server version svent = EDICT_NUM(entnum); if (svent->free) continue; if (entnum > svs.maxclients && !svent->v.modelindex) continue; // // create entity baseline // VectorCopy (svent->v.origin, svent->baseline.origin); VectorCopy (svent->v.angles, svent->baseline.angles); svent->baseline.frame = svent->v.frame; svent->baseline.skin = svent->v.skin; svent->baseline.scale = (int)(svent->v.scale*100.0)&255; svent->baseline.drawflags = svent->v.drawflags; svent->baseline.abslight = (int)(svent->v.abslight*255.0)&255; if (entnum > 0 && entnum <= svs.maxclients) { svent->baseline.colormap = entnum; svent->baseline.modelindex = 0;//SV_ModelIndex("models/paladin.mdl"); } else { svent->baseline.colormap = 0; svent->baseline.modelindex = SV_ModelIndex(PR_GetString(svent->v.model)); } memset (svent->baseline.ClearCount, 99, sizeof(svent->baseline.ClearCount)); // // add to the message // MSG_WriteByte (&sv.signon,svc_spawnbaseline); MSG_WriteShort (&sv.signon,entnum); MSG_WriteShort (&sv.signon, svent->baseline.modelindex); MSG_WriteByte (&sv.signon, svent->baseline.frame); MSG_WriteByte (&sv.signon, svent->baseline.colormap); MSG_WriteByte (&sv.signon, svent->baseline.skin); MSG_WriteByte (&sv.signon, svent->baseline.scale); MSG_WriteByte (&sv.signon, svent->baseline.drawflags); MSG_WriteByte (&sv.signon, svent->baseline.abslight); for (i = 0; i < 3; i++) { MSG_WriteCoord (&sv.signon, svent->baseline.origin[i]); MSG_WriteAngle (&sv.signon, svent->baseline.angles[i]); } } } /* ================ SV_SendReconnect Tell all the clients that the server is changing levels ================ */ static void SV_SendReconnect (void) { byte data[128]; sizebuf_t msg; SZ_Init (&msg, data, sizeof(data)); MSG_WriteChar (&msg, svc_stufftext); MSG_WriteString (&msg, "reconnect\n"); NET_SendToAll (&msg, 5.0); #if !defined(SERVERONLY) if (!isDedicated) #ifdef QUAKE2 Cbuf_InsertText ("reconnect\n"); #else Cmd_ExecuteString ("reconnect\n", src_command); #endif #endif /* ! SERVERONLY */ } /* ================ SV_GetLevelname Return the full levelname ================ */ const char *SV_GetLevelname (void) { int idx = (int)sv.edicts->v.message; if (idx > 0 && idx <= host_string_count) return Host_GetString(idx - 1); /* return "";*/ /* Use netname on map if there is one, so they don't have to edit strings.txt */ return PR_GetString(sv.edicts->v.netname); } /* ================ SV_SaveSpawnparms Grabs the current state of each client for saving across the transition to another level ================ */ void SV_SaveSpawnparms (void) { int i; // int j; svs.serverflags = *sv_globals.serverflags; for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if (!host_client->active) continue; // call the progs to get default spawn parms for the new client // *sv_globals.self = EDICT_TO_PROG(host_client->edict); // PR_ExecuteProgram (*sv_globals.SetChangeParms); // for (j = 0; j < NUM_SPAWN_PARMS; j++) // host_client->spawn_parms[j] = sv_globals.parm[j]; } } /* ================ SV_SpawnServer This is called at the start of each level ================ */ void SV_SpawnServer (const char *server, const char *startspot) { static char dummy[8] = { 0,0,0,0,0,0,0,0 }; edict_t *ent; int i; // let's not have any servers with no name if (hostname.string[0] == 0) Cvar_Set ("hostname", "UNNAMED"); #if !defined(SERVERONLY) scr_centertime_off = 0; #endif Con_DPrintf ("%s: %s\n", __thisfunc__, server); if (svs.changelevel_issued) { SaveGamestate(true); } // // tell all connected clients that we are going to a new level // if (sv.active) { SV_SendReconnect (); } /* if this is GL version, we need to tell D_FlushCaches() whether to flush OGL textures depending on mapname change. */ #ifdef GLQUAKE flush_textures = q_strncasecmp(server, sv.name, 64) ? true : false; #endif // // make cvars consistant // if (coop.integer) Cvar_Set ("deathmatch", "0"); current_skill = skill.integer; if (current_skill < 0) current_skill = 0; if (current_skill > 4) current_skill = 4; Cvar_SetValue ("skill", current_skill); // // set up the new server // //memset (&sv, 0, sizeof(sv)); Host_ClearMemory (); q_strlcpy (sv.name, server, sizeof(sv.name)); if (startspot) q_strlcpy(sv.startspot, startspot, sizeof(sv.startspot)); // load progs to get entity field count #if !defined(SERVERONLY) total_loading_size = 100; current_loading_size = 0; loading_stage = 1; D_ShowLoadingSize(); #endif PR_LoadProgs (); #if !defined(SERVERONLY) current_loading_size += 10; D_ShowLoadingSize(); #endif Host_LoadStrings(); #if !defined(SERVERONLY) current_loading_size += 5; D_ShowLoadingSize(); #endif // allocate server memory /* Host_ClearMemory() called above already cleared the whole sv structure */ sv.states = (client_state2_t *) Hunk_AllocName (svs.maxclients * sizeof(client_state2_t), "states"); sv.edicts = (edict_t *) Hunk_AllocName (MAX_EDICTS*pr_edict_size, "edicts"); SZ_Init (&sv.datagram, sv.datagram_buf, sizeof(sv.datagram_buf)); SZ_Init (&sv.reliable_datagram, sv.reliable_datagram_buf, sizeof(sv.reliable_datagram_buf)); SZ_Init (&sv.signon, sv.signon_buf, sizeof(sv.signon_buf)); // leave slots at start for clients only sv.num_edicts = svs.maxclients + 1 + max_temp_edicts.integer; for (i = 0; i < svs.maxclients; i++) { ent = EDICT_NUM(i+1); svs.clients[i].edict = ent; svs.clients[i].send_all_v = true; } for (i = 0; i < max_temp_edicts.integer; i++) { ent = EDICT_NUM(i + svs.maxclients + 1); ED_ClearEdict(ent); ent->free = true; ent->freetime = -999; } sv.state = ss_loading; sv.paused = false; sv.time = 1.0; q_strlcpy (sv.name, server, sizeof(sv.name)); q_snprintf (sv.modelname, sizeof(sv.modelname), "maps/%s.bsp", server); sv.worldmodel = Mod_ForName (sv.modelname, false); if (!sv.worldmodel) { Con_Printf ("Couldn't spawn server %s\n", sv.modelname); sv.active = false; #if !defined(SERVERONLY) total_loading_size = 0; loading_stage = 0; #endif return; } sv.models[1] = sv.worldmodel; // // clear world interaction links // SV_ClearWorld (); sv.sound_precache[0] = dummy; sv.model_precache[0] = dummy; sv.model_precache[1] = sv.modelname; for (i = 1; i < sv.worldmodel->numsubmodels; i++) { sv.model_precache[1+i] = localmodels[i]; sv.models[i+1] = Mod_ForName (localmodels[i], false); } // // load the rest of the entities // ent = EDICT_NUM(0); memset (&ent->v, 0, progs->entityfields * 4); ent->free = false; ent->v.model = PR_SetEngineString(sv.worldmodel->name); ent->v.modelindex = 1; // world model ent->v.solid = SOLID_BSP; ent->v.movetype = MOVETYPE_PUSH; if (coop.integer) *sv_globals.coop = coop.value; else *sv_globals.deathmatch = deathmatch.value; if (sv_globals.randomclass) *sv_globals.randomclass = randomclass.value; *sv_globals.mapname = PR_SetEngineString(sv.name); *sv_globals.startspot = PR_SetEngineString(sv.startspot); // serverflags are for cross level information (sigils) *sv_globals.serverflags = svs.serverflags; #if !defined(SERVERONLY) current_loading_size += 5; D_ShowLoadingSize(); #endif ED_LoadFromFile (sv.worldmodel->entities); sv.active = true; // all setup is completed, any further precache statements are errors sv.state = ss_active; // run two frames to allow everything to settle host_frametime = 0.1; SV_Physics (); SV_Physics (); // create a baseline for more efficient communications SV_CreateBaseline (); // send serverinfo to all connected clients for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if (host_client->active) SV_SendServerinfo (host_client); } svs.changelevel_issued = false; // now safe to issue another Con_DPrintf ("Server spawned.\n"); #if !defined(SERVERONLY) total_loading_size = 0; loading_stage = 0; #endif } engine/hexen2/sv_move.c000066400000000000000000000366011444734033100153470ustar00rootroot00000000000000/* * sv_move.c -- monster movement * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #define STEPSIZE 18 /* ============= SV_CheckBottom Returns false if any part of the bottom of the entity is off an edge that is not a staircase. ============= */ qboolean SV_CheckBottom (edict_t *ent) { // By this point, ent has been moved to its new position after the // move, and adjusted for steps /* qmodel_t *model; hull_t *wclip_hull; int index; vec3_t check, size; */ vec3_t mins, maxs, start, stop; trace_t trace; int x, y; float mid, bottom; float save_hull; VectorAdd (ent->v.origin, ent->v.mins, mins); VectorAdd (ent->v.origin, ent->v.maxs, maxs); /* // Make it use the clipping hull's size, not their bounding box... model = sv.models[ (int)sv.edicts->v.modelindex ]; VectorSubtract (ent->v.maxs, ent->v.mins, size); if (ent->v.hull) { index = ent->v.hull-1; wclip_hull = &model->hulls[index]; if (!wclip_hull) // Invalid hull { Con_Printf ("ERROR: hull %d is null.\n",wclip_hull); wclip_hull = &model->hulls[0]; } } else // Using the old way uses size to determine hull to use { if (size[0] < 3) // Point wclip_hull = &model->hulls[0]; else if (size[0] <= 8) // Pentacles wclip_hull = &model->hulls[4]; else if (size[0] <= 32 && size[2] <= 28) // Half Player wclip_hull = &model->hulls[3]; else if (size[0] <= 32) // Full Player wclip_hull = &model->hulls[1]; else // Golumn wclip_hull = &model->hulls[5]; } VectorAdd (ent->v.origin, wclip_hull->clip_mins, mins); VectorAdd (ent->v.origin, wclip_hull->clip_maxs, maxs); */ // if all of the points under the corners are solid world, don't bother // with the tougher checks // the corners must be within 16 of the midpoint start[2] = mins[2] - 1; for (x = 0; x < 2; x++) { for (y = 0; y < 2; y++) { // start[0] = x ? maxs[0] : mins[0]; // start[1] = y ? maxs[1] : mins[1]; if (x) start[0] = maxs[0]; else start[0] = mins[0]; if (y) start[1] = maxs[1]; else start[1] = mins[1]; if (SV_PointContents (start) != CONTENTS_SOLID) goto realcheck; } } return true; // we got out easy realcheck: // check it for real... start[2] = mins[2]; // the midpoint must be within 16 of the bottom start[0] = stop[0] = (mins[0] + maxs[0]) * 0.5; start[1] = stop[1] = (mins[1] + maxs[1]) * 0.5; stop[2] = start[2] - 2*STEPSIZE; // do a trace from the bottom center of the ent down // to 36 below the bottom center of the ent, using a point hull // the "true" in this function is telling SV_Move to consider // this ent's movement as MOVE_NOMONSTERS - means it will // not clip against entities in this move // NOTE: these don't check for trace_allsolid of trace_startsolid. // Technically, these can't possibly be a valid result since // the start point is in the ent, but what about imprecision? save_hull = ent->v.hull;//temp hack so it HullForEntity doesn't calculate the wrong offset ent->v.hull = 0; trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent); ent->v.hull = save_hull; /* if ((int)ent->v.flags & FL_MONSTER) { if (trace.allsolid) Con_DPrintf("Checkbottom midpoint check was all solid!!!\n"); else if (trace.startsolid) Con_DPrintf("Checkbottom midpoint check started solid!!!\n"); } */ if (trace.fraction == 1.0) return false; // trace did not reach full 36 below, so set mid and bottom // to whatever distance it did get to below the ent's // bottom centerpoint (start[2]) mid = bottom = trace.endpos[2]; // the corners must be within 16 of the midpoint for (x = 0; x < 2; x++) { for (y = 0; y < 2; y++) { // check 4 corners, in this order: // x = 0, y = 0 (NE) // x = 0, y = 1 (SE) // x = 1, y = 0 (NW) // x = 1, y = 1 (SW) // start[0] = stop[0] = x ? maxs[0] : mins[0]; // start[1] = stop[1] = y ? maxs[1] : mins[1]; if (x) start[0] = stop[0] = maxs[0]; else start[0] = stop[0] = mins[0]; if (y) start[1] = stop[1] = maxs[1]; else start[1] = stop[1] = mins[1]; // same check as above, just from the 4 corners down 36 save_hull = ent->v.hull;//temp hack so it HullForEntity doesn't calculate the wrong offset ent->v.hull = 0; trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent); ent->v.hull = save_hull; /* if ((int)ent->v.flags & FL_MONSTER) { if (trace.allsolid) Con_DPrintf("Checkbottom (x=%d,y=%d) check was all solid!!!\n",x,y); else if (trace.startsolid) Con_DPrintf("Checkbottom (x=%d,y=%d) check started solid!!!\n",x,y); }*/ // Hit a closer surface than did when checked center, // so set the "bottom" to the new, closer z height // we hit if (trace.fraction != 1.0 && trace.endpos[2] > bottom) bottom = trace.endpos[2]; // one of the corners does not have a surface within // 36 below it, or the surface it did hit is more than // 54 below this corner. This is a really stupid check, // because if it hits a surface more than 54 below the // ent,the trace_fraction will be 1 if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE) return false; } } return true; } static void set_move_trace(trace_t *trace) { *sv_globals.trace_allsolid = trace->allsolid; *sv_globals.trace_startsolid = trace->startsolid; *sv_globals.trace_fraction = trace->fraction; *sv_globals.trace_inwater = trace->inwater; *sv_globals.trace_inopen = trace->inopen; VectorCopy (trace->endpos, *sv_globals.trace_endpos); VectorCopy (trace->plane.normal, *sv_globals.trace_plane_normal); *sv_globals.trace_plane_dist = trace->plane.dist; if (trace->ent) *sv_globals.trace_ent = EDICT_TO_PROG(trace->ent); else *sv_globals.trace_ent = EDICT_TO_PROG(sv.edicts); } /* ============= SV_movestep Called by monster program code. The move will be adjusted for slopes and stairs, but if the move isn't possible, no move is done, false is returned, and *sv_globals.trace_normal is set to the normal of the blocking wall ============= */ qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink, qboolean noenemy, qboolean set_trace) { float dz; vec3_t oldorg, neworg, end; trace_t trace; int i; edict_t *enemy = NULL; // avoid compiler warning. // try the move VectorCopy (ent->v.origin, oldorg); VectorAdd (ent->v.origin, move, neworg); // flying monsters don't step up // unless no_z turned on if ( ((int)ent->v.flags & (FL_SWIM|FL_FLY)) && !((int)ent->v.flags & FL_HUNTFACE) && !((int)ent->v.flags & FL_NOZ) ) { // try one move with vertical motion, then one without for (i = 0; i < 2; i++) { VectorAdd (ent->v.origin, move, neworg); if (!noenemy) { enemy = PROG_TO_EDICT(ent->v.enemy); if (i == 0 && enemy != sv.edicts) { dz = ent->v.origin[2] - PROG_TO_EDICT(ent->v.enemy)->v.origin[2]; // This is utterly broken: we already made sure // that FL_HUNTFACE is not set just above. -O.S if ((int)ent->v.flags & FL_HUNTFACE)//Go for face dz += PROG_TO_EDICT(ent->v.enemy)->v.view_ofs[2]; if (dz > 40) neworg[2] -= 8; else if (dz < 30) neworg[2] += 8; } } if ( ((int)ent->v.flags & FL_SWIM) && SV_PointContents(neworg) == CONTENTS_EMPTY ) { //Would end up out of water, don't do z move neworg[2] = ent->v.origin[2]; trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, neworg, false, ent); if (set_trace) set_move_trace(&trace); if (trace.fraction < 1 || SV_PointContents(trace.endpos) == CONTENTS_EMPTY ) return false; // swim monster left water } else { trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, neworg, false, ent); if (set_trace) set_move_trace(&trace); } if (trace.fraction == 1) { VectorCopy (trace.endpos, ent->v.origin); if (relink) SV_LinkEdict (ent, true); return true; } if (noenemy || enemy == sv.edicts) break; } return false; } // push down from a step height above the wished position neworg[2] += STEPSIZE; VectorCopy (neworg, end); end[2] -= STEPSIZE*2; trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent); if (set_trace) { set_move_trace(&trace); } if (trace.allsolid) { return false; } if (trace.startsolid) { neworg[2] -= STEPSIZE; trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent); if (set_trace) { set_move_trace(&trace); } if (trace.allsolid || trace.startsolid) { return false; } } if (trace.fraction == 1) { // if monster had the ground pulled out, go ahead and fall if ( (int)ent->v.flags & FL_PARTIALGROUND ) { VectorAdd (ent->v.origin, move, ent->v.origin); if (relink) SV_LinkEdict (ent, true); ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; // Con_Printf ("fall down\n"); return true; } return false; // walked off an edge } // check point traces down for dangling corners VectorCopy (trace.endpos, ent->v.origin); if (!SV_CheckBottom (ent)) { if ( (int)ent->v.flags & FL_PARTIALGROUND ) { // entity had floor mostly pulled out from underneath it // and is trying to correct if (relink) SV_LinkEdict (ent, true); return true; } VectorCopy (oldorg, ent->v.origin); return false; } if ( (int)ent->v.flags & FL_PARTIALGROUND ) { // Con_Printf ("back on ground\n"); ent->v.flags = (int)ent->v.flags & ~FL_PARTIALGROUND; } ent->v.groundentity = EDICT_TO_PROG(trace.ent); // the move is ok if (relink) SV_LinkEdict (ent, true); return true; } //============================================================================ /* ====================== SV_StepDirection Turns to the movement direction, and walks the current distance if facing it. ====================== */ extern void PF_changeyaw (void); static qboolean SV_StepDirection (edict_t *ent, float yaw, float dist) { vec3_t move, oldorigin; float delta; qboolean set_trace_plane; ent->v.ideal_yaw = yaw; PF_changeyaw(); yaw = yaw * M_PI * 2 / 360; move[0] = cos(yaw) * dist; move[1] = sin(yaw) * dist; move[2] = 0; //FIXME: Make wallcrawlers and flying monsters use this! VectorCopy (ent->v.origin, oldorigin); if ((int)ent->v.flags & FL_SET_TRACE) set_trace_plane = true; else set_trace_plane = false; if (SV_movestep (ent, move, false, false, set_trace_plane)) { delta = ent->v.angles[YAW] - ent->v.ideal_yaw; if (delta > 45 && delta < 315) { // not turned far enough, so don't take the step VectorCopy (oldorigin, ent->v.origin); } SV_LinkEdict (ent, true); return true; } SV_LinkEdict (ent, true); return false; } /* ====================== SV_FixCheckBottom ====================== */ static void SV_FixCheckBottom (edict_t *ent) { // Con_Printf ("SV_FixCheckBottom\n"); ent->v.flags = (int)ent->v.flags | FL_PARTIALGROUND; } /* ================ SV_NewChaseDir ================ */ #define DI_NODIR -1 static void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist) { float deltax, deltay; // float deltaz; float d[3]; float tdir, olddir, turnaround; olddir = anglemod( (int)(actor->v.ideal_yaw / 45) * 45 ); turnaround = anglemod(olddir - 180); deltax = enemy->v.origin[0] - actor->v.origin[0]; deltay = enemy->v.origin[1] - actor->v.origin[1]; /* if ((int)actor->v.flags & FL_FLY) //Pentacles deltaz = enemy->v.origin[2] + enemy->v.view_ofs[2] - actor->v.origin[2]; else deltaz = enemy->v.origin[2] - actor->v.origin[2]; */ if (deltax > 10) d[1] = 0; else if (deltax < -10) d[1] = 180; else d[1] = DI_NODIR; if (deltay < -10) d[2] = 270; else if (deltay > 10) d[2] = 90; else d[2] = DI_NODIR; /* if (deltaz < -10)//Below d[0] = ; else if (deltaz > 10)//above d[0] = ; else d[0] = DI_NODIR; */ // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { if (d[1] == 0) tdir = d[2] == 90 ? 45 : 315; else tdir = d[2] == 90 ? 135 : 215; if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) return; } // try other directions if ( ((rand() & 3) & 1) || abs((int)deltay) > abs((int)deltax) )//If north/sounth diff is greater than east/west diff?!! { tdir = d[1]; d[1] = d[2]; d[2] = tdir; } if (d[1] != DI_NODIR && d[1] != turnaround && SV_StepDirection(actor, d[1], dist)) return; if (d[2] != DI_NODIR && d[2] != turnaround && SV_StepDirection(actor, d[2], dist)) return; /* there is no direct path to the player, so pick another direction */ if (olddir != DI_NODIR && SV_StepDirection(actor, olddir, dist)) return; if (rand() & 1) /* randomly determine direction of search */ { for (tdir = 0; tdir <= 315; tdir += 45) { if (tdir != turnaround && SV_StepDirection(actor, tdir, dist) ) return; } } else { for (tdir = 315; tdir >= 0; tdir -= 45) { if (tdir != turnaround && SV_StepDirection(actor, tdir, dist) ) return; } } if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) ) return; actor->v.ideal_yaw = olddir; // can't move // if a bridge was pulled out from underneath a monster, it may not have // a valid standing position at all if (!SV_CheckBottom (actor)) SV_FixCheckBottom (actor); } /* ====================== SV_CloseEnough ====================== */ static qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist) { int i; for (i = 0; i < 3; i++) { if (goal->v.absmin[i] > ent->v.absmax[i] + dist) return false; if (goal->v.absmax[i] < ent->v.absmin[i] - dist) return false; } return true; } /* ====================== SV_MoveToGoal ====================== */ void SV_MoveToGoal (void) { edict_t *ent, *goal; float dist; #ifdef QUAKE2 edict_t *enemy; #endif ent = PROG_TO_EDICT(*sv_globals.self); // Entity moving goal = PROG_TO_EDICT(ent->v.goalentity); // its goalentity dist = G_FLOAT(OFS_PARM0); // how far to move // Reset trace_plane_normal VectorClear(*sv_globals.trace_plane_normal); // If not onground, flying, or swimming, return 0 if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) { G_FLOAT(OFS_RETURN) = 0; return; } // if the next step hits the enemy, return immediately #ifdef QUAKE2 // Convert a progs entity to an edict? enemy = PROG_TO_EDICT(ent->v.enemy); // If edict is not world and 2 edicts close enough to touch if dist is moved if (enemy != sv.edicts && SV_CloseEnough(ent, enemy, dist) ) #else if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts && SV_CloseEnough(ent, goal, dist) ) #endif { G_FLOAT(OFS_RETURN) = 0; return; } // bump around... //If can't go in a direction (including step check) or 30% chance... if (!SV_StepDirection (ent, ent->v.ideal_yaw, dist)) { // Find a new direction to go in instead SV_NewChaseDir (ent, goal, dist); G_FLOAT(OFS_RETURN) = 0; } else { if ((rand() & 3) == 1) { // Find a new direction to go in instead SV_NewChaseDir (ent, goal, dist); } G_FLOAT(OFS_RETURN) = 1; } } engine/hexen2/sv_phys.c000066400000000000000000001545441444734033100153730ustar00rootroot00000000000000/* * sv_phys.c -- sv physics * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" /* pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move. onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS corpses are SOLID_NOT and MOVETYPE_TOSS crates are SOLID_BBOX and MOVETYPE_TOSS walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY solid_edge items only clip against bsp models. */ cvar_t sv_maxvelocity = { "sv_maxvelocity", "2000", CVAR_NONE }; cvar_t sv_nostep = { "sv_nostep", "0", CVAR_NONE }; cvar_t sv_gravity = { "sv_gravity", "800", CVAR_NOTIFY|CVAR_SERVERINFO }; cvar_t sv_stopspeed = { "sv_stopspeed", "100", CVAR_NONE }; cvar_t sv_friction = { "sv_friction", "4", CVAR_NOTIFY|CVAR_SERVERINFO }; cvar_t sv_flypitch = { "sv_flypitch", "20", CVAR_NONE }; cvar_t sv_walkpitch = { "sv_walkpitch", "0", CVAR_NONE }; #ifdef QUAKE2 static vec3_t vec_origin = {0.0, 0.0, 0.0}; #endif #define MOVE_EPSILON 0.01 static void SV_Physics_Toss (edict_t *ent); /* ================ SV_CheckAllEnts ================ */ #if 0 // all uses of it are commented out static void SV_CheckAllEnts (void) { int e; edict_t *check; // see if any solid entities are inside the final position check = NEXT_EDICT(sv.edicts); for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT(check)) { if (check->free) continue; if (check->v.movetype == MOVETYPE_PUSH || check->v.movetype == MOVETYPE_NONE #ifdef QUAKE2 || check->v.movetype == MOVETYPE_FOLLOW #endif || check->v.movetype == MOVETYPE_NOCLIP) continue; if (SV_TestEntityPosition (check)) Con_Printf ("entity in invalid position\n"); } } #endif /* ================ SV_CheckVelocity ================ */ static void SV_CheckVelocity (edict_t *ent) { int i; float w; // // bound velocity // for (i = 0; i < 3; i++) { if (IS_NAN(ent->v.velocity[i])) { Con_DPrintf ("Got a NaN velocity on %s\n", PR_GetString(ent->v.classname)); ent->v.velocity[i] = 0; } if (IS_NAN(ent->v.origin[i])) { Con_DPrintf ("Got a NaN origin on %s\n", PR_GetString(ent->v.classname)); ent->v.origin[i] = 0; } } w = VectorLength(ent->v.velocity); if (w > sv_maxvelocity.value) { // sv_maxvelocity fix by Maddes VectorScale (ent->v.velocity, sv_maxvelocity.value/w, ent->v.velocity); } } /* ============= SV_RunThink Runs thinking code if time. There is some play in the exact time the think function will be called, because it is called before any movement is done in a frame. Not used for pushmove objects, because they must be exact. Returns false if the entity removed itself. ============= */ static qboolean SV_RunThink (edict_t *ent) { float thinktime; thinktime = ent->v.nextthink; #ifdef PLATFORM_AMIGAOS3 // SV_SpawnServer race condition workaround for meso2 and romeric5 if (thinktime <= 0 || (thinktime + 0.0000001) > sv.time + host_frametime) #else if (thinktime <= 0 || thinktime > sv.time + host_frametime) #endif return true; if (thinktime < sv.time) thinktime = sv.time; // don't let things stay in the past. // it is possible to start that way // by a trigger with a local time. ent->v.nextthink = 0; *sv_globals.time = thinktime; *sv_globals.self = EDICT_TO_PROG(ent); *sv_globals.other = EDICT_TO_PROG(sv.edicts); PR_ExecuteProgram (ent->v.think); return !ent->free; } /* ================== SV_Impact Two entities have touched, so run their touch functions ================== */ static void SV_Impact (edict_t *e1, edict_t *e2) { int old_self, old_other; old_self = *sv_globals.self; old_other = *sv_globals.other; *sv_globals.time = sv.time; if (e1->v.touch && e1->v.solid != SOLID_NOT) { *sv_globals.self = EDICT_TO_PROG(e1); *sv_globals.other = EDICT_TO_PROG(e2); PR_ExecuteProgram (e1->v.touch); } if (e2->v.touch && e2->v.solid != SOLID_NOT) { *sv_globals.self = EDICT_TO_PROG(e2); *sv_globals.other = EDICT_TO_PROG(e1); PR_ExecuteProgram (e2->v.touch); } *sv_globals.self = old_self; *sv_globals.other = old_other; } /* ================== ClipVelocity Slide off of the impacting object returns the blocked flags (1 = floor, 2 = step / wall) ================== */ #define STOP_EPSILON 0.1 static int ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce) { float backoff; float change; int i, blocked; blocked = 0; if (normal[2] > 0) blocked |= 1; // floor if (!normal[2]) blocked |= 2; // step backoff = DotProduct (in, normal) * overbounce; for (i = 0; i < 3; i++) { change = normal[i]*backoff; out[i] = in[i] - change; if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) out[i] = 0; } return blocked; } /* ============ SV_FlyMove The basic solid body movement clip that slides along multiple planes Returns the clipflags if the velocity was modified (hit something solid) 1 = floor 2 = wall / step 4 = dead stop If steptrace is not NULL, the trace of any vertical wall hit will be stored ============ */ #define MAX_CLIP_PLANES 5 static int SV_FlyMove (edict_t *ent, float time, trace_t *steptrace) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity, original_velocity, new_velocity; //rjr vec3_t before_velocity; int i, j; trace_t trace; vec3_t end; float time_left; int blocked; numbumps = 4; blocked = 0; VectorCopy (ent->v.velocity, original_velocity); VectorCopy (ent->v.velocity, primal_velocity); numplanes = 0; time_left = time; for (bumpcount = 0; bumpcount < numbumps; bumpcount++) { if (!ent->v.velocity[0] && !ent->v.velocity[1] && !ent->v.velocity[2]) break; for (i = 0; i < 3; i++) end[i] = ent->v.origin[i] + time_left * ent->v.velocity[i]; trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent); if (trace.allsolid) { // entity is trapped in another solid VectorClear (ent->v.velocity); return 3; } if (trace.fraction > 0) { // actually covered some distance VectorCopy (trace.endpos, ent->v.origin); VectorCopy (ent->v.velocity, original_velocity); numplanes = 0; } if (trace.fraction == 1) break; // moved the entire distance if (!trace.ent) Sys_Error ("%s: !trace.ent", __thisfunc__); if (trace.plane.normal[2] > 0.7) { blocked |= 1; // floor if (trace.ent->v.solid == SOLID_BSP) { ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = EDICT_TO_PROG(trace.ent); } } if (!trace.plane.normal[2]) { blocked |= 2; // step if (steptrace) *steptrace = trace; // save for player extrafriction } //rjr VectorCopy (ent->v.velocity, before_velocity); // // run the impact function // SV_Impact (ent, trace.ent); if (ent->free) break; // removed by the impact function /*rjr if (!VectorCompare(ent->v.velocity, before_velocity)) { break; } */ time_left -= time_left * trace.fraction; // cliped to another plane if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen VectorClear (ent->v.velocity); return 3; } VectorCopy (trace.plane.normal, planes[numplanes]); numplanes++; // // modify original_velocity so it parallels all of the clip planes // for (i = 0; i < numplanes; i++) { ClipVelocity (original_velocity, planes[i], new_velocity, 1); for (j = 0; j < numplanes; j++) { if (j != i) { if (DotProduct (new_velocity, planes[j]) < 0) break; // not ok } } if (j == numplanes) break; } if (i != numplanes) { // go along this plane VectorCopy (new_velocity, ent->v.velocity); } else { // go along the crease if (numplanes != 2) { // Con_Printf ("clip velocity, numplanes == %i\n",numplanes); VectorClear (ent->v.velocity); return 7; } CrossProduct (planes[0], planes[1], dir); d = DotProduct (dir, ent->v.velocity); VectorScale (dir, d, ent->v.velocity); } // // if original velocity is against the original velocity, stop dead // to avoid tiny occilations in sloping corners // if (DotProduct (ent->v.velocity, primal_velocity) <= 0) { VectorClear (ent->v.velocity); return blocked; } } return blocked; } /* ============ SV_FlyExtras ============ */ static const float hoverinc = 0.4; static void SV_FlyExtras (edict_t *ent, float time, trace_t *steptrace) { // Jumping makes you loose this flag so reset it ent->v.flags = (int) ent->v.flags | FL_ONGROUND; if ((ent->v.velocity[2] <= 6) && (ent->v.velocity[2] >= -6)) { ent->v.velocity[2] += ent->v.hoverz; if (ent->v.velocity[2] >= 6) { ent->v.hoverz = -hoverinc; ent->v.velocity[2] += ent->v.hoverz; } else if (ent->v.velocity[2] <= -6) { ent->v.hoverz = hoverinc; ent->v.velocity[2] += ent->v.hoverz; } } else // friction for upward or downward progress once key is released { ent->v.velocity[2] -= sv_player->v.velocity[2] * .1; } } /* ============ SV_AddGravity ============ */ static void SV_AddGravity (edict_t *ent) { float ent_gravity; #ifdef QUAKE2 if (ent->v.gravity) ent_gravity = ent->v.gravity; else ent_gravity = 1.0; #else eval_t *val; val = GetEdictFieldValue(ent, "gravity"); if (val && val->_float) ent_gravity = val->_float; else ent_gravity = 1.0; #endif ent->v.velocity[2] -= ent_gravity * sv_gravity.value * host_frametime; } /* =============================================================================== PUSHMOVE =============================================================================== */ /* ============ SV_PushEntity Does not change the entities velocity at all ============ */ static trace_t SV_PushEntity (edict_t *ent, vec3_t push) { trace_t trace; vec3_t start, end, impact; edict_t *impact_e; VectorCopy (ent->v.origin, start); (void) start; /* variable set but not used */ VectorAdd (ent->v.origin, push, end); if (ent->v.movetype == MOVETYPE_FLYMISSILE || ent->v.movetype == MOVETYPE_BOUNCEMISSILE) trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_MISSILE, ent); else if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT) // only clip against bmodels trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS, ent); else if (ent->v.movetype == MOVETYPE_SWIM) trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_WATER, ent); else trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent); if (ent->v.solid != SOLID_PHASE) { if (ent->v.movetype != MOVETYPE_BOUNCE || (trace.allsolid == 0 && trace.startsolid == 0)) { VectorCopy (trace.endpos, ent->v.origin); // Macro - watchout } else { trace.fraction = 0; return trace; } } else // Entity is PHASED so bounce off walls and other entities, go through monsters and players { if (trace.ent) { // Go through MONSTERS and PLAYERS, can't use FL_CLIENT cause rotating brushes do if (((int) trace.ent->v.flags & FL_MONSTER) || (trace.ent->v.movetype == MOVETYPE_WALK)) { VectorCopy (trace.endpos, impact); impact_e = trace.ent; trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_PHASE, ent); VectorCopy (impact, ent->v.origin); SV_Impact (ent, impact_e); VectorCopy (trace.endpos, ent->v.origin); } else { VectorCopy (trace.endpos, ent->v.origin); } } else { VectorCopy (trace.endpos, ent->v.origin); } } SV_LinkEdict (ent, true); if (trace.ent) SV_Impact (ent, trace.ent); return trace; } /* ============ SV_PushMove ============ */ static void SV_PushMove (edict_t *pusher, float movetime, qboolean update_time) { int i, e; edict_t *check, *block; vec3_t mins, maxs, move; vec3_t entorig, pushorig; int num_moved; edict_t *moved_edict[MAX_EDICTS]; vec3_t moved_from[MAX_EDICTS]; if (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2]) { if (update_time) pusher->v.ltime += movetime; return; } for (i = 0; i < 3; i++) { move[i] = pusher->v.velocity[i] * movetime; mins[i] = pusher->v.absmin[i] + move[i]; maxs[i] = pusher->v.absmax[i] + move[i]; } VectorCopy (pusher->v.origin, pushorig); // move the pusher to it's final position VectorAdd (pusher->v.origin, move, pusher->v.origin); if (update_time) pusher->v.ltime += movetime; SV_LinkEdict (pusher, false); // see if any solid entities are inside the final position num_moved = 0; check = NEXT_EDICT(sv.edicts); for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT(check)) { if (check->free) continue; if (check->v.movetype == MOVETYPE_PUSH || check->v.movetype == MOVETYPE_NONE #ifdef QUAKE2 || check->v.movetype == MOVETYPE_FOLLOW #endif || check->v.movetype == MOVETYPE_NOCLIP) continue; // if the entity is standing on the pusher, it will definately be moved if ( ! ( ((int)check->v.flags & FL_ONGROUND) && PROG_TO_EDICT(check->v.groundentity) == pusher) ) { if ( check->v.absmin[0] >= maxs[0] || check->v.absmin[1] >= maxs[1] || check->v.absmin[2] >= maxs[2] || check->v.absmax[0] <= mins[0] || check->v.absmax[1] <= mins[1] || check->v.absmax[2] <= mins[2] ) continue; // see if the ent's bbox is inside the pusher's final position if (!SV_TestEntityPosition (check)) continue; } // remove the onground flag for non-players if (check->v.movetype != MOVETYPE_WALK) check->v.flags = (int)check->v.flags & ~FL_ONGROUND; VectorCopy (check->v.origin, entorig); VectorCopy (check->v.origin, moved_from[num_moved]); moved_edict[num_moved] = check; num_moved++; // try moving the contacted entity pusher->v.solid = SOLID_NOT; SV_PushEntity (check, move); pusher->v.solid = SOLID_BSP; // if it is still inside the pusher, block block = SV_TestEntityPosition (check); if (block) { // fail the move if (check->v.mins[0] == check->v.maxs[0]) continue; if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER) { // corpse check->v.mins[0] = check->v.mins[1] = 0; VectorCopy (check->v.mins, check->v.maxs); continue; } VectorCopy (entorig, check->v.origin); SV_LinkEdict (check, true); VectorCopy (pushorig, pusher->v.origin); SV_LinkEdict (pusher, false); if (update_time) pusher->v.ltime -= movetime; // if the pusher has a "blocked" function, call it // otherwise, just stay in place until the obstacle is gone if (pusher->v.blocked) { *sv_globals.self = EDICT_TO_PROG(pusher); *sv_globals.other = EDICT_TO_PROG(check); PR_ExecuteProgram (pusher->v.blocked); } // move back any entities we already moved for (i = 0; i < num_moved; i++) { VectorCopy (moved_from[i], moved_edict[i]->v.origin); SV_LinkEdict (moved_edict[i], false); } return; } } } /* ============ SV_PushRotate Pre-Mission Pack fix ============ */ #if 0 // Pre-Mission Pack fix static void SV_PushRotate (edict_t *pusher, float movetime) { int i, e; edict_t *check, *block; vec3_t move, a, amove; vec3_t entorig, pushorig; int num_moved; edict_t *moved_edict[MAX_EDICTS]; vec3_t moved_from[MAX_EDICTS]; vec3_t org, org2; vec3_t forward, right, up; edict_t *ground; // edict_t *master; edict_t *slave; int slaves_moved; qboolean moveit; # if 0 Con_DPrintf("%s entity %i (time=%f)\n", __thisfunc__, NUM_FOR_EDICT(pusher), movetime); Con_DPrintf("%f %f %f (avelocity)\n", pusher->v.avelocity[0], pusher->v.avelocity[1], pusher->v.avelocity[2]); Con_DPrintf("%f %f %f\n", pusher->v.angles[0], pusher->v.angles[1], pusher->v.angles[2]); # endif for (i = 0; i < 3; i++) amove[i] = pusher->v.avelocity[i] * movetime; VectorNegate (amove, a); AngleVectors (a, forward, right, up); VectorCopy (pusher->v.angles, pushorig); // move the pusher to it's final position VectorAdd (pusher->v.angles, amove, pusher->v.angles); pusher->v.ltime += movetime; SV_LinkEdict (pusher, false); slaves_moved = 0; /* master = pusher; while (master->v.aiment) { slave = PROG_TO_EDICT(master->v.aiment); // Con_DPrintf("%f %f %f slave entity %i\n", slave->v.angles[0], slave->v.angles[1], slave->v.angles[2], NUM_FOR_EDICT(slave)); slaves_moved++; VectorCopy (slave->v.angles, moved_from[MAX_EDICTS - slaves_moved]); moved_edict[MAX_EDICTS - slaves_moved] = slave; if (slave->v.movedir[PITCH]) slave->v.angles[PITCH] = master->v.angles[PITCH]; else slave->v.angles[PITCH] += slave->v.avelocity[PITCH] * movetime; if (slave->v.movedir[YAW]) slave->v.angles[YAW] = master->v.angles[YAW]; else slave->v.angles[YAW] += slave->v.avelocity[YAW] * movetime; if (slave->v.movedir[ROLL]) slave->v.angles[ROLL] = master->v.angles[ROLL]; else slave->v.angles[ROLL] += slave->v.avelocity[ROLL] * movetime; slave->v.ltime = master->v.ltime; SV_LinkEdict (slave, false); master = slave; } */ // see if any solid entities are inside the final position num_moved = 0; check = NEXT_EDICT(sv.edicts); for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT(check)) { if (check->free) continue; if (check->v.movetype == MOVETYPE_PUSH || check->v.movetype == MOVETYPE_NONE || check->v.movetype == MOVETYPE_FOLLOW || check->v.movetype == MOVETYPE_NOCLIP) continue; // if the entity is standing on the pusher, it will definitely be moved moveit = false; ground = PROG_TO_EDICT(check->v.groundentity); if ((int)check->v.flags & FL_ONGROUND) { if (ground == pusher) { moveit = true; } else { for (i = 0; i < slaves_moved; i++) { if (ground == moved_edict[MAX_EDICTS - i - 1]) { moveit = true; break; } } } } if (!moveit) { if ( check->v.absmin[0] >= pusher->v.absmax[0] || check->v.absmin[1] >= pusher->v.absmax[1] || check->v.absmin[2] >= pusher->v.absmax[2] || check->v.absmax[0] <= pusher->v.absmin[0] || check->v.absmax[1] <= pusher->v.absmin[1] || check->v.absmax[2] <= pusher->v.absmin[2] ) { for (i = 0; i < slaves_moved; i++) { slave = moved_edict[MAX_EDICTS - i - 1]; if ( check->v.absmin[0] >= slave->v.absmax[0] || check->v.absmin[1] >= slave->v.absmax[1] || check->v.absmin[2] >= slave->v.absmax[2] || check->v.absmax[0] <= slave->v.absmin[0] || check->v.absmax[1] <= slave->v.absmin[1] || check->v.absmax[2] <= slave->v.absmin[2] ) continue; } if (i == slaves_moved) continue; } // see if the ent's bbox is inside the pusher's final position if (!SV_TestEntityPosition (check)) continue; } // remove the onground flag for non-players if (check->v.movetype != MOVETYPE_WALK) check->v.flags = (int)check->v.flags & ~FL_ONGROUND; VectorCopy (check->v.origin, entorig); VectorCopy (check->v.origin, moved_from[num_moved]); moved_edict[num_moved] = check; num_moved++; // calculate destination position VectorSubtract (check->v.origin, pusher->v.origin, org); org2[0] = DotProduct (org, forward); org2[1] = -DotProduct (org, right); org2[2] = DotProduct (org, up); VectorSubtract (org2, org, move); // try moving the contacted entity pusher->v.solid = SOLID_NOT; SV_PushEntity (check, move); //@@TODO: do we ever want to do anybody's angles? maybe just yaw??? // if (!((int)check->v.flags & (FL_CLIENT | FL_MONSTER))) // VectorAdd (check->v.angles, amove, check->v.angles); check->v.angles[YAW] += amove[YAW]; pusher->v.solid = SOLID_BSP; // if it is still inside the pusher, block block = SV_TestEntityPosition (check); if (block) { // fail the move if (check->v.mins[0] == check->v.maxs[0]) continue; if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER) { // corpse check->v.mins[0] = check->v.mins[1] = 0; VectorCopy (check->v.mins, check->v.maxs); continue; } VectorCopy (entorig, check->v.origin); SV_LinkEdict (check, true); VectorCopy (pushorig, pusher->v.angles); SV_LinkEdict (pusher, false); pusher->v.ltime -= movetime; for (i = 0; i < slaves_moved; i++) { slave = moved_edict[MAX_EDICTS - i - 1]; VectorCopy (moved_from[MAX_EDICTS - i - 1], slave->v.angles); SV_LinkEdict (slave, false); slave->v.ltime -= movetime; } // if the pusher has a "blocked" function, call it // otherwise, just stay in place until the obstacle is gone if (pusher->v.blocked) { *sv_globals.self = EDICT_TO_PROG(pusher); *sv_globals.other = EDICT_TO_PROG(check); PR_ExecuteProgram (pusher->v.blocked); } // move back any entities we already moved for (i = 0; i < num_moved; i++) { VectorCopy (moved_from[i], moved_edict[i]->v.origin); //@@TODO:: see above // if (!((int)moved_edict[i]->v.flags & (FL_CLIENT | FL_MONSTER))) // VectorSubtract (moved_edict[i]->v.angles, amove, moved_edict[i]->v.angles); moved_edict[i]->v.angles[YAW] -= amove[YAW]; SV_LinkEdict (moved_edict[i], false); } return; } } # if 0 Con_DPrintf("result:\n"); Con_DPrintf("%f %f %f\n", pusher->v.angles[0], pusher->v.angles[1], pusher->v.angles[2]); for (i = 0; i < slaves_moved; i++) { slave = moved_edict[MAX_EDICTS - i - 1]; Con_DPrintf("%f %f %f slave entity %i\n", slave->v.angles[0], slave->v.angles[1], slave->v.angles[2], NUM_FOR_EDICT(slave)); } Con_DPrintf("\n"); # endif } #endif // end of Pre-Mission Pack fix /* ============ SV_PushRotate NEW ============ */ static void SV_PushRotate (edict_t *pusher, float movetime) { int i, e, t; edict_t *check, *block; vec3_t move, a, amove, mins, maxs, move2, move3, testmove; // vec3_t amove_norm; vec3_t entorig, pushorig, pushorigangles; int num_moved; edict_t *moved_edict[MAX_EDICTS]; vec3_t moved_from[MAX_EDICTS]; vec3_t org, org2, check_center; vec3_t forward, right, up; edict_t *ground; // edict_t *master; edict_t *slave; int slaves_moved; qboolean moveit; // float turn_away, amove_mag; # if 0 Con_DPrintf("%s entity %i (time=%f)\n", __thisfunc__, NUM_FOR_EDICT(pusher), movetime); Con_DPrintf("%f %f %f (avelocity)\n", pusher->v.avelocity[0], pusher->v.avelocity[1], pusher->v.avelocity[2]); Con_DPrintf("%f %f %f\n", pusher->v.angles[0], pusher->v.angles[1], pusher->v.angles[2]); # endif for (i = 0; i < 3; i++) { amove[i] = pusher->v.avelocity[i] * movetime; move[i] = pusher->v.velocity[i] * movetime; mins[i] = pusher->v.absmin[i] + move[i]; maxs[i] = pusher->v.absmax[i] + move[i]; } VectorNegate (amove, a); AngleVectors (a, forward, right, up); VectorCopy (pusher->v.origin, pushorig); VectorCopy (pusher->v.angles, pushorigangles); // move the pusher to it's final position VectorAdd (pusher->v.origin, move, pusher->v.origin); VectorAdd (pusher->v.angles, amove, pusher->v.angles); pusher->v.ltime += movetime; SV_LinkEdict (pusher, false); slaves_moved = 0; /* master = pusher; while (master->v.aiment) { slave = PROG_TO_EDICT(master->v.aiment); // Con_DPrintf("%f %f %f slave entity %i\n", slave->v.angles[0], slave->v.angles[1], slave->v.angles[2], NUM_FOR_EDICT(slave)); slaves_moved++; VectorCopy (slave->v.angles, moved_from[MAX_EDICTS - slaves_moved]); moved_edict[MAX_EDICTS - slaves_moved] = slave; if (slave->v.movedir[PITCH]) slave->v.angles[PITCH] = master->v.angles[PITCH]; else slave->v.angles[PITCH] += slave->v.avelocity[PITCH] * movetime; if (slave->v.movedir[YAW]) slave->v.angles[YAW] = master->v.angles[YAW]; else slave->v.angles[YAW] += slave->v.avelocity[YAW] * movetime; if (slave->v.movedir[ROLL]) slave->v.angles[ROLL] = master->v.angles[ROLL]; else slave->v.angles[ROLL] += slave->v.avelocity[ROLL] * movetime; slave->v.ltime = master->v.ltime; SV_LinkEdict (slave, false); master = slave; } */ // see if any solid entities are inside the final position num_moved = 0; check = NEXT_EDICT(sv.edicts); VectorClear(testmove); // avoid compiler warning: testmove is // initialized with case 0 in the switch for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT(check)) { if (check->free) continue; if (check->v.movetype == MOVETYPE_PUSH || check->v.movetype == MOVETYPE_NONE || check->v.movetype == MOVETYPE_FOLLOW || check->v.movetype == MOVETYPE_NOCLIP) continue; // if the entity is standing on the pusher, it will definitely be moved moveit = false; ground = PROG_TO_EDICT(check->v.groundentity); if ((int)check->v.flags & FL_ONGROUND) { if (ground == pusher) { moveit = true; } else { for (i = 0; i < slaves_moved; i++) { if (ground == moved_edict[MAX_EDICTS - i - 1]) { moveit = true; break; } } } } if (!moveit) { if ( check->v.absmin[0] >= maxs[0] || check->v.absmin[1] >= maxs[1] || check->v.absmin[2] >= maxs[2] || check->v.absmax[0] <= mins[0] || check->v.absmax[1] <= mins[1] || check->v.absmax[2] <= mins[2] ) { for (i = 0; i < slaves_moved; i++) { slave = moved_edict[MAX_EDICTS - i - 1]; if ( check->v.absmin[0] >= slave->v.absmax[0] || check->v.absmin[1] >= slave->v.absmax[1] || check->v.absmin[2] >= slave->v.absmax[2] || check->v.absmax[0] <= slave->v.absmin[0] || check->v.absmax[1] <= slave->v.absmin[1] || check->v.absmax[2] <= slave->v.absmin[2] ) continue; } if (i == slaves_moved) continue; } // see if the ent's bbox is inside the pusher's final position if (!SV_TestEntityPosition (check)) continue; } // remove the onground flag for non-players if (check->v.movetype != MOVETYPE_WALK) check->v.flags = (int)check->v.flags & ~FL_ONGROUND; VectorCopy (check->v.origin, entorig); VectorCopy (check->v.origin, moved_from[num_moved]); moved_edict[num_moved] = check; num_moved++; // put check in first move spot VectorAdd (check->v.origin, move, check->v.origin); // Use center of model, like in QUAKE!!!! // Our origins are on the bottom!!! for (i = 0; i < 3; i++) check_center[i] = (check->v.absmin[i] + check->v.absmax[i]) / 2; // calculate destination position VectorSubtract (check_center, pusher->v.origin, org); // put check back VectorSubtract (check->v.origin, move, check->v.origin); org2[0] = DotProduct (org, forward); org2[1] = -DotProduct (org, right); org2[2] = DotProduct (org, up); VectorSubtract (org2, org, move2); // Con_DPrintf("%f %f %f (move2)\n", move2[0], move2[1], move2[2]); // VectorAdd (check->v.origin, move2, check->v.origin); // Add all moves together VectorAdd(move,move2,move3); // Find the angle of rotation as compared to vector from pusher // origin to check center // turn_away = DotProduct(org,a); // try moving the contacted entity for (t = 0; t < 13; t++) { switch (t) { case 0: //try x, y and z VectorCopy(move3,testmove); break; case 1: //Try xy only VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = move3[0]; testmove[1] = move3[1]; testmove[2] = 0; break; case 2: //Try z only VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = 0; testmove[1] = 0; testmove[2] = move3[2]; break; case 3: //Try none VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = 0; testmove[1] = 0; testmove[2] = 0; break; case 4: //Try xy in opposite dir testmove[0] = move3[0] * -1; testmove[1] = move3[1] * -1; testmove[2] = move3[2]; break; case 5: //Try z in opposite dir VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = move3[0]; testmove[1] = move3[1]; testmove[2] = move3[2] * -1; break; case 6: //Try xyz in opposite dir VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = move3[0] * -1; testmove[1] = move3[1] * -1; testmove[2] = move3[2] * -1; break; case 7: //Try move3 times 2 VectorSubtract(check->v.origin,testmove,check->v.origin); VectorScale(move3,2,testmove); break; case 8: //Try normalized org VectorSubtract(check->v.origin,testmove,check->v.origin); // VectorCopy(amove,amove_norm); // amove_mag = VectorNormalize(amove_norm); // //VectorNormalize(org); // VectorScale(org,amove_mag,org); // VectorNormalize(org); VectorScale(org,movetime,org);//movetime*20? VectorCopy(org,testmove); break; case 9: //Try normalized org z * 3 only VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = 0; testmove[1] = 0; testmove[2] = org[2] * 3;//was: +org[2]*(fabs(org[1])+fabs(org[2])); break; case 10: //Try normalized org xy * 2 only VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = org[0] * 2;//was: +org[0]*fabs(org[2]); testmove[1] = org[1] * 2;//was: +org[1]*fabs(org[2]); testmove[2] = 0; break; case 11: //Try xy in opposite org dir VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = org[0] * -2; testmove[1] = org[1] * -2; testmove[2] = org[2]; break; case 12: //Try z in opposite dir VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = org[0]; testmove[1] = org[1]; testmove[2] = org[2] * -3; break; } if (t != 3) { //THIS IS VERY BAD BAD HACK... pusher->v.solid = SOLID_NOT; SV_PushEntity (check, move3); //@@TODO: do we ever want to do anybody's angles? maybe just yaw??? // if (!((int)check->v.flags & (FL_CLIENT | FL_MONSTER))) // VectorAdd (check->v.angles, amove, check->v.angles); check->v.angles[YAW] += amove[YAW]; pusher->v.solid = SOLID_BSP; } // if it is still inside the pusher, block block = SV_TestEntityPosition (check); if (!block) break; } // Con_DPrintf("t: %i\n",t); // if (turn_away > 0) // { if (block) { // fail the move // Con_DPrintf("Check blocked\n"); if (check->v.mins[0] == check->v.maxs[0]) continue; if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER) { // corpse check->v.mins[0] = check->v.mins[1] = 0; VectorCopy (check->v.mins, check->v.maxs); continue; } VectorCopy (entorig, check->v.origin); SV_LinkEdict (check, true); VectorCopy (pushorig, pusher->v.origin); VectorCopy (pushorigangles, pusher->v.angles); SV_LinkEdict (pusher, false); pusher->v.ltime -= movetime; for (i = 0; i < slaves_moved; i++) { slave = moved_edict[MAX_EDICTS - i - 1]; VectorCopy (moved_from[MAX_EDICTS - i - 1], slave->v.angles); SV_LinkEdict (slave, false); slave->v.ltime -= movetime; } // if the pusher has a "blocked" function, call it // otherwise, just stay in place until the obstacle is gone if (pusher->v.blocked) { *sv_globals.self = EDICT_TO_PROG(pusher); *sv_globals.other = EDICT_TO_PROG(check); PR_ExecuteProgram (pusher->v.blocked); } // move back any entities we already moved for (i = 0; i < num_moved; i++) { VectorCopy (moved_from[i], moved_edict[i]->v.origin); //@@TODO:: see above // if (!((int)moved_edict[i]->v.flags & (FL_CLIENT | FL_MONSTER))) // VectorSubtract (moved_edict[i]->v.angles, amove, moved_edict[i]->v.angles); moved_edict[i]->v.angles[YAW] -= amove[YAW]; SV_LinkEdict (moved_edict[i], false); } return; } // } // else if (block) // undo last move // VectorCopy (entorig, check->v.origin); } # if 0 Con_DPrintf("result:\n"); Con_DPrintf("%f %f %f\n", pusher->v.angles[0], pusher->v.angles[1], pusher->v.angles[2]); for (i = 0; i < slaves_moved; i++) { slave = moved_edict[MAX_EDICTS - i - 1]; Con_DPrintf("%f %f %f slave entity %i\n", slave->v.angles[0], slave->v.angles[1], slave->v.angles[2], NUM_FOR_EDICT(slave)); } Con_DPrintf("\n"); # endif } /* ================ SV_Physics_Pusher ================ */ static void SV_Physics_Pusher (edict_t *ent) { float thinktime; float oldltime; float movetime; oldltime = ent->v.ltime; thinktime = ent->v.nextthink; if (thinktime < ent->v.ltime + host_frametime) { movetime = thinktime - ent->v.ltime; if (movetime < 0) movetime = 0; } else movetime = host_frametime; if (movetime) { if (ent->v.avelocity[0] || ent->v.avelocity[1] || ent->v.avelocity[2]) { //SV_PushMove (ent, movetime, false); SV_PushRotate (ent, movetime); } else SV_PushMove (ent, movetime, true); // advances ent->v.ltime if not blocked } #ifdef PLATFORM_AMIGAOS3 // SV_SpawnServer race condition workaround for meso2 and romeric5 if (thinktime > oldltime && (thinktime - 0.00000001) <= ent->v.ltime) #else if (thinktime > oldltime && thinktime <= ent->v.ltime) #endif { ent->v.nextthink = 0; *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(ent); *sv_globals.other = EDICT_TO_PROG(sv.edicts); PR_ExecuteProgram (ent->v.think); if (ent->free) return; } } /* =============================================================================== CLIENT MOVEMENT =============================================================================== */ /* ============= SV_CheckStuck This is a big hack to try and fix the rare case of getting stuck in the world clipping hull. ============= */ static void SV_CheckStuck (edict_t *ent) { int i, j; int z; vec3_t org; if (!SV_TestEntityPosition(ent)) { VectorCopy (ent->v.origin, ent->v.oldorigin); return; } VectorCopy (ent->v.origin, org); VectorCopy (ent->v.oldorigin, ent->v.origin); if (!SV_TestEntityPosition(ent)) { Con_DPrintf ("moved back Unstuck.\n"); SV_LinkEdict (ent, true); return; } for (z = 0; z < 18; z++) { for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { ent->v.origin[0] = org[0] + i; ent->v.origin[1] = org[1] + j; ent->v.origin[2] = org[2] + z; if (!SV_TestEntityPosition(ent)) { Con_DPrintf ("scooted Unstuck.\n"); SV_LinkEdict (ent, true); return; } } } } VectorCopy (org, ent->v.origin); if (ent->v.oldorigin!=ent->v.origin) Con_DPrintf ("player is stuck.\n"); } /* ============= SV_CheckWater ============= */ static qboolean SV_CheckWater (edict_t *ent) { vec3_t point; int cont; #ifdef QUAKE2 int truecont; #endif point[0] = ent->v.origin[0]; point[1] = ent->v.origin[1]; point[2] = ent->v.origin[2] + ent->v.mins[2] + 1; ent->v.waterlevel = 0; ent->v.watertype = CONTENTS_EMPTY; cont = SV_PointContents (point); if (cont <= CONTENTS_WATER) { #ifdef QUAKE2 truecont = SV_TruePointContents (point); #endif ent->v.watertype = cont; ent->v.waterlevel = 1; point[2] = ent->v.origin[2] + (ent->v.mins[2] + ent->v.maxs[2])*0.5; cont = SV_PointContents (point); if (cont <= CONTENTS_WATER) { ent->v.waterlevel = 2; point[2] = ent->v.origin[2] + ent->v.view_ofs[2]; cont = SV_PointContents (point); if (cont <= CONTENTS_WATER) ent->v.waterlevel = 3; } #ifdef QUAKE2 if (truecont <= CONTENTS_CURRENT_0 && truecont >= CONTENTS_CURRENT_DOWN) { static vec3_t current_table[] = { {1, 0, 0}, {0, 1, 0}, {-1, 0, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1} }; VectorMA (ent->v.basevelocity, 150.0*ent->v.waterlevel/3.0, current_table[CONTENTS_CURRENT_0 - truecont], ent->v.basevelocity); } #endif } return ent->v.waterlevel > 1; } /* ============ SV_WallFriction ============ */ static void SV_WallFriction (edict_t *ent, trace_t *trace) { vec3_t forward, right, up; float d, i; vec3_t into, side; AngleVectors (ent->v.v_angle, forward, right, up); d = DotProduct (trace->plane.normal, forward); d += 0.5; if (d >= 0) return; // cut the tangential velocity i = DotProduct (trace->plane.normal, ent->v.velocity); VectorScale (trace->plane.normal, i, into); VectorSubtract (ent->v.velocity, into, side); ent->v.velocity[0] = side[0] * (1 + d); ent->v.velocity[1] = side[1] * (1 + d); } /* ===================== SV_TryUnstick Player has come to a dead stop, possibly due to the problem with limited float precision at some angle joins in the BSP hull. Try fixing by pushing one pixel in each direction. This is a hack, but in the interest of good gameplay... ====================== */ static int SV_TryUnstick (edict_t *ent, vec3_t oldvel) { int i; vec3_t oldorg; vec3_t dir; int clip; trace_t steptrace; VectorCopy (ent->v.origin, oldorg); VectorClear (dir); for (i = 0; i < 8; i++) { // try pushing a little in an axial direction switch (i) { case 0: dir[0] = 2; dir[1] = 0; break; case 1: dir[0] = 0; dir[1] = 2; break; case 2: dir[0] = -2; dir[1] = 0; break; case 3: dir[0] = 0; dir[1] = -2; break; case 4: dir[0] = 2; dir[1] = 2; break; case 5: dir[0] = -2; dir[1] = 2; break; case 6: dir[0] = 2; dir[1] = -2; break; case 7: dir[0] = -2; dir[1] = -2; break; } SV_PushEntity (ent, dir); // retry the original move ent->v.velocity[0] = oldvel[0]; ent->v. velocity[1] = oldvel[1]; ent->v. velocity[2] = 0; clip = SV_FlyMove (ent, 0.1, &steptrace); if ( fabs(oldorg[1] - ent->v.origin[1]) > 4 || fabs(oldorg[0] - ent->v.origin[0]) > 4 ) { // Con_DPrintf ("unstuck!\n"); return clip; } // go back to the original pos and try again VectorCopy (oldorg, ent->v.origin); } VectorClear (ent->v.velocity); return 7; // still not moving } /* ===================== SV_WalkMove Only used by players ====================== */ #define STEPSIZE 18 static void SV_WalkMove (edict_t *ent) { vec3_t upmove, downmove; vec3_t oldorg, oldvel; vec3_t nosteporg, nostepvel; int clip; int oldonground; trace_t steptrace, downtrace; // // do a regular slide move unless it looks like you ran into a step // oldonground = (int)ent->v.flags & FL_ONGROUND; ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; VectorCopy (ent->v.origin, oldorg); VectorCopy (ent->v.velocity, oldvel); clip = SV_FlyMove (ent, host_frametime, &steptrace); if ( !(clip & 2) ) return; // move didn't block on a step if (!oldonground && ent->v.waterlevel == 0) return; // don't stair up while jumping if (ent->v.movetype != MOVETYPE_WALK) return; // gibbed by a trigger if (sv_nostep.integer) return; if ( (int)sv_player->v.flags & FL_WATERJUMP ) return; VectorCopy (ent->v.origin, nosteporg); VectorCopy (ent->v.velocity, nostepvel); // // try moving up and forward to go up a step // VectorCopy (oldorg, ent->v.origin); // back to start pos VectorClear (upmove); VectorClear (downmove); upmove[2] = STEPSIZE; downmove[2] = -STEPSIZE + oldvel[2]*host_frametime; // move up SV_PushEntity (ent, upmove); // FIXME: don't link? // move forward ent->v.velocity[0] = oldvel[0]; ent->v. velocity[1] = oldvel[1]; ent->v. velocity[2] = 0; clip = SV_FlyMove (ent, host_frametime, &steptrace); // check for stuckness, possibly due to the limited precision of floats // in the clipping hulls if (clip) { if ( fabs(oldorg[1] - ent->v.origin[1]) < 0.03125 && fabs(oldorg[0] - ent->v.origin[0]) < 0.03125 ) { // stepping up didn't make any progress clip = SV_TryUnstick (ent, oldvel); } } // extra friction based on view angle if ( clip & 2 ) SV_WallFriction (ent, &steptrace); // move down downtrace = SV_PushEntity (ent, downmove); // FIXME: don't link? if (downtrace.plane.normal[2] > 0.7) { if (ent->v.solid == SOLID_BSP) { ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = EDICT_TO_PROG(downtrace.ent); } } else { // if the push down didn't end up on good ground, use the move without // the step up. This happens near wall / slope combinations, and can // cause the player to hop up higher on a slope too steep to climb VectorCopy (nosteporg, ent->v.origin); VectorCopy (nostepvel, ent->v.velocity); } } /* ================ SV_Physics_Client Player character actions ================ */ static void SV_Physics_Client (edict_t *ent, int num) { if ( ! svs.clients[num-1].active ) return; // unconnected slot // // call standard client pre-think // *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(ent); PR_ExecuteProgram (*sv_globals.PlayerPreThink); // // do a move // SV_CheckVelocity (ent); // // decide which move function to call // switch ((int)ent->v.movetype) { case MOVETYPE_NONE: if (!SV_RunThink (ent)) return; break; case MOVETYPE_WALK: if (!SV_RunThink (ent)) return; if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) ) SV_AddGravity (ent); SV_CheckStuck (ent); #ifdef QUAKE2 VectorAdd (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); #endif SV_WalkMove (ent); #ifdef QUAKE2 VectorSubtract (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); #endif break; case MOVETYPE_TOSS: case MOVETYPE_BOUNCE: SV_Physics_Toss (ent); break; case MOVETYPE_FLY: case MOVETYPE_SWIM: if (!SV_RunThink (ent)) return; SV_CheckWater (ent); SV_FlyMove (ent, host_frametime, NULL); SV_FlyExtras (ent, host_frametime, NULL); // Hover & friction break; case MOVETYPE_NOCLIP: if (!SV_RunThink (ent)) return; VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin); break; default: Sys_Error ("%s: bad movetype %i", __thisfunc__, (int)ent->v.movetype); } // // call standard player post-think // SV_LinkEdict (ent, true); *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(ent); PR_ExecuteProgram (*sv_globals.PlayerPostThink); } //============================================================================ /* ============= SV_Physics_None Non moving objects can only think ============= */ static void SV_Physics_None (edict_t *ent) { // regular thinking SV_RunThink (ent); } #ifdef QUAKE2 /* ============= SV_Physics_Follow Entities that are "stuck" to another entity ============= */ static void SV_Physics_Follow (edict_t *ent) { // regular thinking SV_RunThink (ent); VectorAdd (PROG_TO_EDICT(ent->v.aiment)->v.origin, ent->v.v_angle, ent->v.origin); SV_LinkEdict (ent, true); } #endif /* ============= SV_Physics_Noclip A moving object that doesn't obey physics ============= */ static void SV_Physics_Noclip (edict_t *ent) { // regular thinking if (!SV_RunThink (ent)) return; VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles); VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin); SV_LinkEdict (ent, false); } /* ============================================================================== TOSS / BOUNCE ============================================================================== */ /* ============= SV_CheckWaterTransition ============= */ static void SV_CheckWaterTransition (edict_t *ent) { int cont; #ifdef QUAKE2 vec3_t point; point[0] = ent->v.origin[0]; point[1] = ent->v.origin[1]; point[2] = ent->v.origin[2] + ent->v.mins[2] + 1; cont = SV_PointContents (point); #else cont = SV_PointContents (ent->v.origin); #endif if (!ent->v.watertype) { // just spawned here ent->v.watertype = cont; ent->v.waterlevel = 1; return; } if (cont <= CONTENTS_WATER) { if (ent->v.watertype == CONTENTS_EMPTY) { // just crossed into water SV_StartSound (ent, 0, "misc/hith2o.wav", 255, 1); } ent->v.watertype = cont; ent->v.waterlevel = 1; } else { if (ent->v.watertype != CONTENTS_EMPTY) { // just crossed into water SV_StartSound (ent, 0, "misc/hith2o.wav", 255, 1); } ent->v.watertype = CONTENTS_EMPTY; ent->v.waterlevel = cont; } } /* ============= SV_Physics_Toss Toss, bounce, and fly movement. When onground, do nothing. ============= */ static void SV_Physics_Toss (edict_t *ent) { trace_t trace; vec3_t move; float backoff; #ifdef QUAKE2 edict_t *groundentity; groundentity = PROG_TO_EDICT(ent->v.groundentity); if ((int)groundentity->v.flags & FL_CONVEYOR) VectorScale(groundentity->v.movedir, groundentity->v.speed, ent->v.basevelocity); else VectorCopy(vec_origin, ent->v.basevelocity); SV_CheckWater (ent); #endif // regular thinking if (!SV_RunThink (ent)) return; #ifdef QUAKE2 if (ent->v.velocity[2] > 0) ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; if ( ((int)ent->v.flags & FL_ONGROUND) ) //@@ if (VectorCompare(ent->v.basevelocity, vec_origin)) return; SV_CheckVelocity (ent); // add gravity if (! ((int)ent->v.flags & FL_ONGROUND) && ent->v.movetype != MOVETYPE_FLY && ent->v.movetype != MOVETYPE_BOUNCEMISSILE && ent->v.movetype != MOVETYPE_FLYMISSILE && ent->v.movetype != MOVETYPE_SWIM) SV_AddGravity (ent); #else // if onground, return without moving if ( ((int)ent->v.flags & FL_ONGROUND) ) return; SV_CheckVelocity (ent); // add gravity if (ent->v.movetype != MOVETYPE_FLY && ent->v.movetype != MOVETYPE_BOUNCEMISSILE && ent->v.movetype != MOVETYPE_FLYMISSILE && ent->v.movetype != MOVETYPE_SWIM) SV_AddGravity (ent); #endif // move angles VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles); // move origin #ifdef QUAKE2 VectorAdd (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); #endif VectorScale (ent->v.velocity, host_frametime, move); // VectorCopy(vec_origin,move); trace = SV_PushEntity (ent, move); #ifdef QUAKE2 VectorSubtract (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); #endif if (trace.fraction == 1) return; if (ent->free) return; if (ent->v.movetype == MOVETYPE_BOUNCE) backoff = 1.5; //#ifdef QUAKE2 else if (ent->v.movetype == MOVETYPE_BOUNCEMISSILE) { // Solid phased missiles don't bounce on monsters or players if ( (ent->v.solid == SOLID_PHASE) && (((int)trace.ent->v.flags & FL_MONSTER) || ((int)trace.ent->v.movetype == MOVETYPE_WALK)) ) { return; } backoff = 2.0; } //#endif else backoff = 1; ClipVelocity (ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff); // stop if on ground if ((trace.plane.normal[2] > 0.7) && (ent->v.movetype != MOVETYPE_BOUNCEMISSILE)) { //#ifdef QUAKE2 if (ent->v.velocity[2] < 60 || ent->v.movetype != MOVETYPE_BOUNCE) //#else // if (ent->v.velocity[2] < 60 || ent->v.movetype != MOVETYPE_BOUNCE) //#endif { ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = EDICT_TO_PROG(trace.ent); VectorClear (ent->v.velocity); VectorClear (ent->v.avelocity); } } // check for in water SV_CheckWaterTransition (ent); } /* =============================================================================== STEPPING MOVEMENT =============================================================================== */ /* ============= SV_Physics_Step Monsters freefall when they don't have a ground entity, otherwise all movement is done with discrete steps. This is also used for objects that have become still on the ground, but will fall if the floor is pulled out from under them. ============= */ #ifdef QUAKE2 static void SV_Physics_Step (edict_t *ent) { qboolean wasonground; qboolean inwater; qboolean hitsound = false; float *vel; float speed, newspeed, control; float friction; edict_t *groundentity; groundentity = PROG_TO_EDICT(ent->v.groundentity); if ((int)groundentity->v.flags & FL_CONVEYOR) VectorScale(groundentity->v.movedir, groundentity->v.speed, ent->v.basevelocity); else VectorCopy(vec_origin, ent->v.basevelocity); //@@ *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(ent); PF_WaterMove(); SV_CheckVelocity (ent); wasonground = (int)ent->v.flags & FL_ONGROUND; // ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; // add gravity except: // flying monsters // swimming monsters who are in the water inwater = SV_CheckWater(ent); if (! wasonground) { if ( !((int)ent->v.flags & FL_FLY) ) { if ( !( ((int)ent->v.flags & FL_SWIM) && (ent->v.waterlevel > 0) ) ) { if (ent->v.velocity[2] < sv_gravity.value*-0.1) hitsound = true; if (!inwater) SV_AddGravity (ent); } } } if (!VectorCompare(ent->v.velocity, vec_origin) || !VectorCompare(ent->v.basevelocity, vec_origin)) { ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; // apply friction // let dead monsters who aren't completely onground slide if (wasonground) { if (!(ent->v.health <= 0.0 && !SV_CheckBottom(ent))) { vel = ent->v.velocity; speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]); if (speed) { friction = sv_friction.value; control = speed < sv_stopspeed.value ? sv_stopspeed.value : speed; newspeed = speed - host_frametime*control*friction; if (newspeed < 0) newspeed = 0; newspeed /= speed; vel[0] = vel[0] * newspeed; vel[1] = vel[1] * newspeed; } } } VectorAdd (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); SV_FlyMove (ent, host_frametime, NULL); VectorSubtract (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); // determine if it's on solid ground at all { vec3_t mins, maxs, point; int x, y; VectorAdd (ent->v.origin, ent->v.mins, mins); VectorAdd (ent->v.origin, ent->v.maxs, maxs); point[2] = mins[2] - 1; for (x = 0; x <= 1; x++) { for (y = 0; y <= 1; y++) { point[0] = x ? maxs[0] : mins[0]; point[1] = y ? maxs[1] : mins[1]; if (SV_PointContents (point) == CONTENTS_SOLID) { ent->v.flags = (int)ent->v.flags | FL_ONGROUND; break; } } } } SV_LinkEdict (ent, true); if (((int)ent->v.flags & FL_ONGROUND) && !((int)ent->v.flags & FL_MONSTER)) { if (!wasonground) if (hitsound) SV_StartSound (ent, 0, "fx/thngland.wav", 255, 1); } } // regular thinking SV_RunThink (ent); SV_CheckWaterTransition (ent); } #else static void SV_Physics_Step (edict_t *ent) { qboolean hitsound; // freefall if not onground if ( ! ((int)ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM) ) ) { if (ent->v.velocity[2] < sv_gravity.value*-0.1) hitsound = true; else hitsound = false; SV_AddGravity (ent); SV_CheckVelocity (ent); SV_FlyMove (ent, host_frametime, NULL); SV_LinkEdict (ent, true); // if ( (int)ent->v.flags & FL_ONGROUND ) // just hit ground if (((int)ent->v.flags & FL_ONGROUND) && !((int)ent->v.flags & FL_MONSTER)) { if (hitsound) SV_StartSound (ent, 0, "fx/thngland.wav", 255, 1); } } // regular thinking SV_RunThink (ent); SV_CheckWaterTransition (ent); } #endif //============================================================================ /* ================ SV_Physics ================ */ void SV_Physics (void) { int i, c, originMoved; edict_t *ent, *ent2; vec3_t oldOrigin, oldAngle; // let the progs know that a new frame has started *sv_globals.self = EDICT_TO_PROG(sv.edicts); *sv_globals.other = EDICT_TO_PROG(sv.edicts); *sv_globals.time = sv.time; PR_ExecuteProgram (*sv_globals.StartFrame); //SV_CheckAllEnts (); // // treat each object in turn // ent = sv.edicts; VectorClear(oldOrigin); // avoid compiler warning VectorClear(oldAngle); // avoid compiler warning for (i = 0; i < sv.num_edicts; i++, ent = NEXT_EDICT(ent)) { if (ent->free) continue; ent2 = PROG_TO_EDICT(ent->v.movechain); if (ent2 != sv.edicts) { VectorCopy(ent->v.origin,oldOrigin); VectorCopy(ent->v.angles,oldAngle); } if (*sv_globals.force_retouch) { SV_LinkEdict (ent, true); // force retouch even for stationary } if (i > 0 && i <= svs.maxclients) SV_Physics_Client (ent, i); else if (ent->v.movetype == MOVETYPE_PUSH) SV_Physics_Pusher (ent); else if (ent->v.movetype == MOVETYPE_NONE) SV_Physics_None (ent); #ifdef QUAKE2 else if (ent->v.movetype == MOVETYPE_FOLLOW) SV_Physics_Follow (ent); #endif else if (ent->v.movetype == MOVETYPE_NOCLIP) SV_Physics_Noclip (ent); else if ((ent->v.movetype == MOVETYPE_STEP) || (ent->v.movetype == MOVETYPE_PUSHPULL)) SV_Physics_Step (ent); else if (ent->v.movetype == MOVETYPE_TOSS || ent->v.movetype == MOVETYPE_BOUNCE //#ifdef QUAKE2 || ent->v.movetype == MOVETYPE_BOUNCEMISSILE //#endif || ent->v.movetype == MOVETYPE_FLY || ent->v.movetype == MOVETYPE_FLYMISSILE || ent->v.movetype == MOVETYPE_SWIM) SV_Physics_Toss (ent); else Sys_Error ("%s: bad movetype %i", __thisfunc__, (int)ent->v.movetype); if (ent2 != sv.edicts) { originMoved = !VectorCompare(ent->v.origin,oldOrigin); if (originMoved || !VectorCompare(ent->v.angles,oldAngle)) { VectorSubtract(ent->v.origin,oldOrigin,oldOrigin); VectorSubtract(ent->v.angles,oldAngle,oldAngle); for (c = 0; c < 10; c++) { // chain a max of 10 objects if (ent2->free) break; VectorAdd(oldOrigin,ent2->v.origin,ent2->v.origin); if ((int)ent2->v.flags & FL_MOVECHAIN_ANGLE) { VectorAdd(oldAngle,ent2->v.angles,ent2->v.angles); } if (originMoved && ent2->v.chainmoved) { // callback function *sv_globals.self = EDICT_TO_PROG(ent2); *sv_globals.other = EDICT_TO_PROG(ent); PR_ExecuteProgram(ent2->v.chainmoved); } ent2 = PROG_TO_EDICT(ent2->v.movechain); if (ent2 == sv.edicts) break; } } } } if (*sv_globals.force_retouch) (*sv_globals.force_retouch)--; sv.time += host_frametime; } #ifdef QUAKE2 trace_t SV_Trace_Toss (edict_t *ent, edict_t *ignore) { edict_t tempent, *tent; trace_t trace; vec3_t move; vec3_t end; double save_frametime; // extern particle_t *active_particles, *free_particles; // particle_t *p; save_frametime = host_frametime; host_frametime = 0.05; memcpy(&tempent, ent, sizeof(edict_t)); tent = &tempent; while (1) { SV_CheckVelocity (tent); SV_AddGravity (tent); VectorMA (tent->v.angles, host_frametime, tent->v.avelocity, tent->v.angles); VectorScale (tent->v.velocity, host_frametime, move); VectorAdd (tent->v.origin, move, end); trace = SV_Move (tent->v.origin, tent->v.mins, tent->v.maxs, end, MOVE_NORMAL, tent); VectorCopy (trace.endpos, tent->v.origin); // p = free_particles; // if (p) // { // free_particles = p->next; // p->next = active_particles; // active_particles = p; // // p->die = 256; // p->color = 15; // p->type = pt_static; // VectorClear (p->vel); // VectorCopy (tent->v.origin, p->org); // } if (trace.ent) if (trace.ent != ignore) break; } // p->color = 224; host_frametime = save_frametime; return trace; } #endif engine/hexen2/sv_user.c000066400000000000000000000454271444734033100153650ustar00rootroot00000000000000/* * sv_user.c -- server code for moving users * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" edict_t *sv_player = NULL; extern cvar_t sv_friction; cvar_t sv_edgefriction = {"edgefriction", "2", CVAR_NONE}; cvar_t sv_altnoclip = {"sv_altnoclip", "1", CVAR_ARCHIVE}; extern cvar_t sv_stopspeed; static vec3_t forward, right, up; // world static float *angles; static float *origin; static float *velocity; static qboolean onground; static usercmd_t cmd; cvar_t sv_idealpitchscale = {"sv_idealpitchscale", "0.8", CVAR_NONE}; cvar_t sv_idealrollscale = {"sv_idealrollscale", "0.8", CVAR_NONE}; #if defined(SERVERONLY) static cvar_t cl_rollspeed = {"cl_rollspeed", "200", CVAR_NONE}; static cvar_t cl_rollangle = {"cl_rollangle", "2.0", CVAR_NONE}; #else extern cvar_t cl_rollspeed; extern cvar_t cl_rollangle; #endif /* =============== SV_SetIdealPitch =============== */ #define MAX_FORWARD 6 void SV_SetIdealPitch (void) { float angleval, sinval, cosval; trace_t tr; vec3_t top, bottom; float z[MAX_FORWARD]; int i, j; int step, dir, steps; float save_hull; if (!((int)sv_player->v.flags & FL_ONGROUND)) return; if (sv_player->v.movetype == MOVETYPE_FLY) return; angleval = sv_player->v.angles[YAW] * M_PI*2 / 360; sinval = sin(angleval); cosval = cos(angleval); save_hull = sv_player->v.hull; sv_player->v.hull = 0; for (i = 0; i < MAX_FORWARD; i++) { top[0] = sv_player->v.origin[0] + cosval*(i+3)*12; top[1] = sv_player->v.origin[1] + sinval*(i+3)*12; top[2] = sv_player->v.origin[2] + sv_player->v.view_ofs[2]; bottom[0] = top[0]; bottom[1] = top[1]; bottom[2] = top[2] - 160; tr = SV_Move (top, vec3_origin, vec3_origin, bottom, 1, sv_player); if ((tr.allsolid) || // looking at a wall, leave ideal the way is was (tr.fraction == 1))// near a dropoff { sv_player->v.hull = save_hull; return; } z[i] = top[2] + tr.fraction*(bottom[2]-top[2]); } sv_player->v.hull = save_hull; //restore dir = 0; steps = 0; for (j = 1; j < i; j++) { step = z[j] - z[j-1]; if (step > -ON_EPSILON && step < ON_EPSILON) continue; if (dir && ( step-dir > ON_EPSILON || step-dir < -ON_EPSILON ) ) return; // mixed changes steps++; dir = step; } if (!dir) { sv_player->v.idealpitch = 0; return; } if (steps < 2) return; sv_player->v.idealpitch = -dir * sv_idealpitchscale.value; } /* ================== SV_UserFriction ================== */ static void SV_UserFriction (void) { float *vel; float speed, newspeed, control; vec3_t start, stop; float friction; trace_t trace; float save_hull; vel = velocity; speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]); if (!speed) return; // if the leading edge is over a dropoff, increase friction start[0] = stop[0] = origin[0] + vel[0]/speed*16; start[1] = stop[1] = origin[1] + vel[1]/speed*16; start[2] = origin[2] + sv_player->v.mins[2]; stop[2] = start[2] - 34; save_hull = sv_player->v.hull; sv_player->v.hull = 0; trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, sv_player); sv_player->v.hull = save_hull; // Hexen II v1.11 progs doesn't initialize the friction properly (always 0). // In fact, applying a patch to the progs can easily solve this issue, but // in that case several unpatched mods would be left broken. Note: Compared // to the AoT solution, the USE_AOT_FRICTION 0 option makes pure hexen2 to // feel slightly more slippery. #if USE_AOT_FRICTION if (progs->crc != PROGS_V112_CRC) friction = 6; else { if (trace.fraction == 1.0) friction = sv_friction.value*sv_edgefriction.value*sv_player->v.friction; else friction = sv_friction.value*sv_player->v.friction; } #else /* not using AoT friction */ if (progs->crc != PROGS_V112_CRC) sv_player->v.friction = 1.0f; if (trace.fraction == 1.0) friction = sv_friction.value*sv_edgefriction.value*sv_player->v.friction; else friction = sv_friction.value*sv_player->v.friction; #endif // These lines were found as commented out in Raven's H2MP source. Probably // it was their pre-mission pack solution. See above for the current stuff // and for the explanation. // if (sv_player->v.friction != 1) //reset their friction to 1, only a trigger touching can change it again // sv_player->v.friction = 1; // apply friction control = speed < sv_stopspeed.value ? sv_stopspeed.value : speed; newspeed = speed - host_frametime*control*friction; if (newspeed < 0) newspeed = 0; newspeed /= speed; vel[0] = vel[0] * newspeed; vel[1] = vel[1] * newspeed; vel[2] = vel[2] * newspeed; } /* ============== SV_Accelerate ============== */ //cvar_t sv_maxspeed = {"sv_maxspeed", "320", CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t sv_maxspeed = {"sv_maxspeed", "640", CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t sv_accelerate = {"sv_accelerate", "10", CVAR_NONE}; /* Old values before the id 1.07 update cvar_t sv_maxspeed = {"sv_maxspeed", "640", CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t sv_accelerate = {"sv_accelerate", "100", CVAR_NONE}; */ #if 0 static void SV_Accelerate (vec3_t wishvel) { int i; float addspeed, accelspeed; vec3_t pushvec; if (wishspeed == 0) return; VectorSubtract (wishvel, velocity, pushvec); addspeed = VectorNormalize (pushvec); accelspeed = sv_accelerate.value*host_frametime*addspeed; if (accelspeed > addspeed) accelspeed = addspeed; for (i = 0; i < 3; i++) velocity[i] += accelspeed*pushvec[i]; } #endif static void SV_Accelerate (float wishspeed, const vec3_t wishdir) { int i; float addspeed, accelspeed, currentspeed; currentspeed = DotProduct (velocity, wishdir); addspeed = wishspeed - currentspeed; if (addspeed <= 0) return; accelspeed = sv_accelerate.value*host_frametime*wishspeed; if (accelspeed > addspeed) accelspeed = addspeed; for (i = 0; i < 3; i++) velocity[i] += accelspeed*wishdir[i]; } static void SV_AirAccelerate (float wishspeed, vec3_t wishveloc) { int i; float addspeed, wishspd, accelspeed, currentspeed; wishspd = VectorNormalize (wishveloc); if (wishspd > 30) wishspd = 30; currentspeed = DotProduct (velocity, wishveloc); addspeed = wishspd - currentspeed; if (addspeed <= 0) return; // accelspeed = sv_accelerate.value * host_frametime; accelspeed = sv_accelerate.value*wishspeed * host_frametime; if (accelspeed > addspeed) accelspeed = addspeed; for (i = 0; i < 3; i++) velocity[i] += accelspeed*wishveloc[i]; } static void DropPunchAngle (void) { float len; len = VectorNormalize (sv_player->v.punchangle); len -= 10*host_frametime; if (len < 0) len = 0; VectorScale (sv_player->v.punchangle, len, sv_player->v.punchangle); } /* =================== SV_FlightMove this is just the same as SV_WaterMove but with a few changes to make it flight =================== */ static void SV_FlightMove (void) { int i; vec3_t wishvel; float speed, newspeed, wishspeed, addspeed, accelspeed; #if !defined(SERVERONLY) cl.nodrift = false; cl.driftmove = 0; #endif // // user intentions // AngleVectors (sv_player->v.v_angle, forward, right, up); for (i = 0; i < 3; i++) wishvel[i] = forward[i]*cmd.forwardmove + right[i]*cmd.sidemove + up[i]* cmd.upmove; wishspeed = VectorLength(wishvel); if (wishspeed > sv_maxspeed.value) { VectorScale (wishvel, sv_maxspeed.value/wishspeed, wishvel); wishspeed = sv_maxspeed.value; } // // water friction // speed = VectorLength (velocity); if (speed) { newspeed = speed - host_frametime * speed * sv_friction.value; if (newspeed < 0) newspeed = 0; VectorScale (velocity, newspeed/speed, velocity); } else newspeed = 0; // // water acceleration // if (!wishspeed) return; addspeed = wishspeed - newspeed; if (addspeed <= 0) return; VectorNormalize (wishvel); accelspeed = sv_accelerate.value * wishspeed * host_frametime; if (accelspeed > addspeed) accelspeed = addspeed; for (i = 0; i < 3; i++) velocity[i] += accelspeed * wishvel[i]; } /* =================== SV_WaterMove =================== */ static void SV_WaterMove (void) { int i; vec3_t wishvel; float speed, newspeed, wishspeed, addspeed, accelspeed; // // user intentions // AngleVectors (sv_player->v.v_angle, forward, right, up); for (i = 0; i < 3; i++) wishvel[i] = forward[i]*cmd.forwardmove + right[i]*cmd.sidemove; if (!cmd.forwardmove && !cmd.sidemove && !cmd.upmove) wishvel[2] -= 60; // drift towards bottom else wishvel[2] += cmd.upmove; wishspeed = VectorLength(wishvel); if (wishspeed > sv_maxspeed.value) { VectorScale (wishvel, sv_maxspeed.value/wishspeed, wishvel); wishspeed = sv_maxspeed.value; } if (sv_player->v.playerclass == CLASS_DEMON) // Paladin Special Ability #1 - unrestricted movement in water wishspeed *= 0.5; else if (sv_player->v.playerclass != CLASS_PALADIN) // Paladin Special Ability #1 - unrestricted movement in water wishspeed *= 0.7; else if (sv_player->v.level == 1) wishspeed *= 0.75; else if (sv_player->v.level == 2) wishspeed *= 0.80; else if ((sv_player->v.level == 3) || (sv_player->v.level == 4)) wishspeed *= 0.85; else if ((sv_player->v.level == 5) || (sv_player->v.level == 6)) wishspeed *= 0.90; else if ((sv_player->v.level == 7) || (sv_player->v.level == 8)) wishspeed *= 0.95; // // water friction // speed = VectorLength (velocity); if (speed) { newspeed = speed - host_frametime * speed * sv_friction.value; if (newspeed < 0) newspeed = 0; VectorScale (velocity, newspeed/speed, velocity); } else newspeed = 0; // // water acceleration // if (!wishspeed) return; addspeed = wishspeed - newspeed; if (addspeed <= 0) return; VectorNormalize (wishvel); accelspeed = sv_accelerate.value * wishspeed * host_frametime; if (accelspeed > addspeed) accelspeed = addspeed; for (i = 0; i < 3; i++) velocity[i] += accelspeed * wishvel[i]; } static void SV_WaterJump (void) { if (sv.time > sv_player->v.teleport_time || !sv_player->v.waterlevel) { sv_player->v.flags = (int)sv_player->v.flags & ~FL_WATERJUMP; sv_player->v.teleport_time = 0; } sv_player->v.velocity[0] = sv_player->v.movedir[0]; sv_player->v.velocity[1] = sv_player->v.movedir[1]; } /* =================== SV_NoclipMove alternate q2-style noclip from Fitzquake old noclip still handled in SV_AirMove() =================== */ static void SV_NoclipMove (void) { AngleVectors (sv_player->v.v_angle, forward, right, up); velocity[0] = forward[0]*cmd.forwardmove + right[0]*cmd.sidemove; velocity[1] = forward[1]*cmd.forwardmove + right[1]*cmd.sidemove; velocity[2] = forward[2]*cmd.forwardmove + right[2]*cmd.sidemove; velocity[2] += cmd.upmove*2; // doubled to match running speed if (VectorLength (velocity) > sv_maxspeed.value) { VectorNormalize (velocity); VectorScale (velocity, sv_maxspeed.value, velocity); } } /* =================== SV_AirMove =================== */ static void SV_AirMove (void) { int i; vec3_t wishvel, wishdir; float wishspeed; float fmove, smove; AngleVectors (sv_player->v.angles, forward, right, up); fmove = cmd.forwardmove; smove = cmd.sidemove; // hack to not let you back into teleporter if (sv.time < sv_player->v.teleport_time && fmove < 0) fmove = 0; for (i = 0; i < 3; i++) wishvel[i] = forward[i]*fmove + right[i]*smove; if ( (int)sv_player->v.movetype != MOVETYPE_WALK) wishvel[2] = cmd.upmove; else wishvel[2] = 0; VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); if (wishspeed > sv_maxspeed.value) { VectorScale (wishvel, sv_maxspeed.value/wishspeed, wishvel); wishspeed = sv_maxspeed.value; } if ( sv_player->v.movetype == MOVETYPE_NOCLIP) { // noclip VectorCopy (wishvel, velocity); } else if (onground) { SV_UserFriction (); SV_Accelerate (wishspeed, wishdir); } else { // not on ground, so little effect on velocity SV_AirAccelerate (wishspeed, wishvel); } } /* =============== V_CalcRoll =============== */ #if defined(SERVERONLY) static float V_CalcRoll (vec3_t Angles, vec3_t Velocity) { vec3_t Fwd, Right, Up; float sign, side, value; AngleVectors (Angles, Fwd, Right, Up); side = DotProduct (Velocity, Right); sign = side < 0 ? -1 : 1; side = fabs(side); value = cl_rollangle.value; if (side < cl_rollspeed.value) side = side * value / cl_rollspeed.value; else side = value; return side*sign; } #endif /* =================== SV_ClientThink the move fields specify an intended velocity in pix/sec the angle fields specify an exact angular motion in degrees =================== */ void SV_ClientThink (void) { vec3_t v_angle; if (sv_player->v.movetype == MOVETYPE_NONE) return; onground = (int)sv_player->v.flags & FL_ONGROUND; origin = sv_player->v.origin; velocity = sv_player->v.velocity; DropPunchAngle (); // // if dead, behave differently // if (sv_player->v.health <= 0) return; // // angles // show 1/3 the pitch angle and all the roll angle cmd = host_client->cmd; angles = sv_player->v.angles; VectorAdd (sv_player->v.v_angle, sv_player->v.punchangle, v_angle); angles[ROLL] = V_CalcRoll(sv_player->v.angles, sv_player->v.velocity) * 4; if (!sv_player->v.fixangle) { angles[PITCH] = -v_angle[PITCH]/3; angles[YAW] = v_angle[YAW]; } if ( (int)sv_player->v.flags & FL_WATERJUMP ) { SV_WaterJump (); return; } // // walk // if (sv_player->v.movetype == MOVETYPE_NOCLIP && sv_altnoclip.integer) { SV_NoclipMove (); // alternate quake2-style noclip return; } if ( (sv_player->v.waterlevel >= 2) && (sv_player->v.movetype != MOVETYPE_NOCLIP) ) { SV_WaterMove (); return; } else if (sv_player->v.movetype == MOVETYPE_FLY) { SV_FlightMove (); return; } SV_AirMove (); } /* =================== SV_ReadClientMove =================== */ static void SV_ReadClientMove (usercmd_t *move) { int i; vec3_t angle; int bits; // read ping time host_client->ping_times[host_client->num_pings%NUM_PING_TIMES] = sv.time - MSG_ReadFloat (); host_client->num_pings++; // read current angles for (i = 0; i < 3; i++) angle[i] = MSG_ReadAngle (); VectorCopy (angle, host_client->edict->v.v_angle); // read movement move->forwardmove = MSG_ReadShort (); move->sidemove = MSG_ReadShort (); move->upmove = MSG_ReadShort (); // read buttons bits = MSG_ReadByte (); host_client->edict->v.button0 = bits & 1; host_client->edict->v.button2 = (bits & 2)>>1; if (bits & 4) // crouched? host_client->edict->v.flags2 = ((int)host_client->edict->v.flags2) | FL2_CROUCHED; else host_client->edict->v.flags2 = ((int)host_client->edict->v.flags2) & (~FL2_CROUCHED); i = MSG_ReadByte (); if (i) host_client->edict->v.impulse = i; // read light level host_client->edict->v.light_level = MSG_ReadByte (); } /* =================== SV_ReadClientMessage Returns false if the client should be killed =================== */ static qboolean SV_ReadClientMessage (void) { int ret; int ccmd; const char *s; do { nextmsg: ret = NET_GetMessage (host_client->netconnection); if (ret == -1) { Sys_Printf ("%s: NET_GetMessage failed\n", __thisfunc__); return false; } if (!ret) return true; MSG_BeginReading (); while (1) { if (!host_client->active) return false; // a command caused an error if (msg_badread) { Sys_Printf ("%s: badread\n", __thisfunc__); return false; } ccmd = MSG_ReadChar (); switch (ccmd) { case -1: goto nextmsg; // end of message default: Sys_Printf ("%s: unknown command char\n", __thisfunc__); return false; case clc_nop: // Sys_Printf ("clc_nop\n"); break; case clc_stringcmd: s = MSG_ReadString (); ret = 0; if (q_strncasecmp(s, "status", 6) == 0) ret = 1; else if (q_strncasecmp(s, "god", 3) == 0) ret = 1; else if (q_strncasecmp(s, "notarget", 8) == 0) ret = 1; else if (q_strncasecmp(s, "fly", 3) == 0) ret = 1; else if (q_strncasecmp(s, "name", 4) == 0) ret = 1; else if (q_strncasecmp(s, "playerclass", 11) == 0) ret = 1; else if (q_strncasecmp(s, "noclip", 6) == 0) ret = 1; else if (q_strncasecmp(s, "say", 3) == 0) ret = 1; else if (q_strncasecmp(s, "say_team", 8) == 0) ret = 1; else if (q_strncasecmp(s, "tell", 4) == 0) ret = 1; else if (q_strncasecmp(s, "color", 5) == 0) ret = 1; else if (q_strncasecmp(s, "kill", 4) == 0) ret = 1; else if (q_strncasecmp(s, "pause", 5) == 0) ret = 1; else if (q_strncasecmp(s, "spawn", 5) == 0) ret = 1; else if (q_strncasecmp(s, "begin", 5) == 0) ret = 1; else if (q_strncasecmp(s, "prespawn", 8) == 0) ret = 1; else if (q_strncasecmp(s, "kick", 4) == 0) ret = 1; else if (q_strncasecmp(s, "ping", 4) == 0) ret = 1; else if (q_strncasecmp(s, "give", 4) == 0) ret = 1; else if (q_strncasecmp(s, "ban", 3) == 0) ret = 1; if (ret == 1) Cmd_ExecuteString (s, src_client); else Con_DPrintf("%s tried to %s\n", host_client->name, s); break; case clc_disconnect: // Sys_Printf ("%s: client disconnected\n", __thisfunc__); return false; case clc_move: SV_ReadClientMove (&host_client->cmd); break; case clc_inv_select: host_client->edict->v.inventory = MSG_ReadByte(); break; case clc_frame: host_client->last_frame = MSG_ReadByte(); host_client->last_sequence = MSG_ReadByte(); break; } } } while (ret == 1); return true; } /* ================== SV_RunClients ================== */ void SV_RunClients (void) { int i; for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if (!host_client->active) continue; sv_player = host_client->edict; if (!SV_ReadClientMessage ()) { SV_DropClient (false); // client misbehaved... continue; } if (!host_client->spawned) { // clear client movement until a new packet is received memset (&host_client->cmd, 0, sizeof(host_client->cmd)); continue; } // always pause in single player if in console or menus #if defined(SERVERONLY) if (!sv.paused) #else if (!sv.paused && (svs.maxclients > 1 || Key_GetDest() == key_game)) #endif SV_ClientThink (); } } /* ============== SV_UserInit ============== */ void SV_UserInit (void) { #if !defined(SERVERONLY) if (!isDedicated) return; #endif /* SERVERONLY */ Cvar_RegisterVariable (&cl_rollspeed); Cvar_RegisterVariable (&cl_rollangle); } engine/hexen2/sys_amiga.c000066400000000000000000000501131444734033100156370ustar00rootroot00000000000000/* sys_amiga.c -- Amiga system interface code * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2012 Szilard Biro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "debuglog.h" #include #include #include #include #include #include #include #if defined(SDLQUAKE) #include "sdl_inc.h" #endif #if defined(__LP64__) #define MIN_STACK_SIZE 0x200000 /* 2 MB stack */ #elif defined(PLATFORM_AMIGAOS3) #define MIN_STACK_SIZE 0x40000 /* 256 KB stack */ #else #define MIN_STACK_SIZE 0x100000 /* 1 MB stack */ #endif #ifdef __CLIB2__ int __stack_size = MIN_STACK_SIZE; #else int __stack = MIN_STACK_SIZE; #if defined(PLATFORM_AMIGAOS3) && defined(__libnix__) /* this pulls in swapstack.o */ /* NOTE: swapstack.o was a stray object in old versions * of libnix, need manually adding to libnix20.a */ extern void __stkinit(void); void * __x = __stkinit; #endif #endif #ifdef __AROS__ #include "incstack.h" /* The problem here is that our real main never returns: the exit * point of program is either Sys_Quit() or Sys_Error(). One way * of making the real main return to the incstack.h main wrapper * is setjmp()'ing in real main and longjmp()'ing from the actual * exit points, avoiding exit(). */ #include static jmp_buf exit_buf; static int my_rc = 0; #define HAVE_AROS_MAIN_WRAPPER #endif #include /* IPTR */ #define MIN_MEM_ALLOC 0x1000000 #define STD_MEM_ALLOC 0x2000000 cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; cvar_t sys_throttle = {"sys_throttle", "0.02", CVAR_ARCHIVE}; qboolean isDedicated; static double starttime; static qboolean first = true; static BPTR amiga_stdin, amiga_stdout; #define MODE_RAW 1 #define MODE_NORMAL 0 struct timerequest *timerio; struct MsgPort *timerport; #if defined(__MORPHOS__) || defined(__VBCC__) struct Library *TimerBase; #else struct Device *TimerBase; #endif #ifdef __CLIB2__ struct IntuitionBase *IntuitionBase; struct Library *IFFParseBase; #endif /* =============================================================================== FILE IO =============================================================================== */ int Sys_mkdir(const char *path, qboolean crash) { BPTR lock = CreateDir((const STRPTR) path); if (lock) { UnLock(lock); return 0; } if (IoErr() == ERROR_OBJECT_EXISTS) return 0; if (crash) Sys_Error("Unable to create directory %s", path); return -1; } int Sys_rmdir (const char *path) { if (DeleteFile((const STRPTR) path) != 0) return 0; return -1; } int Sys_unlink (const char *path) { if (DeleteFile((const STRPTR) path) != 0) return 0; return -1; } int Sys_rename (const char *oldp, const char *newp) { if (Rename((const STRPTR) oldp, (const STRPTR) newp) != 0) return 0; return -1; } long Sys_filesize (const char *path) { long size = -1; BPTR lock = Lock((const STRPTR) path, ACCESS_READ); if (lock) { struct FileInfoBlock *fib = (struct FileInfoBlock*) AllocDosObject(DOS_FIB, NULL); if (fib != NULL) { if (Examine(lock, fib)) { size = fib->fib_Size; } FreeDosObject(DOS_FIB, fib); } UnLock(lock); } return size; } int Sys_FileType (const char *path) { int type = FS_ENT_NONE; BPTR lock = Lock((const STRPTR) path, ACCESS_READ); if (lock) { struct FileInfoBlock *fib = (struct FileInfoBlock*) AllocDosObject(DOS_FIB, NULL); if (fib != NULL) { if (Examine(lock, fib)) { if (fib->fib_DirEntryType >= 0) type = FS_ENT_DIRECTORY; else type = FS_ENT_FILE; } FreeDosObject(DOS_FIB, fib); } UnLock(lock); } return type; } #define COPY_READ_BUFSIZE 8192 /* BUFSIZ */ int Sys_CopyFile (const char *frompath, const char *topath) { char buf[COPY_READ_BUFSIZE]; BPTR in, out; struct FileInfoBlock *fib; struct DateStamp stamp; LONG remaining, count; in = Open((const STRPTR) frompath, MODE_OLDFILE); if (!in) { Con_Printf ("%s: unable to open %s\n", __thisfunc__, frompath); return 1; } fib = (struct FileInfoBlock*) AllocDosObject(DOS_FIB, NULL); if (fib != NULL) { if (ExamineFH(in, fib) == 0) remaining = -1; else { remaining = fib->fib_Size; stamp = fib->fib_Date; } FreeDosObject(DOS_FIB, fib); if (remaining < 0) { Con_Printf ("%s: can't determine size for %s\n", __thisfunc__, frompath); Close(in); return 1; } } else { Con_Printf ("%s: can't allocate FileInfoBlock for %s\n", __thisfunc__, frompath); Close(in); return 1; } out = Open((const STRPTR) topath, MODE_NEWFILE); if (!out) { Con_Printf ("%s: unable to open %s\n", __thisfunc__, topath); Close(in); return 1; } while (remaining) { if (remaining < sizeof(buf)) count = remaining; else count = sizeof(buf); if (Read(in, buf, count) == -1) break; if (Write(out, buf, count) == -1) break; remaining -= count; } Close(in); Close(out); if (remaining != 0) return 1; SetFileDate(topath, &stamp); return 0; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only, not a dirent struct. this is what we presently need in this engine. ================================================= */ static struct AnchorPath apath; static BPTR oldcurrentdir; static STRPTR pattern_str; static STRPTR pattern_helper (const char *pat) { char *pdup; const char *p; int n; for (n = 0, p = pat; *p != '\0'; ++p, ++n) { if ((p = strchr (p, '*')) == NULL) break; } if (n == 0) pdup = Z_Strdup(pat); else { /* replace each "*" by "#?" */ n += (int) strlen(pat) + 1; pdup = (char *) Z_Malloc(n, Z_MAINZONE); for (n = 0, p = pat; *p != '\0'; ++p, ++n) { if (*p != '*') pdup[n] = *p; else { pdup[n] = '#'; ++n; pdup[n] = '?'; } } pdup[n] = '\0'; } return (STRPTR) pdup; } const char *Sys_FindFirstFile (const char *path, const char *pattern) { BPTR newdir; if (pattern_str) Sys_Error ("Sys_FindFirst without FindClose"); memset(&apath, 0, sizeof(apath)); newdir = Lock((const STRPTR) path, SHARED_LOCK); if (newdir) oldcurrentdir = CurrentDir(newdir); else return NULL; pattern_str = pattern_helper (pattern); if (MatchFirst((const STRPTR) pattern_str, &apath) == 0) { if (apath.ap_Info.fib_DirEntryType < 0) return (const char *) (apath.ap_Info.fib_FileName); } return Sys_FindNextFile(); } const char *Sys_FindNextFile (void) { if (!pattern_str) return NULL; while (MatchNext(&apath) == 0) { if (apath.ap_Info.fib_DirEntryType < 0) return (const char *) (apath.ap_Info.fib_FileName); } return NULL; } void Sys_FindClose (void) { if (!pattern_str) return; MatchEnd(&apath); UnLock(CurrentDir(oldcurrentdir)); oldcurrentdir = 0; Z_Free(pattern_str); pattern_str = NULL; } /* File existence check with the "Please insert volume XXX" * system requester disabled. */ qboolean Sys_PathExistsQuiet(const char *name) { struct Process *self = (struct Process *) FindTask(NULL); APTR oldwinptr = self->pr_WindowPtr; BPTR lock; self->pr_WindowPtr = (APTR) -1; lock = Lock((const STRPTR) name, ACCESS_READ); self->pr_WindowPtr = oldwinptr; if (lock) { UnLock(lock); return true; } return false; } /* =============================================================================== SYSTEM IO =============================================================================== */ /* ================ Sys_MakeCodeWriteable ================ */ #if id386 && !defined(GLQUAKE) void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) { /* Not needed on Amiga. */ } #endif /* id386, !GLQUAKE */ /* ================ Sys_Init ================ */ static void Sys_Init (void) { /*MaskExceptions ();*/ Sys_SetFPCW (); #ifdef __CLIB2__ IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 0); if (!IntuitionBase) Sys_Error ("Cannot open intuition.library!"); IFFParseBase = OpenLibrary("iffparse.library", 0); #endif if ((timerport = CreateMsgPort())) { if ((timerio = (struct timerequest *)CreateIORequest(timerport, sizeof(struct timerequest)))) { if (OpenDevice((STRPTR) TIMERNAME, UNIT_MICROHZ, (struct IORequest *) timerio, 0) == 0) { #if defined(__MORPHOS__) || defined(__VBCC__) TimerBase = (struct Library *)timerio->tr_node.io_Device; #else TimerBase = timerio->tr_node.io_Device; #endif } else { DeleteIORequest((struct IORequest *)timerio); DeleteMsgPort(timerport); } } else { DeleteMsgPort(timerport); } } if (!TimerBase) Sys_Error("Can't open timer.device"); /* 1us wait, for timer cleanup success */ timerio->tr_node.io_Command = TR_ADDREQUEST; timerio->tr_time.tv_secs = 0; timerio->tr_time.tv_micro = 1; SendIO((struct IORequest *) timerio); WaitIO((struct IORequest *) timerio); amiga_stdout = Output(); if (isDedicated) { amiga_stdin = Input(); SetMode(amiga_stdin, MODE_RAW); } #if defined(SDLQUAKE) if (SDL_Init(0) < 0) Sys_Error("SDL failed to initialize."); #endif } static void Sys_AtExit (void) { if (amiga_stdin) SetMode(amiga_stdin, MODE_NORMAL); if (TimerBase) { /* if (!CheckIO((struct IORequest *) timerio) { AbortIO((struct IORequest *) timerio); WaitIO((struct IORequest *) timerio); } */ WaitIO((struct IORequest *) timerio); CloseDevice((struct IORequest *) timerio); DeleteIORequest((struct IORequest *) timerio); DeleteMsgPort(timerport); TimerBase = NULL; } #ifdef __CLIB2__ if (IntuitionBase) { CloseLibrary((struct Library *)IntuitionBase); IntuitionBase = NULL; } if (IFFParseBase) { CloseLibrary(IFFParseBase); IFFParseBase = NULL; } #endif #if defined(SDLQUAKE) SDL_Quit(); #endif } void Sys_ErrorMessage(const char *string) { struct EasyStruct es; if (!IntuitionBase) return; es.es_StructSize = sizeof(es); es.es_Flags = 0; es.es_Title = (STRPTR) ENGINE_NAME " error"; es.es_TextFormat = (STRPTR) string; es.es_GadgetFormat = (STRPTR) "Quit"; EasyRequest(0, &es, 0, 0); } #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const unsigned char *p; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (con_debuglog) { LOG_Print (ERROR_PREFIX); LOG_Print (text); LOG_Print ("\n\n"); } Host_Shutdown (); for (p = (const unsigned char *) text2; *p; p++) putc (*p, stderr); for (p = (const unsigned char *) text ; *p; p++) putc (*p, stderr); putc ('\n', stderr); putc ('\n', stderr); if (!isDedicated) Sys_ErrorMessage (text); #ifdef HAVE_AROS_MAIN_WRAPPER Sys_AtExit(); my_rc = 1; longjmp(exit_buf, 1); #else exit (1); #endif } void Sys_PrintTerm (const char *msgtxt) { const unsigned char *p; if (sys_nostdout.integer) return; for (p = (const unsigned char *) msgtxt; *p; p++) putc (*p, stdout); } void Sys_Quit (void) { Host_Shutdown(); #ifdef HAVE_AROS_MAIN_WRAPPER Sys_AtExit(); longjmp(exit_buf, 1); #else exit (0); #endif } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { struct timeval tp; double now; GetSysTime(&tp); now = tp.tv_secs + tp.tv_micro / 1e6; if (first) { first = false; starttime = now; return 0.0; } return now - starttime; } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; time_t t; struct tm *l; int val; if (!buf) buf = strbuf; t = time(NULL); l = localtime(&t); val = l->tm_mon + 1; /* tm_mon: months since January [0,11] */ buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = l->tm_mday; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = l->tm_year / 100 + 19; /* tm_year: #years since 1900. */ buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = l->tm_year % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = l->tm_hour; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = l->tm_min; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = l->tm_sec; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } /* ================ Sys_ConsoleInput ================ */ const char *Sys_ConsoleInput (void) { static char con_text[256]; static int textlen; char c; if (!isDedicated) return NULL; while (WaitForChar(amiga_stdin,10)) { Read (amiga_stdin, &c, 1); if (c == '\n' || c == '\r') { Write(amiga_stdout, "\n", 1); con_text[textlen] = '\0'; textlen = 0; return con_text; } else if (c == 8) { if (textlen) { Write(amiga_stdout, "\b \b", 3); textlen--; con_text[textlen] = '\0'; } continue; } con_text[textlen] = c; textlen++; if (textlen < (int) sizeof(con_text)) { Write(amiga_stdout, &c, 1); con_text[textlen] = '\0'; } else { // buffer is full textlen = 0; con_text[0] = '\0'; Sys_PrintTerm("\nConsole input too long!\n"); break; } } return NULL; } void Sys_Sleep (unsigned long msecs) { timerio->tr_node.io_Command = TR_ADDREQUEST; timerio->tr_time.tv_secs = msecs / 1000; timerio->tr_time.tv_micro = (msecs * 1000) % 1000000; SendIO((struct IORequest *) timerio); WaitIO((struct IORequest *) timerio); } void Sys_SendKeyEvents (void) { IN_SendKeyEvents(); } #define MAX_CLIPBOARDTXT MAXCMDLINE /* 256 */ char *Sys_GetClipboardData (void) { struct IFFHandle *IFFHandle; struct ContextNode *cn; LONG readbytes; char *chunk_buffer = NULL; if (!IFFParseBase) { return NULL; } IFFHandle = AllocIFF(); if (IFFHandle) { IFFHandle->iff_Stream = (IPTR) OpenClipboard(0); if (IFFHandle->iff_Stream) { InitIFFasClip(IFFHandle); if (!OpenIFF(IFFHandle, IFFF_READ)) { if (!StopChunk(IFFHandle, ID_FTXT, ID_CHRS)) { if (!ParseIFF(IFFHandle, IFFPARSE_SCAN)) { cn = CurrentChunk(IFFHandle); if (cn && (cn->cn_Type == ID_FTXT) && (cn->cn_ID == ID_CHRS)) { chunk_buffer = (char *) Z_Malloc(MAX_CLIPBOARDTXT, Z_MAINZONE); readbytes = ReadChunkBytes(IFFHandle, chunk_buffer, MAX_CLIPBOARDTXT - 1); if (readbytes < 0) { readbytes = 0; } chunk_buffer[readbytes] = '\0'; } } } CloseIFF(IFFHandle); } CloseClipboard((struct ClipboardHandle *) IFFHandle->iff_Stream); } FreeIFF(IFFHandle); } return chunk_buffer; } static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { #if 1 int len = q_strlcpy(dst, "PROGDIR:", dstsize); if (len < (int)dstsize) return 0; return -1; #else if (NameFromLock(GetProgramDir(), (STRPTR) dst, dstsize) != 0) return 0; return -1; #endif } static void Sys_CheckSDL (void) { #if defined(SDLQUAKE) const SDL_version *sdl_version; sdl_version = SDL_Linked_Version(); Sys_Printf("Found SDL version %i.%i.%i\n",sdl_version->major,sdl_version->minor,sdl_version->patch); #endif } static void PrintVersion (void) { Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); Sys_Printf ("running on %s engine %4.2f (%s)\n", ENGINE_NAME, ENGINE_VERSION, PLATFORM_STRING); Sys_Printf ("More info / sending bug reports: http://uhexen2.sourceforge.net\n"); } #include "snd_sys.h" static const char *help_strings[] = { " [-v | --version] Display version information", #ifndef DEMOBUILD # if defined(H2MP) " [-noportals] Disable the mission pack support", # else " [-portals | -h2mp ] Run the Portal of Praevus mission pack", # endif #endif " [-w | -window ] Run the game windowed", " [-f | -fullscreen] Run the game fullscreen", " [-width X [-height Y]] Select screen size", #ifdef GLQUAKE " [-bpp] Depth for GL fullscreen mode", " [-g | -gllibrary] Select 3D rendering library", " [-fsaa N] Enable N sample antialiasing", " [-paltex] Enable 8-bit textures", " [-nomtex] Disable multitexture detection/usage", #endif #if !defined(_NO_SOUND) #if SOUND_NUMDRIVERS " [-s | -nosound] Run the game without sound", #endif #if (SOUND_NUMDRIVERS > 1) #if HAVE_SDL_SOUND " [-sndsdl] Use SDL sound", #endif #if HAVE_AHI_SOUND " [-sndahi] Use AHI audio system", #endif #if HAVE_PAULA_SOUND " [-sndpaula] Use Paula DMA audio", #endif #endif /* SOUND_NUMDRIVERS */ #endif /* _NO_SOUND */ " [-nomouse] Disable mouse usage", " [-listen N] Enable multiplayer with max N players", " [-heapsize Bytes] Heapsize (memory to allocate)", NULL }; static void PrintHelp (const char *name) { int i = 0; Sys_Printf ("Usage: %s [options]\n", name); while (help_strings[i]) { Sys_PrintTerm (help_strings[i]); Sys_PrintTerm ("\n"); i++; } Sys_PrintTerm ("\n"); } /* =============================================================================== MAIN =============================================================================== */ static quakeparms_t parms; static char cwd[MAX_OSPATH]; int main (int argc, char **argv) { int i; double time, oldtime, newtime; ULONG availMem; #ifdef HAVE_AROS_MAIN_WRAPPER if (setjmp(exit_buf)) return my_rc; #endif PrintVersion(); if (argc > 1) { for (i = 1; i < argc; i++) { if ( !(strcmp(argv[i], "-v")) || !(strcmp(argv[i], "-version" )) || !(strcmp(argv[i], "--version")) ) { return 0; } else if ( !(strcmp(argv[i], "-h")) || !(strcmp(argv[i], "-help" )) || !(strcmp(argv[i], "-?")) || !(strcmp(argv[i], "--help")) ) { PrintHelp(argv[0]); return 0; } } } /* initialize the host params */ memset (&parms, 0, sizeof(parms)); parms.basedir = cwd; parms.userdir = cwd; parms.argc = argc; parms.argv = argv; parms.errstate = 0; host_parms = &parms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); LOG_Init (&parms); Sys_Printf("basedir is: %s\n", parms.basedir); Sys_Printf("userdir is: %s\n", parms.userdir); COM_ValidateByteorder (); isDedicated = (COM_CheckParm ("-dedicated") != 0); Sys_CheckSDL (); availMem = AvailMem(MEMF_ANY|MEMF_LARGEST); parms.memsize = (isDedicated || availMem < STD_MEM_ALLOC)? MIN_MEM_ALLOC : STD_MEM_ALLOC; i = COM_CheckParm ("-heapsize"); if (i && i < com_argc-1) parms.memsize = atoi (com_argv[i+1]) * 1024; parms.membase = malloc (parms.memsize); if (!parms.membase) Sys_Error ("Insufficient memory."); #ifndef HAVE_AROS_MAIN_WRAPPER atexit (Sys_AtExit); #endif Sys_Init (); Host_Init(); /* running from Workbench */ if (!isDedicated && argc == 0) Cvar_SetQuick(&sys_nostdout, "1"); oldtime = Sys_DoubleTime (); /* main window message loop */ while (1) { if (isDedicated) { newtime = Sys_DoubleTime (); time = newtime - oldtime; while (time < sys_ticrate.value ) { Sys_Sleep(1); newtime = Sys_DoubleTime (); time = newtime - oldtime; } Host_Frame (time); oldtime = newtime; } else { #if defined(SDLQUAKE) /* If we have no input focus at all, sleep a bit */ if (!VID_HasMouseOrInputFocus() || cl.paused) { Sys_Sleep(16); } /* If we're minimised, sleep a bit more */ if (VID_IsMinimized()) { scr_skipupdate = 1; Sys_Sleep(32); } else { scr_skipupdate = 0; } #endif newtime = Sys_DoubleTime (); time = newtime - oldtime; Host_Frame (time); if (time < sys_throttle.value) Sys_Sleep(1); oldtime = newtime; } } return 0; } engine/hexen2/sys_dos.c000066400000000000000000000623601444734033100153550ustar00rootroot00000000000000/* sys_dos.c -- DOS system interface code. * from quake1 source with adaptations for uhexen2. * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include #include #include #include #include #include #include #include #include #include #include /* for _crt0_startup_flags */ #include #include #include #include "quakedef.h" #include "dosisms.h" #include "debuglog.h" #include "sys_dxe.h" #define MIN_MEM_ALLOC 0x1000000 /* minimum 16 mb */ #define STD_MEM_ALLOC 0x2000000 /* standart 32 mb */ /* 2000-07-16, DOSQuake/DJGPP mem detection fix by * Norberto Alfredo Bensa */ int _crt0_startup_flags = _CRT0_FLAG_UNIX_SBRK; #ifdef GLQUAKE /* need at least 1MB stack for 3dfx. */ unsigned int _stklen = 1048576; /* FS: FIXME TUNE. */ #endif int end_of_memory; static qboolean lockmem, lockunlockmem, unlockmem; static int win95; #define KEYBUF_SIZE 256 static unsigned char keybuf[KEYBUF_SIZE]; static int keybuf_head = 0; static int keybuf_tail = 0; static quakeparms_t quakeparms; static int sys_checksum; /* 2000-07-28, DOSQuake "time running too fast" fix * by Norberto Alfredo Bensa. Set USE_UCLOCK_TIME * to 0 if you want to use the old original code. * See Sys_DoubleTime() for information on uclock() */ #define USE_UCLOCK_TIME 1 static void Sys_InitTime (void); #if !USE_UCLOCK_TIME static double curtime = 0.0; static double lastcurtime = 0.0; static double oldtime = 0.0; #endif /* ! USE_UCLOCK_TIME */ cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; cvar_t sys_throttle = {"sys_throttle", "0.02", CVAR_ARCHIVE}; qboolean isDedicated; float fptest_temp; extern char start_of_memory __asm__("start"); //============================================================================= // this is totally dependent on cwsdpmi putting the stack right after tge // global data // This does evil things in a Win95 DOS box!!! #if 0 extern byte end; #define CHECKBYTE 0xed static void Sys_InitStackCheck (void) { int i; for (i = 0; i < 128*1024; i++) (&end)[i] = CHECKBYTE; } void Sys_StackCheck (void) { int i; for (i = 0; i < 128*1024; i++) { if ( (&end)[i] != CHECKBYTE ) break; } Con_Printf ("%i undisturbed stack bytes\n", i); if (end != CHECKBYTE) Sys_Error ("System stack overflow!"); } #endif //============================================================================= static byte scantokey[128] = { // 0 1 2 3 4 5 6 7 // 8 9 A B C D E F 0 , 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 13, K_CTRL, 'a', 's', // 1 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', K_SHIFT, '\\', 'z', 'x', 'c', 'v', // 2 'b', 'n', 'm', ',', '.', '/', K_SHIFT, '*', K_ALT, ' ', 0 , K_F1, K_F2, K_F3, K_F4, K_F5, // 3 K_F6, K_F7, K_F8, K_F9, K_F10, 0 , 0 , K_HOME, K_UPARROW,K_PGUP,'-',K_LEFTARROW,'5',K_RIGHTARROW,'+',K_END, // 4 K_DOWNARROW,K_PGDN,K_INS,K_DEL, 0 , 0 , 0 , K_F11, K_F12, 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 5 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 6 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 }; #if 0 /* not used */ static byte shiftscantokey[128] = { // 0 1 2 3 4 5 6 7 // 8 9 A B C D E F 0 , 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', K_BACKSPACE, 9, // 0 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', 13 , K_CTRL, 'A', 'S', // 1 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"' , '~', K_SHIFT, '|', 'Z', 'X', 'C', 'V', // 2 'B', 'N', 'M', '<', '>', '?', K_SHIFT, '*', K_ALT, ' ', 0 , K_F1, K_F2, K_F3, K_F4, K_F5, // 3 K_F6, K_F7, K_F8, K_F9, K_F10, 0 , 0 , K_HOME, K_UPARROW,K_PGUP,'_',K_LEFTARROW,'%',K_RIGHTARROW,'+',K_END, // 4 K_DOWNARROW,K_PGDN,K_INS,K_DEL, 0 , 0 , 0 , K_F11, K_F12, 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 5 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 6 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 }; #endif static void TrapKey (void) { // static int ctrl = 0; keybuf[keybuf_head] = dos_inportb(0x60); dos_outportb(0x20, 0x20); /* if (scantokey[keybuf[keybuf_head] & 0x7f] == K_CTRL) ctrl = keybuf[keybuf_head] & 0x80; if (ctrl && scantokey[keybuf[keybuf_head] & 0x7f] == 'c') Sys_Error("ctrl-c hit\n"); */ keybuf_head = (keybuf_head + 1) & (KEYBUF_SIZE - 1); } static void Sys_DetectWin95 (void) { __dpmi_regs r; r.x.ax = 0x160a; /* Get Windows Version */ __dpmi_int(0x2f, &r); if (r.x.ax || r.h.bh < 4) /* Not windows or earlier than Win95 */ { win95 = 0; lockmem = true; lockunlockmem = false; unlockmem = true; } else { win95 = 1; lockunlockmem = COM_CheckParm ("-winlockunlock"); if (lockunlockmem) lockmem = true; else lockmem = COM_CheckParm ("-winlock"); unlockmem = lockmem && !lockunlockmem; } } #ifndef GLQUAKE static int minmem; static void *dos_getmaxlockedmem (int *size) { __dpmi_free_mem_info meminfo; __dpmi_meminfo info; int working_size; void *working_memory; int last_locked; int i, j, extra, allocsize; static const char msg[] = "Locking data..."; byte *x; unsigned long ul; // first lock all the current executing image so the locked count will // be accurate. It doesn't hurt to lock the memory multiple times last_locked = __djgpp_selector_limit + 1; info.size = last_locked - 4096; info.address = __djgpp_base_address + 4096; if (lockmem) { if (__dpmi_lock_linear_region(&info)) { Sys_Error ("Lock of current memory at 0x%lx for %ldKb failed!\n", info.address, info.size / 1024); } } __dpmi_get_free_memory_information(&meminfo); if (!win95) /* Not windows or earlier than Win95 */ { ul = meminfo.maximum_locked_page_allocation_in_pages * 4096; } else { ul = meminfo.largest_available_free_block_in_bytes - LEAVE_FOR_CACHE; } if (ul > 0x7fffffff) ul = 0x7fffffff; /* limit to 2GB */ working_size = (int) ul; working_size &= ~0xffff; /* Round down to 64K */ working_size += 0x10000; do { working_size -= 0x10000; /* Decrease 64K and try again */ working_memory = sbrk(working_size); } while (working_memory == (void *)-1); extra = 0xfffc - ((unsigned)sbrk(0) & 0xffff); if (extra > 0) { sbrk(extra); working_size += extra; } // now grab the memory info.address = last_locked + __djgpp_base_address; if (!win95) { info.size = __djgpp_selector_limit + 1 - last_locked; while (info.size > 0 && __dpmi_lock_linear_region(&info)) { info.size -= 0x1000; working_size -= 0x1000; sbrk(-0x1000); } } else { /* Win95 section */ j = COM_CheckParm("-winmem"); // minmem = MIN_MEM_ALLOC; minmem = STD_MEM_ALLOC; if (j && j < com_argc - 1) { allocsize = ((int)(atoi(com_argv[j + 1]))) * 0x100000 + LOCKED_FOR_MALLOC; if (allocsize < (minmem + LOCKED_FOR_MALLOC)) allocsize = minmem + LOCKED_FOR_MALLOC; } else { allocsize = minmem + LOCKED_FOR_MALLOC; } if (!lockmem) { // we won't lock, just sbrk the memory info.size = allocsize; goto UpdateSbrk; } // lock the memory down write (STDOUT_FILENO, msg, strlen (msg)); for (j = allocsize; j > (minmem + LOCKED_FOR_MALLOC); j -= 0x100000) { info.size = j; if (!__dpmi_lock_linear_region(&info)) goto Locked; write (STDOUT_FILENO, ".", 1); } // finally, try with the absolute minimum amount for (i = 0; i < 10; i++) { info.size = minmem + LOCKED_FOR_MALLOC; if (!__dpmi_lock_linear_region(&info)) goto Locked; } Sys_Error ("Can't lock memory; %lu Mb lockable RAM required. " "Try shrinking smartdrv.", info.size / 0x100000); Locked: UpdateSbrk: info.address += info.size; info.address -= __djgpp_base_address + 4; // ending point, malloc align working_size = info.address - (int)working_memory; sbrk(info.address - (int)sbrk(0)); // negative adjustment } if (lockunlockmem) { __dpmi_unlock_linear_region (&info); printf ("Locked and unlocked %d Mb data\n", working_size / 0x100000); } else if (lockmem) { printf ("Locked %d Mb data\n", working_size / 0x100000); } else { printf ("Allocated %d Mb data\n", working_size / 0x100000); } // touch all the memory to make sure it's there. The 16-page skip is to // keep Win 95 from thinking we're trying to page ourselves in (we are // doing that, of course, but there's no reason we shouldn't) x = (byte *)working_memory; for (j = 0; j < 4; j++) { for (i = 0; i < (working_size - 16 * 0x1000); i += 4) { sys_checksum += *(int *)&x[i]; sys_checksum += *(int *)&x[i + 16 * 0x1000]; } } // give some of what we locked back for malloc before returning. Done // by cheating and passing a negative value to sbrk working_size -= LOCKED_FOR_MALLOC; sbrk( -(LOCKED_FOR_MALLOC)); *size = working_size; return working_memory; } #endif int Sys_mkdir (const char *path, qboolean crash) { int rc = mkdir (path, 0777); if (rc != 0 && errno == EEXIST) rc = 0; if (rc != 0 && crash) Sys_Error("Unable to create directory %s", path); return rc; } int Sys_rmdir (const char *path) { return rmdir(path); } int Sys_unlink (const char *path) { return remove(path); } int Sys_rename (const char *oldp, const char *newp) { return rename(oldp, newp); } long Sys_filesize (const char *path) { struct ffblk f; if (findfirst(path, &f, FA_ARCH | FA_RDONLY) != 0) return -1; return (long) f.ff_fsize; } int Sys_FileType (const char *path) { int attr = _chmod(path, 0); /* Root directories on some non-local drives (e.g. CD-ROM) as well as devices may fail _chmod, but we are not interested in such cases. */ if (attr == -1) return FS_ENT_NONE; if (attr & _A_SUBDIR) return FS_ENT_DIRECTORY; if (attr & _A_VOLID) /* we shouldn't hit this! */ return FS_ENT_DIRECTORY; return FS_ENT_FILE; } #define COPY_READ_BUFSIZE 8192 /* BUFSIZ */ int Sys_CopyFile (const char *frompath, const char *topath) { char buf[COPY_READ_BUFSIZE]; int in, out; long remaining, count; struct ftime ft; in = open (frompath, O_RDONLY | O_BINARY); if (in < 0) { Con_Printf ("%s: unable to open %s\n", __thisfunc__, frompath); return 1; } remaining = filelength (in); if (remaining < 0) { Con_Printf ("%s: %s failed filelength()\n", __thisfunc__, frompath); close (in); return 1; } out = open (topath, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, 0666); if (out < 0) { Con_Printf ("%s: unable to create %s\n", __thisfunc__, topath); close (in); return 1; } while (remaining) { if (remaining < sizeof(buf)) count = remaining; else count = sizeof(buf); if (read(in, buf, count) < 0) break; if (write(out, buf, count) < 0) break; remaining -= count; } if (remaining == 0) { /* restore the file's timestamp */ if (getftime(in, &ft) == 0) setftime(out, &ft); } close (in); close (out); return remaining; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only. ================================================= */ static struct ffblk finddata; static int findhandle = -1; static char findstr[MAX_OSPATH]; const char *Sys_FindFirstFile (const char *path, const char *pattern) { if (findhandle == 0) Sys_Error ("Sys_FindFirst without FindClose"); q_snprintf (findstr, sizeof(findstr), "%s/%s", path, pattern); memset (&finddata, 0, sizeof(finddata)); findhandle = findfirst(findstr, &finddata, FA_ARCH | FA_RDONLY); if (findhandle == 0) return finddata.ff_name; return NULL; } const char *Sys_FindNextFile (void) { if (findhandle != 0) return NULL; if (findnext(&finddata) == 0) return finddata.ff_name; return NULL; } void Sys_FindClose (void) { findhandle = -1; } const char *Sys_ConsoleInput (void) { static char con_text[256]; static int textlen = 0; char ch; if (! kbhit()) return NULL; ch = getche(); switch (ch) { case '\r': putch('\n'); if (textlen) { con_text[textlen] = '\0'; textlen = 0; return con_text; } break; case '\b': putch(' '); if (textlen) { textlen--; putch('\b'); } break; default: con_text[textlen] = ch; textlen = (textlen + 1) & 0xff; break; } return NULL; } void Sys_Sleep (unsigned long msecs) { usleep (msecs * 1000); } static void Sys_Init (void) { MaskExceptions (); Sys_SetFPCW (); #if !USE_UCLOCK_TIME dos_outportb(0x43, 0x34); // set system timer to mode 2 dos_outportb(0x40, 0); // for Sys_DoubleTime() dos_outportb(0x40, 0); #endif /* ! USE_UCLOCK_TIME */ Sys_InitTime (); _go32_interrupt_stack_size = 4 * 1024; _go32_rmcb_stack_size = 4 * 1024; Sys_InitDXE3(); } void Sys_Shutdown (void) { if (!isDedicated) dos_restoreintr(9); if (unlockmem) { dos_unlockmem (&start_of_memory, end_of_memory - (int)&start_of_memory); dos_unlockmem (quakeparms.membase, quakeparms.memsize); } } #define SC_UPARROW 0x48 #define SC_DOWNARROW 0x50 #define SC_LEFTARROW 0x4b #define SC_RIGHTARROW 0x4d #define SC_LEFTSHIFT 0x2a #define SC_RIGHTSHIFT 0x36 void Sys_SendKeyEvents (void) { int k, next; int outkey; // get key events while (keybuf_head != keybuf_tail) { k = keybuf[keybuf_tail++]; keybuf_tail &= (KEYBUF_SIZE - 1); if (k == 0xe0) continue; // special / pause keys next = keybuf[(keybuf_tail - 2) & (KEYBUF_SIZE - 1)]; // Pause generates e1 1d 45 e1 9d c5 when pressed, and // nothing when released. e1 is generated only for the // pause key. if (next == 0xe1) continue; // pause key bullshit if (k == 0xc5 && next == 0x9d) { Key_Event (K_PAUSE, true); continue; } // extended keyboard shift key bullshit if ( (k & 0x7f) == SC_LEFTSHIFT || (k & 0x7f) == SC_RIGHTSHIFT ) { if (keybuf[(keybuf_tail - 2) & (KEYBUF_SIZE - 1)] == 0xe0) continue; k &= 0x80; k |= SC_RIGHTSHIFT; } if (k == 0xc5 && keybuf[(keybuf_tail - 2) & (KEYBUF_SIZE - 1)] == 0x9d) continue; // more pause bullshit outkey = scantokey[k & 0x7f]; if (k & 0x80) Key_Event (outkey, false); else Key_Event (outkey, true); } } char *Sys_GetClipboardData (void) { return NULL; } // ======================================================================= // General routines // ======================================================================= void Sys_PrintTerm (const char *msgtxt) { const unsigned char *p; if (sys_nostdout.integer) return; for (p = (const unsigned char *) msgtxt; *p; p++) putc (*p, stdout); } static void Sys_AtExit (void) { // shutdown only once (so Sys_Error can call this function to shutdown, then // print the error message, then call exit without exit calling this function // again) Sys_Shutdown(); } void Sys_Quit (void) { Cvar_SetROM ("sys_nostdout", "0"); // enable printing to terminal Host_Shutdown (); exit (0); } #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const unsigned char *p; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (con_debuglog) { LOG_Print (ERROR_PREFIX); LOG_Print (text); LOG_Print ("\n\n"); } Cvar_SetROM ("sys_nostdout", "0"); // enable printing to terminal Host_Shutdown (); for (p = (const unsigned char *) text2; *p; p++) putc (*p, stderr); for (p = (const unsigned char *) text ; *p; p++) putc (*p, stderr); putc ('\n', stderr); putc ('\n', stderr); // Sys_AtExit is called by exit to shutdown the system exit (1); } /* ================ Sys_MakeCodeWriteable ================ */ void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) { // it's always writeable } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { #if USE_UCLOCK_TIME /* From DJGPP uclock() man page : uclock() returns the number of uclock ticks since an arbitrary time, actually, since the first call to uclock(), which itself returns zero. The number of tics per second is UCLOCKS_PER_SEC (declared in time.h as 1193180.) uclock() is provided for very high-resulution timing. uclock_t is a 64-bit integer. It is currently accurate to better than 1 microsecond (actually about 840 nanoseconds). You cannot time across two midnights with this implementation, giving a maximum useful period of 48 hours and an effective limit of 24 hours. Casting to a 32-bit integer limits its usefulness to about an hour before 32 bits will wrap. Also note that uclock reprograms the interval timer in your PC to act as a rate generator rather than a square wave generator. I've had no problems running in this mode all the time, but if you notice strange things happening with the clock (losing time) after using uclock, check to see if this is the cause of the problem. Windows 3.X doesn't allow to reprogram the timer so the values returned by uclock() there are incorrect. DOS and Windows 9X don't have this problem. Windows NT, 2000 and XP attempt to use the rdtsc feature of newer CPUs instead of the interval timer because the timer tick and interval timer are not coordinated. During calibration the SIGILL signal handler is replaced to protect against systems which do not support or allow rdtsc. If rdtsc is available, uclock will keep the upper bits of the returned value consistent with the bios tick counter by re-calibration if needed. If rdtsc is not available, these systems fall back to interval timer usage, which may show an absolute error of 65536 uclock ticks in the values and not be monotonically increasing. */ return (double) uclock() / (double) UCLOCKS_PER_SEC; #else int r; unsigned t, tick; double ft, time; static int sametimecount; Sys_PushFPCW_SetHigh (); t = *(unsigned short*)real2ptr(0x46c) * 65536; dos_outportb(0x43, 0); // latch time r = dos_inportb(0x40); r |= dos_inportb(0x40) << 8; r = (r - 1) & 0xffff; tick = *(unsigned short*)real2ptr(0x46c) * 65536; if ((tick != t) && (r & 0x8000)) t = tick; ft = (double) (t + (65536 - r)) / 1193200.0; time = ft - oldtime; oldtime = ft; if (time < 0) { if (time > -3000.0) time = 0.0; else time += 3600.0; } curtime += time; if (curtime == lastcurtime) { sametimecount++; if (sametimecount > 100000) { curtime += 1.0; sametimecount = 0; } } else { sametimecount = 0; } lastcurtime = curtime; Sys_PopFPCW (); return curtime; #endif /* ! USE_UCLOCK_TIME */ } /* ================ Sys_InitTime ================ */ static void Sys_InitTime (void) { #if !USE_UCLOCK_TIME int j; Sys_DoubleTime (); oldtime = curtime; j = COM_CheckParm("-starttime"); if (j && j < com_argc - 1) { curtime = (double) (atof(com_argv[j+1])); } else { curtime = 0.0; } lastcurtime = curtime; #endif /* ! USE_UCLOCK_TIME */ } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; struct _dosdate_t d; struct _dostime_t t; unsigned int val; if (!buf) buf = strbuf; _dos_getdate(&d); _dos_gettime(&t); val = d.month; buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = d.day; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = d.year / 100; buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = d.year % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = t.hour; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = t.minute; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = t.second; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } /* ================ Sys_GetMemory ================ */ static void Sys_GetMemory (void) { int j, tsize; j = COM_CheckParm("-mem"); if (j && j < com_argc - 1) { quakeparms.memsize = (int) (atof(com_argv[j + 1]) * 1024 * 1024); quakeparms.membase = malloc (quakeparms.memsize); } #ifdef GLQUAKE else { /* 16 mb is usually enough. Leave rest of mem for gl driver */ quakeparms.memsize = (int) 0x1000000; quakeparms.membase = malloc (quakeparms.memsize); } #else else { quakeparms.membase = dos_getmaxlockedmem (&quakeparms.memsize); } #endif printf("malloc'd: %d\n", quakeparms.memsize); j = COM_CheckParm ("-heapsize"); if (j && j < com_argc - 1) { tsize = atoi (com_argv[j + 1]) * 1024; if (tsize < quakeparms.memsize) quakeparms.memsize = tsize; } } /* ================ Sys_PageInProgram walks the text, data, and bss to make sure it's all paged in so that the actual physical memory detected by Sys_GetMemory is correct. ================ */ static void Sys_PageInProgram (void) { int i, j; end_of_memory = (int)sbrk(0); if (lockmem) { if (dos_lockmem ((void *)&start_of_memory, end_of_memory - (int)&start_of_memory)) Sys_Error ("Couldn't lock text and data"); } if (lockunlockmem) { dos_unlockmem((void *)&start_of_memory, end_of_memory - (int)&start_of_memory); printf ("Locked and unlocked %d Mb image\n", (end_of_memory - (int)&start_of_memory) / 0x100000); } else if (lockmem) { printf ("Locked %d Mb image\n", (end_of_memory - (int)&start_of_memory) / 0x100000); } else { printf ("Loaded %d Mb image\n", (end_of_memory - (int)&start_of_memory) / 0x100000); } // touch the entire image, doing the 16-page skip so Win95 doesn't think we're // trying to page ourselves in for (j = 0; j < 4; j++) { for (i = (int)&start_of_memory; i < (end_of_memory - 16 * 0x1000); i += 4) { sys_checksum += *(int *)i; sys_checksum += *(int *)(i + 16 * 0x1000); } } } /* ================ Sys_NoFPUExceptionHandler ================ */ static void Sys_NoFPUExceptionHandler (int whatever) { const char err[] = "\nError: Hexen II requires a floating-point processor\n"; const unsigned char *p; for (p = (const unsigned char *) err; *p; p++) putc (*p, stderr); exit (1); } /* ================ Sys_DefaultExceptionHandler ================ */ static void Sys_DefaultExceptionHandler (int whatever) { } static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { char *tmp; if (getcwd(dst, dstsize - 1) == NULL) return -1; tmp = dst; while (*tmp != 0) tmp++; while (*tmp == 0 && tmp != dst) { --tmp; if (tmp != dst && (*tmp == '/' || *tmp == '\\')) *tmp = 0; } return 0; } static void PrintVersion (void) { printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); printf ("running on %s engine %4.2f (%s)\n", ENGINE_NAME, ENGINE_VERSION, PLATFORM_STRING); printf ("More info / sending bug reports: http://uhexen2.sourceforge.net\n"); } /* ================ main ================ */ static char cwd[MAX_OSPATH]; int main (int argc, char **argv) { double time, oldtime, newtime; PrintVersion(); // make sure there's an FPU signal(SIGNOFP, Sys_NoFPUExceptionHandler); signal(SIGABRT, Sys_DefaultExceptionHandler); signal(SIGALRM, Sys_DefaultExceptionHandler); signal(SIGKILL, Sys_DefaultExceptionHandler); signal(SIGQUIT, Sys_DefaultExceptionHandler); signal(SIGINT, Sys_DefaultExceptionHandler); if (fptest_temp >= 0.0) fptest_temp += 0.1; /* initialize the host params */ memset (&quakeparms, 0, sizeof(quakeparms)); quakeparms.basedir = cwd; quakeparms.userdir = cwd; quakeparms.argc = argc; quakeparms.argv = argv; quakeparms.errstate = 0; host_parms = &quakeparms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); LOG_Init (&quakeparms); COM_ValidateByteorder (); Sys_DetectWin95 (); Sys_PageInProgram (); Sys_GetMemory (); atexit (Sys_AtExit); // in case we crash isDedicated = (COM_CheckParm ("-dedicated") != 0); Sys_Init (); if (!isDedicated) dos_registerintr(9, TrapKey); // Sys_InitStackCheck (); Host_Init(); // Sys_StackCheck (); // Con_Printf ("Top of stack: 0x%x\n", &time); oldtime = Sys_DoubleTime (); while (1) { newtime = Sys_DoubleTime (); time = newtime - oldtime; if (isDedicated && (time < sys_ticrate.value)) continue; Host_Frame (time); // Sys_StackCheck (); oldtime = newtime; } } engine/hexen2/sys_os2.c000066400000000000000000000317461444734033100152770ustar00rootroot00000000000000/* * sys_os2.c -- OS/2 system interface code * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2016 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(SDLQUAKE) #include "sys_sdl.h" /* alternative implementations using SDL. */ #endif #include "debuglog.h" #define INCL_DOS #define INCL_DOSERRORS #ifdef __EMX__ #define INCL_KBD #define INCL_VIO #endif #include #include #include #ifdef __WATCOMC__ #include #endif #include #if defined(SDLQUAKE) #include "sdl_inc.h" #endif #define MIN_MEM_ALLOC 0x1000000 #define STD_MEM_ALLOC 0x2000000 cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; cvar_t sys_throttle = {"sys_throttle", "0.02", CVAR_ARCHIVE}; qboolean isDedicated; /* =============================================================================== FILE IO =============================================================================== */ int Sys_mkdir (const char *path, qboolean crash) { FILESTATUS3 fs; APIRET rc = DosCreateDir(path, NULL); if (rc == NO_ERROR) return 0; if ((DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)) == NO_ERROR) && (fs.attrFile & FILE_DIRECTORY)) { return 0; /* dir exists */ } if (crash) Sys_Error("Unable to create directory %s (ERR: %lu)", path, rc); return -1; } int Sys_rmdir (const char *path) { APIRET rc = DosDeleteDir(path); return (rc == NO_ERROR)? 0 : -1; } int Sys_unlink (const char *path) { APIRET rc = DosDelete(path); return (rc == NO_ERROR)? 0 : -1; } int Sys_rename (const char *oldp, const char *newp) { APIRET rc = DosMove(oldp, newp); return (rc == NO_ERROR)? 0 : -1; } long Sys_filesize (const char *path) { FILESTATUS3 fs; APIRET rc = DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)); if (rc != NO_ERROR) return -1; if (fs.attrFile & FILE_DIRECTORY) return -1; return (long)fs.cbFile; } int Sys_FileType (const char *path) { FILESTATUS3 fs; APIRET rc = DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)); if (rc != NO_ERROR) return FS_ENT_NONE; if (fs.attrFile & FILE_DIRECTORY) return FS_ENT_DIRECTORY; return FS_ENT_FILE; } int Sys_CopyFile (const char *frompath, const char *topath) { APIRET rc = DosCopy(frompath, topath, DCPY_EXISTING); return (rc == NO_ERROR)? 0 : -1; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only, not a dirent struct. this is what we presently need in this engine. ================================================= */ static HDIR findhandle = HDIR_CREATE; static FILEFINDBUF3 findbuffer; static char findstr[MAX_OSPATH]; const char *Sys_FindFirstFile (const char *path, const char *pattern) { ULONG cnt = 1; APIRET rc; if (findhandle != HDIR_CREATE) Sys_Error ("Sys_FindFirst without FindClose"); q_snprintf (findstr, sizeof(findstr), "%s/%s", path, pattern); findbuffer.oNextEntryOffset = 0; rc = DosFindFirst(findstr, &findhandle, FILE_NORMAL, &findbuffer, sizeof(findbuffer), &cnt, FIL_STANDARD); if (rc != NO_ERROR) { findhandle = HDIR_CREATE; findbuffer.oNextEntryOffset = 0; return NULL; } if (findbuffer.attrFile & FILE_DIRECTORY) return Sys_FindNextFile(); return findbuffer.achName; } const char *Sys_FindNextFile (void) { APIRET rc; ULONG cnt; if (findhandle == HDIR_CREATE) return NULL; while (1) { cnt = 1; rc = DosFindNext(findhandle, &findbuffer, sizeof(findbuffer), &cnt); if (rc != NO_ERROR) return NULL; if (!(findbuffer.attrFile & FILE_DIRECTORY)) return findbuffer.achName; } return NULL; } void Sys_FindClose (void) { if (findhandle != HDIR_CREATE) { DosFindClose(findhandle); findhandle = HDIR_CREATE; findbuffer.oNextEntryOffset = 0; } } /* =============================================================================== SYSTEM IO =============================================================================== */ /* ================ Sys_MakeCodeWriteable ================ */ #if id386 && !defined(GLQUAKE) void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) { APIRET rc = DosSetMem((void *)startaddr, length, PAG_READ | PAG_WRITE | PAG_EXECUTE); if (rc != NO_ERROR) Sys_Error("Protection change failed (ERR: %lu)", rc); } #endif /* id386, !GLQUAKE */ /* ================ Sys_Init ================ */ static void Sys_Init (void) { /* do we really need these with opengl ?? */ Sys_SetFPCW(); #if defined(SDLQUAKE) if (SDL_Init(0) < 0) Sys_Error("SDL failed to initialize."); #endif } static void Sys_AtExit (void) { #if defined(SDLQUAKE) SDL_Quit(); #endif } #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const unsigned char *p; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (con_debuglog) { LOG_Print (ERROR_PREFIX); LOG_Print (text); LOG_Print ("\n\n"); } Host_Shutdown (); for (p = (const unsigned char *) text2; *p; p++) putc (*p, stderr); for (p = (const unsigned char *) text ; *p; p++) putc (*p, stderr); putc ('\n', stderr); putc ('\n', stderr); #if 0 if (!isDedicated) WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, text, ENGINE_NAME " Error", 0, MB_OK | MB_MOVEABLE | MB_ERROR); #endif exit (1); } void Sys_PrintTerm (const char *msgtxt) { const unsigned char *p; if (sys_nostdout.integer) return; for (p = (const unsigned char *) msgtxt; *p; p++) putc (*p, stdout); } void Sys_Quit (void) { Host_Shutdown(); exit (0); } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { union i64 { QWORD qw; long long ll; }; static qboolean first = true; static ULONG ticks_per_sec; static union i64 start; union i64 now; if (first) { first = false; DosTmrQueryFreq(&ticks_per_sec); DosTmrQueryTime(&start.qw); return 0.0; } DosTmrQueryTime(&now.qw); return (double)(now.ll - start.ll) / (double)ticks_per_sec; } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; DATETIME dt; unsigned int val; if (!buf) buf = strbuf; DosGetDateTime (&dt); val = dt.month; buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = dt.day; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = dt.year / 100; buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = dt.year % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = dt.hours; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = dt.minutes; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = dt.seconds; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } /* ================ Sys_ConsoleInput ================ */ #ifdef __EMX__ int putch (int c) { char ch = c; VioWrtTTY(&ch, 1, 0); return c; } int kbhit (void) { KBDKEYINFO k; if (KbdPeek(&k, 0)) return 0; return (k.fbStatus & KBDTRF_FINAL_CHAR_IN); } #endif const char *Sys_ConsoleInput (void) { static char con_text[256]; static int textlen = 0; char ch; if (! kbhit()) return NULL; ch = getche(); switch (ch) { case '\r': putch('\n'); if (textlen) { con_text[textlen] = '\0'; textlen = 0; return con_text; } break; case '\b': putch(' '); if (textlen) { textlen--; putch('\b'); } break; default: con_text[textlen] = ch; textlen = (textlen + 1) & 0xff; break; } return NULL; } void Sys_Sleep (unsigned long msecs) { DosSleep (msecs); } void Sys_SendKeyEvents (void) { IN_SendKeyEvents(); } #if !defined(Sys_GetClipboardData) #define Sys_GetClipboardData Sys_GetClipboardData /* */ char *Sys_GetClipboardData (void) { return NULL; } #endif static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { ULONG l, drv; if (dstsize < 8) return -1; l = dstsize - 3; if (DosQueryCurrentDir(0, (PBYTE) dst + 3, &l) != NO_ERROR) return -1; DosQueryCurrentDisk(&drv, &l); dst[0] = drv + 'A' - 1; dst[1] = ':'; dst[2] = '\\'; return 0; } static void Sys_CheckSDL (void) { #if defined(SDLQUAKE) const SDL_version *sdl_version; sdl_version = SDL_Linked_Version(); Sys_Printf("Found SDL version %i.%i.%i\n",sdl_version->major,sdl_version->minor,sdl_version->patch); #endif } static void PrintVersion (void) { Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); Sys_Printf ("running on %s engine %4.2f (%s)\n", ENGINE_NAME, ENGINE_VERSION, PLATFORM_STRING); Sys_Printf ("More info / sending bug reports: http://uhexen2.sourceforge.net\n"); } #include "snd_sys.h" static const char *help_strings[] = { " [-v | --version] Display version information", #ifndef DEMOBUILD # if defined(H2MP) " [-noportals] Disable the mission pack support", # else " [-portals | -h2mp ] Run the Portal of Praevus mission pack", # endif #endif " [-width X [-height Y]] Select screen size", #ifdef GLQUAKE " [-bpp] Depth for GL fullscreen mode", " [-vsync] Enable sync with monitor refresh", " [-g | -gllibrary] Select 3D rendering library", " [-fsaa N] Enable N sample antialiasing", " [-paltex] Enable 8-bit textures", " [-nomtex] Disable multitexture detection/usage", #endif #if !defined(_NO_SOUND) #if SOUND_NUMDRIVERS " [-s | -nosound] Run the game without sound", #endif #if (SOUND_NUMDRIVERS > 1) #if HAVE_SDL_SOUND " [-sndsdl] Use SDL sound", #endif #endif /* SOUND_NUMDRIVERS */ #endif /* _NO_SOUND */ " [-nomouse] Disable mouse usage", " [-listen N] Enable multiplayer with max N players", " [-heapsize Bytes] Heapsize (memory to allocate)", NULL }; static void PrintHelp (const char *name) { int i = 0; Sys_Printf ("Usage: %s [options]\n", name); while (help_strings[i]) { Sys_PrintTerm (help_strings[i]); Sys_PrintTerm ("\n"); i++; } Sys_PrintTerm ("\n"); } /* =============================================================================== MAIN =============================================================================== */ static quakeparms_t parms; static char cwd[MAX_OSPATH]; int main (int argc, char **argv) { int i; double time, oldtime, newtime; PrintVersion(); if (argc > 1) { for (i = 1; i < argc; i++) { if ( !(strcmp(argv[i], "-v")) || !(strcmp(argv[i], "-version" )) || !(strcmp(argv[i], "--version")) ) { exit(0); } else if ( !(strcmp(argv[i], "-h")) || !(strcmp(argv[i], "-help" )) || !(strcmp(argv[i], "-?")) || !(strcmp(argv[i], "--help")) ) { PrintHelp(argv[0]); exit (0); } } } /* initialize the host params */ memset (&parms, 0, sizeof(parms)); parms.basedir = cwd; parms.userdir = cwd; parms.argc = argc; parms.argv = argv; parms.errstate = 0; host_parms = &parms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); LOG_Init (&parms); Sys_Printf("basedir is: %s\n", parms.basedir); COM_ValidateByteorder (); isDedicated = (COM_CheckParm ("-dedicated") != 0); Sys_CheckSDL (); parms.memsize = (isDedicated)? MIN_MEM_ALLOC : STD_MEM_ALLOC; i = COM_CheckParm ("-heapsize"); if (i && i < com_argc-1) parms.memsize = atoi (com_argv[i+1]) * 1024; parms.membase = malloc (parms.memsize); if (!parms.membase) Sys_Error ("Insufficient memory."); Sys_Init (); atexit (Sys_AtExit); Host_Init(); oldtime = Sys_DoubleTime (); /* main window message loop */ while (1) { if (isDedicated) { newtime = Sys_DoubleTime (); time = newtime - oldtime; while (time < sys_ticrate.value ) { DosSleep (1); newtime = Sys_DoubleTime (); time = newtime - oldtime; } Host_Frame (time); oldtime = newtime; } else { #if defined(SDLQUAKE) /* If we have no input focus at all, sleep a bit */ if (!VID_HasMouseOrInputFocus() || cl.paused) { DosSleep (16); } /* If we're minimised, sleep a bit more */ if (VID_IsMinimized()) { scr_skipupdate = 1; DosSleep (32); } else { scr_skipupdate = 0; } #endif newtime = Sys_DoubleTime (); time = newtime - oldtime; Host_Frame (time); if (time < sys_throttle.value) DosSleep (1); oldtime = newtime; } } return 0; } engine/hexen2/sys_unix.c000066400000000000000000000374151444734033100155560ustar00rootroot00000000000000/* sys_unix.c -- Unix system interface code * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2001 contributors of the Anvil of Thyrion project * Copyright (C) 2004-2005 Steven Atkinson * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(PLATFORM_OSX) #include "sys_osx.h" /* Mac OS X specifics */ #elif defined(SDLQUAKE) #include "sys_sdl.h" /* alternative implementations using SDL. */ #endif #include "userdir.h" #include "debuglog.h" #include #include #if DO_USERDIRS #include #endif #include #include #include #include #include #include #include #include #include #if defined(SDLQUAKE) #include "sdl_inc.h" #endif #define MIN_MEM_ALLOC 0x1000000 #define STD_MEM_ALLOC 0x2000000 cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; cvar_t sys_throttle = {"sys_throttle", "0.02", CVAR_ARCHIVE}; qboolean isDedicated; static double starttime; static qboolean first = true; /* =============================================================================== FILE IO =============================================================================== */ int Sys_mkdir (const char *path, qboolean crash) { int rc = mkdir (path, 0777); if (rc != 0 && errno == EEXIST) { struct stat st; if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) rc = 0; } if (rc != 0 && crash) { rc = errno; Sys_Error("Unable to create directory %s: %s", path, strerror(rc)); } return rc; } int Sys_rmdir (const char *path) { return rmdir(path); } int Sys_unlink (const char *path) { return unlink(path); } int Sys_rename (const char *oldp, const char *newp) { return rename(oldp, newp); } long Sys_filesize (const char *path) { struct stat st; if (stat(path, &st) != 0) return -1; if (! S_ISREG(st.st_mode)) return -1; return (long) st.st_size; } int Sys_FileType (const char *path) { /* if (access(path, R_OK) == -1) return 0; */ struct stat st; if (stat(path, &st) != 0) return FS_ENT_NONE; if (S_ISDIR(st.st_mode)) return FS_ENT_DIRECTORY; if (S_ISREG(st.st_mode)) return FS_ENT_FILE; return FS_ENT_NONE; } #define COPY_READ_BUFSIZE 8192 /* BUFSIZ */ int Sys_CopyFile (const char *frompath, const char *topath) { char buf[COPY_READ_BUFSIZE]; FILE *in, *out; struct stat st; struct utimbuf tm; /* off_t remaining, count;*/ size_t remaining, count; if (stat(frompath, &st) != 0) { Con_Printf ("%s: unable to stat %s\n", __thisfunc__, frompath); return 1; } in = fopen (frompath, "rb"); if (!in) { Con_Printf ("%s: unable to open %s\n", __thisfunc__, frompath); return 1; } out = fopen (topath, "wb"); if (!out) { Con_Printf ("%s: unable to create %s\n", __thisfunc__, topath); fclose (in); return 1; } remaining = st.st_size; while (remaining) { if (remaining < sizeof(buf)) count = remaining; else count = sizeof(buf); if (fread(buf, 1, count, in) != count) break; if (fwrite(buf, 1, count, out) != count) break; remaining -= count; } fclose (in); fclose (out); if (remaining == 0) { /* restore the file's timestamp */ tm.actime = time (NULL); tm.modtime = st.st_mtime; utime (topath, &tm); return 0; } return 1; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only, not a dirent struct. this is what we presently need in this engine. ================================================= */ static DIR *finddir; static struct dirent *finddata; static char *findpath, *findpattern; const char *Sys_FindFirstFile (const char *path, const char *pattern) { if (finddir) Sys_Error ("Sys_FindFirst without FindClose"); finddir = opendir (path); if (!finddir) return NULL; findpattern = Z_Strdup (pattern); findpath = Z_Strdup (path); return Sys_FindNextFile(); } const char *Sys_FindNextFile (void) { struct stat test; if (!finddir) return NULL; while ((finddata = readdir(finddir)) != NULL) { if (!fnmatch (findpattern, finddata->d_name, FNM_PATHNAME)) { if ( (stat(va("%s/%s", findpath, finddata->d_name), &test) == 0) && S_ISREG(test.st_mode)) return finddata->d_name; } } return NULL; } void Sys_FindClose (void) { if (finddir != NULL) { closedir(finddir); finddir = NULL; } if (findpath != NULL) { Z_Free (findpath); findpath = NULL; } if (findpattern != NULL) { Z_Free (findpattern); findpattern = NULL; } } /* =============================================================================== SYSTEM IO =============================================================================== */ /* ================ Sys_MakeCodeWriteable ================ */ #if id386 && !defined(GLQUAKE) void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) { int r; unsigned long endaddr = startaddr + length; // systems with mprotect but not getpagesize (or similar) probably don't // need to page align the arguments to mprotect (eg, QNX) #if !(defined(__QNX__) || defined(__QNXNTO__)) // int psize = getpagesize (); long psize = sysconf (_SC_PAGESIZE); startaddr &= ~(psize - 1); endaddr = (endaddr + psize - 1) & ~(psize - 1); #endif r = mprotect ((char *) startaddr, endaddr - startaddr, PROT_WRITE | PROT_READ | PROT_EXEC); if (r == -1) Sys_Error("Protection change failed\n"); } #endif /* id386, !GLQUAKE */ /* ================ Sys_Init ================ */ static void Sys_Init (void) { /* do we really need these with opengl ?? */ Sys_SetFPCW(); #if defined(SDLQUAKE) if (SDL_Init(0) < 0) Sys_Error("SDL failed to initialize."); #endif } static void Sys_AtExit (void) { #if defined(SDLQUAKE) SDL_Quit(); #endif } #if !defined(Sys_ErrorMessage) #define Sys_ErrorMessage(T) do {} while (0) #endif #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const unsigned char *p; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (con_debuglog) { LOG_Print (ERROR_PREFIX); LOG_Print (text); LOG_Print ("\n\n"); } Host_Shutdown (); for (p = (const unsigned char *) text2; *p; p++) putc (*p, stderr); for (p = (const unsigned char *) text ; *p; p++) putc (*p, stderr); putc ('\n', stderr); putc ('\n', stderr); if (!isDedicated) Sys_ErrorMessage (text); exit (1); } void Sys_PrintTerm (const char *msgtxt) { const unsigned char *p; if (sys_nostdout.integer) return; for (p = (const unsigned char *) msgtxt; *p; p++) putc (*p, stdout); } void Sys_Quit (void) { Host_Shutdown(); exit (0); } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { struct timeval tp; double now; gettimeofday (&tp, NULL); now = tp.tv_sec + tp.tv_usec / 1e6; if (first) { first = false; starttime = now; return 0.0; } return now - starttime; } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; time_t t; struct tm *l; int val; if (!buf) buf = strbuf; t = time(NULL); l = localtime(&t); val = l->tm_mon + 1; /* tm_mon: months since January [0,11] */ buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = l->tm_mday; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = l->tm_year / 100 + 19; /* tm_year: #years since 1900. */ buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = l->tm_year % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = l->tm_hour; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = l->tm_min; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = l->tm_sec; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } /* ================ Sys_ConsoleInput ================ */ const char *Sys_ConsoleInput (void) { static char con_text[256]; static int textlen; char c; fd_set set; struct timeval timeout; FD_ZERO (&set); FD_SET (0, &set); // stdin timeout.tv_sec = 0; timeout.tv_usec = 0; while (select (1, &set, NULL, NULL, &timeout)) { read (0, &c, 1); if (c == '\n' || c == '\r') { con_text[textlen] = '\0'; textlen = 0; return con_text; } else if (c == 8) { if (textlen) { textlen--; con_text[textlen] = '\0'; } continue; } con_text[textlen] = c; textlen++; if (textlen < (int) sizeof(con_text)) con_text[textlen] = '\0'; else { // buffer is full textlen = 0; con_text[0] = '\0'; Sys_PrintTerm("\nConsole input too long!\n"); break; } } return NULL; } void Sys_Sleep (unsigned long msecs) { usleep (msecs * 1000); } void Sys_SendKeyEvents (void) { IN_SendKeyEvents(); } #if !defined(Sys_GetClipboardData) #define Sys_GetClipboardData Sys_GetClipboardData /* */ char *Sys_GetClipboardData (void) { return NULL; } #endif #if !defined(Sys_GetBasedir) #define Sys_GetBasedir UNIX_GetBasedir static int UNIX_GetBasedir (char *argv0, char *dst, size_t dstsize) { char *tmp; if (getcwd(dst, dstsize - 1) == NULL) return -1; tmp = dst; while (*tmp != 0) tmp++; while (*tmp == 0 && tmp != dst) { --tmp; if (tmp != dst && *tmp == '/') *tmp = 0; } return 0; } #endif /* Sys_GetBasedir */ #if DO_USERDIRS static int Sys_GetUserdir (char *dst, size_t dstsize) { size_t n; const char *home_dir = NULL; struct passwd *pwent; pwent = getpwuid(getuid()); if (pwent == NULL) perror("getpwuid"); else home_dir = pwent->pw_dir; if (home_dir == NULL) home_dir = getenv("HOME"); if (home_dir == NULL) return 1; /* what would be a maximum path for a file in the user's directory... * $HOME/AOT_USERDIR/game_dir/dirname1/dirname2/dirname3/filename.ext * still fits in the MAX_OSPATH == 256 definition, but just in case. */ n = strlen(home_dir) + strlen(AOT_USERDIR) + 50; if (n >= dstsize) { Sys_Error ("%s: Insufficient bufsize %d. Need at least %d.", __thisfunc__, (int)dstsize, (int)n); } q_snprintf (dst, dstsize, "%s/%s", home_dir, AOT_USERDIR); return 0; } #endif /* DO_USERDIRS */ static void Sys_CheckSDL (void) { #if defined(SDLQUAKE) const SDL_version *sdl_version; sdl_version = SDL_Linked_Version(); Sys_Printf("Found SDL version %i.%i.%i\n",sdl_version->major,sdl_version->minor,sdl_version->patch); #endif } static void PrintVersion (void) { Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); Sys_Printf ("running on %s engine %4.2f (%s)\n", ENGINE_NAME, ENGINE_VERSION, PLATFORM_STRING); Sys_Printf ("More info / sending bug reports: http://uhexen2.sourceforge.net\n"); } #include "snd_sys.h" static const char *help_strings[] = { " [-v | --version] Display version information", #ifndef DEMOBUILD # if defined(H2MP) " [-noportals] Disable the mission pack support", # else " [-portals | -h2mp ] Run the Portal of Praevus mission pack", # endif #endif #ifndef SVGAQUAKE " [-w | -window ] Run the game windowed", " [-f | -fullscreen] Run the game fullscreen", #endif " [-width X [-height Y]] Select screen size", #ifdef GLQUAKE " [-bpp] Depth for GL fullscreen mode", " [-vsync] Enable sync with monitor refresh", " [-g | -gllibrary] Select 3D rendering library", " [-fsaa N] Enable N sample antialiasing", " [-paltex] Enable 8-bit textures", " [-nomtex] Disable multitexture detection/usage", #endif #if !defined(_NO_SOUND) #if SOUND_NUMDRIVERS " [-s | -nosound] Run the game without sound", #endif #if (SOUND_NUMDRIVERS > 1) #if HAVE_OSS_SOUND " [-sndoss] Use OSS sound", #endif #if HAVE_ALSA_SOUND " [-sndalsa] Use ALSA sound (alsa > 1.0.1)", #endif #if HAVE_SUN_SOUND " [-sndsun | -sndbsd] Use SUN / BSD sound", #endif #if HAVE_SDL_SOUND " [-sndsdl] Use SDL sound", #endif #endif /* SOUND_NUMDRIVERS */ #endif /* _NO_SOUND */ " [-nomouse] Disable mouse usage", " [-listen N] Enable multiplayer with max N players", " [-heapsize Bytes] Heapsize (memory to allocate)", NULL }; static void PrintHelp (const char *name) { int i = 0; Sys_Printf ("Usage: %s [options]\n", name); while (help_strings[i]) { Sys_PrintTerm (help_strings[i]); Sys_PrintTerm ("\n"); i++; } Sys_PrintTerm ("\n"); } /* =============================================================================== MAIN =============================================================================== */ static quakeparms_t parms; static char cwd[MAX_OSPATH]; #if DO_USERDIRS static char userdir[MAX_OSPATH]; #endif int main (int argc, char **argv) { int i; double time, oldtime, newtime; PrintVersion(); if (argc > 1) { for (i = 1; i < argc; i++) { if ( !(strcmp(argv[i], "-v")) || !(strcmp(argv[i], "-version" )) || !(strcmp(argv[i], "--version")) ) { exit(0); } else if ( !(strcmp(argv[i], "-h")) || !(strcmp(argv[i], "-help" )) || !(strcmp(argv[i], "-?")) || !(strcmp(argv[i], "--help")) ) { PrintHelp(argv[0]); exit (0); } } } /* initialize the host params */ memset (&parms, 0, sizeof(parms)); parms.basedir = cwd; parms.userdir = cwd; parms.argc = argc; parms.argv = argv; parms.errstate = 0; host_parms = &parms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); #if DO_USERDIRS memset (userdir, 0, sizeof(userdir)); if (Sys_GetUserdir(userdir, sizeof(userdir)) != 0) Sys_Error ("Couldn't determine userspace directory"); Sys_mkdir(userdir, true); parms.userdir = userdir; #endif LOG_Init (&parms); Sys_Printf("basedir is: %s\n", parms.basedir); Sys_Printf("userdir is: %s\n", parms.userdir); COM_ValidateByteorder (); isDedicated = (COM_CheckParm ("-dedicated") != 0); Sys_CheckSDL (); parms.memsize = (isDedicated)? MIN_MEM_ALLOC : STD_MEM_ALLOC; i = COM_CheckParm ("-heapsize"); if (i && i < com_argc-1) parms.memsize = atoi (com_argv[i+1]) * 1024; parms.membase = malloc (parms.memsize); if (!parms.membase) Sys_Error ("Insufficient memory."); Sys_Init (); atexit (Sys_AtExit); Host_Init(); oldtime = Sys_DoubleTime (); /* main window message loop */ while (1) { if (isDedicated) { newtime = Sys_DoubleTime (); time = newtime - oldtime; while (time < sys_ticrate.value ) { usleep (1000); newtime = Sys_DoubleTime (); time = newtime - oldtime; } Host_Frame (time); oldtime = newtime; } else { #if defined(SDLQUAKE) /* If we have no input focus at all, sleep a bit */ if (!VID_HasMouseOrInputFocus() || cl.paused) { usleep (16000); } /* If we're minimised, sleep a bit more */ if (VID_IsMinimized()) { scr_skipupdate = 1; usleep (32000); } else { scr_skipupdate = 0; } #endif newtime = Sys_DoubleTime (); time = newtime - oldtime; Host_Frame (time); if (time < sys_throttle.value) usleep (1000); oldtime = newtime; } } return 0; } engine/hexen2/sys_win.c000066400000000000000000000445651444734033100153740ustar00rootroot00000000000000/* sys_win.c -- Windows system interface code * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "winquake.h" #include #include "resource.h" #include "debuglog.h" #define MIN_MEM_ALLOC 0x1000000 #define STD_MEM_ALLOC 0x2000000 #define CONSOLE_ERROR_TIMEOUT 60.0 /* # of seconds to wait on Sys_Error running dedicated before exiting */ #define PAUSE_SLEEP 50 /* sleep time on pause or minimization */ #define NOT_FOCUS_SLEEP 20 /* sleep time when not focus */ cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; cvar_t sys_throttle = {"sys_throttle", "0.02", CVAR_ARCHIVE}; qboolean ActiveApp, Minimized; qboolean Win95, Win95old, WinNT, WinVista; qboolean isDedicated; #define TIME_WRAP_VALUE (~(DWORD)0) static DWORD starttime; static qboolean sc_return_on_enter = false; static HANDLE hinput, houtput; static HANDLE tevent; static volatile int sys_checksum; /* ================ Sys_PageIn ================ */ static void Sys_PageIn (void *ptr, int size) { byte *x; int m, n; // touch all the memory to make sure it's there. The 16-page skip is to // keep Win 95 from thinking we're trying to page ourselves in (we are // doing that, of course, but there's no reason we shouldn't) x = (byte *)ptr; for (n = 0; n < 4; n++) { for (m = 0; m < (size - 16 * 0x1000); m += 4) { sys_checksum += *(int *)&x[m]; sys_checksum += *(int *)&x[m + 16 * 0x1000]; } } } /* =============================================================================== FILE IO =============================================================================== */ int Sys_mkdir (const char *path, qboolean crash) { if (CreateDirectory(path, NULL) != 0) return 0; if (GetLastError() == ERROR_ALREADY_EXISTS) return 0; if (crash) Sys_Error("Unable to create directory %s", path); return -1; } int Sys_rmdir (const char *path) { if (RemoveDirectory(path) != 0) return 0; return -1; } int Sys_unlink (const char *path) { if (DeleteFile(path) != 0) return 0; return -1; } int Sys_rename (const char *oldp, const char *newp) { if (MoveFile(oldp, newp) != 0) return 0; return -1; } long Sys_filesize (const char *path) { HANDLE fh; WIN32_FIND_DATA data; long size; fh = FindFirstFile(path, &data); if (fh == INVALID_HANDLE_VALUE) return -1; FindClose(fh); if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) return -1; // we're not dealing with gigabytes of files. // size should normally smaller than INT_MAX. // size = (data.nFileSizeHigh * (MAXDWORD + 1)) + data.nFileSizeLow; size = (long) data.nFileSizeLow; return size; } #ifndef INVALID_FILE_ATTRIBUTES #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #endif int Sys_FileType (const char *path) { DWORD result = GetFileAttributes(path); if (result == INVALID_FILE_ATTRIBUTES) return FS_ENT_NONE; if (result & FILE_ATTRIBUTE_DIRECTORY) return FS_ENT_DIRECTORY; return FS_ENT_FILE; } int Sys_CopyFile (const char *frompath, const char *topath) { /* 3rd param: whether to fail if 'topath' already exists */ if (CopyFile(frompath, topath, FALSE) != 0) return 0; return -1; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only, not a dirent struct. this is what we presently need in this engine. ================================================= */ static HANDLE findhandle = INVALID_HANDLE_VALUE; static WIN32_FIND_DATA finddata; static char findstr[MAX_OSPATH]; const char *Sys_FindFirstFile (const char *path, const char *pattern) { if (findhandle != INVALID_HANDLE_VALUE) Sys_Error ("Sys_FindFirst without FindClose"); q_snprintf (findstr, sizeof(findstr), "%s/%s", path, pattern); findhandle = FindFirstFile(findstr, &finddata); if (findhandle == INVALID_HANDLE_VALUE) return NULL; if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) return Sys_FindNextFile(); return finddata.cFileName; } const char *Sys_FindNextFile (void) { if (findhandle == INVALID_HANDLE_VALUE) return NULL; while (FindNextFile(findhandle, &finddata) != 0) { if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; return finddata.cFileName; } return NULL; } void Sys_FindClose (void) { if (findhandle != INVALID_HANDLE_VALUE) { FindClose(findhandle); findhandle = INVALID_HANDLE_VALUE; } } /* =============================================================================== SYSTEM IO =============================================================================== */ /* ================ Sys_MakeCodeWriteable ================ */ #if id386 && !defined(GLQUAKE) void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) { DWORD flOldProtect; //@@@ copy on write or just read-write? if (!VirtualProtect((LPVOID)startaddr, length, PAGE_EXECUTE_READWRITE, &flOldProtect)) Sys_Error("Protection change failed\n"); } #endif /* id386, !GLQUAKE */ /* ================ Sys_SetDPIAware ================ */ typedef enum { dpi_unaware = 0, dpi_system_aware = 1, dpi_monitor_aware = 2 } dpi_awareness; typedef BOOL (WINAPI *SetProcessDPIAwareFunc)(); typedef HRESULT (WINAPI *SetProcessDPIAwarenessFunc)(dpi_awareness value); static void Sys_SetDPIAware (void) { HMODULE hUser32, hShcore; SetProcessDPIAwarenessFunc setDPIAwareness; SetProcessDPIAwareFunc setDPIAware; /* We do not handle the OS scaling our window. Call * SetProcessDpiAwareness() or SetProcessDPIAware() * to opt out of scaling. */ hShcore = LoadLibraryA ("Shcore.dll"); hUser32 = LoadLibraryA ("user32.dll"); setDPIAwareness = (SetProcessDPIAwarenessFunc) (hShcore ? GetProcAddress (hShcore, "SetProcessDpiAwareness") : NULL); setDPIAware = (SetProcessDPIAwareFunc) (hUser32 ? GetProcAddress (hUser32, "SetProcessDPIAware") : NULL); if (setDPIAwareness) /* Windows 8.1+ */ setDPIAwareness (dpi_monitor_aware); else if (setDPIAware) /* Vista, Win7 or 8.0 */ setDPIAware (); if (hShcore) FreeLibrary (hShcore); if (hUser32) FreeLibrary (hUser32); } /* ================ Sys_Init ================ */ static void Sys_Init (void) { OSVERSIONINFO vinfo; vinfo.dwOSVersionInfoSize = sizeof(vinfo); if (!GetVersionEx (&vinfo)) Sys_Error ("Couldn't get OS info"); if ((vinfo.dwMajorVersion < 4) || (vinfo.dwPlatformId == VER_PLATFORM_WIN32s)) { Sys_Error ("%s requires at least Win95 or NT 4.0", ENGINE_NAME); } if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT) { WinNT = true; if (vinfo.dwMajorVersion >= 6) WinVista = true; } else { WinNT = false; /* Win9x or WinME */ if ((vinfo.dwMajorVersion == 4) && (vinfo.dwMinorVersion == 0)) { Win95 = true; /* Win95-gold or Win95A can't switch bpp automatically */ if (vinfo.szCSDVersion[1] != 'C' && vinfo.szCSDVersion[1] != 'B') Win95old = true; } } if (!isDedicated) Sys_SetDPIAware (); timeBeginPeriod (1); /* 1 ms timer precision */ starttime = timeGetTime (); /* do we really need these with opengl ?? */ MaskExceptions (); Sys_SetFPCW (); } #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const char text3[] = "\n"; const char text4[] = "\nPress Enter to exit\n"; DWORD dummy; double err_begin; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof (text), error, argptr); va_end (argptr); if (con_debuglog) { LOG_Print (ERROR_PREFIX); LOG_Print (text); LOG_Print ("\n\n"); } Host_Shutdown (); if (isDedicated) { WriteFile (houtput, text2, strlen(text2), &dummy, NULL); WriteFile (houtput, text, strlen(text), &dummy, NULL); WriteFile (houtput, text3, strlen(text3), &dummy, NULL); WriteFile (houtput, text4, strlen(text4), &dummy, NULL); err_begin = Sys_DoubleTime (); sc_return_on_enter = true; /* so Enter will get us out of here */ while (!Sys_ConsoleInput () && ((Sys_DoubleTime () - err_begin) < CONSOLE_ERROR_TIMEOUT)) { } } else { MessageBox(NULL, text, ENGINE_NAME " Error", MB_OK | MB_SETFOREGROUND | MB_ICONSTOP); } exit (1); } void Sys_PrintTerm (const char *msgtxt) { DWORD dummy; if (isDedicated) { if (sys_nostdout.integer) return; WriteFile(houtput, msgtxt, strlen(msgtxt), &dummy, NULL); } } void Sys_Quit (void) { Host_Shutdown(); if (tevent) CloseHandle (tevent); if (isDedicated) FreeConsole (); exit (0); } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { DWORD now, passed; now = timeGetTime(); if (now < starttime) /* wrapped? */ { passed = TIME_WRAP_VALUE - starttime; passed += now; } else { passed = now - starttime; } return (passed == 0) ? 0.0 : (passed / 1000.0); } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; SYSTEMTIME st; int val; if (!buf) buf = strbuf; GetLocalTime(&st); val = st.wMonth; buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = st.wDay; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = st.wYear / 100; buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = st.wYear % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = st.wHour; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = st.wMinute; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = st.wSecond; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } const char *Sys_ConsoleInput (void) { static char con_text[256]; static int textlen; INPUT_RECORD recs[1024]; int ch; DWORD dummy, numread, numevents; for ( ;; ) { if (GetNumberOfConsoleInputEvents(hinput, &numevents) == 0) Sys_Error ("Error getting # of console events"); if (! numevents) break; if (ReadConsoleInput(hinput, recs, 1, &numread) == 0) Sys_Error ("Error reading console input"); if (numread != 1) Sys_Error ("Couldn't read console input"); if (recs[0].EventType == KEY_EVENT) { if (recs[0].Event.KeyEvent.bKeyDown == FALSE) { ch = recs[0].Event.KeyEvent.uChar.AsciiChar; switch (ch) { case '\r': WriteFile(houtput, "\r\n", 2, &dummy, NULL); if (textlen != 0) { con_text[textlen] = 0; textlen = 0; return con_text; } else if (sc_return_on_enter) { /* special case to allow exiting from the error handler on Enter */ con_text[0] = '\r'; textlen = 0; return con_text; } break; case '\b': WriteFile(houtput, "\b \b", 3, &dummy, NULL); if (textlen != 0) textlen--; break; default: if (ch >= ' ') { WriteFile(houtput, &ch, 1, &dummy, NULL); con_text[textlen] = ch; textlen = (textlen + 1) & 0xff; } break; } } } } return NULL; } void Sys_Sleep (unsigned long msecs) { Sleep (msecs); } void Sys_SendKeyEvents (void) { MSG msg; while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) { // we always update if there are any event, even if we're paused scr_skipupdate = 0; if (!GetMessage (&msg, NULL, 0, 0)) Sys_Quit (); TranslateMessage (&msg); DispatchMessage (&msg); } } #define MAX_CLIPBOARDTXT MAXCMDLINE /* 256 */ char *Sys_GetClipboardData (void) { char *data = NULL; char *cliptext; if (OpenClipboard(NULL) != 0) { HANDLE hClipboardData; if ((hClipboardData = GetClipboardData(CF_TEXT)) != NULL) { cliptext = (char *) GlobalLock(hClipboardData); if (cliptext != NULL) { size_t size = GlobalSize(hClipboardData) + 1; /* this is intended for simple small text copies * such as an ip address, etc: do chop the size * here, otherwise we may experience Z_Malloc() * failures and all other not-oh-so-fun stuff. */ size = q_min(MAX_CLIPBOARDTXT, size); data = (char *) Z_Malloc(size, Z_MAINZONE); q_strlcpy (data, cliptext, size); GlobalUnlock (hClipboardData); } } CloseClipboard (); } return data; } static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { char *tmp; size_t rc; rc = GetCurrentDirectory(dstsize, dst); if (rc == 0 || rc > dstsize) return -1; tmp = dst; while (*tmp != 0) tmp++; while (*tmp == 0 && tmp != dst) { --tmp; if (tmp != dst && (*tmp == '/' || *tmp == '\\')) *tmp = 0; } return 0; } /* ============================================================================== WINDOWS CRAP ============================================================================== */ /* ================== SleepUntilInput ================== */ static void SleepUntilInput (unsigned long msecs) { MsgWaitForMultipleObjects(1, &tevent, FALSE, msecs, QS_ALLINPUT); } static void PrintVersion (void) { Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); Sys_Printf ("running on %s engine %4.2f (%s)\n", ENGINE_NAME, ENGINE_VERSION, PLATFORM_STRING); Sys_Printf ("More info / sending bug reports: http://uhexen2.sourceforge.net\n"); } /* ================== WinMain ================== */ HINSTANCE global_hInstance; int global_nCmdShow; #if !defined(NO_SPLASHES) HWND hwnd_dialog; #endif /* NO_SPLASHES */ static char *argv[MAX_NUM_ARGVS]; static char cwd[1024]; static char prog[MAX_PATH]; static quakeparms_t parms; static void Sys_CreateInitSplash (HINSTANCE hInstance) { #if !defined(NO_SPLASHES) RECT rect; hwnd_dialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, NULL); if (!hwnd_dialog) return; if (GetWindowRect (hwnd_dialog, &rect)) { if (rect.left > (rect.top * 2)) { SetWindowPos (hwnd_dialog, 0, (rect.left / 2) - ((rect.right - rect.left) / 2), rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); } } ShowWindow (hwnd_dialog, SW_SHOWDEFAULT); UpdateWindow (hwnd_dialog); SetForegroundWindow (hwnd_dialog); #endif /* NO_SPLASHES */ } int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int i; double time, oldtime, newtime; MEMORYSTATUS lpBuffer; /* previous instances do not exist in Win32 */ if (hPrevInstance) return 0; global_hInstance = hInstance; global_nCmdShow = nCmdShow; lpBuffer.dwLength = sizeof(MEMORYSTATUS); GlobalMemoryStatus (&lpBuffer); /* Limit to 2 GB to work around signed int */ if (lpBuffer.dwAvailPhys > 0x7FFFFFFF) lpBuffer.dwAvailPhys = 0x7FFFFFFF; if (lpBuffer.dwTotalPhys > 0x7FFFFFFF) lpBuffer.dwTotalPhys = 0x7FFFFFFF; memset (&parms, 0, sizeof(parms)); parms.basedir = cwd; parms.userdir = cwd; /* no userdir on win32 */ parms.errstate = 0; host_parms = &parms; /* initialize the host params */ memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(NULL, cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); parms.argc = 1; argv[0] = prog; if (GetModuleFileName(NULL, prog, sizeof(prog)) == 0) prog[0] = '\0'; else prog[MAX_PATH - 1] = '\0'; while (*lpCmdLine && (parms.argc < MAX_NUM_ARGVS)) { while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126))) lpCmdLine++; if (*lpCmdLine) { argv[parms.argc] = lpCmdLine; parms.argc++; while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126))) lpCmdLine++; if (*lpCmdLine) { *lpCmdLine = 0; lpCmdLine++; } } } parms.argv = argv; isDedicated = (COM_CheckParm ("-dedicated") != 0); if (isDedicated) { if (!AllocConsole ()) { isDedicated = false; /* so that we have a graphical error dialog */ Sys_Error ("Couldn't create dedicated server console"); } hinput = GetStdHandle (STD_INPUT_HANDLE); houtput = GetStdHandle (STD_OUTPUT_HANDLE); if (hinput == INVALID_HANDLE_VALUE || houtput == INVALID_HANDLE_VALUE || hinput == NULL || houtput == NULL) { isDedicated = false; /* so that we have a graphical error dialog */ Sys_Error ("Couldn't retrieve server console handles"); } PrintVersion(); } LOG_Init (&parms); Sys_Printf("basedir is: %s\n", parms.basedir); Sys_Printf("userdir is: %s\n", parms.userdir); COM_ValidateByteorder (); if (!isDedicated) Sys_CreateInitSplash (global_hInstance); // take the greater of all the available memory or half the total memory, // but at least 16 Mb and no more than 32 Mb, unless explicitly requested. parms.memsize = lpBuffer.dwAvailPhys; if (parms.memsize < MIN_MEM_ALLOC) parms.memsize = MIN_MEM_ALLOC; if (parms.memsize < (int) (lpBuffer.dwTotalPhys >> 1)) parms.memsize = (int) (lpBuffer.dwTotalPhys >> 1); if (parms.memsize > STD_MEM_ALLOC) parms.memsize = STD_MEM_ALLOC; if (isDedicated) parms.memsize = MIN_MEM_ALLOC; i = COM_CheckParm ("-heapsize"); if (i && i < com_argc-1) parms.memsize = atoi (com_argv[i+1]) * 1024; parms.membase = malloc (parms.memsize); if (!parms.membase) Sys_Error ("Insufficient memory."); if (COM_CheckParm("-nopagein") == 0) { Sys_PageIn (parms.membase, parms.memsize); } tevent = CreateEvent(NULL, FALSE, FALSE, NULL); if (!tevent) Sys_Error ("Couldn't create event"); Sys_Init (); Host_Init(); oldtime = Sys_DoubleTime (); /* main window message loop */ while (1) { if (isDedicated) { newtime = Sys_DoubleTime (); time = newtime - oldtime; while (time < sys_ticrate.value ) { Sleep (1); newtime = Sys_DoubleTime (); time = newtime - oldtime; } Host_Frame (time); oldtime = newtime; } else { /* yield the CPU for a little while when paused, minimized or not focused */ if ((cl.paused && !ActiveApp) || Minimized || block_drawing) { SleepUntilInput (PAUSE_SLEEP); scr_skipupdate = 1; /* no point in bothering to draw */ } else if (!ActiveApp) { SleepUntilInput (NOT_FOCUS_SLEEP); scr_skipupdate = 1; /* no point in bothering to draw */ } newtime = Sys_DoubleTime (); time = newtime - oldtime; Host_Frame (time); if (time < sys_throttle.value) Sleep (1); oldtime = newtime; } } return 0; } engine/hexen2/view.c000066400000000000000000000623751444734033100146520ustar00rootroot00000000000000/* * view.c -- player eye positioning * * The view is allowed to move slightly from it's true position * for bobbing, but if it exceeds 8 pixels linear distance * (spherical, not box), the list of entities sent from the server * may not include everything in the pvs, especially when crossing * a water boudnary. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" static float v_dmg_time, v_dmg_roll, v_dmg_pitch; static cvar_t scr_ofsx = {"scr_ofsx", "0", CVAR_NONE}; static cvar_t scr_ofsy = {"scr_ofsy", "0", CVAR_NONE}; static cvar_t scr_ofsz = {"scr_ofsz", "0", CVAR_NONE}; static cvar_t cl_bob = {"cl_bob", "0.02", CVAR_NONE}; static cvar_t cl_bobcycle = {"cl_bobcycle", "0.6", CVAR_NONE}; static cvar_t cl_bobup = {"cl_bobup", "0.5", CVAR_NONE}; static cvar_t v_kicktime = {"v_kicktime", "0.5", CVAR_NONE}; static cvar_t v_kickroll = {"v_kickroll", "0.6", CVAR_NONE}; static cvar_t v_kickpitch = {"v_kickpitch", "0.6", CVAR_NONE}; static cvar_t v_iyaw_cycle = {"v_iyaw_cycle", "2", CVAR_NONE}; static cvar_t v_iroll_cycle = {"v_iroll_cycle", "0.5", CVAR_NONE}; static cvar_t v_ipitch_cycle = {"v_ipitch_cycle", "1", CVAR_NONE}; static cvar_t v_iyaw_level = {"v_iyaw_level", "0.3", CVAR_NONE}; static cvar_t v_iroll_level = {"v_iroll_level", "0.1", CVAR_NONE}; static cvar_t v_ipitch_level = {"v_ipitch_level", "0.3", CVAR_NONE}; static cvar_t v_idlescale = {"v_idlescale", "0", CVAR_NONE}; cvar_t cl_rollspeed = {"cl_rollspeed", "200", CVAR_NONE}; cvar_t cl_rollangle = {"cl_rollangle", "2.0", CVAR_NONE}; cvar_t crosshair = {"crosshair", "0", CVAR_ARCHIVE}; cvar_t cl_crossx = {"cl_crossx", "0", CVAR_ARCHIVE}; cvar_t cl_crossy = {"cl_crossy", "0", CVAR_ARCHIVE}; cvar_t crosshaircolor = {"crosshaircolor", "75", CVAR_ARCHIVE}; // 79 seemed too bright //============================================================================= /* =============== V_CalcRoll Used by view and sv_user =============== */ float V_CalcRoll (vec3_t angles, vec3_t velocity) { vec3_t forward, right, up; float sign; float side; float value; AngleVectors (angles, forward, right, up); side = DotProduct (velocity, right); sign = side < 0 ? -1 : 1; side = fabs(side); value = cl_rollangle.value; // if (cl.inwater) // value *= 6; if (side < cl_rollspeed.value) side = side * value / cl_rollspeed.value; else side = value; return side*sign; } /* =============== V_CalcBob =============== */ static float V_CalcBob (void) { float bob; float cycle; cycle = cl.time - (int)(cl.time/cl_bobcycle.value)*cl_bobcycle.value; cycle /= cl_bobcycle.value; if (cycle < cl_bobup.value) cycle = M_PI * cycle / cl_bobup.value; else cycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value); // bob is proportional to velocity in the xy plane // (don't count Z, or jumping messes it up) bob = Q_sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value; bob = bob*0.3 + bob*0.7*q_sinrad(cycle); if (bob > 4) bob = 4; else if (bob < -7) bob = -7; return bob; } //============================================================================= static cvar_t v_centermove = {"v_centermove", "0.15", CVAR_NONE}; static cvar_t v_centerspeed = {"v_centerspeed", "500", CVAR_NONE}; static cvar_t v_centerrollspeed = {"v_centerrollspeed", "125", CVAR_NONE}; void V_StartPitchDrift (void) { #if 1 if (cl.laststop == cl.time) return; // something else is keeping it from drifting #endif if (cl.nodrift || !cl.pitchvel) { cl.pitchvel = v_centerspeed.value; cl.nodrift = false; cl.driftmove = 0; } } void V_StopPitchDrift (void) { cl.laststop = cl.time; cl.nodrift = true; cl.pitchvel = 0; } /* =============== V_DriftPitch Moves the client pitch angle towards cl.idealpitch sent by the server. If the user is adjusting pitch manually, either with lookup/lookdown, mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped. Drifting is enabled when the center view key is hit, mlook is released and lookspring is non 0, or when =============== */ static void V_DriftPitch (void) { float delta, move; if (!cl.onground || cl.v.movetype == MOVETYPE_FLY || cl.v.movetype == MOVETYPE_NOCLIP || cls.demoplayback) { cl.driftmove = 0; cl.pitchvel = 0; return; } // don't count small mouse motion if (cl.nodrift) { if ( fabs(cl.cmd.forwardmove) < (cl.v.hasted*cl_forwardspeed.value)-10) cl.driftmove = 0; else cl.driftmove += host_frametime; if ( cl.driftmove > v_centermove.value) { if (lookspring.integer) V_StartPitchDrift (); } return; } delta = cl.idealpitch - cl.viewangles[PITCH]; if (!delta) { cl.pitchvel = 0; return; } move = host_frametime * cl.pitchvel; cl.pitchvel += host_frametime * v_centerspeed.value; if (delta > 0) { if (move > delta) { cl.pitchvel = 0; move = delta; } cl.viewangles[PITCH] += move; } else if (delta < 0) { if (move > -delta) { cl.pitchvel = 0; move = -delta; } cl.viewangles[PITCH] -= move; } } /* =============== V_DriftRoll Moves the client pitch angle towards cl.idealroll sent by the server. If the user is adjusting pitch manually, either with lookup/lookdown, mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped. =============== */ static void V_DriftRoll (void) { float delta, move; if (cl.v.movetype == MOVETYPE_NOCLIP || cls.demoplayback) { return; } delta = cl.idealroll - cl.viewangles[ROLL]; if (!delta) { cl.rollvel = 0; return; } move = host_frametime * cl.rollvel; cl.rollvel += host_frametime * v_centerrollspeed.value; if (delta > 0) { if (move > delta) { cl.rollvel = 0; move = delta; } cl.viewangles[ROLL] += move; } else if (delta < 0) { if (move > -delta) { cl.rollvel = 0; move = -delta; } cl.viewangles[ROLL] -= move; } } /* ============================================================================== PALETTE FLASHES ============================================================================== */ cshift_t cshift_empty = { {130,80,50}, 0 }; cshift_t cshift_water = { {130,80,50}, 128 }; cshift_t cshift_slime = { {0,25,5}, 150 }; cshift_t cshift_lava = { {255,80,0}, 150 }; cvar_t v_gamma = {"gamma", "1", CVAR_ARCHIVE}; byte gammatable[256]; // palette is sent through this static void BuildGammaTable (float g) { int i, inf; if (g == 1.0) { for (i = 0; i < 256; i++) gammatable[i] = i; return; } for (i = 0; i < 256; i++) { inf = 255 * pow((i + 0.5) / 255.5, g) + 0.5; if (inf < 0) inf = 0; else if (inf > 255) inf = 255; gammatable[i] = inf; } } /* =============== V_ParseDamage =============== */ void V_ParseDamage (void) { int armor, blood; vec3_t from; int i; vec3_t forward, right, up; entity_t *ent; float side; float count; armor = MSG_ReadByte (); blood = MSG_ReadByte (); for (i = 0; i < 3; i++) from[i] = MSG_ReadCoord (); count = blood*0.5 + armor*0.5; if (count < 10) count = 10; cl.faceanimtime = cl.time + 0.2; // put sbar face into pain frame cl.cshifts[CSHIFT_DAMAGE].percent += 3 * count; if (cl.cshifts[CSHIFT_DAMAGE].percent < 0) cl.cshifts[CSHIFT_DAMAGE].percent = 0; else if (cl.cshifts[CSHIFT_DAMAGE].percent > 150) cl.cshifts[CSHIFT_DAMAGE].percent = 150; if (armor > blood) { cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200; cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100; cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100; } else if (armor) { cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220; cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50; cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50; } else { cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255; cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0; cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0; } // // calculate view angle kicks // ent = &cl_entities[cl.viewentity]; VectorSubtract (from, ent->origin, from); VectorNormalizeFast (from); AngleVectors (ent->angles, forward, right, up); side = DotProduct (from, right); v_dmg_roll = count*side*v_kickroll.value; side = DotProduct (from, forward); v_dmg_pitch = count*side*v_kickpitch.value; v_dmg_time = v_kicktime.value; } /* ================== V_cshift_f ================== */ static void V_cshift_f (void) { cshift_empty.destcolor[0] = atoi(Cmd_Argv(1)); cshift_empty.destcolor[1] = atoi(Cmd_Argv(2)); cshift_empty.destcolor[2] = atoi(Cmd_Argv(3)); cshift_empty.percent = atoi(Cmd_Argv(4)); } /* ================== V_BonusFlash_f When you run over an item, the server sends this command ================== */ static void V_BonusFlash_f (void) { cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215; cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186; cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69; cl.cshifts[CSHIFT_BONUS].percent = 50; } static void V_DarkFlash_f (void) { cl.cshifts[CSHIFT_BONUS].destcolor[0] = 0; cl.cshifts[CSHIFT_BONUS].destcolor[1] = 0; cl.cshifts[CSHIFT_BONUS].destcolor[2] = 0; cl.cshifts[CSHIFT_BONUS].percent = 255; } static void V_WhiteFlash_f (void) { cl.cshifts[CSHIFT_BONUS].destcolor[0] = 255; cl.cshifts[CSHIFT_BONUS].destcolor[1] = 255; cl.cshifts[CSHIFT_BONUS].destcolor[2] = 255; cl.cshifts[CSHIFT_BONUS].percent = 255; } /* ============= V_SetContentsColor Underwater, lava, etc each has a color shift ============= */ void V_SetContentsColor (int contents) { switch (contents) { case CONTENTS_EMPTY: case CONTENTS_SOLID: cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; break; case CONTENTS_LAVA: cl.cshifts[CSHIFT_CONTENTS] = cshift_lava; break; case CONTENTS_SLIME: cl.cshifts[CSHIFT_CONTENTS] = cshift_slime; break; default: cl.cshifts[CSHIFT_CONTENTS] = cshift_water; } } /* ============= V_CalcPowerupCshift ============= */ static void V_CalcPowerupCshift(void) { if ((int)cl.v.artifact_active&ARTFLAG_DIVINE_INTERVENTION) { cl.cshifts[CSHIFT_BONUS].destcolor[0] = 255; cl.cshifts[CSHIFT_BONUS].destcolor[1] = 255; cl.cshifts[CSHIFT_BONUS].destcolor[2] = 255; cl.cshifts[CSHIFT_BONUS].percent = 256; } if ((int)cl.v.artifact_active&ARTFLAG_FROZEN) { cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 20; cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 70; cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255; cl.cshifts[CSHIFT_POWERUP].percent = 65; } else if ((int)cl.v.artifact_active&ARTFLAG_STONED) { cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 205; cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 205; cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 205; //cl.cshifts[CSHIFT_POWERUP].percent = 80; cl.cshifts[CSHIFT_POWERUP].percent = 11000; } else if ((int)cl.v.artifact_active&ART_INVISIBILITY) { cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100; cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100; cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100; cl.cshifts[CSHIFT_POWERUP].percent = 100; } else if ((int)cl.v.artifact_active&ART_INVINCIBILITY) { cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255; cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; cl.cshifts[CSHIFT_POWERUP].percent = 30; } else { cl.cshifts[CSHIFT_POWERUP].percent = 0; } } /* ============= V_CalcBlend ============= */ #ifdef GLQUAKE float v_blend[4]; // rgba 0.0 - 1.0 void V_CalcBlend (void) { float r, g, b, a, a2; int j; r = 0; g = 0; b = 0; a = 0; for (j = 0; j < NUM_CSHIFTS; j++) { if (cl.cshifts[j].percent > 10000) { // Set percent for grayscale cl.cshifts[j].percent = 80; } a2 = cl.cshifts[j].percent / 255.0; if (!a2) continue; a = a + a2*(1-a); // Con_Printf ("j:%i a:%f\n", j, a); a2 = a2/a; r = r*(1-a2) + cl.cshifts[j].destcolor[0]*a2; g = g*(1-a2) + cl.cshifts[j].destcolor[1]*a2; b = b*(1-a2) + cl.cshifts[j].destcolor[2]*a2; } v_blend[0] = r / 255.0; v_blend[1] = g / 255.0; v_blend[2] = b / 255.0; v_blend[3] = a; if (v_blend[3] > 1) v_blend[3] = 1; else if (v_blend[3] < 0) v_blend[3] = 0; } #endif /* GLQUAKE */ /* ============= V_UpdatePalette ============= */ #ifdef GLQUAKE unsigned short ramps[3][256]; void V_UpdatePalette (void) { int i, j; unsigned int t; if (cls.state == ca_active) V_CalcPowerupCshift (); for (i = 0; i < NUM_CSHIFTS; i++) { if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent) cl.prev_cshifts[i].percent = cl.cshifts[i].percent; for (j = 0; j < 3; j++) { if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j]) cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j]; } } // drop the damage value cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime * 150; if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0) cl.cshifts[CSHIFT_DAMAGE].percent = 0; // drop the bonus value cl.cshifts[CSHIFT_BONUS].percent -= host_frametime * 100; if (cl.cshifts[CSHIFT_BONUS].percent <= 0) cl.cshifts[CSHIFT_BONUS].percent = 0; if (v_gamma.flags & CVAR_CHANGED) { if (v_gamma.value > 1.0 || v_gamma.value < (1.0 / GAMMA_MAX)) Cvar_SetQuick (&v_gamma, "1"); v_gamma.flags &= ~CVAR_CHANGED; vid.recalc_refdef = 1; BuildGammaTable (v_gamma.value); for (i = 0; i < 256; i++) { t = gammatable[i] << 8; ramps[0][i] = t; ramps[1][i] = t; ramps[2][i] = t; } VID_ShiftPalette(NULL); } } #else /* !GLQUAKE */ void V_UpdatePalette (void) { int i, j; qboolean is_new; byte *basepal, *newpal; byte pal[768]; int r, g, b; if (cls.state == ca_active) V_CalcPowerupCshift (); is_new = false; for (i = 0; i < NUM_CSHIFTS; i++) { if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent) { is_new = true; cl.prev_cshifts[i].percent = cl.cshifts[i].percent; } for (j = 0; j < 3; j++) { if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j]) { is_new = true; cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j]; } } } // drop the damage value cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime * 150; if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0) cl.cshifts[CSHIFT_DAMAGE].percent = 0; // drop the bonus value cl.cshifts[CSHIFT_BONUS].percent -= host_frametime * 100; if (cl.cshifts[CSHIFT_BONUS].percent <= 0) cl.cshifts[CSHIFT_BONUS].percent = 0; if (v_gamma.flags & CVAR_CHANGED) { if (v_gamma.value > 1.0 || v_gamma.value < (1.0 / GAMMA_MAX)) Cvar_SetQuick (&v_gamma, "1"); v_gamma.flags &= ~CVAR_CHANGED; BuildGammaTable (v_gamma.value); vid.recalc_refdef = 1; // force a surface cache flush is_new = true; } if (!is_new) return; basepal = host_basepal; newpal = pal; for (i = 0; i < 256; i++) { r = basepal[0]; g = basepal[1]; b = basepal[2]; basepal += 3; for (j = 0; j < NUM_CSHIFTS; j++) { if (cl.cshifts[j].percent > 10000) { // Create a grayscale r = g = b = (r*76 + g*141 + b*38) / 256; } else { r += (cl.cshifts[j].percent * (cl.cshifts[j].destcolor[0] - r)) >> 8; g += (cl.cshifts[j].percent * (cl.cshifts[j].destcolor[1] - g)) >> 8; b += (cl.cshifts[j].percent * (cl.cshifts[j].destcolor[2] - b)) >> 8; } } newpal[0] = gammatable[r]; newpal[1] = gammatable[g]; newpal[2] = gammatable[b]; newpal += 3; } VID_ShiftPalette (pal); } #endif /* !GLQUAKE */ /* ============================================================================== VIEW RENDERING ============================================================================== */ static float angledelta (float a) { a = anglemod(a); if (a > 180) a -= 360; return a; } /* ================== CalcGunAngle ================== */ static void CalcGunAngle (void) { float yaw, pitch, move; static float oldyaw = 0; static float oldpitch = 0; yaw = r_refdef.viewangles[YAW]; pitch = -r_refdef.viewangles[PITCH]; yaw = angledelta(yaw - r_refdef.viewangles[YAW]) * 0.4; if (yaw > 10) yaw = 10; else if (yaw < -10) yaw = -10; pitch = angledelta(-pitch - r_refdef.viewangles[PITCH]) * 0.4; if (pitch > 10) pitch = 10; else if (pitch < -10) pitch = -10; move = host_frametime * 20; if (yaw > oldyaw) { if (oldyaw + move < yaw) yaw = oldyaw + move; } else { if (oldyaw - move > yaw) yaw = oldyaw - move; } if (pitch > oldpitch) { if (oldpitch + move < pitch) pitch = oldpitch + move; } else { if (oldpitch - move > pitch) pitch = oldpitch - move; } oldyaw = yaw; oldpitch = pitch; cl.viewent.angles[YAW] = r_refdef.viewangles[YAW] + yaw; cl.viewent.angles[PITCH] = - (r_refdef.viewangles[PITCH] + pitch); if (v_idlescale.value == 0) return; cl.viewent.angles[ROLL] -= v_idlescale.value * q_sinrad(cl.time*v_iroll_cycle.value) * v_iroll_level.value; cl.viewent.angles[PITCH] -= v_idlescale.value * q_sinrad(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; cl.viewent.angles[YAW] -= v_idlescale.value * q_sinrad(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value; } /* ============== V_BoundOffsets ============== */ static void V_BoundOffsets (void) { entity_t *ent; ent = &cl_entities[cl.viewentity]; // absolutely bound refresh reletive to entity clipping hull // so the view can never be inside a solid wall if (r_refdef.vieworg[0] < ent->origin[0] - 14) r_refdef.vieworg[0] = ent->origin[0] - 14; else if (r_refdef.vieworg[0] > ent->origin[0] + 14) r_refdef.vieworg[0] = ent->origin[0] + 14; if (r_refdef.vieworg[1] < ent->origin[1] - 14) r_refdef.vieworg[1] = ent->origin[1] - 14; else if (r_refdef.vieworg[1] > ent->origin[1] + 14) r_refdef.vieworg[1] = ent->origin[1] + 14; if (r_refdef.vieworg[2] < ent->origin[2] - 0) r_refdef.vieworg[2] = ent->origin[2] - 0; else if (r_refdef.vieworg[2] > ent->origin[2] + 86) r_refdef.vieworg[2] = ent->origin[2] + 86; } /* ============== V_AddIdle Idle swaying ============== */ static void V_AddIdle (void) { if (v_idlescale.value == 0) return; r_refdef.viewangles[ROLL] += v_idlescale.value * q_sinrad(cl.time*v_iroll_cycle.value) * v_iroll_level.value; r_refdef.viewangles[PITCH] += v_idlescale.value * q_sinrad(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; r_refdef.viewangles[YAW] += v_idlescale.value * q_sinrad(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value; } /* ============== V_CalcViewRoll Roll is induced by movement and damage ============== */ static void V_CalcViewRoll (void) { float side; side = V_CalcRoll (cl_entities[cl.viewentity].angles, cl.velocity); r_refdef.viewangles[ROLL] += side; if (v_dmg_time > 0) { r_refdef.viewangles[ROLL] += v_dmg_time / v_kicktime.value * v_dmg_roll; r_refdef.viewangles[PITCH] += v_dmg_time / v_kicktime.value * v_dmg_pitch; v_dmg_time -= host_frametime; } if (cl.v.health <= 0) { r_refdef.viewangles[ROLL] = 80; // dead view angle return; } } /* ================== V_CalcIntermissionRefdef ================== */ static void V_CalcIntermissionRefdef (void) { entity_t *ent, *view; float old; // ent is the player model (visible when out of body) ent = &cl_entities[cl.viewentity]; // view is the weapon model (only visible from inside body) view = &cl.viewent; VectorCopy (ent->origin, r_refdef.vieworg); VectorCopy (ent->angles, r_refdef.viewangles); view->model = NULL; r_refdef.vieworg[2] += cl.viewheight; // always idle in intermission old = v_idlescale.value; v_idlescale.value = 1; V_AddIdle (); v_idlescale.value = old; } /* ================== V_CalcRefdef ================== */ static void V_CalcRefdef (void) { entity_t *ent, *view; int i; vec3_t forward, right, up; vec3_t angles; float bob; static float oldz = 0; if (!cl.v.cameramode) { V_DriftPitch (); V_DriftRoll (); } // ent is the player model (visible when out of body) ent = &cl_entities[cl.viewentity]; // view is the weapon model (only visible from inside body) view = &cl.viewent; // transform the view offset by the model's matrix to get the // offset from model origin for the view // the model should face the view dir ent->angles[YAW] = cl.viewangles[YAW]; ent->angles[PITCH] = -cl.viewangles[PITCH]; if (cl.v.movetype != MOVETYPE_FLY) bob = V_CalcBob (); else // no bobbing when you fly bob = 1; // refresh position VectorCopy (ent->origin, r_refdef.vieworg); r_refdef.vieworg[2] += cl.viewheight + bob; // never let it sit exactly on a node line, because a water plane can // dissapear when viewed with the eye exactly on it. // the server protocol only specifies to 1/16 pixel, so add 1/32 in // each axis r_refdef.vieworg[0] += 1.0/32; r_refdef.vieworg[1] += 1.0/32; r_refdef.vieworg[2] += 1.0/32; VectorCopy (cl.viewangles, r_refdef.viewangles); V_CalcViewRoll (); V_AddIdle (); // offsets angles[PITCH] = -ent->angles[PITCH]; // because entity pitches are // actually backward angles[YAW] = ent->angles[YAW]; angles[ROLL] = ent->angles[ROLL]; AngleVectors (angles, forward, right, up); for (i = 0; i < 3; i++) r_refdef.vieworg[i] += scr_ofsx.value*forward[i] + scr_ofsy.value*right[i] + scr_ofsz.value*up[i]; V_BoundOffsets (); // set up gun position VectorCopy (cl.viewangles, view->angles); CalcGunAngle (); VectorCopy (ent->origin, view->origin); view->origin[2] += cl.viewheight; for (i = 0; i < 3; i++) { view->origin[i] += forward[i]*bob*0.4; // view->origin[i] += right[i]*bob*0.4; // view->origin[i] += up[i]*bob*0.8; } view->origin[2] += bob; // fudge position around to keep amount of weapon visible // roughly equal with different FOV #if 0 if (cl.model_precache[cl.stats[STAT_WEAPON]] && strcmp (cl.model_precache[cl.stats[STAT_WEAPON]]->name, "progs/v_shot2.mdl")) #endif if (scr_viewsize.integer >= 110) view->origin[2] += 1; else if (scr_viewsize.integer == 100) view->origin[2] += 2; else if (scr_viewsize.integer == 90) view->origin[2] += 1; else if (scr_viewsize.integer == 80) view->origin[2] += 0.5; view->model = cl.model_precache[cl.stats[STAT_WEAPON]]; view->frame = cl.stats[STAT_WEAPONFRAME]; if (!view->colorshade) { view->colormap = vid.colormap; } else { #ifdef GLQUAKE view->colormap = vid.colormap; #else view->colormap = globalcolormap; #endif } // Place weapon in powered up mode if ((ent->drawflags & MLS_MASKIN) == MLS_POWERMODE) view->drawflags = (view->drawflags & MLS_MASKOUT) | MLS_POWERMODE; else view->drawflags = (view->drawflags & MLS_MASKOUT) | 0; // set up the refresh position VectorAdd (r_refdef.viewangles, cl.punchangle, r_refdef.viewangles); // smooth out stair step ups if (cl.onground && ent->origin[2] - oldz > 0) { float steptime; steptime = cl.time - cl.oldtime; if (steptime < 0) // FIXME steptime = 0; oldz += steptime * 80; if (oldz > ent->origin[2]) oldz = ent->origin[2]; if (ent->origin[2] - oldz > 12) oldz = ent->origin[2] - 12; r_refdef.vieworg[2] += oldz - ent->origin[2]; view->origin[2] += oldz - ent->origin[2]; } else oldz = ent->origin[2]; if (chase_active.integer) Chase_Update (); } /* ================== V_RenderView The player's clipping box goes from (-16 -16 -24) to (16 16 32) from the entity origin, so any view position inside that will be valid ================== */ void V_RenderView (void) { if (con_forcedup) return; // don't allow cheats in multiplayer if (cl.maxclients > 1) { Cvar_SetQuick (&scr_ofsx, "0"); Cvar_SetQuick (&scr_ofsy, "0"); Cvar_SetQuick (&scr_ofsz, "0"); } if (cl.intermission) { // intermission / finale rendering V_CalcIntermissionRefdef (); } else { if (!cl.paused /*&& (cl.maxclients > 1 || Key_GetDest() == key_game)*/) V_CalcRefdef (); } R_PushDlights (); R_RenderView (); } //============================================================================ /* ============= V_Init ============= */ void V_Init (void) { unsigned int i; Cmd_AddCommand ("v_cshift", V_cshift_f); Cmd_AddCommand ("bf", V_BonusFlash_f); Cmd_AddCommand ("df", V_DarkFlash_f); Cmd_AddCommand ("wf", V_WhiteFlash_f); Cmd_AddCommand ("centerview", V_StartPitchDrift); Cvar_RegisterVariable (&v_centermove); Cvar_RegisterVariable (&v_centerspeed); Cvar_RegisterVariable (&v_centerrollspeed); Cvar_RegisterVariable (&v_iyaw_cycle); Cvar_RegisterVariable (&v_iroll_cycle); Cvar_RegisterVariable (&v_ipitch_cycle); Cvar_RegisterVariable (&v_iyaw_level); Cvar_RegisterVariable (&v_iroll_level); Cvar_RegisterVariable (&v_ipitch_level); Cvar_RegisterVariable (&v_idlescale); Cvar_RegisterVariable (&crosshair); Cvar_RegisterVariable (&cl_crossx); Cvar_RegisterVariable (&cl_crossy); Cvar_RegisterVariable (&crosshaircolor); Cvar_RegisterVariable (&scr_ofsx); Cvar_RegisterVariable (&scr_ofsy); Cvar_RegisterVariable (&scr_ofsz); Cvar_RegisterVariable (&cl_rollspeed); Cvar_RegisterVariable (&cl_rollangle); Cvar_RegisterVariable (&cl_bob); Cvar_RegisterVariable (&cl_bobcycle); Cvar_RegisterVariable (&cl_bobup); Cvar_RegisterVariable (&v_kicktime); Cvar_RegisterVariable (&v_kickroll); Cvar_RegisterVariable (&v_kickpitch); Cvar_RegisterVariable (&v_gamma); /* no gamma yet */ for (i = 0; i < 256; i++) { gammatable[i] = i; #ifdef GLQUAKE ramps[0][i] = i << 8; ramps[1][i] = i << 8; ramps[2][i] = i << 8; #endif } } engine/hexen2/world.c000066400000000000000000000613641444734033100150240ustar00rootroot00000000000000/* * world.c -- world query functions * * entities never clip against themselves, or their owner * line of sight checks trace->crosscontent, but bullets don't * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" typedef struct { vec3_t boxmins, boxmaxs;// enclose the test object along entire move float *mins, *maxs; // size of the moving object vec3_t mins2, maxs2; // size when clipping against mosnters float *start, *end; trace_t trace; int type; edict_t *passedict; } moveclip_t; #if !id386 static int SV_HullPointContents (hull_t *hull, int num, vec3_t p); #endif /* =============================================================================== HULL BOXES =============================================================================== */ static hull_t box_hull; static mclipnode_t box_clipnodes[6]; static mplane_t box_planes[6]; static int move_type; /* =================== SV_InitBoxHull Set up the planes and clipnodes so that the six floats of a bounding box can just be stored out and get a proper hull_t structure. =================== */ static void SV_InitBoxHull (void) { int i; int side; box_hull.clipnodes = box_clipnodes; box_hull.planes = box_planes; box_hull.firstclipnode = 0; box_hull.lastclipnode = 5; for (i = 0; i < 6; i++) { box_clipnodes[i].planenum = i; side = i & 1; box_clipnodes[i].children[side] = CONTENTS_EMPTY; if (i != 5) box_clipnodes[i].children[side^1] = i + 1; else box_clipnodes[i].children[side^1] = CONTENTS_SOLID; box_planes[i].type = i>>1; box_planes[i].normal[i>>1] = 1; } } /* =================== SV_HullForBox To keep everything totally uniform, bounding boxes are turned into small BSP trees instead of being compared directly. =================== */ static hull_t *SV_HullForBox (vec3_t mins, vec3_t maxs) { box_planes[0].dist = maxs[0]; box_planes[1].dist = mins[0]; box_planes[2].dist = maxs[1]; box_planes[3].dist = mins[1]; box_planes[4].dist = maxs[2]; box_planes[5].dist = mins[2]; return &box_hull; } /* ================ SV_HullForEntity Returns a hull that can be used for testing or clipping an object of mins/maxs size. Offset is filled in to contain the adjustment that must be added to the testing object's origin to get a point to use with the returned hull. ================ */ static hull_t *SV_HullForEntity (edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t offset, edict_t *move_ent) { qmodel_t *model; vec3_t size; vec3_t hullmins, hullmaxs; hull_t *hull; int idx; // decide which clipping hull to use, based on the size if (ent->v.solid == SOLID_BSP) { // explicit hulls in the BSP model if (ent->v.movetype != MOVETYPE_PUSH) Sys_Error ("SOLID_BSP without MOVETYPE_PUSH"); model = sv.models[ (int)ent->v.modelindex ]; if (!model || model->type != mod_brush) Sys_Error ("SOLID_BSP with a non bsp model"); VectorSubtract (maxs, mins, size); //THIS IS WHERE THE MONSTER STEPPING ERROR WAS- IN CHECKBOTTOM, //A '0 0 0' MINS AND MAXS WERE SENT, BUT HERE, IT LOOKS TO SEE //IF THE MONSTER HAS A HULL AND CALCULATES THE OFFSET FROM //THE HULL MINS AND MAXS AND THE PASSED MINS AND MAXS, //THIS WILL INCORRECTLY OFFSET THE TEST MOVE BY THE MINS AND //MAXS OF THE MONSTER! WILL CHECK FOR SIDE EFFECTS... if (move_ent->v.hull) // Entity is specifying which hull to use { idx = move_ent->v.hull-1; hull = &model->hulls[idx]; if (!hull) // Invalid hull { Con_Printf ("ERROR: hull %d is null.\n", idx); hull = &model->hulls[0]; } } else // Using the old way uses size to determine hull to use { // if ((int)move_ent->v.flags & FL_MONSTER) // Con_DPrintf("ERROR: auto-detecing hull for monster!\n"); if (size[0] < 3) // Point hull = &model->hulls[0]; else if ( (size[0] <= 8) && ((int)(sv.edicts->v.spawnflags) & 1) ) // Pentacles hull = &model->hulls[4]; else if (size[0] <= 32 && size[2] <= 28) // Half Player hull = &model->hulls[3]; else if (size[0] <= 32) // Full Player hull = &model->hulls[1]; else // Golem hull = &model->hulls[5]; } // calculate an offset value to center the origin: VectorSubtract (hull->clip_mins, mins, offset); if ((int)move_ent->v.flags & FL_MONSTER) { if (offset[0] != 0 || offset[1] != 0) { // Con_DPrintf("ERROR: Non-zero offset (%f,%f,%f)!!!\n", // offset[0], offset[1], offset[2]); // 524288 (FL_MISMATCHEDBOUNDS ?) is an abandoned H2MP flag? // if ((int)move_ent->v.flags2 & 524288) offset[0] = 0; offset[1] = 0; } } VectorAdd (offset, ent->v.origin, offset); } else { // create a temp hull from bounding box sizes VectorSubtract (ent->v.mins, maxs, hullmins); VectorSubtract (ent->v.maxs, mins, hullmaxs); hull = SV_HullForBox (hullmins, hullmaxs); VectorCopy (ent->v.origin, offset); } return hull; } /* =============================================================================== ENTITY AREA CHECKING =============================================================================== */ static areanode_t sv_areanodes[AREA_NODES]; static int sv_numareanodes; /* =============== SV_CreateAreaNode =============== */ static areanode_t *SV_CreateAreaNode (int depth, vec3_t mins, vec3_t maxs) { areanode_t *anode; vec3_t size; vec3_t mins1, maxs1, mins2, maxs2; anode = &sv_areanodes[sv_numareanodes]; sv_numareanodes++; ClearLink (&anode->trigger_edicts); ClearLink (&anode->solid_edicts); if (depth == AREA_DEPTH) { anode->axis = -1; anode->children[0] = anode->children[1] = NULL; return anode; } VectorSubtract (maxs, mins, size); if (size[0] > size[1]) anode->axis = 0; else anode->axis = 1; anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]); VectorCopy (mins, mins1); VectorCopy (mins, mins2); VectorCopy (maxs, maxs1); VectorCopy (maxs, maxs2); maxs1[anode->axis] = mins2[anode->axis] = anode->dist; anode->children[0] = SV_CreateAreaNode (depth+1, mins2, maxs2); anode->children[1] = SV_CreateAreaNode (depth+1, mins1, maxs1); return anode; } /* =============== SV_ClearWorld =============== */ void SV_ClearWorld (void) { SV_InitBoxHull (); memset (sv_areanodes, 0, sizeof(sv_areanodes)); sv_numareanodes = 0; SV_CreateAreaNode (0, sv.worldmodel->mins, sv.worldmodel->maxs); } /* =============== SV_UnlinkEdict =============== */ static link_t **sv_link_next; static link_t **sv_link_prev; void SV_UnlinkEdict (edict_t *ent) { if (!ent->area.prev) return; // not linked in anywhere RemoveLink (&ent->area); if (sv_link_next && *sv_link_next == &ent->area) *sv_link_next = ent->area.next; if (sv_link_prev && *sv_link_prev == &ent->area) *sv_link_prev = ent->area.prev; ent->area.prev = ent->area.next = NULL; } /* ==================== SV_TouchLinks ==================== */ static void SV_TouchLinks (edict_t *ent, areanode_t *node) { link_t *l, *lnext; edict_t *touch; int old_self, old_other; loc0: // touch linked edicts sv_link_next = &lnext; for (l = node->trigger_edicts.next ; l != &node->trigger_edicts ; l = lnext) { if (!l) { // my area got removed out from under me! Con_Printf ("%s: encountered NULL link!\n", __thisfunc__); break; } lnext = l->next; touch = EDICT_FROM_AREA(l); if (touch == ent) continue; if (!touch->v.touch || touch->v.solid != SOLID_TRIGGER) continue; if (ent->v.absmin[0] > touch->v.absmax[0] || ent->v.absmin[1] > touch->v.absmax[1] || ent->v.absmin[2] > touch->v.absmax[2] || ent->v.absmax[0] < touch->v.absmin[0] || ent->v.absmax[1] < touch->v.absmin[1] || ent->v.absmax[2] < touch->v.absmin[2] ) continue; old_self = *sv_globals.self; old_other = *sv_globals.other; *sv_globals.self = EDICT_TO_PROG(touch); *sv_globals.other = EDICT_TO_PROG(ent); *sv_globals.time = sv.time; PR_ExecuteProgram (touch->v.touch); *sv_globals.self = old_self; *sv_globals.other = old_other; } sv_link_next = NULL; // recurse down both sides if (node->axis == -1) return; // LordHavoc: optimized recursion //if (ent->v.absmax[node->axis] > node->dist) SV_TouchLinks (ent, node->children[0]); //if (ent->v.absmin[node->axis] < node->dist) SV_TouchLinks (ent, node->children[1]); if (ent->v.absmax[node->axis] > node->dist) { if (ent->v.absmin[node->axis] < node->dist) SV_TouchLinks(ent, node->children[1]); // order reversed to reduce code node = node->children[0]; goto loc0; } else { if (ent->v.absmin[node->axis] < node->dist) { node = node->children[1]; goto loc0; } } } /* =============== SV_FindTouchedLeafs =============== */ static void SV_FindTouchedLeafs (edict_t *ent, mnode_t *node) { mplane_t *splitplane; mleaf_t *leaf; int sides; int leafnum; loc0: if (node->contents == CONTENTS_SOLID) return; // add an efrag if the node is a leaf if (node->contents < 0) { if (ent->num_leafs == MAX_ENT_LEAFS) return; leaf = (mleaf_t *)node; leafnum = leaf - sv.worldmodel->leafs - 1; ent->leafnums[ent->num_leafs] = leafnum; ent->num_leafs++; return; } // NODE_MIXED splitplane = node->plane; sides = BOX_ON_PLANE_SIDE(ent->v.absmin, ent->v.absmax, splitplane); // recurse down the contacted sides // LordHavoc: optimized recursion //if (sides & 1) SV_FindTouchedLeafs (ent, node->children[0]); //if (sides & 2) SV_FindTouchedLeafs (ent, node->children[1]); switch (sides) { case 1: node = node->children[0]; goto loc0; case 2: node = node->children[1]; goto loc0; default: // 3 if (node->children[0]->contents != CONTENTS_SOLID) SV_FindTouchedLeafs (ent, node->children[0]); node = node->children[1]; goto loc0; } } /* =============== SV_LinkEdict =============== */ void SV_LinkEdict (edict_t *ent, qboolean touch_triggers) { areanode_t *node; if (ent->area.prev) SV_UnlinkEdict (ent); // unlink from old position if (ent == sv.edicts) return; // don't add the world if (ent->free) return; // set the abs box if (ent->v.solid == SOLID_BSP && (ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) ) { // expand for rotation float v, maxv; int i; maxv = 0; for (i = 0; i < 3; i++) { v = fabs(ent->v.mins[i]); if (v > maxv) maxv = v; v = fabs(ent->v.maxs[i]); if (v > maxv) maxv = v; } for (i = 0; i < 3; i++) { ent->v.absmin[i] = ent->v.origin[i] - maxv; ent->v.absmax[i] = ent->v.origin[i] + maxv; } } else { VectorAdd (ent->v.origin, ent->v.mins, ent->v.absmin); VectorAdd (ent->v.origin, ent->v.maxs, ent->v.absmax); } // // to make items easier to pick up and allow them to be grabbed off // of shelves, the abs sizes are expanded // if ((int)ent->v.flags & FL_ITEM) { ent->v.absmin[0] -= 15; ent->v.absmin[1] -= 15; ent->v.absmax[0] += 15; ent->v.absmax[1] += 15; } else { // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch ent->v.absmin[0] -= 1; ent->v.absmin[1] -= 1; ent->v.absmin[2] -= 1; ent->v.absmax[0] += 1; ent->v.absmax[1] += 1; ent->v.absmax[2] += 1; } // link to PVS leafs ent->num_leafs = 0; if (ent->v.modelindex) SV_FindTouchedLeafs (ent, sv.worldmodel->nodes); if (ent->v.solid == SOLID_NOT) return; // find the first node that the ent's box crosses node = sv_areanodes; while (1) { if (node->axis == -1) break; if (ent->v.absmin[node->axis] > node->dist) node = node->children[0]; else if (ent->v.absmax[node->axis] < node->dist) node = node->children[1]; else break; // crosses the node } // link it in if (ent->v.solid == SOLID_TRIGGER) InsertLinkBefore (&ent->area, &node->trigger_edicts); else InsertLinkBefore (&ent->area, &node->solid_edicts); // if touch_triggers, touch all entities at this node and decend for more if (touch_triggers) SV_TouchLinks ( ent, sv_areanodes ); } /* =============================================================================== POINT TESTING IN HULLS =============================================================================== */ #if !id386 /* ================== SV_HullPointContents ================== */ static int SV_HullPointContents (hull_t *hull, int num, vec3_t p) { float d; mclipnode_t *node; mplane_t *plane; while (num >= 0) { if (num < hull->firstclipnode || num > hull->lastclipnode) Sys_Error ("%s: bad node number", __thisfunc__); node = hull->clipnodes + num; plane = hull->planes + node->planenum; if (plane->type < 3) d = p[plane->type] - plane->dist; else d = DotProductDBL(plane->normal, p) - plane->dist; if (d < 0) num = node->children[1]; else num = node->children[0]; } return num; } #endif /* !id386 */ /* ================== SV_PointContents ================== */ int SV_PointContents (vec3_t p) { int cont; cont = SV_HullPointContents (&sv.worldmodel->hulls[0], 0, p); if (cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN) cont = CONTENTS_WATER; return cont; } #ifdef QUAKE2 int SV_TruePointContents (vec3_t p) { return SV_HullPointContents (&sv.worldmodel->hulls[0], 0, p); } #endif //=========================================================================== /* ============ SV_TestEntityPosition This could be a lot more efficient... FIXME!!! For rotating doors, this is totally inaccurate ============ */ edict_t *SV_TestEntityPosition (edict_t *ent) { trace_t trace; trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, 0, ent); if (trace.startsolid) { // Con_DPrintf("%s inside check\n", PR_GetString(trace.ent->v.classname)); return sv.edicts; } return NULL; } /* =============================================================================== LINE TESTING IN HULLS =============================================================================== */ static void WackyBugFixer(float *p1, float *p2, float *frac, float *mid) { int i; for (i = 0; i < 3; i++) mid[i] = p1[i] + (*frac)*(p2[i] - p1[i]); } /* ================== SV_RecursiveHullCheck ================== */ qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace) { mclipnode_t *node; mplane_t *plane; float t1, t2; float frac; int i; vec3_t mid; int side; float midf; int contents; loc0: // optimized recursion // check for empty if (num < 0) { if (num != CONTENTS_SOLID) { trace->allsolid = false; if (num == CONTENTS_EMPTY) trace->inopen = true; else trace->inwater = true; } else trace->startsolid = true; return true; // empty } if (num < hull->firstclipnode || num > hull->lastclipnode) Sys_Error ("%s: bad node number", __thisfunc__); // // find the point distances // node = hull->clipnodes + num; plane = hull->planes + node->planenum; if (plane->type < 3) { t1 = p1[plane->type] - plane->dist; t2 = p2[plane->type] - plane->dist; } else { t1 = DotProductDBL(plane->normal, p1) - plane->dist; t2 = DotProductDBL(plane->normal, p2) - plane->dist; } #if 1 if (t1 >= 0 && t2 >= 0) { //return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); num = node->children[0]; goto loc0; } if (t1 < 0 && t2 < 0) { //return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); num = node->children[1]; goto loc0; } #else if ( (t1 >= DIST_EPSILON && t2 >= DIST_EPSILON) || (t2 > t1 && t1 >= 0) ) return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); if ( (t1 <= -DIST_EPSILON && t2 <= -DIST_EPSILON) || (t2 < t1 && t1 <= 0) ) return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); #endif // put the crosspoint DIST_EPSILON pixels on the near side side = (t1 < 0); if (side) frac = (t1 + DIST_EPSILON)/(t1-t2); else frac = (t1 - DIST_EPSILON)/(t1-t2); if (frac < 0) frac = 0; else if (frac > 1) frac = 1; midf = p1f + (p2f - p1f)*frac; for (i = 0; i < 3; i++) mid[i] = p1[i] + frac*(p2[i] - p1[i]); // move up to the node if (!SV_RecursiveHullCheck (hull, node->children[side], p1f, midf, p1, mid, trace) ) return false; #ifdef PARANOID if (SV_HullPointContents (hull, node->children[side], mid) == CONTENTS_SOLID) { Con_Printf ("mid PointInHullSolid\n"); return false; } #endif // LordHavoc: this recursion can not be optimized because mid would need to be duplicated on a stack contents = SV_HullPointContents (hull, node->children[side^1], mid); // if (contents != CONTENTS_SOLID && (contents == CONTENTS_WATER || move_type != MOVE_WATER)) if (contents != CONTENTS_SOLID) // go past the node return SV_RecursiveHullCheck (hull, node->children[side^1], midf, p2f, mid, p2, trace); if (trace->allsolid) return false; // never got out of the solid area //================== // the other side of the node is solid, this is the impact point //================== if (!side) { VectorCopy (plane->normal, trace->plane.normal); trace->plane.dist = plane->dist; } else { VectorNegate (plane->normal, trace->plane.normal); trace->plane.dist = -plane->dist; } // while (SV_HullPointContents (hull, hull->firstclipnode, mid) == CONTENTS_SOLID) while (1) { // shouldn't really happen, but does occasionally contents = SV_HullPointContents (hull, hull->firstclipnode, mid); // if (contents != CONTENTS_SOLID && (contents == CONTENTS_WATER || move_type != MOVE_WATER)) if (contents != CONTENTS_SOLID) break; frac -= 0.1; if (frac < 0) { trace->fraction = midf; VectorCopy (mid, trace->endpos); Con_DPrintf ("backup past 0\n"); return false; } midf = p1f + (p2f - p1f)*frac; // for (i = 0; i < 3; i++) // mid[i] = p1[i] + frac * (p2[i] - p1[i]); WackyBugFixer(p1, p2, &frac, mid); } trace->fraction = midf; VectorCopy (mid, trace->endpos); return false; } /* ================== SV_ClipMoveToEntity Handles selection or creation of a clipping hull, and offseting (and eventually rotation) of the end points ================== */ static trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t *move_ent) { trace_t trace; vec3_t offset; vec3_t start_l, end_l; hull_t *hull; // fill in a default trace memset (&trace, 0, sizeof(trace_t)); trace.fraction = 1; trace.allsolid = true; VectorCopy (end, trace.endpos); // get the clipping hull hull = SV_HullForEntity (ent, mins, maxs, offset, move_ent); VectorSubtract (start, offset, start_l); VectorSubtract (end, offset, end_l); // rotate start and end into the models frame of reference if (ent->v.solid == SOLID_BSP && (fabs(ent->v.angles[0]) > 1 || fabs(ent->v.angles[1]) > 1 || fabs(ent->v.angles[2]) > 1) ) { vec3_t forward, right, up; vec3_t temp; AngleVectors (ent->v.angles, forward, right, up); VectorCopy (start_l, temp); start_l[0] = DotProduct (temp, forward); start_l[1] = -DotProduct (temp, right); start_l[2] = DotProduct (temp, up); VectorCopy (end_l, temp); end_l[0] = DotProduct (temp, forward); end_l[1] = -DotProduct (temp, right); end_l[2] = DotProduct (temp, up); } // trace a line through the apropriate clipping hull SV_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace); if (move_type == MOVE_WATER) { if (SV_PointContents (trace.endpos) != CONTENTS_WATER) { VectorCopy(start_l, trace.endpos); trace.fraction = 0; } } // rotate endpos back to world frame of reference if (ent->v.solid == SOLID_BSP && (fabs(ent->v.angles[0]) > 1 || fabs(ent->v.angles[1]) > 1 || fabs(ent->v.angles[2]) > 1) ) { vec3_t a; vec3_t forward, right, up; vec3_t temp; if (trace.fraction != 1) { VectorNegate (ent->v.angles, a); AngleVectors (a, forward, right, up); VectorCopy (trace.endpos, temp); trace.endpos[0] = DotProduct (temp, forward); trace.endpos[1] = -DotProduct (temp, right); trace.endpos[2] = DotProduct (temp, up); VectorCopy (trace.plane.normal, temp); trace.plane.normal[0] = DotProduct (temp, forward); trace.plane.normal[1] = -DotProduct (temp, right); trace.plane.normal[2] = DotProduct (temp, up); } } // fix trace up by the offset if (trace.fraction != 1) VectorAdd (trace.endpos, offset, trace.endpos); // did we clip the move? if (trace.fraction < 1 || trace.startsolid) trace.ent = ent; return trace; } //=========================================================================== /* ==================== SV_ClipToLinks Mins and maxs enclose the entire area swept by the move ==================== */ static void SV_ClipToLinks (areanode_t *node, moveclip_t *clip) { link_t *l, *next; edict_t *touch; trace_t trace; loc0: // optimized recursion // touch linked edicts for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next) { next = l->next; touch = EDICT_FROM_AREA(l); if (touch->v.solid == SOLID_NOT) continue; if (touch == clip->passedict) continue; if (touch->v.solid == SOLID_TRIGGER) Sys_Error ("Trigger in clipping list (%s)", PR_GetString(touch->v.classname)); if ((clip->type == MOVE_NOMONSTERS || clip->type == MOVE_PHASE) && touch->v.solid != SOLID_BSP) continue; if (clip->boxmins[0] > touch->v.absmax[0] || clip->boxmins[1] > touch->v.absmax[1] || clip->boxmins[2] > touch->v.absmax[2] || clip->boxmaxs[0] < touch->v.absmin[0] || clip->boxmaxs[1] < touch->v.absmin[1] || clip->boxmaxs[2] < touch->v.absmin[2] ) continue; if (clip->passedict && clip->passedict->v.size[0] && !touch->v.size[0]) continue; // points never interact // might intersect, so do an exact clip if (clip->trace.allsolid) return; if (clip->passedict) { if (PROG_TO_EDICT(touch->v.owner) == clip->passedict) continue; // don't clip against own missiles if (PROG_TO_EDICT(clip->passedict->v.owner) == touch) continue; // don't clip against owner } if ((int)touch->v.flags & FL_MONSTER) trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins2, clip->maxs2, clip->end, touch); else trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins, clip->maxs, clip->end, touch); if (trace.allsolid || trace.startsolid || trace.fraction < clip->trace.fraction) { trace.ent = touch; if (clip->trace.startsolid) { clip->trace = trace; clip->trace.startsolid = true; } else clip->trace = trace; } else if (trace.startsolid) clip->trace.startsolid = true; } // recurse down both sides if (node->axis == -1) return; if ( clip->boxmaxs[node->axis] > node->dist ) { //SV_ClipToLinks ( node->children[0], clip ); if (clip->boxmins[node->axis] < node->dist) SV_ClipToLinks(node->children[1], clip); node = node->children[0]; goto loc0; } else if ( clip->boxmins[node->axis] < node->dist ) { //SV_ClipToLinks ( node->children[1], clip ); node = node->children[1]; goto loc0; } } /* ================== SV_MoveBounds ================== */ static void SV_MoveBounds (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs) { #if 0 // debug to test against everything boxmins[0] = boxmins[1] = boxmins[2] = -9999999; //FIXME: change to FLT_MAX/-FLT_MAX boxmaxs[0] = boxmaxs[1] = boxmaxs[2] = 9999999; #else int i; for (i = 0; i < 3; i++) { if (end[i] > start[i]) { boxmins[i] = start[i] + mins[i] - 1; boxmaxs[i] = end[i] + maxs[i] + 1; } else { boxmins[i] = end[i] + mins[i] - 1; boxmaxs[i] = start[i] + maxs[i] + 1; } } #endif } /* ================== SV_Move ================== */ trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict) { moveclip_t clip; int i; // type = MOVE_WATER; memset ( &clip, 0, sizeof ( moveclip_t ) ); move_type = type; // clip to world clip.trace = SV_ClipMoveToEntity ( sv.edicts, start, mins, maxs, end, passedict ); clip.start = start; clip.end = end; clip.mins = mins; clip.maxs = maxs; clip.type = type; clip.passedict = passedict; if (type == MOVE_MISSILE || type == MOVE_PHASE) { //Larger for projectiles against monsters for (i = 0; i < 3; i++) { clip.mins2[i] = -15; clip.maxs2[i] = 15; } } else { VectorCopy (mins, clip.mins2); VectorCopy (maxs, clip.maxs2); } // create the bounding box of the entire move SV_MoveBounds ( start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs ); // clip to entities SV_ClipToLinks ( sv_areanodes, &clip ); return clip.trace; } engine/hexen2/world.h000066400000000000000000000063101444734033100150170ustar00rootroot00000000000000/* * world.c -- world query functions * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_WORLD_H #define __HX2_WORLD_H typedef struct { vec3_t normal; float dist; } plane_t; typedef struct { qboolean allsolid; // if true, plane is not valid qboolean startsolid; // if true, the initial point was in a solid area qboolean inopen, inwater; float fraction; // time completed, 1.0 = didn't hit anything vec3_t endpos; // final position plane_t plane; // surface normal at impact edict_t *ent; // entity the surface is on } trace_t; #define MOVE_NORMAL 0 #define MOVE_NOMONSTERS 1 #define MOVE_MISSILE 2 #define MOVE_WATER 3 #define MOVE_PHASE 4 typedef struct areanode_s { int axis; // -1 = leaf node float dist; struct areanode_s *children[2]; link_t trigger_edicts; link_t solid_edicts; } areanode_t; #define AREA_DEPTH 4 #define AREA_NODES 32 void SV_ClearWorld (void); // called after the world model has been loaded, before linking any entities void SV_UnlinkEdict (edict_t *ent); // call before removing an entity, and before trying to move one, // so it doesn't clip against itself // flags ent->v.modified void SV_LinkEdict (edict_t *ent, qboolean touch_triggers); // Needs to be called any time an entity changes origin, mins, maxs, or solid // flags ent->v.modified // sets ent->v.absmin and ent->v.absmax // if touchtriggers, calls prog functions for the intersected triggers int SV_PointContents (vec3_t p); #ifdef QUAKE2 int SV_TruePointContents (vec3_t p); #endif // returns the CONTENTS_* value from the world at the given point. // does not check any entities at all // the non-true version remaps the water current contents to content_water edict_t *SV_TestEntityPosition (edict_t *ent); trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict); // mins and maxs are reletive // if the entire move stays in a solid volume, trace.allsolid will be set // if the starting point is in a solid, it will be allowed to move out // to an open area // nomonsters is used for line of sight or edge testing, where mosnters // shouldn't be considered solid objects // passedict is explicitly excluded from clipping checks (normally NULL) ASM_LINKAGE_BEGIN #if id386 int SV_HullPointContents (hull_t *hull, int num, vec3_t p); #endif ASM_LINKAGE_END qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace); #endif /* __HX2_WORLD_H */ engine/hexenworld/000077500000000000000000000000001444734033100145055ustar00rootroot00000000000000engine/hexenworld/build.sh000077500000000000000000000015221444734033100161430ustar00rootroot00000000000000#!/bin/sh if test "$1" = "strip"; then exe_ext= if env | grep -i windir > /dev/null; then exe_ext=".exe" fi strip server/hwsv$exe_ext \ client/hwcl$exe_ext \ client/glhwcl$exe_ext exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac if test "$1" = "clean"; then $MAKE_CMD -C client clean $MAKE_CMD -C server clean exit 0 fi echo "Building hexenworld server..." $MAKE_CMD -C server $* || exit 1 $MAKE_CMD -s -C server clean echo "" && echo "Building hexenworld client (software renderer)" $MAKE_CMD -C client hw $* || exit 1 $MAKE_CMD -s -C client localclean echo "" && echo "Building hexenworld client (opengl renderer)" $MAKE_CMD -C client glhw USE_X86_ASM=no $* || exit 1 $MAKE_CMD -s -C client clean engine/hexenworld/build_cross_morphos.sh000077500000000000000000000014771444734033100211340ustar00rootroot00000000000000#!/bin/sh UHEXEN2_TOP=../.. . $UHEXEN2_TOP/scripts/cross_defs.morphos if test "$1" = "strip"; then echo "Stripping all HexenWorld binaries" $STRIPPER -S server/hwsv client/hwcl client/glhwcl exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac if test "$1" = "clean"; then $MAKE_CMD -C client clean $MAKE_CMD -C server clean exit 0 fi echo "Building HexenWorld Server" $MAKE_CMD -C server $* || exit 1 $MAKE_CMD -s -C server clean echo "" && echo "Building HexenWorld Client (Software renderer)" $MAKE_CMD -C client hw $* || exit 1 $MAKE_CMD -s -C client clean echo "" && echo "Building HexenWorld Client (OpenGL renderer)" $MAKE_CMD -C client glhw $* || exit 1 $MAKE_CMD -s -C client clean engine/hexenworld/client/000077500000000000000000000000001444734033100157635ustar00rootroot00000000000000engine/hexenworld/client/Makefile000066400000000000000000001143651444734033100174350ustar00rootroot00000000000000# GNU Makefile for hexenworld clients using GCC. # # Remember to "make clean" between different types of builds or targets. # Unix platforms require SDL !!! # # To cross-compile for Win32 on Unix: either pass the W32BUILD=1 # argument to make, or export it. Also see build_cross_win32.sh. # Requires: a mingw or mingw-w64 compiler toolchain. # # To cross-compile for Win64 on Unix: either pass the W64BUILD=1 # argument to make, or export it. Also see build_cross_win64.sh. # Requires: a mingw-w64 compiler toolchain. # # To cross-compile for MacOSX on Unix: either pass the OSXBUILD=1 # argument to make, or export it. You would also need to pass a # suitable MACH_TYPE=xxx (ppc, x86, x86_64, or ppc64) argument to # make. Also see build_cross_osx.sh. # # To (cross-)compile for DOS: either pass the DOSBUILD=1 argument # to make, or export it. Also see build_cross_dos.sh. Requires: a # djgpp compiler toolchain. # # To see valid targets: make help # # To use a compiler other than gcc: make CC=compiler_name [other stuff] # # To use a different nasm-compatible assembler, such as yasm: # make NASM=yasm [other stuff] # # To specify X installation at somewhere other than /usr/X11R6 # such as /usr/X11R7 : make X11BASE=/your/x11/path [other stuff] # # To build for the demo version: make DEMO=1 [other stuff] # # To build a debug version: make DEBUG=1 [other stuff] # # PATH SETTINGS: UHEXEN2_TOP:=../../.. ENGINE_TOP:=../.. HW_TOP:=.. COMMONDIR:=$(ENGINE_TOP)/h2shared COMMON_HW:=$(HW_TOP)/shared UHEXEN2_SHARED:=$(UHEXEN2_TOP)/common LIBS_DIR:=$(UHEXEN2_TOP)/libs OSLIBS:=$(UHEXEN2_TOP)/oslibs # GENERAL OPTIONS (customize as required) # X directory X11BASE =/usr/X11R6 # the sdl-config command SDL_CONFIG =sdl-config SDL_CFLAGS = $(shell $(SDL_CONFIG) --cflags 2> /dev/null) SDL_LIBS = $(shell $(SDL_CONFIG) --libs 2> /dev/null) # use fast x86 assembly on ia32 machines? (auto-disabled for # any other cpu.) USE_X86_ASM=yes # use optimized m68k assembly for amiga # requires vasm from http://sun.hasenbraten.de/vasm/ USE_M68K_ASM=yes # link to the opengl libraries at compile time? (defaults # to no, so the binaries will dynamically load the necessary # libraries and functions at runtime.) LINK_GL_LIBS=no # enable evil 3dfx glide hacks for native hardware gamma for # the old Voodoo Graphics and Voodoo2 cards? (Linux/FreeBSD) USE_3DFXGAMMA=no # enable sound support? USE_SOUND=yes # ALSA audio support? (req: alsa-lib and alsa-kernel modules # >= 1.0.1. v0.9.8 and v1.0.0 might work, but not supported. # If not Linux, ALSA will be automatically be disabled.) USE_ALSA=yes # OSS audio support? (for Unix. enabled on Linux and FreeBSD. # automatically disabled on other platforms: see snd_sys.h) USE_OSS=yes # SUN audio support? (enabled on OpenBSD, NetBSD and SUN. # automatically disabled on others: see snd_sys.h) USE_SUNAUDIO=yes # SDL audio support? (enabled on all unix-like platforms.) USE_SDLAUDIO=yes # include target's MIDI driver if available? USE_MIDI=yes # CDAudio support? USE_CDAUDIO=yes # use SDL cdaudio? (otherwise platform specific cdrom code will # be used. The only problem with SDL_cdrom is that it lacks # proper volume controls. See cd_unix.h for the availability of # platform specific cdaudio drivers. # NOTE: SDL dropped cdaudio support in version 1.3.0 and later!) USE_SDLCD=no # link to the directx libraries at compile time? (otherwise, load # the necessary DLLs and functions dynamically at runtime, which # ensures our exe to function on ancient windows versions without # a directx installation.) LINK_DIRECTX=no # enable startup splash screens? (windows) WITH_SPLASHES=yes # use WinSock2 instead of WinSock-1.1? (disabled for w32 for compat. # with old Win95 machines.) (enabled for Win64 in the win64 section.) USE_WINSOCK2=no # Enable/disable codecs for streaming music support: USE_CODEC_WAVE=yes USE_CODEC_FLAC=no USE_CODEC_MP3=yes USE_CODEC_VORBIS=yes USE_CODEC_OPUS=no # either xmp or mikmod (or modplug) USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no # either timidity (preferred) or wildmidi (both possible # but not needed nor meaningful) USE_CODEC_TIMIDITY=yes USE_CODEC_WILDMIDI=no # which library to use for mp3 decoding: mad or mpg123 MP3LIB=mad # which library to use for ogg decoding: vorbis or tremor VORBISLIB=vorbis # whether the codecs allocate on the zone instead of # system memory (set to yes for DOS builds, for example) CODECS_USE_ZONE=no # include the common dirty stuff include $(UHEXEN2_TOP)/scripts/makefile.inc # Names of the binaries SW_BINARY:=hwcl$(exe_ext) GL_BINARY:=glhwcl$(exe_ext) ############################################################# # Compiler flags ############################################################# ifeq ($(MACH_TYPE),x86) CPU_X86=-march=i586 endif # Overrides for the default CPUFLAGS CPUFLAGS=$(CPU_X86) CFLAGS += -Wall CFLAGS += $(CPUFLAGS) ifdef DEBUG CFLAGS += -g else # optimization flags CFLAGS += -O2 -DNDEBUG=1 -ffast-math # NOTE: -fomit-frame-pointer is broken with ancient gcc versions!! CFLAGS += -fomit-frame-pointer endif CPPFLAGS= LDFLAGS = # linkage may be sensitive to order: add SYSLIBS after all others. SYSLIBS = # compiler includes INCLUDES= -I. -I$(COMMON_HW) -I$(COMMONDIR) -I$(UHEXEN2_SHARED) # nasm includes: the trailing directory separator matters NASM_INC= -I./ -I$(COMMON_HW)/ -I$(COMMONDIR)/ # windows resource compiler includes RC_INC = -I. -I$(COMMON_HW) -I$(COMMONDIR) # end of compiler flags ############################################################# ############################################################# # Other build flags ############################################################# CPPFLAGS+= -DH2W # disable x86 assembly if it is not an x86. ifneq ($(MACH_TYPE),x86) USE_X86_ASM=no endif # disable m68k assembly if it is not a m68k. ifneq ($(MACH_TYPE),m68k) USE_M68K_ASM=no endif ifdef DEMO CPPFLAGS+= -DDEMOBUILD endif ifdef DEBUG # This activates some extra code in hexen2/hexenworld C source CPPFLAGS+= -DDEBUG=1 -DDEBUG_BUILD=1 endif ############################################################# # OpenGL settings ############################################################# GL_DEFS = -DGLQUAKE GL_LIBS = ############################################################# # streaming music initial setup ############################################################# ifneq ($(USE_SOUND),yes) USE_CODEC_WAVE=no USE_CODEC_FLAC=no USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no USE_CODEC_MP3=no USE_CODEC_VORBIS=no USE_CODEC_OPUS=no endif # sanity checking for decoder library options ifneq ($(VORBISLIB),vorbis) ifneq ($(VORBISLIB),tremor) $(error Invalid VORBISLIB setting) endif endif ifneq ($(MP3LIB),mpg123) ifneq ($(MP3LIB),mad) $(error Invalid MP3LIB setting) endif endif ############################################################# # Mac OS X flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),darwin) NASMFLAGS=-f macho CPUFLAGS= # @rpath can be used when targeting 10.5+ USE_RPATH=no ifeq ($(MACH_TYPE),x86) # x86 requires 10.4. CFLAGS +=-mmacosx-version-min=10.4 LDFLAGS +=-mmacosx-version-min=10.4 endif ifeq ($(MACH_TYPE),ppc) # require 10.2 for ppc builds (midi_osx.c requirement.) CFLAGS +=-mmacosx-version-min=10.2 LDFLAGS +=-mmacosx-version-min=10.2 endif ifeq ($(MACH_TYPE),ppc64) # require 10.5 for ppc64 (actually SDL doesn't support ppc64.) CFLAGS +=-mmacosx-version-min=10.5 LDFLAGS +=-mmacosx-version-min=10.5 USE_RPATH=yes endif ifeq ($(MACH_TYPE),x86_64) # require 10.6 for amd64 builds (not 10.5: SDL's requirement.) # bundle1.o is needed for dyld_stub_binding_helper CFLAGS +=-mmacosx-version-min=10.6 LDFLAGS +=-mmacosx-version-min=10.6 -Wl,-lbundle1.o USE_RPATH=yes endif ifeq ($(USE_RPATH),yes) LDFLAGS +=-Wl,-rpath,@executable_path/../Frameworks endif ifeq ($(USE_SOUND),yes) # enable the extra codecs USE_CODEC_FLAC=yes USE_CODEC_OPUS=yes USE_CODEC_MIKMOD=yes USE_CODEC_UMX=yes endif # no need for timidity, we have a midi driver USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no ifeq ($(USE_MIDI),yes) # note: midi_mac.c requires -Wl,-framework,QuickTime which we are not using anymore. LDFLAGS += -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit -Wl,-framework,CoreServices endif ifeq ($(USE_SOUND),yes) ifneq ($(USE_SDLAUDIO),yes) CPPFLAGS+= -DNO_SDL_AUDIO endif endif USE_SDLCD=yes ifeq ($(USE_CDAUDIO),yes) ifeq ($(USE_SDLCD),yes) CPPFLAGS+= -DWITH_SDLCD endif endif # Unix builds rely on SDL: # SDLQUAKE must be defined for all SDL using platforms/targets CPPFLAGS+= -DSDLQUAKE # not relying on sdl-config command and assuming # /Library/Frameworks/SDL.framework is available SDL_CFLAGS =-D_GNU_SOURCE=1 -D_THREAD_SAFE SDL_CFLAGS+=-DSDL_FRAMEWORK -DNO_SDL_CONFIG SDL_LIBS = # default to our local SDL[2].framework for build SDL_FRAMEWORK_PATH ?=$(OSLIBS)/macosx ifneq ($(SDL_FRAMEWORK_PATH),) SDL_LIBS +=-F$(SDL_FRAMEWORK_PATH) SDL_CFLAGS+=-F$(SDL_FRAMEWORK_PATH) endif SDL_LIBS +=-Wl,-framework,SDL -Wl,-framework,Cocoa CFLAGS += $(SDL_CFLAGS) LDFLAGS += $(SDL_LIBS) GL_LINK=-Wl,-framework,OpenGL ifeq ($(USE_CODEC_FLAC),yes) CODEC_INC = -I$(OSLIBS)/macosx/codecs/include CODEC_LINK= -L$(OSLIBS)/macosx/codecs/lib endif ifeq ($(USE_CODEC_VORBIS),yes) CODEC_INC = -I$(OSLIBS)/macosx/codecs/include CODEC_LINK= -L$(OSLIBS)/macosx/codecs/lib endif ifeq ($(USE_CODEC_MP3),yes) CODEC_INC = -I$(OSLIBS)/macosx/codecs/include CODEC_LINK= -L$(OSLIBS)/macosx/codecs/lib endif ifeq ($(USE_CODEC_OPUS),yes) CODEC_INC = -I$(OSLIBS)/macosx/codecs/include CODEC_LINK= -L$(OSLIBS)/macosx/codecs/lib endif ifeq ($(USE_CODEC_MIKMOD),yes) CODEC_INC = -I$(OSLIBS)/macosx/codecs/include CODEC_LINK= -L$(OSLIBS)/macosx/codecs/lib endif ifeq ($(USE_CODEC_XMP),yes) CODEC_INC = -I$(OSLIBS)/macosx/codecs/include CODEC_LINK= -L$(OSLIBS)/macosx/codecs/lib endif ifeq ($(USE_CODEC_MODPLUG),yes) CODEC_INC = -I$(OSLIBS)/macosx/codecs/include CODEC_LINK= -L$(OSLIBS)/macosx/codecs/lib endif INCLUDES += $(CODEC_INC) LDFLAGS += $(CODEC_LINK) endif # End of Mac OS X settings ############################################################# ############################################################# # Unix flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),unix) # common unix: NASMFLAGS=-f elf -d_NO_PREFIX ifneq ($(X11BASE),) INCLUDES+= -I$(X11BASE)/include endif ifeq ($(HOST_OS),qnx) SYSLIBS += -lsocket endif ifeq ($(HOST_OS),haiku) SYSLIBS += -lnetwork endif ifeq ($(HOST_OS),sunos) SYSLIBS += -lsocket -lnsl -lresolv endif SYSLIBS += -lm ifneq ($(X11BASE),) GL_LINK=-L$(X11BASE)/lib -lGL else GL_LINK=-lGL endif ifeq ($(USE_SOUND),yes) ifneq ($(HOST_OS),linux) # alsa is only for linux USE_ALSA=no endif ifneq ($(USE_ALSA),yes) CPPFLAGS+= -DNO_ALSA_AUDIO else # snd_alsa uses dlopen() & co. SYSLIBS += -ldl endif ifneq ($(USE_OSS),yes) CPPFLAGS+= -DNO_OSS_AUDIO endif ifneq ($(USE_SUNAUDIO),yes) CPPFLAGS+= -DNO_SUN_AUDIO endif ifneq ($(USE_SDLAUDIO),yes) CPPFLAGS+= -DNO_SDL_AUDIO endif endif ifeq ($(USE_CDAUDIO),yes) ifeq ($(USE_SDLCD),yes) CPPFLAGS+= -DWITH_SDLCD endif endif # Unix builds rely on SDL: # SDLQUAKE must be defined for all SDL using platforms/targets CPPFLAGS+= -DSDLQUAKE CFLAGS += $(SDL_CFLAGS) LDFLAGS += $(SDL_LIBS) ifeq ($(USE_CODEC_OPUS),yes) # opus and opusfile put their *.h under /opus, # but they include the headers without the opus directory # prefix and rely on pkg-config. ewww... INCLUDES+= $(shell pkg-config --cflags opusfile) LDFLAGS += $(shell pkg-config --libs opusfile) endif endif # End of Unix settings ############################################################# ############################################################# # OS/2 flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),os2) # OS/2, using EMX: INCLUDES+= -I$(OSLIBS)/os2/emx/include CFLAGS += -Zmt LDFLAGS += -Zmt ifndef DEBUG LDFLAGS += -s # -fomit-frame-pointer/-ffast-math seems to cause trouble # with EMX at least with the old compilers I tried. CFLAGS += -fno-omit-frame-pointer endif # using SDL for now: SDL_CFLAGS=-I$(OSLIBS)/os2/SDL/include SDL_LIBS = -L$(OSLIBS)/os2/SDL/lib -lSDL12 USE_SDLCD=yes USE_SDLAUDIO=yes NASMFLAGS=-f aout SYSLIBS += -lsocket LINK_GL_LIBS=yes GL_LINK=-lopengl ifeq ($(USE_SOUND),yes) ifneq ($(USE_SDLAUDIO),yes) CPPFLAGS+= -DNO_SDL_AUDIO endif endif ifeq ($(USE_CDAUDIO),yes) ifeq ($(USE_SDLCD),yes) CPPFLAGS+= -DWITH_SDLCD endif endif # SDLQUAKE must be defined for all SDL using platforms/targets CPPFLAGS+= -DSDLQUAKE CFLAGS += $(SDL_CFLAGS) LDFLAGS += $(SDL_LIBS) # use Tremor as Vorbis decoder for OS/2? VORBISLIB=tremor ifeq ($(USE_CODEC_VORBIS),yes) CODEC_INC = -I$(OSLIBS)/os2/codecs/include CODEC_LINK= -L$(OSLIBS)/os2/codecs/lib endif ifeq ($(USE_CODEC_MP3),yes) CODEC_INC = -I$(OSLIBS)/os2/codecs/include CODEC_LINK= -L$(OSLIBS)/os2/codecs/lib endif ifeq ($(USE_CODEC_FLAC),yes) CODEC_INC = -I$(OSLIBS)/os2/codecs/include CODEC_LINK= -L$(OSLIBS)/os2/codecs/lib endif ifeq ($(USE_CODEC_OPUS),yes) CODEC_INC = -I$(OSLIBS)/os2/codecs/include CODEC_LINK= -L$(OSLIBS)/os2/codecs/lib endif ifeq ($(USE_CODEC_MIKMOD),yes) CODEC_INC = -I$(OSLIBS)/os2/codecs/include CODEC_LINK= -L$(OSLIBS)/os2/codecs/lib endif ifeq ($(USE_CODEC_XMP),yes) CODEC_INC = -I$(OSLIBS)/os2/codecs/include CODEC_LINK= -L$(OSLIBS)/os2/codecs/lib endif ifeq ($(USE_CODEC_MODPLUG),yes) CODEC_INC = -I$(OSLIBS)/os2/codecs/include CODEC_LINK= -L$(OSLIBS)/os2/codecs/lib endif INCLUDES += $(CODEC_INC) LDFLAGS += $(CODEC_LINK) endif # End of OS/2 settings ############################################################# ############################################################# # DOS flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),dos) NASMFLAGS=-f coff # Use x86 assembly for DOS USE_X86_ASM=yes USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no # no need for Opus on DOS USE_CODEC_OPUS=no # no need for Flac on DOS USE_CODEC_FLAC=no # use Tremor as Vorbis decoder for DOS? VORBISLIB=tremor # make DOS decoders allocate on the zone CODECS_USE_ZONE=yes INCLUDES += -I$(OSLIBS)/dos # WATT-32 is needed for DOS CPPFLAGS+= -DUSE_WATT32 -DWATT32_NO_OLDIES INCLUDES+= -I$(OSLIBS)/dos/watt32/inc LDFLAGS += -L$(OSLIBS)/dos/watt32/lib -lwatt # DOS GL experiments: INCLUDES += -I$(OSLIBS)/dos/opengl/include INCLUDES += -I$(OSLIBS)/dos/glide3/include GL_LINK = -L$(OSLIBS)/dos/opengl/lib/$(GLDIR) -lgl # enable dynamic loading of gl functions LINK_GL_LIBS=no # enable 3dfx gamma hacks USE_3DFXGAMMA=yes ifeq ($(LINK_GL_LIBS),yes) # compile which gl driver in: mesa, sage, or fxmesa. (only one.) DOSGL_DRIVER=fxmesa ifeq ($(DOSGL_DRIVER),fxmesa) GL_DEFS+= -DREFGL_FXMESA endif ifeq ($(DOSGL_DRIVER),mesa) GL_DEFS+= -DREFGL_MESA endif ifeq ($(DOSGL_DRIVER),sage) GL_DEFS+= -DREFGL_SAGE endif GLDIR=$(DOSGL_DRIVER) else SYSOBJ_GL_DXE=yes # choose which dosgl api(s) to support. (at least one.) GL_DEFS+= -DREFGL_FXMESA GL_DEFS+= -DREFGL_MESA GL_DEFS+= -DREFGL_SAGE endif SYSLIBS += $(OSLIBS)/dos/djtime/djtime.a -lc -lgcc #SYSLIBS += -lm # PCI sound card support through libau USE_PCIAUDIO=yes # PCI sound card support as a DXE module PCIAUDIO_DXE=yes ifeq ($(USE_SOUND),yes) ifneq ($(USE_PCIAUDIO),yes) CPPFLAGS+= -DNO_PCI_AUDIO else INCLUDES+= -I$(OSLIBS)/dos/libau/include ifeq ($(PCIAUDIO_DXE),yes) SYSOBJ_SOFT_DXE=yes SYSOBJ_GL_DXE=yes CPPFLAGS+= -DSNDPCI_DXE else LDFLAGS += -L$(OSLIBS)/dos/libau/lib -lau endif endif endif ifeq ($(USE_CODEC_VORBIS),yes) CODEC_INC = -I$(OSLIBS)/dos/codecs/include CODEC_LINK= -L$(OSLIBS)/dos/codecs/lib endif ifeq ($(USE_CODEC_MP3),yes) CODEC_INC = -I$(OSLIBS)/dos/codecs/include CODEC_LINK= -L$(OSLIBS)/dos/codecs/lib endif ifeq ($(USE_CODEC_FLAC),yes) CODEC_INC = -I$(OSLIBS)/dos/codecs/include CODEC_LINK= -L$(OSLIBS)/dos/codecs/lib endif ifeq ($(USE_CODEC_OPUS),yes) CODEC_INC = -I$(OSLIBS)/dos/codecs/include CODEC_LINK= -L$(OSLIBS)/dos/codecs/lib endif ifeq ($(USE_CODEC_MIKMOD),yes) CODEC_INC = -I$(OSLIBS)/dos/codecs/include CODEC_LINK= -L$(OSLIBS)/dos/codecs/lib endif ifeq ($(USE_CODEC_XMP),yes) CODEC_INC = -I$(OSLIBS)/dos/codecs/include CODEC_LINK= -L$(OSLIBS)/dos/codecs/lib endif ifeq ($(USE_CODEC_MODPLUG),yes) CODEC_INC = -I$(OSLIBS)/dos/codecs/include CODEC_LINK= -L$(OSLIBS)/dos/codecs/lib endif INCLUDES += $(CODEC_INC) LDFLAGS += $(CODEC_LINK) endif # End of DOS settings ############################################################# ############################################################# # Win32 flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),win32) RC_DEFS=$(CPPFLAGS) RCFLAGS=--output-format=coff --target=pe-i386 NASMFLAGS=-f win32 GL_LINK=-lopengl32 ifeq ($(USE_SOUND),yes) # enable the extra codecs USE_CODEC_FLAC=yes USE_CODEC_OPUS=yes USE_CODEC_MIKMOD=yes USE_CODEC_UMX=yes endif # timidity not needed, we have a midi driver. if you want it, # comment out the following USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no ifeq ($(USE_WINSOCK2),yes) CPPFLAGS+=-D_USE_WINSOCK2 LIBWINSOCK=ws2_32 else LIBWINSOCK=wsock32 endif CPPFLAGS+= -DWIN32_LEAN_AND_MEAN INCLUDES+= -I$(OSLIBS)/windows/misc/include -I$(OSLIBS)/windows/dxsdk/include CFLAGS += -m32 LDFLAGS += -m32 -mwindows LDFLAGS += -l$(LIBWINSOCK) -lwinmm ifneq ($(LINK_DIRECTX),yes) CPPFLAGS+= -DDX_DLSYM else LDFLAGS += -L$(OSLIBS)/windows/dxsdk/x86 -ldsound -ldinput -ldxguid endif ifneq ($(WITH_SPLASHES),yes) CPPFLAGS+= -DNO_SPLASHES endif ifeq ($(USE_CODEC_FLAC),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x86 endif ifeq ($(USE_CODEC_VORBIS),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x86 endif ifeq ($(USE_CODEC_MP3),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x86 endif ifeq ($(USE_CODEC_OPUS),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x86 endif ifeq ($(USE_CODEC_MIKMOD),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x86 endif ifeq ($(USE_CODEC_XMP),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x86 endif ifeq ($(USE_CODEC_MODPLUG),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x86 endif INCLUDES += $(CODEC_INC) LDFLAGS += $(CODEC_LINK) endif # End of Win32 settings ############################################################# ############################################################# # Win64 flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),win64) NASMFLAGS=-f win64 -d_NO_PREFIX RC_DEFS=$(CPPFLAGS) RCFLAGS=--output-format=coff --target=pe-x86-64 GL_LINK=-lopengl32 ifeq ($(USE_SOUND),yes) # enable the extra codecs USE_CODEC_FLAC=yes USE_CODEC_OPUS=yes USE_CODEC_MIKMOD=yes USE_CODEC_UMX=yes endif # timidity not needed, we have a midi driver. if you want it, # comment out the following USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no # use winsock2 for win64 USE_WINSOCK2=yes ifeq ($(USE_WINSOCK2),yes) CPPFLAGS+=-D_USE_WINSOCK2 LIBWINSOCK=ws2_32 else LIBWINSOCK=wsock32 endif CPPFLAGS+= -DWIN32_LEAN_AND_MEAN INCLUDES+= -I$(OSLIBS)/windows/misc/include -I$(OSLIBS)/windows/dxsdk/include CFLAGS += -m64 LDFLAGS += -m64 -mwindows LDFLAGS += -l$(LIBWINSOCK) -lwinmm ifneq ($(LINK_DIRECTX),yes) CPPFLAGS+= -DDX_DLSYM else LDFLAGS += -L$(OSLIBS)/windows/dxsdk/x64 -ldsound -ldinput -ldxguid endif ifneq ($(WITH_SPLASHES),yes) CPPFLAGS+= -DNO_SPLASHES endif ifeq ($(USE_CODEC_FLAC),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x64 endif ifeq ($(USE_CODEC_VORBIS),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x64 endif ifeq ($(USE_CODEC_MP3),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x64 endif ifeq ($(USE_CODEC_OPUS),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x64 endif ifeq ($(USE_CODEC_MIKMOD),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x64 endif ifeq ($(USE_CODEC_XMP),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x64 endif ifeq ($(USE_CODEC_MODPLUG),yes) CODEC_INC = -I$(OSLIBS)/windows/codecs/include CODEC_LINK= -L$(OSLIBS)/windows/codecs/x64 endif INCLUDES += $(CODEC_INC) LDFLAGS += $(CODEC_LINK) endif # End of Win64 settings ############################################################# ############################################################# # MorphOS flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),morphos) # MorphOS builds can use SDL or native code USE_SDL=no ifneq ($(USE_SDL),yes) USE_SDLCD=no USE_SDLAUDIO=no # no native cdaudio code yet USE_CDAUDIO=no endif # don't build the CAMD MIDI driver yet USE_MIDI=no CFLAGS += -noixemul LDFLAGS += -noixemul SYSLIBS += -lm INCLUDES += -I$(OSLIBS)/morphos/misc/include GL_LINK=-lGL # dynamic loading of ogl functions doesn't work LINK_GL_LIBS=yes USE_CODEC_FLAC=no USE_CODEC_VORBIS=yes USE_CODEC_MP3=yes USE_CODEC_OPUS=no USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no ifeq ($(USE_CODEC_FLAC),yes) CODEC_INC = -I$(OSLIBS)/morphos/codecs/include CODEC_LINK= -L$(OSLIBS)/morphos/codecs/lib endif ifeq ($(USE_CODEC_VORBIS),yes) CODEC_INC = -I$(OSLIBS)/morphos/codecs/include CODEC_LINK= -L$(OSLIBS)/morphos/codecs/lib endif ifeq ($(USE_CODEC_MP3),yes) CODEC_INC = -I$(OSLIBS)/morphos/codecs/include CODEC_LINK= -L$(OSLIBS)/morphos/codecs/lib endif ifeq ($(USE_CODEC_OPUS),yes) CODEC_INC = -I$(OSLIBS)/morphos/codecs/include CODEC_LINK= -L$(OSLIBS)/morphos/codecs/lib endif INCLUDES += $(CODEC_INC) LDFLAGS += $(CODEC_LINK) ifeq ($(USE_SOUND),yes) ifneq ($(USE_SDLAUDIO),yes) CPPFLAGS+= -DNO_SDL_AUDIO endif endif ifeq ($(USE_CDAUDIO),yes) ifeq ($(USE_SDLCD),yes) CPPFLAGS+= -DWITH_SDLCD endif endif ifeq ($(USE_SDL),yes) # SDLQUAKE must be defined for all SDL using platforms/targets CPPFLAGS+= -DSDLQUAKE CFLAGS += $(SDL_CFLAGS) LDFLAGS += $(SDL_LIBS) endif endif # End of MorphOS settings ############################################################# ############################################################# # AROS flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),aros) CFLAGS += -fno-common # AROS builds can use SDL or native code USE_SDL=no ifneq ($(USE_SDL),yes) USE_SDLCD=no USE_SDLAUDIO=no # no native cdaudio code yet USE_CDAUDIO=no # native ogl code need this: LINK_GL_LIBS=yes endif # don't build the CAMD MIDI driver yet USE_MIDI=no NASMFLAGS=-f elf -d_NO_PREFIX #USE_X86_ASM=no INCLUDES+= -I$(OSLIBS)/aros-$(MACH_TYPE)/misc/include # use the old AROSMesa api (default), or the new glA api USE_GLA=no ifeq ($(USE_GLA),yes) GL_DEFS += -DAROS_USE_GLA endif GL_LINK=-lGL USE_CODEC_FLAC=no USE_CODEC_OPUS=no USE_CODEC_MIKMOD=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no # both WildMidi and the local timidity work nice on AROS USE_CODEC_WILDMIDI=no USE_CODEC_TIMIDITY=yes ifeq ($(USE_CODEC_FLAC),yes) CODEC_INC = -I$(OSLIBS)/aros-$(MACH_TYPE)/codecs/include CODEC_LINK= -L$(OSLIBS)/aros-$(MACH_TYPE)/codecs/lib endif ifeq ($(USE_CODEC_MP3),yes) CODEC_INC = -I$(OSLIBS)/aros-$(MACH_TYPE)/codecs/include CODEC_LINK= -L$(OSLIBS)/aros-$(MACH_TYPE)/codecs/lib endif ifeq ($(USE_CODEC_VORBIS),yes) CODEC_INC = -I$(OSLIBS)/aros-$(MACH_TYPE)/codecs/include CODEC_LINK= -L$(OSLIBS)/aros-$(MACH_TYPE)/codecs/lib endif ifeq ($(USE_CODEC_OPUS),yes) CODEC_INC = -I$(OSLIBS)/aros-$(MACH_TYPE)/codecs/include CODEC_LINK= -L$(OSLIBS)/aros-$(MACH_TYPE)/codecs/lib endif INCLUDES += $(CODEC_INC) LDFLAGS += $(CODEC_LINK) ifeq ($(USE_SOUND),yes) ifneq ($(USE_SDLAUDIO),yes) CPPFLAGS+= -DNO_SDL_AUDIO endif endif ifeq ($(USE_CDAUDIO),yes) ifeq ($(USE_SDLCD),yes) CPPFLAGS+= -DWITH_SDLCD endif endif ifeq ($(USE_SDL),yes) # SDLQUAKE must be defined for all SDL using platforms/targets CPPFLAGS+= -DSDLQUAKE CFLAGS += $(SDL_CFLAGS) LDFLAGS += $(SDL_LIBS) endif endif # End of AROS settings ############################################################# ############################################################# # AmigaOS flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),amigaos) # use Bebbo's GCC6 toolchain BEBBO_TOOLCHAIN=yes # crt: libnix or clib2: USE_CLIB2=yes ifeq ($(BEBBO_TOOLCHAIN),yes) USE_CLIB2=no endif # Don't use SDL on AmigaOS USE_SDL=no ifneq ($(USE_SDL),yes) USE_SDLCD=no USE_SDLAUDIO=no endif ifndef DEBUG ifneq ($(BEBBO_TOOLCHAIN),yes) # -fomit-frame-pointer / -ffast-math causes trouble with gcc2.95 CFLAGS += -fno-omit-frame-pointer else # these break the game with bebbo's gcc-6.x: CFLAGS += -fbbb=- -fno-tree-forwprop -fno-tree-ter -fno-tree-fre -fno-tree-sra -fno-move-loop-invariants -fno-inline-functions-called-once endif endif ifeq ($(USE_CLIB2),yes) CRT_FLAGS=-mcrt=clib2 else CRT_FLAGS=-noixemul endif CFLAGS += $(CRT_FLAGS) -m68060 -m68881 LDFLAGS += $(CRT_FLAGS) -m68060 -m68881 SYSLIBS += -lm # for M68K assembly: AS=vasm ASFLAGS = -Faout -m68060 -phxass -quiet ifeq ($(USE_M68K_ASM),yes) CPPFLAGS+= -DUSE_M68K_ASM endif # for extra missing headers INCLUDES += -I$(OSLIBS)/amigaos/include ifneq ($(BEBBO_TOOLCHAIN),yes) # Roadshow SDK NET_INC = -I$(OSLIBS)/amigaos/netinclude endif # Build for which Amiga GL implementation: # Either amesa (StormMesa), or minigl (Hyperion's MiniGL 1.2) AMIGA_GLIMP=minigl ifeq ($(AMIGA_GLIMP),minigl) GL_DEFS+= -DREFGL_MINIGL #GL_DEFS+= -DUSE_MGLAPI=1 GL_DEFS+= -I$(OSLIBS)/amigaos/MiniGL/include GL_LINK = -L$(OSLIBS)/amigaos/MiniGL/lib -lmgl endif ifeq ($(AMIGA_GLIMP),amesa) GL_DEFS+= -DREFGL_AMESA GL_DEFS+= -I$(OSLIBS)/amigaos/StormMesa/include GL_LINK = -L$(OSLIBS)/amigaos/StormMesa/lib -lgl endif # dynamic loading of ogl functions doesn't work LINK_GL_LIBS=yes USE_CODEC_TIMIDITY=no USE_CODEC_FLAC=no USE_CODEC_MP3=no USE_CODEC_VORBIS=no USE_CODEC_OPUS=no USE_CODEC_MIKMOD=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no #use integer-only Tremor as Vorbis decoder for m68k-amigaos VORBISLIB=tremor ifeq ($(USE_CODEC_MP3),yes) CODEC_INC = -I$(OSLIBS)/amigaos/codecs/include CODEC_LINK= -L$(OSLIBS)/amigaos/codecs/lib endif ifeq ($(USE_CODEC_VORBIS),yes) CODEC_INC = -I$(OSLIBS)/amigaos/codecs/include CODEC_LINK= -L$(OSLIBS)/amigaos/codecs/lib endif INCLUDES += $(CODEC_INC) LDFLAGS += $(CODEC_LINK) ifeq ($(USE_SOUND),yes) ifneq ($(USE_SDLAUDIO),yes) CPPFLAGS+= -DNO_SDL_AUDIO endif endif ifeq ($(USE_CDAUDIO),yes) ifeq ($(USE_SDLCD),yes) CPPFLAGS+= -DWITH_SDLCD endif endif ifeq ($(USE_SDL),yes) # SDLQUAKE must be defined for all SDL using platforms/targets CPPFLAGS+= -DSDLQUAKE CFLAGS += $(SDL_CFLAGS) LDFLAGS += $(SDL_LIBS) endif endif # End of AmigaOS settings ############################################################# ############################################################# # Streaming music settings ############################################################# ifeq ($(MP3LIB),mad) mp3_obj=snd_mp3 lib_mp3dec=-lmad endif ifeq ($(MP3LIB),mpg123) mp3_obj=snd_mpg123 lib_mp3dec=-lmpg123 endif ifeq ($(VORBISLIB),vorbis) cpp_vorbisdec= lib_vorbisdec=-lvorbisfile -lvorbis -logg endif ifeq ($(VORBISLIB),tremor) cpp_vorbisdec=-DVORBIS_USE_TREMOR lib_vorbisdec=-lvorbisidec -logg endif ifeq ($(USE_CODEC_FLAC),yes) CPPFLAGS+= -DUSE_CODEC_FLAC LDFLAGS+= -lFLAC endif ifeq ($(USE_CODEC_WAVE),yes) CPPFLAGS+= -DUSE_CODEC_WAVE endif ifeq ($(USE_CODEC_OPUS),yes) CPPFLAGS+= -DUSE_CODEC_OPUS ifneq ($(TARGET_OS),unix) # we use pkg-config on unix LDFLAGS+= -lopusfile -lopus -logg endif endif ifeq ($(USE_CODEC_VORBIS),yes) CPPFLAGS+= -DUSE_CODEC_VORBIS $(cpp_vorbisdec) LDFLAGS+= $(lib_vorbisdec) endif ifeq ($(USE_CODEC_MP3),yes) CPPFLAGS+= -DUSE_CODEC_MP3 LDFLAGS+= $(lib_mp3dec) endif ifeq ($(USE_CODEC_MIKMOD),yes) CPPFLAGS+= -DUSE_CODEC_MIKMOD LDFLAGS+= -lmikmod endif ifeq ($(USE_CODEC_XMP),yes) CPPFLAGS+= -DUSE_CODEC_XMP LDFLAGS+= -lxmp endif ifeq ($(USE_CODEC_MODPLUG),yes) CPPFLAGS+= -DUSE_CODEC_MODPLUG LDFLAGS+= -lmodplug endif ifeq ($(USE_CODEC_UMX),yes) CPPFLAGS+= -DUSE_CODEC_UMX endif ifeq ($(USE_CODEC_TIMIDITY),yes) CPPFLAGS+= -DUSE_CODEC_TIMIDITY LDFLAGS+= -L$(LIBS_DIR)/timidity -ltimidity ifeq ($(TARGET_OS),os2) LIBTIMIDITY=timidity.a else LIBTIMIDITY=libtimidity.a endif TIMIDEPS=$(LIBS_DIR)/timidity/$(LIBTIMIDITY) else TIMIDEPS= endif ifeq ($(USE_CODEC_WILDMIDI),yes) CPPFLAGS+= -DUSE_CODEC_WILDMIDI LDFLAGS+= -lWildMidi endif ifeq ($(CODECS_USE_ZONE),yes) CPPFLAGS+=-DCODECS_USE_ZONE endif # End of streaming music settings ############################################################# ############################################################# # Finalize things after the system specific overrides: ############################################################# ifeq ($(USE_X86_ASM),yes) CPPFLAGS+= -DUSE_INTEL_ASM endif ifeq ($(USE_3DFXGAMMA),yes) # linux 3dfx gamma evil hack GL_DEFS+= -DUSE_3DFXGAMMA endif ifneq ($(LINK_GL_LIBS),yes) GL_DEFS+= -DGL_DLSYM else GL_LIBS+= $(GL_LINK) endif # ############################################################# # Rules for turning source files into .o files %.o: %.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(COMMON_HW)/%.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(COMMONDIR)/%.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(UHEXEN2_SHARED)/%.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: %.m $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(COMMONDIR)/%.m $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(UHEXEN2_SHARED)/%.m $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: %.asm $(NASM) $(NASM_INC) $(NASMFLAGS) -o $@ $< %.o: $(COMMON_HW)/%.asm $(NASM) $(NASM_INC) $(NASMFLAGS) -o $@ $< %.o: $(COMMONDIR)/%.asm $(NASM) $(NASM_INC) $(NASMFLAGS) -o $@ $< %.res: %.rc $(WINDRES) $(RC_DEFS) $(RC_INC) $(RCFLAGS) -o $@ $< %.res: $(COMMON_HW)/%.rc $(WINDRES) $(RC_DEFS) $(RC_INC) $(RCFLAGS) -o $@ $< %.res: $(COMMONDIR)/%.rc $(WINDRES) $(RC_DEFS) $(RC_INC) $(RCFLAGS) -o $@ $< ifeq ($(TARGET_OS),amigaos) #%.o: %.s # $(AS) $(ASFLAGS) -o $@ $< %.o: $(COMMONDIR)/%.s $(AS) $(ASFLAGS) -o $@ $< %.o: $(UHEXEN2_SHARED)/%.s $(AS) $(ASFLAGS) -o $@ $< endif # Objects # Intel asm objects ifeq ($(USE_X86_ASM),yes) COMMON_ASM= math.o \ sys_ia32.o SOFT_ASM = \ d_draw.o \ d_draw16.o \ d_draw16t.o \ d_parta.o \ d_partb.o \ d_polysa.o \ d_polysa2.o \ d_polysa3.o \ d_polysa4.o \ d_polysa5.o \ d_scana.o \ d_spr8.o \ d_spr8t.o \ d_spr8t2.o \ d_varsa.o \ r_aclipa.o \ r_aliasa.o \ r_drawa.o \ r_edgea.o \ r_edgeb.o \ r_varsa.o \ surf8.o \ surf16.o SOUND_ASM = snd_mixa.o else # M68K asm objects ifeq ($(USE_M68K_ASM),yes) SOFT_ASM = \ amiga_r_68k.o \ d_part68k.o \ d_polyse68k.o \ d_scan68k.o \ d_sky68k.o \ d_sprite68k.o \ r_aclip68k.o \ r_alias68k.o \ r_bsp68k.o \ r_edge68k.o \ r_sky68k.o \ r_surf68k.o COMMON_ASM = swap_68k.o SOUND_ASM = snd_mixamiga68k.o else SOFT_ASM = COMMON_ASM = SOUND_ASM = endif endif # Sound objects ifneq ($(USE_SOUND),yes) MUSIC_OBJS:= bgmnull.o SOUND_ASM := CPPFLAGS += -D_NO_SOUND SYSOBJ_SND := COMOBJ_SND := snd_null.o $(MUSIC_OBJS) else MUSIC_OBJS:= bgmusic.o \ snd_codec.o \ snd_flac.o \ snd_wave.o \ snd_vorbis.o \ snd_opus.o \ $(mp3_obj).o \ snd_mp3tag.o \ snd_mikmod.o \ snd_modplug.o \ snd_xmp.o \ snd_umx.o \ snd_timidity.o \ snd_wildmidi.o COMOBJ_SND := snd_sys.o snd_dma.o snd_mix.o $(SOUND_ASM) snd_mem.o $(MUSIC_OBJS) ifeq ($(TARGET_OS),win32) SYSOBJ_SND := snd_win.o snd_dsound.o endif ifeq ($(TARGET_OS),win64) SYSOBJ_SND := snd_win.o snd_dsound.o endif ifeq ($(TARGET_OS),dos) SYSOBJ_SND := snd_sb.o snd_gus.o snd_pci.o endif ifeq ($(TARGET_OS),os2) SYSOBJ_SND := snd_sdl.o endif ifeq ($(TARGET_OS),unix) SYSOBJ_SND := snd_oss.o snd_alsa.o snd_sun.o snd_sdl.o endif ifeq ($(TARGET_OS),darwin) SYSOBJ_SND := snd_sdl.o endif ifeq ($(TARGET_OS),aros) SYSOBJ_SND := snd_ahi.o snd_sdl.o endif ifeq ($(TARGET_OS),morphos) SYSOBJ_SND := snd_ahi.o snd_sdl.o endif ifeq ($(TARGET_OS),amigaos) SYSOBJ_SND := snd_ahi.o snd_paula.o snd_sdl.o endif # end of Sound objects endif # MIDI objects ifneq ($(USE_MIDI),yes) SYSOBJ_MIDI:= midi_nul.o CPPFLAGS += -D_NO_MIDIDRV else ifeq ($(TARGET_OS),win32) SYSOBJ_MIDI:= midi_win.o mid2strm.o endif ifeq ($(TARGET_OS),win64) SYSOBJ_MIDI:= midi_win.o mid2strm.o endif ifeq ($(TARGET_OS),dos) SYSOBJ_MIDI:= midi_nul.o CPPFLAGS += -D_NO_MIDIDRV endif ifeq ($(TARGET_OS),os2) SYSOBJ_MIDI:= midi_nul.o CPPFLAGS += -D_NO_MIDIDRV endif ifeq ($(TARGET_OS),unix) SYSOBJ_MIDI:= midi_nul.o CPPFLAGS += -D_NO_MIDIDRV endif ifeq ($(TARGET_OS),darwin) # not using midi_mac.c SYSOBJ_MIDI:= midi_osx.o endif ifeq ($(TARGET_OS),aros) SYSOBJ_MIDI:= midi_camd.o endif ifeq ($(TARGET_OS),morphos) SYSOBJ_MIDI:= midi_camd.o endif ifeq ($(TARGET_OS),amigaos) SYSOBJ_MIDI:= midi_camd.o endif # end of MIDI objects endif # CDAudio objects ifneq ($(USE_CDAUDIO),yes) SYSOBJ_CDA:= cd_null.o CPPFLAGS += -D_NO_CDAUDIO else ifeq ($(TARGET_OS),win32) SYSOBJ_CDA := cd_win.o endif ifeq ($(TARGET_OS),win64) SYSOBJ_CDA := cd_win.o endif ifeq ($(TARGET_OS),dos) SYSOBJ_CDA := cd_dos.o endif ifeq ($(TARGET_OS),os2) SYSOBJ_CDA := cd_sdl.o endif ifeq ($(TARGET_OS),unix) SYSOBJ_CDA := cd_sdl.o cd_bsd.o cd_linux.o endif ifeq ($(TARGET_OS),darwin) SYSOBJ_CDA := cd_sdl.o endif ifeq ($(TARGET_OS),aros) SYSOBJ_CDA := cd_sdl.o endif ifeq ($(TARGET_OS),morphos) SYSOBJ_CDA := cd_sdl.o endif ifeq ($(TARGET_OS),amigaos) SYSOBJ_CDA := cd_amiga.o endif # end of CDAudio objects endif # Other platform specific object settings ifeq ($(TARGET_OS),win32) SYSOBJ_INPUT := in_win.o SYSOBJ_GL_VID:= gl_vidnt.o SYSOBJ_SOFT_VID:= vid_win.o SYSOBJ_SYS := sys_win.o win32res.res endif ifeq ($(TARGET_OS),win64) SYSOBJ_INPUT := in_win.o SYSOBJ_GL_VID:= gl_vidnt.o SYSOBJ_SOFT_VID:= vid_win.o SYSOBJ_SYS := sys_win.o win32res.res endif ifeq ($(TARGET_OS),dos) SYSOBJ_INPUT := in_dos.o SYSOBJ_SOFT_VID:= vid_vga.o vid_ext.o vid_dos.o vregset.o SYSOBJ_GL_VID:= dos_dmesa.o dos_fxmesa.o dos_sage.o gl_viddos.o ifeq ($(SYSOBJ_GL_DXE),yes) SYSOBJ_GL_VID+= dxe.o else SYSOBJ_GL_VID+= no_dxe.o endif ifeq ($(USE_3DFXGAMMA),yes) SYSOBJ_GL_VID+= fx_gamma.o endif ifeq ($(USE_X86_ASM),yes) SYSOBJ_SOFT_VID+= d_copy.o endif ifeq ($(SYSOBJ_SOFT_DXE),yes) SYSOBJ_SOFT_VID+= dxe.o else SYSOBJ_SOFT_VID+= no_dxe.o endif SYSOBJ_SYS := dos_v2.o sys_dos.o # dosasm.o endif ifeq ($(TARGET_OS),os2) SYSOBJ_INPUT := in_sdl.o SYSOBJ_GL_VID:= gl_vidsdl.o SYSOBJ_SOFT_VID:= vid_sdl.o SYSOBJ_SYS := sys_os2.o SYSOBJ_SYS += sys_sdl.o endif ifeq ($(TARGET_OS),unix) # unix clients use SDL: SYSOBJ_INPUT := in_sdl.o SYSOBJ_GL_VID:= gl_vidsdl.o ifeq ($(USE_3DFXGAMMA),yes) SYSOBJ_GL_VID+= fx_gamma.o endif SYSOBJ_SOFT_VID:= vid_sdl.o SYSOBJ_SYS := sys_unix.o SYSOBJ_SYS += sys_sdl.o endif ifeq ($(TARGET_OS),darwin) # OSX clients use SDL: SYSOBJ_INPUT := in_sdl.o SYSOBJ_GL_VID:= gl_vidsdl.o SYSOBJ_SOFT_VID:= vid_sdl.o SYSOBJ_SYS := sys_unix.o SYSOBJ_SYS += sys_osx.o SYSOBJ_SYS += SDLMain.o endif ifeq ($(TARGET_OS),aros) ifeq ($(USE_SDL),yes) SYSOBJ_INPUT := in_sdl.o SYSOBJ_GL_VID:= gl_vidsdl.o SYSOBJ_SOFT_VID:= vid_sdl.o else SYSOBJ_INPUT := in_amiga.o SYSOBJ_GL_VID:= gl_vidamiga.o SYSOBJ_SOFT_VID:= vid_cgx.o endif SYSOBJ_SYS := sys_amiga.o endif ifeq ($(TARGET_OS),morphos) ifeq ($(USE_SDL),yes) SYSOBJ_INPUT := in_sdl.o SYSOBJ_GL_VID:= gl_vidsdl.o SYSOBJ_SOFT_VID:= vid_sdl.o else SYSOBJ_INPUT := in_amiga.o SYSOBJ_GL_VID:= gl_vidamiga.o SYSOBJ_SOFT_VID:= vid_cgx.o endif SYSOBJ_SYS := sys_amiga.o endif ifeq ($(TARGET_OS),amigaos) ifeq ($(USE_SDL),yes) SYSOBJ_INPUT := in_sdl.o SYSOBJ_GL_VID:= gl_vidsdl.o SYSOBJ_SOFT_VID:= vid_sdl.o else SYSOBJ_INPUT := in_amiga.o SYSOBJ_GL_VID:= gl_vidamiga.o SYSOBJ_SOFT_VID:= vid_cgx.o ifeq ($(USE_M68K_ASM),yes) CPPFLAGS+= -DUSE_C2P SYSOBJ_SOFT_VID+= c2p1x1_8_c5_bm.o c2p1x1_8_c5_bm_040.o ## c2p1x1_8_c5_030.o c2p1x1_8_c5_040.o endif endif SYSOBJ_SYS := sys_amiga.o endif # Final list of objects SOFTOBJS = \ d_edge.o \ d_fill.o \ d_init.o \ d_modech.o \ d_part.o \ d_polyse.o \ d_scan.o \ d_sky.o \ d_sprite.o \ d_surf.o \ d_vars.o \ d_zpoint.o \ r_aclip.o \ r_alias.o \ r_bsp.o \ r_draw.o \ r_edge.o \ r_efrag.o \ r_light.o \ r_main.o \ r_misc.o \ r_part.o \ r_sky.o \ r_sprite.o \ r_surf.o \ r_vars.o \ screen.o \ $(SYSOBJ_SOFT_VID) \ draw.o \ model.o GLOBJS = \ gl_refrag.o \ gl_rlight.o \ gl_rmain.o \ gl_rmisc.o \ gl_ngraph.o \ r_part.o \ gl_rsurf.o \ gl_screen.o \ gl_warp.o \ $(SYSOBJ_GL_VID) \ gl_draw.o \ gl_mesh.o \ gl_model.o COMMONOBJS = \ $(SYSOBJ_INPUT) \ $(COMOBJ_SND) \ $(SYSOBJ_SND) \ $(SYSOBJ_CDA) \ $(SYSOBJ_MIDI) \ huffman.o \ net_udp.o \ net_chan.o \ cl_cam.o \ cl_demo.o \ cl_effect.o \ cl_ents.o \ cl_inlude.o \ cl_input.o \ cl_main.o \ cl_parse.o \ cl_pred.o \ cl_tent.o \ cl_cmd.o \ console.o \ keys.o \ menu.o \ sbar.o \ skin.o \ view.o \ wad.o \ cmd.o \ q_endian.o \ link_ops.o \ sizebuf.o \ strlcat.o \ strlcpy.o \ qsnprint.o \ msg_io.o \ common.o \ debuglog.o \ quakefs.o \ info_str.o \ crc.o \ cvar.o \ cfgfile.o \ host_string.o \ mathlib.o \ pmove.o \ pmovetst.o \ zone.o \ hashindex.o \ $(SYSOBJ_SYS) # Targets .PHONY: help clean distclean localclean report $(TIMIDEPS) #default: glhw #all: default help: report @echo @echo "Valid targets: (read/edit the makefile for several options)" @echo @echo "* $(MAKE) help : this help" @echo "* $(MAKE) clean : delete all files produced by the build" @echo "* $(MAKE) hw : hexenworld client, software renderer" @echo "* $(MAKE) glhw : hexenworld client, opengl renderer" @echo hw: $(SW_BINARY) glhw: $(GL_BINARY) $(GL_BINARY): CPPFLAGS+= $(GL_DEFS) ifeq ($(TARGET_OS),amigaos) # workaround stupid AmiTCP SDK mess for old aos3 net_udp.o: INCLUDES+= $(NET_INC) endif # extra rule for snd_timidity.c because of timidity.h and libtimidity.a snd_timidity.o: $(COMMONDIR)/snd_timidity.c $(CC) -c $(INCLUDES) -I$(LIBS_DIR)/timidity $(CPPFLAGS) $(CFLAGS) -DTIMIDITY_STATIC=1 -o $@ $(COMMONDIR)/snd_timidity.c include $(LIBS_DIR)/timidity/_timi.mak $(SW_BINARY): $(SOFT_ASM) $(SOFTOBJS) $(COMMON_ASM) $(COMMONOBJS) $(TIMIDEPS) $(LINKER) $(SOFT_ASM) $(SOFTOBJS) $(COMMON_ASM) $(COMMONOBJS) $(LDFLAGS) $(SYSLIBS) -o $@ $(GL_BINARY): $(GLOBJS) $(COMMON_ASM) $(COMMONOBJS) $(TIMIDEPS) $(LINKER) $(GLOBJS) $(COMMON_ASM) $(COMMONOBJS) $(LDFLAGS) $(GL_LIBS) $(SYSLIBS) -o $@ localclean: rm -f *.o *.res core clean: localclean timi_clean distclean: clean rm -f $(SW_BINARY) $(GL_BINARY) report: @echo "Host OS :" $(HOST_OS) @echo "Target OS:" $(TARGET_OS) @echo "Machine :" $(MACH_TYPE) engine/hexenworld/client/Makefile.os2000066400000000000000000000227621444734033100201360ustar00rootroot00000000000000# makefile to build hw.exe for OS/2 using Open Watcom: # wmake -f Makefile.os2 # # to build opengl version glhw.exe : # wmake -f Makefile.os2 BUILDGL=1 # PATH SETTINGS: !ifndef __UNIX__ PATH_SEP=\ UHEXEN2_TOP=..\..\.. ENGINE_TOP=..\.. HW_TOP=.. COMMONDIR=$(ENGINE_TOP)\h2shared COMMON_HW=$(HW_TOP)\shared UHEXEN2_SHARED=$(UHEXEN2_TOP)\common LIBS_DIR=$(UHEXEN2_TOP)\libs OSLIBS=$(UHEXEN2_TOP)\oslibs !else PATH_SEP=/ UHEXEN2_TOP=../../.. ENGINE_TOP=../.. HW_TOP=.. COMMONDIR=$(ENGINE_TOP)/h2shared COMMON_HW=$(HW_TOP)/shared UHEXEN2_SHARED=$(UHEXEN2_TOP)/common LIBS_DIR=$(UHEXEN2_TOP)/libs OSLIBS=$(UHEXEN2_TOP)/oslibs !endif # GENERAL OPTIONS (customize as required) # link to the opengl libraries at compile time? (defaults # to no, so the binaries will dynamically load the necessary # libraries and functions at runtime.) LINK_GL_LIBS=yes # use fast x86 assembly on ia32 machines? (auto-disabled for # any other cpu.) USE_X86_ASM=yes # enable sound support? USE_SOUND=yes # include target's MIDI driver if available? USE_MIDI=yes # CDAudio support? USE_CDAUDIO=yes # Enable/disable codecs for streaming music support: USE_CODEC_WAVE=yes USE_CODEC_FLAC=no USE_CODEC_MP3=yes USE_CODEC_VORBIS=yes USE_CODEC_OPUS=no # either xmp or mikmod (or modplug) USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no # either timidity (preferred) or wildmidi (both possible # but not needed nor meaningful) USE_CODEC_TIMIDITY=yes USE_CODEC_WILDMIDI=no # which library to use for mp3 decoding: mad or mpg123 MP3LIB=mad # which library to use for ogg decoding: vorbis or tremor VORBISLIB=tremor # Names of the binaries SW_BINARY=hwcl.exe GL_BINARY=glhwcl.exe ############################################################# # Compiler flags ############################################################# CFLAGS = -zq -wx -bm -bt=os2 -5s -sg -otexan -fp5 -fpi87 -ei -j -zp8 # newer OpenWatcom versions enable W303 by default CFLAGS+= -wcd=303 # compiler includes INCLUDES= -I. -I$(COMMON_HW) -I$(COMMONDIR) -I$(UHEXEN2_SHARED) # nasm includes: the trailing directory separator matters NASM_INC= -I.$(PATH_SEP) -I$(COMMON_HW)$(PATH_SEP) -I$(COMMONDIR)$(PATH_SEP) # end of compiler flags ############################################################# ############################################################# # Other build flags ############################################################# CPPFLAGS+= -DH2W !ifdef DEMO CPPFLAGS+= -DDEMOBUILD !endif !ifdef DEBUG CFLAGS += -d2 # This activates some extra code in hexen2/hexenworld C source CPPFLAGS+= -DDEBUG=1 -DDEBUG_BUILD=1 !endif ############################################################# # OpenGL settings ############################################################# GL_DEFS = -DGLQUAKE GL_LIBS = ############################################################# # streaming music initial setup ############################################################# !ifneq USE_SOUND yes USE_CODEC_WAVE=no USE_CODEC_FLAC=no USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no USE_CODEC_MP3=no USE_CODEC_VORBIS=no USE_CODEC_OPUS=no !endif ############################################################# # OS/2 flags/settings and overrides: ############################################################# NASMFLAGS=-f obj -d__OS2__ -d_NO_PREFIX !ifndef __UNIX__ INCLUDES+= -I$(OSLIBS)\os2\codecs\include CODECLIBS= $(OSLIBS)\os2\codecs\lib\ SDL_CFLAGS=-I$(OSLIBS)\os2\SDL\include SDL_LIBS = $(OSLIBS)\os2\SDL\lib\SDL12.lib !else INCLUDES+= -I$(OSLIBS)/os2/codecs/include CODECLIBS= $(OSLIBS)/os2/codecs/lib/ SDL_CFLAGS=-I$(OSLIBS)/os2/SDL/include SDL_LIBS = $(OSLIBS)/os2/SDL/lib/SDL12.lib !endif # use SDL for now CPPFLAGS+= -DSDLQUAKE CFLAGS += $(SDL_CFLAGS) LIBS += $(SDL_LIBS) GL_LINK=opengl.lib ############################################################# # Streaming music settings ############################################################# !ifeq MP3LIB mad mp3_obj=snd_mp3 lib_mp3dec=$(CODECLIBS)mad.lib !endif !ifeq MP3LIB mpg123 mp3_obj=snd_mpg123 lib_mp3dec=$(CODECLIBS)mpg123.lib !endif !ifeq VORBISLIB vorbis cpp_vorbisdec= lib_vorbisdec=$(CODECLIBS)vorbisfile.lib $(CODECLIBS)vorbis.lib $(CODECLIBS)ogg.lib !endif !ifeq VORBISLIB tremor cpp_vorbisdec=-DVORBIS_USE_TREMOR lib_vorbisdec=$(CODECLIBS)vorbisidec.lib $(CODECLIBS)ogg.lib !endif !ifeq USE_CODEC_FLAC yes CPPFLAGS+= -DUSE_CODEC_FLAC LIBS += $(CODECLIBS)FLAC.lib !endif !ifeq USE_CODEC_WAVE yes CPPFLAGS+= -DUSE_CODEC_WAVE !endif !ifeq USE_CODEC_OPUS yes CPPFLAGS+= -DUSE_CODEC_OPUS LIBS += $(CODECLIBS)opusfile.lib $(CODECLIBS)opus.lib !ifneq USE_CODEC_VORBIS yes LIBS += $(CODECLIBS)ogg.lib !endif !endif !ifeq USE_CODEC_VORBIS yes CPPFLAGS+= -DUSE_CODEC_VORBIS $(cpp_vorbisdec) LIBS += $(lib_vorbisdec) !endif !ifeq USE_CODEC_MP3 yes CPPFLAGS+= -DUSE_CODEC_MP3 LIBS += $(lib_mp3dec) !endif !ifeq USE_CODEC_MIKMOD yes CPPFLAGS+= -DUSE_CODEC_MIKMOD LIBS += $(CODECLIBS)mikmod.lib !endif !ifeq USE_CODEC_XMP yes CPPFLAGS+= -DUSE_CODEC_XMP LIBS += $(CODECLIBS)libxmp.lib !endif !ifeq USE_CODEC_MODPLUG yes CPPFLAGS+= -DUSE_CODEC_MODPLUG LIBS += $(CODECLIBS)modplug.lib !endif !ifeq USE_CODEC_UMX yes CPPFLAGS+= -DUSE_CODEC_UMX !endif !ifeq USE_CODEC_TIMIDITY yes CPPFLAGS+= -DUSE_CODEC_TIMIDITY LIBS += $(CODECLIBS)timidity.lib !endif !ifeq USE_CODEC_WILDMIDI yes CPPFLAGS+= -DUSE_CODEC_WILDMIDI LIBS += $(CODECLIBS)WildMidi.lib !endif # End of streaming music settings ############################################################# ############################################################# # Finalize things after the system specific overrides: ############################################################# !ifeq USE_X86_ASM yes CPPFLAGS+= -DUSE_INTEL_ASM !endif !ifneq LINK_GL_LIBS yes GL_DEFS+= -DGL_DLSYM !else GL_LIBS+= $(GL_LINK) !endif !ifndef BUILDGL BUILD_TARGET=$(SW_BINARY) !else CPPFLAGS+= $(GL_DEFS) BUILD_TARGET=$(GL_BINARY) !endif # ############################################################# .c: $(COMMON_HW);$(COMMONDIR);$(UHEXEN2_SHARED) .asm: $(COMMON_HW);$(COMMONDIR) .c.obj: wcc386 $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -fo=$^@ $< .asm.obj: nasm $(NASM_INC) $(NASMFLAGS) -o $^@ $< # Objects # Intel asm objects !ifeq USE_X86_ASM yes COMMON_ASM= math.obj & sys_ia32.obj SOFT_ASM = & d_draw.obj & d_draw16.obj & d_draw16t.obj & d_parta.obj & d_partb.obj & d_polysa.obj & d_polysa2.obj & d_polysa3.obj & d_polysa4.obj & d_polysa5.obj & d_scana.obj & d_spr8.obj & d_spr8t.obj & d_spr8t2.obj & d_varsa.obj & r_aclipa.obj & r_aliasa.obj & r_drawa.obj & r_edgea.obj & r_edgeb.obj & r_varsa.obj & surf8.obj & surf16.obj SOUND_ASM = snd_mixa.obj !else SOFT_ASM = COMMON_ASM = SOUND_ASM = !endif # Sound objects !ifneq USE_SOUND yes MUSIC_OBJS= bgmnull.obj SOUND_ASM = CPPFLAGS += -D_NO_SOUND SYSOBJ_SND = COMOBJ_SND = snd_null.obj $(MUSIC_OBJS) !else MUSIC_OBJS= bgmusic.obj & snd_codec.obj & snd_flac.obj & snd_wave.obj & snd_vorbis.obj & snd_opus.obj & $(mp3_obj).obj & snd_mp3tag.obj & snd_mikmod.obj & snd_modplug.obj & snd_xmp.obj & snd_umx.obj & snd_timidity.obj & snd_wildmidi.obj COMOBJ_SND = snd_sys.obj snd_dma.obj snd_mix.obj $(SOUND_ASM) snd_mem.obj $(MUSIC_OBJS) SYSOBJ_SND = snd_sdl.obj !endif !ifneq USE_MIDI yes SYSOBJ_MIDI= midi_nul.obj CPPFLAGS += -D_NO_MIDIDRV !else # FIXME: need to cook something. SYSOBJ_MIDI= midi_nul.obj CPPFLAGS += -D_NO_MIDIDRV !endif # CDAudio objects !ifneq USE_CDAUDIO yes SYSOBJ_CDA= cd_null.obj CPPFLAGS += -D_NO_CDAUDIO !else CPPFLAGS+= -DWITH_SDLCD SYSOBJ_CDA = cd_sdl.obj !endif SYSOBJ_INPUT = in_sdl.obj SYSOBJ_GL_VID= gl_vidsdl.obj SYSOBJ_SOFT_VID= vid_sdl.obj SYSOBJ_SYS = sys_os2.obj # Final list of objects SOFTOBJS = & d_edge.obj & d_fill.obj & d_init.obj & d_modech.obj & d_part.obj & d_polyse.obj & d_scan.obj & d_sky.obj & d_sprite.obj & d_surf.obj & d_vars.obj & d_zpoint.obj & r_aclip.obj & r_alias.obj & r_bsp.obj & r_draw.obj & r_edge.obj & r_efrag.obj & r_light.obj & r_main.obj & r_misc.obj & r_part.obj & r_sky.obj & r_sprite.obj & r_surf.obj & r_vars.obj & screen.obj & $(SYSOBJ_SOFT_VID) & draw.obj & model.obj GLOBJS = & gl_refrag.obj & gl_rlight.obj & gl_rmain.obj & gl_rmisc.obj & gl_ngraph.obj & r_part.obj & gl_rsurf.obj & gl_screen.obj & gl_warp.obj & $(SYSOBJ_GL_VID) & gl_draw.obj & gl_mesh.obj & gl_model.obj COMMONOBJS = & $(SYSOBJ_INPUT) & $(COMOBJ_SND) & $(SYSOBJ_SND) & $(SYSOBJ_CDA) & $(SYSOBJ_MIDI) & huffman.obj & net_udp.obj & net_chan.obj & cl_cam.obj & cl_demo.obj & cl_effect.obj & cl_ents.obj & cl_inlude.obj & cl_input.obj & cl_main.obj & cl_parse.obj & cl_pred.obj & cl_tent.obj & cl_cmd.obj & console.obj & keys.obj & menu.obj & sbar.obj & skin.obj & view.obj & wad.obj & cmd.obj & q_endian.obj & link_ops.obj & sizebuf.obj & strlcat.obj & strlcpy.obj & qsnprint.obj & msg_io.obj & common.obj & debuglog.obj & quakefs.obj & info_str.obj & crc.obj & cvar.obj & cfgfile.obj & host_string.obj & mathlib.obj & pmove.obj & pmovetst.obj & zone.obj & hashindex.obj & $(SYSOBJ_SYS) all: $(BUILD_TARGET) # 1 MB stack size. $(SW_BINARY): $(SOFT_ASM) $(SOFTOBJS) $(COMMON_ASM) $(COMMONOBJS) wlink N $@ SYS OS2V2 OPTION q OPTION STACK=0x100000 LIBR {$(LIBS)} F {$(SOFT_ASM) $(SOFTOBJS) $(COMMON_ASM) $(COMMONOBJS)} # 1 MB stack size. $(GL_BINARY): $(GLOBJS) $(COMMON_ASM) $(COMMONOBJS) wlink N $@ SYS OS2V2 OPTION q OPTION STACK=0x100000 LIBR {$(LIBS) $(GL_LIBS)} F {$(GLOBJS) $(COMMON_ASM) $(COMMONOBJS)} clean: .symbolic rm -f *.obj *.res *.err distclean: clean .symbolic rm -f $(SW_BINARY) $(GL_BINARY) engine/hexenworld/client/Makefile.svga000066400000000000000000000246101444734033100203650ustar00rootroot00000000000000# GNU Makefile for hexenworld svgalib-client using GCC. # # To use a compiler other than gcc: make CC=compiler_name [other stuff] # To use a different nasm-compatible assembler, such as yasm: # make NASM=yasm [other stuff] # To build for the demo version: make DEMO=1 [other stuff] # To build a debug version: make DEBUG=1 [other stuff] # # PATH SETTINGS: UHEXEN2_TOP:=../../.. ENGINE_TOP:=../.. HW_TOP:=.. COMMONDIR:=$(ENGINE_TOP)/h2shared COMMON_HW:=$(HW_TOP)/shared UHEXEN2_SHARED:=$(UHEXEN2_TOP)/common LIBS_DIR:=$(UHEXEN2_TOP)/libs OSLIBS:=$(UHEXEN2_TOP)/oslibs # GENERAL OPTIONS (customize as required) # use fast x86 assembly on ia32 machines? (auto-disabled for # any other cpu.) USE_X86_ASM=yes # enable sound support? USE_SOUND=yes # ALSA audio support? (req: alsa-lib and alsa-kernel modules # >= 1.0.1. v0.9.8 and v1.0.0 might work, but not supported. # If not Linux, ALSA will be automatically be disabled.) USE_ALSA=yes # OSS audio support? (for Unix. enabled on Linux and FreeBSD. # automatically disabled on other platforms: see snd_sys.h) USE_OSS=yes # include target's MIDI driver if available? USE_MIDI=no # CDAudio support? USE_CDAUDIO=yes # Enable/disable codecs for streaming music support: USE_CODEC_WAVE=yes USE_CODEC_FLAC=no USE_CODEC_MP3=yes USE_CODEC_VORBIS=yes USE_CODEC_OPUS=no # either xmp or mikmod (or modplug) USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no # either timidity (preferred) or wildmidi (both possible # but not needed nor meaningful) USE_CODEC_TIMIDITY=yes USE_CODEC_WILDMIDI=no # which library to use for mp3 decoding: mad or mpg123 MP3LIB=mad # which library to use for ogg decoding: vorbis or tremor VORBISLIB=vorbis # whether the codecs allocate on the zone instead of # system memory (set to yes for DOS builds, for example) CODECS_USE_ZONE=no # include the common dirty stuff include $(UHEXEN2_TOP)/scripts/makefile.inc # Names of the binaries SW_BINARY:=hwcl.svga ############################################################# # Compiler flags ############################################################# ifeq ($(MACH_TYPE),x86) CPU_X86=-march=i586 endif # Overrides for the default CPUFLAGS CPUFLAGS=$(CPU_X86) CFLAGS += -Wall CFLAGS += $(CPUFLAGS) ifdef DEBUG CFLAGS += -g else # optimization flags CFLAGS += -O2 -DNDEBUG=1 -ffast-math # NOTE: -fomit-frame-pointer is broken with ancient gcc versions!! CFLAGS += -fomit-frame-pointer endif CPPFLAGS= LDFLAGS = # linkage may be sensitive to order: add SYSLIBS after all others. SYSLIBS = # compiler includes INCLUDES= -I. -I$(COMMON_HW) -I$(COMMONDIR) -I$(UHEXEN2_SHARED) # nasm includes: the trailing directory separator matters NASM_INC= -I./ -I$(COMMON_HW)/ -I$(COMMONDIR)/ # end of compiler flags ############################################################# ############################################################# # Other build flags ############################################################# CPPFLAGS+= -DH2W # disable x86 assembly if it is not an x86. ifneq ($(MACH_TYPE),x86) USE_X86_ASM=no endif ifdef DEMO CPPFLAGS+= -DDEMOBUILD endif ifdef DEBUG # This activates some extra code in hexen2/hexenworld C source CPPFLAGS+= -DDEBUG=1 -DDEBUG_BUILD=1 endif ############################################################# # streaming music initial setup ############################################################# ifneq ($(USE_SOUND),yes) USE_CODEC_WAVE=no USE_CODEC_FLAC=no USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no USE_CODEC_MP3=no USE_CODEC_VORBIS=no USE_CODEC_OPUS=no endif # sanity checking for decoder library options ifneq ($(VORBISLIB),vorbis) ifneq ($(VORBISLIB),tremor) $(error Invalid VORBISLIB setting) endif endif ifneq ($(MP3LIB),mpg123) ifneq ($(MP3LIB),mad) $(error Invalid MP3LIB setting) endif endif ############################################################# # Unix flags/settings and overrides: ############################################################# NASMFLAGS=-f elf -d_NO_PREFIX SYSLIBS += -lvga -lm CPPFLAGS+= -DSVGAQUAKE ifeq ($(USE_SOUND),yes) ifneq ($(HOST_OS),linux) # alsa is only for linux USE_ALSA=no endif ifneq ($(USE_ALSA),yes) CPPFLAGS+= -DNO_ALSA_AUDIO else # snd_alsa uses dlopen() & co. SYSLIBS += -ldl endif ifneq ($(USE_OSS),yes) CPPFLAGS+= -DNO_OSS_AUDIO endif CPPFLAGS+= -DNO_SUN_AUDIO CPPFLAGS+= -DNO_SDL_AUDIO endif ifeq ($(USE_CODEC_OPUS),yes) # opus and opusfile put their *.h under /opus, # but they include the headers without the opus directory # prefix and rely on pkg-config. ewww... INCLUDES+= $(shell pkg-config --cflags opusfile) LDFLAGS += $(shell pkg-config --libs opusfile) endif # End of Unix settings ############################################################# ############################################################# # Streaming music settings ############################################################# ifeq ($(MP3LIB),mad) mp3_obj=snd_mp3 lib_mp3dec=-lmad endif ifeq ($(MP3LIB),mpg123) mp3_obj=snd_mpg123 lib_mp3dec=-lmpg123 endif ifeq ($(VORBISLIB),vorbis) cpp_vorbisdec= lib_vorbisdec=-lvorbisfile -lvorbis -logg endif ifeq ($(VORBISLIB),tremor) cpp_vorbisdec=-DVORBIS_USE_TREMOR lib_vorbisdec=-lvorbisidec -logg endif ifeq ($(USE_CODEC_FLAC),yes) CPPFLAGS+= -DUSE_CODEC_FLAC LDFLAGS+= -lFLAC endif ifeq ($(USE_CODEC_WAVE),yes) CPPFLAGS+= -DUSE_CODEC_WAVE endif ifeq ($(USE_CODEC_OPUS),yes) CPPFLAGS+= -DUSE_CODEC_OPUS endif ifeq ($(USE_CODEC_VORBIS),yes) CPPFLAGS+= -DUSE_CODEC_VORBIS $(cpp_vorbisdec) LDFLAGS+= $(lib_vorbisdec) endif ifeq ($(USE_CODEC_MP3),yes) CPPFLAGS+= -DUSE_CODEC_MP3 LDFLAGS+= $(lib_mp3dec) endif ifeq ($(USE_CODEC_MIKMOD),yes) CPPFLAGS+= -DUSE_CODEC_MIKMOD LDFLAGS+= -lmikmod endif ifeq ($(USE_CODEC_XMP),yes) CPPFLAGS+= -DUSE_CODEC_XMP LDFLAGS+= -lxmp endif ifeq ($(USE_CODEC_MODPLUG),yes) CPPFLAGS+= -DUSE_CODEC_MODPLUG LDFLAGS+= -lmodplug endif ifeq ($(USE_CODEC_UMX),yes) CPPFLAGS+= -DUSE_CODEC_UMX endif ifeq ($(USE_CODEC_TIMIDITY),yes) CPPFLAGS+= -DUSE_CODEC_TIMIDITY LDFLAGS+= -L$(LIBS_DIR)/timidity -ltimidity LIBTIMIDITY=libtimidity.a TIMIDEPS=$(LIBS_DIR)/timidity/$(LIBTIMIDITY) else TIMIDEPS= endif ifeq ($(USE_CODEC_WILDMIDI),yes) CPPFLAGS+= -DUSE_CODEC_WILDMIDI LDFLAGS+= -lWildMidi endif ifeq ($(CODECS_USE_ZONE),yes) CPPFLAGS+=-DCODECS_USE_ZONE endif # End of streaming music settings ############################################################# ############################################################# # Finalize things after the system specific overrides: ############################################################# ifeq ($(USE_X86_ASM),yes) CPPFLAGS+= -DUSE_INTEL_ASM endif # ############################################################# # Rules for turning source files into .o files %.o: %.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(COMMON_HW)/%.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(COMMONDIR)/%.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(UHEXEN2_SHARED)/%.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: %.asm $(NASM) $(NASM_INC) $(NASMFLAGS) -o $@ $< %.o: $(COMMON_HW)/%.asm $(NASM) $(NASM_INC) $(NASMFLAGS) -o $@ $< %.o: $(COMMONDIR)/%.asm $(NASM) $(NASM_INC) $(NASMFLAGS) -o $@ $< # Objects # Intel asm objects ifeq ($(USE_X86_ASM),yes) COMMON_ASM= math.o \ sys_ia32.o SOFT_ASM = \ d_draw.o \ d_draw16.o \ d_draw16t.o \ d_parta.o \ d_partb.o \ d_polysa.o \ d_polysa2.o \ d_polysa3.o \ d_polysa4.o \ d_polysa5.o \ d_scana.o \ d_spr8.o \ d_spr8t.o \ d_spr8t2.o \ d_varsa.o \ r_aclipa.o \ r_aliasa.o \ r_drawa.o \ r_edgea.o \ r_edgeb.o \ r_varsa.o \ surf8.o \ surf16.o SOUND_ASM = snd_mixa.o else SOFT_ASM = COMMON_ASM = SOUND_ASM = endif # Sound objects ifneq ($(USE_SOUND),yes) MUSIC_OBJS:= bgmnull.o SOUND_ASM := CPPFLAGS += -D_NO_SOUND SYSOBJ_SND := COMOBJ_SND := snd_null.o $(MUSIC_OBJS) else MUSIC_OBJS:= bgmusic.o \ snd_codec.o \ snd_flac.o \ snd_wave.o \ snd_vorbis.o \ snd_opus.o \ $(mp3_obj).o \ snd_mp3tag.o \ snd_mikmod.o \ snd_modplug.o \ snd_xmp.o \ snd_umx.o \ snd_timidity.o \ snd_wildmidi.o COMOBJ_SND := snd_sys.o snd_dma.o snd_mix.o $(SOUND_ASM) snd_mem.o $(MUSIC_OBJS) SYSOBJ_SND := snd_oss.o snd_alsa.o # end of Sound objects endif # MIDI objects ifneq ($(USE_MIDI),yes) SYSOBJ_MIDI:= midi_nul.o CPPFLAGS += -D_NO_MIDIDRV else $(error Midi driver not implemented yet.) # end of MIDI objects endif # CDAudio objects ifneq ($(USE_CDAUDIO),yes) SYSOBJ_CDA:= cd_null.o CPPFLAGS += -D_NO_CDAUDIO else SYSOBJ_CDA := cd_bsd.o cd_linux.o # end of CDAudio objects endif # Other platform specific object settings SYSOBJ_INPUT := in_svgalib.o SYSOBJ_SOFT_VID:= vid_svgalib.o # d_copy.o not needed anymore SYSOBJ_SYS := sys_unix.o # Final list of objects SOFTOBJS = \ d_edge.o \ d_fill.o \ d_init.o \ d_modech.o \ d_part.o \ d_polyse.o \ d_scan.o \ d_sky.o \ d_sprite.o \ d_surf.o \ d_vars.o \ d_zpoint.o \ r_aclip.o \ r_alias.o \ r_bsp.o \ r_draw.o \ r_edge.o \ r_efrag.o \ r_light.o \ r_main.o \ r_misc.o \ r_part.o \ r_sky.o \ r_sprite.o \ r_surf.o \ r_vars.o \ screen.o \ $(SYSOBJ_SOFT_VID) \ draw.o \ model.o COMMONOBJS = \ $(SYSOBJ_INPUT) \ $(COMOBJ_SND) \ $(SYSOBJ_SND) \ $(SYSOBJ_CDA) \ $(SYSOBJ_MIDI) \ huffman.o \ net_udp.o \ net_chan.o \ cl_cam.o \ cl_demo.o \ cl_effect.o \ cl_ents.o \ cl_inlude.o \ cl_input.o \ cl_main.o \ cl_parse.o \ cl_pred.o \ cl_tent.o \ cl_cmd.o \ console.o \ keys.o \ menu.o \ sbar.o \ skin.o \ view.o \ wad.o \ cmd.o \ q_endian.o \ link_ops.o \ sizebuf.o \ strlcat.o \ strlcpy.o \ qsnprint.o \ msg_io.o \ common.o \ debuglog.o \ quakefs.o \ info_str.o \ crc.o \ cvar.o \ cfgfile.o \ host_string.o \ mathlib.o \ pmove.o \ pmovetst.o \ zone.o \ hashindex.o \ $(SYSOBJ_SYS) # Targets .PHONY: clean distclean localclean $(TIMIDEPS) default: $(SW_BINARY) all: default hw: $(SW_BINARY) hwcl: $(SW_BINARY) # extra rule for snd_timidity.c because of timidity.h and libtimidity.a snd_timidity.o: $(COMMONDIR)/snd_timidity.c $(CC) -c $(INCLUDES) -I$(LIBS_DIR)/timidity $(CPPFLAGS) $(CFLAGS) -DTIMIDITY_STATIC=1 -o $@ $(COMMONDIR)/snd_timidity.c include $(LIBS_DIR)/timidity/_timi.mak $(SW_BINARY): $(SOFT_ASM) $(SOFTOBJS) $(COMMON_ASM) $(COMMONOBJS) $(TIMIDEPS) $(LINKER) $(SOFT_ASM) $(SOFTOBJS) $(COMMON_ASM) $(COMMONOBJS) $(LDFLAGS) $(SYSLIBS) -o $@ localclean: rm -f *.o *.res core clean: localclean timi_clean distclean: clean rm -f $(SW_BINARY) report: @echo "Host OS :" $(HOST_OS) @echo "Target OS:" $(TARGET_OS) @echo "Machine :" $(MACH_TYPE) engine/hexenworld/client/Makefile.wat000066400000000000000000000261171444734033100202240ustar00rootroot00000000000000# makefile to build hwcl.exe for Windows using Open Watcom: # wmake -f Makefile.wat # # to build opengl version glhw.exe : # wmake -f Makefile.wat BUILDGL=1 # PATH SETTINGS: !ifndef __UNIX__ PATH_SEP=\ UHEXEN2_TOP=..\..\.. ENGINE_TOP=..\.. HW_TOP=.. COMMONDIR=$(ENGINE_TOP)\h2shared COMMON_HW=$(HW_TOP)\shared UHEXEN2_SHARED=$(UHEXEN2_TOP)\common LIBS_DIR=$(UHEXEN2_TOP)\libs OSLIBS=$(UHEXEN2_TOP)\oslibs !else PATH_SEP=/ UHEXEN2_TOP=../../.. ENGINE_TOP=../.. HW_TOP=.. COMMONDIR=$(ENGINE_TOP)/h2shared COMMON_HW=$(HW_TOP)/shared UHEXEN2_SHARED=$(UHEXEN2_TOP)/common LIBS_DIR=$(UHEXEN2_TOP)/libs OSLIBS=$(UHEXEN2_TOP)/oslibs !endif # GENERAL OPTIONS (customize as required) # link to the opengl libraries at compile time? (defaults # to no, so the binaries will dynamically load the necessary # libraries and functions at runtime.) LINK_GL_LIBS=no # use fast x86 assembly on ia32 machines? (auto-disabled for # any other cpu.) USE_X86_ASM=yes # enable sound support? USE_SOUND=yes # include target's MIDI driver if available? USE_MIDI=yes # CDAudio support? USE_CDAUDIO=yes # link to the directx libraries at compile time? (otherwise, load # the necessary DLLs and functions dynamically at runtime, which # ensures our exe to function on ancient windows versions without # a directx installation.) LINK_DIRECTX=no # enable startup splash screens? (windows) WITH_SPLASHES=yes # use WinSock2 instead of WinSock-1.1? (disabled for w32 for compat. # with old Win95 machines.) (enabled for Win64 in the win64 section.) USE_WINSOCK2=no # Enable/disable codecs for streaming music support: USE_CODEC_WAVE=yes USE_CODEC_FLAC=no USE_CODEC_MP3=yes USE_CODEC_VORBIS=yes USE_CODEC_OPUS=no # either xmp or mikmod (or modplug) USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no # either timidity (preferred) or wildmidi (both possible # but not needed nor meaningful) USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no # which library to use for mp3 decoding: mad or mpg123 MP3LIB=mad # which library to use for ogg decoding: vorbis or tremor VORBISLIB=tremor # Names of the binaries SW_NAME=hwcl GL_NAME=glhwcl SW_BINARY=$(SW_NAME).exe GL_BINARY=$(GL_NAME).exe ############################################################# # Compiler flags ############################################################# CFLAGS = -zq -wx -bm -bt=nt -5s -sg -otexan -fp5 -fpi87 -ei -j -zp8 # newer OpenWatcom versions enable W303 by default CFLAGS+= -wcd=303 # compiler includes INCLUDES= -I. -I$(COMMON_HW) -I$(COMMONDIR) -I$(UHEXEN2_SHARED) # nasm includes: the trailing directory separator matters NASM_INC= -I.$(PATH_SEP) -I$(COMMON_HW)$(PATH_SEP) -I$(COMMONDIR)$(PATH_SEP) # windows resource compiler includes RC_INC = -I. -I$(COMMON_HW) -I$(COMMONDIR) # end of compiler flags ############################################################# ############################################################# # Other build flags ############################################################# CPPFLAGS+= -DH2W !ifdef DEMO CPPFLAGS+= -DDEMOBUILD !endif !ifdef DEBUG CFLAGS += -d2 # This activates some extra code in hexen2/hexenworld C source CPPFLAGS+= -DDEBUG=1 -DDEBUG_BUILD=1 !endif ############################################################# # OpenGL settings ############################################################# GL_DEFS = -DGLQUAKE GL_LIBS = ############################################################# # streaming music initial setup ############################################################# !ifneq USE_SOUND yes USE_CODEC_WAVE=no USE_CODEC_FLAC=no USE_CODEC_TIMIDITY=no USE_CODEC_WILDMIDI=no USE_CODEC_MIKMOD=no USE_CODEC_XMP=no USE_CODEC_MODPLUG=no USE_CODEC_UMX=no USE_CODEC_MP3=no USE_CODEC_VORBIS=no USE_CODEC_OPUS=no !endif ############################################################# # Win32 flags/settings and overrides: ############################################################# RC_DEFS=$(CPPFLAGS) RCFLAGS=-bt=nt NASMFLAGS=-f win32 -d_NO_PREFIX !ifndef __UNIX__ INCLUDES+= -I$(OSLIBS)\windows\dxsdk\include INCLUDES+= -I$(OSLIBS)\windows\misc\include INCLUDES+= -I$(OSLIBS)\windows\codecs\include DXLIBS = $(OSLIBS)\windows\dxsdk\x86\ CODECLIBS= $(OSLIBS)\windows\codecs\x86-watcom\ !else INCLUDES+= -I$(OSLIBS)/windows/dxsdk/include INCLUDES+= -I$(OSLIBS)/windows/misc/include INCLUDES+= -I$(OSLIBS)/windows/codecs/include DXLIBS = $(OSLIBS)/windows/dxsdk/x86/ CODECLIBS= $(OSLIBS)/windows/codecs/x86-watcom/ !endif GL_LINK=opengl32.lib !ifeq USE_WINSOCK2 yes CPPFLAGS+=-D_USE_WINSOCK2 LIBWINSOCK=ws2_32.lib !else LIBWINSOCK=wsock32.lib !endif CPPFLAGS+= -DWIN32_LEAN_AND_MEAN LIBS += $(LIBWINSOCK) winmm.lib !ifneq LINK_DIRECTX yes CPPFLAGS+= -DDX_DLSYM !else LIBS += $(DXLIBS)dsound.lib $(DXLIBS)dinput.lib $(DXLIBS)dxguid.lib !endif !ifneq WITH_SPLASHES yes CPPFLAGS+= -DNO_SPLASHES !endif ############################################################# # Streaming music settings ############################################################# !ifeq MP3LIB mad mp3_obj=snd_mp3 lib_mp3dec=$(CODECLIBS)mad.lib !endif !ifeq MP3LIB mpg123 mp3_obj=snd_mpg123 lib_mp3dec=$(CODECLIBS)mpg123.lib !endif !ifeq VORBISLIB vorbis cpp_vorbisdec= lib_vorbisdec=$(CODECLIBS)vorbisfile.lib $(CODECLIBS)vorbis.lib $(CODECLIBS)ogg.lib !endif !ifeq VORBISLIB tremor cpp_vorbisdec=-DVORBIS_USE_TREMOR lib_vorbisdec=$(CODECLIBS)vorbisidec.lib $(CODECLIBS)ogg.lib !endif !ifeq USE_CODEC_FLAC yes CPPFLAGS+= -DUSE_CODEC_FLAC # for static linkage CPPFLAGS+= -DFLAC__NO_DLL LIBS += $(CODECLIBS)FLAC.lib !endif !ifeq USE_CODEC_WAVE yes CPPFLAGS+= -DUSE_CODEC_WAVE !endif !ifeq USE_CODEC_OPUS yes CPPFLAGS+= -DUSE_CODEC_OPUS LIBS += $(CODECLIBS)opusfile.lib $(CODECLIBS)opus.lib !ifneq USE_CODEC_VORBIS yes LIBS += $(CODECLIBS)ogg.lib !endif !endif !ifeq USE_CODEC_VORBIS yes CPPFLAGS+= -DUSE_CODEC_VORBIS $(cpp_vorbisdec) LIBS += $(lib_vorbisdec) !endif !ifeq USE_CODEC_MP3 yes CPPFLAGS+= -DUSE_CODEC_MP3 LIBS += $(lib_mp3dec) !endif !ifeq USE_CODEC_MIKMOD yes CPPFLAGS+= -DUSE_CODEC_MIKMOD # for static linkage CPPFLAGS+= -DMIKMOD_STATIC LIBS += $(CODECLIBS)mikmod.lib !endif !ifeq USE_CODEC_XMP yes CPPFLAGS+= -DUSE_CODEC_XMP # for static linkage CPPFLAGS+= -DLIBXMP_STATIC LIBS += $(CODECLIBS)libxmp.lib !endif !ifeq USE_CODEC_MODPLUG yes CPPFLAGS+= -DUSE_CODEC_MODPLUG CPPFLAGS+= -DMODPLUG_STATIC LIBS += $(CODECLIBS)modplug.lib !endif !ifeq USE_CODEC_UMX yes CPPFLAGS+= -DUSE_CODEC_UMX !endif !ifeq USE_CODEC_TIMIDITY yes CPPFLAGS+= -DUSE_CODEC_TIMIDITY LIBS += $(CODECLIBS)timidity.lib !endif !ifeq USE_CODEC_WILDMIDI yes CPPFLAGS+= -DUSE_CODEC_WILDMIDI LIBS += $(CODECLIBS)WildMidi.lib !endif # End of streaming music settings ############################################################# ############################################################# # Finalize things after the system specific overrides: ############################################################# !ifeq USE_X86_ASM yes CPPFLAGS+= -DUSE_INTEL_ASM !endif !ifneq LINK_GL_LIBS yes GL_DEFS+= -DGL_DLSYM !else GL_LIBS+= $(GL_LINK) !endif !ifndef BUILDGL TARGET_NAME=$(SW_NAME) BUILD_TARGET=$(SW_BINARY) !else CPPFLAGS+= $(GL_DEFS) TARGET_NAME=$(GL_NAME) BUILD_TARGET=$(GL_BINARY) !endif # ############################################################# .EXTENSIONS: .res .rc .c: $(COMMON_HW);$(COMMONDIR);$(UHEXEN2_SHARED) .asm: $(COMMON_HW);$(COMMONDIR) .rc: $(COMMON_HW);$(COMMONDIR) .c.obj: wcc386 $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -fo=$^@ $< .asm.obj: nasm $(NASM_INC) $(NASMFLAGS) -o $^@ $< .rc.res: wrc -q -r $(RCFLAGS) $(RC_DEFS) $(RC_INC) -fo=$^@ $< # Objects # Intel asm objects !ifeq USE_X86_ASM yes COMMON_ASM= math.obj & sys_ia32.obj SOFT_ASM = & d_draw.obj & d_draw16.obj & d_draw16t.obj & d_parta.obj & d_partb.obj & d_polysa.obj & d_polysa2.obj & d_polysa3.obj & d_polysa4.obj & d_polysa5.obj & d_scana.obj & d_spr8.obj & d_spr8t.obj & d_spr8t2.obj & d_varsa.obj & r_aclipa.obj & r_aliasa.obj & r_drawa.obj & r_edgea.obj & r_edgeb.obj & r_varsa.obj & surf8.obj & surf16.obj SOUND_ASM = snd_mixa.obj !else SOFT_ASM = COMMON_ASM = SOUND_ASM = !endif # Sound objects !ifneq USE_SOUND yes MUSIC_OBJS= bgmnull.obj SOUND_ASM = CPPFLAGS += -D_NO_SOUND SYSOBJ_SND = COMOBJ_SND = snd_null.obj $(MUSIC_OBJS) !else MUSIC_OBJS= bgmusic.obj & snd_codec.obj & snd_flac.obj & snd_wave.obj & snd_vorbis.obj & snd_opus.obj & $(mp3_obj).obj & snd_mp3tag.obj & snd_mikmod.obj & snd_modplug.obj & snd_xmp.obj & snd_umx.obj & snd_timidity.obj & snd_wildmidi.obj COMOBJ_SND = snd_sys.obj snd_dma.obj snd_mix.obj $(SOUND_ASM) snd_mem.obj $(MUSIC_OBJS) SYSOBJ_SND = snd_win.obj snd_dsound.obj !endif !ifneq USE_MIDI yes SYSOBJ_MIDI= midi_nul.obj CPPFLAGS += -D_NO_MIDIDRV !else SYSOBJ_MIDI= midi_win.obj mid2strm.obj !endif # CDAudio objects !ifneq USE_CDAUDIO yes SYSOBJ_CDA= cd_null.obj CPPFLAGS += -D_NO_CDAUDIO !else SYSOBJ_CDA = cd_win.obj !endif SYSOBJ_INPUT = in_win.obj SYSOBJ_GL_VID= gl_vidnt.obj SYSOBJ_SOFT_VID= vid_win.obj SYSOBJ_SYS = sys_win.obj SYSOBJ_RES = $(TARGET_NAME).res # Final list of objects SOFTOBJS = & d_edge.obj & d_fill.obj & d_init.obj & d_modech.obj & d_part.obj & d_polyse.obj & d_scan.obj & d_sky.obj & d_sprite.obj & d_surf.obj & d_vars.obj & d_zpoint.obj & r_aclip.obj & r_alias.obj & r_bsp.obj & r_draw.obj & r_edge.obj & r_efrag.obj & r_light.obj & r_main.obj & r_misc.obj & r_part.obj & r_sky.obj & r_sprite.obj & r_surf.obj & r_vars.obj & screen.obj & $(SYSOBJ_SOFT_VID) & draw.obj & model.obj GLOBJS = & gl_refrag.obj & gl_rlight.obj & gl_rmain.obj & gl_rmisc.obj & gl_ngraph.obj & r_part.obj & gl_rsurf.obj & gl_screen.obj & gl_warp.obj & $(SYSOBJ_GL_VID) & gl_draw.obj & gl_mesh.obj & gl_model.obj COMMONOBJS = & $(SYSOBJ_INPUT) & $(COMOBJ_SND) & $(SYSOBJ_SND) & $(SYSOBJ_CDA) & $(SYSOBJ_MIDI) & huffman.obj & net_udp.obj & net_chan.obj & cl_cam.obj & cl_demo.obj & cl_effect.obj & cl_ents.obj & cl_inlude.obj & cl_input.obj & cl_main.obj & cl_parse.obj & cl_pred.obj & cl_tent.obj & cl_cmd.obj & console.obj & keys.obj & menu.obj & sbar.obj & skin.obj & view.obj & wad.obj & cmd.obj & q_endian.obj & link_ops.obj & sizebuf.obj & strlcat.obj & strlcpy.obj & qsnprint.obj & msg_io.obj & common.obj & debuglog.obj & quakefs.obj & info_str.obj & crc.obj & cvar.obj & cfgfile.obj & host_string.obj & mathlib.obj & pmove.obj & pmovetst.obj & zone.obj & hashindex.obj & $(SYSOBJ_SYS) all: $(BUILD_TARGET) $(TARGET_NAME).res: win32res.rc wrc -q -r $(RCFLAGS) $(RC_DEFS) $(RC_INC) -fo=$^@ $< snd_timidity.obj: $(COMMONDIR)/snd_timidity.c wcc386 $(INCLUDES) -I$(LIBS_DIR)/timidity $(CPPFLAGS) $(CFLAGS) -DTIMIDITY_STATIC=1 -fo=$^@ $< # 1 MB stack size. $(SW_BINARY): $(SOFT_ASM) $(SOFTOBJS) $(COMMON_ASM) $(COMMONOBJS) $(SYSOBJ_RES) wlink N $@ SYS NT_WIN OPTION q OPTION STACK=0x100000 OPTION RESOURCE=$^*.res LIBR {$(LIBS)} F {$(SOFT_ASM) $(SOFTOBJS) $(COMMON_ASM) $(COMMONOBJS)} # 1 MB stack size. $(GL_BINARY): $(GLOBJS) $(COMMON_ASM) $(COMMONOBJS) $(SYSOBJ_RES) wlink N $@ SYS NT_WIN OPTION q OPTION STACK=0x100000 OPTION RESOURCE=$^*.res LIBR {$(LIBS) $(GL_LIBS)} F {$(GLOBJS) $(COMMON_ASM) $(COMMONOBJS)} clean: .symbolic rm -f *.obj *.res *.err distclean: clean .symbolic rm -f $(SW_BINARY) $(GL_BINARY) engine/hexenworld/client/build_all.sh000077500000000000000000000004601444734033100202510ustar00rootroot00000000000000#!/bin/sh HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac $MAKE_CMD clean $MAKE_CMD hw $* || exit 1 $MAKE_CMD localclean $MAKE_CMD glhw USE_X86_ASM=no $* || exit 1 $MAKE_CMD clean engine/hexenworld/client/build_cross_amigaos.sh000077500000000000000000000007441444734033100223370ustar00rootroot00000000000000#!/bin/sh UHEXEN2_TOP=../../.. . $UHEXEN2_TOP/scripts/cross_defs.amigaos if test "$1" = "strip"; then $STRIPPER -S hwcl glhwcl exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac if test "$1" = "all"; then shift $MAKE_CMD clean $MAKE_CMD glhw $* || exit 1 $MAKE_CMD clean $MAKE_CMD hw $* || exit 1 $MAKE_CMD clean exit 0 fi exec $MAKE_CMD $* engine/hexenworld/client/build_cross_aros.sh000077500000000000000000000010151444734033100216530ustar00rootroot00000000000000#!/bin/sh # for x86-AROS cross-builds UHEXEN2_TOP=../../.. . $UHEXEN2_TOP/scripts/cross_defs.aros if test "$1" = "strip"; then $STRIPPER -S hwcl glhwcl exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac if test "$1" = "all"; then shift $MAKE_CMD clean $MAKE_CMD glhw USE_X86_ASM=no $* || exit 1 $MAKE_CMD clean $MAKE_CMD hw $* || exit 1 $MAKE_CMD clean exit 0 fi exec $MAKE_CMD $* engine/hexenworld/client/build_cross_aros64.sh000077500000000000000000000010171444734033100220270ustar00rootroot00000000000000#!/bin/sh # for x86_64-AROS cross-builds UHEXEN2_TOP=../../.. . $UHEXEN2_TOP/scripts/cross_defs.aros64 if test "$1" = "strip"; then $STRIPPER -S hwcl glhwcl exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac if test "$1" = "all"; then shift $MAKE_CMD clean $MAKE_CMD glhw USE_GLA=yes $* || exit 1 $MAKE_CMD clean $MAKE_CMD hw $* || exit 1 $MAKE_CMD clean exit 0 fi exec $MAKE_CMD $* engine/hexenworld/client/build_cross_morphos.sh000077500000000000000000000007441444734033100224060ustar00rootroot00000000000000#!/bin/sh UHEXEN2_TOP=../../.. . $UHEXEN2_TOP/scripts/cross_defs.morphos if test "$1" = "strip"; then $STRIPPER -S hwcl glhwcl exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac if test "$1" = "all"; then shift $MAKE_CMD clean $MAKE_CMD glhw $* || exit 1 $MAKE_CMD clean $MAKE_CMD hw $* || exit 1 $MAKE_CMD clean exit 0 fi exec $MAKE_CMD $* engine/hexenworld/client/cl_cam.c000066400000000000000000000304621444734033100173520ustar00rootroot00000000000000/* cl_cam.c -- Player camera tracking in Spectator mode -- ZOID * This takes over player controls for spectator automatic camera. * Player moves as a spectator, but the camera tracks an enemy player * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #define PM_SPECTATORMAXSPEED 500 #define PM_STOPSPEED 100 #define PM_MAXSPEED 320 #define BUTTON_JUMP 2 #define BUTTON_ATTACK 1 #define MAX_ANGLE_TURN 10 #define CAM_NONE 0 #define CAM_TRACK 1 static vec3_t desired_position; // where the camera wants to be static qboolean locked = false; static int oldbuttons; // track high fragger static cvar_t cl_hightrack = {"cl_hightrack", "0", CVAR_NONE}; //static cvar_t cl_camera_maxpitch = {"cl_camera_maxpitch", "10", CVAR_NONE}; //static cvar_t cl_camera_maxyaw = {"cl_camera_maxyaw", "30", CVAR_NONE}; static qboolean cam_forceview; static double cam_lastviewtime; #if 0 static vec3_t cam_viewangles; #endif static int spec_track = 0; // player# of who we are tracking static int autocam = CAM_NONE; static void vectoangles(vec3_t vec, vec3_t ang) { float forward; float yaw, pitch; if (vec[1] == 0 && vec[0] == 0) { yaw = 0; if (vec[2] > 0) pitch = 90; else pitch = 270; } else { yaw = (int) (atan2(vec[1], vec[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; forward = sqrt (vec[0]*vec[0] + vec[1]*vec[1]); pitch = (int) (atan2(vec[2], forward) * 180 / M_PI); if (pitch < 0) pitch += 360; } ang[0] = pitch; ang[1] = yaw; ang[2] = 0; } static void Cam_Unlock(void) { if (autocam) { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, "ptrack"); autocam = CAM_NONE; locked = false; Sbar_Changed(); } } static void Cam_Lock(int playernum) { char st[40]; sprintf(st, "ptrack %i", playernum); MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, st); spec_track = playernum; cam_forceview = true; locked = false; Sbar_Changed(); } static pmtrace_t Cam_DoTrace(vec3_t vec1, vec3_t vec2) { #if 0 memset(&pmove, 0, sizeof(pmove)); pmove.numphysent = 1; VectorClear (pmove.physents[0].origin); pmove.physents[0].model = cl.worldmodel; #endif VectorCopy (vec1, pmove.origin); return PM_PlayerMove(pmove.origin, vec2); } // Returns distance or 9999 if invalid for some reason static float Cam_TryFlyby(player_state_t *self, player_state_t *player, vec3_t vec, qboolean checkvis) { vec3_t v; pmtrace_t trace; float len; vectoangles(vec, v); // v[0] = -v[0]; VectorCopy (v, pmove.angles); VectorNormalize(vec); VectorMA(player->origin, 800, vec, v); // v is endpos // fake a player move trace = Cam_DoTrace(player->origin, v); if (/*trace.inopen ||*/ trace.inwater) return 9999; VectorCopy(trace.endpos, vec); VectorSubtract(trace.endpos, player->origin, v); len = VectorLength(v); if (len < 32 || len > 800) return 9999; if (checkvis) { VectorSubtract(trace.endpos, self->origin, v); len = VectorLength(v); trace = Cam_DoTrace(self->origin, vec); if (trace.fraction != 1 || trace.inwater) return 9999; } return len; } // Is player visible? static qboolean Cam_IsVisible(player_state_t *player, vec3_t vec) { pmtrace_t trace; vec3_t v; float d; trace = Cam_DoTrace(player->origin, vec); if (trace.fraction != 1 || /*trace.inopen ||*/ trace.inwater) return false; // check distance, don't let the player get too far away or too close VectorSubtract(player->origin, vec, v); d = VectorLength(v); if (d < 16) return false; return true; } static qboolean InitFlyby(player_state_t *self, player_state_t *player, int checkvis) { float f, maxlen; vec3_t vec, vec2; vec3_t forward, right, up; VectorCopy(player->viewangles, vec); vec[0] = 0; AngleVectors (vec, forward, right, up); // for (i = 0; i < 3; i++) // forward[i] *= 3; maxlen = 1000; VectorAdd(forward, up, vec2); VectorAdd(vec2, right, vec2); if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < maxlen) { maxlen = f; VectorCopy(vec2, vec); } VectorAdd(forward, up, vec2); VectorSubtract(vec2, right, vec2); if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < maxlen) { maxlen = f; VectorCopy(vec2, vec); } VectorAdd(forward, right, vec2); if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < maxlen) { maxlen = f; VectorCopy(vec2, vec); } VectorSubtract(forward, right, vec2); if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < maxlen) { maxlen = f; VectorCopy(vec2, vec); } VectorAdd(forward, up, vec2); if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < maxlen) { maxlen = f; VectorCopy(vec2, vec); } VectorSubtract(forward, up, vec2); if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < maxlen) { maxlen = f; VectorCopy(vec2, vec); } VectorAdd(up, right, vec2); VectorSubtract(vec2, forward, vec2); if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < maxlen) { maxlen = f; VectorCopy(vec2, vec); } VectorSubtract(up, right, vec2); VectorSubtract(vec2, forward, vec2); if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < maxlen) { maxlen = f; VectorCopy(vec2, vec); } // invert VectorNegate(forward, vec2); if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < maxlen) { maxlen = f; VectorCopy(vec2, vec); } VectorCopy(forward, vec2); if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < maxlen) { maxlen = f; VectorCopy(vec2, vec); } // invert VectorNegate(right, vec2); if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < maxlen) { maxlen = f; VectorCopy(vec2, vec); } VectorCopy(right, vec2); if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < maxlen) { maxlen = f; VectorCopy(vec2, vec); } // ack, can't find him if (maxlen >= 1000) { // Cam_Unlock(); return false; } locked = true; VectorCopy(vec, desired_position); return true; } static void Cam_CheckHighTarget(void) { int i, j, maxfrags; player_info_t *s; j = -1; for (i = 0, maxfrags = -9999; i < MAX_CLIENTS; i++) { s = &cl.players[i]; if (s->name[0] && !s->spectator && s->frags > maxfrags) { maxfrags = s->frags; j = i; } } if (j >= 0) { if (!locked || cl.players[j].frags > cl.players[spec_track].frags) Cam_Lock(j); } else { Cam_Unlock(); } } // ZOID // // Take over the user controls and track a player. // We find a nice position to watch the player and move there void Cam_Track(usercmd_t *cmd) { player_state_t *player, *self; frame_t *frame; vec3_t vec; float len; if (!cl.spectator) return; if (cl_hightrack.integer && !locked) Cam_CheckHighTarget(); if (!autocam || cls.state != ca_active) return; if (locked && (!cl.players[spec_track].name[0] || cl.players[spec_track].spectator)) { locked = false; if (cl_hightrack.integer) Cam_CheckHighTarget(); else Cam_Unlock(); return; } frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; player = frame->playerstate + spec_track; self = frame->playerstate + cl.playernum; if (!locked || !Cam_IsVisible(player, desired_position)) { if (!locked || realtime - cam_lastviewtime > 0.1) { if (!InitFlyby(self, player, true)) InitFlyby(self, player, false); cam_lastviewtime = realtime; } } else { cam_lastviewtime = realtime; } // couldn't track for some reason if (!locked || !autocam) return; // Ok, move to our desired position and set our angles to view // the player VectorSubtract(desired_position, self->origin, vec); len = VectorLength(vec); cmd->forwardmove = cmd->sidemove = cmd->upmove = 0; if (len > 16) { // close enough? MSG_WriteByte (&cls.netchan.message, clc_tmove); MSG_WriteCoord (&cls.netchan.message, desired_position[0]); MSG_WriteCoord (&cls.netchan.message, desired_position[1]); MSG_WriteCoord (&cls.netchan.message, desired_position[2]); } // move there locally immediately VectorCopy(desired_position, self->origin); VectorSubtract(player->origin, desired_position, vec); vectoangles(vec, cl.viewangles); cl.viewangles[0] = -cl.viewangles[0]; } #if 0 static float adjustang(float current, float ideal, float speed) { float move; current = anglemod(current); ideal = anglemod(ideal); if (current == ideal) return current; move = ideal - current; if (ideal > current) { if (move >= 180) move = move - 360; } else { if (move <= -180) move = move + 360; } if (move > 0) { if (move > speed) move = speed; } else { if (move < -speed) move = -speed; } // Con_Printf("c/i: %4.2f/%4.2f move: %4.2f\n", current, ideal, move); return anglemod (current + move); } #endif #if 0 void Cam_SetView(void) { player_state_t *player, *self; frame_t *frame; vec3_t vec, vec2; if (cls.state != ca_active || !cl.spectator || !autocam || !locked) return; frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; player = frame->playerstate + spec_track; self = frame->playerstate + cl.playernum; VectorSubtract(player->origin, cl.simorg, vec); if (cam_forceview) { cam_forceview = false; vectoangles(vec, cam_viewangles); cam_viewangles[0] = -cam_viewangles[0]; } else { vectoangles(vec, vec2); vec2[PITCH] = -vec2[PITCH]; cam_viewangles[PITCH] = adjustang(cam_viewangles[PITCH], vec2[PITCH], cl_camera_maxpitch.value); cam_viewangles[YAW] = adjustang(cam_viewangles[YAW], vec2[YAW], cl_camera_maxyaw.value); } VectorCopy(cam_viewangles, cl.viewangles); VectorCopy(cl.viewangles, cl.simangles); } #endif void Cam_FinishMove(usercmd_t *cmd) { int i; player_info_t *s; int end; if (cls.state != ca_active || server_version < 1.57) return; if (!cl.spectator) // only in spectator mode return; #if 0 if (autocam && locked) { frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; player = frame->playerstate + spec_track; self = frame->playerstate + cl.playernum; VectorSubtract(player->origin, self->origin, vec); if (cam_forceview) { cam_forceview = false; vectoangles(vec, cam_viewangles); cam_viewangles[0] = -cam_viewangles[0]; } else { vectoangles(vec, vec2); vec2[PITCH] = -vec2[PITCH]; cam_viewangles[PITCH] = adjustang(cam_viewangles[PITCH], vec2[PITCH], cl_camera_maxpitch.value); cam_viewangles[YAW] = adjustang(cam_viewangles[YAW], vec2[YAW], cl_camera_maxyaw.value); } VectorCopy(cam_viewangles, cl.viewangles); } #endif if (cmd->buttons & BUTTON_ATTACK) { if (!(oldbuttons & BUTTON_ATTACK)) { oldbuttons |= BUTTON_ATTACK; autocam++; if (autocam > CAM_TRACK) { Cam_Unlock(); VectorCopy(cl.viewangles, cmd->angles); return; } } else { return; } } else { oldbuttons &= ~BUTTON_ATTACK; if (!autocam) return; } if (autocam && cl_hightrack.integer) { Cam_CheckHighTarget(); return; } if (locked) { if ((cmd->buttons & BUTTON_JUMP) && (oldbuttons & BUTTON_JUMP)) return; // don't pogo stick if (!(cmd->buttons & BUTTON_JUMP)) { oldbuttons &= ~BUTTON_JUMP; return; } oldbuttons |= BUTTON_JUMP; // don't jump again until released } // Con_Printf("Selecting track target...\n"); if (locked && autocam) end = (spec_track + 1) % MAX_CLIENTS; else end = spec_track; i = end; do { s = &cl.players[i]; if (s->name[0] && !s->spectator) { Cam_Lock(i); return; } i = (i + 1) % MAX_CLIENTS; } while (i != end); // stay on same guy? i = spec_track; s = &cl.players[i]; if (s->name[0] && !s->spectator) { Cam_Lock(i); return; } Con_Printf("No target found ...\n"); autocam = locked = false; } void Cam_Reset(void) { autocam = CAM_NONE; spec_track = 0; } void CL_InitCam(void) { Cvar_RegisterVariable (&cl_hightrack); // Cvar_RegisterVariable (&cl_camera_maxpitch); // Cvar_RegisterVariable (&cl_camera_maxyaw); } engine/hexenworld/client/cl_cmd.c000066400000000000000000000040761444734033100173570ustar00rootroot00000000000000/* * cl_cmd.c -- client command forwarding to server * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" /* =================== Cmd_ForwardToServer adds the current command line as a clc_stringcmd to the client message. things like godmode, noclip, etc, are commands directed to the server, so when they are typed in at the console, they will need to be forwarded. =================== */ void Cmd_ForwardToServer (void) { if (cls.state == ca_disconnected) { Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0)); return; } if (cls.demoplayback) return; // not really connected MSG_WriteByte (&cls.netchan.message, clc_stringcmd); SZ_Print (&cls.netchan.message, Cmd_Argv(0)); if (Cmd_Argc() > 1) { SZ_Print (&cls.netchan.message, " "); SZ_Print (&cls.netchan.message, Cmd_Args()); } } // This is the command variant of the above. The only difference // is that it doesn't forward the first argument, which is "cmd" void Cmd_ForwardToServer_f (void) { if (cls.state == ca_disconnected) { Con_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0)); return; } if (cls.demoplayback) return; // not really connected if (Cmd_Argc() > 1) { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); SZ_Print (&cls.netchan.message, Cmd_Args()); } } void CL_Cmd_Init (void) { Cmd_AddCommand ("cmd", Cmd_ForwardToServer_f); } engine/hexenworld/client/cl_demo.c000066400000000000000000000240051444734033100175320ustar00rootroot00000000000000/* * cl_demo.c -- demo recording and playback * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" static void CL_FinishTimeDemo (void); /* ============================================================================== DEMO CODE When a demo is playing back, all NET_SendMessages are skipped, and NET_GetMessages are read from the demo file. Whenever cl.time gets past the last received message, another message is read from the demo file. ============================================================================== */ /* ============== CL_StopPlayback Called when a demo file runs out, or the user starts a game ============== */ void CL_StopPlayback (void) { if (!cls.demoplayback) return; fclose (cls.demofile); cls.demoplayback = false; cls.demofile = NULL; cls.state = ca_disconnected; if (cls.timedemo) CL_FinishTimeDemo (); } #define dem_cmd 0 #define dem_read 1 /* ==================== CL_WriteDemoCmd Writes the current user cmd ==================== */ void CL_WriteDemoCmd (const usercmd_t *pcmd) { int i; float f; byte c; usercmd_t cmd; // Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime); f = LittleFloat((float)realtime); fwrite (&f, 4, 1, cls.demofile); c = dem_cmd; fwrite (&c, sizeof(c), 1, cls.demofile); // correct for byte order, bytes don't matter cmd = *pcmd; for (i = 0; i < 3; i++) cmd.angles[i] = LittleFloat(cmd.angles[i]); cmd.forwardmove = LittleShort(cmd.forwardmove); cmd.sidemove = LittleShort(cmd.sidemove); cmd.upmove = LittleShort(cmd.upmove); fwrite(&cmd, sizeof(cmd), 1, cls.demofile); for (i = 0; i < 3; i++) { f = LittleFloat (cl.viewangles[i]); fwrite (&f, 4, 1, cls.demofile); } fflush (cls.demofile); } /* ==================== CL_WriteDemoMessage Dumps the current net message, prefixed by the length and view angles ==================== */ static void CL_WriteDemoMessage (sizebuf_t *msg) { int len; float f; byte c; // Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime); f = LittleFloat((float)realtime); fwrite (&f, 4, 1, cls.demofile); c = dem_read; fwrite (&c, sizeof(c), 1, cls.demofile); len = LittleLong (msg->cursize); fwrite (&len, 4, 1, cls.demofile); fwrite (msg->data, msg->cursize, 1, cls.demofile); fflush (cls.demofile); } /* ==================== CL_GetDemoMessage -- FIXME.. ==================== */ static qboolean CL_GetDemoMessage (void) { int r, i, j; float f; float demotime; byte c; usercmd_t *pcmd; // read the time from the packet fread(&demotime, sizeof(demotime), 1, cls.demofile); demotime = LittleFloat(demotime); // decide if it is time to grab the next message if (cls.timedemo) { if (cls.td_lastframe < 0) { cls.td_lastframe = demotime; } else if (demotime > cls.td_lastframe) { cls.td_lastframe = demotime; // rewind back to time fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime), SEEK_SET); return 0; // already read this frame's message } if (!cls.td_starttime && cls.state == ca_active) { cls.td_starttime = Sys_DoubleTime(); cls.td_startframe = host_framecount; } realtime = demotime; // warp } else if (cls.state >= ca_onserver) { // always grab until fully connected if (realtime + 1.0 < demotime) { // too far back realtime = demotime - 1.0; // rewind back to time fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime), SEEK_SET); return 0; } else if (realtime < demotime) { // rewind back to time fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime), SEEK_SET); return 0; // don't need another message yet } } else { realtime = demotime; // we're warping } if (cls.state < ca_demostart) Host_Error ("%s: cls.state != ca_active", __thisfunc__); // get the msg type fread (&c, sizeof(c), 1, cls.demofile); switch (c) { case dem_cmd : // user sent input i = cls.netchan.outgoing_sequence & UPDATE_MASK; pcmd = &cl.frames[i].cmd; r = fread (pcmd, sizeof(*pcmd), 1, cls.demofile); if (r != 1) { CL_StopPlayback (); return 0; } // byte order stuff for (j = 0; j < 3; j++) pcmd->angles[j] = LittleFloat(pcmd->angles[j]); pcmd->forwardmove = LittleShort(pcmd->forwardmove); pcmd->sidemove = LittleShort(pcmd->sidemove); pcmd->upmove = LittleShort(pcmd->upmove); cl.frames[i].senttime = demotime; cl.frames[i].receivedtime = -1; // we haven't gotten a reply yet cls.netchan.outgoing_sequence++; for (i = 0; i < 3; i++) { r = fread (&f, 4, 1, cls.demofile); cl.viewangles[i] = LittleFloat (f); } break; case dem_read: // get the next message fread (&net_message.cursize, 4, 1, cls.demofile); net_message.cursize = LittleLong (net_message.cursize); //Con_Printf("read: %ld bytes\n", net_message.cursize); if (net_message.cursize > MAX_MSGLEN) Sys_Error ("Demo message > MAX_MSGLEN"); r = fread (net_message.data, net_message.cursize, 1, cls.demofile); if (r != 1) { CL_StopPlayback (); return 0; } break; default : Con_Printf("Corrupted demo.\n"); CL_StopPlayback (); return 0; } return 1; } /* ==================== CL_GetMessage Handles recording and playback of demos, on top of NET_ code ==================== */ qboolean CL_GetMessage (void) { if (cls.demoplayback) return CL_GetDemoMessage (); if (!NET_GetPacket ()) return false; if (cls.demorecording) CL_WriteDemoMessage (&net_message); return true; } /* ==================== CL_Stop_f stop recording a demo ==================== */ void CL_Stop_f (void) { if (!cls.demorecording) { Con_Printf ("Not recording a demo.\n"); return; } // write a disconnect message to the demo file SZ_Clear (&net_message); MSG_WriteLong (&net_message, -1); // -1 sequence means out of band MSG_WriteByte (&net_message, svc_disconnect); MSG_WriteString (&net_message, "EndOfDemo"); CL_WriteDemoMessage (&net_message); // finish up fclose (cls.demofile); cls.demofile = NULL; cls.demorecording = false; Con_Printf ("Completed demo\n"); } /* ==================== CL_Record_f record ==================== */ void CL_Record_f (void) { int c; char name[MAX_OSPATH]; const char *p; if (cls.demorecording) CL_Stop_f(); c = Cmd_Argc(); if (c != 3) { Con_Printf ("record \n"); return; } p = Cmd_Argv(1); if (*p == '.' || strstr(p, "..")) { Con_Printf ("Invalid demo name.\n"); return; } FS_MakePath_BUF (FS_USERDIR, NULL, name, sizeof(name), p); COM_AddExtension (name, ".qwd", sizeof(name)); cls.demofile = fopen (name, "wb"); if (!cls.demofile) { Con_Printf ("ERROR: couldn't create %s\n", name); return; } if (cls.state != ca_disconnected) CL_Disconnect(); Con_Printf ("recording to %s.\n", name); cls.demorecording = true; // start the map Cmd_ExecuteString ( va("connect %s", Cmd_Argv(2)), src_command); } /* ==================== CL_ReRecord_f record ==================== */ void CL_ReRecord_f (void) { int c; char name[MAX_OSPATH]; const char *p; if (cls.demorecording) CL_Stop_f(); c = Cmd_Argc(); if (c != 2) { Con_Printf ("rerecord \n"); return; } p = Cmd_Argv(1); if (*p == '.' || strstr(p, "..")) { Con_Printf ("Invalid demo name.\n"); return; } if (!*cls.servername) { Con_Printf("No server to reconnect to...\n"); return; } FS_MakePath_BUF (FS_USERDIR, NULL, name, sizeof(name), p); COM_AddExtension (name, ".qwd", sizeof(name)); cls.demofile = fopen (name, "wb"); if (!cls.demofile) { Con_Printf ("ERROR: couldn't create %s\n", name); return; } Con_Printf ("recording to %s.\n", name); cls.demorecording = true; CL_Disconnect(); CL_SendConnectPacket (); } /* ==================== CL_PlayDemo_f play [demoname] ==================== */ void CL_PlayDemo_f (void) { char name[MAX_OSPATH]; if (Cmd_Argc() != 2) { Con_Printf ("playdemo : plays a demo\n"); return; } // disconnect from server CL_Disconnect (); // open the demo file q_strlcpy (name, Cmd_Argv(1), sizeof(name)); COM_AddExtension (name, ".qwd", sizeof(name)); Con_Printf ("Playing demo from %s.\n", name); FS_OpenFile (name, &cls.demofile, NULL); if (!cls.demofile) { Con_Printf ("ERROR: couldn't open %s\n", name); cls.demonum = -1; // stop demo loop return; } // get rid of the menu and/or console Key_SetDest (key_game); cls.demoplayback = true; cls.state = ca_demostart; Netchan_Setup (&cls.netchan, &net_from); realtime = 0; } /* ==================== CL_FinishTimeDemo ==================== */ static void CL_FinishTimeDemo (void) { int frames; float time; cls.timedemo = false; // the first frame didn't count frames = (host_framecount - cls.td_startframe) - 1; time = Sys_DoubleTime() - cls.td_starttime; if (!time) time = 1; Con_Printf ("%i frames %5.1f seconds %5.1f fps\n", frames, time, frames/time); } /* ==================== CL_TimeDemo_f timedemo [demoname] ==================== */ void CL_TimeDemo_f (void) { if (Cmd_Argc() != 2) { Con_Printf ("timedemo : gets demo speeds\n"); return; } CL_PlayDemo_f (); if (!cls.demofile) return; // if (cls.state != ca_active) // return; // cls.td_starttime will be grabbed at the second frame of the demo, so // all the loading time doesn't get counted cls.timedemo = true; cls.td_starttime = 0; cls.td_startframe = host_framecount; cls.td_lastframe = -1; // get a new message this frame } engine/hexenworld/client/cl_effect.c000066400000000000000000002275741444734033100200620ustar00rootroot00000000000000/* * cl_effect.c -- Client side effects. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // HEADER FILES ------------------------------------------------------------ #include "quakedef.h" // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- #define MAX_EFFECT_ENTITIES 256 // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- extern void CreateStream (int type, int ent, int flags, int tag, float duration, int skin, vec3_t source, vec3_t dest); extern void CLTENT_XbowImpact (vec3_t pos, vec3_t vel, int chType, int damage, int arrowType); /* so xbow effect can use tents. */ extern void CLTENT_SpawnDeathBubble (vec3_t pos); extern void CreateRavenDeath(vec3_t pos); extern void CreateRavenExplosions (vec3_t pos); extern void CreateExplosionWithSound(vec3_t pos); extern entity_state_t *FindState (int EntNum); extern int TempSoundChannel (void); extern void setseed (unsigned int seed); extern float seedrand (void); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static int NewEffectEntity (void); static void FreeEffectEntity (int idx); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- // PRIVATE DATA DEFINITIONS ------------------------------------------------ static entity_t EffectEntities[MAX_EFFECT_ENTITIES]; static qboolean EntityUsed[MAX_EFFECT_ENTITIES]; static int EffectEntityCount; // CODE -------------------------------------------------------------------- static void vectoangles (vec3_t vec, vec3_t ang) { float forward; float yaw, pitch; if (vec[1] == 0 && vec[0] == 0) { yaw = 0; if (vec[2] > 0) pitch = 90; else pitch = 270; } else { yaw = (int) (atan2(vec[1], vec[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; forward = Q_sqrt (vec[0]*vec[0] + vec[1]*vec[1]); pitch = (int) (atan2(vec[2], forward) * 180 / M_PI); if (pitch < 0) pitch += 360; } ang[0] = pitch; ang[1] = yaw; ang[2] = 0; } //========================================================================== // // CL_InitTEnts // //========================================================================== static sfx_t *cl_fxsfx_bone; static sfx_t *cl_fxsfx_bonefpow; static sfx_t *cl_fxsfx_xbowshoot; static sfx_t *cl_fxsfx_xbowfshoot; static sfx_t *cl_fxsfx_explode; static sfx_t *cl_fxsfx_mmfire; static sfx_t *cl_fxsfx_eidolon; static sfx_t *cl_fxsfx_scarabwhoosh; static sfx_t *cl_fxsfx_scarabgrab; static sfx_t *cl_fxsfx_scarabhome; static sfx_t *cl_fxsfx_scarabrip; static sfx_t *cl_fxsfx_scarabbyebye; static sfx_t *cl_fxsfx_ravensplit; static sfx_t *cl_fxsfx_ravenfire; static sfx_t *cl_fxsfx_ravengo; static sfx_t *cl_fxsfx_drillashoot; static sfx_t *cl_fxsfx_drillaspin; static sfx_t *cl_fxsfx_drillameat; static sfx_t *cl_fxsfx_arr2flsh; static sfx_t *cl_fxsfx_arr2wood; static sfx_t *cl_fxsfx_met2stn; static sfx_t *cl_fxsfx_ripple; static sfx_t *cl_fxsfx_splash; void CL_InitEffects (void) { cl_fxsfx_bone = S_PrecacheSound ("necro/bonefnrm.wav"); cl_fxsfx_bonefpow = S_PrecacheSound ("necro/bonefpow.wav"); cl_fxsfx_xbowshoot = S_PrecacheSound ("assassin/firebolt.wav"); cl_fxsfx_xbowfshoot = S_PrecacheSound ("assassin/firefblt.wav"); cl_fxsfx_explode = S_PrecacheSound ("weapons/explode.wav"); cl_fxsfx_mmfire = S_PrecacheSound ("necro/mmfire.wav"); cl_fxsfx_eidolon = S_PrecacheSound ("eidolon/spell.wav"); cl_fxsfx_scarabwhoosh = S_PrecacheSound ("misc/whoosh.wav"); cl_fxsfx_scarabgrab = S_PrecacheSound ("assassin/chn2flsh.wav"); cl_fxsfx_scarabhome = S_PrecacheSound ("assassin/chain.wav"); cl_fxsfx_scarabrip = S_PrecacheSound ("assassin/chntear.wav"); cl_fxsfx_scarabbyebye = S_PrecacheSound ("items/itmspawn.wav"); cl_fxsfx_ravensplit = S_PrecacheSound ("raven/split.wav"); cl_fxsfx_ravenfire = S_PrecacheSound ("raven/rfire1.wav"); cl_fxsfx_ravengo = S_PrecacheSound ("raven/ravengo.wav"); cl_fxsfx_drillashoot = S_PrecacheSound ("assassin/pincer.wav"); cl_fxsfx_drillaspin = S_PrecacheSound ("assassin/spin.wav"); cl_fxsfx_drillameat = S_PrecacheSound ("assassin/core.wav"); cl_fxsfx_arr2flsh = S_PrecacheSound ("assassin/arr2flsh.wav"); cl_fxsfx_arr2wood = S_PrecacheSound ("assassin/arr2wood.wav"); cl_fxsfx_met2stn = S_PrecacheSound ("weapons/met2stn.wav"); cl_fxsfx_ripple = S_PrecacheSound ("misc/drip.wav"); cl_fxsfx_splash = S_PrecacheSound ("raven/outwater.wav"); } void CL_ClearEffects (void) { memset(cl.Effects, 0, sizeof(cl.Effects)); memset(EntityUsed, 0, sizeof(EntityUsed)); EffectEntityCount = 0; } static void CL_FreeEffect (int idx) { int i; switch (cl.Effects[idx].type) { case CE_RAIN: break; case CE_FOUNTAIN: break; case CE_QUAKE: break; case CE_TELESMK1: FreeEffectEntity(cl.Effects[idx].ef.Smoke.entity_index2); /* FALLTHRU */ case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: case CE_ACID_MUZZFL: case CE_FLAMESTREAM: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: case CE_RIPPLE: FreeEffectEntity(cl.Effects[idx].ef.Smoke.entity_index); break; case CE_DEATHBUBBLES: break; /* Just go through animation and then remove */ case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_SM_EXPLOSION2: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_BRN_BOUNCE: case CE_LSHOCK: case CE_ACID_HIT: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_LBALL_EXPL: case CE_FBOOM: case CE_BOMB: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: FreeEffectEntity(cl.Effects[idx].ef.Smoke.entity_index); break; /* Go forward then backward through animation then remove */ case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_HWSPLITFLASH: case CE_RED_FLASH: FreeEffectEntity(cl.Effects[idx].ef.Flash.entity_index); break; case CE_RIDER_DEATH: break; case CE_TELEPORTERPUFFS: for (i = 0 ; i < 8 ; ++i) FreeEffectEntity(cl.Effects[idx].ef.Teleporter.entity_index[i]); break; case CE_HWSHEEPINATOR: for (i = 0 ; i < 5 ; ++i) FreeEffectEntity(cl.Effects[idx].ef.Xbow.ent[i]); break; case CE_HWXBOWSHOOT: for (i = 0 ; i < cl.Effects[idx].ef.Xbow.bolts ; ++i) FreeEffectEntity(cl.Effects[idx].ef.Xbow.ent[i]); break; case CE_TELEPORTERBODY: FreeEffectEntity(cl.Effects[idx].ef.Teleporter.entity_index[0]); break; case CE_HWDRILLA: case CE_BONESHARD: case CE_BONESHRAPNEL: case CE_HWBONEBALL: case CE_HWRAVENSTAFF: case CE_HWRAVENPOWER: FreeEffectEntity(cl.Effects[idx].ef.Missile.entity_index); break; case CE_TRIPMINESTILL: FreeEffectEntity(cl.Effects[idx].ef.Chain.ent1); break; case CE_SCARABCHAIN: case CE_TRIPMINE: FreeEffectEntity(cl.Effects[idx].ef.Chain.ent1); break; case CE_HWMISSILESTAR: FreeEffectEntity(cl.Effects[idx].ef.Star.ent2); /* FALLTHRU */ case CE_HWEIDOLONSTAR: FreeEffectEntity(cl.Effects[idx].ef.Star.ent1); FreeEffectEntity(cl.Effects[idx].ef.Star.entity_index); break; default: // Con_Printf("Freeing unknown effect type\n"); break; } memset(&cl.Effects[idx], 0, sizeof(struct EffectT)); } //========================================================================== // // CL_ParseEffect // //========================================================================== // All changes need to be in SV_SendEffect(), SV_ParseEffect(), // SV_SaveEffects(), SV_LoadEffects(), CL_ParseEffect() void CL_ParseEffect (void) { int i, idx; qboolean ImmediateFree; entity_t *ent; int dir; float sinval, cosval; float skinnum; vec3_t forward, right, up, vtemp; vec3_t forward2, right2, up2; vec3_t origin; ImmediateFree = false; idx = MSG_ReadByte(); if (cl.Effects[idx].type) CL_FreeEffect(idx); memset(&cl.Effects[idx], 0, sizeof(struct EffectT)); cl.Effects[idx].type = MSG_ReadByte(); switch (cl.Effects[idx].type) { case CE_RAIN: cl.Effects[idx].ef.Rain.min_org[0] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.min_org[1] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.min_org[2] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.max_org[0] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.max_org[1] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.max_org[2] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.e_size[0] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.e_size[1] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.e_size[2] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.dir[0] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.dir[1] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.dir[2] = MSG_ReadCoord(); cl.Effects[idx].ef.Rain.color = MSG_ReadShort(); cl.Effects[idx].ef.Rain.count = MSG_ReadShort(); cl.Effects[idx].ef.Rain.wait = MSG_ReadFloat(); break; case CE_FOUNTAIN: cl.Effects[idx].ef.Fountain.pos[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Fountain.pos[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Fountain.pos[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Fountain.angle[0] = MSG_ReadAngle (); cl.Effects[idx].ef.Fountain.angle[1] = MSG_ReadAngle (); cl.Effects[idx].ef.Fountain.angle[2] = MSG_ReadAngle (); cl.Effects[idx].ef.Fountain.movedir[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Fountain.movedir[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Fountain.movedir[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Fountain.color = MSG_ReadShort (); cl.Effects[idx].ef.Fountain.cnt = MSG_ReadByte (); AngleVectors (cl.Effects[idx].ef.Fountain.angle, cl.Effects[idx].ef.Fountain.vforward, cl.Effects[idx].ef.Fountain.vright, cl.Effects[idx].ef.Fountain.vup); break; case CE_QUAKE: cl.Effects[idx].ef.Quake.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Quake.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Quake.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Quake.radius = MSG_ReadFloat (); break; case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK1: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: case CE_ACID_MUZZFL: case CE_FLAMESTREAM: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: case CE_RIPPLE: cl.Effects[idx].ef.Smoke.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Smoke.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Smoke.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Smoke.velocity[0] = MSG_ReadFloat (); cl.Effects[idx].ef.Smoke.velocity[1] = MSG_ReadFloat (); cl.Effects[idx].ef.Smoke.velocity[2] = MSG_ReadFloat (); cl.Effects[idx].ef.Smoke.framelength = MSG_ReadFloat (); if ((cl.Effects[idx].ef.Smoke.entity_index = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Smoke.entity_index]; VectorCopy(cl.Effects[idx].ef.Smoke.origin, ent->origin); if ((cl.Effects[idx].type == CE_WHITE_SMOKE) || (cl.Effects[idx].type == CE_SLOW_WHITE_SMOKE)) ent->model = Mod_ForName("models/whtsmk1.spr", true); else if (cl.Effects[idx].type == CE_GREEN_SMOKE) ent->model = Mod_ForName("models/grnsmk1.spr", true); else if (cl.Effects[idx].type == CE_GREY_SMOKE) ent->model = Mod_ForName("models/grysmk1.spr", true); else if (cl.Effects[idx].type == CE_RED_SMOKE) ent->model = Mod_ForName("models/redsmk1.spr", true); else if (cl.Effects[idx].type == CE_TELESMK1) ent->model = Mod_ForName("models/telesmk1.spr", true); else if (cl.Effects[idx].type == CE_TELESMK2) ent->model = Mod_ForName("models/telesmk2.spr", true); else if (cl.Effects[idx].type == CE_REDCLOUD) ent->model = Mod_ForName("models/rcloud.spr", true); else if (cl.Effects[idx].type == CE_FLAMESTREAM) ent->model = Mod_ForName("models/flamestr.spr", true); else if (cl.Effects[idx].type == CE_ACID_MUZZFL) { ent->model = Mod_ForName("models/muzzle1.spr", true); ent->drawflags = DRF_TRANSLUCENT | MLS_ABSLIGHT; ent->abslight = 51; } else if (cl.Effects[idx].type == CE_FLAMEWALL) ent->model = Mod_ForName("models/firewal1.spr", true); else if (cl.Effects[idx].type == CE_FLAMEWALL2) ent->model = Mod_ForName("models/firewal2.spr", true); else if (cl.Effects[idx].type == CE_ONFIRE) { float rdm = rand() & 3; if (rdm < 1) ent->model = Mod_ForName("models/firewal1.spr", true); else if (rdm < 2) ent->model = Mod_ForName("models/firewal2.spr", true); else ent->model = Mod_ForName("models/firewal3.spr", true); ent->drawflags = DRF_TRANSLUCENT; ent->abslight = 255; ent->frame = cl.Effects[idx].ef.Smoke.frame; } else if (cl.Effects[idx].type == CE_RIPPLE) { if (cl.Effects[idx].ef.Smoke.framelength == 2) { R_SplashParticleEffect (cl.Effects[idx].ef.Smoke.origin, 200, 406 + (rand() % 8), pt_slowgrav, 40); /* splash */ S_StartSound (TempSoundChannel(), 1, cl_fxsfx_splash, cl.Effects[idx].ef.Smoke.origin, 1, 1); } else if (cl.Effects[idx].ef.Smoke.framelength == 1) { R_SplashParticleEffect (cl.Effects[idx].ef.Smoke.origin, 100, 406 + (rand() % 8), pt_slowgrav, 20); /* splash */ } else { S_StartSound (TempSoundChannel(), 1, cl_fxsfx_ripple, cl.Effects[idx].ef.Smoke.origin, 1, 1); } cl.Effects[idx].ef.Smoke.framelength = 0.05; ent->model = Mod_ForName("models/ripple.spr", true); ent->drawflags = DRF_TRANSLUCENT;// | SCALE_TYPE_XYONLY | SCALE_ORIGIN_CENTER; ent->angles[0] = 90; //ent->scale = 1; } else { ImmediateFree = true; Con_Printf ("Bad effect type %d\n",(int)cl.Effects[idx].type); } if (cl.Effects[idx].type != CE_REDCLOUD && cl.Effects[idx].type != CE_ACID_MUZZFL && cl.Effects[idx].type != CE_FLAMEWALL && cl.Effects[idx].type != CE_RIPPLE) ent->drawflags = DRF_TRANSLUCENT; if (cl.Effects[idx].type == CE_FLAMESTREAM) { ent->drawflags = DRF_TRANSLUCENT | MLS_ABSLIGHT; ent->abslight = 255; ent->frame = cl.Effects[idx].ef.Smoke.frame; } if (cl.Effects[idx].type == CE_GHOST) { ent->model = Mod_ForName("models/ghost.spr", true); ent->drawflags = DRF_TRANSLUCENT | MLS_ABSLIGHT; ent->abslight = 127; } if (cl.Effects[idx].type == CE_TELESMK1) { S_StartSound (TempSoundChannel(), 1, cl_fxsfx_ravenfire, cl.Effects[idx].ef.Smoke.origin, 1, 1); if ((cl.Effects[idx].ef.Smoke.entity_index2 = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Smoke.entity_index2]; VectorCopy(cl.Effects[idx].ef.Smoke.origin, ent->origin); ent->model = Mod_ForName("models/telesmk1.spr", true); ent->drawflags = DRF_TRANSLUCENT; } else { ImmediateFree = true; FreeEffectEntity(cl.Effects[idx].ef.Smoke.entity_index); } } } else { ImmediateFree = true; } break; case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_SM_EXPLOSION2: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_BRN_BOUNCE: case CE_LSHOCK: case CE_ACID_HIT: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_LBALL_EXPL: case CE_FBOOM: case CE_BOMB: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: cl.Effects[idx].ef.Smoke.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Smoke.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Smoke.origin[2] = MSG_ReadCoord (); if ((cl.Effects[idx].ef.Smoke.entity_index = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Smoke.entity_index]; VectorCopy(cl.Effects[idx].ef.Smoke.origin, ent->origin); if (cl.Effects[idx].type == CE_BLUESPARK) ent->model = Mod_ForName("models/bspark.spr", true); else if (cl.Effects[idx].type == CE_YELLOWSPARK) ent->model = Mod_ForName("models/spark.spr", true); else if (cl.Effects[idx].type == CE_SM_CIRCLE_EXP) ent->model = Mod_ForName("models/fcircle.spr", true); else if (cl.Effects[idx].type == CE_BG_CIRCLE_EXP) ent->model = Mod_ForName("models/xplod29.spr", true); else if (cl.Effects[idx].type == CE_SM_WHITE_FLASH) ent->model = Mod_ForName("models/sm_white.spr", true); else if (cl.Effects[idx].type == CE_YELLOWRED_FLASH) { ent->model = Mod_ForName("models/yr_flsh.spr", true); ent->drawflags = DRF_TRANSLUCENT; } else if (cl.Effects[idx].type == CE_SM_EXPLOSION) ent->model = Mod_ForName("models/sm_expld.spr", true); else if (cl.Effects[idx].type == CE_SM_EXPLOSION2) { ent->model = Mod_ForName("models/sm_expld.spr", true); S_StartSound (TempSoundChannel(), 1, cl_fxsfx_explode, cl.Effects[idx].ef.Smoke.origin, 1, 1); } else if (cl.Effects[idx].type == CE_LG_EXPLOSION) { ent->model = Mod_ForName("models/bg_expld.spr", true); S_StartSound (TempSoundChannel(), 1, cl_fxsfx_explode, cl.Effects[idx].ef.Smoke.origin, 1, 1); } else if (cl.Effects[idx].type == CE_FLOOR_EXPLOSION) ent->model = Mod_ForName("models/fl_expld.spr", true); else if (cl.Effects[idx].type == CE_BLUE_EXPLOSION) ent->model = Mod_ForName("models/xpspblue.spr", true); else if (cl.Effects[idx].type == CE_REDSPARK) ent->model = Mod_ForName("models/rspark.spr", true); else if (cl.Effects[idx].type == CE_GREENSPARK) ent->model = Mod_ForName("models/gspark.spr", true); else if (cl.Effects[idx].type == CE_ICEHIT) ent->model = Mod_ForName("models/icehit.spr", true); else if (cl.Effects[idx].type == CE_MEDUSA_HIT) ent->model = Mod_ForName("models/medhit.spr", true); else if (cl.Effects[idx].type == CE_MEZZO_REFLECT) ent->model = Mod_ForName("models/mezzoref.spr", true); else if (cl.Effects[idx].type == CE_FLOOR_EXPLOSION2) ent->model = Mod_ForName("models/flrexpl2.spr", true); else if (cl.Effects[idx].type == CE_XBOW_EXPLOSION) ent->model = Mod_ForName("models/xbowexpl.spr", true); else if (cl.Effects[idx].type == CE_NEW_EXPLOSION) ent->model = Mod_ForName("models/gen_expl.spr", true); else if (cl.Effects[idx].type == CE_MAGIC_MISSILE_EXPLOSION) { ent->model = Mod_ForName("models/mm_expld.spr", true); S_StartSound (TempSoundChannel(), 1, cl_fxsfx_explode, cl.Effects[idx].ef.Smoke.origin, 1, 1); } else if (cl.Effects[idx].type == CE_BONE_EXPLOSION) ent->model = Mod_ForName("models/bonexpld.spr", true); else if (cl.Effects[idx].type == CE_BLDRN_EXPL) ent->model = Mod_ForName("models/xplsn_1.spr", true); else if (cl.Effects[idx].type == CE_ACID_HIT) ent->model = Mod_ForName("models/axplsn_2.spr", true); else if (cl.Effects[idx].type == CE_ACID_SPLAT) ent->model = Mod_ForName("models/axplsn_1.spr", true); else if (cl.Effects[idx].type == CE_ACID_EXPL) { ent->model = Mod_ForName("models/axplsn_5.spr", true); ent->drawflags = MLS_ABSLIGHT; ent->abslight = 255; } else if (cl.Effects[idx].type == CE_FBOOM) ent->model = Mod_ForName("models/fboom.spr", true); else if (cl.Effects[idx].type == CE_BOMB) ent->model = Mod_ForName("models/pow.spr", true); else if (cl.Effects[idx].type == CE_LBALL_EXPL) ent->model = Mod_ForName("models/Bluexp3.spr", true); else if (cl.Effects[idx].type == CE_FIREWALL_SMALL) ent->model = Mod_ForName("models/firewal1.spr", true); else if (cl.Effects[idx].type == CE_FIREWALL_MEDIUM) ent->model = Mod_ForName("models/firewal5.spr", true); else if (cl.Effects[idx].type == CE_FIREWALL_LARGE) ent->model = Mod_ForName("models/firewal4.spr", true); else if (cl.Effects[idx].type == CE_BRN_BOUNCE) ent->model = Mod_ForName("models/spark.spr", true); else if (cl.Effects[idx].type == CE_LSHOCK) { ent->model = Mod_ForName("models/vorpshok.mdl", true); ent->drawflags = MLS_TORCH; ent->angles[2] = 90; ent->scale = 255; } } else { ImmediateFree = true; } break; case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_HWSPLITFLASH: case CE_RED_FLASH: cl.Effects[idx].ef.Flash.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Flash.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Flash.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Flash.reverse = 0; if ((cl.Effects[idx].ef.Flash.entity_index = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Flash.entity_index]; VectorCopy(cl.Effects[idx].ef.Flash.origin, ent->origin); if (cl.Effects[idx].type == CE_WHITE_FLASH) ent->model = Mod_ForName("models/gryspt.spr", true); else if (cl.Effects[idx].type == CE_BLUE_FLASH) ent->model = Mod_ForName("models/bluflash.spr", true); else if (cl.Effects[idx].type == CE_SM_BLUE_FLASH) ent->model = Mod_ForName("models/sm_blue.spr", true); else if (cl.Effects[idx].type == CE_RED_FLASH) ent->model = Mod_ForName("models/redspt.spr", true); else if (cl.Effects[idx].type == CE_HWSPLITFLASH) { ent->model = Mod_ForName("models/sm_blue.spr", true); S_StartSound (TempSoundChannel(), 1, cl_fxsfx_ravensplit, cl.Effects[idx].ef.Flash.origin, 1, 1); } ent->drawflags = DRF_TRANSLUCENT; } else { ImmediateFree = true; } break; case CE_RIDER_DEATH: cl.Effects[idx].ef.RD.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.RD.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.RD.origin[2] = MSG_ReadCoord (); break; case CE_TELEPORTERPUFFS: cl.Effects[idx].ef.Teleporter.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Teleporter.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Teleporter.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Teleporter.framelength = .05; dir = 0; for (i = 0 ; i < 8 ; ++i) { if ((cl.Effects[idx].ef.Teleporter.entity_index[i] = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Teleporter.entity_index[i]]; VectorCopy(cl.Effects[idx].ef.Teleporter.origin, ent->origin); q_sincosdeg(dir, &sinval, &cosval); cl.Effects[idx].ef.Teleporter.velocity[i][0] = 10*cosval; cl.Effects[idx].ef.Teleporter.velocity[i][1] = 10*sinval; cl.Effects[idx].ef.Teleporter.velocity[i][2] = 0; dir += 45; ent->model = Mod_ForName("models/telesmk2.spr", true); ent->drawflags = DRF_TRANSLUCENT; } else { ImmediateFree = true; break; } } if (ImmediateFree) { for (i = 0 ; i < 8 ; ++i) { if (cl.Effects[idx].ef.Teleporter.entity_index[i] == -1) break; FreeEffectEntity(cl.Effects[idx].ef.Teleporter.entity_index[i]); } } break; case CE_TELEPORTERBODY: cl.Effects[idx].ef.Teleporter.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Teleporter.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Teleporter.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Teleporter.velocity[0][0] = MSG_ReadFloat (); cl.Effects[idx].ef.Teleporter.velocity[0][1] = MSG_ReadFloat (); cl.Effects[idx].ef.Teleporter.velocity[0][2] = MSG_ReadFloat (); skinnum = MSG_ReadFloat (); cl.Effects[idx].ef.Teleporter.framelength = .05; dir = 0; if ((cl.Effects[idx].ef.Teleporter.entity_index[0] = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Teleporter.entity_index[0]]; VectorCopy(cl.Effects[idx].ef.Teleporter.origin, ent->origin); ent->model = Mod_ForName("models/teleport.mdl", true); ent->drawflags = SCALE_TYPE_XYONLY | DRF_TRANSLUCENT; ent->scale = 100; ent->skinnum = skinnum; } else { ImmediateFree = true; } break; case CE_BONESHRAPNEL: case CE_HWBONEBALL: cl.Effects[idx].ef.Missile.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Missile.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Missile.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Missile.velocity[0] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.velocity[1] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.velocity[2] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.angle[0] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.angle[1] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.angle[2] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.avelocity[0] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.avelocity[1] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.avelocity[2] = MSG_ReadFloat (); if ((cl.Effects[idx].ef.Missile.entity_index = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Missile.entity_index]; VectorCopy(cl.Effects[idx].ef.Missile.origin, ent->origin); VectorCopy(cl.Effects[idx].ef.Missile.angle, ent->angles); if (cl.Effects[idx].type == CE_BONESHRAPNEL) ent->model = Mod_ForName("models/boneshrd.mdl", true); else if (cl.Effects[idx].type == CE_HWBONEBALL) { ent->model = Mod_ForName("models/bonelump.mdl", true); S_StartSound (TempSoundChannel(), 1, cl_fxsfx_bonefpow, cl.Effects[idx].ef.Missile.origin, 1, 1); } } else { ImmediateFree = true; } break; case CE_BONESHARD: case CE_HWRAVENSTAFF: case CE_HWRAVENPOWER: cl.Effects[idx].ef.Missile.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Missile.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Missile.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Missile.velocity[0] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.velocity[1] = MSG_ReadFloat (); cl.Effects[idx].ef.Missile.velocity[2] = MSG_ReadFloat (); vectoangles(cl.Effects[idx].ef.Missile.velocity, cl.Effects[idx].ef.Missile.angle); if ((cl.Effects[idx].ef.Missile.entity_index = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Missile.entity_index]; VectorCopy(cl.Effects[idx].ef.Missile.origin, ent->origin); VectorCopy(cl.Effects[idx].ef.Missile.angle, ent->angles); if (cl.Effects[idx].type == CE_HWRAVENSTAFF) { cl.Effects[idx].ef.Missile.avelocity[2] = 1000; ent->model = Mod_ForName("models/vindsht1.mdl", true); } else if (cl.Effects[idx].type == CE_BONESHARD) { cl.Effects[idx].ef.Missile.avelocity[0] = (rand() % 1554) - 777; ent->model = Mod_ForName("models/boneshot.mdl", true); S_StartSound (TempSoundChannel(), 1, cl_fxsfx_bone, cl.Effects[idx].ef.Missile.origin, 1, 1); } else if (cl.Effects[idx].type == CE_HWRAVENPOWER) { ent->model = Mod_ForName("models/ravproj.mdl", true); S_StartSound (TempSoundChannel(), 1, cl_fxsfx_ravengo, cl.Effects[idx].ef.Missile.origin, 1, 1); } } else { ImmediateFree = true; } break; case CE_DEATHBUBBLES: cl.Effects[idx].ef.Bubble.owner = MSG_ReadShort(); cl.Effects[idx].ef.Bubble.offset[0] = MSG_ReadByte(); cl.Effects[idx].ef.Bubble.offset[1] = MSG_ReadByte(); cl.Effects[idx].ef.Bubble.offset[2] = MSG_ReadByte(); cl.Effects[idx].ef.Bubble.count = MSG_ReadByte(); /* num of bubbles */ cl.Effects[idx].ef.Bubble.time_amount = 0; break; case CE_HWXBOWSHOOT: origin[0] = MSG_ReadCoord (); origin[1] = MSG_ReadCoord (); origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Xbow.angle[0] = MSG_ReadAngle (); cl.Effects[idx].ef.Xbow.angle[1] = MSG_ReadAngle (); cl.Effects[idx].ef.Xbow.angle[2] = 0;//MSG_ReadFloat (); cl.Effects[idx].ef.Xbow.bolts = MSG_ReadByte(); cl.Effects[idx].ef.Xbow.randseed = MSG_ReadByte(); cl.Effects[idx].ef.Xbow.turnedbolts = MSG_ReadByte(); cl.Effects[idx].ef.Xbow.activebolts= MSG_ReadByte(); setseed(cl.Effects[idx].ef.Xbow.randseed); AngleVectors (cl.Effects[idx].ef.Xbow.angle, forward, right, up); VectorNormalizeFast(forward); VectorCopy(forward, cl.Effects[idx].ef.Xbow.velocity); // VectorScale(forward, 1000, cl.Effects[idx].ef.Xbow.velocity); if (cl.Effects[idx].ef.Xbow.bolts > 5) cl.Effects[idx].ef.Xbow.bolts = 5; if (cl.Effects[idx].ef.Xbow.bolts == 3) { S_StartSound (TempSoundChannel(), 1, cl_fxsfx_xbowshoot, origin, 1, 1); } else if (cl.Effects[idx].ef.Xbow.bolts == 5) { S_StartSound (TempSoundChannel(), 1, cl_fxsfx_xbowfshoot, origin, 1, 1); } for (i = 0 ; i < cl.Effects[idx].ef.Xbow.bolts ; i++) { cl.Effects[idx].ef.Xbow.gonetime[i] = 1 + seedrand()*2; cl.Effects[idx].ef.Xbow.state[i] = 0; if ((1<origin); vectoangles(cl.Effects[idx].ef.Xbow.vel[i], ent->angles); if (cl.Effects[idx].ef.Xbow.bolts == 5) ent->model = Mod_ForName("models/flaming.mdl", true); else ent->model = Mod_ForName("models/arrow.mdl", true); } } break; case CE_HWSHEEPINATOR: origin[0] = MSG_ReadCoord (); origin[1] = MSG_ReadCoord (); origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Xbow.angle[0] = MSG_ReadAngle (); cl.Effects[idx].ef.Xbow.angle[1] = MSG_ReadAngle (); cl.Effects[idx].ef.Xbow.angle[2] = 0;//MSG_ReadFloat (); cl.Effects[idx].ef.Xbow.turnedbolts = MSG_ReadByte(); cl.Effects[idx].ef.Xbow.activebolts= MSG_ReadByte(); cl.Effects[idx].ef.Xbow.bolts = 5; cl.Effects[idx].ef.Xbow.randseed = 0; AngleVectors (cl.Effects[idx].ef.Xbow.angle, forward, right, up); VectorNormalizeFast(forward); VectorCopy(forward, cl.Effects[idx].ef.Xbow.velocity); // S_StartSound (TempSoundChannel(), 1, cl_fxsfx_xbowshoot, origin, 1, 1); for (i = 0 ; i < 5 ; i++) { cl.Effects[idx].ef.Xbow.gonetime[i] = 0; cl.Effects[idx].ef.Xbow.state[i] = 0; if ((1<origin); vectoangles(cl.Effects[idx].ef.Xbow.vel[i], ent->angles); ent->model = Mod_ForName("models/polymrph.spr", true); } } break; case CE_HWDRILLA: cl.Effects[idx].ef.Missile.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Missile.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Missile.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Missile.angle[0] = MSG_ReadAngle (); cl.Effects[idx].ef.Missile.angle[1] = MSG_ReadAngle (); cl.Effects[idx].ef.Missile.angle[2] = 0; cl.Effects[idx].ef.Missile.speed = MSG_ReadShort(); S_StartSound (TempSoundChannel(), 1, cl_fxsfx_drillashoot, cl.Effects[idx].ef.Missile.origin, 1, 1); AngleVectors(cl.Effects[idx].ef.Missile.angle, cl.Effects[idx].ef.Missile.velocity, right, up); VectorScale(cl.Effects[idx].ef.Missile.velocity, cl.Effects[idx].ef.Missile.speed, cl.Effects[idx].ef.Missile.velocity); if ((cl.Effects[idx].ef.Missile.entity_index = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Missile.entity_index]; VectorCopy(cl.Effects[idx].ef.Missile.origin, ent->origin); VectorCopy(cl.Effects[idx].ef.Missile.angle, ent->angles); ent->model = Mod_ForName("models/scrbstp1.mdl", true); } else { ImmediateFree = true; } break; case CE_SCARABCHAIN: cl.Effects[idx].ef.Chain.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Chain.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Chain.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Chain.owner = MSG_ReadShort (); cl.Effects[idx].ef.Chain.tag = MSG_ReadByte(); cl.Effects[idx].ef.Chain.material = cl.Effects[idx].ef.Chain.owner>>12; cl.Effects[idx].ef.Chain.owner &= 0x0fff; cl.Effects[idx].ef.Chain.height = 16;//MSG_ReadByte (); cl.Effects[idx].ef.Chain.sound_time = cl.time; cl.Effects[idx].ef.Chain.state = 0; /* state 0: move slowly toward owner */ S_StartSound (TempSoundChannel(), 1, cl_fxsfx_scarabwhoosh, cl.Effects[idx].ef.Chain.origin, 1, 1); if ((cl.Effects[idx].ef.Chain.ent1 = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Chain.ent1]; VectorCopy(cl.Effects[idx].ef.Chain.origin, ent->origin); ent->model = Mod_ForName("models/scrbpbdy.mdl", true); } else { ImmediateFree = true; } break; case CE_TRIPMINE: cl.Effects[idx].ef.Chain.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Chain.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Chain.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Chain.velocity[0] = MSG_ReadFloat (); cl.Effects[idx].ef.Chain.velocity[1] = MSG_ReadFloat (); cl.Effects[idx].ef.Chain.velocity[2] = MSG_ReadFloat (); if ((cl.Effects[idx].ef.Chain.ent1 = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Chain.ent1]; VectorCopy(cl.Effects[idx].ef.Chain.origin, ent->origin); ent->model = Mod_ForName("models/twspike.mdl", true); } else { ImmediateFree = true; } break; case CE_TRIPMINESTILL: // Con_DPrintf("Allocating chain effect...\n"); cl.Effects[idx].ef.Chain.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Chain.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Chain.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Chain.velocity[0] = MSG_ReadFloat (); cl.Effects[idx].ef.Chain.velocity[1] = MSG_ReadFloat (); cl.Effects[idx].ef.Chain.velocity[2] = MSG_ReadFloat (); if ((cl.Effects[idx].ef.Chain.ent1 = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Chain.ent1]; VectorCopy(cl.Effects[idx].ef.Chain.velocity, ent->origin); ent->model = Mod_ForName("models/twspike.mdl", true); } else { // Con_DPrintf("ERROR: Couldn't allocate chain effect!\n"); ImmediateFree = true; } break; case CE_HWMISSILESTAR: case CE_HWEIDOLONSTAR: cl.Effects[idx].ef.Star.origin[0] = MSG_ReadCoord (); cl.Effects[idx].ef.Star.origin[1] = MSG_ReadCoord (); cl.Effects[idx].ef.Star.origin[2] = MSG_ReadCoord (); cl.Effects[idx].ef.Star.velocity[0] = MSG_ReadFloat (); cl.Effects[idx].ef.Star.velocity[1] = MSG_ReadFloat (); cl.Effects[idx].ef.Star.velocity[2] = MSG_ReadFloat (); vectoangles(cl.Effects[idx].ef.Star.velocity, cl.Effects[idx].ef.Star.angle); cl.Effects[idx].ef.Missile.avelocity[2] = 300 + (rand() % 300); if ((cl.Effects[idx].ef.Star.entity_index = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Star.entity_index]; VectorCopy(cl.Effects[idx].ef.Star.origin, ent->origin); VectorCopy(cl.Effects[idx].ef.Star.angle, ent->angles); ent->model = Mod_ForName("models/ball.mdl", true); } else { ImmediateFree = true; } cl.Effects[idx].ef.Star.scaleDir = 1; cl.Effects[idx].ef.Star.scale = 0.3; if ((cl.Effects[idx].ef.Star.ent1 = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Star.ent1]; VectorCopy(cl.Effects[idx].ef.Star.origin, ent->origin); ent->drawflags |= MLS_ABSLIGHT; ent->abslight = 127; ent->angles[2] = 90; if (cl.Effects[idx].type == CE_HWMISSILESTAR) { ent->model = Mod_ForName("models/star.mdl", true); ent->scale = 30; S_StartSound (TempSoundChannel(), 1, cl_fxsfx_mmfire, ent->origin, 1, 1); } else { ent->model = Mod_ForName("models/glowball.mdl", true); S_StartSound (TempSoundChannel(), 1, cl_fxsfx_eidolon, ent->origin, 1, 1); } } if (cl.Effects[idx].type == CE_HWMISSILESTAR) { if ((cl.Effects[idx].ef.Star.ent2 = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Star.ent2]; VectorCopy(cl.Effects[idx].ef.Star.origin, ent->origin); ent->model = Mod_ForName("models/star.mdl", true); ent->drawflags |= MLS_ABSLIGHT; ent->abslight = 127; ent->scale = 30; } } break; default: Host_Error ("%s: bad type", __thisfunc__); } if (ImmediateFree) { cl.Effects[idx].type = CE_NONE; } } void CL_EndEffect (void) { int idx; entity_t *ent; idx = MSG_ReadByte(); switch (cl.Effects[idx].type) { case CE_HWRAVENPOWER: ent = &EffectEntities[cl.Effects[idx].ef.Missile.entity_index]; CreateRavenDeath(ent->origin); break; case CE_HWRAVENSTAFF: ent = &EffectEntities[cl.Effects[idx].ef.Missile.entity_index]; CreateExplosionWithSound(ent->origin); break; } CL_FreeEffect(idx); } static void XbowImpactPuff (vec3_t origin, int material) /* hopefully can use this with xbow & chain both */ { int part_color; switch (material) { case XBOW_IMPACT_REDFLESH: part_color = 256 + 8 * 16 + (rand() % 9); /* Blood red */ break; case XBOW_IMPACT_STONE: part_color = 256 + 20 + (rand() % 8); /* Gray */ break; case XBOW_IMPACT_METAL: part_color = 256 + (8 * 15); /* Sparks */ break; case XBOW_IMPACT_WOOD: part_color = 256 + (5 * 16) + (rand() % 8); /* Wood chunks */ break; case XBOW_IMPACT_ICE: part_color = 406 + (rand() % 8); /* Ice particles */ break; case XBOW_IMPACT_GREENFLESH: part_color = 256 + 183 + (rand() % 8); /* Spider's have green blood */ break; default: part_color = 256 + (3 * 16) + 4; /* Dust Brown */ break; } R_RunParticleEffect4 (origin, 24, part_color, pt_fastgrav, 20); } void CL_ReviseEffect(void) /* be sure to read, in the switch statement, everything * in this message, even if the effect is not the right * kind or invalid, or else client is sure to crash. */ { int idx, type, revisionCode; int curEnt, material, takedamage; entity_t *ent; vec3_t forward, right, up, pos; float dist, speed; entity_state_t *es; idx = MSG_ReadByte (); type = MSG_ReadByte (); if (cl.Effects[idx].type == type) { switch (type) { case CE_SCARABCHAIN: /* attach to new guy or retract if new guy is world */ curEnt = MSG_ReadShort(); cl.Effects[idx].ef.Chain.material = curEnt>>12; curEnt &= 0x0fff; if (curEnt) { cl.Effects[idx].ef.Chain.state = 1; cl.Effects[idx].ef.Chain.owner = curEnt; es = FindState(cl.Effects[idx].ef.Chain.owner); if (es) { ent = &EffectEntities[cl.Effects[idx].ef.Chain.ent1]; XbowImpactPuff(ent->origin, cl.Effects[idx].ef.Chain.material); } } else { cl.Effects[idx].ef.Chain.state = 2; } break; case CE_HWXBOWSHOOT: revisionCode = MSG_ReadByte(); /* this is one packed byte! highest bit: for impact revision, indicates whether damage is done for redirect revision, indicates whether new origin was sent next 3 high bits: for all revisions, indicates which bolt is to be revised highest 3 of the low 4 bits: for impact revision, indicates the material that was hit lowest bit: indicates whether revision is of impact or redirect variety */ curEnt = (revisionCode >> 4) & 7; if (revisionCode & 1) /* impact effect: */ { cl.Effects[idx].ef.Xbow.activebolts &= ~(1<origin); material = (revisionCode & 14); takedamage = (revisionCode & 128); if (takedamage) { cl.Effects[idx].ef.Xbow.gonetime[curEnt] = cl.time; } else { cl.Effects[idx].ef.Xbow.gonetime[curEnt] += cl.time; } VectorCopy(cl.Effects[idx].ef.Xbow.vel[curEnt], forward); VectorNormalizeFast(forward); VectorScale(forward, 8, forward); /* do a particle effect here, with the color depending on chType */ XbowImpactPuff(ent->origin, material); /* impact sound: */ switch (material) { case XBOW_IMPACT_GREENFLESH: case XBOW_IMPACT_REDFLESH: case XBOW_IMPACT_MUMMY: S_StartSound (TempSoundChannel(), 0, cl_fxsfx_arr2flsh, ent->origin, 1, 1); break; case XBOW_IMPACT_WOOD: S_StartSound (TempSoundChannel(), 0, cl_fxsfx_arr2wood, ent->origin, 1, 1); break; default: S_StartSound (TempSoundChannel(), 0, cl_fxsfx_met2stn, ent->origin, 1, 1); break; } CLTENT_XbowImpact(ent->origin, forward, material, takedamage, cl.Effects[idx].ef.Xbow.bolts); } } else { if (cl.Effects[idx].ef.Xbow.ent[curEnt] != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Xbow.ent[curEnt]]; ent->angles[0] = MSG_ReadAngle(); if (ent->angles[0] < 0) ent->angles[0] += 360; ent->angles[0] *= -1; ent->angles[1] = MSG_ReadAngle(); if (ent->angles[1] < 0) ent->angles[1] += 360; ent->angles[2] = 0; if (revisionCode & 128) /* new origin */ { cl.Effects[idx].ef.Xbow.origin[curEnt][0] = MSG_ReadCoord(); cl.Effects[idx].ef.Xbow.origin[curEnt][1] = MSG_ReadCoord(); cl.Effects[idx].ef.Xbow.origin[curEnt][2] = MSG_ReadCoord(); } AngleVectors(ent->angles, forward, right, up); speed = VectorLengthFast(cl.Effects[idx].ef.Xbow.vel[curEnt]); VectorScale(forward, speed, cl.Effects[idx].ef.Xbow.vel[curEnt]); VectorCopy(cl.Effects[idx].ef.Xbow.origin[curEnt], ent->origin); } else { pos[0] = MSG_ReadAngle(); if (pos[0] < 0) pos[0] += 360; pos[0] *= -1; pos[1] = MSG_ReadAngle(); if (pos[1] < 0) pos[1] += 360; pos[2] = 0; if (revisionCode & 128) /* new origin */ { cl.Effects[idx].ef.Xbow.origin[curEnt][0] = MSG_ReadCoord(); cl.Effects[idx].ef.Xbow.origin[curEnt][1] = MSG_ReadCoord(); cl.Effects[idx].ef.Xbow.origin[curEnt][2] = MSG_ReadCoord(); } AngleVectors(pos, forward, right,up); speed = VectorLengthFast(cl.Effects[idx].ef.Xbow.vel[curEnt]); VectorScale(forward, speed, cl.Effects[idx].ef.Xbow.vel[curEnt]); } } break; case CE_HWSHEEPINATOR: revisionCode = MSG_ReadByte(); curEnt = (revisionCode >> 4) & 7; if (revisionCode & 1) /* impact */ { dist = MSG_ReadCoord(); cl.Effects[idx].ef.Xbow.activebolts &= ~(1<origin); R_ColoredParticleExplosion(ent->origin, (rand() % 16) + 144 /*(144,159)*/, 20, 30); } } else /* direction change */ { if (cl.Effects[idx].ef.Xbow.ent[curEnt] != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Xbow.ent[curEnt]]; ent->angles[0] = MSG_ReadAngle(); if (ent->angles[0] < 0) ent->angles[0] += 360; ent->angles[0] *= -1; ent->angles[1] = MSG_ReadAngle(); if (ent->angles[1] < 0) ent->angles[1] += 360; ent->angles[2] = 0; if (revisionCode & 128) /* new origin */ { cl.Effects[idx].ef.Xbow.origin[curEnt][0] = MSG_ReadCoord(); cl.Effects[idx].ef.Xbow.origin[curEnt][1] = MSG_ReadCoord(); cl.Effects[idx].ef.Xbow.origin[curEnt][2] = MSG_ReadCoord(); } AngleVectors(ent->angles, forward, right, up); speed = VectorLengthFast(cl.Effects[idx].ef.Xbow.vel[curEnt]); VectorScale(forward, speed, cl.Effects[idx].ef.Xbow.vel[curEnt]); VectorCopy(cl.Effects[idx].ef.Xbow.origin[curEnt], ent->origin); } else { pos[0] = MSG_ReadAngle(); if (pos[0] < 0) pos[0] += 360; pos[0] *= -1; pos[1] = MSG_ReadAngle(); if (pos[1] < 0) pos[1] += 360; pos[2] = 0; if (revisionCode & 128) /* new origin */ { cl.Effects[idx].ef.Xbow.origin[curEnt][0] = MSG_ReadCoord(); cl.Effects[idx].ef.Xbow.origin[curEnt][1] = MSG_ReadCoord(); cl.Effects[idx].ef.Xbow.origin[curEnt][2] = MSG_ReadCoord(); } AngleVectors(pos, forward, right, up); speed = VectorLengthFast(cl.Effects[idx].ef.Xbow.vel[curEnt]); VectorScale(forward, speed, cl.Effects[idx].ef.Xbow.vel[curEnt]); } } break; case CE_HWDRILLA: revisionCode = MSG_ReadByte(); if (revisionCode == 0) /* impact */ { pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); material = MSG_ReadByte(); /* throw lil bits of victim at entry */ XbowImpactPuff(pos, material); if ((material == XBOW_IMPACT_GREENFLESH) || (material == XBOW_IMPACT_GREENFLESH)) { /* meaty sound and some chunks too */ S_StartSound (TempSoundChannel(), 0, cl_fxsfx_drillameat, pos, 1, 1); /* todo: the chunks */ } /* lil bits at exit */ VectorCopy(cl.Effects[idx].ef.Missile.velocity, forward); VectorNormalizeFast(forward); VectorScale(forward, 36, forward); VectorAdd(forward, pos, pos); XbowImpactPuff(pos, material); } else /* turn */ { ent = &EffectEntities[cl.Effects[idx].ef.Missile.entity_index]; ent->angles[0] = MSG_ReadAngle(); if (ent->angles[0] < 0) ent->angles[0] += 360; ent->angles[0] *= -1; ent->angles[1] = MSG_ReadAngle(); if (ent->angles[1] < 0) ent->angles[1] += 360; ent->angles[2] = 0; cl.Effects[idx].ef.Missile.origin[0] = MSG_ReadCoord(); cl.Effects[idx].ef.Missile.origin[1] = MSG_ReadCoord(); cl.Effects[idx].ef.Missile.origin[2] = MSG_ReadCoord(); AngleVectors(ent->angles, forward, right, up); speed = VectorLengthFast(cl.Effects[idx].ef.Missile.velocity); VectorScale(forward, speed, cl.Effects[idx].ef.Missile.velocity); VectorCopy(cl.Effects[idx].ef.Missile.origin, ent->origin); } break; } } else { // Con_DPrintf("Received Unrecognized Effect Update!\n"); switch (type) { case CE_SCARABCHAIN: /* attach to new guy or retract if new guy is world */ curEnt = MSG_ReadShort(); break; case CE_HWXBOWSHOOT: revisionCode = MSG_ReadByte(); curEnt = (revisionCode >> 4) & 7; if (revisionCode & 1) /* impact effect: */ { MSG_ReadCoord(); } else { MSG_ReadAngle(); MSG_ReadAngle(); if (revisionCode & 128) /* new origin */ { MSG_ReadCoord(); MSG_ReadCoord(); MSG_ReadCoord(); /* create a clc message to retrieve effect information */ // MSG_WriteByte (&cls.netchan.message, clc_get_effect); // MSG_WriteByte (&cls.netchan.message, idx); } } break; case CE_HWSHEEPINATOR: revisionCode = MSG_ReadByte(); curEnt = (revisionCode >> 4) & 7; if (revisionCode & 1) /* impact */ { MSG_ReadCoord(); } else /* direction change */ { MSG_ReadAngle(); MSG_ReadAngle(); if (revisionCode & 128) /* new origin */ { MSG_ReadCoord(); MSG_ReadCoord(); MSG_ReadCoord(); /* create a clc message to retrieve effect information */ // MSG_WriteByte (&cls.netchan.message, clc_get_effect); // MSG_WriteByte (&cls.netchan.message, idx); } } break; case CE_HWDRILLA: revisionCode = MSG_ReadByte(); if (revisionCode == 0) /* impact */ { MSG_ReadCoord(); MSG_ReadCoord(); MSG_ReadCoord(); MSG_ReadByte(); } else /* turn */ { MSG_ReadAngle(); MSG_ReadAngle(); MSG_ReadCoord(); MSG_ReadCoord(); MSG_ReadCoord(); /* create a clc message to retrieve effect information */ // MSG_WriteByte (&cls.netchan.message, clc_get_effect); // MSG_WriteByte (&cls.netchan.message, idx); } break; } } } static void UpdateMissilePath (vec3_t oldorg, vec3_t neworg, vec3_t newvel, float time) { vec3_t endpos; /* the position it should be at currently */ float delta; delta = cl.time - time; VectorMA(neworg, delta, newvel, endpos); (void) endpos; /* set but not used?.. */ VectorCopy(neworg, oldorg); /* set orig, maybe vel too */ } void CL_TurnEffect (void) { int idx; entity_t *ent; vec3_t pos, vel; float time; idx = MSG_ReadByte (); time = MSG_ReadFloat (); pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); vel[0] = MSG_ReadCoord(); vel[1] = MSG_ReadCoord(); vel[2] = MSG_ReadCoord(); switch (cl.Effects[idx].type) { case CE_HWRAVENSTAFF: case CE_HWRAVENPOWER: case CE_BONESHARD: case CE_BONESHRAPNEL: case CE_HWBONEBALL: ent = &EffectEntities[cl.Effects[idx].ef.Missile.entity_index]; UpdateMissilePath(ent->origin, pos, vel, time); VectorCopy(vel, cl.Effects[idx].ef.Missile.velocity); vectoangles(cl.Effects[idx].ef.Missile.velocity, cl.Effects[idx].ef.Missile.angle); break; case CE_HWMISSILESTAR: case CE_HWEIDOLONSTAR: ent = &EffectEntities[cl.Effects[idx].ef.Star.entity_index]; UpdateMissilePath(ent->origin, pos, vel, time); VectorCopy(vel, cl.Effects[idx].ef.Star.velocity); break; case 0: /* create a clc message to retrieve effect information */ // MSG_WriteByte (&cls.netchan.message, clc_get_effect); // MSG_WriteByte (&cls.netchan.message, idx); // Con_Printf("%s: null effect %d\n", __thisfunc__, idx); break; default: Con_Printf ("%s: bad type %d\n", __thisfunc__, cl.Effects[idx].type); break; } } static void CL_LinkEntity (entity_t *ent) { if (cl_numvisedicts == MAX_VISEDICTS) { /* object list is full */ // Host_Error("%s: Out of vis edicts", __thisfunc__); return; } /* debug: if visedicts getting messed up, it should appear here. */ // if (ent->model < 10) // { // ent->model = 3; // } cl_visedicts[cl_numvisedicts++] = *ent; } void CL_UpdateEffects (void) { int i; int idx, cur_frame; vec3_t mymin, mymax; float frametime; // edict_t test; // trace_t trace; vec3_t org, org2, old_origin; int x_dir, y_dir; entity_t *ent, *ent2; float smoketime; entity_state_t *es; mleaf_t *l; frametime = host_frametime; if (!frametime) return; // Con_Printf("Here at %f\n",cl.time); for (idx = 0 ; idx < MAX_EFFECTS ; idx++) { if (!cl.Effects[idx].type) continue; switch (cl.Effects[idx].type) { case CE_RAIN: org[0] = cl.Effects[idx].ef.Rain.min_org[0]; org[1] = cl.Effects[idx].ef.Rain.min_org[1]; org[2] = cl.Effects[idx].ef.Rain.max_org[2]; org2[0] = cl.Effects[idx].ef.Rain.e_size[0]; org2[1] = cl.Effects[idx].ef.Rain.e_size[1]; org2[2] = cl.Effects[idx].ef.Rain.e_size[2]; x_dir = cl.Effects[idx].ef.Rain.dir[0]; y_dir = cl.Effects[idx].ef.Rain.dir[1]; cl.Effects[idx].ef.Rain.next_time += frametime; if (cl.Effects[idx].ef.Rain.next_time >= cl.Effects[idx].ef.Rain.wait) { R_RainEffect(org, org2, x_dir, y_dir, cl.Effects[idx].ef.Rain.color, cl.Effects[idx].ef.Rain.count); cl.Effects[idx].ef.Rain.next_time = 0; } break; case CE_FOUNTAIN: mymin[0] = (-3 * cl.Effects[idx].ef.Fountain.vright[0] * cl.Effects[idx].ef.Fountain.movedir[0]) + (-3 * cl.Effects[idx].ef.Fountain.vforward[0] * cl.Effects[idx].ef.Fountain.movedir[1]) + (2 * cl.Effects[idx].ef.Fountain.vup[0] * cl.Effects[idx].ef.Fountain.movedir[2]); mymin[1] = (-3 * cl.Effects[idx].ef.Fountain.vright[1] * cl.Effects[idx].ef.Fountain.movedir[0]) + (-3 * cl.Effects[idx].ef.Fountain.vforward[1] * cl.Effects[idx].ef.Fountain.movedir[1]) + (2 * cl.Effects[idx].ef.Fountain.vup[1] * cl.Effects[idx].ef.Fountain.movedir[2]); mymin[2] = (-3 * cl.Effects[idx].ef.Fountain.vright[2] * cl.Effects[idx].ef.Fountain.movedir[0]) + (-3 * cl.Effects[idx].ef.Fountain.vforward[2] * cl.Effects[idx].ef.Fountain.movedir[1]) + (2 * cl.Effects[idx].ef.Fountain.vup[2] * cl.Effects[idx].ef.Fountain.movedir[2]); mymin[0] *= 15; mymin[1] *= 15; mymin[2] *= 15; mymax[0] = (3 * cl.Effects[idx].ef.Fountain.vright[0] * cl.Effects[idx].ef.Fountain.movedir[0]) + (3 * cl.Effects[idx].ef.Fountain.vforward[0] * cl.Effects[idx].ef.Fountain.movedir[1]) + (10 * cl.Effects[idx].ef.Fountain.vup[0] * cl.Effects[idx].ef.Fountain.movedir[2]); mymax[1] = (3 * cl.Effects[idx].ef.Fountain.vright[1] * cl.Effects[idx].ef.Fountain.movedir[0]) + (3 * cl.Effects[idx].ef.Fountain.vforward[1] * cl.Effects[idx].ef.Fountain.movedir[1]) + (10 * cl.Effects[idx].ef.Fountain.vup[1] * cl.Effects[idx].ef.Fountain.movedir[2]); mymax[2] = (3 * cl.Effects[idx].ef.Fountain.vright[2] * cl.Effects[idx].ef.Fountain.movedir[0]) + (3 * cl.Effects[idx].ef.Fountain.vforward[2] * cl.Effects[idx].ef.Fountain.movedir[1]) + (10 * cl.Effects[idx].ef.Fountain.vup[2] * cl.Effects[idx].ef.Fountain.movedir[2]); mymax[0] *= 15; mymax[1] *= 15; mymax[2] *= 15; R_RunParticleEffect2 (cl.Effects[idx].ef.Fountain.pos, mymin, mymax, cl.Effects[idx].ef.Fountain.color, pt_fastgrav, cl.Effects[idx].ef.Fountain.cnt); /* memset(&test, 0, sizeof(test)); trace = SV_Move (cl.Effects[idx].ef.Fountain.pos, mymin, mymax, mymin, false, &test); Con_Printf("Fraction is %f\n", trace.fraction); */ break; case CE_QUAKE: R_RunQuakeEffect (cl.Effects[idx].ef.Quake.origin, cl.Effects[idx].ef.Quake.radius); break; case CE_RIPPLE: cl.Effects[idx].ef.Smoke.time_amount += frametime; ent = &EffectEntities[cl.Effects[idx].ef.Smoke.entity_index]; smoketime = cl.Effects[idx].ef.Smoke.framelength; if (!smoketime) smoketime = HX_FRAME_TIME*2; while (cl.Effects[idx].ef.Smoke.time_amount >= smoketime && ent->scale < 250) { ent->frame++; ent->angles[1] += 1; cl.Effects[idx].ef.Smoke.time_amount -= smoketime; } if (ent->frame >= 10) { CL_FreeEffect(idx); } else { CL_LinkEntity(ent); } break; case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK1: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: case CE_FLAMESTREAM: case CE_ACID_MUZZFL: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: cl.Effects[idx].ef.Smoke.time_amount += frametime; ent = &EffectEntities[cl.Effects[idx].ef.Smoke.entity_index]; smoketime = cl.Effects[idx].ef.Smoke.framelength; if (!smoketime) smoketime = HX_FRAME_TIME; ent->origin[0] += (frametime/smoketime) * cl.Effects[idx].ef.Smoke.velocity[0]; ent->origin[1] += (frametime/smoketime) * cl.Effects[idx].ef.Smoke.velocity[1]; ent->origin[2] += (frametime/smoketime) * cl.Effects[idx].ef.Smoke.velocity[2]; i = 0; while (cl.Effects[idx].ef.Smoke.time_amount >= smoketime) { ent->frame++; i++; cl.Effects[idx].ef.Smoke.time_amount -= smoketime; } if (ent->frame >= ent->model->numframes) { CL_FreeEffect(idx); } else { CL_LinkEntity(ent); } if (cl.Effects[idx].type == CE_TELESMK1) { ent = &EffectEntities[cl.Effects[idx].ef.Smoke.entity_index2]; ent->origin[0] -= (frametime/smoketime) * cl.Effects[idx].ef.Smoke.velocity[0]; ent->origin[1] -= (frametime/smoketime) * cl.Effects[idx].ef.Smoke.velocity[1]; ent->origin[2] -= (frametime/smoketime) * cl.Effects[idx].ef.Smoke.velocity[2]; ent->frame += i; if (ent->frame < ent->model->numframes) { CL_LinkEntity(ent); } } break; /* Just go through animation and then remove */ case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_SM_EXPLOSION2: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_BRN_BOUNCE: case CE_ACID_HIT: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_LBALL_EXPL: case CE_FBOOM: case CE_BOMB: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: cl.Effects[idx].ef.Smoke.time_amount += frametime; ent = &EffectEntities[cl.Effects[idx].ef.Smoke.entity_index]; if (cl.Effects[idx].type != CE_BG_CIRCLE_EXP) { while (cl.Effects[idx].ef.Smoke.time_amount >= HX_FRAME_TIME) { ent->frame++; cl.Effects[idx].ef.Smoke.time_amount -= HX_FRAME_TIME; } } else { while (cl.Effects[idx].ef.Smoke.time_amount >= HX_FRAME_TIME * 2) { ent->frame++; cl.Effects[idx].ef.Smoke.time_amount -= HX_FRAME_TIME * 2; } } if (ent->frame >= ent->model->numframes) { CL_FreeEffect(idx); } else { CL_LinkEntity(ent); } break; /* Go forward then backward through animation then remove */ case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_HWSPLITFLASH: case CE_RED_FLASH: cl.Effects[idx].ef.Flash.time_amount += frametime; ent = &EffectEntities[cl.Effects[idx].ef.Flash.entity_index]; while (cl.Effects[idx].ef.Flash.time_amount >= HX_FRAME_TIME) { if (!cl.Effects[idx].ef.Flash.reverse) { if (ent->frame >= ent->model->numframes-1) { /* Ran through forward animation */ cl.Effects[idx].ef.Flash.reverse = 1; ent->frame--; } else ent->frame++; } else { ent->frame--; } cl.Effects[idx].ef.Flash.time_amount -= HX_FRAME_TIME; } if ((ent->frame <= 0) && (cl.Effects[idx].ef.Flash.reverse)) { CL_FreeEffect(idx); } else { CL_LinkEntity(ent); } break; case CE_RIDER_DEATH: { float sinval, cosval; cl.Effects[idx].ef.RD.time_amount += frametime; if (cl.Effects[idx].ef.RD.time_amount >= 1) { cl.Effects[idx].ef.RD.stage++; cl.Effects[idx].ef.RD.time_amount -= 1; } VectorCopy(cl.Effects[idx].ef.RD.origin, org); q_sincosrad(cl.Effects[idx].ef.RD.time_amount * 2 * M_PI, &sinval, &cosval); org[0] += sinval * 30; org[1] += cosval * 30; if (cl.Effects[idx].ef.RD.stage <= 6) { RiderParticle(cl.Effects[idx].ef.RD.stage + 1, org); } else { /* To set the rider's origin point for the particles */ RiderParticle(0, org); if (cl.Effects[idx].ef.RD.stage == 7) { cl.cshifts[CSHIFT_BONUS].destcolor[0] = 255; cl.cshifts[CSHIFT_BONUS].destcolor[1] = 255; cl.cshifts[CSHIFT_BONUS].destcolor[2] = 255; cl.cshifts[CSHIFT_BONUS].percent = 256; } else if (cl.Effects[idx].ef.RD.stage > 13) { // cl.Effects[idx].ef.RD.stage = 0; CL_FreeEffect(idx); } } } break; case CE_TELEPORTERPUFFS: cl.Effects[idx].ef.Teleporter.time_amount += frametime; smoketime = cl.Effects[idx].ef.Teleporter.framelength; ent = &EffectEntities[cl.Effects[idx].ef.Teleporter.entity_index[0]]; while (cl.Effects[idx].ef.Teleporter.time_amount >= HX_FRAME_TIME) { ent->frame++; cl.Effects[idx].ef.Teleporter.time_amount -= HX_FRAME_TIME; } cur_frame = ent->frame; if (cur_frame >= ent->model->numframes) { CL_FreeEffect(idx); break; } for (i = 0 ; i < 8 ; ++i) { ent = &EffectEntities[cl.Effects[idx].ef.Teleporter.entity_index[i]]; ent->origin[0] += (frametime/smoketime) * cl.Effects[idx].ef.Teleporter.velocity[i][0]; ent->origin[1] += (frametime/smoketime) * cl.Effects[idx].ef.Teleporter.velocity[i][1]; ent->origin[2] += (frametime/smoketime) * cl.Effects[idx].ef.Teleporter.velocity[i][2]; ent->frame = cur_frame; CL_LinkEntity(ent); } break; case CE_TELEPORTERBODY: cl.Effects[idx].ef.Teleporter.time_amount += frametime; smoketime = cl.Effects[idx].ef.Teleporter.framelength; ent = &EffectEntities[cl.Effects[idx].ef.Teleporter.entity_index[0]]; while (cl.Effects[idx].ef.Teleporter.time_amount >= HX_FRAME_TIME) { ent->scale -= 15; cl.Effects[idx].ef.Teleporter.time_amount -= HX_FRAME_TIME; } ent = &EffectEntities[cl.Effects[idx].ef.Teleporter.entity_index[0]]; ent->angles[1] += 45; if (ent->scale <= 10) { CL_FreeEffect(idx); } else { CL_LinkEntity(ent); } break; case CE_HWDRILLA: cl.Effects[idx].ef.Missile.time_amount += frametime; ent = &EffectEntities[cl.Effects[idx].ef.Missile.entity_index]; if ((int)(cl.time) != (int)(cl.time - host_frametime)) { S_StartSound (TempSoundChannel(), 1, cl_fxsfx_drillaspin, ent->origin, 1, 1); } ent->angles[0] += frametime * cl.Effects[idx].ef.Missile.avelocity[0]; ent->angles[1] += frametime * cl.Effects[idx].ef.Missile.avelocity[1]; ent->angles[2] += frametime * cl.Effects[idx].ef.Missile.avelocity[2]; VectorCopy(ent->origin, old_origin); ent->origin[0] += frametime * cl.Effects[idx].ef.Missile.velocity[0]; ent->origin[1] += frametime * cl.Effects[idx].ef.Missile.velocity[1]; ent->origin[2] += frametime * cl.Effects[idx].ef.Missile.velocity[2]; R_RocketTrail (old_origin, ent->origin, rt_setstaff); CL_LinkEntity(ent); break; case CE_HWXBOWSHOOT: cl.Effects[idx].ef.Xbow.time_amount += frametime; for (i = 0 ; i < cl.Effects[idx].ef.Xbow.bolts ; i++) { /* only update valid effect ents */ if (cl.Effects[idx].ef.Xbow.ent[i] != -1) { if (cl.Effects[idx].ef.Xbow.activebolts & (1<origin[0] += frametime * cl.Effects[idx].ef.Xbow.vel[i][0]; ent->origin[1] += frametime * cl.Effects[idx].ef.Xbow.vel[i][1]; ent->origin[2] += frametime * cl.Effects[idx].ef.Xbow.vel[i][2]; CL_LinkEntity(ent); } else if (cl.Effects[idx].ef.Xbow.bolts == 5) { /* fiery bolts don't just go away */ if (cl.Effects[idx].ef.Xbow.state[i] == 0) { /* waiting to explode state */ if (cl.Effects[idx].ef.Xbow.gonetime[i] > cl.time) { /* fiery bolts stick around for a while */ ent = &EffectEntities[cl.Effects[idx].ef.Xbow.ent[i]]; CL_LinkEntity(ent); } else { /* when time's up on fiery guys, they explode * set state to exploding */ cl.Effects[idx].ef.Xbow.state[i] = 1; ent = &EffectEntities[cl.Effects[idx].ef.Xbow.ent[i]]; /* move bolt back a little to make explosion look better */ VectorNormalizeFast(cl.Effects[idx].ef.Xbow.vel[i]); VectorScale(cl.Effects[idx].ef.Xbow.vel[i], -8, cl.Effects[idx].ef.Xbow.vel[i]); VectorAdd(ent->origin, cl.Effects[idx].ef.Xbow.vel[i], ent->origin); /* turn bolt entity into an explosion */ ent->model = Mod_ForName("models/xbowexpl.spr", true); ent->frame = 0; /* set frame change counter */ cl.Effects[idx].ef.Xbow.gonetime[i] = cl.time + HX_FRAME_TIME * 2; /* play explosion sound */ S_StartSound (TempSoundChannel(), 1, cl_fxsfx_explode, ent->origin, 1, 1); CL_LinkEntity(ent); } } else if (cl.Effects[idx].ef.Xbow.state[i] == 1) { /* fiery bolt exploding state */ ent = &EffectEntities[cl.Effects[idx].ef.Xbow.ent[i]]; /* increment frame if it's time */ while (cl.Effects[idx].ef.Xbow.gonetime[i] <= cl.time) { ent->frame++; cl.Effects[idx].ef.Xbow.gonetime[i] += HX_FRAME_TIME * 0.75; } if (ent->frame >= ent->model->numframes) { cl.Effects[idx].ef.Xbow.state[i] = 2; /* if anim is over, set me to inactive state */ } else { CL_LinkEntity(ent); } } } } } break; case CE_HWSHEEPINATOR: cl.Effects[idx].ef.Xbow.time_amount += frametime; for (i = 0 ; i < cl.Effects[idx].ef.Xbow.bolts ; i++) { /* only update valid effect ents */ if (cl.Effects[idx].ef.Xbow.ent[i] != -1) { if (cl.Effects[idx].ef.Xbow.activebolts & (1<origin[0] += frametime * cl.Effects[idx].ef.Xbow.vel[i][0]; ent->origin[1] += frametime * cl.Effects[idx].ef.Xbow.vel[i][1]; ent->origin[2] += frametime * cl.Effects[idx].ef.Xbow.vel[i][2]; R_RunParticleEffect4(ent->origin, 7, (rand() % 15) + 144, pt_explode2, (rand() % 5) + 1); CL_LinkEntity(ent); } } } break; case CE_DEATHBUBBLES: cl.Effects[idx].ef.Bubble.time_amount += frametime; if (cl.Effects[idx].ef.Bubble.time_amount > 0.1)/* 10 bubbles a sec */ { cl.Effects[idx].ef.Bubble.time_amount = 0; cl.Effects[idx].ef.Bubble.count--; es = FindState(cl.Effects[idx].ef.Bubble.owner); if (es) { VectorCopy(es->origin, org); VectorAdd(org, cl.Effects[idx].ef.Bubble.offset, org); l = Mod_PointInLeaf (org, cl.worldmodel); if (l->contents != CONTENTS_WATER) { /* not in water anymore */ CL_FreeEffect(idx); break; } else { CLTENT_SpawnDeathBubble(org); } } } if (cl.Effects[idx].ef.Bubble.count <= 0) CL_FreeEffect(idx); break; case CE_SCARABCHAIN: cl.Effects[idx].ef.Chain.time_amount += frametime; ent = &EffectEntities[cl.Effects[idx].ef.Chain.ent1]; switch (cl.Effects[idx].ef.Chain.state) { case 0: /* zooming in toward owner */ es = FindState(cl.Effects[idx].ef.Chain.owner); if (cl.Effects[idx].ef.Chain.sound_time <= cl.time) { cl.Effects[idx].ef.Chain.sound_time = cl.time + 0.5; S_StartSound (TempSoundChannel(), 1, cl_fxsfx_scarabhome, ent->origin, 1, 1); } if (es) { VectorCopy(es->origin, org); org[2] += cl.Effects[idx].ef.Chain.height; VectorSubtract(org, ent->origin, org); if (fabs(VectorNormalizeFast(org)) < 500*frametime) { S_StartSound (TempSoundChannel(), 1, cl_fxsfx_scarabgrab, ent->origin, 1, 1); cl.Effects[idx].ef.Chain.state = 1; VectorCopy(es->origin, ent->origin); ent->origin[2] += cl.Effects[idx].ef.Chain.height; XbowImpactPuff(ent->origin, cl.Effects[idx].ef.Chain.material); } else { VectorScale(org, 500*frametime, org); VectorAdd(ent->origin, org, ent->origin); } } break; case 1: /* attached--snap to owner's pos */ es = FindState(cl.Effects[idx].ef.Chain.owner); if (es) { VectorCopy(es->origin, ent->origin); ent->origin[2] += cl.Effects[idx].ef.Chain.height; } break; case 2: /* unattaching, server needs to set this state */ VectorCopy(ent->origin, org); VectorSubtract(cl.Effects[idx].ef.Chain.origin, org, org); if (fabs(VectorNormalizeFast(org)) > 350*frametime) /* closer than 30 is too close? */ { VectorScale(org, 350*frametime, org); VectorAdd(ent->origin, org, ent->origin); } else /* done -- flash & get outa here (change type to redflash) */ { S_StartSound (TempSoundChannel(), 1, cl_fxsfx_scarabbyebye, ent->origin, 1, 1); cl.Effects[idx].ef.Flash.entity_index = cl.Effects[idx].ef.Chain.ent1; cl.Effects[idx].type = CE_RED_FLASH; VectorCopy(ent->origin, cl.Effects[idx].ef.Flash.origin); cl.Effects[idx].ef.Flash.reverse = 0; ent->model = Mod_ForName("models/redspt.spr", true); ent->frame = 0; ent->drawflags = DRF_TRANSLUCENT; } break; } CL_LinkEntity(ent); /* damndamndamn--add stream stuff here! */ VectorCopy(cl.Effects[idx].ef.Chain.origin, org); VectorCopy(ent->origin, org2); CreateStream(TE_STREAM_CHAIN, cl.Effects[idx].ef.Chain.ent1, 1, cl.Effects[idx].ef.Chain.tag, 0.1, 0, org, org2); break; case CE_TRIPMINESTILL: cl.Effects[idx].ef.Chain.time_amount += frametime; ent = &EffectEntities[cl.Effects[idx].ef.Chain.ent1]; // if (cl.Effects[idx].ef.Chain.ent1 < 0)//fixme: remove this!!! // Con_DPrintf("OHSHITOHSHIT--bad chain ent\n"); CL_LinkEntity(ent); // Con_DPrintf("Chain Ent at: %d %d %d\n", (int)cl.Effects[idx].ef.Chain.origin[0], // (int)cl.Effects[idx].ef.Chain.origin[1], // (int)cl.Effects[idx].ef.Chain.origin[2]); /* damndamndamn--add stream stuff here! */ VectorCopy(cl.Effects[idx].ef.Chain.origin, org); VectorCopy(ent->origin, org2); CreateStream(TE_STREAM_CHAIN, cl.Effects[idx].ef.Chain.ent1, 1, 1, 0.1, 0, org, org2); break; case CE_TRIPMINE: cl.Effects[idx].ef.Chain.time_amount += frametime; ent = &EffectEntities[cl.Effects[idx].ef.Chain.ent1]; ent->origin[0] += frametime * cl.Effects[idx].ef.Chain.velocity[0]; ent->origin[1] += frametime * cl.Effects[idx].ef.Chain.velocity[1]; ent->origin[2] += frametime * cl.Effects[idx].ef.Chain.velocity[2]; CL_LinkEntity(ent); /* damndamndamn--add stream stuff here! */ VectorCopy(cl.Effects[idx].ef.Chain.origin, org); VectorCopy(ent->origin, org2); CreateStream(TE_STREAM_CHAIN, cl.Effects[idx].ef.Chain.ent1, 1, 1, 0.1, 0, org, org2); break; case CE_BONESHARD: case CE_BONESHRAPNEL: case CE_HWBONEBALL: case CE_HWRAVENSTAFF: case CE_HWRAVENPOWER: cl.Effects[idx].ef.Missile.time_amount += frametime; ent = &EffectEntities[cl.Effects[idx].ef.Missile.entity_index]; // ent->angles[0] = cl.Effects[idx].ef.Missile.angle[0]; // ent->angles[1] = cl.Effects[idx].ef.Missile.angle[1]; // ent->angles[2] = cl.Effects[idx].ef.Missile.angle[2]; ent->angles[0] += frametime * cl.Effects[idx].ef.Missile.avelocity[0]; ent->angles[1] += frametime * cl.Effects[idx].ef.Missile.avelocity[1]; ent->angles[2] += frametime * cl.Effects[idx].ef.Missile.avelocity[2]; ent->origin[0] += frametime * cl.Effects[idx].ef.Missile.velocity[0]; ent->origin[1] += frametime * cl.Effects[idx].ef.Missile.velocity[1]; ent->origin[2] += frametime * cl.Effects[idx].ef.Missile.velocity[2]; if (cl.Effects[idx].type == CE_HWRAVENPOWER) { while (cl.Effects[idx].ef.Missile.time_amount >= HX_FRAME_TIME) { ent->frame++; cl.Effects[idx].ef.Missile.time_amount -= HX_FRAME_TIME; } if (ent->frame > 7) { ent->frame = 0; break; } } CL_LinkEntity(ent); if (cl.Effects[idx].type == CE_HWBONEBALL) { R_RunParticleEffect4 (ent->origin, 10, 368 + (rand() % 16), pt_slowgrav, 3); } break; case CE_HWMISSILESTAR: case CE_HWEIDOLONSTAR: /* update scale */ if (cl.Effects[idx].ef.Star.scaleDir) { cl.Effects[idx].ef.Star.scale += 0.05; if (cl.Effects[idx].ef.Star.scale >= 1) { cl.Effects[idx].ef.Star.scaleDir = 0; } } else { cl.Effects[idx].ef.Star.scale -= 0.05; if (cl.Effects[idx].ef.Star.scale <= 0.01) { cl.Effects[idx].ef.Star.scaleDir = 1; } } cl.Effects[idx].ef.Star.time_amount += frametime; ent = &EffectEntities[cl.Effects[idx].ef.Star.entity_index]; ent->angles[0] += frametime * cl.Effects[idx].ef.Star.avelocity[0]; ent->angles[1] += frametime * cl.Effects[idx].ef.Star.avelocity[1]; ent->angles[2] += frametime * cl.Effects[idx].ef.Star.avelocity[2]; ent->origin[0] += frametime * cl.Effects[idx].ef.Star.velocity[0]; ent->origin[1] += frametime * cl.Effects[idx].ef.Star.velocity[1]; ent->origin[2] += frametime * cl.Effects[idx].ef.Star.velocity[2]; CL_LinkEntity(ent); if (cl.Effects[idx].ef.Star.ent1 != -1) { ent2= &EffectEntities[cl.Effects[idx].ef.Star.ent1]; VectorCopy(ent->origin, ent2->origin); ent2->scale = cl.Effects[idx].ef.Star.scale * 100; ent2->angles[1] += frametime * 300; ent2->angles[2] += frametime * 400; CL_LinkEntity(ent2); } if (cl.Effects[idx].type == CE_HWMISSILESTAR) { if (cl.Effects[idx].ef.Star.ent2 != -1) { ent2 = &EffectEntities[cl.Effects[idx].ef.Star.ent2]; VectorCopy(ent->origin, ent2->origin); ent2->scale = cl.Effects[idx].ef.Star.scale * 100; ent2->angles[1] += frametime * -300; ent2->angles[2] += frametime * -400; CL_LinkEntity(ent2); } } if (rand() % 10 < 3) { R_RunParticleEffect4 (ent->origin, 7, 148 + (rand() % 11), pt_grav, 10 + (rand() % 10)); } break; } } // Con_DPrintf("Effect Ents: %d\n",EffectEntityCount); } /* this creates multi effects from one packet */ void CL_ParseMultiEffect (void) { int type, idx, count; vec3_t orig, vel; entity_t *ent; type = MSG_ReadByte(); switch (type) { case CE_HWRAVENPOWER: orig[0] = MSG_ReadCoord(); orig[1] = MSG_ReadCoord(); orig[2] = MSG_ReadCoord(); vel[0] = MSG_ReadCoord(); vel[1] = MSG_ReadCoord(); vel[2] = MSG_ReadCoord(); for (count = 0 ; count < 3 ; count++) { idx = MSG_ReadByte(); /* create the effect */ cl.Effects[idx].type = type; VectorCopy(orig, cl.Effects[idx].ef.Missile.origin); VectorCopy(vel, cl.Effects[idx].ef.Missile.velocity); vectoangles(cl.Effects[idx].ef.Missile.velocity, cl.Effects[idx].ef.Missile.angle); if ((cl.Effects[idx].ef.Missile.entity_index = NewEffectEntity()) != -1) { ent = &EffectEntities[cl.Effects[idx].ef.Missile.entity_index]; VectorCopy(cl.Effects[idx].ef.Missile.origin, ent->origin); VectorCopy(cl.Effects[idx].ef.Missile.angle, ent->angles); ent->model = Mod_ForName("models/ravproj.mdl", true); S_StartSound (TempSoundChannel(), 1, cl_fxsfx_ravengo, cl.Effects[idx].ef.Missile.origin, 1, 1); } else { cl.Effects[idx].type = CE_NONE; } } CreateRavenExplosions(orig); break; default: Host_Error ("%s: bad type", __thisfunc__); } } //========================================================================== // // NewEffectEntity // //========================================================================== static int NewEffectEntity (void) { entity_t *ent; int counter; if (cl_numvisedicts == MAX_VISEDICTS) return -1; if (EffectEntityCount == MAX_EFFECT_ENTITIES) return -1; for (counter = 0 ; counter < MAX_EFFECT_ENTITIES ; counter++) { if (!EntityUsed[counter]) break; } EntityUsed[counter] = true; EffectEntityCount++; ent = &EffectEntities[counter]; memset(ent, 0, sizeof(*ent)); ent->colormap = vid.colormap; return counter; } static void FreeEffectEntity (int idx) { if (idx == -1) return; if (EntityUsed[idx]) { EntityUsed[idx] = false; EffectEntityCount--; } else /* FIXME: CAN THIS REALLY HAPPEN?? -- O.S. */ { // Con_DPrintf("ERROR: Redeleting Effect Entity: %d!\n",idx); /* still decrement counter; since value is -1, counter was incremented. */ EffectEntityCount--; } } engine/hexenworld/client/cl_ents.c000066400000000000000000001162311444734033100175620ustar00rootroot00000000000000/* cl_ents.c -- entity parsing and management * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" extern cvar_t cl_predict_players; extern cvar_t cl_predict_players2; extern cvar_t cl_solid_players; extern qmodel_t *player_models[MAX_PLAYER_CLASS]; static struct predicted_player { int flags; qboolean active; vec3_t origin; // predicted origin } predicted_players[MAX_CLIENTS]; #define U_MODEL (1<<16) #define U_SOUND (1<<17) #define U_DRAWFLAGS (1<<18) #define U_ABSLIGHT (1<<19) static const char *parsedelta_strings[] = { "U_ANGLE1", //0 "U_ANGLE3", //1 "U_SCALE", //2 "U_COLORMAP", //3 "U_SKIN", //4 "U_EFFECTS", //5 "U_MODEL16", //6 "U_MOREBITS2", //7 "", //8 is unused "U_ORIGIN1", //9 "U_ORIGIN2", //10 "U_ORIGIN3", //11 "U_ANGLE2", //12 "U_FRAME", //13 "U_REMOVE", //14 "U_MOREBITS", //15 "U_MODEL", //16 "U_SOUND", //17 "U_DRAWFLAGS", //18 "U_ABSLIGHT" //19 }; //============================================================ /* =============== CL_AllocDlight =============== */ dlight_t *CL_AllocDlight (int key) { int i; dlight_t *dl; // first look for an exact key match dl = cl_dlights; if (key) { for (i = 0; i < MAX_DLIGHTS; i++, dl++) { if (dl->key == key) goto done; } } // then look for anything else dl = cl_dlights; for (i = 0; i < MAX_DLIGHTS; i++, dl++) { if (dl->die < cl.time) goto done; } dl = &cl_dlights[0]; done: memset (dl, 0, sizeof(*dl)); dl->key = key; dl->color[0] = dl->color[1] = dl->color[2] = dl->color[3] = 1.0; return dl; } /* =============== CL_NewDlight =============== */ #if 0 /* all of the uses are commented out */ static void CL_NewDlight (int key, float x, float y, float z, float radius, float time, int type) { dlight_t *dl; dl = CL_AllocDlight (key); dl->origin[0] = x; dl->origin[1] = y; dl->origin[2] = z; dl->radius = radius; dl->die = cl.time + time; if (type == 0) { dl->color[0] = 0.2; dl->color[1] = 0.1; dl->color[2] = 0.05; dl->color[3] = 0.7; } else if (type == 1) { dl->color[0] = 0.05; dl->color[1] = 0.05; dl->color[2] = 0.3; dl->color[3] = 0.7; } else if (type == 2) { dl->color[0] = 0.5; dl->color[1] = 0.05; dl->color[2] = 0.05; dl->color[3] = 0.7; } else if (type == 3) { dl->color[0]=0.5; dl->color[1] = 0.05; dl->color[2] = 0.4; dl->color[3] = 0.7; } } #endif /* =============== CL_DecayLights =============== */ void CL_DecayLights (void) { dlight_t *dl; int i; dl = cl_dlights; for (i = 0; i < MAX_DLIGHTS; i++, dl++) { if (dl->die < cl.time || !dl->radius) continue; if (dl->radius > 0) { dl->radius -= host_frametime*dl->decay; if (dl->radius < 0) dl->radius = 0; } else { dl->radius += host_frametime*dl->decay; if (dl->radius > 0) dl->radius = 0; } } } /* ========================================================================= PACKET ENTITY PARSING / LINKING ========================================================================= */ /* ================== CL_ParseDelta Can go from either a baseline or a previous packet_entity ================== */ static void ShowNetParseDelta(int x) { int i; char orstring[2]; if (cl_shownet.integer != 2) return; orstring[0]=0; orstring[1]=0; Con_Printf("bits: "); for (i = 0; i <= 19; i++) { if (x != 8) { if (x & (1 << i)) { Con_Printf("%s%s",orstring,parsedelta_strings[i]); orstring[0]= '|'; } } } Con_Printf("\n"); } static int bitcounts[32]; /// just for protocol profiling static void CL_ParseDelta (entity_state_t *from, entity_state_t *to, int bits) { int i; // set everything to the state we are delta'ing from *to = *from; to->number = bits & 511; bits &= ~511; if (bits & U_MOREBITS) { // read in the low order bits i = MSG_ReadByte (); bits |= i; } if (bits & U_MOREBITS2) { i = MSG_ReadByte (); bits |= (i << 16); } ShowNetParseDelta(bits); // count the bits for net profiling for (i = 0; i < 19; i++) { if (bits & (1<flags = bits; if (bits & U_MODEL) { if (bits & U_MODEL16) to->modelindex = MSG_ReadShort (); else to->modelindex = MSG_ReadByte (); } if (bits & U_FRAME) to->frame = MSG_ReadByte (); if (bits & U_COLORMAP) to->colormap = MSG_ReadByte(); if (bits & U_SKIN) to->skinnum = MSG_ReadByte(); if (bits & U_DRAWFLAGS) to->drawflags = MSG_ReadByte(); if (bits & U_EFFECTS) to->effects = MSG_ReadLong(); if (bits & U_ORIGIN1) to->origin[0] = MSG_ReadCoord (); if (bits & U_ANGLE1) to->angles[0] = MSG_ReadAngle(); if (bits & U_ORIGIN2) to->origin[1] = MSG_ReadCoord (); if (bits & U_ANGLE2) to->angles[1] = MSG_ReadAngle(); if (bits & U_ORIGIN3) to->origin[2] = MSG_ReadCoord (); if (bits & U_ANGLE3) to->angles[2] = MSG_ReadAngle(); if (bits & U_SCALE) to->scale = MSG_ReadByte(); if (bits & U_ABSLIGHT) to->abslight = MSG_ReadByte(); if (bits & U_SOUND) { i = MSG_ReadShort(); S_StartSound(to->number, 1, cl.sound_precache[i], to->origin, 1.0, 1.0); } } /* ================= FlushEntityPacket ================= */ static void FlushEntityPacket (void) { entity_state_t olde, newe; int word; Con_DPrintf ("FlushEntityPacket\n"); memset (&olde, 0, sizeof(olde)); cl.validsequence = 0; // can't render a frame cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].invalid = true; // read it all, but ignore it while (1) { word = (unsigned short)MSG_ReadShort (); if (msg_badread) { // something didn't parse right... Host_EndGame ("msg_badread in packetentities\n"); return; } if (!word) break; // done CL_ParseDelta (&olde, &newe, word); } } /* ================== CL_ParsePacketEntities An svc_packetentities has just been parsed, deal with the rest of the data stream. ================== */ void CL_ParsePacketEntities (qboolean delta) { packet_entities_t *oldp, *newp, dummy; int oldpacket, newpacket; int oldindex, newindex; int word, newnum, oldnum; qboolean full; byte from; newpacket = cls.netchan.incoming_sequence&UPDATE_MASK; newp = &cl.frames[newpacket].packet_entities; cl.frames[newpacket].invalid = false; if (delta) { from = MSG_ReadByte (); oldpacket = cl.frames[newpacket].delta_sequence; if ((from & UPDATE_MASK) != (oldpacket & UPDATE_MASK)) Con_DPrintf ("WARNING: from mismatch\n"); } else { oldpacket = -1; } full = false; if (oldpacket != -1) { if (cls.netchan.outgoing_sequence - oldpacket >= UPDATE_BACKUP-1) { // we can't use this, it is too old FlushEntityPacket (); return; } cl.validsequence = cls.netchan.incoming_sequence; oldp = &cl.frames[oldpacket&UPDATE_MASK].packet_entities; } else { // this is a full update that we can start delta compressing from now oldp = &dummy; dummy.num_entities = 0; cl.validsequence = cls.netchan.incoming_sequence; full = true; } oldindex = 0; newindex = 0; newp->num_entities = 0; while (1) { word = (unsigned short)MSG_ReadShort (); if (msg_badread) { // something didn't parse right... Host_EndGame ("msg_badread in packetentities\n"); return; } if (!word) { while (oldindex < oldp->num_entities) { // copy all the rest of the entities from the old packet //Con_Printf ("copy %i\n", oldp->entities[oldindex].number); if (newindex >= MAX_PACKET_ENTITIES) Host_EndGame ("%s: newindex == MAX_PACKET_ENTITIES", __thisfunc__); newp->entities[newindex] = oldp->entities[oldindex]; newindex++; oldindex++; } break; } newnum = word & 511; oldnum = oldindex >= oldp->num_entities ? 9999 : oldp->entities[oldindex].number; while (newnum > oldnum) { if (full) { Con_Printf ("WARNING: oldcopy on full update"); FlushEntityPacket (); return; } //Con_Printf ("copy %i\n", oldnum); // copy one of the old entities over to the new packet unchanged if (newindex >= MAX_PACKET_ENTITIES) Host_EndGame ("%s: newindex == MAX_PACKET_ENTITIES", __thisfunc__); newp->entities[newindex] = oldp->entities[oldindex]; newindex++; oldindex++; oldnum = oldindex >= oldp->num_entities ? 9999 : oldp->entities[oldindex].number; } if (newnum < oldnum) { // new from baseline //Con_Printf ("baseline %i\n", newnum); if (word & U_REMOVE) { if (full) { cl.validsequence = 0; Con_Printf ("WARNING: U_REMOVE on full update\n"); FlushEntityPacket (); return; } continue; } if (newindex >= MAX_PACKET_ENTITIES) Host_EndGame ("%s: newindex == MAX_PACKET_ENTITIES", __thisfunc__); CL_ParseDelta (&cl_baselines[newnum], &newp->entities[newindex], word); newindex++; continue; } if (newnum == oldnum) { // delta from previous if (full) { cl.validsequence = 0; Con_Printf ("WARNING: delta on full update"); } if (word & U_REMOVE) { oldindex++; continue; } //Con_Printf ("delta %i\n",newnum); CL_ParseDelta (&oldp->entities[oldindex], &newp->entities[newindex], word); newindex++; oldindex++; } } newp->num_entities = newindex; } static void HandleEffects(int effects, int number, entity_t *ent, vec3_t oldOrg) { dlight_t *dl; int rotateSet = 0; // Effect Flags if (effects & EF_BRIGHTFIELD) { // show that this guy is cool or something... //R_EntityParticles (ent); dl = CL_AllocDlight (number); //dl->color[0] = .5 + cos(cl.time*7)*.5; //dl->color[1] = .5 + cos(cl.time*7 + M_PI*2/3)*.5; //dl->color[2] = .5 + cos(cl.time*7 + M_PI*4/3)*.5; //dl->color[3] = 1.0; VectorCopy (ent->origin, dl->origin); dl->radius = 200 + q_cosrad(cl.time*5)*100; dl->die = cl.time + 0.001; R_BrightFieldSource (ent->origin); } if (effects & EF_DARKFIELD) R_DarkFieldParticles (ent); if (effects & EF_MUZZLEFLASH) { vec3_t fv, rv, uv; dl = CL_AllocDlight (number); VectorCopy (ent->origin, dl->origin); dl->origin[2] += 16; AngleVectors (ent->angles, fv, rv, uv); VectorMA (dl->origin, 18, fv, dl->origin); dl->radius = 200 + (rand() & 31); dl->minlight = 32; dl->die = cl.time + 0.1; /* since the server never sends svc_muzzleflash, CL_MuzzleFlash() of cl_parse.c does not handle this for us */ # ifdef GLQUAKE if (gl_colored_dynamic_lights.integer) { // Make the dynamic light yellow dl->color[0] = 1.0; dl->color[1] = 1.0; dl->color[2] = 0.5; dl->color[3] = 0.7; } # endif } if (effects & EF_BRIGHTLIGHT) { dl = CL_AllocDlight (number); VectorCopy (ent->origin, dl->origin); dl->origin[2] += 16; dl->radius = 400 + (rand() & 31); dl->die = cl.time + 0.001; # ifdef GLQUAKE if (gl_colored_dynamic_lights.integer) { dl->color[0] = 0.8; dl->color[1] = 0.8; dl->color[2] = 1.0; dl->color[3] = 0.7; } # endif } if (effects & EF_DIMLIGHT) { dl = CL_AllocDlight (number); VectorCopy (ent->origin, dl->origin); dl->radius = 200 + (rand() & 31); dl->die = cl.time + 0.001; # ifdef GLQUAKE if (gl_colored_dynamic_lights.integer) { dl->color[0] = 0.8; dl->color[1] = 0.6; dl->color[2] = 0.2; dl->color[3] = 0.7; } # endif } /* if (effects & EF_DARKLIGHT) { dl = CL_AllocDlight (number); VectorCopy (ent->origin, dl->origin); dl->radius = 200.0 + (rand() & 31); dl->die = cl.time + 0.001; //rjr dl->dark = true; } */ if (effects & EF_LIGHT) { dl = CL_AllocDlight (number); VectorCopy (ent->origin, dl->origin); dl->radius = 200; dl->die = cl.time + 0.001; # ifdef GLQUAKE if (gl_colored_dynamic_lights.integer) { dl->color[0] = 0.8; dl->color[1] = 0.4; dl->color[2] = 0.2; dl->color[3] = 0.7; } # endif } if (effects & EF_POISON_GAS) CL_UpdatePoisonGas(ent, number); if (effects & EF_ACIDBLOB) { ent->angleAdd[0] = 0; ent->angleAdd[1] = 0; ent->angleAdd[2] = 200 * cl.time; rotateSet = 1; CL_UpdateAcidBlob(ent, number); } if (effects & EF_ONFIRE) CL_UpdateOnFire(ent, number); if (effects & EF_POWERFLAMEBURN) CL_UpdatePowerFlameBurn(ent, number); if (effects & EF_ICESTORM_EFFECT) CL_UpdateIceStorm(ent, number); if (effects & EF_HAMMER_EFFECTS) { ent->angleAdd[0] = 200 * cl.time; ent->angleAdd[1] = 0; ent->angleAdd[2] = 0; rotateSet = 1; CL_UpdateHammer(ent, number); } if (effects & EF_BEETLE_EFFECTS) CL_UpdateBug(ent); if (effects & EF_DARKLIGHT)//EF_INVINC_CIRC) R_SuccubusInvincibleParticles (ent); if (effects & EF_UPDATESOUND) S_UpdateSoundPos (number, 7, ent->origin); if (!rotateSet) { ent->angleAdd[0] = 0; ent->angleAdd[1] = 0; ent->angleAdd[2] = 0; } } /* =============== CL_LinkPacketEntities =============== */ static void CL_LinkPacketEntities (void) { entity_t *ent; packet_entities_t *pack; entity_state_t *s1, *s2; float f; qmodel_t *model; vec3_t old_origin; // float autorotate; int i, pnum; dlight_t *dl; pack = &cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities; // autorotate = anglemod(100*cl.time); f = 0; // FIXME: no interpolation right now for (pnum = 0; pnum < pack->num_entities; pnum++) { s1 = &pack->entities[pnum]; s2 = s1; // FIXME: no interpolation right now /* // spawn light flashes, even ones coming from invisible objects if (s1->effects & (EF_BLUE | EF_RED) == (EF_BLUE | EF_RED)) CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2], 200 + (rand() & 31), 0.1, 3); else if (s1->effects & EF_BLUE) CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2], 200 + (rand() & 31), 0.1, 1); else if (s1->effects & EF_RED) CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2], 200 + (rand() & 31), 0.1, 2); else if (s1->effects & EF_BRIGHTLIGHT) CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2] + 16, 400 + (rand() & 31), 0.1, 0); else if (s1->effects & EF_DIMLIGHT) CL_NewDlight (s1->number, s1->origin[0], s1->origin[1], s1->origin[2], 200 + (rand() & 31), 0.1, 0); */ // if set to invisible, skip if (!s1->modelindex) continue; // create a new entity if (cl_numvisedicts == MAX_VISEDICTS) break; // object list is full ent = &cl_visedicts[cl_numvisedicts]; cl_numvisedicts++; ent->keynum = s1->number; ent->model = model = cl.model_precache[s1->modelindex]; ent->sourcecolormap = vid.colormap; if (!s1->colormap) { ent->colorshade = 0; ent->colormap = ent->sourcecolormap; ent->scoreboard = NULL; } else { ent->colorshade = s1->colormap; ent->colormap = globalcolormap; ent->scoreboard = NULL; } /* // set colormap if (s1->colormap && (s1->colormap < MAX_CLIENTS) && !strcmp(ent->model->name,"models/paladin.mdl") ) { ent->colormap = cl.players[s1->colormap-1].translations; ent->scoreboard = &cl.players[s1->colormap-1]; } else { ent->colormap = vid.colormap; ent->scoreboard = NULL; } */ // set skin ent->skinnum = s1->skinnum; // set frame ent->frame = s1->frame; ent->drawflags = s1->drawflags; ent->scale = s1->scale; ent->abslight = s1->abslight; // rotate binary objects locally /* rjr rotate them in renderer if (model->flags & EF_ROTATE) { ent->angles[0] = 0; ent->angles[1] = autorotate; ent->angles[2] = 0; } else*/ { float a1, a2; for (i = 0; i < 3; i++) { a1 = s1->angles[i]; a2 = s2->angles[i]; if (a1 - a2 > 180) a1 -= 360; if (a1 - a2 < -180) a1 += 360; ent->angles[i] = a2 + f * (a1 - a2); } } // calculate origin for (i = 0; i < 3; i++) ent->origin[i] = s2->origin[i] + f * (s1->origin[i] - s2->origin[i]); // scan the old entity display list for a matching for (i = 0; i < cl_oldnumvisedicts; i++) { if (cl_oldvisedicts[i].keynum == ent->keynum) { VectorCopy (cl_oldvisedicts[i].origin, old_origin); break; } } if (i == cl_oldnumvisedicts) continue; // not in last message for (i = 0; i < 3; i++) { // if (abs((int)(old_origin[i] - ent->origin[i])) > 128) if (abs((int)(old_origin[i] - ent->origin[i])) > 512) // this is an issue for laggy situations... { // no trail if too far VectorCopy (ent->origin, old_origin); break; } } // some of the effects need to know how far the thing has moved... // cl.players[s1->number].invis=false; if (cl_siege) { if ((int)s1->effects & EF_NODRAW) { ent->skinnum = 101;//ice, but in siege will be invis skin for dwarf to see ent->drawflags |= DRF_TRANSLUCENT; s1->effects &= ~EF_NODRAW; // cl.players[s1->number].invis=true; } } HandleEffects(s1->effects, s1->number, ent, old_origin); // add automatic particle trails if (!model->flags) continue; // Model Flags if (ent->model->flags & EF_GIB) R_RocketTrail (old_origin, ent->origin, 2); else if (ent->model->flags & EF_ZOMGIB) R_RocketTrail (old_origin, ent->origin, 4); else if (ent->model->flags & EF_TRACER) R_RocketTrail (old_origin, ent->origin, 3); else if (ent->model->flags & EF_TRACER2) R_RocketTrail (old_origin, ent->origin, 5); else if (ent->model->flags & EF_ROCKET) { R_RocketTrail (old_origin, ent->origin, 0); /* dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200; dl->die = cl.time + 0.01; */ } else if (ent->model->flags & EF_FIREBALL) { R_RocketTrail (old_origin, ent->origin, rt_fireball); dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 120 - (rand() % 20); dl->die = cl.time + 0.01; # ifdef GLQUAKE if (gl_colored_dynamic_lights.integer) { dl->color[0] = 0.8; dl->color[1] = 0.2; dl->color[2] = 0.2; dl->color[3] = 0.7; } # endif } else if (ent->model->flags & EF_ICE) { R_RocketTrail (old_origin, ent->origin, rt_ice); } else if (ent->model->flags & EF_SPIT) { R_RocketTrail (old_origin, ent->origin, rt_spit); dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = -120 - (rand() % 20); dl->die = cl.time + 0.05; # ifdef GLQUAKE if (gl_colored_dynamic_lights.integer) { // Make the dynamic light green dl->color[0] = 0.2; dl->color[1] = 0.6; dl->color[2] = 0.2; dl->color[3] = 0.7; } # endif } else if (ent->model->flags & EF_SPELL) { R_RocketTrail (old_origin, ent->origin, rt_spell); } else if (ent->model->flags & EF_GRENADE) { // R_RunParticleEffect4(old_origin, 3, 284, pt_slowgrav, 3); R_RocketTrail (old_origin, ent->origin, rt_grensmoke); } else if (ent->model->flags & EF_TRACER3) { R_RocketTrail (old_origin, ent->origin, 6); } else if (ent->model->flags & EF_VORP_MISSILE) { R_RocketTrail (old_origin, ent->origin, rt_vorpal); # ifdef GLQUAKE // extra dynamic lights if (gl_extra_dynamic_lights.integer) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 240 - (rand() % 20); dl->die = cl.time + 0.01; if (gl_colored_dynamic_lights.integer) { // Make the dynamic light blue dl->color[0] = 0.3; dl->color[1] = 0.3; dl->color[2] = 0.8; dl->color[3] = 0.7; } } # endif } else if (ent->model->flags & EF_SET_STAFF) { R_RocketTrail (old_origin, ent->origin,rt_setstaff); } else if (ent->model->flags & EF_MAGICMISSILE) { if ((rand() & 3) < 1) R_RocketTrail (old_origin, ent->origin, rt_magicmissile); # ifdef GLQUAKE // extra dynamic lights if (gl_extra_dynamic_lights.integer) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 240 - (rand() % 20); dl->die = cl.time + 0.01; if (gl_colored_dynamic_lights.integer) { // Make the dynamic light blue dl->color[0] = 0.1; dl->color[1] = 0.1; dl->color[2] = 0.8; dl->color[3] = 0.7; } } # endif } else if (ent->model->flags & EF_BONESHARD) { R_RocketTrail (old_origin, ent->origin, rt_boneshard); } else if (ent->model->flags & EF_SCARAB) { R_RocketTrail (old_origin, ent->origin, rt_scarab); # ifdef GLQUAKE // extra dynamic lights if (gl_extra_dynamic_lights.integer) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 240 - (rand() % 20); dl->die = cl.time + 0.01; if (gl_colored_dynamic_lights.integer) { // Make the dynamic light orange dl->color[0] = 0.9; dl->color[1] = 0.6; dl->color[2] = 0.1; dl->color[3] = 0.7; } } # endif } else if (ent->model->flags & EF_ACIDBALL) { R_RocketTrail (old_origin, ent->origin, rt_acidball); # ifdef GLQUAKE // extra dynamic lights if (gl_extra_dynamic_lights.integer) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 120 - (rand() % 20); dl->die = cl.time + 0.01; if (gl_colored_dynamic_lights.integer) { // Make the dynamic light green dl->color[0] = 0.2; dl->color[1] = 0.8; dl->color[2] = 0.2; dl->color[3] = 0.7; } } # endif } else if (ent->model->flags & EF_BLOODSHOT) { R_RocketTrail (old_origin, ent->origin, rt_bloodshot); } } } /* ========================================================================= PROJECTILE PARSING / LINKING changed for use with the powered ravens ========================================================================= */ typedef struct { int modelindex; vec3_t origin; vec3_t angles; int frame; } projectile_t; #define MAX_PROJECTILES 32 static projectile_t cl_projectiles[MAX_PROJECTILES]; static int cl_num_projectiles; extern int cl_ravenindex, cl_raven2index; void CL_ClearProjectiles (void) { cl_num_projectiles = 0; } /* ===================== CL_ParseProjectiles Nails are passed as efficient temporary entities Note: all references to these functions have been replaced with calls to the Missile functions below ===================== */ void CL_ParseProjectiles (void) { int i, c, j; byte bits[6]; projectile_t *pr; c = MSG_ReadByte (); for (i = 0; i < c; i++) { for (j = 0; j < 6; j++) bits[j] = MSG_ReadByte (); if (cl_num_projectiles == MAX_PROJECTILES) continue; pr = &cl_projectiles[cl_num_projectiles]; cl_num_projectiles++; pr->modelindex = cl_ravenindex; pr->origin[0] = ( ( bits[0] + ((bits[1]&15)<<8) ) <<1) - 4096; pr->origin[1] = ( ( (bits[1]>>4) + (bits[2]<<4) ) <<1) - 4096; pr->origin[2] = ( ( bits[3] + ((bits[4]&15)<<8) ) <<1) - 4096; pr->angles[0] = 360*(bits[4]>>4)/16; pr->angles[1] = 360*(bits[5]&31)/32; pr->frame = (bits[5]>>5) & 7; } c = MSG_ReadByte (); for (i = 0; i < c; i++) { for (j = 0; j < 6; j++) bits[j] = MSG_ReadByte (); if (cl_num_projectiles == MAX_PROJECTILES) continue; pr = &cl_projectiles[cl_num_projectiles]; cl_num_projectiles++; pr->modelindex = cl_raven2index; pr->origin[0] = ((bits[0] + ((bits[1]&15)<<8)) <<1) - 4096; pr->origin[1] = (((bits[1]>>4) + (bits[2]<<4)) <<1) - 4096; pr->origin[2] = ((bits[3] + ((bits[4]&15)<<8)) <<1) - 4096; pr->angles[0] = 360*(bits[4]>>4)/16; pr->angles[1] = 360*bits[5]/256; pr->frame = 0; } } /* ============= CL_LinkProjectiles ============= */ static void CL_LinkProjectiles (void) { int i; projectile_t *pr; entity_t *ent; for (i = 0, pr = cl_projectiles; i < cl_num_projectiles; i++, pr++) { // grab an entity to fill in if (cl_numvisedicts == MAX_VISEDICTS) break; // object list is full ent = &cl_visedicts[cl_numvisedicts]; cl_numvisedicts++; ent->keynum = 0; if (pr->modelindex < 1) continue; ent->model = cl.model_precache[pr->modelindex]; ent->skinnum = 0; ent->frame = 0; ent->colormap = vid.colormap; ent->scoreboard = NULL; ent->frame = pr->frame; VectorCopy (pr->origin, ent->origin); VectorCopy (pr->angles, ent->angles); } } /* ========================================================================= MISSILE PROJECTILES ========================================================================= */ typedef struct { int modelindex; vec3_t origin; int type; } missile_t; #define MAX_MISSILES 32 static missile_t cl_missiles[MAX_MISSILES]; static int cl_num_missiles; extern int cl_ballindex, cl_missilestarindex; void CL_ClearMissiles (void) { cl_num_missiles = 0; } /* ===================== CL_ParseProjectiles Missiles are passed as efficient temporary entities ===================== */ void CL_ParsePackMissiles (void) { int i, c, j; byte bits[5]; missile_t *pr; c = MSG_ReadByte (); for (i = 0; i < c; i++) { for (j = 0; j < 5; j++) bits[j] = MSG_ReadByte (); if (cl_num_missiles == MAX_MISSILES) continue; pr = &cl_missiles[cl_num_missiles]; cl_num_missiles++; pr->origin[0] = ( ( bits[0] + ((bits[1]&15)<<8) ) <<1) - 4096; pr->origin[1] = ( ( (bits[1]>>4) + (bits[2]<<4) ) <<1) - 4096; pr->origin[2] = ( ( bits[3] + ((bits[4]&15)<<8) ) <<1) - 4096; pr->type = bits[4]>>4; //type may be used later to select models } } /* ============= CL_LinkMissile ============= */ static vec3_t missilestar_angle; static void CL_LinkMissiles (void) { int i; missile_t *pr; entity_t *ent; missilestar_angle[1] += host_frametime * 300; missilestar_angle[2] += host_frametime * 400; for (i = 0, pr = cl_missiles; i < cl_num_missiles; i++, pr++) { // grab an entity to fill in for missile itself if (cl_numvisedicts == MAX_VISEDICTS) break; // object list is full ent = &cl_visedicts[cl_numvisedicts]; if (rand() % 10 < 3) { R_RunParticleEffect4 (ent->origin, 7, 148 + (rand() % 11), pt_grav, 10 + (rand() % 10)); } cl_numvisedicts++; ent->keynum = 0; if (pr->type == 1) { //ball ent->model = cl.model_precache[cl_ballindex]; ent->scale = 10; } else { //missilestar ent->model = cl.model_precache[cl_missilestarindex]; ent->scale = 50; VectorCopy (missilestar_angle , ent->angles); } ent->skinnum = 0; ent->frame = 0; ent->colormap = vid.colormap; ent->scoreboard = NULL; ent->drawflags = SCALE_ORIGIN_CENTER; VectorCopy (pr->origin, ent->origin); } } //======================================== extern int cl_spikeindex, cl_playerindex[MAX_PLAYER_CLASS], cl_flagindex; entity_t *CL_NewTempEntity (void); /* =================== CL_ParsePlayerinfo =================== */ extern int parsecountmod; extern double parsecounttime; void CL_SavePlayer (void) { int num; // player_info_t *info; /* variable set but not used */ player_state_t *state; num = MSG_ReadByte (); if (num > MAX_CLIENTS) Sys_Error ("%s: bad num", __thisfunc__); // info = &cl.players[num]; state = &cl.frames[parsecountmod].playerstate[num]; state->messagenum = cl.parsecount; state->state_time = parsecounttime; } void CL_ParsePlayerinfo (void) { int i, num, msec, flags; player_info_t *info; player_state_t *state; qboolean playermodel = false; num = MSG_ReadByte (); if (num > MAX_CLIENTS) Sys_Error ("%s: bad num", __thisfunc__); info = &cl.players[num]; state = &cl.frames[parsecountmod].playerstate[num]; flags = state->flags = MSG_ReadShort (); state->messagenum = cl.parsecount; state->origin[0] = MSG_ReadCoord (); state->origin[1] = MSG_ReadCoord (); state->origin[2] = MSG_ReadCoord (); state->frame = MSG_ReadByte (); // the other player's last move was likely some time // before the packet was sent out, so accurately track // the exact time it was valid at if (flags & PF_MSEC) { msec = MSG_ReadByte (); state->state_time = parsecounttime - msec*0.001; } else { state->state_time = parsecounttime; } if (flags & PF_COMMAND) MSG_ReadUsercmd (&state->command, false); for (i = 0; i < 3; i++) { if (flags & (PF_VELOCITY1<velocity[i] = MSG_ReadShort(); else state->velocity[i] = 0; } if (flags & PF_MODEL) { state->modelindex = MSG_ReadShort (); } else { playermodel = true; i = info->playerclass; if (i >= 1 && i <= MAX_PLAYER_CLASS) state->modelindex = cl_playerindex[i - 1]; else state->modelindex = cl_playerindex[0]; } if (flags & PF_SKINNUM) state->skinnum = MSG_ReadByte (); else { if (info->siege_team == ST_ATTACKER && playermodel) state->skinnum = 1;//using a playermodel and attacker - skin is set to 1 else state->skinnum = 0; } if (flags & PF_EFFECTS) state->effects = MSG_ReadByte (); else state->effects = 0; if (flags & PF_EFFECTS2) state->effects |= (MSG_ReadByte() << 8); else state->effects &= 0xff; if (flags & PF_WEAPONFRAME) state->weaponframe = MSG_ReadByte (); else state->weaponframe = 0; if (flags & PF_DRAWFLAGS) state->drawflags = MSG_ReadByte(); else state->drawflags = 0; if (flags & PF_SCALE) state->scale = MSG_ReadByte(); else state->scale = 0; if (flags & PF_ABSLIGHT) state->abslight = MSG_ReadByte(); else state->abslight = 0; if (flags & PF_SOUND) { i = MSG_ReadShort (); S_StartSound(num, 1, cl.sound_precache[i], state->origin, 1.0, 1.0); } VectorCopy (state->command.angles, state->viewangles); } #if 0 /* not used */ /* ================ CL_AddFlagModels Called when the CTF flags are set ================ */ static void CL_AddFlagModels (entity_t *ent, int team) { int i; float f; vec3_t v_forward, v_right, v_up; entity_t *newent; if (cl_flagindex == -1) return; f = 14; if (ent->frame >= 29 && ent->frame <= 40) { if (ent->frame >= 29 && ent->frame <= 34) { //axpain if (ent->frame == 29) f = f + 2; else if (ent->frame == 30) f = f + 8; else if (ent->frame == 31) f = f + 12; else if (ent->frame == 32) f = f + 11; else if (ent->frame == 33) f = f + 10; else if (ent->frame == 34) f = f + 4; } else if (ent->frame >= 35 && ent->frame <= 40) { // pain if (ent->frame == 35) f = f + 2; else if (ent->frame == 36) f = f + 10; else if (ent->frame == 37) f = f + 10; else if (ent->frame == 38) f = f + 8; else if (ent->frame == 39) f = f + 4; else if (ent->frame == 40) f = f + 2; } } else if (ent->frame >= 103 && ent->frame <= 118) { if (ent->frame >= 103 && ent->frame <= 104) f = f + 6; //nailattack else if (ent->frame >= 105 && ent->frame <= 106) f = f + 6; //light else if (ent->frame >= 107 && ent->frame <= 112) f = f + 7; //rocketattack else if (ent->frame >= 112 && ent->frame <= 118) f = f + 7; //shotattack } newent = CL_NewTempEntity (); newent->model = cl.model_precache[cl_flagindex]; newent->skinnum = team; AngleVectors (ent->angles, v_forward, v_right, v_up); v_forward[2] = -v_forward[2]; // reverse z component for (i = 0; i < 3; i++) newent->origin[i] = ent->origin[i] - f*v_forward[i] + 22*v_right[i]; newent->origin[2] -= 16; VectorCopy (ent->angles, newent->angles) newent->angles[2] -= 45; } #endif /* ============= CL_LinkPlayers Create visible entities in the correct position for all current players ============= */ static void CL_LinkPlayers (void) { int j, msec, oldphysent; player_info_t *info; player_state_t *state; player_state_t exact; double playertime; entity_t *ent; frame_t *frame; playertime = realtime - cls.latency + 0.02; if (playertime > realtime) playertime = realtime; frame = &cl.frames[cl.parsecount&UPDATE_MASK]; for (j = 0, info = cl.players, state=frame->playerstate; j < MAX_CLIENTS; j++, info++, state++) { if (state->messagenum != cl.parsecount) continue; // not present this frame // spawn light flashes, even ones coming from invisible objects #ifdef GLQUAKE if (!gl_flashblend.integer || j != cl.playernum) { #endif /* if (state->effects & (EF_BLUE | EF_RED) == (EF_BLUE | EF_RED)) CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2], 200 + (rand() & 31), 0.1, 3); else if (state->effects & EF_BLUE) CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2], 200 + (rand() & 31), 0.1, 1); else if (state->effects & EF_RED) CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2], 200 + (rand() & 31), 0.1, 2); else if (state->effects & EF_BRIGHTLIGHT) CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2] + 16, 400 + (rand() & 31), 0.1, 0); else if (state->effects & EF_DIMLIGHT) CL_NewDlight (j, state->origin[0], state->origin[1], state->origin[2], 200 + (rand() & 31), 0.1, 0); */ #ifdef GLQUAKE } #endif if (!state->modelindex) continue; cl.players[j].modelindex = state->modelindex; // grab an entity to fill in if (cl_numvisedicts == MAX_VISEDICTS) break; // object list is full ent = &cl_visedicts[cl_numvisedicts]; ent->keynum = 0; ent->model = cl.model_precache[state->modelindex]; ent->skinnum = state->skinnum; ent->frame = state->frame; ent->sourcecolormap = info->translations; ent->colorshade = 0; ent->colormap = ent->sourcecolormap; ent->drawflags = state->drawflags; ent->scale = state->scale; ent->abslight = state->abslight; if (ent->model == player_models[0] || ent->model == player_models[1] || ent->model == player_models[2] || ent->model == player_models[3] || ent->model == player_models[4] ||//mg-siege ent->model == player_models[5]) { ent->scoreboard = info; // use custom skin } else { ent->scoreboard = NULL; } // // angles // ent->angles[PITCH] = -state->viewangles[PITCH]/3; ent->angles[YAW] = state->viewangles[YAW]; ent->angles[ROLL] = 0; ent->angles[ROLL] = V_CalcRoll (ent->angles, state->velocity)*4; // only predict half the move to minimize overruns msec = 500*(playertime - state->state_time); if (msec <= 0 || (!cl_predict_players.integer && !cl_predict_players2.integer) || j == cl.playernum) { VectorCopy (state->origin, ent->origin); //Con_DPrintf ("nopredict\n"); } else { // predict players movement if (msec > 255) msec = 255; state->command.msec = msec; //Con_DPrintf ("predict: %i\n", msec); oldphysent = pmove.numphysent; CL_SetSolidPlayers (j); CL_PredictUsercmd (state, &exact, &state->command, false); pmove.numphysent = oldphysent; VectorCopy (exact.origin, ent->origin); } if (cl_siege) { if ((int)state->effects & EF_NODRAW) { ent->skinnum = 101;//ice, but in siege will be invis skin for dwarf to see ent->drawflags |= DRF_TRANSLUCENT; state->effects &= ~EF_NODRAW; } } HandleEffects(state->effects, j+1, ent, NULL); // the player object never gets added if (j == cl.playernum) continue; cl_numvisedicts++; // if (state->effects & EF_FLAG1) // CL_AddFlagModels (ent, 0); // else if (state->effects & EF_FLAG2) // CL_AddFlagModels (ent, 1); } } //====================================================================== /* =============== CL_SetSolid Builds all the pmove physents for the current frame =============== */ void CL_SetSolidEntities (void) { frame_t *frame; packet_entities_t *pak; entity_state_t *state; int i; pmove.physents[0].model = cl.worldmodel; VectorClear (pmove.physents[0].origin); pmove.physents[0].info = 0; pmove.numphysent = 1; frame = &cl.frames[parsecountmod]; pak = &frame->packet_entities; for (i = 0; i < pak->num_entities; i++) { state = &pak->entities[i]; if (!state->modelindex) continue; if (!cl.model_precache[state->modelindex]) continue; if (cl.model_precache[state->modelindex]->hulls[1].firstclipnode) { pmove.physents[pmove.numphysent].model = cl.model_precache[state->modelindex]; VectorCopy (state->origin, pmove.physents[pmove.numphysent].origin); VectorCopy (state->angles, pmove.physents[pmove.numphysent].angles); pmove.numphysent++; } } } /* === Calculate the new position of players, without other player clipping We do this to set up real player prediction. Players are predicted twice, first without clipping other players, then with clipping against them. This sets up the first phase. === */ void CL_SetUpPlayerPrediction(qboolean dopred) { struct predicted_player *pplayer; player_state_t *state; player_state_t exact; double playertime; int msec; frame_t *frame; int j; playertime = realtime - cls.latency + 0.02; if (playertime > realtime) playertime = realtime; frame = &cl.frames[cl.parsecount&UPDATE_MASK]; for (j = 0, pplayer = predicted_players, state = frame->playerstate; j < MAX_CLIENTS; j++, pplayer++, state++) { pplayer->active = false; if (state->messagenum != cl.parsecount) continue; // not present this frame if (!state->modelindex) continue; pplayer->active = true; pplayer->flags = state->flags; // note that the local player is special, since he moves locally // we use his last predicted postition if (j == cl.playernum) { VectorCopy(cl.frames[cls.netchan.outgoing_sequence&UPDATE_MASK].playerstate[cl.playernum].origin, pplayer->origin); } else { // only predict half the move to minimize overruns msec = 500*(playertime - state->state_time); if (msec <= 0 || (!cl_predict_players.integer && !cl_predict_players2.integer) || !dopred) { VectorCopy (state->origin, pplayer->origin); //Con_DPrintf ("nopredict\n"); } else { // predict players movement if (msec > 255) msec = 255; state->command.msec = msec; //Con_DPrintf ("predict: %i\n", msec); CL_PredictUsercmd (state, &exact, &state->command, false); VectorCopy (exact.origin, pplayer->origin); } } } } /* =============== CL_SetSolid Builds all the pmove physents for the current frame Note that CL_SetUpPlayerPrediction() must be called first! pmove must be setup with world and solid entity hulls before calling (via CL_PredictMove) =============== */ extern vec3_t player_mins, player_maxs, player_maxs_crouch; void CL_SetSolidPlayers (int playernum) { int j; struct predicted_player *pplayer; physent_t *pent; if (!cl_solid_players.integer) return; pent = pmove.physents + pmove.numphysent; for (j = 0, pplayer = predicted_players; j < MAX_CLIENTS; j++, pplayer++) { if (!pplayer->active) continue; // not present this frame // the player object never gets added if (j == playernum) continue; if (pplayer->flags & PF_DEAD) continue; // dead players aren't solid pent->model = 0; VectorCopy(pplayer->origin, pent->origin); /*shitbox if (!q_strcasecmp(cl.model_precache[cl.players[playernum].modelindex]->name,"models/yakman.mdl")) {//use golem hull Sys_Error("Using beast model"); VectorCopy(beast_mins, pent->mins); VectorCopy(beast_maxs, pent->mins); } else {*/ VectorCopy(player_mins, pent->mins); if (pplayer->flags & PF_CROUCH) { VectorCopy(player_maxs_crouch, pent->maxs); } else { VectorCopy(player_maxs, pent->maxs); } // } pmove.numphysent++; pent++; } } /* =============== CL_EmitEntities Builds the visedicts array for cl.time Made up of: clients, packet_entities, nails, and tents =============== */ void CL_EmitEntities (void) { if (cls.state != ca_active) return; if (!cl.validsequence) return; cl_oldnumvisedicts = cl_numvisedicts; cl_oldvisedicts = cl_visedicts_list[(cls.netchan.incoming_sequence-1)&1]; cl_visedicts = cl_visedicts_list[cls.netchan.incoming_sequence&1]; cl_numvisedicts = 0; CL_LinkPlayers (); CL_LinkPacketEntities (); CL_LinkProjectiles (); CL_LinkMissiles(); CL_UpdateTEnts (); } engine/hexenworld/client/cl_inlude.c000066400000000000000000000115641444734033100200740ustar00rootroot00000000000000/* cl_interlude.c: Setup the Hexen II intermission screen flags. * Only the intermission index is sent by the server, therefore the * rest of the stuff is unfortunately hardcoded here in the engine. * * Copyright (C) 2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "quakedef.h" void CL_SetupIntermission (int num) { if (oem.integer && num == 1) cl.intermission = 9; else cl.intermission = num; switch (cl.intermission) { case 1: /* defeated famine: episode 1 (village) to 2 (mazaera) */ cl.completed_time = cl.time; cl.message_index = 1 + 394; cl.intermission_flags = 0; cl.intermission_pic = "gfx/meso.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 2: /* defeated death: episode 2 (mazaera) to 3 (egypt) */ cl.completed_time = cl.time; cl.message_index = 2 + 394; cl.intermission_flags = 0; cl.intermission_pic = "gfx/egypt.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 3: /* defeated pestilence: episode 3 (egypt) to 4 (roman) */ cl.completed_time = cl.time; cl.message_index = 3 + 394; cl.intermission_flags = 0; cl.intermission_pic = "gfx/roman.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 4: /* defeated war: episode 4 (roman) to last (castle) */ cl.completed_time = cl.time; cl.message_index = 4 + 394; cl.intermission_flags = 0; cl.intermission_pic = "gfx/castle.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 5: /* finale for the demo version */ cl.completed_time = cl.time; /* DEMO_MSG_INDEX is 408 for H2, 410 for H2MP strings.txt. * in uHexen2, the demo version isn't allowed in combination * with the mission pack, so we are good with 408. */ cl.message_index = 408; cl.intermission_flags = 0; cl.intermission_pic = "gfx/castle.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 6: /* defeated eidolon: finale, part 1/3 */ cl.completed_time = cl.time; cl.message_index = 6 + 386; cl.intermission_flags = INTERMISSION_PRINT_DELAY|INTERMISSION_PRINT_WHITE|INTERMISSION_PRINT_TOP; cl.intermission_pic = "gfx/end-1.lmp"; cl.lasting_time = 15; cl.intermission_next = 7; break; case 7: /* defeated eidolon: finale, part 2/3 */ cl.completed_time = cl.time; cl.message_index = 7 + 386; cl.intermission_flags = INTERMISSION_PRINT_DELAY|INTERMISSION_PRINT_WHITE|INTERMISSION_PRINT_TOP; cl.intermission_pic = "gfx/end-2.lmp"; cl.lasting_time = 15; cl.intermission_next = 8; break; case 8: /* defeated eidolon: finale, part 2/3 */ cl.completed_time = cl.time; cl.message_index = 8 + 386; cl.intermission_flags = INTERMISSION_PRINT_WHITE|INTERMISSION_PRINT_DELAY|INTERMISSION_PRINT_TOPMOST; cl.intermission_pic = "gfx/end-3.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 9: /* finale for the bundle (oem) version */ cl.completed_time = cl.time; cl.message_index = 391; cl.intermission_flags = INTERMISSION_PRINT_WHITE; cl.intermission_pic = "gfx/castle.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 10: /* Siege: Defender win - wipe out or time limit */ cl.completed_time = cl.time; cl.message_index = -1; cl.intermission_flags = INTERMISSION_NO_MESSAGE; cl.intermission_pic = "gfx/defwin.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 11: /* Siege: Attacker win - caught crown */ cl.completed_time = cl.time; cl.message_index = -1; cl.intermission_flags = INTERMISSION_NO_MESSAGE; cl.intermission_pic = "gfx/attwin.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; case 12: /* Siege: Attacker win 2 - wiped out. */ cl.completed_time = cl.time; cl.message_index = -1; cl.intermission_flags = INTERMISSION_NO_MESSAGE; cl.intermission_pic = "gfx/attwin2.lmp"; cl.lasting_time = 0; cl.intermission_next = 0; break; default: /* unexpected: */ cl.completed_time = cl.time; cl.message_index = Q_MAXINT; cl.intermission_flags = 0; cl.intermission_pic = NULL; cl.lasting_time = 0; cl.intermission_next = 0; // Host_Error("%s: Bad intermission number %d", __thisfunc__, cl.intermission); Con_Printf("%s: Bad intermission number %d\n", __thisfunc__, cl.intermission); break; } } engine/hexenworld/client/cl_input.c000066400000000000000000000370761444734033100177610ustar00rootroot00000000000000/* * cl.input.c -- builds an intended movement command to send to the server * * Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. * All rights reserved. * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" static cvar_t cl_nodelta = {"cl_nodelta", "0", CVAR_NONE}; /* =============================================================================== KEY BUTTONS Continuous button event tracking is complicated by the fact that two different input sources (say, mouse button 1 and the control key) can both press the same button, but the button should only be released when both of the pressing key have been released. When a key event issues a button command (+forward, +attack, etc), it appends its key number as a parameter to the command so it can be matched up with the release. state bit 0 is the current state of the key state bit 1 is edge triggered on the up to down transition state bit 2 is edge triggered on the down to up transition =============================================================================== */ kbutton_t in_mlook, in_klook; kbutton_t in_left, in_right, in_forward, in_back; kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright; kbutton_t in_strafe, in_speed, in_use, in_jump, in_attack; kbutton_t in_up, in_down, in_crouch; int in_impulse; static void KeyDown (kbutton_t *b) { int k; const char *c; c = Cmd_Argv(1); if (c[0]) k = atoi(c); else k = -1; // typed manually at the console for continuous down if (k == b->down[0] || k == b->down[1]) return; // repeating key if (!b->down[0]) b->down[0] = k; else if (!b->down[1]) b->down[1] = k; else { Con_Printf ("Three keys down for a button!\n"); return; } if (b->state & 1) return; // still down b->state |= 1 + 2; // down + impulse down } static void KeyUp (kbutton_t *b) { int k; const char *c; c = Cmd_Argv(1); if (c[0]) { k = atoi(c); } else { // typed manually at the console, assume for unsticking, so clear all b->down[0] = b->down[1] = 0; b->state = 4; // impulse up return; } if (b->down[0] == k) b->down[0] = 0; else if (b->down[1] == k) b->down[1] = 0; else return; // key up without coresponding down (menu pass through) if (b->down[0] || b->down[1]) return; // some other key is still holding it down if (!(b->state & 1)) return; // still up (this should not happen) b->state &= ~1; // now up b->state |= 4; // impulse up } static void IN_KLookDown (void) { KeyDown(&in_klook); } static void IN_KLookUp (void) { KeyUp(&in_klook); } static void IN_MLookDown (void) { KeyDown(&in_mlook); } static void IN_MLookUp (void) { KeyUp(&in_mlook); if (!(in_mlook.state & 1) && lookspring.integer) V_StartPitchDrift(); } static void IN_UpDown (void) { KeyDown(&in_up); } static void IN_UpUp (void) { KeyUp(&in_up); } static void IN_DownDown (void) { KeyDown(&in_down); } static void IN_DownUp (void) { KeyUp(&in_down); } static void IN_LeftDown (void) { KeyDown(&in_left); } static void IN_LeftUp (void) { KeyUp(&in_left); } static void IN_RightDown (void) { KeyDown(&in_right); } static void IN_RightUp (void) { KeyUp(&in_right); } static void IN_ForwardDown (void) { KeyDown(&in_forward); } static void IN_ForwardUp (void) { KeyUp(&in_forward); } static void IN_BackDown (void) { KeyDown(&in_back); } static void IN_BackUp (void) { KeyUp(&in_back); } static void IN_LookupDown (void) { KeyDown(&in_lookup); } static void IN_LookupUp (void) { KeyUp(&in_lookup); } static void IN_LookdownDown (void) { KeyDown(&in_lookdown); } static void IN_LookdownUp (void) { KeyUp(&in_lookdown); } static void IN_MoveleftDown (void) { KeyDown(&in_moveleft); } static void IN_MoveleftUp (void) { KeyUp(&in_moveleft); } static void IN_MoverightDown (void) { KeyDown(&in_moveright); } static void IN_MoverightUp (void) { KeyUp(&in_moveright); } static void IN_SpeedDown (void) { KeyDown(&in_speed); } static void IN_SpeedUp (void) { KeyUp(&in_speed); } static void IN_StrafeDown (void) { KeyDown(&in_strafe); } static void IN_StrafeUp (void) { KeyUp(&in_strafe); } static void IN_AttackDown (void) { KeyDown(&in_attack); } static void IN_AttackUp (void) { KeyUp(&in_attack); } static void IN_UseDown (void) { KeyDown(&in_use); } static void IN_UseUp (void) { KeyUp(&in_use); } static void IN_JumpDown (void) { KeyDown(&in_jump); } static void IN_JumpUp (void) { KeyUp(&in_jump); } static void IN_Impulse (void) { in_impulse = atoi(Cmd_Argv(1)); } static void IN_CrouchDown (void) { if (Key_GetDest() == key_game) { // int state = in_crouch.state; KeyDown(&in_crouch); // if (!(state & 1) && (in_crouch.state & 1)) // in_impulse = 22; } } static void IN_CrouchUp (void) { // if (Key_GetDest() == key_game) // { // int state = in_crouch.state; KeyUp(&in_crouch); // if ((state & 1) && !(in_crouch.state & 1)) // in_impulse = 22; // } } /* =============== CL_KeyState Returns 0.25 if a key was pressed and released during the frame, 0.5 if it was pressed and held 0 if held then released, and 1.0 if held for the entire time =============== */ static float CL_KeyState (kbutton_t *key) { float val; qboolean impulsedown, impulseup, down; impulsedown = key->state & 2; impulseup = key->state & 4; down = key->state & 1; val = 0; if (impulsedown && !impulseup) { if (down) val = 0.5; // pressed and held this frame else val = 0; } if (impulseup && !impulsedown) { if (down) val = 0; else val = 0; // released this frame } if (!impulsedown && !impulseup) { if (down) val = 1.0; // held the entire frame else val = 0; // up the entire frame } if (impulsedown && impulseup) { if (down) val = 0.75; // released and re-pressed this frame else val = 0.25; // pressed and released this frame } key->state &= 1; // clear impulses return val; } //========================================================================== cvar_t cl_upspeed = {"cl_upspeed", "200", CVAR_NONE}; cvar_t cl_forwardspeed = {"cl_forwardspeed", "200", CVAR_ARCHIVE}; cvar_t cl_backspeed = {"cl_backspeed", "200", CVAR_ARCHIVE}; cvar_t cl_sidespeed = {"cl_sidespeed", "225", CVAR_NONE}; cvar_t cl_movespeedkey = {"cl_movespeedkey", "2.0", CVAR_NONE}; cvar_t cl_yawspeed = {"cl_yawspeed", "140", CVAR_NONE}; cvar_t cl_pitchspeed = {"cl_pitchspeed", "150", CVAR_NONE}; cvar_t cl_anglespeedkey = {"cl_anglespeedkey", "1.5", CVAR_NONE}; /* ================ CL_AdjustAngles Moves the local angle positions ================ */ static void CL_AdjustAngles (void) { float speed; float up, down; if ((in_speed.state & 1) || cl.spectator) speed = host_frametime * cl_anglespeedkey.value; else speed = host_frametime; if (!(in_strafe.state & 1)) { cl.viewangles[YAW] -= speed*cl_yawspeed.value*CL_KeyState (&in_right); cl.viewangles[YAW] += speed*cl_yawspeed.value*CL_KeyState (&in_left); cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]); } if (in_klook.state & 1) { V_StopPitchDrift (); cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * CL_KeyState (&in_forward); cl.viewangles[PITCH] += speed*cl_pitchspeed.value * CL_KeyState (&in_back); } // FIXME: This is a cheap way of doing this, it belongs in V_CalcViewRoll // but I don't see where I can get the yaw velocity. cl.idealroll = 0; if (cl.v.movetype == MOVETYPE_FLY) { if (CL_KeyState (&in_left) != 0) cl.idealroll = -10; else if (CL_KeyState (&in_right) != 0) cl.idealroll = 10; } up = CL_KeyState (&in_lookup); down = CL_KeyState(&in_lookdown); cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * up; cl.viewangles[PITCH] += speed*cl_pitchspeed.value * down; if (up || down) V_StopPitchDrift (); if (cl.viewangles[PITCH] > 80) cl.viewangles[PITCH] = 80; if (cl.viewangles[PITCH] < -70) cl.viewangles[PITCH] = -70; if (cl.viewangles[ROLL] > 50) cl.viewangles[ROLL] = 50; if (cl.viewangles[ROLL] < -50) cl.viewangles[ROLL] = -50; } /* ================ CL_BaseMove Send the intended movement message to the server ================ */ void CL_BaseMove (usercmd_t *cmd) { if (cl.v.cameramode) // stuck in a different camera so don't move { memset (cmd, 0, sizeof(*cmd)); return; } CL_AdjustAngles (); memset (cmd, 0, sizeof(*cmd)); VectorCopy (cl.viewangles, cmd->angles); if (in_strafe.state & 1) { // cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_right); // cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_left); cmd->sidemove += 225 * CL_KeyState (&in_right); cmd->sidemove -= 225 * CL_KeyState (&in_left); } // cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_moveright); // cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_moveleft); cmd->sidemove += 225 * CL_KeyState (&in_moveright); cmd->sidemove -= 225 * CL_KeyState (&in_moveleft); cmd->upmove += cl_upspeed.value * CL_KeyState (&in_up); cmd->upmove -= cl_upspeed.value * CL_KeyState (&in_down); if (! (in_klook.state & 1)) { // cmd->forwardmove += cl_forwardspeed.value * CL_KeyState (&in_forward); cmd->forwardmove += 200 * CL_KeyState (&in_forward); // cmd->forwardmove -= cl_backspeed.value * CL_KeyState (&in_back); cmd->forwardmove -= 200 * CL_KeyState (&in_back); } // adjust for speed key, but not if "always run" has been chosen // speed key now acts as slow key when always run is chosen - OS // if ( ( (cl_forwardspeed.value > 200) ||(in_speed.state & 1) || cl.spectator) if ( (((cl_forwardspeed.value > 200) ^ (in_speed.state & 1)) || cl.spectator) && (cl.v.hasted <= 1) ) { cmd->forwardmove *= cl_movespeedkey.value; cmd->sidemove *= cl_movespeedkey.value; cmd->upmove *= cl_movespeedkey.value; } // Hasted player? if (cl.v.hasted) { cmd->forwardmove = cmd->forwardmove * cl.v.hasted; cmd->sidemove = cmd->sidemove * cl.v.hasted; cmd->upmove = cmd->upmove * cl.v.hasted; } cmd->light_level = cl.light_level; } static int MakeChar (int i) { i &= ~3; if (i < -127*4) i = -127*4; if (i > 127*4) i = 127*4; return i; } /* ============== CL_FinishMove ============== */ static void CL_FinishMove (usercmd_t *cmd) { int i, ms; // always dump the first two message, because it may // contain leftover inputs from the last level if (++cl.movemessages <= 2) return; // figure button bits if (in_attack.state & 3) cmd->buttons |= 1; in_attack.state &= ~2; if (in_jump.state & 3) cmd->buttons |= 2; in_jump.state &= ~2; if (in_crouch.state & 1) cmd->buttons |= 4; // send milliseconds of time to apply the move ms = host_frametime * 1000; if (ms > 250) ms = 100; // time was unreasonable cmd->msec = ms; VectorCopy (cl.viewangles, cmd->angles); cmd->impulse = in_impulse; in_impulse = 0; // chop down so no extra bits are kept that the server wouldn't get if ((int)cl.v.artifact_active & ARTFLAG_FROZEN) { cmd->forwardmove = 0; cmd->sidemove = 0; cmd->upmove = 0; } else { cmd->forwardmove = MakeChar (cmd->forwardmove); cmd->sidemove = MakeChar (cmd->sidemove); cmd->upmove = MakeChar (cmd->upmove); } for (i = 0; i < 3; i++) cmd->angles[i] = ((int)(cmd->angles[i]*65536.0/360)&65535) * (360.0/65536.0); } /* ================= CL_SendCmd ================= */ void CL_SendCmd (void) { sizebuf_t buf; byte data[128]; int i; usercmd_t *cmd; if (cls.demoplayback) return; // save this command off for prediction i = cls.netchan.outgoing_sequence & UPDATE_MASK; cmd = &cl.frames[i].cmd; cl.frames[i].senttime = realtime; cl.frames[i].receivedtime = -1; // we haven't gotten a reply yet // get basic movement from keyboard CL_BaseMove (cmd); // allow mice or other external controllers to add to the move IN_Move (cmd); // if we are spectator, try autocam if (cl.spectator) Cam_Track(cmd); CL_FinishMove(cmd); Cam_FinishMove(cmd); // send this and the previous cmds in the message, so // if the last packet was dropped, it can be recovered SZ_Init (&buf, data, sizeof(data)); MSG_WriteByte (&buf, clc_move); i = (cls.netchan.outgoing_sequence-2) & UPDATE_MASK; MSG_WriteUsercmd (&buf, &cl.frames[i].cmd, false); i = (cls.netchan.outgoing_sequence-1) & UPDATE_MASK; MSG_WriteUsercmd (&buf, &cl.frames[i].cmd, false); i = (cls.netchan.outgoing_sequence) & UPDATE_MASK; MSG_WriteUsercmd (&buf, &cl.frames[i].cmd, true); // Con_Printf("I %hd %hd %hd\n",cmd->forwardmove, cmd->sidemove, cmd->upmove); // request delta compression of entities if (cls.netchan.outgoing_sequence - cl.validsequence >= UPDATE_BACKUP-1) cl.validsequence = 0; if (cl.validsequence && !cl_nodelta.integer && cls.state == ca_active && !cls.demorecording) { cl.frames[cls.netchan.outgoing_sequence&UPDATE_MASK].delta_sequence = cl.validsequence; MSG_WriteByte (&buf, clc_delta); MSG_WriteByte (&buf, cl.validsequence&255); } else cl.frames[cls.netchan.outgoing_sequence&UPDATE_MASK].delta_sequence = -1; if (cls.demorecording) CL_WriteDemoCmd(cmd); // deliver the message Netchan_Transmit (&cls.netchan, buf.cursize, buf.data); } /* ============ CL_InitInput ============ */ void CL_InitInput (void) { Cmd_AddCommand ("+moveup",IN_UpDown); Cmd_AddCommand ("-moveup",IN_UpUp); Cmd_AddCommand ("+movedown",IN_DownDown); Cmd_AddCommand ("-movedown",IN_DownUp); Cmd_AddCommand ("+left",IN_LeftDown); Cmd_AddCommand ("-left",IN_LeftUp); Cmd_AddCommand ("+right",IN_RightDown); Cmd_AddCommand ("-right",IN_RightUp); Cmd_AddCommand ("+forward",IN_ForwardDown); Cmd_AddCommand ("-forward",IN_ForwardUp); Cmd_AddCommand ("+back",IN_BackDown); Cmd_AddCommand ("-back",IN_BackUp); Cmd_AddCommand ("+lookup", IN_LookupDown); Cmd_AddCommand ("-lookup", IN_LookupUp); Cmd_AddCommand ("+lookdown", IN_LookdownDown); Cmd_AddCommand ("-lookdown", IN_LookdownUp); Cmd_AddCommand ("+strafe", IN_StrafeDown); Cmd_AddCommand ("-strafe", IN_StrafeUp); Cmd_AddCommand ("+moveleft", IN_MoveleftDown); Cmd_AddCommand ("-moveleft", IN_MoveleftUp); Cmd_AddCommand ("+moveright", IN_MoverightDown); Cmd_AddCommand ("-moveright", IN_MoverightUp); Cmd_AddCommand ("+speed", IN_SpeedDown); Cmd_AddCommand ("-speed", IN_SpeedUp); Cmd_AddCommand ("+attack", IN_AttackDown); Cmd_AddCommand ("-attack", IN_AttackUp); Cmd_AddCommand ("+use", IN_UseDown); Cmd_AddCommand ("-use", IN_UseUp); Cmd_AddCommand ("+jump", IN_JumpDown); Cmd_AddCommand ("-jump", IN_JumpUp); Cmd_AddCommand ("impulse", IN_Impulse); Cmd_AddCommand ("+klook", IN_KLookDown); Cmd_AddCommand ("-klook", IN_KLookUp); Cmd_AddCommand ("+mlook", IN_MLookDown); Cmd_AddCommand ("-mlook", IN_MLookUp); Cmd_AddCommand ("+crouch", IN_CrouchDown); Cmd_AddCommand ("-crouch", IN_CrouchUp); Cvar_RegisterVariable (&cl_nodelta); } /* ============ CL_ClearStates ============ */ #if 0 void CL_ClearStates (void) { in_mlook.state = 0; in_klook.state = 0; in_left.state = 0; in_right.state = 0; in_forward.state = 0; in_back.state = 0; in_lookup.state = 0; in_lookdown.state = 0; in_moveleft.state = 0; in_moveright.state = 0; in_strafe.state = 0; in_speed.state = 0; in_use.state = 0; in_jump.state = 0; in_attack.state = 0; in_up.state = 0; in_down.state = 0; in_crouch.state = 0; } #endif engine/hexenworld/client/cl_main.c000066400000000000000000000757011444734033100175430ustar00rootroot00000000000000/* * cl_main.c -- hexenworld client main loop * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include #include "arch_def.h" #if defined(PLATFORM_WINDOWS) #include "winquake.h" #endif #include "quakedef.h" #include "huffman.h" #include "cfgfile.h" #include "debuglog.h" #include "bgmusic.h" #include "cdaudio.h" static cvar_t rcon_password = {"rcon_password", "", CVAR_NONE}; static cvar_t rcon_address = {"rcon_address", "", CVAR_NONE}; static cvar_t cl_timeout = {"cl_timeout", "60", CVAR_NONE}; cvar_t cl_shownet = {"cl_shownet", "0", CVAR_NONE}; // can be 0, 1, or 2 cvar_t cfg_unbindall = {"cfg_unbindall", "1", CVAR_ARCHIVE}; cvar_t lookspring = {"lookspring", "0", CVAR_ARCHIVE}; cvar_t lookstrafe = {"lookstrafe", "0", CVAR_ARCHIVE}; cvar_t sensitivity = {"sensitivity", "3", CVAR_ARCHIVE}; cvar_t mwheelthreshold = {"mwheelthreshold", "120", CVAR_ARCHIVE}; cvar_t m_pitch = {"m_pitch", "0.022", CVAR_ARCHIVE}; cvar_t m_yaw = {"m_yaw", "0.022", CVAR_NONE}; cvar_t m_forward = {"m_forward", "1", CVAR_NONE}; cvar_t m_side = {"m_side", "0.8", CVAR_NONE}; cvar_t entlatency = {"entlatency", "20", CVAR_NONE}; cvar_t cl_predict_players = {"cl_predict_players", "1", CVAR_NONE}; cvar_t cl_predict_players2 = {"cl_predict_players2", "1", CVAR_NONE}; cvar_t cl_solid_players = {"cl_solid_players", "1", CVAR_NONE}; // // info mirrors // cvar_t password = {"password", "", CVAR_USERINFO}; cvar_t spectator = {"spectator", "", CVAR_USERINFO}; cvar_t name = {"name", "unnamed", CVAR_USERINFO|CVAR_ARCHIVE}; cvar_t playerclass = {"playerclass", "1", CVAR_USERINFO|CVAR_ARCHIVE}; cvar_t team = {"team", "", CVAR_USERINFO|CVAR_ARCHIVE}; cvar_t skin = {"skin", "", CVAR_USERINFO|CVAR_ARCHIVE}; cvar_t topcolor = {"topcolor", "0", CVAR_USERINFO|CVAR_ARCHIVE}; cvar_t bottomcolor = {"bottomcolor", "0", CVAR_USERINFO|CVAR_ARCHIVE}; cvar_t rate = {"rate", "2500", CVAR_USERINFO|CVAR_ARCHIVE}; /* 5760 (72 fps) ? */ cvar_t noaim = {"noaim", "0", CVAR_USERINFO|CVAR_ARCHIVE}; cvar_t talksounds = {"talksounds", "1", CVAR_ARCHIVE}; cvar_t msg = {"msg", "1", CVAR_USERINFO|CVAR_ARCHIVE}; extern cvar_t baseskin; extern cvar_t noskins; client_static_t cls; client_state_t cl; entity_state_t cl_baselines[MAX_EDICTS]; efrag_t cl_efrags[MAX_EFRAGS]; entity_t cl_static_entities[MAX_STATIC_ENTITIES]; lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; dlight_t cl_dlights[MAX_DLIGHTS]; // refresh list // this is double buffered so the last frame // can be scanned for oldorigins of trailing objects int cl_numvisedicts, cl_oldnumvisedicts; entity_t *cl_visedicts, *cl_oldvisedicts; entity_t cl_visedicts_list[2][MAX_VISEDICTS]; static double connect_time = -1; // for connection retransmits quakeparms_t *host_parms; qboolean host_initialized; // true if into command execution static jmp_buf host_abort; double host_frametime; double realtime; // without any filtering or bounding static double oldrealtime; // last frame run int host_framecount; static int host_hunklevel; byte *host_basepal; byte *host_colormap; netadr_t master_adr; // address of the master server static cvar_t host_speeds = {"host_speeds", "0", CVAR_NONE}; // set for running times cvar_t developer = {"developer", "0", CVAR_NONE}; float server_version = 0; // version of server we connected to // // globals for Siege: // qboolean cl_siege; // whether this is a Siege game byte cl_fraglimit; float cl_timelimit; float cl_server_time_offset; int cl_keyholder = -1; int cl_doc = -1; // Defender of Crown (Excalibur) unsigned int defLosses; // Defender losses unsigned int attLosses; // Attacker losses static void CL_Callback_Userinfo (cvar_t *var) { Info_SetValueForKey (cls.userinfo, var->name, var->string, MAX_INFO_STRING); if (cls.state >= ca_connected) { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); SZ_Print (&cls.netchan.message, va("setinfo \"%s\" \"%s\"\n", var->name, var->string)); } } /* ================== CL_Quit_f ================== */ static void CL_Quit_f (void) { if (1 /* Key_GetDest() != key_console && cls.state == ca_active */) { M_Menu_Quit_f (); return; } CL_Disconnect (); Sys_Quit (); } /* ======================= CL_Version_f ====================== */ static void CL_Version_f (void) { Con_Printf ("Version %4.2f\n", ENGINE_VERSION); Con_Printf ("Exe: " __TIME__ " " __DATE__ "\n"); } /* ======================= CL_SendConnectPacket called by CL_Connect_f and CL_CheckResend ====================== */ extern qboolean menu_disabled_mouse; void CL_SendConnectPacket (void) { netadr_t adr; char data[2048]; double t1, t2; // JACK: Fixed bug where DNS lookups would cause two connects real fast // Now, adds lookup time to the connect time. // Should I add it to realtime instead?!?! t1 = Sys_DoubleTime (); if (!NET_StringToAdr (cls.servername, &adr)) { Con_Printf ("Bad server address\n"); connect_time = -1; return; } if (adr.port == 0) adr.port = BigShort (PORT_SERVER); t2 = Sys_DoubleTime (); connect_time = realtime + t2 - t1; // for retransmit requests Con_Printf ("Connecting to %s...\n", cls.servername); q_snprintf (data, sizeof(data), "%c%c%c%cconnect %d \"%s\"\n", 255, 255, 255, 255, ((gameflags & GAME_PORTALS) == GAME_PORTALS), cls.userinfo); NET_SendPacket (strlen(data), data, &adr); // When we connect to a server, check the mouse is going - S.A. menu_disabled_mouse = false; IN_ActivateMouse(); } /* ================= CL_CheckForResend Resend a connect message if the last one has timed out ================= */ static void CL_CheckForResend (void) { if (connect_time == -1) return; if (cls.state != ca_disconnected) return; if (realtime - connect_time > 5.0) CL_SendConnectPacket (); } /* ================ CL_Connect_f ================ */ static void CL_Connect_f (void) { const char *server; if (Cmd_Argc() != 2) { Con_Printf ("usage: connect \n"); return; } server = Cmd_Argv (1); CL_Disconnect (); Key_SetDest (key_game); // remove console or menu q_strlcpy (cls.servername, server, sizeof(cls.servername)); CL_SendConnectPacket (); } /* ===================== CL_Rcon_f Send the rest of the command line over as an unconnected command. ===================== */ static const unsigned char rcon_hdr[10] = { 255, 255, 255, 255, 'r', 'c', 'o', 'n', ' ', '\0' }; static void CL_Rcon_f (void) { char message[1024]; int i; netadr_t to; if (!rcon_password.string[0]) { Con_Printf ("You must set 'rcon_password' before\n" "issuing an rcon command.\n"); return; } memcpy (message, rcon_hdr, sizeof(rcon_hdr)); q_strlcat (message, rcon_password.string, sizeof(message)); q_strlcat (message, " ", sizeof(message)); for (i = 1; i < Cmd_Argc(); i++) { q_strlcat (message, Cmd_Argv(i), sizeof(message)); q_strlcat (message, " ", sizeof(message)); } if (cls.state >= ca_connected) { to = cls.netchan.remote_address; } else { if (rcon_address.string[0] == '\0') { Con_Printf ("You must either be connected,\n" "or set the 'rcon_address' cvar\n" "to issue rcon commands\n"); return; } NET_StringToAdr (rcon_address.string, &to); if (to.port == 0) { to.port = BigShort (PORT_SERVER); } } NET_SendPacket (strlen(message)+1, message, &to); } /* ===================== CL_ClearState ===================== */ void CL_ClearState (void) { int i; S_StopAllSounds (true); Con_DPrintf ("Clearing memory\n"); D_FlushCaches (); Mod_ClearAll (); /* host_hunklevel MUST be set at this point */ Hunk_FreeToLowMark (host_hunklevel); CL_ClearTEnts (); CL_ClearEffects(); // wipe the entire cl structure memset (&cl, 0, sizeof(cl)); SZ_Clear (&cls.netchan.message); // clear other arrays memset (cl_efrags, 0, sizeof(cl_efrags)); memset (cl_dlights, 0, sizeof(cl_dlights)); memset (cl_lightstyle, 0, sizeof(cl_lightstyle)); // // allocate the efrags and chain together into a free list // cl.free_efrags = cl_efrags; for (i = 0; i < MAX_EFRAGS-1; i++) cl.free_efrags[i].entnext = &cl.free_efrags[i+1]; cl.free_efrags[i].entnext = NULL; SCR_SetPlaqueMessage(""); SB_InvReset(); } /* ===================== CL_Disconnect Sends a disconnect message to the server This is also called on Host_Error, so it shouldn't cause any errors ===================== */ void CL_Disconnect (void) { byte final[10]; connect_time = -1; #ifdef PLATFORM_WINDOWS SetWindowText (mainwindow, "HexenWorld: disconnected"); #endif // don't get stuck in chat mode if (Key_GetDest() == key_message) Key_EndChat (); // reset any active palette shifts (see view.c:V_UpdatePalette()) memset (cl.cshifts, 0, sizeof(cl.cshifts)); // no more siege display, etc. cl_siege = false; // stop sounds (especially looping!) S_StopAllSounds (true); BGM_Stop(); CDAudio_Stop(); // if running a local server, shut it down if (cls.demoplayback) { CL_StopPlayback (); } else if (cls.state != ca_disconnected) { if (cls.demorecording) CL_Stop_f (); final[0] = clc_stringcmd; strcpy ((char *) &final[1], "drop"); Netchan_Transmit (&cls.netchan, 6, final); Netchan_Transmit (&cls.netchan, 6, final); Netchan_Transmit (&cls.netchan, 6, final); cls.state = ca_disconnected; cls.demoplayback = cls.demorecording = cls.timedemo = false; } Cam_Reset(); cl.intermission = 0; } static void CL_Disconnect_f (void) { CL_Disconnect (); } /* ===================== CL_Map_f prints the current map and level names ===================== */ static void CL_Map_f (void) { if (Cmd_Argc() > 1) Con_Printf ("only a server can start or change a map\n"); else if (cls.state == ca_active) Con_Printf ("Current level: %s [ %s ]\n", cl.levelname, cl.mapname); else Con_Printf ("Not connected to a server\n"); } /* ==================== CL_User_f user Dump userdata / masterdata for a user ==================== */ static void CL_User_f (void) { int i, uid; if (Cmd_Argc() != 2) { Con_Printf ("Usage: user \n"); return; } uid = atoi(Cmd_Argv(1)); for (i = 0; i < MAX_CLIENTS; i++) { if (!cl.players[i].name[0]) continue; if (cl.players[i].userid == uid || !strcmp(cl.players[i].name, Cmd_Argv(1)) ) { Info_Print (cl.players[i].userinfo); return; } } Con_Printf ("User not in server.\n"); } /* ==================== CL_Users_f Dump userids for all current players ==================== */ static void CL_Users_f (void) { int i, c; c = 0; Con_Printf ("userid frags name\n"); Con_Printf ("------ ----- ----\n"); for (i = 0; i < MAX_CLIENTS; i++) { if (cl.players[i].name[0]) { Con_Printf ("%6i %4i %s\n", cl.players[i].userid, cl.players[i].frags, cl.players[i].name); c++; } } Con_Printf ("%i total users\n", c); } static void CL_Color_f (void) { // just for quake compatability... int top, bottom; char num[16]; if (Cmd_Argc() == 1) { Con_Printf ("\"color\" is \"%s %s\"\n", Info_ValueForKey (cls.userinfo, "topcolor"), Info_ValueForKey (cls.userinfo, "bottomcolor") ); Con_Printf ("color <0-13> [0-13]\n"); return; } if (Cmd_Argc() == 2) { top = bottom = atoi(Cmd_Argv(1)); } else { top = atoi(Cmd_Argv(1)); bottom = atoi(Cmd_Argv(2)); } top &= 15; if (top > 13) top = 13; bottom &= 15; if (bottom > 13) bottom = 13; q_snprintf (num, sizeof(num), "%i", top); Cvar_SetQuick (&topcolor, num); q_snprintf (num, sizeof(num), "%i", bottom); Cvar_SetQuick (&bottomcolor, num); } /* ================== CL_FullServerinfo_f Sent by server when serverinfo changes ================== */ static void CL_FullServerinfo_f (void) { const char *p; float v; if (Cmd_Argc() != 2) { Con_Printf ("usage: fullserverinfo \n"); return; } q_strlcpy (cl.serverinfo, Cmd_Argv(1), MAX_SERVERINFO_STRING); p = Info_ValueForKey(cl.serverinfo, "*version"); if (*p) { v = atof(p); if (v) { if (!server_version) Con_Printf("Version %1.2f Server\n", v); server_version = v; } } } /* ================== CL_FullInfo_f Allow clients to change userinfo ================== */ static void CL_FullInfo_f (void) { char key[512]; char value[512]; char *o; const char *s; if (Cmd_Argc() != 2) { Con_Printf ("fullinfo \n"); return; } s = Cmd_Argv(1); if (*s == '\\') s++; while (*s) { o = key; while (*s && *s != '\\') *o++ = *s++; *o = 0; if (!*s) { Con_Printf ("MISSING VALUE\n"); return; } o = value; s++; while (*s && *s != '\\') *o++ = *s++; *o = 0; if (*s) s++; Info_SetValueForKey (cls.userinfo, key, value, MAX_INFO_STRING); } } /* ================== CL_SetInfo_f Allow clients to change userinfo ================== */ static void CL_SetInfo_f (void) { if (Cmd_Argc() == 1) { Info_Print (cls.userinfo); return; } if (Cmd_Argc() != 3) { Con_Printf ("usage: setinfo [ ]\n"); return; } if (!q_strcasecmp(Cmd_Argv(1), "pmodel") || !strcmp(Cmd_Argv(1), "emodel")) return; Info_SetValueForKey (cls.userinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_INFO_STRING); if (cls.state >= ca_connected) Cmd_ForwardToServer (); } /* ==================== CL_Packet_f packet Contents allows \n escape character ==================== */ static void CL_Packet_f (void) { unsigned char senddata[2048]; int i, l; const char *in; char *out; netadr_t adr; if (Cmd_Argc() != 3) { Con_Printf ("packet \n"); return; } if (!NET_StringToAdr (Cmd_Argv(1), &adr)) { Con_Printf ("Bad address\n"); return; } in = Cmd_Argv(2); out = (char *)senddata + 4; senddata[0] = senddata[1] = senddata[2] = senddata[3] = 0xff; l = strlen (in); for (i = 0; i < l; i++) { if (in[i] == '\\' && in[i+1] == 'n') { *out++ = '\n'; i++; } else { *out++ = in[i]; } } *out = 0; l = (int) (out - (char *)senddata); NET_SendPacket (l, senddata, &adr); } /* ===================== CL_NextDemo Called to play the next demo in the demo loop ===================== */ void CL_NextDemo (void) { char str[1024]; if (cls.demonum == -1) return; // don't play demos if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS) { cls.demonum = 0; if (!cls.demos[cls.demonum][0]) { // Con_Printf ("No demos listed with startdemos\n"); cls.demonum = -1; return; } } q_snprintf (str, sizeof(str),"playdemo %s\n", cls.demos[cls.demonum]); Cbuf_InsertText (str); cls.demonum++; } /* ================= CL_Changing_f Just sent as a hint to the client that they should drop to full console ================= */ static void CL_Changing_f (void) { S_StopAllSounds (true); cl.intermission = 0; cls.state = ca_connected; // not active anymore, but not disconnected Con_Printf ("\nChanging map...\n"); } /* ================= CL_Reconnect_f The server is changing levels ================= */ static void CL_Reconnect_f (void) { S_StopAllSounds (true); if (cls.state == ca_connected) { Con_Printf ("reconnecting...\n"); MSG_WriteChar (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, "new"); return; } if (!*cls.servername) { Con_Printf("No server to reconnect to...\n"); return; } CL_Disconnect(); CL_SendConnectPacket (); } /* ================= CL_ConnectionlessPacket Responses to broadcasts, etc ================= */ static void CL_ConnectionlessPacket (void) { const char *s; int c; MSG_BeginReading (); MSG_ReadLong (); // skip the -1 c = MSG_ReadByte (); if (!cls.demoplayback) Con_Printf ("%s:\n", NET_AdrToString (&net_from)); Con_DPrintf ("%s", net_message.data + 5); if (c == S2C_CONNECTION) { if (cls.state == ca_connected) { if (!cls.demoplayback) Con_Printf ("Dup connect received. Ignored.\n"); return; } Netchan_Setup (&cls.netchan, &net_from); MSG_WriteChar (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, "new"); cls.state = ca_connected; Con_Printf ("Connected.\n"); return; } // remote command from gui front end if (c == A2C_CLIENT_COMMAND) { if (!NET_CompareBaseAdr(&net_from, &net_local_adr) && !NET_CompareBaseAdr(&net_from, &net_loopback_adr)) { Con_Printf ("Command packet from remote host. Ignored.\n"); return; } #ifdef PLATFORM_WINDOWS ShowWindow (mainwindow, SW_RESTORE); SetForegroundWindow (mainwindow); #endif s = MSG_ReadString (); Cbuf_AddText (s); return; } // print command from somewhere if (c == A2C_PRINT) { s = MSG_ReadString (); Con_Printf ("%s", s); return; } // ping from somewhere if (c == A2A_PING) { byte data[6]; data[0] = 0xff; data[1] = 0xff; data[2] = 0xff; data[3] = 0xff; data[4] = A2A_ACK; data[5] = 0; NET_SendPacket (6, data, &net_from); return; } if (c == svc_disconnect) { Host_EndGame ("Server disconnected\n"); return; } Con_Printf ("Unknown command:\n%c\n", c); } /* ================= CL_ReadPackets ================= */ static void CL_ReadPackets (void) { // while (NET_GetPacket()) while (CL_GetMessage()) { // remote command packet if (*(int *)net_message.data == -1) { CL_ConnectionlessPacket (); continue; } if (net_message.cursize < 8) { Con_Printf ("%s: Runt packet\n",NET_AdrToString(&net_from)); continue; } // packet from server if (!cls.demoplayback && !NET_CompareAdr (&net_from, &cls.netchan.remote_address)) { Con_Printf ("%s: sequenced packet without connection\n", NET_AdrToString(&net_from)); continue; } if (!Netchan_Process(&cls.netchan)) continue; // wasn't accepted for some reason CL_ParseServerMessage (); } // check timeout if (cls.state >= ca_connected && realtime - cls.netchan.last_received > cl_timeout.value) { Con_Printf ("\nServer connection timed out.\n"); CL_Disconnect (); return; } } //============================================================================= /* ===================== CL_Download_f ===================== */ static void CL_Download_f (void) { char tmp[MAX_OSPATH]; int err; if (cls.state == ca_disconnected) { Con_Printf ("Must be connected.\n"); return; } if (Cmd_Argc() != 2) { Con_Printf ("Usage: download \n"); return; } if (strstr(Cmd_Argv(1), "..")) { Con_Printf ("Relative pathnames are not allowed.\n"); return; } FS_MakePath_BUF (FS_USERDIR, &err, tmp, sizeof(tmp), Cmd_Argv(1)); if (err) { Host_Error("%s: %d: string buffer overflow!", __thisfunc__, __LINE__); return; } if (FS_CreatePath(tmp) != 0) { Con_Printf ("Unable to create directory for downloading %s\n", Cmd_Argv(1)); return; } cls.download = fopen (tmp, "wb"); if (!cls.download) { Con_Printf ("Unable to create file for downloading %s\n", Cmd_Argv(1)); return; } // don't use the full user path in order to avoid rename failed messages q_strlcpy (cls.downloadname, Cmd_Argv(1), MAX_OSPATH); q_strlcpy (cls.downloadtempname, Cmd_Argv(1), MAX_OSPATH); cls.downloadtype = dl_single; MSG_WriteByte (&cls.netchan.message, clc_stringcmd); SZ_Print (&cls.netchan.message, va("download %s\n",Cmd_Argv(1))); } #ifdef _WINDOWS /* ================= CL_Minimize_f ================= */ static void CL_Windows_f (void) { SendMessage(mainwindow, WM_SYSKEYUP, VK_TAB, 1 | (0x0F << 16) | (1<<29)); } #endif static void CL_Sensitivity_save_f (void) { static float save_sensitivity = 3; if (Cmd_Argc() != 2) { Con_Printf ("sensitivity_save \n"); } else if (q_strcasecmp(Cmd_Argv(1),"save") == 0) { save_sensitivity = sensitivity.value; } else if (q_strcasecmp(Cmd_Argv(1),"restore") == 0) { Cvar_SetValueQuick (&sensitivity, save_sensitivity); } } /* ================= CL_Init ================= */ static void Host_SaveConfig_f (void); void CL_Init (void) { cls.state = ca_disconnected; Info_SetValueForKey (cls.userinfo, "name", "unnamed", MAX_INFO_STRING); Info_SetValueForKey (cls.userinfo, "playerclass", "1", MAX_INFO_STRING); Info_SetValueForKey (cls.userinfo, "topcolor", "0", MAX_INFO_STRING); Info_SetValueForKey (cls.userinfo, "bottomcolor", "0", MAX_INFO_STRING); Info_SetValueForKey (cls.userinfo, "rate", "2500", MAX_INFO_STRING); Info_SetValueForKey (cls.userinfo, "msg", "1", MAX_INFO_STRING); // capabilities info (single char flags) -- adapted from QuakeForge: // c: chunked connection sequence for sound/modellists (protocol 26) Info_SetValueForStarKey (cls.userinfo, "*cap", "c", MAX_INFO_STRING); CL_InitInput (); CL_InitTEnts (); CL_InitPrediction (); CL_InitEffects (); CL_InitCam (); Pmove_Init (); // register our commands Cmd_AddCommand ("saveconfig", Host_SaveConfig_f); Cvar_RegisterVariable (&developer); if (COM_CheckParm("-developer")) { Cvar_Set ("developer", "1"); Cvar_LockVar ("developer"); } Cvar_RegisterVariable (&sys_throttle); Cvar_RegisterVariable (&host_speeds); Cvar_RegisterVariable (&cl_upspeed); Cvar_RegisterVariable (&cl_forwardspeed); Cvar_RegisterVariable (&cl_backspeed); Cvar_RegisterVariable (&cl_sidespeed); Cvar_RegisterVariable (&cl_movespeedkey); Cvar_RegisterVariable (&cl_yawspeed); Cvar_RegisterVariable (&cl_pitchspeed); Cvar_RegisterVariable (&cl_anglespeedkey); Cvar_RegisterVariable (&cl_shownet); Cvar_RegisterVariable (&cl_timeout); Cvar_RegisterVariable (&lookspring); Cvar_RegisterVariable (&lookstrafe); Cvar_RegisterVariable (&sensitivity); Cvar_RegisterVariable (&mwheelthreshold); Cvar_RegisterVariable (&m_pitch); Cvar_RegisterVariable (&m_yaw); Cvar_RegisterVariable (&m_forward); Cvar_RegisterVariable (&m_side); Cvar_RegisterVariable (&rcon_password); Cvar_RegisterVariable (&rcon_address); Cvar_RegisterVariable (&entlatency); Cvar_RegisterVariable (&cl_predict_players2); Cvar_RegisterVariable (&cl_predict_players); Cvar_RegisterVariable (&cl_solid_players); Cvar_RegisterVariable (&baseskin); Cvar_RegisterVariable (&noskins); Cvar_RegisterVariable (&cfg_unbindall); // info mirrors Cvar_SetCallback (&name, CL_Callback_Userinfo); Cvar_SetCallback (&playerclass, CL_Callback_Userinfo); Cvar_SetCallback (&password, CL_Callback_Userinfo); Cvar_SetCallback (&spectator, CL_Callback_Userinfo); Cvar_SetCallback (&skin, CL_Callback_Userinfo); Cvar_SetCallback (&team, CL_Callback_Userinfo); Cvar_SetCallback (&topcolor, CL_Callback_Userinfo); Cvar_SetCallback (&bottomcolor, CL_Callback_Userinfo); Cvar_SetCallback (&rate, CL_Callback_Userinfo); Cvar_SetCallback (&msg, CL_Callback_Userinfo); Cvar_SetCallback (&noaim, CL_Callback_Userinfo); Cvar_RegisterVariable (&name); Cvar_RegisterVariable (&playerclass); Cvar_RegisterVariable (&password); Cvar_RegisterVariable (&spectator); Cvar_RegisterVariable (&skin); Cvar_RegisterVariable (&team); Cvar_RegisterVariable (&topcolor); Cvar_RegisterVariable (&bottomcolor); Cvar_RegisterVariable (&rate); Cvar_RegisterVariable (&msg); Cvar_RegisterVariable (&noaim); Cvar_RegisterVariable (&talksounds); Cmd_AddCommand ("version", CL_Version_f); Cmd_AddCommand ("changing", CL_Changing_f); Cmd_AddCommand ("disconnect", CL_Disconnect_f); Cmd_AddCommand ("record", CL_Record_f); Cmd_AddCommand ("rerecord", CL_ReRecord_f); Cmd_AddCommand ("stop", CL_Stop_f); Cmd_AddCommand ("playdemo", CL_PlayDemo_f); Cmd_AddCommand ("timedemo", CL_TimeDemo_f); Cmd_AddCommand ("map", CL_Map_f); Cmd_AddCommand ("skins", Skin_Skins_f); Cmd_AddCommand ("allskins", Skin_AllSkins_f); Cmd_AddCommand ("quit", CL_Quit_f); Cmd_AddCommand ("connect", CL_Connect_f); Cmd_AddCommand ("reconnect", CL_Reconnect_f); Cmd_AddCommand ("rcon", CL_Rcon_f); Cmd_AddCommand ("packet", CL_Packet_f); Cmd_AddCommand ("user", CL_User_f); Cmd_AddCommand ("users", CL_Users_f); Cmd_AddCommand ("setinfo", CL_SetInfo_f); Cmd_AddCommand ("fullinfo", CL_FullInfo_f); Cmd_AddCommand ("fullserverinfo", CL_FullServerinfo_f); Cmd_AddCommand ("color", CL_Color_f); Cmd_AddCommand ("download", CL_Download_f); Cmd_AddCommand ("sensitivity_save", CL_Sensitivity_save_f); // forward to server commands Cmd_AddCommand ("kill", NULL); Cmd_AddCommand ("say", NULL); Cmd_AddCommand ("say_team", NULL); Cmd_AddCommand ("serverinfo", NULL); // Windows commands #ifdef _WINDOWS Cmd_AddCommand ("windows", CL_Windows_f); #endif } /* ================ Host_EndGame Call this to drop to a console without exiting the qwcl Does not return due longjmp() ================ */ void Host_EndGame (const char *message, ...) { va_list argptr; char string[1024]; va_start (argptr,message); q_vsnprintf (string, sizeof(string), message, argptr); va_end (argptr); Con_Printf ("\n===========================\n"); Con_Printf ("%s: %s\n", __thisfunc__, string); Con_Printf ("===========================\n\n"); CL_Disconnect (); longjmp (host_abort, 1); } /* ================ Host_Error This shuts down the client and exits qwcl ================ */ void Host_Error (const char *error, ...) { va_list argptr; char string[1024]; static qboolean inerror = false; if (inerror) Sys_Error ("%s: recursive error!", __thisfunc__); inerror = true; va_start (argptr,error); q_vsnprintf (string, sizeof(string), error, argptr); va_end (argptr); Con_Printf ("%s: %s\n", __thisfunc__, string); CL_Disconnect (); cls.demonum = -1; inerror = false; // FIXME Sys_Error ("%s: %s\n", __thisfunc__, string); } /* =============== Host_WriteConfiguration Writes key bindings and archived cvars to config.cfg =============== */ void Host_WriteConfiguration (const char *fname) { FILE *f; if (host_initialized && !host_parms->errstate) { f = fopen (FS_MakePath(FS_USERDIR,NULL,fname), "w"); if (!f) { Con_Printf ("Couldn't write %s.\n",fname); return; } Key_WriteBindings (f); Cvar_WriteVariables (f); // if mlook was down, keep it that way: if (in_mlook.state & 1) fprintf (f, "+mlook\n"); fclose (f); } } static void Host_SaveConfig_f (void) { const char *p; if (Cmd_Argc() != 2) { Con_Printf ("saveconfig : save a config file\n"); return; } p = Cmd_Argv(1); if (*p == '.' || strstr(p, "..")) { Con_Printf ("Invalid config name.\n"); return; } Host_WriteConfiguration (p); } //============================================================================ /* ================== Host_Frame Runs all active servers ================== */ void Host_Frame (float time) { static double time1 = 0; static double time2 = 0; static double time3 = 0; int pass1, pass2, pass3; float fps; if (setjmp(host_abort)) return; // something bad happened, or the server disconnected // decide the simulation time realtime += time; if (oldrealtime > realtime) oldrealtime = 0; fps = q_max(30.0, q_min(rate.value/80.0, 72.0)); if (!cls.timedemo && realtime - oldrealtime < 1.0/fps) return; // framerate is too high host_frametime = realtime - oldrealtime; oldrealtime = realtime; if (host_frametime > 0.2) host_frametime = 0.2; // get new key events Sys_SendKeyEvents (); // allow mice or other external controllers to add commands IN_Commands (); // process console commands Cbuf_Execute (); // fetch results from server CL_ReadPackets (); // send intentions now // resend a connection request if necessary if (cls.state == ca_disconnected) { CL_CheckForResend (); } else { CL_SendCmd (); } // Set up prediction for other players CL_SetUpPlayerPrediction(false); // do client side motion prediction CL_PredictMove (); // Set up prediction for other players CL_SetUpPlayerPrediction(true); // build a refresh entity list CL_EmitEntities (); // update video if (host_speeds.integer) time1 = Sys_DoubleTime (); SCR_UpdateScreen (); if (host_speeds.integer) time2 = Sys_DoubleTime (); // update audio BGM_Update(); // adds music raw samples and/or advances midi driver if (cls.state == ca_active) { S_Update (r_origin, vpn, vright, vup); CL_DecayLights (); } else { S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin); } CDAudio_Update(); if (host_speeds.integer) { pass1 = (time1 - time3)*1000; time3 = Sys_DoubleTime (); pass2 = (time2 - time1)*1000; pass3 = (time3 - time2)*1000; Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n", pass1+pass2+pass3, pass1, pass2, pass3); } host_framecount++; } //============================================================================ /* ==================== Host_Init ==================== */ void Host_Init (void) { Sys_Printf ("Host_Init\n"); Memory_Init (host_parms->membase, host_parms->memsize); HuffInit (); Cbuf_Init (); Cmd_Init (); Cvar_RegisterVariable (&sys_nostdout); COM_Init (); FS_Init (); CL_Cmd_Init (); NET_Init (PORT_CLIENT); Netchan_Init (); CFG_OpenConfig ("config.cfg"); Mod_Init (); Con_Printf ("Exe: " __TIME__ " " __DATE__ "\n"); Con_Printf ("%4.1f megs RAM used.\n", host_parms->memsize/(1024*1024.0)); R_InitTextures (); V_Init (); W_LoadWadFile ("gfx.wad"); Key_Init (); Con_Init (); M_Init (); host_basepal = (byte *)FS_LoadHunkFile ("gfx/palette.lmp", NULL); if (!host_basepal) Sys_Error ("Couldn't load gfx/palette.lmp"); host_colormap = (byte *)FS_LoadHunkFile ("gfx/colormap.lmp", NULL); if (!host_colormap) Sys_Error ("Couldn't load gfx/colormap.lmp"); VID_Init (host_basepal); Draw_Init (); SCR_Init (); R_Init (); Sbar_Init (); cls.state = ca_disconnected; S_Init (); CDAudio_Init (); MIDI_Init (); BGM_Init (); CL_Init (); IN_Init (); CFG_CloseConfig(); // move commands and cvars used by progs to the front for faster access Cmd_MoveToFront ("bf"); #ifdef GLQUAKE /* analogous to host_hunklevel, this will mark OpenGL texture * beyond which everything will need to be purged on new map */ gl_texlevel = numgltextures; #endif Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); host_hunklevel = Hunk_LowMark (); host_initialized = true; Con_Printf ("\n======= HexenWorld Initialized ========\n\n"); /* execute the hexen.rc file: a valid file runs default.cfg, config.cfg * and autoexec.cfg in this order, then processes the cmdline arguments * by sending "stuffcmds". */ Cbuf_InsertText ("exec hexen.rc\n"); if (!setjmp(host_abort)) /* in case exec fails with a longjmp(), e.g. Host_Error() */ Cbuf_Execute (); Cvar_UnlockAll (); /* unlock the early-set cvars after init */ } /* =============== Host_Shutdown FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better to run quit through here before the final handoff to the sys code. =============== */ void Host_Shutdown(void) { static qboolean isdown = false; if (isdown) { printf ("recursive shutdown\n"); return; } isdown = true; Host_WriteConfiguration ("config.cfg"); BGM_Shutdown (); CDAudio_Shutdown (); MIDI_Cleanup (); NET_Shutdown (); S_Shutdown(); IN_Shutdown (); if (host_basepal) VID_Shutdown(); LOG_Close (); } engine/hexenworld/client/cl_parse.c000066400000000000000000001234141444734033100177240ustar00rootroot00000000000000/* * cl_parse.c -- parse a message received from the server * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "bgmusic.h" #include "cdaudio.h" #include "r_shared.h" static const char *svc_strings[] = { "svc_bad", "svc_nop", "svc_disconnect", "svc_updatestat", "svc_version", // [long] server version "svc_setview", // [short] entity number "svc_sound", // "svc_time", // [float] server time "svc_print", // [string] null terminated string "svc_stufftext", // [string] stuffed into client's console buffer // the string should be \n terminated "svc_setangle", // [vec3] set the view angle to this absolute value "svc_serverdata", // [long] version ... "svc_lightstyle", // [byte] [string] "svc_updatename", // [byte] [string] "svc_updatefrags", // [byte] [short] "svc_clientdata", // "svc_stopsound", // "svc_updatecolors", // [byte] [byte] "svc_particle", // [vec3] "svc_damage", // [byte] impact [byte] blood [vec3] from "svc_spawnstatic", "OBSOLETE svc_spawnbinary", "svc_spawnbaseline", "svc_temp_entity", // "svc_setpause", "svc_signonnum", "svc_centerprint", "svc_killedmonster", "svc_foundsecret", "svc_spawnstaticsound", "svc_intermission", "svc_finale", "svc_cdtrack", "svc_sellscreen", "svc_smallkick", "svc_bigkick", "svc_updateping", "svc_updateentertime", "svc_updatestatlong", "svc_muzzleflash", "svc_updateuserinfo", "svc_download", "svc_playerinfo", "svc_nails", "svc_choke", "svc_modellist", "svc_soundlist", "svc_packetentities", "svc_deltapacketentities", "svc_maxspeed", "svc_entgravity", "svc_plaque", "svc_particle_explosion", "svc_set_view_tint", "svc_start_effect", "svc_end_effect", "svc_set_view_flags", "svc_clear_view_flags", "svc_update_inv", "svc_particle2", "svc_particle3", "svc_particle4", "svc_turn_effect", "svc_update_effect", "svc_multieffect", "svc_midi_name", "svc_raineffect", "svc_packmissile", "svc_indexed_print", "svc_targetupdate", "svc_name_print", "svc_sound_update_pos", "svc_update_piv", "svc_player_sound", "svc_updatepclass", "svc_updatedminfo", // [byte] [short] [byte] "svc_updatesiegeinfo", // [byte] [byte] "svc_updatesiegeteam", // [byte] [byte] "svc_updatesiegelosses",// [byte] [byte] "svc_haskey", // [byte] [byte] "svc_nonehaskey", // [byte] "svc_isdoc", // [byte] [byte] "svc_nodoc", // [byte] "svc_playerskipped" // [byte] "NEW PROTOCOL", "NEW PROTOCOL", "NEW PROTOCOL", "NEW PROTOCOL" }; #define NUM_SVC_STRINGS (sizeof(svc_strings) / sizeof(svc_strings[0])) int parsecountmod; double parsecounttime; qmodel_t *player_models[MAX_PLAYER_CLASS]; int cl_spikeindex, cl_playerindex[MAX_PLAYER_CLASS], cl_flagindex; int cl_ballindex, cl_missilestarindex, cl_ravenindex, cl_raven2index; //============================================================================= /* =============== CL_CheckOrDownloadFile Returns true if the file exists, otherwise it attempts to start a download from the server. =============== */ qboolean CL_CheckOrDownloadFile (const char *filename) { if (strstr (filename, "..")) { Con_Printf ("Refusing to download a path with ..\n"); return true; } if (FS_FileExists(filename, NULL)) { // it exists, no need to download return true; } //ZOID - can't download when recording if (cls.demorecording) { Con_Printf("Unable to download %s in record mode.\n", cls.downloadname); return true; } //ZOID - can't download when playback if (cls.demoplayback) return true; q_strlcpy (cls.downloadname, filename, MAX_OSPATH); Con_Printf ("Downloading %s...\n", cls.downloadname); // download to a temp name, and only rename // to the real name when done, so if interrupted // a runt file wont be left COM_StripExtension (cls.downloadname, cls.downloadtempname, sizeof(cls.downloadtempname)); q_strlcat (cls.downloadtempname, ".tmp", sizeof(cls.downloadtempname)); MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, va("download %s", cls.downloadname)); cls.downloadnumber++; return false; } /* ================= Model_NextDownload ================= */ static void Model_NextDownload (void) { const char *s; int i; if (cls.downloadnumber == 0) { Con_Printf ("Checking models...\n"); cls.downloadnumber = 1; } cls.downloadtype = dl_model; for ( ; cl.model_name[cls.downloadnumber][0] ; cls.downloadnumber++) { s = cl.model_name[cls.downloadnumber]; if (s[0] == '*') continue; // inline brush model if (!CL_CheckOrDownloadFile(s)) return; // started a download } for (i = 1; i < MAX_MODELS; i++) { if (!cl.model_name[i][0]) break; cl.model_precache[i] = Mod_ForName (cl.model_name[i], false); if (!cl.model_precache[i]) { Con_Printf ("\nThe required model file '%s' could not be found or downloaded.\n\n", cl.model_name[i]); Con_Printf ("You may need to download or purchase a %s client " "pack in order to play on this server.\n\n", fs_gamedir_nopath); CL_Disconnect (); return; } } // copy the naked name of the map file to the cl structure COM_FileBase (cl.model_name[1], cl.mapname, sizeof(cl.mapname)); // all done cl.worldmodel = cl.model_precache[1]; R_NewMap (); Host_LoadStrings(); Hunk_Check (); // make sure nothing is hurt // done with modellist, request first of static signon messages MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, va("prespawn %i", cl.servercount)); } /* ================= Sound_NextDownload ================= */ static void Sound_NextDownload (void) { const char *s; int i; if (cls.downloadnumber == 0) { Con_Printf ("Checking sounds...\n"); cls.downloadnumber = 1; } cls.downloadtype = dl_sound; for ( ; cl.sound_name[cls.downloadnumber][0] ; cls.downloadnumber++) { s = cl.sound_name[cls.downloadnumber]; if (!CL_CheckOrDownloadFile(va("sound/%s",s))) return; // started a download } for (i = 1; i < MAX_SOUNDS; i++) { if (!cl.sound_name[i][0]) break; cl.sound_precache[i] = S_PrecacheSound (cl.sound_name[i]); } // done with sounds, request models now MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, va("modellist %i", cl.servercount)); } /* ====================== CL_RequestNextDownload ====================== */ static void CL_RequestNextDownload (void) { switch (cls.downloadtype) { case dl_single: break; case dl_skin: Skin_NextDownload (); break; case dl_model: Model_NextDownload (); break; case dl_sound: Sound_NextDownload (); break; case dl_none: default: Con_DPrintf("Unknown download type.\n"); } } /* ===================== CL_ParseDownload A download message has been received from the server ===================== */ static void CL_ParseDownload (void) { int size, percent; char name[MAX_OSPATH]; // read the data size = MSG_ReadShort (); percent = MSG_ReadByte (); if (cls.demoplayback) { if (size > 0) msg_readcount += size; return; // not in demo playback } if (size == -1) { Con_Printf ("File not found.\n"); if (cls.download) { Con_Printf ("cls.download shouldn't have been set\n"); fclose (cls.download); cls.download = NULL; } CL_RequestNextDownload (); return; } // open the file if not opened yet if (!cls.download) { FS_MakePath_BUF (FS_USERDIR, NULL, name, sizeof(name), cls.downloadtempname); if ( FS_CreatePath(name) ) { msg_readcount += size; Con_Printf ("Unable to create directory for downloading %s\n", cls.downloadtempname); CL_RequestNextDownload (); return; } cls.download = fopen (name, "wb"); if (!cls.download) { msg_readcount += size; Con_Printf ("Failed to open %s\n", cls.downloadtempname); CL_RequestNextDownload (); return; } } fwrite (net_message.data + msg_readcount, 1, size, cls.download); msg_readcount += size; if (percent != 100) { // request next block cls.downloadpercent = percent; MSG_WriteByte (&cls.netchan.message, clc_stringcmd); SZ_Print (&cls.netchan.message, "nextdl"); } else { // rename the temp file to it's final name char oldn[MAX_OSPATH]; char newn[MAX_OSPATH]; fclose (cls.download); cls.download = NULL; cls.downloadpercent = 0; FS_MakePath_BUF (FS_USERDIR, NULL, oldn, sizeof(oldn), cls.downloadtempname); FS_MakePath_BUF (FS_USERDIR, NULL, newn, sizeof(newn), cls.downloadname); if (Sys_rename(oldn, newn) != 0) Con_Printf ("failed to rename.\n"); // get another file if needed CL_RequestNextDownload (); } } /* ===================================================================== SERVER CONNECTING MESSAGES ===================================================================== */ /* ================== CL_ParseServerData ================== */ static void CL_ParseServerData (void) { const char *str; Con_DPrintf ("Serverdata packet received.\n"); // // wipe the client_state_t struct // CL_ClearState (); // CL_ClearState() clears the cl structure already, // so no need zero'ing the sound/model list arrays. //memset (cl.sound_precache, 0, sizeof(cl.sound_precache)); //memset (cl.model_precache, 0, sizeof(cl.model_precache)); cl_playerindex[0] = -1; cl_playerindex[1] = -1; cl_playerindex[2] = -1; cl_playerindex[3] = -1; cl_playerindex[4] = -1; cl_playerindex[5] = -1;//mg-siege cl_spikeindex = -1; cl_flagindex = -1; cl_ballindex = -1; cl_missilestarindex = -1; cl_ravenindex = -1; cl_raven2index = -1; // parse protocol version number cl.protocol = MSG_ReadLong (); switch (cl.protocol) { case OLD_PROTOCOL_VERSION: case PROTOCOL_VERSION: case PROTOCOL_VERSION_EXT: Con_Printf ("Server using protocol %i\n", cl.protocol); break; default: Host_EndGame ("Server returned unsupported protocol %i", cl.protocol); } cl.servercount = MSG_ReadLong (); // game directory str = MSG_ReadString (); if (q_strcasecmp(fs_gamedir_nopath, str)) { Con_Printf("Server set the gamedir to %s\n", str); // save current config Host_WriteConfiguration ("config.cfg"); // set the new gamedir and userdir FS_Gamedir(str); // ZOID - run autoexec.cfg in the gamedir if it exists if (FS_FileInGamedir("config.cfg")) { // remove any weird mod specific key bindings / aliases Cbuf_AddText("unbindall\n"); Cbuf_AddText("unaliasall\n"); Cbuf_AddText("exec autoexec.cfg\n"); Cbuf_AddText("exec config.cfg\n"); } // gamespy crap if (FS_FileInGamedir("frontend.cfg")) Cbuf_AddText("exec frontend.cfg\n"); Cbuf_Execute (); // re-init draw Draw_ReInit (); } // parse player slot, high bit means spectator cl.playernum = MSG_ReadByte (); if (cl.playernum & 128) { cl.spectator = true; cl.playernum &= ~128; } // get the full level name str = MSG_ReadString (); q_strlcpy (cl.levelname, str, sizeof(cl.levelname)); // get the movevars if (cl.protocol >= PROTOCOL_VERSION) { movevars.gravity = MSG_ReadFloat(); movevars.stopspeed = MSG_ReadFloat(); movevars.maxspeed = MSG_ReadFloat(); movevars.spectatormaxspeed = MSG_ReadFloat(); movevars.accelerate = MSG_ReadFloat(); movevars.airaccelerate = MSG_ReadFloat(); movevars.wateraccelerate = MSG_ReadFloat(); movevars.friction = MSG_ReadFloat(); movevars.waterfriction = MSG_ReadFloat(); movevars.entgravity = MSG_ReadFloat(); } else { movevars.gravity = 800; movevars.stopspeed = 100; movevars.maxspeed = 320; movevars.spectatormaxspeed = 500; movevars.accelerate = 10; movevars.airaccelerate = 0.7; movevars.wateraccelerate = 10; movevars.friction = 6; movevars.waterfriction = 1; movevars.entgravity = 1.0; } // seperate the printfs so the server message can have a color Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n"); Con_Printf ("%c%s\n", 2, str); // ask for the sound list next MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, va("soundlist %i", cl.servercount)); // now waiting for downloads, etc cls.state = ca_onserver; cl_keyholder = -1; cl_doc = -1; } static void CL_ParseSoundlistChunks (void) /* from QW */ { int numsounds, n; const char *str; // precache sounds numsounds = MSG_ReadLong (); for (;;) { str = MSG_ReadString (); if (!str[0]) break; numsounds++; if (numsounds >= MAX_SOUNDS) Host_Error ("Server sent too many sound_precache"); strcpy (cl.sound_name[numsounds], str); } n = MSG_ReadLong (); if (n) { if (!cls.demoplayback) { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, va ("soundlist %i %i", cl.servercount, n)); } return; } cls.downloadnumber = 0; cls.downloadtype = dl_sound; Sound_NextDownload (); } /* ================== CL_ParseSoundlist ================== */ static void CL_ParseSoundlist (void) { int numsounds; const char *str; // precache sounds for (numsounds = 1 ; ; numsounds++) { str = MSG_ReadString (); if (!str[0]) break; if (numsounds == MAX_SOUNDS) Host_EndGame ("Server sent too many sound_precache"); q_strlcpy (cl.sound_name[numsounds], str, MAX_QPATH); } cls.downloadnumber = 0; cls.downloadtype = dl_sound; Sound_NextDownload (); } static void CL_ParseModellistChunks (void) /* from QW */ { int nummodels, n; const char *str; // precache models and note certain default indexes nummodels = MSG_ReadLong (); for (;;) { str = MSG_ReadString (); if (!str[0]) break; nummodels++; if (nummodels >= MAX_MODELS) Host_EndGame ("Server sent too many model_precache"); q_strlcpy (cl.model_name[nummodels], str, MAX_QPATH); if (!strcmp(cl.model_name[nummodels],"progs/spike.mdl")) cl_spikeindex = nummodels; if (!strcmp(cl.model_name[nummodels],"models/paladin.mdl")) cl_playerindex[0] = nummodels; if (!strcmp(cl.model_name[nummodels],"models/crusader.mdl")) cl_playerindex[1] = nummodels; if (!strcmp(cl.model_name[nummodels],"models/necro.mdl")) cl_playerindex[2] = nummodels; if (!strcmp(cl.model_name[nummodels],"models/assassin.mdl")) cl_playerindex[3] = nummodels; if (!strcmp(cl.model_name[nummodels],"models/succubus.mdl")) cl_playerindex[4] = nummodels; if (!strcmp(cl.model_name[nummodels],"models/hank.mdl")) cl_playerindex[5] = nummodels;//mg-siege if (!strcmp(cl.model_name[nummodels],"progs/flag.mdl")) cl_flagindex = nummodels; if (!strcmp(cl.model_name[nummodels],"models/ball.mdl")) cl_ballindex = nummodels; if (!strcmp(cl.model_name[nummodels],"models/newmmis.mdl")) cl_missilestarindex = nummodels; if (!strcmp(cl.model_name[nummodels],"models/ravproj.mdl")) cl_ravenindex = nummodels; if (!strcmp(cl.model_name[nummodels],"models/vindsht1.mdl")) cl_raven2index = nummodels; } player_models[0] = (qmodel_t *)Mod_FindName ("models/paladin.mdl"); player_models[1] = !(gameflags & GAME_OLD_DEMO) ? (qmodel_t *)Mod_FindName ("models/crusader.mdl") : NULL; player_models[2] = !(gameflags & GAME_OLD_DEMO) ? (qmodel_t *)Mod_FindName ("models/necro.mdl") : NULL; player_models[3] = (qmodel_t *)Mod_FindName ("models/assassin.mdl"); player_models[4] = (qmodel_t *)Mod_FindName ("models/succubus.mdl"); player_models[5] = (qmodel_t *)Mod_FindName ("models/hank.mdl");//siege n = MSG_ReadLong (); if (n) { if (!cls.demoplayback) { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, va ("modellist %i %i", cl.servercount, n)); } return; } cls.downloadnumber = 0; cls.downloadtype = dl_model; Model_NextDownload (); } /* ================== CL_ParseModellist ================== */ static void CL_ParseModellist (void) { int nummodels; const char *str; // precache models and note certain default indexes for (nummodels = 1 ; ; nummodels++) { str = MSG_ReadString (); if (!str[0]) break; if (nummodels == MAX_MODELS) Host_EndGame ("Server sent too many model_precache"); q_strlcpy (cl.model_name[nummodels], str, MAX_QPATH); if (!strcmp(cl.model_name[nummodels],"progs/spike.mdl")) cl_spikeindex = nummodels; if (!strcmp(cl.model_name[nummodels],"models/paladin.mdl")) cl_playerindex[0] = nummodels; if (!strcmp(cl.model_name[nummodels],"models/crusader.mdl")) cl_playerindex[1] = nummodels; if (!strcmp(cl.model_name[nummodels],"models/necro.mdl")) cl_playerindex[2] = nummodels; if (!strcmp(cl.model_name[nummodels],"models/assassin.mdl")) cl_playerindex[3] = nummodels; if (!strcmp(cl.model_name[nummodels],"models/succubus.mdl")) cl_playerindex[4] = nummodels; if (!strcmp(cl.model_name[nummodels],"models/hank.mdl")) cl_playerindex[5] = nummodels;//mg-siege if (!strcmp(cl.model_name[nummodels],"progs/flag.mdl")) cl_flagindex = nummodels; if (!strcmp(cl.model_name[nummodels],"models/ball.mdl")) cl_ballindex = nummodels; if (!strcmp(cl.model_name[nummodels],"models/newmmis.mdl")) cl_missilestarindex = nummodels; if (!strcmp(cl.model_name[nummodels],"models/ravproj.mdl")) cl_ravenindex = nummodels; if (!strcmp(cl.model_name[nummodels],"models/vindsht1.mdl")) cl_raven2index = nummodels; } player_models[0] = (qmodel_t *)Mod_FindName ("models/paladin.mdl"); player_models[1] = !(gameflags & GAME_OLD_DEMO) ? (qmodel_t *)Mod_FindName ("models/crusader.mdl") : NULL; player_models[2] = !(gameflags & GAME_OLD_DEMO) ? (qmodel_t *)Mod_FindName ("models/necro.mdl") : NULL; player_models[3] = (qmodel_t *)Mod_FindName ("models/assassin.mdl"); player_models[4] = (qmodel_t *)Mod_FindName ("models/succubus.mdl"); player_models[5] = (qmodel_t *)Mod_FindName ("models/hank.mdl");//siege cls.downloadnumber = 0; cls.downloadtype = dl_model; Model_NextDownload (); } /* ================== CL_ParseBaseline ================== */ static void CL_ParseBaseline (entity_state_t *es) { int i; es->modelindex = MSG_ReadShort (); es->frame = MSG_ReadByte (); es->colormap = MSG_ReadByte(); es->skinnum = MSG_ReadByte(); es->scale = MSG_ReadByte(); es->drawflags = MSG_ReadByte(); es->abslight = MSG_ReadByte(); for (i = 0; i < 3; i++) { es->origin[i] = MSG_ReadCoord (); es->angles[i] = MSG_ReadAngle (); } } /* ===================== CL_ParseStatic Static entities are non-interactive world objects like torches ===================== */ static void CL_ParseStatic (void) { entity_t *ent; int i; entity_state_t es; CL_ParseBaseline (&es); i = cl.num_statics; if (i >= MAX_STATIC_ENTITIES) Host_EndGame ("Too many static entities"); ent = &cl_static_entities[i]; cl.num_statics++; // copy it to the current state ent->model = cl.model_precache[es.modelindex]; ent->frame = es.frame; ent->colormap = vid.colormap; ent->skinnum = es.skinnum; ent->scale = es.scale; ent->drawflags = es.drawflags; ent->abslight = es.abslight; VectorCopy (es.origin, ent->origin); VectorCopy (es.angles, ent->angles); R_AddEfrags (ent); } /* =================== CL_ParseStaticSound =================== */ static void CL_ParseStaticSound (void) { vec3_t org; int sound_num, vol, atten; int i; for (i = 0; i < 3; i++) org[i] = MSG_ReadCoord (); sound_num = MSG_ReadByte (); vol = MSG_ReadByte (); atten = MSG_ReadByte (); S_StaticSound (cl.sound_precache[sound_num], org, vol, atten); } /* ===================================================================== ACTION MESSAGES ===================================================================== */ /* ================== CL_ParseStartSoundPacket ================== */ static void CL_ParseStartSoundPacket(void) { vec3_t pos; int channel, ent; int sound_num, volume; float attenuation; int i; channel = MSG_ReadShort(); if (channel & SND_VOLUME) volume = MSG_ReadByte (); else volume = DEFAULT_SOUND_PACKET_VOLUME; if (channel & SND_ATTENUATION) attenuation = MSG_ReadByte () / 32.0; else attenuation = DEFAULT_SOUND_PACKET_ATTENUATION; sound_num = MSG_ReadByte (); for (i = 0; i < 3; i++) pos[i] = MSG_ReadCoord (); ent = (channel>>3)&1023; channel &= 7; if (ent > MAX_EDICTS) Host_EndGame ("%s: ent = %i", __thisfunc__, ent); S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation); } /* ================== CL_ParseClientdata Server information pertaining to this client only, sent every frame ================== */ static void CL_ParseClientdata (void) { int i; float latency; frame_t *frame; // calculate simulated time of message i = cls.netchan.incoming_acknowledged; cl.parsecount = i; i &= UPDATE_MASK; parsecountmod = i; frame = &cl.frames[i]; parsecounttime = cl.frames[i].senttime; frame->receivedtime = realtime; // calculate latency latency = frame->receivedtime - frame->senttime; if (latency < 0 || latency > 1.0) { // Con_Printf ("Odd latency: %5.2f\n", latency); } else { // drift the average latency towards the observed latency if (latency < cls.latency) cls.latency = latency; else cls.latency += 0.001; // drift up, so correction are needed } } /* ===================== CL_NewTranslation ===================== */ static void CL_NewTranslation (int slot) { #ifdef GLQUAKE if (slot >= MAX_CLIENTS) Sys_Error ("%s: slot > MAX_CLIENTS", __thisfunc__); R_TranslatePlayerSkin(slot); #else int i, j; int top, bottom; byte *dest, *source, *sourceA, *sourceB, *colorA, *colorB; player_info_t *player; if (slot >= MAX_CLIENTS) Sys_Error ("%s: slot > MAX_CLIENTS", __thisfunc__); player = &cl.players[slot]; if (!player->playerclass) return; dest = player->translations; source = vid.colormap; memcpy (dest, vid.colormap, sizeof(player->translations)); top = player->topcolor; if (top > 10 || top < 0) top = 10; bottom = player->bottomcolor; if (bottom > 10 || bottom < 0) bottom = 10; top -= 1; bottom -= 1; for (i = 0; i < VID_GRADES; i++, dest += 256, source += 256) { colorA = playerTranslation + 256 + color_offsets[(int)player->playerclass-1]; colorB = colorA + 256; sourceA = colorB + 256 + (top * 256); sourceB = colorB + 256 + (bottom * 256); for (j = 0; j < 256; j++, colorA++, colorB++, sourceA++, sourceB++) { if (top >= 0 && (*colorA != 255)) dest[j] = source[*sourceA]; if (bottom >= 0 && (*colorB != 255)) dest[j] = source[*sourceB]; } } #endif } /* ============== CL_UpdateUserinfo ============== */ static void CL_UpdateUserinfo (void) { int slot; player_info_t *player; slot = MSG_ReadByte (); if (slot >= MAX_CLIENTS) Host_EndGame ("%s: svc_updateuserinfo > MAX_CLIENTS", __thisfunc__); player = &cl.players[slot]; player->userid = MSG_ReadLong (); q_strlcpy (player->userinfo, MSG_ReadString(), sizeof(player->userinfo)); q_strlcpy (player->name, Info_ValueForKey (player->userinfo, "name"), sizeof(player->name)); player->topcolor = atoi(Info_ValueForKey (player->userinfo, "topcolor")); player->bottomcolor = atoi(Info_ValueForKey (player->userinfo, "bottomcolor")); if (Info_ValueForKey (player->userinfo, "*spectator")[0]) player->spectator = true; else player->spectator = false; if (cls.state == ca_active) Skin_Find (player); player->playerclass = atoi(Info_ValueForKey (player->userinfo, "playerclass")); /* if (cl.playernum == slot && player->playerclass != playerclass.integer) Cvar_SetValue ("playerclass",player->playerclass); */ Sbar_Changed (); player->Translated = false; CL_NewTranslation (slot); } /* ===================== CL_SetStat ===================== */ static void CL_SetStat (int idx, int value) { int j; if (idx < 0 || idx >= MAX_CL_STATS) Sys_Error ("%s: %i is invalid", __thisfunc__, idx); Sbar_Changed (); if (idx == STAT_ITEMS) { // set flash times Sbar_Changed (); for (j = 0; j < 32; j++) if ((value & (1<= MAX_CLIENTS) return; #ifdef GLQUAKE // don't draw our own muzzle flash in gl if flashblending if (i - 1 == cl.playernum && gl_flashblend.integer) return; #endif pl = &cl.frames[parsecountmod].playerstate[i-1]; dl = CL_AllocDlight (i); VectorCopy (pl->origin, dl->origin); AngleVectors (pl->viewangles, fv, rv, uv); VectorMA (dl->origin, 18, fv, dl->origin); dl->radius = 200 + (rand() & 31); dl->minlight = 32; dl->die = cl.time + 0.1; dl->color[0] = 0.2; dl->color[1] = 0.1; dl->color[2] = 0.05; dl->color[3] = 0.7; } static void CL_Plaque(void) { int idx; idx = MSG_ReadShort (); if (idx > 0 && idx <= host_string_count) SCR_SetPlaqueMessage (Host_GetString(idx - 1)); else SCR_SetPlaqueMessage (""); } static void CL_IndexedPrint(void) { int idx, i; i = MSG_ReadByte (); if (i == PRINT_CHAT) { S_LocalSound ("misc/talk.wav"); con_ormask = 256; } idx = MSG_ReadShort (); if (idx > 0 && idx <= host_string_count) Con_Printf ("%s", Host_GetString(idx - 1)); con_ormask = 0; } static void CL_NamePrint(void) { int idx, i; i = MSG_ReadByte (); if (i == PRINT_CHAT) { S_LocalSound ("misc/talk.wav"); con_ormask = 256; } idx = MSG_ReadByte (); if (idx >= 0 && idx < MAX_CLIENTS) Con_Printf ("%s", cl.players[idx].name); con_ormask = 0; } static void CL_ParticleExplosion(void) { vec3_t org; short color, radius, counter; org[0] = MSG_ReadCoord(); org[1] = MSG_ReadCoord(); org[2] = MSG_ReadCoord(); color = MSG_ReadShort(); radius = MSG_ReadShort(); counter = MSG_ReadShort(); R_ColoredParticleExplosion(org,color,radius,counter); } #if 0 /* for debugging. from fteqw. */ static void CL_DumpPacket (void) { int i, pos; unsigned char *packet = net_message.data; Con_Printf("%s, BEGIN:\n", __thisfunc__); pos = 0; while (pos < net_message.cursize) { Con_Printf("%5i ", pos); for (i = 0; i < 16; i++) { if (pos >= net_message.cursize) Con_Printf(" X "); else Con_Printf("%2x ", packet[pos]); pos++; } pos -= 16; for (i = 0; i < 16; i++) { if (pos >= net_message.cursize) Con_Printf("X"); else if (packet[pos] == 0) Con_Printf("."); else Con_Printf("%c", packet[pos]); pos++; } Con_Printf("\n"); } Con_Printf("%s, --- END ---\n", __thisfunc__); } #endif /* CL_DumpPacket */ #define SHOWNET(S) \ do { \ if (cl_shownet.integer == 2) \ Con_Printf ("%3i:%s\n", msg_readcount-1, (S)); \ } while (0) /* ===================== CL_ParseServerMessage ===================== */ //static int received_framecount; int LastServerMessageSize = 0; void CL_ParseServerMessage (void) { int cmd; const char *s; int i, j; unsigned int sc1, sc2; byte test; char temp[100]; vec3_t pos; LastServerMessageSize += net_message.cursize; // received_framecount = host_framecount; cl.last_servermessage = realtime; CL_ClearProjectiles (); CL_ClearMissiles (); v_targDist = 0; // This clears out the target field on each netupdate; // it won't be drawn unless another update comes... // if recording demos, copy the message out if (cl_shownet.integer == 1) Con_Printf ("%i ",net_message.cursize); else if (cl_shownet.integer == 2) Con_Printf ("------------------\n"); CL_ParseClientdata (); // parse the message while (1) { if (msg_badread) { Host_EndGame ("%s: Bad server message", __thisfunc__); break; } cmd = MSG_ReadByte (); if (cmd == -1) { msg_readcount++; // so the EOM showner has the right value SHOWNET("END OF MESSAGE"); break; } if (cmd < (int)NUM_SVC_STRINGS) { SHOWNET(svc_strings[cmd]); } // other commands switch (cmd) { default: // CL_DumpPacket (); Host_EndGame ("%s: Illegible server message %d", __thisfunc__, cmd); break; case svc_nop: // Con_Printf ("svc_nop\n"); break; case svc_disconnect: Host_EndGame ("Server disconnected\n"); break; case svc_print: i = MSG_ReadByte (); if (i == PRINT_CHAT) { S_LocalSound ("misc/talk.wav"); con_ormask = 256; } else if (i >= PRINT_SOUND) { if (talksounds.integer) { q_snprintf (temp, sizeof(temp), "taunt/taunt%.3d.wav", i - PRINT_SOUND + 1); S_LocalSound (temp); con_ormask = 256; } else { MSG_ReadString(); break; } } Con_Printf ("%s", MSG_ReadString ()); con_ormask = 0; break; case svc_centerprint: SCR_CenterPrint (MSG_ReadString ()); break; case svc_stufftext: s = MSG_ReadString (); Con_DPrintf ("stufftext: %s\n", s); Cbuf_AddText (s); break; case svc_damage: V_ParseDamage (); break; case svc_serverdata: Cbuf_Execute (); // make sure any stuffed commands are done CL_ParseServerData (); vid.recalc_refdef = true; // leave full screen intermission break; case svc_setangle: for (i = 0; i < 3; i++) cl.viewangles[i] = MSG_ReadAngle (); // cl.viewangles[PITCH] = cl.viewangles[ROLL] = 0; break; case svc_lightstyle: i = MSG_ReadByte (); if (i >= MAX_LIGHTSTYLES) Sys_Error ("svc_lightstyle > MAX_LIGHTSTYLES"); q_strlcpy (cl_lightstyle[i].map, MSG_ReadString(), MAX_STYLESTRING); cl_lightstyle[i].length = strlen(cl_lightstyle[i].map); break; case svc_sound: CL_ParseStartSoundPacket(); break; case svc_sound_update_pos: { // FIXME: put a field on the entity that lists the channels // it should update when it moves- if a certain flag // is on the ent, this update_channels field could // be set automatically by each sound and stopSound // called for this ent? int channel, ent_num; channel = MSG_ReadShort (); ent_num = channel >> 3; channel &= 7; if (ent_num > MAX_EDICTS) Host_Error ("svc_sound_update_pos: ent = %i", ent_num); for (i = 0; i < 3; i++) pos[i] = MSG_ReadCoord (); S_UpdateSoundPos (ent_num, channel, pos); } break; case svc_stopsound: i = MSG_ReadShort(); S_StopSound(i>>3, i&7); break; case svc_updatefrags: Sbar_Changed (); i = MSG_ReadByte (); if (i >= MAX_CLIENTS) Host_EndGame ("%s: svc_updatefrags > MAX_CLIENTS", __thisfunc__); cl.players[i].frags = MSG_ReadShort (); break; case svc_updateping: i = MSG_ReadByte (); if (i >= MAX_CLIENTS) Host_EndGame ("%s: svc_updateping > MAX_CLIENTS", __thisfunc__); cl.players[i].ping = MSG_ReadShort (); break; case svc_updateentertime: // time is sent over as seconds ago i = MSG_ReadByte (); if (i >= MAX_CLIENTS) Host_EndGame ("%s: svc_updateentertime > MAX_CLIENTS", __thisfunc__); cl.players[i].entertime = realtime - MSG_ReadFloat (); break; case svc_updatepclass: // playerclass has changed for this dude i = MSG_ReadByte (); if (i >= MAX_CLIENTS) Host_EndGame ("%s: svc_updatepclass > MAX_CLIENTS", __thisfunc__); cl.players[i].playerclass = MSG_ReadByte (); cl.players[i].level = cl.players[i].playerclass&31; cl.players[i].playerclass = cl.players[i].playerclass>>5; break; case svc_updatedminfo: // This dude killed someone, update his frags and level i = MSG_ReadByte (); if (i >= MAX_CLIENTS) Host_EndGame ("%s: svc_updatedminfo > MAX_CLIENTS", __thisfunc__); cl.players[i].frags = MSG_ReadShort (); cl.players[i].playerclass = MSG_ReadByte (); cl.players[i].level = cl.players[i].playerclass&31; cl.players[i].playerclass = cl.players[i].playerclass>>5; break; case svc_updatesiegelosses: // This dude killed someone, update his frags and level defLosses = MSG_ReadByte (); attLosses = MSG_ReadByte (); break; case svc_updatesiegeteam: // This dude killed someone, update his frags and level i = MSG_ReadByte (); if (i >= MAX_CLIENTS) Host_EndGame ("%s: svc_updatesiegeteam > MAX_CLIENTS", __thisfunc__); cl.players[i].siege_team = MSG_ReadByte (); break; case svc_updatesiegeinfo: // We are on a siege server, set cl_siege cl_siege = true; cl_timelimit = MSG_ReadByte () * 60; cl_fraglimit = MSG_ReadByte (); break; case svc_haskey: cl_keyholder = MSG_ReadShort() - 1; break; case svc_isdoc: cl_doc = MSG_ReadShort() - 1; break; case svc_nonehaskey: cl_keyholder = -1; break; case svc_nodoc: cl_doc = -1; break; case svc_time: cl_server_time_offset = ((int)MSG_ReadFloat()) - cl.time; break; case svc_spawnbaseline: i = MSG_ReadShort (); CL_ParseBaseline (&cl_baselines[i]); break; case svc_spawnstatic: CL_ParseStatic (); break; case svc_temp_entity: CL_ParseTEnt (); break; case svc_killedmonster: cl.stats[STAT_MONSTERS]++; break; case svc_foundsecret: cl.stats[STAT_SECRETS]++; break; case svc_updatestat: i = MSG_ReadByte (); j = MSG_ReadByte (); CL_SetStat (i, j); break; case svc_updatestatlong: i = MSG_ReadByte (); j = MSG_ReadLong (); CL_SetStat (i, j); break; case svc_spawnstaticsound: CL_ParseStaticSound (); break; case svc_cdtrack: cl.cdtrack = MSG_ReadByte (); if (q_strcasecmp(bgmtype.string,"cd") != 0) CDAudio_Stop (); else CDAudio_Play ((byte)cl.cdtrack, true); break; case svc_midi_name: q_strlcpy (cl.midi_name, MSG_ReadString(), sizeof(cl.midi_name)); if (q_strcasecmp(bgmtype.string,"midi") != 0) BGM_Stop(); else BGM_PlayMIDIorMusic(cl.midi_name); break; case svc_intermission: /* if (cl_siege) {//MG */ CL_SetupIntermission (MSG_ReadByte()); vid.recalc_refdef = true; // go to full screen break; /* } else { // Old Quake way- won't work cl.intermission = 1; cl.completed_time = realtime; vid.recalc_refdef = true; // go to full screen for (i = 0; i < 3; i++) cl.simorg[i] = MSG_ReadCoord (); for (i = 0; i < 3; i++) cl.simangles[i] = MSG_ReadAngle (); VectorClear (cl.simvel); break; } */ case svc_finale: cl.intermission = 2; cl.completed_time = realtime; vid.recalc_refdef = true; // go to full screen SCR_CenterPrint (MSG_ReadString ()); break; case svc_sellscreen: Cmd_ExecuteString ("help", src_command); break; case svc_smallkick: cl.punchangle = -2; break; case svc_bigkick: cl.punchangle = -4; break; case svc_muzzleflash: CL_MuzzleFlash (); break; case svc_updateuserinfo: CL_UpdateUserinfo (); break; case svc_download: CL_ParseDownload (); break; case svc_playerinfo: CL_ParsePlayerinfo (); break; case svc_playerskipped: CL_SavePlayer (); break; case svc_nails: CL_ParseProjectiles (); break; case svc_packmissile: CL_ParsePackMissiles (); break; case svc_chokecount: // some preceding packets were choked i = MSG_ReadByte (); for (j = 0; j < i; j++) cl.frames[ (cls.netchan.incoming_acknowledged-1-j)&UPDATE_MASK ].receivedtime = -2; break; case svc_modellist: if (cl.protocol >= PROTOCOL_VERSION_EXT) /* from QW */ { CL_ParseModellistChunks (); break; } CL_ParseModellist (); break; case svc_soundlist: if (cl.protocol >= PROTOCOL_VERSION_EXT) /* from QW */ { CL_ParseSoundlistChunks (); break; } CL_ParseSoundlist (); break; case svc_packetentities: CL_ParsePacketEntities (false); break; case svc_deltapacketentities: CL_ParsePacketEntities (true); break; case svc_maxspeed : movevars.maxspeed = MSG_ReadFloat(); break; case svc_entgravity : movevars.entgravity = MSG_ReadFloat(); break; case svc_plaque: CL_Plaque(); break; case svc_indexed_print: CL_IndexedPrint(); break; case svc_name_print: CL_NamePrint(); break; case svc_particle_explosion: CL_ParticleExplosion(); break; case svc_set_view_tint: i = MSG_ReadByte(); // cl.viewent.colorshade = i; break; case svc_start_effect: CL_ParseEffect(); break; case svc_end_effect: CL_EndEffect(); break; case svc_set_view_flags: cl.viewent.drawflags |= MSG_ReadByte(); break; case svc_clear_view_flags: cl.viewent.drawflags &= ~MSG_ReadByte(); break; case svc_update_inv: sc1 = sc2 = 0; test = MSG_ReadByte(); if (test & 1) sc1 |= ((int)MSG_ReadByte()); if (test & 2) sc1 |= ((int)MSG_ReadByte())<<8; if (test & 4) sc1 |= ((int)MSG_ReadByte())<<16; if (test & 8) sc1 |= ((int)MSG_ReadByte())<<24; if (test & 16) sc2 |= ((int)MSG_ReadByte()); if (test & 32) sc2 |= ((int)MSG_ReadByte())<<8; if (test & 64) sc2 |= ((int)MSG_ReadByte())<<16; if (test & 128) sc2 |= ((int)MSG_ReadByte())<<24; if (sc1 & SC1_HEALTH) cl.v.health = MSG_ReadShort(); if (sc1 & SC1_LEVEL) cl.v.level = MSG_ReadByte(); if (sc1 & SC1_INTELLIGENCE) cl.v.intelligence = MSG_ReadByte(); if (sc1 & SC1_WISDOM) cl.v.wisdom = MSG_ReadByte(); if (sc1 & SC1_STRENGTH) cl.v.strength = MSG_ReadByte(); if (sc1 & SC1_DEXTERITY) cl.v.dexterity = MSG_ReadByte(); // if (sc1 & SC1_WEAPON) // cl.v.weapon = MSG_ReadByte(); if (sc1 & SC1_TELEPORT_TIME) { // Con_Printf("Teleport_time>time, got bit\n"); cl.v.teleport_time = realtime + 2; // can't airmove for 2s } if (sc1 & SC1_BLUEMANA) cl.v.bluemana = MSG_ReadByte(); if (sc1 & SC1_GREENMANA) cl.v.greenmana = MSG_ReadByte(); if (sc1 & SC1_EXPERIENCE) cl.v.experience = MSG_ReadLong(); if (sc1 & SC1_CNT_TORCH) cl.v.cnt_torch = MSG_ReadByte(); if (sc1 & SC1_CNT_H_BOOST) cl.v.cnt_h_boost = MSG_ReadByte(); if (sc1 & SC1_CNT_SH_BOOST) cl.v.cnt_sh_boost = MSG_ReadByte(); if (sc1 & SC1_CNT_MANA_BOOST) cl.v.cnt_mana_boost = MSG_ReadByte(); if (sc1 & SC1_CNT_TELEPORT) cl.v.cnt_teleport = MSG_ReadByte(); if (sc1 & SC1_CNT_TOME) cl.v.cnt_tome = MSG_ReadByte(); if (sc1 & SC1_CNT_SUMMON) cl.v.cnt_summon = MSG_ReadByte(); if (sc1 & SC1_CNT_INVISIBILITY) cl.v.cnt_invisibility = MSG_ReadByte(); if (sc1 & SC1_CNT_GLYPH) cl.v.cnt_glyph = MSG_ReadByte(); if (sc1 & SC1_CNT_HASTE) cl.v.cnt_haste = MSG_ReadByte(); if (sc1 & SC1_CNT_BLAST) cl.v.cnt_blast = MSG_ReadByte(); if (sc1 & SC1_CNT_POLYMORPH) cl.v.cnt_polymorph = MSG_ReadByte(); if (sc1 & SC1_CNT_FLIGHT) cl.v.cnt_flight = MSG_ReadByte(); if (sc1 & SC1_CNT_CUBEOFFORCE) cl.v.cnt_cubeofforce = MSG_ReadByte(); if (sc1 & SC1_CNT_INVINCIBILITY) cl.v.cnt_invincibility = MSG_ReadByte(); if (sc1 & SC1_ARTIFACT_ACTIVE) cl.v.artifact_active = MSG_ReadByte(); if (sc1 & SC1_ARTIFACT_LOW) cl.v.artifact_low = MSG_ReadByte(); if (sc1 & SC1_MOVETYPE) cl.v.movetype = MSG_ReadByte(); if (sc1 & SC1_CAMERAMODE) cl.v.cameramode = MSG_ReadByte(); if (sc1 & SC1_HASTED) cl.v.hasted = MSG_ReadFloat(); if (sc1 & SC1_INVENTORY) cl.v.inventory = MSG_ReadByte(); if (sc1 & SC1_RINGS_ACTIVE) cl.v.rings_active = MSG_ReadByte(); if (sc2 & SC2_RINGS_LOW) cl.v.rings_low = MSG_ReadByte(); if (sc2 & SC2_AMULET) cl.v.armor_amulet = MSG_ReadByte(); if (sc2 & SC2_BRACER) cl.v.armor_bracer = MSG_ReadByte(); if (sc2 & SC2_BREASTPLATE) cl.v.armor_breastplate = MSG_ReadByte(); if (sc2 & SC2_HELMET) cl.v.armor_helmet = MSG_ReadByte(); if (sc2 & SC2_FLIGHT_T) cl.v.ring_flight = MSG_ReadByte(); if (sc2 & SC2_WATER_T) cl.v.ring_water = MSG_ReadByte(); if (sc2 & SC2_TURNING_T) cl.v.ring_turning = MSG_ReadByte(); if (sc2 & SC2_REGEN_T) cl.v.ring_regeneration = MSG_ReadByte(); // if (sc2 & SC2_HASTE_T) // cl.v.haste_time = MSG_ReadFloat(); // if (sc2 & SC2_TOME_T) // cl.v.tome_time = MSG_ReadFloat(); if (sc2 & SC2_PUZZLE1) q_snprintf(cl.puzzle_pieces[0], sizeof(cl.puzzle_pieces[0]), "%.9s", MSG_ReadString()); if (sc2 & SC2_PUZZLE2) q_snprintf(cl.puzzle_pieces[1], sizeof(cl.puzzle_pieces[0]), "%.9s", MSG_ReadString()); if (sc2 & SC2_PUZZLE3) q_snprintf(cl.puzzle_pieces[2], sizeof(cl.puzzle_pieces[0]), "%.9s", MSG_ReadString()); if (sc2 & SC2_PUZZLE4) q_snprintf(cl.puzzle_pieces[3], sizeof(cl.puzzle_pieces[0]), "%.9s", MSG_ReadString()); if (sc2 & SC2_PUZZLE5) q_snprintf(cl.puzzle_pieces[4], sizeof(cl.puzzle_pieces[0]), "%.9s", MSG_ReadString()); if (sc2 & SC2_PUZZLE6) q_snprintf(cl.puzzle_pieces[5], sizeof(cl.puzzle_pieces[0]), "%.9s", MSG_ReadString()); if (sc2 & SC2_PUZZLE7) q_snprintf(cl.puzzle_pieces[6], sizeof(cl.puzzle_pieces[0]), "%.9s", MSG_ReadString()); if (sc2 & SC2_PUZZLE8) q_snprintf(cl.puzzle_pieces[7], sizeof(cl.puzzle_pieces[0]), "%.9s", MSG_ReadString()); if (sc2 & SC2_MAXHEALTH) cl.v.max_health = MSG_ReadShort(); if (sc2 & SC2_MAXMANA) cl.v.max_mana = MSG_ReadByte(); if (sc2 & SC2_FLAGS) cl.v.flags = MSG_ReadFloat(); if ((sc1 & SC1_STAT_BAR) || (sc2 & SC2_STAT_BAR)) Sbar_Changed(); if ((sc1 & SC1_INV) || (sc2 & SC2_INV)) SB_InvChanged(); break; case svc_particle: R_ParseParticleEffect (); break; case svc_particle2: R_ParseParticleEffect2 (); break; case svc_particle3: R_ParseParticleEffect3 (); break; case svc_particle4: R_ParseParticleEffect4 (); break; case svc_turn_effect: CL_TurnEffect (); break; case svc_update_effect: CL_ReviseEffect(); break; case svc_multieffect: CL_ParseMultiEffect(); break; case svc_raineffect: R_ParseRainEffect(); break; case svc_targetupdate: V_ParseTarget(); break; case svc_update_piv: cl.PIV = MSG_ReadLong(); break; case svc_player_sound: test = MSG_ReadByte(); pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); i = MSG_ReadShort (); S_StartSound(test, 1, cl.sound_precache[i], pos, 1.0, 1.0); break; } } CL_SetSolidEntities (); } engine/hexenworld/client/cl_pred.c000066400000000000000000000147111444734033100175430ustar00rootroot00000000000000/* * cl_pred.c --client side player movement prediction * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #ifdef PLATFORM_WINDOWS #include "winquake.h" #endif static cvar_t cl_nopred = {"cl_nopred", "0", CVAR_NONE}; static cvar_t cl_pushlatency = {"pushlatency", "-50", CVAR_ARCHIVE}; extern frame_t *view_frame; static qboolean player_crouching; #if 0 /* not used */ /* ================= CL_NudgePosition If pmove.origin is in a solid position, try nudging slightly on all axis to allow for the cut precision of the net coordinates ================= */ void CL_NudgePosition (void) { vec3_t base; int x, y; if (PM_HullPointContents(&cl.model_precache[1]->hulls[1], 0, pmove.origin) == CONTENTS_EMPTY) return; VectorCopy (pmove.origin, base); for (x = -1; x <= 1; x++) { for (y = -1; y <= 1; y++) { pmove.origin[0] = base[0] + x * 1.0/8; pmove.origin[1] = base[1] + y * 1.0/8; if (PM_HullPointContents(&cl.model_precache[1]->hulls[1], 0, pmove.origin) == CONTENTS_EMPTY) return; } } Con_DPrintf ("CL_NudgePosition: stuck\n"); } #endif /* CL_NudgePosition */ /* ============== CL_PredictUsercmd ============== */ void CL_PredictUsercmd (player_state_t *from, player_state_t *to, usercmd_t *u, qboolean spectate) { // split up very long moves if (u->msec > 50) { player_state_t temp; usercmd_t split; split = *u; split.msec /= 2; CL_PredictUsercmd (from, &temp, &split, spectate); CL_PredictUsercmd (&temp, to, &split, spectate); return; } // Con_Printf("O %hd %hd %hd\n",u->forwardmove, u->sidemove, u->upmove); VectorCopy (from->origin, pmove.origin); // VectorCopy (from->viewangles, pmove.angles); VectorCopy (u->angles, pmove.angles); VectorCopy (from->velocity, pmove.velocity); pmove.oldbuttons = from->oldbuttons; pmove.waterjumptime = from->waterjumptime; pmove.dead = cl.v.health <= 0; pmove.spectator = spectate; pmove.hasted = cl.v.hasted; pmove.movetype = cl.v.movetype; pmove.teleport_time = cl.v.teleport_time; pmove.cmd = *u; pmove.crouched = player_crouching; PlayerMove (); // for (i = 0; i < 3; i++) // pmove.origin[i] = ((int)(pmove.origin[i]*8))*0.125; to->waterjumptime = pmove.waterjumptime; to->oldbuttons = pmove.cmd.buttons; VectorCopy (pmove.origin, to->origin); VectorCopy (pmove.angles, to->viewangles); VectorCopy (pmove.velocity, to->velocity); to->onground = onground; to->weaponframe = from->weaponframe; } /* ============== CL_PredictMove ============== */ void CL_PredictMove (void) { int i; float f; frame_t *from, *to = NULL; int oldphysent; if (cl_pushlatency.value > 0) Cvar_SetQuick (&cl_pushlatency, "0"); cl.time = realtime - cls.latency - cl_pushlatency.value*0.001; if (cl.time > realtime) cl.time = realtime; if (cl.intermission) return; if (!cl.validsequence) return; if (cls.netchan.outgoing_sequence - cls.netchan.incoming_sequence >= UPDATE_BACKUP-1) return; VectorCopy (cl.viewangles, cl.simangles); // this is the last frame received from the server from = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; player_crouching = ((from->playerstate[cl.playernum].flags) & PF_CROUCH)>>10; // we can now render a frame if (cls.state == ca_onserver) { // first update is the final signon stage char text[1024]; cls.state = ca_active; q_snprintf (text, sizeof(text), "HexenWorld: %s", cls.servername); #ifdef PLATFORM_WINDOWS SetWindowText (mainwindow, text); #endif } if (cl_nopred.integer) { VectorCopy (from->playerstate[cl.playernum].velocity, cl.simvel); VectorCopy (from->playerstate[cl.playernum].origin, cl.simorg); return; } // predict forward until cl.time <= to->senttime oldphysent = pmove.numphysent; CL_SetSolidPlayers (cl.playernum); // to = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; for (i = 1; i < UPDATE_BACKUP-1 && cls.netchan.incoming_sequence + i < cls.netchan.outgoing_sequence; i++) { to = &cl.frames[(cls.netchan.incoming_sequence+i) & UPDATE_MASK]; CL_PredictUsercmd (&from->playerstate[cl.playernum], &to->playerstate[cl.playernum], &to->cmd, cl.spectator); if (to->senttime >= cl.time) break; from = to; } pmove.numphysent = oldphysent; if (i == UPDATE_BACKUP-1 || !to) return; // net hasn't deliver packets in a long time... // now interpolate some fraction of the final frame if (to->senttime == from->senttime) { f = 0; } else { f = (cl.time - from->senttime) / (to->senttime - from->senttime); if (f < 0) f = 0; else if (f > 1) f = 1; } for (i = 0; i < 3; i++) { if ( fabs(from->playerstate[cl.playernum].origin[i] - to->playerstate[cl.playernum].origin[i]) > 128) { // teleported, so don't lerp VectorCopy (to->playerstate[cl.playernum].velocity, cl.simvel); VectorCopy (to->playerstate[cl.playernum].origin, cl.simorg); return; } } for (i = 0; i < 3; i++) { cl.simorg[i] = from->playerstate[cl.playernum].origin[i] + f * (to->playerstate[cl.playernum].origin[i] - from->playerstate[cl.playernum].origin[i]); cl.simvel[i] = from->playerstate[cl.playernum].velocity[i] + f * (to->playerstate[cl.playernum].velocity[i] - from->playerstate[cl.playernum].velocity[i]); } /* Con_Printf("(%5.2f %5.2f %5.2f) (%5.2f %5.2f %5.2f)\n", cl.simorg[0] - to->playerstate[cl.playernum].origin[0], cl.simorg[1] - to->playerstate[cl.playernum].origin[1], cl.simorg[2] - to->playerstate[cl.playernum].origin[2], cl.simvel[0] - to->playerstate[cl.playernum].velocity[0], cl.simvel[1] - to->playerstate[cl.playernum].velocity[1], cl.simvel[2] - to->playerstate[cl.playernum].velocity[2]); */ } /* ============== CL_InitPrediction ============== */ void CL_InitPrediction (void) { Cvar_RegisterVariable (&cl_pushlatency); Cvar_RegisterVariable (&cl_nopred); } engine/hexenworld/client/cl_tent.c000066400000000000000000004117701444734033100175710ustar00rootroot00000000000000/* * cl_tent.c -- client side temporary entities * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // HEADER FILES ------------------------------------------------------------ #include "quakedef.h" // MACROS ------------------------------------------------------------------ #define MAX_BEAMS 8 #define MAX_STREAMS 32 #define STREAM_ATTACHED 16 #define STREAM_TRANSLUCENT 32 #define MAX_EXPLOSIONS 128 #define FRANDOM() (rand()*(1.0/RAND_MAX)) static unsigned int randomseed; void setseed(unsigned int seed) { randomseed = seed; } /*unsigned int seedrand(int max)*/ float seedrand(void) { randomseed = (randomseed * 877 + 573) % 9968; return (float)randomseed / 9968; } // TYPES ------------------------------------------------------------------- typedef struct { int entity; struct qmodel_s *model; float endtime; vec3_t start, end; } beam_t; typedef struct { int type; int entity; int tag; int flags; int skin; struct qmodel_s *models[4]; vec3_t source; vec3_t dest; vec3_t offset; float endTime; float lastTrailTime; } stream_t; typedef enum { EXFLAG_ROTATE = 1, EXFLAG_COLLIDE = 2, EXFLAG_STILL_FRAME = 4 } exflags_t; typedef struct explosion_t explosion_t; struct explosion_t { vec3_t origin; vec3_t oldorg;// holds position from last frame float startTime; float endTime; vec3_t velocity; vec3_t accel; vec3_t angles; vec3_t avel; // angular velocity int flags; int abslight; int exflags; // exflags_t int skin; int scale; qmodel_t *model; void (*frameFunc)(explosion_t *ex); void (*removeFunc)(explosion_t *ex); float data; //for easy transition of script code that relied on counters of some sort }; // PUBLIC FUNCTION DEFINITIONS --------------------------------------------- int TempSoundChannel (void) { static int last = -1; last--; if (last < -20) last = -1; return last; } // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void ParseStream(int type); static stream_t *NewStream(int ent, int tag); static void MultiGrenadeThink (explosion_t *ex); static void MultiGrenadePieceThink (explosion_t *ex); static void MultiGrenadePiece2Think (explosion_t *ex); static void ChunkThink(explosion_t *ex); static void BubbleThink(explosion_t *ex); static void MissileFlashThink(explosion_t *ex); static void TeleportFlashThink(explosion_t *ex); static void CheckSpaceThink(explosion_t *ex); static void SwordFrameFunc(explosion_t *ex); static void zapFrameFunc(explosion_t *ex); static void fireBallUpdate(explosion_t *ex); static void sunBallUpdate(explosion_t *ex); static void sunPowerUpdate(explosion_t *ex); #if 0 static void purify1Update(explosion_t *ex); #endif static void telEffectUpdate (explosion_t *ex); static void CL_UpdateTargetBall(void); static void updateBloodRain(explosion_t *ex); static void updatePurify2(explosion_t *ex); static void updateSwordShot(explosion_t *ex); static void updateIceShot(explosion_t *ex); static void updateMeteor(explosion_t *ex); static void SmokeRingFrameFunc(explosion_t *ex); static void updateAcidBlob(explosion_t *ex); static void updateAcidBall(explosion_t *ex); static void MeteorCrushSpawnThink(explosion_t *ex); // PRIVATE DATA DEFINITIONS ------------------------------------------------ static beam_t cl_beams[MAX_BEAMS]; static explosion_t cl_explosions[MAX_EXPLOSIONS]; static stream_t cl_Streams[MAX_STREAMS]; static float playIceSound = .6; static int MultiGrenadeCurrentChannel; //static sfx_t *cl_sfx_wizhit; //static sfx_t *cl_sfx_knighthit; static sfx_t *cl_sfx_tink1; static sfx_t *cl_sfx_ric1; static sfx_t *cl_sfx_ric2; static sfx_t *cl_sfx_ric3; static sfx_t *cl_sfx_r_exp3; static sfx_t *cl_sfx_explode; static sfx_t *cl_sfx_bonehit; static sfx_t *cl_sfx_bonewal; static sfx_t *cl_sfx_bonephit; static sfx_t *cl_sfx_ravendie; static sfx_t *cl_sfx_buzzbee; static sfx_t *cl_sfx_iceflesh; static sfx_t *cl_sfx_icewall; static sfx_t *cl_sfx_iceshatter; static sfx_t *cl_sfx_icestorm; static sfx_t *cl_sfx_sunstaff; static sfx_t *cl_sfx_sunhit; static sfx_t *cl_sfx_lightning1; static sfx_t *cl_sfx_lightning2; static sfx_t *cl_sfx_hammersound; static sfx_t *cl_sfx_tornado; static sfx_t *cl_sfx_swordExplode; static sfx_t *cl_sfx_axeBounce; static sfx_t *cl_sfx_axeExplode; static sfx_t *cl_sfx_fireBall; static sfx_t *cl_sfx_purify2; static sfx_t *cl_sfx_telefrag; static sfx_t *cl_sfx_big_gib; static sfx_t *cl_sfx_gib1; static sfx_t *cl_sfx_gib2; static sfx_t *cl_sfx_purify1_fire; static sfx_t *cl_sfx_purify1_hit; static sfx_t *cl_sfx_acidhit; static sfx_t *cl_sfx_dropfizzle; static sfx_t *cl_sfx_flameend; static sfx_t *cl_sfx_teleport[5]; static sfx_t *cl_sfx_ravengo; // CODE -------------------------------------------------------------------- /* ================= CL_InitTEnts ================= */ void CL_InitTEnts (void) { // cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav"); // cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav"); cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav"); cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav"); cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav"); cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav"); cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav"); cl_sfx_explode = S_PrecacheSound ("weapons/explode.wav"); cl_sfx_bonephit = S_PrecacheSound ("necro/bonephit.wav"); cl_sfx_bonehit = S_PrecacheSound ("necro/bonenhit.wav"); cl_sfx_bonewal = S_PrecacheSound ("necro/bonenwal.wav"); cl_sfx_ravendie = S_PrecacheSound ("raven/death.wav"); cl_sfx_buzzbee = S_PrecacheSound ("assassin/scrbfly.wav"); cl_sfx_iceflesh = S_PrecacheSound ("crusader/icehit.wav"); cl_sfx_icewall = S_PrecacheSound ("crusader/icewall.wav"); cl_sfx_iceshatter = S_PrecacheSound ("misc/icestatx.wav"); cl_sfx_icestorm = S_PrecacheSound ("crusader/blizzard.wav"); cl_sfx_sunstaff = S_PrecacheSound ("crusader/sunhum.wav"); cl_sfx_sunhit = S_PrecacheSound ("crusader/sunhit.wav"); cl_sfx_lightning1 = S_PrecacheSound ("crusader/lghtn1.wav"); cl_sfx_lightning2 = S_PrecacheSound ("crusader/lghtn2.wav"); cl_sfx_hammersound = S_PrecacheSound ("paladin/axblade.wav"); cl_sfx_tornado = S_PrecacheSound ("crusader/tornado.wav"); cl_sfx_swordExplode = S_PrecacheSound ("weapons/explode.wav"); cl_sfx_axeBounce = S_PrecacheSound ("paladin/axric1.wav"); cl_sfx_axeExplode = S_PrecacheSound ("weapons/explode.wav"); cl_sfx_fireBall = S_PrecacheSound ("weapons/fbfire.wav"); cl_sfx_purify2 = S_PrecacheSound ("weapons/exphuge.wav"); cl_sfx_telefrag = S_PrecacheSound ("player/telefrag.wav"); cl_sfx_big_gib = S_PrecacheSound ("player/megagib.wav"); cl_sfx_gib1 = S_PrecacheSound ("player/gib1.wav"); cl_sfx_gib2 = S_PrecacheSound ("player/gib2.wav"); cl_sfx_purify1_fire = S_PrecacheSound ("paladin/purfire.wav"); cl_sfx_purify1_hit = S_PrecacheSound ("weapons/expsmall.wav"); cl_sfx_acidhit = S_PrecacheSound ("succubus/acidhit.wav"); cl_sfx_dropfizzle = S_PrecacheSound ("succubus/dropfizz.wav"); cl_sfx_flameend = S_PrecacheSound ("succubus/flamend.wav"); cl_sfx_teleport[0] = S_PrecacheSound ("misc/teleprt1.wav"); cl_sfx_teleport[1] = S_PrecacheSound ("misc/teleprt2.wav"); cl_sfx_teleport[2] = S_PrecacheSound ("misc/teleprt3.wav"); cl_sfx_teleport[3] = S_PrecacheSound ("misc/teleprt4.wav"); cl_sfx_teleport[4] = S_PrecacheSound ("misc/teleprt5.wav"); cl_sfx_ravengo = S_PrecacheSound ("raven/ravengo.wav"); } static void vectoangles(vec3_t vec, vec3_t ang) { float forward; float yaw, pitch; if (vec[1] == 0 && vec[0] == 0) { yaw = 0; if (vec[2] > 0) pitch = 90; else pitch = 270; } else { yaw = (int) (atan2(vec[1], vec[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; forward = Q_sqrt (vec[0]*vec[0] + vec[1]*vec[1]); pitch = (int) (atan2(vec[2], forward) * 180 / M_PI); if (pitch < 0) pitch += 360; } ang[0] = pitch; ang[1] = yaw; ang[2] = 0; } /* ================= CL_ClearTEnts ================= */ void CL_ClearTEnts (void) { memset (cl_beams, 0, sizeof(cl_beams)); memset (cl_explosions, 0, sizeof(cl_explosions)); memset (cl_Streams, 0, sizeof(cl_Streams)); } /* ================= CL_AllocExplosion **** CAREFUL!!! This may overwrite an explosion!!!!! ================= */ static explosion_t *CL_AllocExplosion (void) { int i, idx, freeSlot; float time; idx = 0; freeSlot = false; for (i = 0; i < MAX_EXPLOSIONS; i++) { if (!cl_explosions[i].model) { idx = i; freeSlot = true; break; } } // find the oldest explosion time = cl.time; if (!freeSlot) { for (i = 0; i < MAX_EXPLOSIONS; i++) { if (cl_explosions[i].startTime < time) { time = cl_explosions[i].startTime; idx = i; } } } //zero out velocity and acceleration, funcs memset (&cl_explosions[idx], 0, sizeof(explosion_t)); return &cl_explosions[idx]; } /* ================= CL_ParseBeam ================= */ static void CL_ParseBeam (qmodel_t *m) { int i, ent; vec3_t start, end; beam_t *b; ent = MSG_ReadShort (); start[0] = MSG_ReadCoord (); start[1] = MSG_ReadCoord (); start[2] = MSG_ReadCoord (); end[0] = MSG_ReadCoord (); end[1] = MSG_ReadCoord (); end[2] = MSG_ReadCoord (); if (!m) return; // override any beam with the same entity for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++) { if (b->entity == ent) { b->entity = ent; b->model = m; b->endtime = cl.time + 0.2; VectorCopy (start, b->start); VectorCopy (end, b->end); return; } } // find a free beam for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++) { if (!b->model || b->endtime < cl.time) { b->entity = ent; b->model = m; b->endtime = cl.time + 0.2; VectorCopy (start, b->start); VectorCopy (end, b->end); return; } } Con_Printf ("beam list overflow!\n"); } entity_state_t *FindState(int EntNum) { packet_entities_t *pack; static entity_state_t pretend_player; int pnum; if (EntNum >= 1 && EntNum <= MAX_CLIENTS) { EntNum--; if (EntNum == cl.playernum) { VectorCopy(cl.simorg, pretend_player.origin); } else { VectorCopy(cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].playerstate[EntNum].origin, pretend_player.origin); } return &pretend_player; } pack = &cl.frames[cls.netchan.incoming_sequence&UPDATE_MASK].packet_entities; for (pnum = 0; pnum < pack->num_entities; pnum++) { if (pack->entities[pnum].number == EntNum) return &pack->entities[pnum]; } return NULL; } //========================================================================== // // CreateStream--for use mainly external to cl_tent (i.e. cl_effect) // // generally pass in 0 for skin // //========================================================================== void CreateStream(int type, int ent, int flags, int tag, float duration, int skin, vec3_t source, vec3_t dest) { stream_t *stream; qmodel_t *models[4]; entity_state_t *state; models[1] = models[2] = models[3] = NULL; switch (type) { case TE_STREAM_CHAIN: models[0] = Mod_ForName("models/stchain.mdl", true); break; case TE_STREAM_SUNSTAFF1: models[0] = Mod_ForName("models/stsunsf1.mdl", true); models[1] = Mod_ForName("models/stsunsf2.mdl", true); models[2] = Mod_ForName("models/stsunsf3.mdl", true); models[3] = Mod_ForName("models/stsunsf4.mdl", true); break; case TE_STREAM_SUNSTAFF2: models[0] = Mod_ForName("models/stsunsf5.mdl", true); models[2] = Mod_ForName("models/stsunsf3.mdl", true); models[3] = Mod_ForName("models/stsunsf4.mdl", true); break; case TE_STREAM_LIGHTNING: models[0] = Mod_ForName("models/stlghtng.mdl", true); break; case TE_STREAM_LIGHTNING_SMALL: models[0] = Mod_ForName("models/stltng2.mdl", true); break; case TE_STREAM_FAMINE: models[0] = Mod_ForName("models/fambeam.mdl", true); break; case TE_STREAM_COLORBEAM: models[0] = Mod_ForName("models/stclrbm.mdl", true); break; case TE_STREAM_ICECHUNKS: models[0] = Mod_ForName("models/stice.mdl", true); break; case TE_STREAM_GAZE: models[0] = Mod_ForName("models/stmedgaz.mdl", true); break; default: models[0] = NULL; break; } if (models[0] == NULL) Sys_Error("%s: bad type", __thisfunc__); if ((stream = NewStream(ent, tag)) == NULL) { Con_Printf("stream list overflow\n"); return; } stream->type = type; stream->tag = tag; stream->flags = flags; stream->entity = ent; stream->skin = skin; stream->models[0] = models[0]; stream->models[1] = models[1]; stream->models[2] = models[2]; stream->models[3] = models[3]; stream->endTime = cl.time + duration; stream->lastTrailTime = 0; VectorCopy(source, stream->source); VectorCopy(dest, stream->dest); if (flags & STREAM_ATTACHED) { VectorClear(stream->offset); state = FindState(ent); if (state) { // rjr - potential problem if this doesn't ever get set - origin might have to be set properly in script code? VectorSubtract(source, state->origin, stream->offset); } } } void CLTENT_SpawnDeathBubble(vec3_t pos) { explosion_t *ex; //generic spinny impact image ex = CL_AllocExplosion(); VectorCopy(pos,ex->origin); VectorSet(ex->velocity,0,0,17); ex->data = cl.time; ex->scale = 128; ex->frameFunc = BubbleThink; ex->startTime = cl.time; ex->endTime = cl.time + 15; ex->model = Mod_ForName ("models/s_bubble.spr", true); ex->flags = DRF_TRANSLUCENT | MLS_ABSLIGHT; ex->abslight = 175; } void CLTENT_XbowImpact(vec3_t pos, vec3_t vel, int chType, int damage, int arrowType)//arrowType is total # of arrows in effect { explosion_t *ex; float cnt; int i; //generic spinny impact image ex = CL_AllocExplosion(); ex->origin[0] = pos[0] - vel[0]; ex->origin[1] = pos[1] - vel[1]; ex->origin[2] = pos[2] - vel[2]; vectoangles(vel,ex->angles); ex->avel[2] = (rand() % 500) + 200; ex->scale = 10; ex->frameFunc = MissileFlashThink; ex->startTime = cl.time; ex->endTime = cl.time + 0.3; ex->model = Mod_ForName ("models/arrowhit.mdl", true); ex->exflags = EXFLAG_ROTATE; ex->flags = DRF_TRANSLUCENT | MLS_ABSLIGHT; ex->abslight = 175; //white smoke if invulnerable impact if (!damage) { ex = CL_AllocExplosion(); ex->origin[0] = pos[0] - vel[0]*2; ex->origin[1] = pos[1] - vel[1]*2; ex->origin[2] = pos[2] - vel[2]*2; ex->velocity[0] = 0.0; ex->velocity[1] = 0.0; ex->velocity[2] = 80.0; vectoangles(vel,ex->angles); ex->startTime = cl.time; ex->endTime = cl.time + 0.35; ex->model = Mod_ForName ("models/whtsmk1.spr", true); ex->flags = DRF_TRANSLUCENT; if (arrowType == 3) // little arrows go away { if (rand() & 3) // chunky go { cnt = (rand() % 2) + 1; for (i = 0; i < cnt; i++) { float final; ex = CL_AllocExplosion(); ex->frameFunc = ChunkThink; VectorSubtract(pos,vel,ex->origin); // temp modify them... ex->velocity[0] = (rand() % 140) - 70; ex->velocity[1] = (rand() % 140) - 70; ex->velocity[2] = (rand() % 140) - 70; // are these in degrees or radians? ex->angles[0] = rand() % 360; ex->angles[1] = rand() % 360; ex->angles[2] = rand() % 360; ex->exflags = EXFLAG_ROTATE; ex->avel[0] = (rand() % 850) - 425; ex->avel[1] = (rand() % 850) - 425; ex->avel[2] = (rand() % 850) - 425; ex->scale = 30 + 100 * (cnt / 40.0) + (rand() % 40); ex->data = THINGTYPE_WOOD; final = (rand() % 100) * .01; if (final < 0.25) ex->model = Mod_ForName ("models/splnter1.mdl", true); else if (final < 0.50) ex->model = Mod_ForName ("models/splnter2.mdl", true); else if (final < 0.75) ex->model = Mod_ForName ("models/splnter3.mdl", true); else ex->model = Mod_ForName ("models/splnter4.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + 4.0; } } else if (rand() & 1) // whole go { ex = CL_AllocExplosion(); ex->frameFunc = ChunkThink; VectorSubtract(pos,vel,ex->origin); // temp modify them... ex->velocity[0] = (rand() % 140) - 70; ex->velocity[1] = (rand() % 140) - 70; ex->velocity[2] = (rand() % 140) - 70; // are these in degrees or radians? ex->angles[0] = rand() % 360; ex->angles[1] = rand() % 360; ex->angles[2] = rand() % 360; ex->exflags = EXFLAG_ROTATE; ex->avel[0] = (rand() % 850) - 425; ex->avel[1] = (rand() % 850) - 425; ex->avel[2] = (rand() % 850) - 425; ex->scale = 128; ex->data = THINGTYPE_WOOD; ex->model = Mod_ForName ("models/arrow.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + 4.0; } } } } //========================================================================== // // ParseStream // //========================================================================== static void ParseStream(int type) { int ent, tag, flags, skin; vec3_t source, dest; stream_t *stream; float duration; qmodel_t *models[4]; entity_state_t *state; ent = MSG_ReadShort(); flags = MSG_ReadByte(); tag = flags&15; duration = (float)MSG_ReadByte()*0.05; skin = 0; if (type == TE_STREAM_COLORBEAM) skin = MSG_ReadByte(); source[0] = MSG_ReadCoord(); source[1] = MSG_ReadCoord(); source[2] = MSG_ReadCoord(); dest[0] = MSG_ReadCoord(); dest[1] = MSG_ReadCoord(); dest[2] = MSG_ReadCoord(); models[1] = models[2] = models[3] = NULL; switch (type) { case TE_STREAM_CHAIN: models[0] = Mod_ForName("models/stchain.mdl", true); break; case TE_STREAM_SUNSTAFF1: models[0] = Mod_ForName("models/stsunsf1.mdl", true); models[1] = Mod_ForName("models/stsunsf2.mdl", true); models[2] = Mod_ForName("models/stsunsf3.mdl", true); models[3] = Mod_ForName("models/stsunsf4.mdl", true); break; case TE_STREAM_SUNSTAFF2: models[0] = Mod_ForName("models/stsunsf5.mdl", true); models[2] = Mod_ForName("models/stsunsf3.mdl", true); models[3] = Mod_ForName("models/stsunsf4.mdl", true); break; case TE_STREAM_LIGHTNING: models[0] = Mod_ForName("models/stlghtng.mdl", true); break; case TE_STREAM_LIGHTNING_SMALL: models[0] = Mod_ForName("models/stltng2.mdl", true); break; case TE_STREAM_FAMINE: models[0] = Mod_ForName("models/fambeam.mdl", true); break; case TE_STREAM_COLORBEAM: models[0] = Mod_ForName("models/stclrbm.mdl", true); break; case TE_STREAM_ICECHUNKS: models[0] = Mod_ForName("models/stice.mdl", true); break; case TE_STREAM_GAZE: models[0] = Mod_ForName("models/stmedgaz.mdl", true); break; default: models[0] = NULL; break; } if (models[0] == NULL) Sys_Error("%s: bad type", __thisfunc__); if ((stream = NewStream(ent, tag)) == NULL) { Con_Printf("stream list overflow\n"); return; } stream->type = type; stream->tag = tag; stream->flags = flags; stream->entity = ent; stream->skin = skin; stream->models[0] = models[0]; stream->models[1] = models[1]; stream->models[2] = models[2]; stream->models[3] = models[3]; stream->endTime = cl.time + duration; stream->lastTrailTime = 0; VectorCopy(source, stream->source); VectorCopy(dest, stream->dest); if (flags & STREAM_ATTACHED) { VectorClear(stream->offset); state = FindState(ent); if (state) { // rjr - potential problem if this doesn't ever get set - origin might have to be set properly in script code? VectorSubtract(source, state->origin, stream->offset); } } } //========================================================================== // // NewStream // //========================================================================== static stream_t *NewStream(int ent, int tag) { stream_t *stream; int i; // Search for a stream with matching entity and tag for (i = 0, stream = cl_Streams; i < MAX_STREAMS; i++, stream++) { if (stream->entity == ent && stream->tag == tag) return stream; } // Search for a free stream for (i = 0, stream = cl_Streams; i < MAX_STREAMS; i++, stream++) { if (!stream->models[0] || stream->endTime < cl.time) return stream; } return NULL; } /* ================= CL_ParseTEnt ================= */ void CL_ParseTEnt (void) { int cnt, cnt2, i, rnd; int type, damage, chType; float scale = 1.0; // init to 1, make compiler happy float dir, cosval, sinval, volume; dlight_t *dl; explosion_t *ex; vec3_t pos, vel, movedir, offset; type = MSG_ReadByte (); switch (type) { case TE_WIZSPIKE: // spike hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 20, 30); // S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1); break; case TE_KNIGHTSPIKE: // spike hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 226, 20); // S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1); break; case TE_SPIKE: // spike hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 0, 10); if (rand() % 5) { S_StartSound (TempSoundChannel(), 0, cl_sfx_tink1, pos, 1, 1); } else { rnd = rand() & 3; if (rnd == 1) S_StartSound (TempSoundChannel(), 0, cl_sfx_ric1, pos, 1, 1); else if (rnd == 2) S_StartSound (TempSoundChannel(), 0, cl_sfx_ric2, pos, 1, 1); else S_StartSound (TempSoundChannel(), 0, cl_sfx_ric3, pos, 1, 1); } break; case TE_SUPERSPIKE: // super spike hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 0, 20); if (rand() % 5) { S_StartSound (TempSoundChannel(), 0, cl_sfx_tink1, pos, 1, 1); } else { rnd = rand() & 3; if (rnd == 1) S_StartSound (TempSoundChannel(), 0, cl_sfx_ric1, pos, 1, 1); else if (rnd == 2) S_StartSound (TempSoundChannel(), 0, cl_sfx_ric2, pos, 1, 1); else S_StartSound (TempSoundChannel(), 0, cl_sfx_ric3, pos, 1, 1); } break; case TE_DRILLA_EXPLODE: // particles pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); ex = CL_AllocExplosion(); VectorCopy(pos,ex->origin); ex->model = Mod_ForName("models/gen_expl.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + ex->model->numframes * 0.1; // sound S_StartSound (TempSoundChannel(), 0, cl_sfx_explode, pos, 1, 1); break; case TE_EXPLOSION: // rocket explosion // particles pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_ParticleExplosion (pos); // light dl = CL_AllocDlight (0); VectorCopy (pos, dl->origin); dl->radius = 350; dl->die = cl.time + 0.5; dl->decay = 300; dl->color[0] = 0.2; dl->color[1] = 0.1; dl->color[2] = 0.05; dl->color[3] = 0.7; // sound S_StartSound (TempSoundChannel(), 0, cl_sfx_r_exp3, pos, 1, 1); break; case TE_TAREXPLOSION: // tarbaby explosion pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_BlobExplosion (pos); S_StartSound (TempSoundChannel(), 0, cl_sfx_r_exp3, pos, 1, 1); break; case TE_LIGHTNING1: // lightning bolts CL_ParseBeam (NULL); break; case TE_LIGHTNING2: // lightning bolts CL_ParseBeam (NULL); break; case TE_LIGHTNING3: // lightning bolts CL_ParseBeam (NULL); break; case TE_STREAM_CHAIN: case TE_STREAM_SUNSTAFF1: case TE_STREAM_SUNSTAFF2: case TE_STREAM_LIGHTNING: case TE_STREAM_LIGHTNING_SMALL: case TE_STREAM_COLORBEAM: case TE_STREAM_ICECHUNKS: case TE_STREAM_GAZE: case TE_STREAM_FAMINE: ParseStream(type); break; case TE_LAVASPLASH: pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_LavaSplash (pos); break; case TE_TELEPORT: pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_TeleportSplash (pos); break; case TE_GUNSHOT: // bullet hitting wall cnt = MSG_ReadByte (); pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 0, 20*cnt); break; case TE_BLOOD: // bullets hitting body cnt = MSG_ReadByte (); pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 73, 20*cnt); break; case TE_LIGHTNINGBLOOD: // lightning hitting body pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 225, 50); break; case TE_BIGGRENADE: // effect for big grenade pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos,pos,225,1024); ex = CL_AllocExplosion(); VectorCopy(pos,ex->origin); ex->frameFunc = MultiGrenadeThink; ex->data = 250; ex->model = Mod_ForName("models/sm_expld.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + ex->model->numframes * 0.1; break; case TE_CHUNK: //directed chunks case TE_CHUNK2: //volume based chunks pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); vel[0] = MSG_ReadCoord(); //vel for CHUNK, volume for CHUNK2 vel[1] = MSG_ReadCoord(); vel[2] = MSG_ReadCoord(); chType = MSG_ReadByte(); if (type == TE_CHUNK) { cnt = MSG_ReadByte(); } else { volume = vel[0] * vel[1] * vel[2]; cnt = volume / 8192; if (volume < 5000) { scale = .20; cnt *= 3; // Because so few pieces come out of a small object } else if (volume < 50000) { scale = .45; cnt *= 3; // Because so few pieces come out of a small object } else if (volume < 500000) { scale = .50; } else if (volume < 1000000) { scale = .75; } else { scale = 1; } if (cnt > 30) { cnt = 30; } } for (i = 0; i < cnt; i++) { float final; ex = CL_AllocExplosion(); ex->frameFunc = ChunkThink; if (type == TE_CHUNK) { VectorCopy(pos,ex->origin); VectorCopy(vel, ex->velocity); VectorScale(ex->velocity, .80 + ((rand() % 4) / 10.0), ex->velocity); // temp modify them... ex->velocity[0] += (rand() % 140) - 70; ex->velocity[1] += (rand() % 140) - 70; ex->velocity[2] += (rand() % 140) - 70; // are these in degrees or radians? ex->angles[0] = rand() % 360; ex->angles[1] = rand() % 360; ex->angles[2] = rand() % 360; ex->exflags = EXFLAG_ROTATE; ex->avel[0] = (rand() % 850) - 425; ex->avel[1] = (rand() % 850) - 425; ex->avel[2] = (rand() % 850) - 425; ex->scale = 30 + 100 * (cnt / 40.0) + (rand() % 40); } else { // set origin (origin is really absmin here) VectorCopy(pos, ex->origin); ex->origin[0] += FRANDOM() * vel[0]; ex->origin[1] += FRANDOM() * vel[1]; ex->origin[2] += FRANDOM() * vel[2]; // set velocity ex->velocity[0] = -210 + FRANDOM() * 420; ex->velocity[1] = -210 + FRANDOM() * 420; ex->velocity[2] = -210 + FRANDOM() * 490; // set scale ex->scale = scale*100; // set angles, avel ex->angles[0] = rand() % 360; ex->angles[1] = rand() % 360; ex->angles[2] = rand() % 360; ex->avel[0] = rand() % 1200; ex->avel[1] = rand() % 1200; ex->avel[2] = rand() % 1200; } ex->data = chType; final = (rand() % 100) * .01; if ((chType == THINGTYPE_GLASS) || (chType == THINGTYPE_REDGLASS) || (chType == THINGTYPE_CLEARGLASS) || (chType == THINGTYPE_WEBS)) { if (final < 0.20) ex->model = Mod_ForName ("models/shard1.mdl", true); else if (final < 0.40) ex->model = Mod_ForName ("models/shard2.mdl", true); else if (final < 0.60) ex->model = Mod_ForName ("models/shard3.mdl", true); else if (final < 0.80) ex->model = Mod_ForName ("models/shard4.mdl", true); else ex->model = Mod_ForName ("models/shard5.mdl", true); if (chType == THINGTYPE_CLEARGLASS) { ex->skin = 1; ex->flags |= DRF_TRANSLUCENT; } else if (chType == THINGTYPE_REDGLASS) { ex->skin = 2; } else if (chType == THINGTYPE_WEBS) { ex->skin = 3; ex->flags |= DRF_TRANSLUCENT; } } else if (chType == THINGTYPE_WOOD) { if (final < 0.25) ex->model = Mod_ForName ("models/splnter1.mdl", true); else if (final < 0.50) ex->model = Mod_ForName ("models/splnter2.mdl", true); else if (final < 0.75) ex->model = Mod_ForName ("models/splnter3.mdl", true); else ex->model = Mod_ForName ("models/splnter4.mdl", true); } else if (chType == THINGTYPE_METAL) { if (final < 0.25) ex->model = Mod_ForName ("models/metlchk1.mdl", true); else if (final < 0.50) ex->model = Mod_ForName ("models/metlchk2.mdl", true); else if (final < 0.75) ex->model = Mod_ForName ("models/metlchk3.mdl", true); else ex->model = Mod_ForName ("models/metlchk4.mdl", true); } else if (chType == THINGTYPE_FLESH) { if (final < 0.33) ex->model = Mod_ForName ("models/flesh1.mdl", true); else if (final < 0.66) ex->model = Mod_ForName ("models/flesh2.mdl", true); else ex->model = Mod_ForName ("models/flesh3.mdl", true); } else if (chType == THINGTYPE_BROWNSTONE || chType == THINGTYPE_DIRT) { if (final < 0.25) ex->model = Mod_ForName ("models/schunk1.mdl", true); else if (final < 0.50) ex->model = Mod_ForName ("models/schunk2.mdl", true); else if (final < 0.75) ex->model = Mod_ForName ("models/schunk3.mdl", true); else ex->model = Mod_ForName ("models/schunk4.mdl", true); ex->skin = 1; } else if (chType == THINGTYPE_CLAY) { if (final < 0.25) ex->model = Mod_ForName ("models/clshard1.mdl", true); else if (final < 0.50) ex->model = Mod_ForName ("models/clshard2.mdl", true); else if (final < 0.75) ex->model = Mod_ForName ("models/clshard3.mdl", true); else ex->model = Mod_ForName ("models/clshard4.mdl", true); } else if (chType == THINGTYPE_LEAVES) { if (final < 0.33) ex->model = Mod_ForName ("models/leafchk1.mdl", true); else if (final < 0.66) ex->model = Mod_ForName ("models/leafchk2.mdl", true); else ex->model = Mod_ForName ("models/leafchk3.mdl", true); } else if (chType == THINGTYPE_HAY) { if (final < 0.33) ex->model = Mod_ForName ("models/hay1.mdl", true); else if (final < 0.66) ex->model = Mod_ForName ("models/hay2.mdl", true); else ex->model = Mod_ForName ("models/hay3.mdl", true); } else if (chType == THINGTYPE_CLOTH) { if (final < 0.33) ex->model = Mod_ForName ("models/clthchk1.mdl", true); else if (final < 0.66) ex->model = Mod_ForName ("models/clthchk2.mdl", true); else ex->model = Mod_ForName ("models/clthchk3.mdl", true); } else if (chType == THINGTYPE_WOOD_LEAF) { if (final < 0.14) ex->model = Mod_ForName ("models/splnter1.mdl", true); else if (final < 0.28) ex->model = Mod_ForName ("models/leafchk1.mdl", true); else if (final < 0.42) ex->model = Mod_ForName ("models/splnter2.mdl", true); else if (final < 0.56) ex->model = Mod_ForName ("models/leafchk2.mdl", true); else if (final < 0.70) ex->model = Mod_ForName ("models/splnter3.mdl", true); else if (final < 0.84) ex->model = Mod_ForName ("models/leafchk3.mdl", true); else ex->model = Mod_ForName ("models/splnter4.mdl", true); } else if (chType == THINGTYPE_WOOD_METAL) { if (final < 0.125) ex->model = Mod_ForName ("models/splnter1.mdl", true); else if (final < 0.25) ex->model = Mod_ForName ("models/metlchk1.mdl", true); else if (final < 0.375) ex->model = Mod_ForName ("models/splnter2.mdl", true); else if (final < 0.50) ex->model = Mod_ForName ("models/metlchk2.mdl", true); else if (final < 0.625) ex->model = Mod_ForName ("models/splnter3.mdl", true); else if (final < 0.75) ex->model = Mod_ForName ("models/metlchk3.mdl", true); else if (final < 0.875) ex->model = Mod_ForName ("models/splnter4.mdl", true); else ex->model = Mod_ForName ("models/metlchk4.mdl", true); } else if (chType == THINGTYPE_WOOD_STONE) { if (final < 0.125) ex->model = Mod_ForName ("models/splnter1.mdl", true); else if (final < 0.25) ex->model = Mod_ForName ("models/schunk1.mdl", true); else if (final < 0.375) ex->model = Mod_ForName ("models/splnter2.mdl", true); else if (final < 0.50) ex->model = Mod_ForName ("models/schunk2.mdl", true); else if (final < 0.625) ex->model = Mod_ForName ("models/splnter3.mdl", true); else if (final < 0.75) ex->model = Mod_ForName ("models/schunk3.mdl", true); else if (final < 0.875) ex->model = Mod_ForName ("models/splnter4.mdl", true); else ex->model = Mod_ForName ("models/schunk4.mdl", true); } else if (chType == THINGTYPE_METAL_STONE) { if (final < 0.125) ex->model = Mod_ForName ("models/metlchk1.mdl", true); else if (final < 0.25) ex->model = Mod_ForName ("models/schunk1.mdl", true); else if (final < 0.375) ex->model = Mod_ForName ("models/metlchk2.mdl", true); else if (final < 0.50) ex->model = Mod_ForName ("models/schunk2.mdl", true); else if (final < 0.625) ex->model = Mod_ForName ("models/metlchk3.mdl", true); else if (final < 0.75) ex->model = Mod_ForName ("models/schunk3.mdl", true); else if (final < 0.875) ex->model = Mod_ForName ("models/metlchk4.mdl", true); else ex->model = Mod_ForName ("models/schunk4.mdl", true); } else if (chType == THINGTYPE_METAL_CLOTH) { if (final < 0.14) ex->model = Mod_ForName ("models/metlchk1.mdl", true); else if (final < 0.28) ex->model = Mod_ForName ("models/clthchk1.mdl", true); else if (final < 0.42) ex->model = Mod_ForName ("models/metlchk2.mdl", true); else if (final < 0.56) ex->model = Mod_ForName ("models/clthchk2.mdl", true); else if (final < 0.70) ex->model = Mod_ForName ("models/metlchk3.mdl", true); else if (final < 0.84) ex->model = Mod_ForName ("models/clthchk3.mdl", true); else ex->model = Mod_ForName ("models/metlchk4.mdl", true); } else if (chType == THINGTYPE_ICE) { ex->model = Mod_ForName("models/shard.mdl", true); ex->skin = 0; //ent->frame = rand() % 2; ex->flags |= DRF_TRANSLUCENT|MLS_ABSLIGHT; //ent->abslight = 0.5; } else if (chType == THINGTYPE_METEOR) { ex->model = Mod_ForName("models/tempmetr.mdl", true); ex->skin = 0; //ex->scale *= .6; VectorScale(ex->avel, 4.0, ex->avel); } else if (chType == THINGTYPE_ACID) { // no spinning if possible... ex->model = Mod_ForName("models/sucwp2p.mdl", true); ex->skin = 0; } else if (chType == THINGTYPE_GREENFLESH) { // spider guts if (final < 0.33) ex->model = Mod_ForName ("models/sflesh1.mdl", true); else if (final < 0.66) ex->model = Mod_ForName ("models/sflesh2.mdl", true); else ex->model = Mod_ForName ("models/sflesh3.mdl", true); ex->skin = 0; } else// if (chType == THINGTYPE_GREYSTONE) { if (final < 0.25) ex->model = Mod_ForName ("models/schunk1.mdl", true); else if (final < 0.50) ex->model = Mod_ForName ("models/schunk2.mdl", true); else if (final < 0.75) ex->model = Mod_ForName ("models/schunk3.mdl", true); else ex->model = Mod_ForName ("models/schunk4.mdl", true); ex->skin = 0; } ex->startTime = cl.time; ex->endTime = ex->startTime + 4.0; } break; case TE_XBOWHIT: pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); vel[0] = MSG_ReadCoord(); vel[1] = MSG_ReadCoord(); vel[2] = MSG_ReadCoord(); chType = MSG_ReadByte(); damage = MSG_ReadByte(); // do a particle effect here, with the color depending on chType // impact sound: switch (chType) { case THINGTYPE_FLESH: // S_StartSound (TempSoundChannel(), 0, cl_sfx_arr2flsh, pos, 1, 1); break; case THINGTYPE_WOOD: // S_StartSound (TempSoundChannel(), 0, cl_sfx_arr2wood, pos, 1, 1); break; default: // S_StartSound (TempSoundChannel(), 0, cl_sfx_met2stn, pos, 1, 1); break; } //generic spinny impact image ex = CL_AllocExplosion(); ex->origin[0] = pos[0] - vel[0]; ex->origin[1] = pos[1] - vel[1]; ex->origin[2] = pos[2] - vel[2]; vectoangles(vel,ex->angles); ex->avel[2] = (rand() % 500) + 200; ex->scale = 10; ex->startTime = cl.time; ex->endTime = cl.time + 0.3; ex->model = Mod_ForName ("models/arrowhit.mdl", true); ex->exflags = EXFLAG_ROTATE; ex->flags = DRF_TRANSLUCENT | MLS_ABSLIGHT; ex->abslight = 128; //white smoke if invulnerable impact if (!damage) { ex = CL_AllocExplosion(); ex->origin[0] = pos[0] - vel[0]*2; ex->origin[1] = pos[1] - vel[1]*2; ex->origin[2] = pos[2] - vel[2]*2; ex->velocity[0] = 0.0; ex->velocity[1] = 0.0; ex->velocity[2] = 80.0; vectoangles(vel,ex->angles); ex->startTime = cl.time; ex->endTime = cl.time + 0.35; ex->model = Mod_ForName ("models/whtsmk1.spr", true); ex->flags = DRF_TRANSLUCENT; } break; case TE_METEORHIT: pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); // always make 8 meteors i = (host_frametime < .07) ? 0 : 4; // based on framerate for ( ; i < 8; i++) { ex = CL_AllocExplosion(); VectorCopy(pos,ex->origin); ex->frameFunc = ChunkThink; // temp modify them... ex->velocity[0] += (rand() % 400) - 200; ex->velocity[1] += (rand() % 400) - 200; ex->velocity[2] += (rand() % 200) + 150; // are these in degrees or radians? ex->angles[0] = rand() % 360; ex->angles[1] = rand() % 360; ex->angles[2] = rand() % 360; ex->exflags = EXFLAG_ROTATE; ex->avel[0] = (rand() % 850) - 425; ex->avel[1] = (rand() % 850) - 425; ex->avel[2] = (rand() % 850) - 425; ex->scale = 45 + (rand() % 10); ex->data = THINGTYPE_METEOR; ex->model = Mod_ForName("models/tempmetr.mdl", true); ex->skin = 0; VectorScale(ex->avel, 4.0, ex->avel); ex->startTime = cl.time; ex->endTime = ex->startTime + 4.0; } // make the actual explosion i = (host_frametime < .07) ? 0 : 8; // based on framerate for ( ; i < 11; i++) { ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->origin[0] += (rand() % 10) - 5; ex->origin[1] += (rand() % 10) - 5; ex->origin[2] += (rand() % 10) - 5; ex->velocity[0] = (ex->origin[0] - pos[0])*12; ex->velocity[1] = (ex->origin[1] - pos[1])*12; ex->velocity[2] = (ex->origin[2] - pos[2])*12; switch (rand() % 4) { case 0: case 1: ex->model = Mod_ForName("models/sm_expld.spr", true); break; case 2: ex->model = Mod_ForName("models/bg_expld.spr", true); break; case 3: ex->model = Mod_ForName("models/gen_expl.spr", true); break; } if (host_frametime < .07) ex->flags |= MLS_ABSLIGHT|DRF_TRANSLUCENT; ex->abslight = 160 + (rand() % 64); ex->skin = 0; ex->scale = 80 + (rand() % 40); ex->startTime = cl.time + ((rand() % 50) / 200.0); ex->endTime = ex->startTime + ex->model->numframes * 0.04; } S_StartSound (TempSoundChannel(), 0, cl_sfx_axeExplode, pos, 1, 1); break; case TE_HWBONEPOWER: cnt = MSG_ReadByte (); pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); movedir[0] = MSG_ReadCoord (); movedir[1] = MSG_ReadCoord (); movedir[2] = MSG_ReadCoord (); R_RunParticleEffect4 (pos, 50, 368 + (rand() % 16), pt_grav, 10); // particle4 (50, rand(368-384), grav, 10); ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); VectorMA(ex->origin, -6, movedir, ex->origin); ex->data = 250; ex->model = Mod_ForName("models/sm_expld.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + ex->model->numframes * 0.1; for (cnt2 = 0; cnt2 < cnt; cnt2++) { offset[0] = (rand() % 40) - 20; offset[1] = (rand() % 40) - 20; offset[2] = (rand() % 40) - 20; ex = CL_AllocExplosion (); VectorAdd(pos, offset, ex->origin); VectorMA(ex->origin, -8, movedir, ex->origin); VectorCopy(offset, ex->velocity); ex->velocity[2] += 30; ex->data = 250; ex->model = Mod_ForName("models/ghost.spr", true); ex->abslight = 128; ex->flags = DRF_TRANSLUCENT | MLS_ABSLIGHT; ex->startTime = cl.time; ex->endTime = ex->startTime + ex->model->numframes * 0.1; } for (cnt2 = 0; cnt2 < 20; cnt2++) { // want faster velocity to hide the fact that these // aren't in the real location offset[0] = (rand() % 400) + 300; if (rand() % 2) offset[0] = -offset[0]; offset[1] = (rand() % 400) + 300; if (rand() % 2) offset[1] = -offset[1]; offset[2] = (rand() % 400) + 300; if (rand() % 2) offset[2] = -offset[2]; ex = CL_AllocExplosion (); VectorMA(pos, 1/700, offset, ex->origin); VectorCopy(offset, ex->velocity); ex->data = 250; ex->model = Mod_ForName("models/boneshrd.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + ((rand() * 50) / 100); ex->flags |= EXFLAG_ROTATE|EXFLAG_COLLIDE; ex->angles[0] = rand() % 700; ex->angles[1] = rand() % 700; ex->angles[2] = rand() % 700; ex->avel[0] = rand() % 700; ex->avel[1] = rand() % 700; ex->avel[2] = rand() % 700; ex->frameFunc = CheckSpaceThink; } S_StartSound(TempSoundChannel(), 1, cl_sfx_bonephit, pos, 1, 1); break; case TE_HWBONEPOWER2: cnt2 = MSG_ReadByte (); //did it hit? changes sound pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); // white smoke ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/whtsmk1.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + ex->model->numframes * 0.1; //sound if (cnt2) S_StartSound(TempSoundChannel(), 1, cl_sfx_bonehit, pos, 1, 1); else S_StartSound(TempSoundChannel(), 1, cl_sfx_bonewal, pos, 1, 1); R_RunParticleEffect4 (pos, 3, 368 + (rand() % 16), pt_grav, 7); // particle4(self.origin, 3, random(368,384), PARTICLETYPE_GRAV, self.dmg/2); break; case TE_HWRAVENDIE: pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); ex->velocity[1] = 8; ex->velocity[2] = -10; ex->model = Mod_ForName("models/whtsmk1.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + HX_FRAME_TIME * 10; ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); ex->velocity[2] = -10; ex->model = Mod_ForName("models/redsmk1.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + HX_FRAME_TIME * 10; ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); ex->velocity[1] = -8; ex->velocity[2] = -10; ex->model = Mod_ForName("models/whtsmk1.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + HX_FRAME_TIME * 10; S_StartSound(TempSoundChannel(), 1, cl_sfx_ravendie, pos, 1, 1); break; case TE_HWRAVENEXPLODE: pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); ex->velocity[2] = 8; ex->model = Mod_ForName("models/whtsmk1.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + HX_FRAME_TIME * 10; ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); ex->origin[2] -= 5; ex->velocity[2] = 8; ex->model = Mod_ForName("models/whtsmk1.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + HX_FRAME_TIME * 10; ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); ex->origin[2] -= 10; ex->velocity[2] = 8; ex->model = Mod_ForName("models/whtsmk1.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + HX_FRAME_TIME * 10; S_StartSound(TempSoundChannel(), 1, cl_sfx_ravengo, pos, 1, 1); break; case TE_ICEHIT: pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); cnt2 = MSG_ReadByte(); // 0 for person, 1 for wall if (cnt2) { i = (host_frametime < .07) ? 0 : 5; // based on framerate for ( ; i < 9; i++) { ex = CL_AllocExplosion(); VectorCopy(pos,ex->origin); ex->frameFunc = ChunkThink; ex->velocity[0] += (rand() % 1000) - 500; ex->velocity[1] += (rand() % 1000) - 500; ex->velocity[2] += (rand() % 200 ) - 50; // are these in degrees or radians? ex->angles[0] = rand() % 360; ex->angles[1] = rand() % 360; ex->angles[2] = rand() % 360; ex->exflags = EXFLAG_ROTATE; ex->avel[0] = (rand() % 850) - 425; ex->avel[1] = (rand() % 850) - 425; ex->avel[2] = (rand() % 850) - 425; if (cnt2 == 2) ex->scale = 65 + (rand() % 10); else ex->scale = 35 + (rand() % 10); ex->data = THINGTYPE_ICE; ex->model = Mod_ForName("models/shard.mdl", true); ex->skin = 0; //ent->frame = rand() % 2; ex->flags |= DRF_TRANSLUCENT|MLS_ABSLIGHT; ex->abslight = 128; ex->startTime = cl.time; ex->endTime = ex->startTime + 2.0; if (cnt2 == 2) ex->endTime += 3.0; } } else { vec3_t dmin = {-10, -10, -10}; vec3_t dmax = {10, 10, 10}; R_ColoredParticleExplosion(pos,14,10,10); R_RunParticleEffect2 (pos, dmin, dmax, 145, pt_explode, 14); } // make the actual explosion ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/icehit.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + ex->model->numframes * 0.1; // Add in the sound if (cnt2 == 1) S_StartSound (TempSoundChannel(), 0, cl_sfx_icewall, pos, 1, 1); // hit a wall else if (cnt2 == 2) S_StartSound (TempSoundChannel(), 0, cl_sfx_iceshatter, pos, 1, 1); else S_StartSound (TempSoundChannel(), 0, cl_sfx_iceflesh, pos, 1, 1); // hit a person break; case TE_ICESTORM: { int ent; vec3_t center; stream_t *stream; qmodel_t *models[2]; entity_state_t *state; ent = MSG_ReadShort(); state = FindState(ent); if (state) { VectorCopy(state->origin, center); playIceSound += host_frametime; if (playIceSound >= .6) { S_StartSound (TempSoundChannel(), 0, cl_sfx_icestorm, center, 1, 1); playIceSound -= .6; } for (i = 0; i < 5; i++) { // make some ice beams... models[0] = Mod_ForName("models/stice.mdl", true); if ((stream = NewStream(ent, i)) == NULL) { Con_Printf("stream list overflow\n"); return; } stream->type = TE_STREAM_ICECHUNKS; stream->tag = (i)&15; // FIXME stream->flags = (i+STREAM_ATTACHED); stream->entity = ent; stream->skin = 0; stream->models[0] = models[0]; stream->endTime = cl.time + 0.3; stream->lastTrailTime = 0; VectorCopy(center, stream->source); stream->source[0] += (rand() % 100) - 50; stream->source[1] += (rand() % 100) - 50; VectorCopy(stream->source, stream->dest); stream->dest[2] += 128; VectorClear(stream->offset); VectorSubtract(stream->source, state->origin, stream->offset); } } } break; case TE_HWMISSILEFLASH: pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); vel[0] = MSG_ReadCoord (); //angles vel[1] = MSG_ReadCoord (); vel[2] = rand() % 360; ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); VectorCopy(vel, ex->angles); ex->frameFunc = MissileFlashThink; ex->model = Mod_ForName("models/handfx.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + 2; ex->exflags = EXFLAG_ROTATE; ex->avel[2] = (rand() * 360) + 360; ex->flags = MLS_ABSLIGHT|DRF_TRANSLUCENT; ex->abslight = 128; //ex->scale = .8 + (rand() % 100) * 0.004; // .8 to 1.2 ex->scale = 80 + (rand() % 40); break; case TE_SUNSTAFF_CHEAP: { int ent; vec3_t points[4]; int j, reflect_count; short int tempVal; entity_state_t *state; stream_t *stream; qmodel_t *models[4]; ent = MSG_ReadShort(); reflect_count = MSG_ReadByte(); state = FindState(ent); if (state) { // read in up to 4 points for up to 3 beams for (i = 0; i < 3; i++) { tempVal = MSG_ReadCoord(); points[0][i] = tempVal; } for (i = 1; i < 2 + reflect_count; i++) { for (j = 0; j < 3; j++) { tempVal = MSG_ReadCoord(); points[i][j] = tempVal; } } // actually create the sun model pieces for (i = 0; i < reflect_count + 1; i++) { models[0] = Mod_ForName("models/stsunsf1.mdl", true); models[1] = Mod_ForName("models/stsunsf2.mdl", true); models[2] = Mod_ForName("models/stsunsf3.mdl", true); models[3] = Mod_ForName("models/stsunsf4.mdl", true); if ((stream = NewStream(ent, i)) == NULL) { Con_Printf("stream list overflow\n"); return; } stream->type = TE_STREAM_SUNSTAFF1; stream->tag = i; if (!i) stream->flags = (i+STREAM_ATTACHED); else stream->flags = i; stream->entity = ent; stream->skin = 0; stream->models[0] = models[0]; stream->models[1] = models[1]; stream->models[2] = models[2]; stream->models[3] = models[3]; stream->endTime = cl.time + 0.5; // FIXME stream->lastTrailTime = 0; VectorCopy(points[i], stream->source); VectorCopy(points[i+1], stream->dest); if (!i) { VectorClear(stream->offset); VectorSubtract(stream->source, state->origin, stream->offset); } } } else { // read in everything to keep everything in sync for (i = 0; i < (2 + reflect_count)*3; i++) tempVal = MSG_ReadShort(); } } break; case TE_LIGHTNING_HAMMER: { int ent; stream_t *stream; qmodel_t *models[2]; entity_state_t *state; ent = MSG_ReadShort(); state = FindState(ent); if (state) { if (rand() & 1) S_StartSound (TempSoundChannel(), 0, cl_sfx_lightning1, state->origin, 1, 1); else S_StartSound (TempSoundChannel(), 0, cl_sfx_lightning2, state->origin, 1, 1); for (i = 0; i < 5; i++) { // make some lightning models[0] = Mod_ForName("models/stlghtng.mdl", true); if ((stream = NewStream(ent, i)) == NULL) { Con_Printf("stream list overflow\n"); return; } //stream->type = TE_STREAM_ICECHUNKS; stream->type = TE_STREAM_LIGHTNING; stream->tag = i; stream->flags = i; stream->entity = ent; stream->skin = 0; stream->models[0] = models[0]; stream->endTime = cl.time + 0.5; stream->lastTrailTime = 0; VectorCopy(state->origin, stream->source); stream->source[0] += (rand() % 30) - 15; stream->source[1] += (rand() % 30) - 15; VectorCopy(stream->source, stream->dest); stream->dest[0] += (rand() % 80) - 40; stream->dest[1] += (rand() % 80) - 40; stream->dest[2] += 64 + (rand() % 48); } } } break; case TE_HWTELEPORT: pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); cnt = MSG_ReadShort(); //skin# S_StartSound (TempSoundChannel(), 0, cl_sfx_teleport[rand() % 5], pos, 1, 1); ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); ex->frameFunc = TeleportFlashThink; ex->model = Mod_ForName("models/teleport.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + 2; ex->avel[2] = (rand() * 360) + 360; ex->flags = SCALE_TYPE_XYONLY | DRF_TRANSLUCENT; ex->skin = cnt; ex->scale = 100; for (dir = 0; dir < 360; dir += 45) { q_sincosdeg(dir, &sinval, &cosval); sinval *= 10; cosval *= 10; ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/telesmk2.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + .5; ex->velocity[0] = cosval; ex->velocity[1] = sinval; ex->flags = DRF_TRANSLUCENT; ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); ex->origin[2] += 64; ex->model = Mod_ForName("models/telesmk2.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + .5; ex->velocity[0] = cosval; ex->velocity[1] = sinval; ex->flags = DRF_TRANSLUCENT; } break; case TE_SWORD_EXPLOSION: { int ent; stream_t *stream; qmodel_t *models[2]; entity_state_t *state; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); ent = MSG_ReadShort(); state = FindState(ent); if (state) { if (rand() & 1) S_StartSound (TempSoundChannel(), 0, cl_sfx_lightning1, pos, 1, 1); else S_StartSound (TempSoundChannel(), 0, cl_sfx_lightning2, pos, 1, 1); for (i = 0; i < 5; i++) { // make some lightning models[0] = Mod_ForName("models/stlghtng.mdl", true); if ((stream = NewStream(ent, i)) == NULL) { Con_Printf("stream list overflow\n"); return; } //stream->type = TE_STREAM_ICECHUNKS; stream->type = TE_STREAM_LIGHTNING; stream->tag = i; stream->flags = i; stream->entity = ent; stream->skin = 0; stream->models[0] = models[0]; stream->endTime = cl.time + 0.5; stream->lastTrailTime = 0; VectorCopy(pos, stream->source); stream->source[0] += (rand() % 30) - 15; stream->source[1] += (rand() % 30) - 15; VectorCopy(stream->source, stream->dest); stream->dest[0] += (rand() % 80) - 40; stream->dest[1] += (rand() % 80) - 40; stream->dest[2] += 64 + (rand() % 48); } } ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/vorpshok.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + 1.0; ex->flags |= MLS_ABSLIGHT; ex->abslight = 128; ex->skin = 0; ex->scale = 100; ex->frameFunc = SwordFrameFunc; S_StartSound (TempSoundChannel(), 0, cl_sfx_swordExplode, pos, 1, 1); } break; case TE_AXE_BOUNCE: pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/spark.spr", true); ex->startTime = cl.time; ex->flags |= MLS_ABSLIGHT; ex->abslight = 128; ex->skin = 0; ex->scale = 100; ex->endTime = ex->startTime + ex->model->numframes * 0.05; S_StartSound (TempSoundChannel(), 0, cl_sfx_axeBounce, pos, 1, 1); break; case TE_AXE_EXPLODE: pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); i = (host_frametime < .07) ? 0 : 3; // based on framerate for ( ; i < 5; i++) { ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->origin[0] += (rand() % 6) - 3; ex->origin[1] += (rand() % 6) - 3; ex->origin[2] += (rand() % 6) - 3; ex->velocity[0] = (ex->origin[0] - pos[0])*15; ex->velocity[1] = (ex->origin[1] - pos[1])*15; ex->velocity[2] = (ex->origin[2] - pos[2])*15; switch (rand() % 6) { case 0: case 1: ex->model = Mod_ForName("models/xpspblue.spr", true); break; case 2: case 3: ex->model = Mod_ForName("models/xpspblue.spr", true); ex->flags |= MLS_ABSLIGHT|DRF_TRANSLUCENT; break; case 4: case 5: ex->model = Mod_ForName("models/spark0.spr", true); ex->flags |= MLS_ABSLIGHT|DRF_TRANSLUCENT; break; } ex->flags |= MLS_ABSLIGHT;//|DRF_TRANSLUCENT; ex->abslight = 160 + (rand() % 24); ex->skin = 0; ex->scale = 80 + (rand() % 40); ex->startTime = cl.time + ((rand() % 50) / 200.0); ex->endTime = ex->startTime + ex->model->numframes * 0.05; } S_StartSound (TempSoundChannel(), 0, cl_sfx_axeExplode, pos, 1, 1); break; case TE_TIME_BOMB: pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); i = (host_frametime < .07) ? 0 : 14; // based on framerate for ( ; i < 20; i++) { ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->origin[0] += (rand() % 6) - 3; ex->origin[1] += (rand() % 6) - 3; ex->origin[2] += (rand() % 6) - 3; ex->velocity[0] = (ex->origin[0] - pos[0])*40; ex->velocity[1] = (ex->origin[1] - pos[1])*40; ex->velocity[2] = (ex->origin[2] - pos[2])*40; switch (rand() % 4) { case 0: ex->model = Mod_ForName("models/sm_expld.spr", true); break; case 2: ex->model = Mod_ForName("models/bg_expld.spr", true); break; case 1: case 3: ex->model = Mod_ForName("models/gen_expl.spr", true); break; } ex->flags |= MLS_ABSLIGHT|DRF_TRANSLUCENT; ex->abslight = 160 + (rand() % 24); ex->skin = 0; ex->scale = 80 + (rand() % 40); ex->startTime = cl.time + ((rand() % 50) / 200.0); ex->endTime = ex->startTime + ex->model->numframes * 0.05; } S_StartSound (TempSoundChannel(), 0, cl_sfx_axeExplode, pos, 1, 1); break; case TE_FIREBALL: pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/blast.mdl", true); ex->flags |= MLS_ABSLIGHT|SCALE_TYPE_UNIFORM|SCALE_ORIGIN_CENTER; ex->abslight = 128; ex->skin = 0; ex->scale = 1; ex->startTime = cl.time; ex->endTime = ex->startTime + 1.0; ex->frameFunc = fireBallUpdate; ex->exflags = EXFLAG_ROTATE; ex->avel[0] = 50; ex->avel[1] = 50; ex->avel[2] = 50; S_StartSound (TempSoundChannel(), 0, cl_sfx_fireBall, pos, 1, 1); break; case TE_SUNSTAFF_POWER: { int ent; stream_t *stream; qmodel_t *models[4]; entity_state_t *state; ent = MSG_ReadShort(); vel[0] = MSG_ReadCoord(); vel[1] = MSG_ReadCoord(); vel[2] = MSG_ReadCoord(); pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); for (i = 0; i < 2; i++) { ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); if (i) { ex->model = Mod_ForName("models/stsunsf3.mdl", true); ex->scale = 200; ex->frameFunc = sunPowerUpdate; } else { ex->model = Mod_ForName("models/blast.mdl", true); ex->flags |= DRF_TRANSLUCENT; ex->frameFunc = sunBallUpdate; ex->scale = 120; } ex->flags |= MLS_ABSLIGHT|SCALE_TYPE_UNIFORM|SCALE_ORIGIN_CENTER; ex->abslight = 128; ex->skin = 0; ex->scale = 200; ex->startTime = cl.time; ex->endTime = ex->startTime + .8; ex->exflags = EXFLAG_ROTATE; ex->avel[0] = 50; ex->avel[1] = 50; ex->avel[2] = 50; } S_StartSound (TempSoundChannel(), 0, cl_sfx_fireBall, pos, 1, 1); state = FindState(ent); if (state) { S_StartSound (TempSoundChannel(), 0, cl_sfx_sunstaff, state->origin, 1, 1); models[0] = Mod_ForName("models/stsunsf2.mdl", true); models[1] = Mod_ForName("models/stsunsf1.mdl", true); models[2] = Mod_ForName("models/stsunsf3.mdl", true); models[3] = Mod_ForName("models/stsunsf4.mdl", true); if ((stream = NewStream(ent, 0)) == NULL) { Con_Printf("stream list overflow\n"); return; } stream->type = TE_STREAM_SUNSTAFF2; stream->tag = 0; //stream->flags = STREAM_ATTACHED; stream->flags = 0; stream->entity = ent; stream->skin = 0; stream->models[0] = models[0]; stream->models[1] = models[1]; stream->models[2] = models[2]; stream->models[3] = models[3]; stream->endTime = cl.time + 0.8; stream->lastTrailTime = 0; VectorCopy(vel, stream->source); VectorCopy(pos, stream->dest); //VectorClear(stream->offset); //VectorSubtract(stream->source, vel, stream->offset); // make some spiffy particles to glue it all together } } break; case TE_PURIFY2_EXPLODE: pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/xplod29.spr", true); ex->startTime = cl.time; ex->flags |= MLS_ABSLIGHT; ex->abslight = 128; ex->skin = 0; ex->scale = 100; ex->endTime = ex->startTime + ex->model->numframes * 0.05; i = (host_frametime < .07) ? 0 : 8; // based on framerate for (i = 0; i < 12; i++) { ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->origin[0] += (rand() % 20) - 10; ex->origin[1] += (rand() % 20) - 10; ex->origin[2] += (rand() % 20) - 10; ex->velocity[0] = (ex->origin[0] - pos[0])*12; ex->velocity[1] = (ex->origin[1] - pos[1])*12; ex->velocity[2] = (ex->origin[2] - pos[2])*12; switch (rand() % 4) { case 0: case 1: ex->model = Mod_ForName("models/sm_expld.spr", true); break; case 2: ex->model = Mod_ForName("models/bg_expld.spr", true); break; case 3: ex->model = Mod_ForName("models/gen_expl.spr", true); break; } if (host_frametime < 0.07) { ex->flags |= MLS_ABSLIGHT|DRF_TRANSLUCENT; } ex->abslight = 160 + (rand() % 64); ex->skin = 0; ex->scale = 80 + (rand() % 40); ex->startTime = cl.time + ((rand() % 50) / 200.0); ex->endTime = ex->startTime + ex->model->numframes * 0.04; } S_StartSound (TempSoundChannel(), 0, cl_sfx_purify2, pos, 1, 1); break; case TE_PLAYER_DEATH: { int angle; // from 0 to 256 int pitch; // from 0 to 256 int force, style; float throwPower, curAng, curPitch; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); angle = MSG_ReadByte(); pitch = MSG_ReadByte(); force = MSG_ReadByte(); style = MSG_ReadByte(); i = (host_frametime < 0.07) ? 0 : 8; for ( ; i < 12; i++) { ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->origin[0] += (rand() % 40) - 20; ex->origin[1] += (rand() % 40) - 20; ex->origin[2] += (rand() % 40); ex->frameFunc = ChunkThink; throwPower = 3.5 + ((rand() % 100) / 100.0); throwPower *= force; curAng = angle*6.28/256.0 + ((rand() % 100) / 50.0) - 1.0; curPitch = pitch*6.28/256.0 + ((rand() % 100) / 100.0) - .5; q_sincosrad(curAng, &sinval, &cosval); ex->velocity[0] = cosval; ex->velocity[1] = sinval; q_sincosrad(curPitch, &sinval, &cosval); ex->velocity[0] *= cosval; ex->velocity[1] *= cosval; ex->velocity[2] = sinval; VectorScale(ex->velocity, throwPower, ex->velocity); // are these in degrees or radians? ex->angles[0] = rand() % 360; ex->angles[1] = rand() % 360; ex->angles[2] = rand() % 360; ex->exflags = EXFLAG_ROTATE; ex->avel[0] = (rand() % 850) - 425; ex->avel[1] = (rand() % 850) - 425; ex->avel[2] = (rand() % 850) - 425; ex->scale = 80 + (rand() % 40); ex->data = THINGTYPE_FLESH; switch (rand() % 3) { case 0: ex->model = Mod_ForName("models/flesh1.mdl", true); break; case 1: ex->model = Mod_ForName("models/flesh2.mdl", true); break; case 2: ex->model = Mod_ForName("models/flesh3.mdl", true); break; } ex->skin = 0; ex->startTime = cl.time; ex->endTime = ex->startTime + 4.0; } switch (style) { case 0: S_StartSound (TempSoundChannel(), 0, cl_sfx_big_gib, pos, 1, 1); break; case 1: if (rand() % 2) S_StartSound (TempSoundChannel(), 0, cl_sfx_gib1, pos, 1, 1); else S_StartSound (TempSoundChannel(), 0, cl_sfx_gib2, pos, 1, 1); break; case 2: S_StartSound (TempSoundChannel(), 0, cl_sfx_telefrag, pos, 1, 1); break; } } break; case TE_PURIFY1_EFFECT: { float angle, pitch, dist; vec3_t endPos; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); angle = MSG_ReadByte()*6.28/256.0; pitch = MSG_ReadByte()*6.28/256.0; dist = MSG_ReadShort(); q_sincosrad(angle, &sinval, &cosval); endPos[0] = cosval; endPos[1] = sinval; q_sincosrad(pitch, &sinval, &cosval); endPos[0] *= cosval; endPos[1] *= cosval; endPos[2] = sinval; VectorScale(endPos, dist, endPos); VectorAdd(endPos, pos, endPos); R_RocketTrail (pos, endPos, rt_purify); S_StartSound (TempSoundChannel(), 0, cl_sfx_purify1_fire, pos, 1, 1); S_StartSound (TempSoundChannel(), 0, cl_sfx_purify1_hit, endPos, 1, 1); ex = CL_AllocExplosion(); VectorCopy(endPos, ex->origin); ex->model = Mod_ForName("models/fcircle.spr", true); ex->startTime = cl.time; ex->flags |= MLS_ABSLIGHT; ex->abslight = 128; ex->skin = 0; ex->scale = 100; ex->endTime = ex->startTime + ex->model->numframes * 0.05; } break; case TE_TELEPORT_LINGER: { float duration; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); duration = MSG_ReadCoord(); ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/bspark.spr", true); ex->startTime = cl.time; ex->flags |= MLS_ABSLIGHT; ex->abslight = 128; ex->frameFunc = telEffectUpdate; ex->skin = 0; ex->scale = 0; ex->endTime = ex->startTime + duration; } break; case TE_LINE_EXPLOSION: { int distance; vec3_t endPos,midPos,curPos,distVec; float ratio; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); endPos[0] = MSG_ReadCoord(); endPos[1] = MSG_ReadCoord(); endPos[2] = MSG_ReadCoord(); VectorAdd(pos,endPos,midPos); VectorScale(midPos,0.5,midPos); VectorSubtract(midPos,pos,distVec); distance = (int)(VectorNormalizeFast(distVec)*0.025); if (distance > 0) { VectorScale(distVec,40,distVec); VectorCopy(midPos,curPos); for (i = 0; i < distance; i++) { ex = CL_AllocExplosion(); VectorCopy(curPos, ex->origin); switch (rand() % 3) { case 0: ex->model = Mod_ForName("models/gen_expl.spr", true); break; case 1: ex->model = Mod_ForName("models/bg_expld.spr", true); break; case 2: ex->model = Mod_ForName("models/sm_expld.spr", true); break; } ex->flags |= MLS_ABSLIGHT; ex->abslight = 128; ex->skin = 0; ratio = (float)i/(float)distance; ex->scale = 200-(int)(150.0*ratio); ex->startTime = cl.time + ratio*0.75; ex->endTime = ex->startTime + ex->model->numframes * (0.025 + FRANDOM()*0.01); VectorAdd(curPos,distVec,curPos); } VectorScale(distVec,-1,distVec); VectorCopy(midPos,curPos); for (i = 0; i < distance; i++) { ex = CL_AllocExplosion(); VectorCopy(curPos, ex->origin); switch (rand() % 3) { case 0: ex->model = Mod_ForName("models/gen_expl.spr", true); break; case 1: ex->model = Mod_ForName("models/bg_expld.spr", true); break; case 2: ex->model = Mod_ForName("models/sm_expld.spr", true); break; } ex->flags |= MLS_ABSLIGHT; ex->abslight = 128; ex->skin = 0; ratio = (float)i / (float)distance; ex->scale = 200 - (int)(150.0*ratio); ex->startTime = cl.time + ratio*0.75; ex->endTime = ex->startTime + ex->model->numframes * (0.025+FRANDOM()*0.01); VectorAdd(curPos,distVec,curPos); } } } break; case TE_METEOR_CRUSH: { float maxDist; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); maxDist = MSG_ReadLong(); ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/null.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + 0.4; ex->frameFunc = MeteorCrushSpawnThink; ex->data = maxDist; S_StartSound (TempSoundChannel(), 0, cl_sfx_axeExplode, pos, 1, 1); } break; case TE_ACIDBALL: pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); i = (host_frametime < 0.07) ? 0 : 2; for ( ; i < 5; i++) { ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->origin[0] += (rand() % 6) - 3; ex->origin[1] += (rand() % 6) - 3; ex->origin[2] += (rand() % 6) - 3; ex->velocity[0] = (ex->origin[0] - pos[0])*6; ex->velocity[1] = (ex->origin[1] - pos[1])*6; ex->velocity[2] = (ex->origin[2] - pos[2])*6; ex->model = Mod_ForName("models/axplsn_2.spr", true); if (host_frametime < 0.07) ex->flags |= MLS_ABSLIGHT|DRF_TRANSLUCENT; ex->abslight = 160 + (rand() % 24); ex->skin = 0; ex->scale = 80 + (rand() % 40); ex->startTime = cl.time + ((rand() % 50) / 200.0); ex->endTime = ex->startTime + ex->model->numframes * 0.05; } S_StartSound (TempSoundChannel(), 0, cl_sfx_acidhit, pos, 1, 1); break; case TE_ACIDBLOB: pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); i = (host_frametime < 0.07) ? 0 : 7; for ( ; i < 12; i++) { ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->origin[0] += (rand() % 6) - 3; ex->origin[1] += (rand() % 6) - 3; ex->origin[2] += (rand() % 6) - 3; ex->velocity[0] = (ex->origin[0] - pos[0])*25; ex->velocity[1] = (ex->origin[1] - pos[1])*25; ex->velocity[2] = (ex->origin[2] - pos[2])*25; switch (rand() % 4) { case 0: ex->model = Mod_ForName("models/axplsn_2.spr", true); break; case 1: ex->model = Mod_ForName("models/axplsn_1.spr", true); break; case 2: case 3: ex->model = Mod_ForName("models/axplsn_5.spr", true); break; } if (host_frametime < 0.07) ex->flags |= MLS_ABSLIGHT|DRF_TRANSLUCENT; ex->abslight = 1; ex->skin = 0; ex->scale = 80 + (rand() % 40); ex->startTime = cl.time + ((rand() % 50) / 200.0); ex->endTime = ex->startTime + ex->model->numframes * 0.05; } // always make 8 meteors i = (host_frametime < 0.07) ? 0 : 4; for ( ; i < 8; i++) { ex = CL_AllocExplosion(); VectorCopy(pos,ex->origin); ex->frameFunc = ChunkThink; // temp modify them... ex->velocity[0] = (rand() % 500) - 250; ex->velocity[1] = (rand() % 500) - 250; ex->velocity[2] = (rand() % 200) + 200; // are these in degrees or radians? ex->angles[0] = rand() % 360; ex->angles[1] = rand() % 360; ex->angles[2] = rand() % 360; ex->exflags = EXFLAG_ROTATE; ex->avel[0] = (rand() % 850) - 425; ex->avel[1] = (rand() % 850) - 425; ex->avel[2] = (rand() % 850) - 425; ex->scale = 45 + (rand() % 10); ex->data = THINGTYPE_ACID; ex->model = Mod_ForName("models/sucwp2p.mdl", true); ex->skin = 0; VectorScale(ex->avel, 4.0, ex->avel); ex->startTime = cl.time; ex->endTime = ex->startTime + 4.0; } S_StartSound (TempSoundChannel(), 0, cl_sfx_acidhit, pos, 1, 1); break; case TE_FIREWALL: { float travelAng, travelPitch; float fireCounts; vec3_t endPos, curPos, posAdd; dlight_t *dlx; mleaf_t *l; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); travelAng = MSG_ReadByte()*6.28/256.0; travelPitch = MSG_ReadByte()*6.28/256.0; fireCounts = MSG_ReadByte(); dlx = CL_AllocDlight (0); VectorCopy (pos, dlx->origin); dlx->radius = 200 + (rand() & 31); dlx->die = cl.time + 0.001; q_sincosrad(travelAng, &sinval, &cosval); endPos[0] = cosval; endPos[1] = sinval; q_sincosrad(travelPitch, &sinval, &cosval); endPos[0] *= cosval; endPos[1] *= cosval; endPos[2] = sinval; VectorScale(endPos, 450, endPos); VectorAdd(endPos, pos, endPos); VectorCopy(pos, curPos); VectorSubtract(endPos, pos, posAdd); VectorScale(posAdd, .125, posAdd); for (i = 0; i < fireCounts; i++) { ex = CL_AllocExplosion(); VectorCopy(curPos, ex->origin); switch (rand() % 3) { case 0: ex->model = Mod_ForName("models/firewal1.spr", true); break; case 1: ex->model = Mod_ForName("models/firewal5.spr", true); break; case 2: ex->model = Mod_ForName("models/firewal4.spr", true); break; } ex->startTime = cl.time + .3/8.0 * i; ex->endTime = ex->startTime + ex->model->numframes * 0.05; do { // I dunno how expensive this is, but it kind of sucks anyway around it... l = Mod_PointInLeaf (ex->origin, cl.worldmodel); if (l->contents == CONTENTS_EMPTY) ex->origin[2] -= 16; else ex->origin[2] += 16; } while (l->contents == CONTENTS_EMPTY); ex->origin[0] += (rand() % 8) - 4; ex->origin[1] += (rand() % 8) - 4; ex->origin[2] += (rand() % 6) + 21; ex = CL_AllocExplosion(); VectorCopy(curPos, ex->origin); ex->origin[0] += (rand() % 8) - 4; ex->origin[1] += (rand() % 8) - 4; ex->origin[2] += (rand() % 6) - 3; ex->model = Mod_ForName("models/flamestr.spr", true); ex->startTime = cl.time + .3/8.0 * i; ex->endTime = ex->startTime + ex->model->numframes * 0.05; ex->flags |= DRF_TRANSLUCENT; VectorAdd(curPos, posAdd, curPos); } } break; case TE_FIREWALL_IMPACT: // Add in the actual explosion pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); i = (host_frametime < 0.07) ? 0 : 8; for ( ; i < 12; i++) { ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->origin[0] += (rand() % 32) - 16; ex->origin[1] += (rand() % 32) - 16; ex->origin[2] += (rand() % 32) - 16; ex->model = Mod_ForName("models/fboom.spr", true); ex->startTime = cl.time + ((rand() % 150) / 200); ex->endTime = ex->startTime + ex->model->numframes * 0.05; } S_StartSound (TempSoundChannel(), 0, cl_sfx_flameend, pos, 1, 1); break; case TE_HWBONERIC: pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); cnt = MSG_ReadByte (); R_RunParticleEffect4 (pos, 3, 368 + (rand() % 16), pt_grav, cnt); rnd = rand() % 100; if (rnd > 95) S_StartSound (TempSoundChannel(), 0, cl_sfx_ric1, pos, 1, 1); else if (rnd > 91) S_StartSound (TempSoundChannel(), 0, cl_sfx_ric2, pos, 1, 1); else if (rnd > 87) S_StartSound (TempSoundChannel(), 0, cl_sfx_ric3, pos, 1, 1); break; case TE_POWERFLAME: { float travelAng, travelPitch; float fireCounts; dlight_t *dlx; vec3_t endPos, curPos, posAdd; vec3_t angles, forward, right, up; float svTime; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); travelAng = MSG_ReadByte()*6.28/256.0; travelPitch = MSG_ReadByte()*6.28/256.0; fireCounts = MSG_ReadByte(); svTime = MSG_ReadLong(); angles[0] = travelPitch*360/(2*M_PI); angles[1] = travelAng*360/(2*M_PI); angles[2] = 0; AngleVectors(angles, forward, right, up); dlx = CL_AllocDlight (0); VectorCopy (pos, dlx->origin); dlx->radius = 200 + (rand() & 31); dlx->die = cl.time + 0.001; q_sincosrad(travelAng, &sinval, &cosval); endPos[0] = cosval; endPos[1] = sinval; q_sincosrad(travelPitch, &sinval, &cosval); endPos[0] *= cosval; endPos[1] *= cosval; endPos[2] = sinval; VectorScale(endPos, 375, endPos); VectorAdd(endPos, pos, endPos); VectorCopy(pos, curPos); VectorSubtract(endPos, pos, posAdd); VectorScale(posAdd, .125, posAdd); for (i = 0; i < fireCounts; i++) { q_sincosrad((svTime + (i*.3/8.0))*8, &sinval, &cosval); sinval *= 10; cosval *= 10; ex = CL_AllocExplosion(); VectorCopy(curPos, ex->origin); VectorMA(ex->origin, cosval, right, ex->origin); VectorMA(ex->origin, sinval, up, ex->origin); ex->origin[0] += (rand() % 8) - 4; ex->origin[1] += (rand() % 8) - 4; ex->origin[2] += (rand() % 6) - 3; ex->model = Mod_ForName("models/flamestr.spr", true); ex->startTime = cl.time + .3/8.0 * i; ex->endTime = ex->startTime + ex->model->numframes * 0.05; ex->flags |= DRF_TRANSLUCENT; ex->velocity[0] = 0; ex->velocity[1] = 0; ex->velocity[2] = 0; VectorMA(ex->velocity, cosval * 4.0, right, ex->velocity); VectorMA(ex->velocity, sinval * 4.0, up, ex->velocity); ex = CL_AllocExplosion(); VectorCopy(curPos, ex->origin); VectorMA(ex->origin, -cosval, right, ex->origin); VectorMA(ex->origin, -sinval, up, ex->origin); ex->origin[0] += (rand() % 8) - 4; ex->origin[1] += (rand() % 8) - 4; ex->origin[2] += (rand() % 6) - 3; ex->model = Mod_ForName("models/flamestr.spr", true); ex->startTime = cl.time + .3/8.0 * i; ex->endTime = ex->startTime + ex->model->numframes * 0.05; ex->flags |= DRF_TRANSLUCENT; ex->velocity[0] = 0; ex->velocity[1] = 0; ex->velocity[2] = 0; VectorMA(ex->velocity, -cosval * 4.0, right, ex->velocity); VectorMA(ex->velocity, -sinval * 4.0, up, ex->velocity); VectorAdd(curPos, posAdd, curPos); } } break; case TE_BLOODRAIN: { float travelAng, travelPitch; float trailLen; unsigned char health; vec3_t angles, forward, right, up; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); travelAng = MSG_ReadByte()*6.28/256.0; travelPitch = MSG_ReadByte()*6.28/256.0; trailLen = MSG_ReadByte(); health = MSG_ReadByte(); q_sincosrad(travelAng, &sinval, &cosval); vel[0] = cosval; vel[1] = sinval; q_sincosrad(travelPitch, &sinval, &cosval); vel[0] *= cosval; vel[1] *= cosval; vel[2] = sinval; VectorScale(vel, 800, vel); ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/sucwp1p.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + trailLen*.3/240.0; ex->angles[0] = travelPitch*360/6.28; ex->angles[1] = travelAng*360/6.28; ex->scale = health; VectorCopy(vel, ex->velocity); ex->frameFunc = updateBloodRain; ex->exflags |= EXFLAG_COLLIDE; if (health > 90) { angles[0] = travelPitch*360/(2*M_PI); angles[1] = travelAng*360/(2*M_PI); angles[2] = 0; AngleVectors(angles, forward, right, up); ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); VectorMA(ex->origin, 7, right, ex->origin); ex->model = Mod_ForName("models/sucwp1p.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + trailLen*.3/240.0; ex->angles[0] = travelPitch*360/6.28; ex->angles[1] = travelAng*360/6.28; ex->scale = health - 90; VectorCopy(vel, ex->velocity); ex->frameFunc = updateBloodRain; ex->exflags |= EXFLAG_COLLIDE; ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); VectorMA(ex->origin, -7, right, ex->origin); ex->model = Mod_ForName("models/sucwp1p.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + trailLen*.3/240.0; ex->angles[0] = travelPitch*360/6.28; ex->angles[1] = travelAng*360/6.28; ex->scale = health - 90; VectorCopy(vel, ex->velocity); ex->frameFunc = updateBloodRain; ex->exflags |= EXFLAG_COLLIDE; } } break; case TE_AXE: { float travelAng, travelPitch; float trailLen; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); travelAng = MSG_ReadByte()*6.28/256.0; travelPitch = MSG_ReadByte()*6.28/256.0; trailLen = MSG_ReadByte() * .01; q_sincosrad(travelAng, &sinval, &cosval); vel[0] = cosval; vel[1] = sinval; q_sincosrad(travelPitch, &sinval, &cosval); vel[0] *= cosval; vel[1] *= cosval; vel[2] = sinval; VectorScale(vel, 1100, vel); ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/axblade.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + trailLen*.3; ex->angles[0] = travelPitch*360/6.28; ex->angles[1] = travelAng*360/6.28; VectorCopy(vel, ex->velocity); ex->exflags |= EXFLAG_COLLIDE; ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/axtail.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + trailLen*.3; ex->angles[0] = travelPitch*360/6.28; ex->angles[1] = travelAng*360/6.28; VectorCopy(vel, ex->velocity); ex->exflags |= EXFLAG_COLLIDE; ex->exflags |= EXFLAG_STILL_FRAME; ex->data = 0; ex->flags |= MLS_ABSLIGHT|DRF_TRANSLUCENT; ex->abslight = 128; ex->skin = 0; } break; case TE_PURIFY2_MISSILE: { float travelAng, travelPitch; float trailLen; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); travelAng = MSG_ReadByte()*6.28/256.0; travelPitch = MSG_ReadByte()*6.28/256.0; trailLen = MSG_ReadByte() * .01; q_sincosrad(travelAng, &sinval, &cosval); vel[0] = cosval; vel[1] = sinval; q_sincosrad(travelPitch, &sinval, &cosval); vel[0] *= cosval; vel[1] *= cosval; vel[2] = sinval; VectorScale(vel, 1000, vel); ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/drgnball.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + trailLen*.3; ex->angles[0] = travelPitch*360/6.28; ex->angles[1] = travelAng*360/6.28; VectorCopy(vel, ex->velocity); ex->exflags |= EXFLAG_COLLIDE; ex->frameFunc = updatePurify2; ex->scale = 150; } break; case TE_SWORD_SHOT: { float travelAng, travelPitch; float trailLen; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); travelAng = MSG_ReadByte()*6.28/256.0; travelPitch = MSG_ReadByte()*6.28/256.0; trailLen = MSG_ReadByte() * .01; q_sincosrad(travelAng, &sinval, &cosval); vel[0] = cosval; vel[1] = sinval; q_sincosrad(travelPitch, &sinval, &cosval); vel[0] *= cosval; vel[1] *= cosval; vel[2] = sinval; VectorScale(vel, 1200, vel); ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/vorpshot.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + trailLen*.3; ex->angles[0] = travelPitch*360/6.28; ex->angles[1] = travelAng*360/6.28; VectorCopy(vel, ex->velocity); ex->exflags |= EXFLAG_COLLIDE; ex->frameFunc = updateSwordShot; ex->scale = 100; ex->exflags |= EXFLAG_STILL_FRAME; ex->data = 16 + ((int)(cl.time * 20.0)%13); } break; case TE_ICESHOT: { float travelAng, travelPitch; float trailLen; explosion_t *ex2; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); travelAng = MSG_ReadByte()*6.28/256.0; travelPitch = MSG_ReadByte()*6.28/256.0; trailLen = MSG_ReadByte() * .01; q_sincosrad(travelAng, &sinval, &cosval); vel[0] = cosval; vel[1] = sinval; q_sincosrad(travelPitch, &sinval, &cosval); vel[0] *= cosval; vel[1] *= cosval; vel[2] = sinval; VectorScale(vel, 1200, vel); ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/iceshot1.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + trailLen*.3; ex->angles[0] = travelPitch*360/6.28; ex->angles[1] = travelAng*360/6.28; VectorCopy(vel, ex->velocity); ex->exflags |= EXFLAG_COLLIDE; ex->frameFunc = updateIceShot; ex->scale = 100; ex->exflags = EXFLAG_ROTATE; ex->avel[0] = 425; ex->avel[1] = 425; ex->avel[2] = 425; ex2 = ex; ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/iceshot2.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + trailLen*.3; ex->angles[0] = travelPitch*360/6.28; ex->angles[1] = travelAng*360/6.28; VectorCopy(vel, ex->velocity); ex->exflags |= EXFLAG_COLLIDE; //ex->frameFunc = updateSwordShot; ex->scale = 200; ex->flags |= MLS_ABSLIGHT|DRF_TRANSLUCENT; ex->abslight = 128; ex->exflags = EXFLAG_ROTATE; VectorCopy(ex2->avel, ex->avel); } break; case TE_METEOR: { float travelAng, travelPitch; float trailLen; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); travelAng = MSG_ReadByte()*6.28/256.0; travelPitch = MSG_ReadByte()*6.28/256.0; trailLen = MSG_ReadByte() * .01; q_sincosrad(travelAng, &sinval, &cosval); vel[0] = cosval; vel[1] = sinval; q_sincosrad(travelPitch, &sinval, &cosval); vel[0] *= cosval; vel[1] *= cosval; vel[2] = sinval; VectorScale(vel, 1000, vel); ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/tempmetr.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + trailLen*.3; ex->angles[0] = travelPitch*360/6.28; ex->angles[1] = travelAng*360/6.28; VectorCopy(vel, ex->velocity); ex->exflags |= EXFLAG_COLLIDE; ex->frameFunc = updateMeteor; ex->scale = 100; ex->exflags = EXFLAG_ROTATE; ex->avel[0] = 200; ex->avel[1] = 200; ex->avel[2] = 200; } break; case TE_LIGHTNINGBALL: { float travelAng, travelPitch; float trailLen, speed; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); travelAng = MSG_ReadByte()*6.28/256.0; travelPitch = MSG_ReadByte()*6.28/256.0; speed = MSG_ReadShort(); trailLen = MSG_ReadByte() * .01; // light dl = CL_AllocDlight (0); VectorCopy (pos, dl->origin); dl->radius = 350; dl->die = cl.time + 0.5; dl->decay = 300; dl->color[0] = 0.2; dl->color[1] = 0.1; dl->color[2] = 0.05; dl->color[3] = 0.7; q_sincosrad(travelAng, &sinval, &cosval); vel[0] = cosval; vel[1] = sinval; q_sincosrad(travelPitch, &sinval, &cosval); vel[0] *= cosval; vel[1] *= cosval; vel[2] = sinval; VectorScale(vel, speed, vel); ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/lball.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + trailLen*.2; ex->angles[0] = travelPitch*360/6.28; ex->angles[1] = travelAng*360/6.28; VectorCopy(vel, ex->velocity); ex->exflags |= EXFLAG_COLLIDE; //ex->frameFunc = updateMeteor; //ex->scale = 230; //ex->exflags = EXFLAG_ROTATE; //ex->avel[0] = 200; //ex->avel[1] = 200; //ex->avel[2] = 200; } break; case TE_MEGAMETEOR: { float travelAng, travelPitch; float trailLen; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); travelAng = MSG_ReadByte()*6.28/256.0; travelPitch = MSG_ReadByte()*6.28/256.0; trailLen = MSG_ReadByte() * .01; q_sincosrad(travelAng, &sinval, &cosval); vel[0] = cosval; vel[1] = sinval; q_sincosrad(travelPitch, &sinval, &cosval); vel[0] *= cosval; vel[1] *= cosval; vel[2] = sinval; VectorScale(vel, 1600, vel); ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/tempmetr.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + trailLen*.3; ex->angles[0] = travelPitch*360/6.28; ex->angles[1] = travelAng*360/6.28; VectorCopy(vel, ex->velocity); ex->exflags |= EXFLAG_COLLIDE; ex->frameFunc = updateMeteor; ex->scale = 230; ex->flags |= DRF_TRANSLUCENT; ex->exflags = EXFLAG_ROTATE; ex->avel[0] = 200; ex->avel[1] = 200; ex->avel[2] = 200; } break; case TE_CUBEBEAM: { int ent, ent2, tag, flags, skin; vec3_t source, dest; float duration; entity_state_t *state; entity_state_t *state2; type = TE_STREAM_COLORBEAM; ent = MSG_ReadShort(); ent2 = MSG_ReadShort(); flags = 0; tag = 0; duration = 0.1; skin = rand() % 5; state = FindState(ent); state2 = FindState(ent2); if (state || state2) { if (state) { VectorCopy(state->origin, source); } else //don't know where the damn cube is--prolly { //won't see beam anyway then, so put it all //at the target VectorCopy(state2->origin, source); source[2] += 10; } if (state2) { //in case they're both valid, copy me again VectorCopy(state2->origin, dest); dest[2] += 30; } else //don't know where the damn victim is--prolly { //won't see beam anyway then, so put it all //at the cube VectorCopy(source, dest); dest[2] += 10; } S_StartSound (TempSoundChannel(), 0, cl_sfx_sunstaff, source, 1, 1); S_StartSound (TempSoundChannel(), 0, cl_sfx_sunhit, dest, 1, 1); R_SunStaffTrail(dest, source); CreateStream(TE_STREAM_COLORBEAM, ent, flags, tag, duration, skin, source, dest); } } break; case TE_LIGHTNINGEXPLODE: { int ent; stream_t *stream; qmodel_t *models[2]; entity_state_t *state; float tempAng, tempPitch; ent = MSG_ReadShort(); state = FindState(ent); (void) state; /* variable set but not used */ pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); if (rand() & 1) S_StartSound (TempSoundChannel(), 0, cl_sfx_lightning1, pos, 1, 1); else S_StartSound (TempSoundChannel(), 0, cl_sfx_lightning2, pos, 1, 1); for (i = 0; i < 10; i++) { // make some lightning models[0] = Mod_ForName("models/stlghtng.mdl", true); if ((stream = NewStream(ent, i)) == NULL) { Con_Printf("stream list overflow\n"); return; } stream->type = TE_STREAM_LIGHTNING; stream->tag = i; stream->flags = i; stream->entity = ent; stream->skin = 0; stream->models[0] = models[0]; stream->endTime = cl.time + 0.5; stream->lastTrailTime = 0; tempAng = (rand() % 628) / 100.0; tempPitch = (rand() % 628) / 100.0; VectorCopy(pos, stream->source); q_sincosrad(tempAng, &sinval, &cosval); stream->dest[0] = cosval; stream->dest[1] = sinval; q_sincosrad(tempPitch, &sinval, &cosval); stream->dest[0] *= cosval; stream->dest[1] *= cosval; stream->dest[2] = sinval; VectorScale(stream->dest, 75, stream->dest); VectorAdd(stream->dest, stream->source, stream->dest); } } break; case TE_ACID_BALL_FLY: { float travelAng, travelPitch; float trailLen; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); travelAng = MSG_ReadByte()*6.28/256.0; travelPitch = MSG_ReadByte()*6.28/256.0; trailLen = MSG_ReadByte() * .01; q_sincosrad(travelAng, &sinval, &cosval); vel[0] = cosval; vel[1] = sinval; q_sincosrad(travelPitch, &sinval, &cosval); vel[0] *= cosval; vel[1] *= cosval; vel[2] = sinval; VectorScale(vel, 850, vel); ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/sucwp2p.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + trailLen*.3; ex->angles[0] = travelPitch*360/6.28; ex->angles[1] = travelAng*360/6.28; VectorCopy(vel, ex->velocity); ex->exflags |= EXFLAG_COLLIDE; ex->frameFunc = updateAcidBall; } break; case TE_ACID_BLOB_FLY: { float travelAng, travelPitch; float trailLen; pos[0] = MSG_ReadCoord(); pos[1] = MSG_ReadCoord(); pos[2] = MSG_ReadCoord(); travelAng = MSG_ReadByte()*6.28/256.0; travelPitch = MSG_ReadByte()*6.28/256.0; trailLen = MSG_ReadByte() * .01; q_sincosrad(travelAng, &sinval, &cosval); vel[0] = cosval; vel[1] = sinval; q_sincosrad(travelPitch, &sinval, &cosval); vel[0] *= cosval; vel[1] *= cosval; vel[2] = sinval; VectorScale(vel, 1000, vel); ex = CL_AllocExplosion(); VectorCopy(pos, ex->origin); ex->model = Mod_ForName("models/sucwp2p.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + trailLen*.3; ex->angles[0] = travelPitch*360/6.28; ex->angles[1] = travelAng*360/6.28; VectorCopy(vel, ex->velocity); ex->exflags |= EXFLAG_COLLIDE; ex->frameFunc = updateAcidBlob; ex->scale = 230; //ex->exflags = EXFLAG_ROTATE; //ex->avel[0] = 200; //ex->avel[1] = 200; //ex->avel[2] = 200; } break; case TE_CHAINLIGHTNING: { vec3_t points[12]; int numTargs = 0; int ent, oldNum, temp; stream_t *stream; qmodel_t *models[2]; ent = MSG_ReadShort(); do { points[numTargs][0] = MSG_ReadCoord(); points[numTargs][1] = MSG_ReadCoord(); points[numTargs][2] = MSG_ReadCoord(); oldNum = numTargs; if (points[numTargs][0] || points[numTargs][1] || points[numTargs][2]) { if (numTargs < 9) numTargs++; } } while (points[oldNum][0] || points[oldNum][1] || points[oldNum][2]); if (numTargs == 0) break; for (temp = 0; temp < numTargs - 1; temp++) { // make the connecting lightning... models[0] = Mod_ForName("models/stlghtng.mdl", true); if ((stream = NewStream(ent, temp)) == NULL) { Con_Printf("stream list overflow\n"); return; } stream->type = TE_STREAM_LIGHTNING; stream->tag = temp; stream->flags = temp; stream->entity = ent; stream->skin = 0; stream->models[0] = models[0]; stream->endTime = cl.time + 0.3; stream->lastTrailTime = 0; VectorCopy(points[temp], stream->source); VectorCopy(points[temp + 1], stream->dest); ex = CL_AllocExplosion(); VectorCopy(points[temp+1], ex->origin); ex->model = Mod_ForName("models/vorpshok.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + .3; ex->flags |= MLS_ABSLIGHT;//|DRF_TRANSLUCENT; //ex->abslight = 128; ex->abslight = 224; ex->skin = rand() & 1; ex->scale = 150; ex->frameFunc = zapFrameFunc; //ex->frameFunc = SwordFrameFunc; //ex->flags = DRF_TRANSLUCENT;// | MLS_ABSLIGHT; //ex->angles[0] = travelPitch*360/6.28; //ex->angles[1] = travelAng*360/6.28; } } break; default: Sys_Error ("%s: bad type", __thisfunc__); } } /* ================= CL_NewTempEntity ================= */ entity_t *CL_NewTempEntity (void) { entity_t *ent; if (cl_numvisedicts == MAX_VISEDICTS) return NULL; ent = &cl_visedicts[cl_numvisedicts]; cl_numvisedicts++; ent->keynum = 0; memset (ent, 0, sizeof(*ent)); ent->colormap = vid.colormap; return ent; } /* ================= CL_UpdateBeams ================= */ static void CL_UpdateBeams (void) { entity_t *ent; float d, yaw, pitch, forward; beam_t *b; vec3_t dist, org; int i; // update lightning for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++) { if (!b->model || b->endtime < cl.time) continue; // if coming from the player, update the start position if (b->entity == cl.playernum+1) // entity 0 is the world { VectorCopy (cl.simorg, b->start); b->start[2] -= 22; // adjust for view height } // calculate pitch and yaw VectorSubtract (b->end, b->start, dist); if (dist[1] == 0 && dist[0] == 0) { yaw = 0; if (dist[2] > 0) pitch = 90; else pitch = 270; } else { yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; forward = Q_sqrt (dist[0]*dist[0] + dist[1]*dist[1]); pitch = (int) (atan2(dist[2], forward) * 180 / M_PI); if (pitch < 0) pitch += 360; } // add new entities for the lightning VectorCopy (b->start, org); d = VectorNormalizeFast(dist); while (d > 0) { ent = CL_NewTempEntity (); if (!ent) return; VectorCopy (org, ent->origin); ent->model = b->model; ent->angles[0] = pitch; ent->angles[1] = yaw; ent->angles[2] = rand() % 360; for (i = 0; i < 3; i++) org[i] += dist[i]*30; d -= 30; } } } /* ================= CL_UpdateExplosions ================= */ static void CL_UpdateExplosions (void) { int i, f; explosion_t *ex; entity_t *ent; mleaf_t *l; for (i = 0, ex = cl_explosions; i < MAX_EXPLOSIONS; i++, ex++) { if (!ex->model) continue; if (ex->exflags & EXFLAG_COLLIDE) { l = Mod_PointInLeaf (ex->origin, cl.worldmodel); if (l->contents != CONTENTS_EMPTY) { if (ex->removeFunc) ex->removeFunc(ex); ex->model = NULL; continue; } } // if we hit endTime, get rid of explosion // (i assume endTime is greater than startTime, etc) if (ex->endTime <= cl.time) { if (ex->removeFunc) ex->removeFunc(ex); ex->model = NULL; continue; } VectorCopy(ex->origin, ex->oldorg); // set the current frame so i finish anim at endTime if (ex->exflags & EXFLAG_STILL_FRAME) { // if it's a still frame, use the data field f = (int)ex->data; } else { f = (ex->model->numframes - 1) * (cl.time - ex->startTime) / (ex->endTime - ex->startTime); } // apply velocity ex->origin[0] += host_frametime * ex->velocity[0]; ex->origin[1] += host_frametime * ex->velocity[1]; ex->origin[2] += host_frametime * ex->velocity[2]; // apply acceleration ex->velocity[0] += host_frametime * ex->accel[0]; ex->velocity[1] += host_frametime * ex->accel[1]; ex->velocity[2] += host_frametime * ex->accel[2]; // add in angular velocity if (ex->exflags & EXFLAG_ROTATE) { VectorMA(ex->angles, host_frametime, ex->avel, ex->angles); } // you can set startTime to some point in the future // to delay the explosion showing up or thinking; // it'll still move, though if (ex->startTime > cl.time) continue; if (ex->frameFunc) ex->frameFunc(ex); // allow for the possibility for the frame func to reset startTime if (ex->startTime > cl.time) continue; // just incase the frameFunc eliminates the thingy here. if (ex->model == NULL) continue; ent = CL_NewTempEntity (); if (!ent) continue; VectorCopy (ex->origin, ent->origin); VectorCopy (ex->angles, ent->angles); ent->model = ex->model; ent->frame = f; ent->skinnum = ex->skin; ent->drawflags = ex->flags; if (ex->flags & MLS_ABSLIGHT) ent->abslight = ex->abslight; if (ex->scale) ent->scale = ex->scale; } } static void CL_UpdateStreams(void) { entity_t *ent; stream_t *stream; entity_state_t *state; vec3_t dist, org, discard, right, up; float cosTime = 0.0, sinTime = 0.0; // init to 0, make compiler happy float cos2Time = 0.0, sin2Time = 0.0, lifeTime = 0.0; // ditto float d, yaw, pitch, forward; int i, j, offset; // Update streams for (i = 0, stream = cl_Streams; i < MAX_STREAMS; i++, stream++) { if (!stream->models[0])// || stream->endTime < cl.time) continue; // inactive if (stream->endTime < cl.time) { // inactive if (stream->type != TE_STREAM_LIGHTNING && stream->type != TE_STREAM_LIGHTNING_SMALL) continue; else if (stream->endTime + 0.25 < cl.time) continue; } if (stream->flags & STREAM_ATTACHED && stream->endTime >= cl.time) { // Attach the start position to owner state = FindState(stream->entity); if (state) { VectorAdd(state->origin, stream->offset, stream->source); } } VectorSubtract(stream->dest, stream->source, dist); if (dist[1] == 0 && dist[0] == 0) { yaw = 0; if (dist[2] > 0) pitch = 90; else pitch = 270; } else { yaw = (int)(atan2(dist[1], dist[0])*180/M_PI); if (yaw < 0) yaw += 360; forward = Q_sqrt(dist[0]*dist[0]+dist[1]*dist[1]); pitch = (int)(atan2(dist[2], forward)*180/M_PI); if (pitch < 0) pitch += 360; } VectorCopy(stream->source, org); d = VectorNormalizeFast(dist); if (stream->type == TE_STREAM_SUNSTAFF2) { discard[YAW] = yaw; discard[PITCH] = pitch; discard[ROLL] = 0; AngleVectors(discard, discard, right, up); lifeTime = ((stream->endTime - cl.time)/.8); q_sincosrad(cl.time*5, &sinTime, &cosTime); q_sincosrad(cl.time*5 + 3.14, &sin2Time, &cos2Time); } if (stream->type == TE_STREAM_ICECHUNKS) { offset = (int)(cl.time*40)%30; for (j = 0; j < 3; j++) org[j] += dist[j]*offset; } while (d > 0) { ent = CL_NewTempEntity(); if (!ent) return; VectorCopy(org, ent->origin); ent->model = stream->models[0]; ent->angles[0] = pitch; ent->angles[1] = yaw; switch (stream->type) { case TE_STREAM_CHAIN: ent->angles[2] = 0; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; break; case TE_STREAM_SUNSTAFF1: ent->angles[2] = (int)(cl.time*10)%360; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; ent->scale = 50 + 100 * ((stream->endTime - cl.time)/.3); //ent->frame = (int)(cl.time*20)%20; ent = CL_NewTempEntity(); if (!ent) return; VectorCopy(org, ent->origin); ent->model = stream->models[1]; ent->angles[0] = pitch; ent->angles[1] = yaw; ent->angles[2] = (int)(cl.time*50)%360; ent->drawflags = MLS_ABSLIGHT|DRF_TRANSLUCENT; ent->abslight = 128; ent->scale = 50 + 100 * ((stream->endTime - cl.time)/.5); //stream->endTime = cl.time + 0.3; // FIXME break; case TE_STREAM_SUNSTAFF2: ent->angles[2] = (int)(cl.time*100)%360; ent->drawflags = MLS_ABSLIGHT|DRF_TRANSLUCENT; ent->abslight = 128; ent->scale = 100 + 150 * lifeTime; VectorMA(ent->origin, cosTime * (40 * lifeTime), right, ent->origin); VectorMA(ent->origin, sinTime * (40 * lifeTime), up, ent->origin); ent = CL_NewTempEntity(); if (!ent) return; VectorCopy(org, ent->origin); ent->model = stream->models[0]; ent->angles[0] = pitch; ent->angles[1] = yaw; ent->angles[2] = (int)(cl.time*100)%360; ent->drawflags = MLS_ABSLIGHT|DRF_TRANSLUCENT; ent->abslight = 128; ent->scale = 100 + 150 * lifeTime; VectorMA(ent->origin, cos2Time * (40 * lifeTime), right, ent->origin); VectorMA(ent->origin, sin2Time * (40 * lifeTime), up, ent->origin); for (j = 0; j < 2; j++) { ent = CL_NewTempEntity(); if (!ent) return; VectorCopy(org, ent->origin); if (j) { VectorMA(ent->origin, cos2Time * (40 * lifeTime), right, ent->origin); VectorMA(ent->origin, sin2Time * (40 * lifeTime), up, ent->origin); } else { VectorMA(ent->origin, cosTime * (40 * lifeTime), right, ent->origin); VectorMA(ent->origin, sinTime * (40 * lifeTime), up, ent->origin); } ent->model = stream->models[1]; ent->angles[0] = pitch; ent->angles[1] = yaw; ent->angles[2] = (int)(cl.time*20)%360; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; ent->scale = 100 + 150 * lifeTime; } break; case TE_STREAM_LIGHTNING: if (stream->endTime < cl.time) {//fixme: keep last non-translucent frame and angle ent->drawflags = MLS_ABSLIGHT|DRF_TRANSLUCENT; ent->abslight = 128 + (stream->endTime - cl.time)*192; } else { ent->angles[2] = rand() % 360; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; ent->frame = rand() % 6; } break; case TE_STREAM_LIGHTNING_SMALL: if (stream->endTime < cl.time) { ent->drawflags = MLS_ABSLIGHT|DRF_TRANSLUCENT; ent->abslight = 128 + (stream->endTime - cl.time)*192; } else { ent->angles[2] = rand() % 360; ent->frame = rand() % 6; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; } break; case TE_STREAM_FAMINE: ent->angles[2] = rand() % 360; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; ent->frame = 0; break; case TE_STREAM_COLORBEAM: ent->angles[2] = 0; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; ent->skinnum = stream->skin; break; case TE_STREAM_GAZE: ent->angles[2] = 0; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; ent->frame = (int)(cl.time*40)%36; break; case TE_STREAM_ICECHUNKS: ent->angles[2] = rand() % 360; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; ent->frame = rand() % 5; break; default: ent->angles[2] = 0; } for (j = 0; j < 3; j++) org[j] += dist[j]*30; d -= 30; } if (stream->type == TE_STREAM_SUNSTAFF1) { if (stream->lastTrailTime+0.2 < cl.time) { stream->lastTrailTime = cl.time; R_SunStaffTrail(stream->source, stream->dest); } ent = CL_NewTempEntity(); if (ent == NULL) return; VectorCopy(stream->dest, ent->origin); ent->model = stream->models[2]; ent->drawflags = MLS_ABSLIGHT; ent->abslight = 128; ent->scale = 80 + (rand() & 15); //ent->frame = (int)(cl.time*20)%20; ent = CL_NewTempEntity(); if (ent == NULL) return; VectorCopy(stream->dest, ent->origin); ent->model = stream->models[3]; ent->drawflags = MLS_ABSLIGHT|DRF_TRANSLUCENT; ent->abslight = 128; ent->scale = 150 + (rand() & 15); } } } /* ================= CL_UpdateTEnts ================= */ void CL_UpdateTEnts (void) { CL_UpdateBeams (); CL_UpdateExplosions (); CL_UpdateStreams(); CL_UpdateTargetBall(); } static void MultiGrenadeExplodeSound (explosion_t *ex) { //plug up all of -1's channels w/ grenade sounds if (! (rand() & 7)) { if (MultiGrenadeCurrentChannel >= MAX_DYNAMIC_CHANNELS || MultiGrenadeCurrentChannel < 0) MultiGrenadeCurrentChannel = 0; S_StartSound (TempSoundChannel(), MultiGrenadeCurrentChannel++, cl_sfx_explode, ex->origin, 1, 1); } ex->frameFunc=NULL; } static void MultiGrenadeThink (explosion_t *ex) {//FIXME: too messy explosion_t *missile; int attack_counter, number_explosions; float ftemp; VectorSet(ex->velocity, 0, 0, 0); VectorSet(ex->accel, 0, 0, 0); ex->frameFunc = MultiGrenadeExplodeSound; attack_counter = 0; number_explosions = (rand() & 2) + 6; while (attack_counter < number_explosions) { attack_counter++; missile = CL_AllocExplosion(); missile->frameFunc = MultiGrenadePieceThink; VectorCopy(ex->origin,missile->origin); if (rand() & 1) missile->model = Mod_ForName("models/gen_expl.spr", true); else missile->model = Mod_ForName("models/bg_expld.spr", true); switch (attack_counter % 3) { case 2: missile->frameFunc = MultiGrenadePieceThink; missile->velocity[0] = (rand() % 600) - 300; missile->velocity[1] = (rand() % 600) - 300; missile->velocity[2] = 0; //(rand() % 100) + 50; ftemp = (rand() / RAND_MAX * 0.5); missile->startTime = cl.time;// + 0.1 + ftemp - (ex->startTime - cl.time); ftemp = (rand() / RAND_MAX * 0.3) - 0.15; missile->endTime = missile->startTime + missile->model->numframes * 0.1 + ftemp; break; case 1: missile->frameFunc = MultiGrenadePiece2Think; missile->velocity[0] = (rand() % 80 ) - 40; missile->velocity[1] = (rand() % 80 ) - 40; missile->velocity[2] = (rand() % 150) + 150; ftemp = (rand() / RAND_MAX * 0.5); missile->startTime = cl.time; // + 0.1 + ftemp - (ex->startTime - cl.time); ftemp = (rand() / RAND_MAX * 0.3) - 0.15; missile->endTime = missile->startTime + missile->model->numframes * 0.1 + ftemp; break; default://some extra explosions for at first missile->frameFunc = NULL; missile->origin[0] += (rand() % 50) - 25; missile->origin[1] += (rand() % 50) - 25; missile->origin[2] += (rand() % 50) - 25; ftemp = (rand() / RAND_MAX * 0.2); missile->startTime = cl.time + ftemp; ftemp = (rand() / RAND_MAX * 0.2) - 0.1; missile->endTime = missile->startTime + missile->model->numframes * 0.1 + ftemp; break; } ftemp = (rand() / RAND_MAX * 0.2); missile->data = ex->data * (0.7 + ftemp); } } static void MultiGrenadePieceThink (explosion_t *ex) {//FIXME: too messy explosion_t *missile; int attack_counter, number_explosions; float ftemp; VectorSet(ex->velocity, 0, 0, 0); VectorSet(ex->accel, 0, 0, 0); ex->frameFunc = MultiGrenadeExplodeSound; if (ex->data < 70) ex->data = 70; ftemp = (rand() / RAND_MAX * 0.2) - 0.1; ex->startTime = cl.time + ((1 - (ex->data - 69) / 180.0) + ftemp - 0.3) * 1.25; if (ex->startTime < cl.time) ex->startTime = cl.time; ftemp = (rand() / RAND_MAX * 0.4) - 0.4; ex->endTime = ex->startTime + ex->model->numframes * 0.1 + ftemp; if (ex->model->numframes > 14) { ex->startTime -= 0.4; ex->endTime -= 0.4; } if (ex->data <= 71) return; attack_counter = 0; number_explosions = (rand() & 3) + 2; while (attack_counter < number_explosions) { attack_counter += 1; missile = CL_AllocExplosion(); missile->frameFunc = MultiGrenadePieceThink; VectorCopy(ex->origin,missile->origin); missile->origin[0] += (rand() % 100) - 50; missile->origin[1] += (rand() % 100) - 50; missile->origin[2] += (rand() % 10 ) - 4; ftemp = (rand() / RAND_MAX * 0.2); missile->data = ex->data * (0.7 + ftemp); if (rand() & 7) missile->model = Mod_ForName("models/bg_expld.spr", true); else missile->model = Mod_ForName("models/fl_expld.spr", true); ftemp = (rand() / RAND_MAX * 0.5); missile->startTime = cl.time + 0.01; missile->endTime = missile->startTime + missile->model->numframes * 0.1; } } static void MultiGrenadePiece2Think (explosion_t *ex) {//FIXME: too messy explosion_t *missile; int attack_counter, number_explosions; float ftemp; VectorSet(ex->velocity, 0, 0, 0); VectorSet(ex->accel, 0, 0, 0); ex->frameFunc = MultiGrenadeExplodeSound; if (ex->data < 70) ex->data = 70; ftemp = 0; //(rand() / RAND_MAX * 0.2) - 0.1; ex->startTime = cl.time + (((1 - (ex->data - 69) / 200.0) + ftemp) * 1.5) - 0.2; ftemp = (rand() / RAND_MAX * 0.4) - 0.2; ex->endTime = ex->startTime + ex->model->numframes * 0.1 + ftemp; if (ex->data <= 71) return; attack_counter = 0; number_explosions = (rand() % 3) + 2; while (attack_counter < number_explosions) { attack_counter += 1; missile = CL_AllocExplosion(); missile->frameFunc = MultiGrenadePiece2Think; VectorCopy(ex->origin,missile->origin); missile->origin[0] += (rand() % 30) - 15; missile->origin[1] += (rand() % 30) - 15; ftemp = rand() / RAND_MAX; missile->origin[2] += (ftemp * 7) + 30; missile->data = ex->data - ftemp * 10 - 20; if (rand() & 1) missile->model = Mod_ForName("models/gen_expl.spr", true); else missile->model = Mod_ForName("models/bg_expld.spr", true); ftemp = (rand() / RAND_MAX * 0.5); missile->startTime = cl.time + 0.01; missile->endTime = missile->startTime + missile->model->numframes * 0.1; } } static void ChunkThink(explosion_t *ex) { vec3_t oldorg; mleaf_t *l; int moving = 1; l = Mod_PointInLeaf (ex->origin, cl.worldmodel); if (l->contents != CONTENTS_EMPTY) // || in_solid == true { //collided with world VectorCopy(ex->oldorg, ex->origin); if ((int)ex->data == THINGTYPE_FLESH) { if (VectorNormalizeFast(ex->velocity) > 100.0) { // hit, now make a splash of blood vec3_t dmin = {-40, -40, 10}; vec3_t dmax = {40, 40, 40}; //R_RunParticleEffect4 (ex->origin, 300.0, 136 + (rand() % 5), pt_darken, 25); //R_RunParticleEffect2 (ex->origin, dmin, dmax, 136 + (rand() % 5), pt_fastgrav, 12); R_RunParticleEffect2 (ex->origin, dmin, dmax, 136 + (rand() % 5), pt_darken, 20); } } else if ((int)ex->data == THINGTYPE_ACID) { if (VectorNormalizeFast(ex->velocity) > 100.0) { // hit, now make a splash of acid // vec3_t dmin = {-40, -40, 10}; // vec3_t dmax = {40, 40, 40}; // R_RunParticleEffect2 (ex->origin, dmin, dmax, 136 + (rand() % 5), pt_darken, 20); // FIXME - These should be green if (! (rand() % 3)) S_StartSound (TempSoundChannel(), 0, cl_sfx_dropfizzle, ex->origin, 1, 1); } } ex->velocity[0] = 0; ex->velocity[1] = 0; ex->velocity[2] = 0; ex->avel[0] = 0; ex->avel[1] = 0; ex->avel[2] = 0; moving = 0; } if (cl.time + host_frametime * 5 > ex->endTime) { // chunk leaves in 5 frames about switch ((int)ex->data) { case THINGTYPE_METEOR: if (cl.time + host_frametime * 4 < ex->endTime) ex->abslight = 200; // just crossed the threshold else ex->abslight -= 35; ex->flags |= DRF_TRANSLUCENT|MLS_ABSLIGHT; ex->scale *= 1.4; ex->angles[0] += 20; break; default: ex->scale *= .8; break; } } ex->velocity[2] -= host_frametime * movevars.gravity; // this is gravity switch ((int)ex->data) { case THINGTYPE_GREYSTONE: break; case THINGTYPE_WOOD: break; case THINGTYPE_METAL: break; case THINGTYPE_FLESH: if (moving) R_RocketTrail (ex->oldorg, ex->origin, rt_blood); break; case THINGTYPE_FIRE: break; case THINGTYPE_CLAY: break; case THINGTYPE_LEAVES: break; case THINGTYPE_HAY: break; case THINGTYPE_BROWNSTONE: case THINGTYPE_DIRT: break; case THINGTYPE_CLOTH: break; case THINGTYPE_WOOD_LEAF: break; case THINGTYPE_WOOD_METAL: break; case THINGTYPE_WOOD_STONE: break; case THINGTYPE_METAL_STONE: break; case THINGTYPE_METAL_CLOTH: break; case THINGTYPE_WEBS: break; case THINGTYPE_GLASS: break; case THINGTYPE_ICE: ex->velocity[2] += host_frametime * movevars.gravity * 0.5; // lower gravity for ice chunks if (moving) R_RocketTrail (ex->oldorg, ex->origin, rt_ice); break; case THINGTYPE_CLEARGLASS: break; case THINGTYPE_REDGLASS: break; case THINGTYPE_ACID: if (moving) R_RocketTrail (ex->oldorg, ex->origin, 16); break; case THINGTYPE_METEOR: VectorCopy(ex->oldorg, oldorg); if (!moving) { // resting meteors still give off smoke oldorg[0] += (rand() % 7) - 3; oldorg[1] += (rand() % 7) - 3; oldorg[2] += 16; } R_RocketTrail (oldorg, ex->origin, 1); break; case THINGTYPE_GREENFLESH: if (moving) R_RocketTrail (ex->oldorg, ex->origin, 16); break; } } static void BubbleThink(explosion_t *ex) { mleaf_t *l; l = Mod_PointInLeaf (ex->origin, cl.worldmodel); if (l->contents == CONTENTS_WATER) { //still in water if (ex->data < cl.time)//change course { ex->velocity[0] += (rand() % 20) - 10; ex->velocity[1] += (rand() % 20) - 10; ex->velocity[2] += (rand() % 10) + 10; if (ex->velocity[0] > 10) ex->velocity[0] = 5; else if (ex->velocity[0] < -10) ex->velocity[0] = -5; if (ex->velocity[1] > 10) ex->velocity[1] = 5; else if (ex->velocity[1] < -10) ex->velocity[1] = -5; if (ex->velocity[2] < 10) ex->velocity[2] = 15; else if (ex->velocity[2] > 30) ex->velocity[2] = 25; ex->data += 0.5; } } else { ex->endTime= cl.time; } } static void MissileFlashThink(explosion_t *ex) { ex->abslight-=(0.05*256); ex->scale+=5; if (ex->abslight < (0.05*256)) ex->endTime = ex->startTime; //remove } static void TeleportFlashThink(explosion_t *ex) { ex->scale -= 15; if (ex->scale < 10) ex->endTime = ex->startTime; } // remove tent if not in open air static void CheckSpaceThink(explosion_t *ex) { mleaf_t *l; l = Mod_PointInLeaf (ex->origin, cl.worldmodel); if (l->contents != CONTENTS_EMPTY) ex->endTime = ex->startTime; } // functions to create tempents from client effect calls void CreateRavenExplosions(vec3_t pos) { explosion_t *ex; ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); ex->velocity[2] = 8; ex->model = Mod_ForName("models/whtsmk1.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + HX_FRAME_TIME * 10; ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); ex->origin[2] -= 5; ex->velocity[2] = 8; ex->model = Mod_ForName("models/whtsmk1.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + HX_FRAME_TIME * 10; ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); ex->origin[2] -= 10; ex->velocity[2] = 8; ex->model = Mod_ForName("models/whtsmk1.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + HX_FRAME_TIME * 10; } void CreateRavenDeath(vec3_t pos) { explosion_t *ex; ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); ex->velocity[1] = 8; ex->velocity[2] = -10; ex->model = Mod_ForName("models/whtsmk1.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + HX_FRAME_TIME * 10; ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); ex->velocity[2] = -10; ex->model = Mod_ForName("models/redsmk1.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + HX_FRAME_TIME * 10; ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); ex->velocity[1] = -8; ex->velocity[2] = -10; ex->model = Mod_ForName("models/whtsmk1.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + HX_FRAME_TIME * 10; S_StartSound(TempSoundChannel(), 1, cl_sfx_ravendie, pos, 1, 1); } void CreateExplosionWithSound(vec3_t pos) { explosion_t *ex; ex = CL_AllocExplosion (); VectorCopy(pos, ex->origin); ex->startTime = cl.time; ex->endTime = ex->startTime + HX_FRAME_TIME * 10; ex->model = Mod_ForName("models/sm_expld.spr", true); S_StartSound (TempSoundChannel(), 1, cl_sfx_explode, pos, 1, 1); } static void SwordFrameFunc(explosion_t *ex) { ex->scale = (ex->endTime - cl.time)*150 + 1; if (((int)(cl.time * 20.0)) % 2) ex->skin = 0; else ex->skin = 1; } static void zapFrameFunc(explosion_t *ex) { ex->scale = (ex->endTime - cl.time)*(150/.3) + 1; //ex->abslight = (224/.3) * (ex->endTime - cl.time) + 1; if (((int)(cl.time * 20.0)) % 2) ex->skin = 0; else ex->skin = 1; } static void fireBallUpdate(explosion_t *ex) { ex->scale = (int)(((cl.time - ex->startTime) / 1.0) * 250) + 1; } static void sunBallUpdate(explosion_t *ex) { ex->scale = 121 - (int)(((cl.time - ex->startTime) / .8) * 120); } static void sunPowerUpdate(explosion_t *ex) { } #if 0 /* not used */ static void purify1Update(explosion_t *ex) { R_RocketTrail (ex->oldorg, ex->origin, rt_purify); //R_RocketTrail (ex->oldorg, ex->origin, rt_setstaff); } #endif void MeteorBlastThink(explosion_t *ex) { int i, maxI; mleaf_t *l; explosion_t *ex2; vec3_t tempVect, oldPos; int hitWall = 0; R_RocketTrail (ex->oldorg, ex->origin, rt_fireball); ex->data -= 1600 * host_frametime; // decrease distance, roughly... VectorClear(oldPos); // pacify compiler. if (ex->data <= 0) { // ran out of juice VectorCopy(ex->origin, oldPos); hitWall = 1; } else { VectorCopy(ex->oldorg, tempVect); for (i = 0; (i < 10) && (!hitWall); i++) { VectorCopy(tempVect, oldPos); VectorScale(ex->origin, .1 * (i+1), tempVect); VectorMA(tempVect, 1.0 - (.1 * (i+1)), ex->oldorg, tempVect); l = Mod_PointInLeaf (tempVect, cl.worldmodel); if (l->contents != CONTENTS_EMPTY) hitWall = 1; } } if (hitWall) { //collided with world VectorCopy(oldPos, ex->origin); maxI = (host_frametime <= 0.05) ? 12:5; for (i = 0; i < maxI; i++) { ex2 = CL_AllocExplosion(); VectorCopy(ex->origin, ex2->origin); ex2->origin[0] += (rand() % 10) - 5; ex2->origin[1] += (rand() % 10) - 5; ex2->origin[2] += (rand() % 10); ex2->velocity[0] = (ex2->origin[0] - ex->origin[0])*12; ex2->velocity[1] = (ex2->origin[1] - ex->origin[1])*12; ex2->velocity[2] = (ex2->origin[2] - ex->origin[2])*12; switch (rand() % 4) { case 0: case 1: ex2->model = Mod_ForName("models/sm_expld.spr", true); break; case 2: ex2->model = Mod_ForName("models/bg_expld.spr", true); break; case 3: ex2->model = Mod_ForName("models/gen_expl.spr", true); break; } ex2->flags |= MLS_ABSLIGHT|DRF_TRANSLUCENT; ex2->abslight = 160 + (rand() % 64); ex2->skin = 0; ex2->scale = 80 + (rand() % 40); ex2->startTime = cl.time + ((rand() % 30) / 200.0); ex2->endTime = ex2->startTime + ex2->model->numframes * 0.03; } if (rand() & 1) S_StartSound (TempSoundChannel(), 0, cl_sfx_axeExplode, ex->origin, 1, 1); ex->model = NULL; ex->endTime = cl.time; } } static void MeteorCrushSpawnThink(explosion_t *ex) { float chance; explosion_t *ex2; if (host_frametime <= 0.05) chance = (host_frametime / .4 * 16); else chance = (host_frametime / .4 * 8); while (chance > 0) { if (chance < 1.0) { // take care of fractional numbers of meteors... if (rand() % 100 > chance * 100) return; } ex2 = CL_AllocExplosion(); VectorCopy(ex->origin, ex2->origin); ex2->origin[0] += (rand() % 160) - 80; ex2->origin[1] += (rand() % 160) - 80; ex2->model = Mod_ForName("models/tempmetr.mdl", true); ex2->startTime = cl.time; ex2->endTime = ex2->startTime + 2.0; ex2->frameFunc = MeteorBlastThink; ex2->exflags = EXFLAG_ROTATE; ex2->avel[0] = 800; ex2->avel[1] = 800; ex2->avel[2] = 800; // ex2->velocity[0] = (rand() % 1200) - 600; // ex2->velocity[1] = (rand() % 1200) - 600; ex2->velocity[0] = (rand() % 180) - 90; ex2->velocity[1] = (rand() % 180) - 90; ex2->velocity[2] = -1600 - (rand() % 200); ex2->data = ex->data; // stores how far the thingy can travel max ex2->scale = 200 + (rand() % 120); chance -= 1.0; } } static void updateBloodRain(explosion_t *ex) { R_RocketTrail (ex->oldorg, ex->origin, rt_blood); ex->scale -= host_frametime / .3 * 60; if (ex->scale <= 0) ex->endTime = cl.time - 1; } static void updatePurify2(explosion_t *ex) { explosion_t *ex2; int numSprites; R_RocketTrail (ex->oldorg, ex->origin, rt_purify); if (host_frametime <= 0.05) numSprites = 20; else numSprites = 8; if ((rand() % 100) / 100.0 < numSprites * host_frametime) { ex2 = CL_AllocExplosion(); VectorCopy(ex->origin, ex2->origin); ex2->model = Mod_ForName("models/ring.mdl", true); ex2->startTime = cl.time; ex2->endTime = ex2->startTime + 1.2; ex2->scale = 150; VectorCopy(ex->angles, ex2->angles); ex2->angles[2] += 90; ex2->exflags |= EXFLAG_STILL_FRAME; ex2->data = 0; ex2->skin = 0; ex2->frameFunc = SmokeRingFrameFunc; ex2->velocity[2] = 15.0; ex2->flags |= DRF_TRANSLUCENT | SCALE_ORIGIN_CENTER; } } static void updateSwordShot(explosion_t *ex) { int testVal; R_RocketTrail (ex->oldorg, ex->origin, rt_vorpal); ex->data = 16 + ((int)(cl.time * 20.0)%13); ex->flags |= MLS_ABSLIGHT; ex->abslight = 128; testVal = (int)(cl.time * 20.0); if (testVal % 2) ex->skin = 0; else ex->skin = 1; } static void updateIceShot(explosion_t *ex) { R_RocketTrail (ex->oldorg, ex->origin, rt_ice); } static void updateMeteor(explosion_t *ex) { R_RocketTrail (ex->oldorg, ex->origin, rt_smoke); } static void updateAcidBlob(explosion_t *ex) { explosion_t *ex2; int testVal, testVal2; R_RocketTrail (ex->oldorg, ex->origin, rt_acidball); testVal = (int)(cl.time * 10.0); testVal2 = (int)((cl.time - host_frametime)*10.0); if (testVal != testVal2) { if (! (testVal % 2)) { ex2 = CL_AllocExplosion(); VectorCopy(ex->origin, ex2->origin); ex2->model = Mod_ForName("models/muzzle1.spr", true); ex2->startTime = cl.time; ex2->endTime = ex2->startTime + .4; VectorCopy(ex->angles, ex2->angles); ex2->angles[2] += 90; //ex->exflags |= EXFLAG_STILL_FRAME; //ex->data = 0; ex2->skin = 0; ex2->velocity[0] = 0; ex2->velocity[1] = 0; ex2->velocity[2] = 0; ex2->flags |= DRF_TRANSLUCENT | SCALE_ORIGIN_CENTER; } } } static void updateAcidBall(explosion_t *ex) { R_RocketTrail (ex->oldorg, ex->origin, rt_acidball); } //***************************************************************************** // // FLAG UPDATE FUNCTIONS // //***************************************************************************** void CL_UpdatePoisonGas(entity_t *ent, int edict_num) { explosion_t *ex; float smokeCount; if (host_frametime <= 0.05) smokeCount = 32 * host_frametime; else smokeCount = 16 * host_frametime; while (smokeCount > 0) { if (smokeCount < 1.0) { if ((rand() % 100) / 100 > smokeCount) { // account for fractional chance of more smoke... smokeCount = 0; continue; } } ex = CL_AllocExplosion(); VectorCopy(ent->origin, ex->origin); ex->model = Mod_ForName("models/grnsmk1.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + .7 + (rand() % 200)*.001; ex->scale = 100; VectorCopy(ent->angles, ex->angles); ex->angles[2] += 90; //ex->exflags |= EXFLAG_STILL_FRAME; //ex->data = 0; ex->skin = 0; ex->velocity[0] = (rand() % 100) - 50; ex->velocity[1] = (rand() % 100) - 50; ex->velocity[2] = 150.0; ex->flags |= DRF_TRANSLUCENT | SCALE_ORIGIN_CENTER; smokeCount -= 1.0; } } void CL_UpdateAcidBlob(entity_t *ent, int edict_num) { explosion_t *ex; int testVal, testVal2; testVal = (int)(cl.time * 10.0); testVal2 = (int)((cl.time - host_frametime)*10.0); if (testVal != testVal2) { if ( !(testVal%2) ) { ex = CL_AllocExplosion(); VectorCopy(ent->origin, ex->origin); ex->model = Mod_ForName("models/muzzle1.spr", true); ex->startTime = cl.time; ex->endTime = ex->startTime + .4; ex->scale = 100; VectorCopy(ent->angles, ex->angles); ex->angles[2] += 90; //ex->exflags |= EXFLAG_STILL_FRAME; //ex->data = 0; ex->skin = 0; ex->velocity[0] = 0; ex->velocity[1] = 0; ex->velocity[2] = 0; ex->flags |= DRF_TRANSLUCENT | SCALE_ORIGIN_CENTER; } } } void CL_UpdateOnFire(entity_t *ent, int edict_num) { explosion_t *ex; if ((rand() % 100) / 100.0 < 5.0 * host_frametime) { ex = CL_AllocExplosion(); VectorCopy(ent->origin, ex->origin); //raise and randomize origin some //ex->origin[0] += (rand() % ent->maxs[0]) - ent->maxs[0]/2; //ex->origin[1] += (rand() % ent->maxs[1]) - ent->maxs[1]/2; //ex->origin[2] += ent->maxs[2]/2; ex->origin[0] += (rand() % 10) - 5; ex->origin[1] += (rand() % 10) - 5; ex->origin[2] += (rand() % 20) + 10;//at least 10 up from origin, sprite's origin is in center! switch (rand() % 3) { case 0: ex->model = Mod_ForName("models/firewal1.spr", true); break; case 1: ex->model = Mod_ForName("models/firewal2.spr", true); break; case 2: ex->model = Mod_ForName("models/firewal3.spr", true); break; } ex->startTime = cl.time; ex->endTime = ex->startTime + ex->model->numframes * 0.05; ex->scale = 100; VectorCopy(ent->angles, ex->angles); ex->angles[2] += 90; ex->velocity[0] = (rand() % 40) - 20; ex->velocity[1] = (rand() % 40) - 20; ex->velocity[2] = 50 + (rand() % 100); if (rand() % 5) //translucent 80% of the time ex->flags |= DRF_TRANSLUCENT; } } static void PowerFlameBurnRemove(explosion_t *ex) { explosion_t *ex2; ex2 = CL_AllocExplosion(); VectorCopy(ex->origin, ex2->origin); switch (rand() % 3) { case 0: ex2->model = Mod_ForName("models/sm_expld.spr", true); break; case 1: ex2->model = Mod_ForName("models/fboom.spr", true); break; case 2: ex2->model = Mod_ForName("models/pow.spr", true); break; } ex2->startTime = cl.time; ex2->endTime = ex2->startTime + ex2->model->numframes * 0.05; ex2->scale = 100; ex2->flags |= MLS_ABSLIGHT|DRF_TRANSLUCENT; ex2->abslight = 128; if (rand() & 1) S_StartSound(TempSoundChannel(), 1, cl_sfx_flameend, ex2->origin, 1, 1); } void CL_UpdatePowerFlameBurn(entity_t *ent, int edict_num) { explosion_t *ex, *ex2; vec3_t srcVec; if ((rand() % 100) / 100.0 < host_frametime * 10) { ex = CL_AllocExplosion(); VectorCopy(ent->origin, ex->origin); ex->origin[0] += (rand() % 120) - 60; ex->origin[1] += (rand() % 120) - 60; ex->origin[2] += (rand() % 120) - 60 + 120; ex->model = Mod_ForName("models/sucwp1p.mdl", true); ex->startTime = cl.time; ex->endTime = ex->startTime + .25; ex->removeFunc = PowerFlameBurnRemove; ex->scale = 100; VectorSubtract(ent->origin, ex->origin, srcVec); VectorCopy(srcVec, ex->velocity); ex->velocity[2] += 24; VectorScale(ex->velocity, 1.0 / .25, ex->velocity); VectorNormalizeFast(srcVec); vectoangles(srcVec, ex->angles); ex->flags |= MLS_ABSLIGHT;//|DRF_TRANSLUCENT; ex->abslight = 128; // I'm not seeing this right now... (?) ex2 = CL_AllocExplosion(); VectorCopy(ex->origin, ex2->origin); ex2->model = Mod_ForName("models/flamestr.spr", true); ex2->startTime = cl.time; ex2->endTime = ex2->startTime + ex2->model->numframes * 0.05; ex2->flags |= DRF_TRANSLUCENT; } } void CL_UpdateHammer(entity_t *ent, int edict_num) { int testVal, testVal2; // do this every .3 seconds testVal = (int)(cl.time * 10.0); testVal2 = (int)((cl.time - host_frametime)*10.0); if (testVal != testVal2) { if (! (testVal % 3)) { //S_StartSound(TempSoundChannel(), 1, cl_sfx_hammersound, ent->origin, 1, 1); S_StartSound(edict_num, 2, cl_sfx_hammersound, ent->origin, 1, 1); } } } void CL_UpdateBug(entity_t *ent) { int testVal, testVal2; testVal = (int)(cl.time * 10.0); testVal2 = (int)((cl.time - host_frametime)*10.0); if (testVal != testVal2) { // do this every .1 seconds // if (! (testVal % 3)) S_StartSound(TempSoundChannel(), 1, cl_sfx_buzzbee, ent->origin, 1, 1); } } void CL_UpdateIceStorm(entity_t *ent, int edict_num) { vec3_t center, side1; vec3_t side2 = {160, 160, 128}; entity_state_t *state; state = FindState(edict_num); if (state) { VectorCopy(state->origin, center); // handle the particles VectorCopy(center, side1); side1[0] -= 80; side1[1] -= 80; side1[2] += 104; R_RainEffect2(side1, side2, (rand() % 400) - 200, (rand() % 400) - 200, (rand() % 15) + 9*16, (int)(30*20*host_frametime)); playIceSound+=host_frametime; if (playIceSound >= .6) { S_StartSound (TempSoundChannel(), 0, cl_sfx_icestorm, center, 1, 1); playIceSound -= .6; } } // toss little ice chunks if (rand() % 100 < host_frametime * 100.0 * 3) { explosion_t *ex; ex = CL_AllocExplosion(); VectorCopy(ent->origin,ex->origin); ex->origin[0] += (rand() % 32) - 16; ex->origin[1] += (rand() % 32) - 16; ex->origin[2] += 48 + (rand() % 32); ex->frameFunc = ChunkThink; ex->velocity[0] += (rand() % 300) - 150; ex->velocity[1] += (rand() % 300) - 150; ex->velocity[2] += (rand() % 200) - 50; // are these in degrees or radians? ex->angles[0] = (rand() % 360); ex->angles[1] = (rand() % 360); ex->angles[2] = (rand() % 360); ex->exflags = EXFLAG_ROTATE; ex->avel[0] = (rand() % 850) - 425; ex->avel[1] = (rand() % 850) - 425; ex->avel[2] = (rand() % 850) - 425; ex->scale = 65 + (rand() % 10); ex->data = THINGTYPE_ICE; ex->model = Mod_ForName("models/shard.mdl", true); ex->skin = 0; //ent->frame = rand() % 2; ex->flags |= DRF_TRANSLUCENT|MLS_ABSLIGHT; ex->abslight = 128; ex->startTime = cl.time; ex->endTime = ex->startTime + 2.0; } } static void telPuffMove (explosion_t *ex) { vec3_t tvec, tvec2; VectorSubtract(ex->angles, ex->origin, tvec); VectorCopy(tvec,tvec2); VectorNormalizeFast(tvec); VectorScale(tvec,320,tvec); ex->velocity[0] = tvec[1]; ex->velocity[1] = -tvec[0]; ex->velocity[2] += FRANDOM(); if (ex->velocity[2] > 40) ex->velocity[2] = 30; //keep it from getting too spread out: ex->velocity[0] += tvec2[0]; ex->velocity[1] += tvec2[1]; R_RocketTrail (ex->oldorg, ex->origin, rt_magicmissile); // ex->accel[0] = ex->velocity[1]*5; // ex->accel[1] = -ex->velocity[0]*5; // ex->accel[2] += FRANDOM()*3.0; } static void telEffectUpdate (explosion_t *ex) { explosion_t *ex2; float angle; int testVal, testVal2; vec3_t tvec; if (ex->endTime - cl.time <= 1.2) ex->frameFunc = NULL; testVal = (int)(cl.time * 10.0); testVal2 = (int)((cl.time - host_frametime)*10.0); if (testVal != testVal2) { if (! (testVal % 3)) { ex2 = CL_AllocExplosion(); angle = FRANDOM() * 3.14159; VectorCopy(ex->origin,ex2->origin); VectorCopy(ex->origin,ex2->angles); q_sincosrad(angle, &tvec[1], &tvec[0]); ex2->origin[0] += tvec[0]*10; ex2->origin[1] += tvec[1]*10; VectorSubtract(ex->origin, ex2->origin, tvec); VectorScale(tvec,20,tvec); ex2->model = Mod_ForName("models/sm_blue.spr", true); ex2->startTime = cl.time; ex2->endTime = ex2->startTime + 3.2; ex2->scale = 100; ex2->data = 0; ex2->skin = 0; ex2->frameFunc = telPuffMove; ex2->velocity[0] = tvec[1]; ex2->velocity[1] = -tvec[0]; ex2->velocity[2] = 20.0; ex2->flags |= MLS_ABSLIGHT; ex2->abslight = 128; } } if (ex->endTime > cl.time + 0.01) ex->startTime = cl.time + 0.01; } static void CL_UpdateTargetBall(void) { int i; explosion_t *ex1 = NULL; explosion_t *ex2 = NULL; qmodel_t *iceMod; vec3_t newOrg; float newScale; float sinval, cosval; if (v_targDist < 24) return; // either there is no ball, or it's too close to be needed... // search for the two thingies. If they // don't exist, make new ones and set v_oldTargOrg iceMod = Mod_ForName("models/iceshot2.mdl", true); for (i = 0; i < MAX_EXPLOSIONS; i++) { if (cl_explosions[i].endTime > cl.time) { // make certain it's an active one if (cl_explosions[i].model == iceMod) { if (cl_explosions[i].flags & DRF_TRANSLUCENT) ex1 = &cl_explosions[i]; else ex2 = &cl_explosions[i]; } } } q_sincosrad(v_targAngle*M_PI*2/256.0, &sinval, &cosval); newOrg[0] = cosval; newOrg[1] = sinval; q_sincosrad(v_targPitch*M_PI*2/256.0, &sinval, &cosval); newOrg[0] *= cosval; newOrg[1] *= cosval; newOrg[2] = sinval; VectorScale(newOrg, 50, newOrg); VectorAdd(newOrg, cl.simorg, newOrg); newOrg[2] += 44; newOrg[2] += q_cosrad(cl.time*2) * 5; if (v_targDist < 60) // make it scale back down up close... newScale = 172 - (172 * (1.0 - (v_targDist - 24.0)/36.0)); else newScale = 80 + (120 * ((256.0 - v_targDist)/256.0)); if (ex1 == NULL) { ex1 = CL_AllocExplosion(); ex1->model = iceMod; ex1->exflags |= EXFLAG_STILL_FRAME; ex1->data = 0; ex1->flags |= MLS_ABSLIGHT|DRF_TRANSLUCENT; ex1->skin = 0; VectorCopy(newOrg, ex1->origin); ex1->scale = newScale; } VectorScale(ex1->origin, (.75 - host_frametime * 1.5), ex1->origin); // FIXME this should be affected by frametime... VectorMA(ex1->origin, (.25 + host_frametime * 1.5), newOrg, ex1->origin); ex1->startTime = cl.time; ex1->endTime = ex1->startTime + host_frametime + 0.2; ex1->scale = (ex1->scale * (.75 - host_frametime * 1.5) + newScale * (.25 + host_frametime * 1.5)); ex1->angles[0] = v_targPitch*360/256.0; ex1->angles[1] = v_targAngle*360/256.0; ex1->angles[2] = cl.time * 240; ex1->abslight = 96 + (32 * q_cosrad(cl.time*6.5)) + (64 * ((256.0 - v_targDist)/256.0)); if (v_targDist < 60) // make it scale back down up close... newScale = 76 - (76 * (1.0 - (v_targDist - 24.0)/36.0)); else newScale = 30 + (60 * ((256.0 - v_targDist)/256.0)); if (ex2 == NULL) { ex2 = CL_AllocExplosion(); ex2->model = iceMod; ex2->exflags |= EXFLAG_STILL_FRAME; ex2->data = 0; ex2->flags |= MLS_ABSLIGHT; ex2->skin = 0; ex2->scale = newScale; } VectorCopy(ex1->origin, ex2->origin); ex2->startTime = cl.time; ex2->endTime = ex2->startTime + host_frametime + 0.2; ex2->scale = (ex2->scale * (.75 - host_frametime * 1.5) + newScale * (.25 + host_frametime * 1.5)); ex2->angles[0] = ex1->angles[0]; ex2->angles[1] = ex1->angles[1]; ex2->angles[2] = cl.time * -360; ex2->abslight = 96 + (128 * q_cosrad(cl.time*4.5)); R_TargetBallEffect (ex1->origin); } static void SmokeRingFrameFunc(explosion_t *ex) { if (cl.time - ex->startTime < .3) ex->skin = 0; else if (cl.time - ex->startTime < .6) ex->skin = 1; else if (cl.time - ex->startTime < .9) ex->skin = 2; else ex->skin = 3; } engine/hexenworld/client/client.h000066400000000000000000000366071444734033100174260ustar00rootroot00000000000000/* client.h -- client main header * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __H2W_CLIENT_H #define __H2W_CLIENT_H typedef struct { char name[16]; qboolean failedload; // the name isn't a valid skin cache_user_t cache; } skin_t; // player_state_t is the information needed by a player entity // to do move prediction and to generate a drawable entity typedef struct { int messagenum; // all player's won't be updated each frame double state_time; // not the same as the packet time, // because player commands come asyncronously usercmd_t command; // last command for prediction vec3_t origin; vec3_t viewangles; // only for demos, not from server vec3_t velocity; int weaponframe; int modelindex; int frame; int skinnum; int effects; int drawflags; int scale; int abslight; int flags; // dead, gib, etc float waterjumptime; int onground; // -1 = in air, else pmove entity number int oldbuttons; } player_state_t; #define MAX_SCOREBOARDNAME 16 typedef struct player_info_s { int userid; char userinfo[MAX_INFO_STRING]; // scoreboard information char name[MAX_SCOREBOARDNAME]; float entertime; int frags; int ping; // skin information int topcolor; int bottomcolor; int playerclass; int level; int spectator; byte translations[VID_GRADES*256]; skin_t *skin; int modelindex; qboolean Translated; int siege_team; qboolean shownames_off; } player_info_t; typedef struct { // generated on client side usercmd_t cmd; // cmd that generated the frame double senttime; // time cmd was sent off int delta_sequence; // sequence number to delta from, -1 = full update // received from server double receivedtime; // time message was received, or -1 player_state_t playerstate[MAX_CLIENTS]; // message received that reflects performing // the usercmd packet_entities_t packet_entities; qboolean invalid; // true if the packet_entities delta was invalid } frame_t; typedef struct { int destcolor[3]; int percent; // 0-256 } cshift_t; #define CSHIFT_CONTENTS 0 #define CSHIFT_DAMAGE 1 #define CSHIFT_BONUS 2 #define CSHIFT_POWERUP 3 #define CSHIFT_INTERVENTION 4 #define NUM_CSHIFTS 5 // // client_state_t should hold all pieces of the client state // #define MAX_DLIGHTS 32 typedef struct { int key; // so entities can reuse same entry vec3_t origin; float radius; float die; // stop lighting after this time float decay; // drop this each second float minlight; // don't add when contributing less float color[4]; qboolean dark; // subtracts light instead of adding } dlight_t; typedef struct { int length; char map[MAX_STYLESTRING]; } lightstyle_t; #define MAX_EFRAGS 512 #define MAX_DEMOS 8 #define MAX_DEMONAME 16 typedef enum { ca_disconnected, // full screen console with no connection ca_demostart, // starting up a demo ca_connected, // netchan_t established, waiting for svc_serverdata ca_onserver, // processing data lists, donwloading, etc ca_active // everything is in, so frames can be rendered } cactive_t; typedef enum { dl_none, dl_model, dl_sound, dl_skin, dl_single } dltype_t; // download type // // the client_static_t structure is persistant through an arbitrary number // of server connections // typedef struct { // connection information cactive_t state; // network stuff netchan_t netchan; // private userinfo for sending to masterless servers char userinfo[MAX_INFO_STRING]; char servername[MAX_OSPATH]; // name of server from original connect FILE *download; // file transfer from server char downloadtempname[MAX_OSPATH]; char downloadname[MAX_OSPATH]; int downloadnumber; dltype_t downloadtype; int downloadpercent; // demo loop control int demonum; // -1 = don't play demos char demos[MAX_DEMOS][MAX_DEMONAME]; // when not playing // demo recording info must be here, because record is started before // entering a map (and clearing client_state_t) qboolean demorecording; qboolean demoplayback; qboolean timedemo; FILE *demofile; float td_lastframe; // to meter out one message a frame int td_startframe; // host_framecount at start float td_starttime; // realtime at second frame of timedemo float latency; // rolling average } client_static_t; extern client_static_t cls; // // the client_state_t structure is wiped completely at every // server signon // typedef struct { int servercount; // server identification for prespawns char serverinfo[MAX_SERVERINFO_STRING]; int parsecount; // server message counter int validsequence; // this is the sequence number of the last good // packetentity_t we got. If this is 0, we can't // render a frame yet int movemessages; // since connecting to this server // throw out the first couple, so the player // doesn't accidentally do something the // first frame int protocol; int spectator; double last_ping_request; // while showing scoreboard double last_servermessage; // sentcmds[cl.netchan.outgoing_sequence & UPDATE_MASK] = cmd frame_t frames[UPDATE_BACKUP]; // information for local display int stats[MAX_CL_STATS]; // health, etc int inv_order[MAX_INVENTORY]; int inv_count, inv_startpos, inv_selected; float item_gettime[32]; // cl.time of aquiring item, for blinking float faceanimtime; // use anim frame if cl.time < this entvars_t v; // NOTE: not every field will be update // you must specifically add them in // functions SV_WriteClientdatatToMessage() // and CL_ParseClientdata() cshift_t cshifts[NUM_CSHIFTS]; // color shifts for damage, powerups cshift_t prev_cshifts[NUM_CSHIFTS]; // and content types char puzzle_pieces[8][10]; // puzzle piece names // the client maintains its own idea of view angles, which are // sent to the server each frame. And only reset at level change // and teleport times vec3_t viewangles; // the client simulates or interpolates movement to get these values double time; // this is the time value that the client // is rendering at. always <= realtime vec3_t simorg; vec3_t simvel; vec3_t simangles; float idealroll; float rollvel; // pitch drifting vars float idealpitch; float pitchvel; qboolean nodrift; float driftmove; double laststop; byte light_level; float crouch; // local amount for smoothing stepups qboolean paused; // send over by server float punchangle; // temporary view kick from weapon firing // intermissions: setup by CL_SetupIntermission() and run by SB_IntermissionOverlay() int intermission; // don't change view angle, full screen, etc int completed_time; // latched at intermission start int message_index; int intermission_flags; const char *intermission_pic; int lasting_time; int intermission_next; // // information that is static for the entire time connected to a server // char model_name[MAX_MODELS][MAX_QPATH]; char sound_name[MAX_SOUNDS][MAX_QPATH]; struct qmodel_s *model_precache[MAX_MODELS]; struct sfx_s *sound_precache[MAX_SOUNDS]; char mapname[40]; char levelname[40]; // for display on solo scoreboard int playernum; // refresh related state struct qmodel_s *worldmodel; // cl_entitites[0].model struct efrag_s *free_efrags; int num_entities; // stored bottom up in cl_entities array int num_statics; // stored top down in cl_entitiers int cdtrack; // cd audio char midi_name[128]; // midi file name entity_t viewent; // weapon model struct EffectT Effects[MAX_EFFECTS]; unsigned int PIV; // players in view // all player information player_info_t players[MAX_CLIENTS]; } client_state_t; // // edict->flags // #define FL_FLY 1 #define FL_SWIM 2 #define FL_CONVEYOR 4 #define FL_CLIENT 8 #define FL_INWATER 16 #define FL_MONSTER 32 #define FL_GODMODE 64 #define FL_NOTARGET 128 #define FL_ITEM 256 #define FL_ONGROUND 512 #define FL_PARTIALGROUND 1024 // not all corners are valid #define FL_WATERJUMP 2048 // player jumping out of water #define FL_JUMPRELEASED 4096 // for jump debouncing #define FL_FLASHLIGHT 8192 #define FL_ARCHIVE_OVERRIDE 1048576 #define FL_ARTIFACTUSED 16384 #define FL_MOVECHAIN_ANGLE 32768 // when in a move chain, will update the angle #define FL_CLASS_DEPENDENT 2097152 // model will appear different to each player #define FL_SPECIAL_ABILITY1 4194304 // has 1st special ability #define FL_SPECIAL_ABILITY2 8388608 // has 2nd special ability #define FL2_CROUCHED 4096 // // edict->movetype values // #define MOVETYPE_NONE 0 // never moves #define MOVETYPE_ANGLENOCLIP 1 #define MOVETYPE_ANGLECLIP 2 #define MOVETYPE_WALK 3 // gravity #define MOVETYPE_STEP 4 // gravity, special edge handling #define MOVETYPE_FLY 5 #define MOVETYPE_TOSS 6 // gravity #define MOVETYPE_PUSH 7 // no clip to world, push and crush #define MOVETYPE_NOCLIP 8 #define MOVETYPE_FLYMISSILE 9 // extra size to monsters #define MOVETYPE_BOUNCE 10 //#ifdef QUAKE2 #define MOVETYPE_BOUNCEMISSILE 11 // bounce w/o gravity #define MOVETYPE_FOLLOW 12 // track movement of aiment //#endif #define MOVETYPE_PUSHPULL 13 // pushable/pullable object #define MOVETYPE_SWIM 14 // should keep the object in water // // cvars // extern cvar_t cl_upspeed; extern cvar_t cl_forwardspeed; extern cvar_t cl_backspeed; extern cvar_t cl_sidespeed; extern cvar_t cl_movespeedkey; extern cvar_t cl_yawspeed; extern cvar_t cl_pitchspeed; extern cvar_t cl_anglespeedkey; extern cvar_t cl_shownet; extern cvar_t cfg_unbindall; extern cvar_t cl_pitchdriftspeed; extern cvar_t lookspring; extern cvar_t lookstrafe; extern cvar_t sensitivity; extern cvar_t m_pitch; extern cvar_t m_yaw; extern cvar_t m_forward; extern cvar_t m_side; extern cvar_t playerclass; extern cvar_t spectator; #define MAX_STATIC_ENTITIES 256 // torches, etc extern client_state_t cl; // FIXME, allocate dynamically extern entity_state_t cl_baselines[MAX_EDICTS]; extern efrag_t cl_efrags[MAX_EFRAGS]; extern entity_t cl_static_entities[MAX_STATIC_ENTITIES]; extern lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; extern dlight_t cl_dlights[MAX_DLIGHTS]; extern const int color_offsets[MAX_PLAYER_CLASS]; //============================================================================= extern float server_version; // version of server we connected to // // cl_main // dlight_t *CL_AllocDlight (int key); void CL_DecayLights (void); void CL_Init (void); void CL_ClearState (void); void CL_SendConnectPacket (void); void CL_Disconnect (void); void CL_NextDemo (void); qboolean CL_DemoBehind(void); #define MAX_VISEDICTS 512 extern int cl_numvisedicts, cl_oldnumvisedicts; extern entity_t *cl_visedicts, *cl_oldvisedicts; extern entity_t cl_visedicts_list[2][MAX_VISEDICTS]; // // cl_cmd // void Cmd_ForwardToServer (void); void CL_Cmd_Init (void); // // cl_input // typedef struct { int down[2]; // key nums holding it down int state; // low bit is down state } kbutton_t; extern kbutton_t in_mlook, in_klook; extern kbutton_t in_strafe; extern kbutton_t in_speed; extern int in_impulse; void CL_InitInput (void); void CL_SendCmd (void); void CL_BaseMove (usercmd_t *cmd); // // cl_demo.c // void CL_StopPlayback (void); qboolean CL_GetMessage (void); void CL_Stop_f (void); void CL_Record_f (void); void CL_ReRecord_f (void); void CL_PlayDemo_f (void); void CL_TimeDemo_f (void); void CL_WriteDemoCmd (const usercmd_t *pcmd); // // cl_interlude.c // #define INTERMISSION_NOT_CONNECTED (1<<0) /* can not use cl.time, use realtime */ #define INTERMISSION_NO_MENUS (1<<1) /* don't allow drawing the menus */ #define INTERMISSION_NO_MESSAGE (1<<2) /* doesn't need a valid message index */ #define INTERMISSION_PRINT_TOP (1<<3) /* print centered in top half of screen */ #define INTERMISSION_PRINT_TOPMOST (1<<4) /* print at top-most side of the screen */ /* without either of the above two, prints centered on the whole screen */ #define INTERMISSION_PRINT_WHITE (1<<5) /* print in white, not in red */ #define INTERMISSION_PRINT_DELAY (1<<6) /* delay message print for ca. 2.5s */ void CL_SetupIntermission (int n); // // cl_parse.c // void CL_ParseServerMessage (void); qboolean CL_CheckOrDownloadFile (const char *filename); // // cl_cam.c // void CL_InitCam (void); void Cam_Reset (void); void Cam_Track (usercmd_t *cmd); void Cam_FinishMove (usercmd_t *cmd); // // view // void V_StartPitchDrift (void); void V_StopPitchDrift (void); void V_RenderView (void); void V_UpdatePalette (void); void V_Register (void); void V_ParseDamage (void); void V_SetContentsColor (int contents); void V_ParseTarget(void); extern float v_targAngle; extern float v_targPitch; extern float v_targDist; // // cl_effect // void CL_InitEffects (void); void CL_ClearEffects (void); void CL_EndEffect (void); void CL_ParseEffect (void); void CL_ParseMultiEffect (void); void CL_UpdateEffects (void); void CL_TurnEffect (void); void CL_ReviseEffect (void); // // cl_tent // void CL_InitTEnts (void); void CL_ClearTEnts (void); void CL_ParseTEnt (void); void CL_UpdateTEnts (void); void CL_UpdateHammer(entity_t *ent, int edict_num); void CL_UpdateBug(entity_t *ent); void CL_UpdateIceStorm(entity_t *ent, int edict_num); void CL_UpdatePoisonGas(entity_t *ent, int edict_num); void CL_UpdateAcidBlob(entity_t *ent, int edict_num); void CL_UpdateOnFire(entity_t *ent, int edict_num); void CL_UpdatePowerFlameBurn(entity_t *ent, int edict_num); // // cl_ents.c // void CL_EmitEntities (void); void CL_SetUpPlayerPrediction (qboolean dopred); void CL_SetSolidPlayers (int playernum); void CL_SetSolidEntities (void); void CL_ClearProjectiles (void); void CL_ClearMissiles (void); void CL_ParseProjectiles (void); void CL_ParsePackMissiles (void); void CL_ParsePacketEntities (qboolean delta); void CL_ParsePlayerinfo (void); void CL_SavePlayer (void); // // cl_pred.c // void CL_InitPrediction (void); void CL_PredictMove (void); void CL_PredictUsercmd (player_state_t *from, player_state_t *to, usercmd_t *u, qboolean spectate); // // skin.c // typedef struct { char manufacturer; char version; char encoding; char bits_per_pixel; unsigned short xmin,ymin,xmax,ymax; unsigned short hres,vres; unsigned char palette[48]; char reserved; char color_planes; unsigned short bytes_per_line; unsigned short palette_type; char filler[58]; unsigned char data; // unbounded } pcx_t; void Skin_Find (player_info_t *sc); byte *Skin_Cache (skin_t *skin); void Skin_Skins_f (void); void Skin_AllSkins_f (void); void Skin_NextDownload (void); // // globals for Siege: // extern qboolean cl_siege; // whether this is a Siege game extern byte cl_fraglimit; extern float cl_timelimit; extern float cl_server_time_offset; extern unsigned int defLosses; // Defender losses extern unsigned int attLosses; // Attacker losses extern int cl_keyholder; extern int cl_doc; // Defender of Crown (Excalibur) #endif /* __H2W_CLIENT_H */ engine/hexenworld/client/console.c000066400000000000000000000337141444734033100176010ustar00rootroot00000000000000/* console.c -- in-game console and chat message buffer handling * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "debuglog.h" console_t *con; qboolean con_initialized; static int con_linewidth; // characters across screen static int con_vislines; int con_notifylines; // scan lines to clear for notify lines int con_totallines; // total lines in console scrollback static float con_cursorspeed = 4; qboolean con_forcedup; // because no entities to refresh int con_ormask; static cvar_t con_notifytime = {"con_notifytime", "3", CVAR_NONE}; //seconds #define NUM_CON_TIMES 4 static float con_times[NUM_CON_TIMES]; // realtime time the line was generated // for transparent notify lines extern qboolean menu_disabled_mouse; static void Key_ClearTyping (void) { key_lines[edit_line][1] = 0; // clear any typing key_linepos = 1; } /* ================ Con_ToggleConsole_f ================ */ void Con_ToggleConsole_f (void) { keydest_t dest = Key_GetDest(); // activate mouse when in console in // case it is disabled somewhere else menu_disabled_mouse = false; IN_ActivateMouse (); Key_ClearTyping (); if (dest == key_console || (dest == key_game && con_forcedup)) { if (cls.state == ca_active) Key_SetDest (key_game); // else // M_Menu_Main_f (); } else { Key_SetDest (key_console); } Con_ClearNotify (); } /* ================ Con_ToggleChat_f ================ */ static void Con_ToggleChat_f (void) { Key_ClearTyping (); if (Key_GetDest() == key_console) { if (cls.state == ca_active) Key_SetDest (key_game); } else Key_SetDest (key_console); Con_ClearNotify (); } /* ================ Con_Clear_f ================ */ static void Con_Clear_f (void) { int i; for (i = 0; i < CON_TEXTSIZE; i++) con->text[i] = ' '; } /* ================ Con_ClearNotify ================ */ void Con_ClearNotify (void) { int i; for (i = 0; i < NUM_CON_TIMES; i++) con_times[i] = 0; } /* ================ Con_MessageMode_f ================ */ static void Con_MessageMode_f (void) { if (cls.state != ca_active || cls.demoplayback) return; chat_team = false; Key_SetDest (key_message); } /* ================ Con_MessageMode2_f ================ */ static void Con_MessageMode2_f (void) { if (cls.state != ca_active || cls.demoplayback) return; chat_team = true; Key_SetDest (key_message); } /* ================ Con_CheckResize If the line width has changed, reformat the buffer. ================ */ void Con_CheckResize (void) { int i, j, width, oldwidth, oldtotallines, numlines, numchars; short tbuf[CON_TEXTSIZE]; width = (vid.width >> 3) - 2; if (width == con_linewidth) return; if (width < 1) // video hasn't been initialized yet { width = 38; con_linewidth = width; con_totallines = CON_TEXTSIZE / con_linewidth; Con_Clear_f(); } else { oldwidth = con_linewidth; con_linewidth = width; oldtotallines = con_totallines; con_totallines = CON_TEXTSIZE / con_linewidth; numlines = oldtotallines; if (con_totallines < numlines) numlines = con_totallines; numchars = oldwidth; if (con_linewidth < numchars) numchars = con_linewidth; memcpy (tbuf, con->text, CON_TEXTSIZE*sizeof(short)); Con_Clear_f(); for (i = 0; i < numlines; i++) { for (j = 0; j < numchars; j++) { con->text[(con_totallines - 1 - i) * con_linewidth + j] = tbuf[((con->current - i + oldtotallines) % oldtotallines) * oldwidth + j]; } } Con_ClearNotify (); } con->current = con_totallines - 1; con->display = con->current; } /* ================ Con_Init ================ */ void Con_Init (void) { con = (console_t *) Hunk_AllocName (sizeof(console_t), "con_main"); con_linewidth = -1; Con_CheckResize (); Con_Printf ("Console initialized.\n"); Cvar_RegisterVariable (&con_notifytime); Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f); Cmd_AddCommand ("togglechat", Con_ToggleChat_f); Cmd_AddCommand ("messagemode", Con_MessageMode_f); Cmd_AddCommand ("messagemode2", Con_MessageMode2_f); Cmd_AddCommand ("clear", Con_Clear_f); con_initialized = true; } /* =============== Con_Linefeed =============== */ static void Con_Linefeed (void) { int i, j; con->x = 0; if (con->display == con->current) con->display++; con->current++; j = (con->current%con_totallines) * con_linewidth; for (i = 0; i < con_linewidth; i++) con->text[i+j] = ' '; } /* ================ Con_Print Handles cursor positioning, line wrapping, etc All console printing must go through this in order to be logged to disk If no console is visible, the notify window will pop up. ================ */ static void Con_Print (const char *txt) { int y; int c, l; static int cr; int mask; qboolean boundary; if (txt[0] == 1) { mask = 256; // go to colored text txt++; } else if (txt[0] == 2) { mask = 256; // go to colored text txt++; } else mask = 0; boundary = true; while ( (c = (byte)*txt) ) { if (c <= ' ') { boundary = true; } else if (boundary) { // count word length for (l = 0; l < con_linewidth; l++) if (txt[l] <= ' ') break; // word wrap if (l != con_linewidth && (con->x + l > con_linewidth)) con->x = 0; boundary = false; } txt++; if (cr) { con->current--; cr = false; } if (!con->x) { Con_Linefeed (); // mark time for transparent overlay if (con->current >= 0) con_times[con->current % NUM_CON_TIMES] = realtime; } switch (c) { case '\n': con->x = 0; break; case '\r': con->x = 0; cr = 1; break; default: // display character and advance y = con->current % con_totallines; con->text[y*con_linewidth+con->x] = c | mask | con_ormask; con->x++; if (con->x >= con_linewidth) con->x = 0; break; } } } /* ================ CON_Printf Prepare the message to be printed and send it to the proper handlers. ================ */ void CON_Printf (unsigned int flags, const char *fmt, ...) { va_list argptr; char msg[MAX_PRINTMSG]; static qboolean inupdate; if (flags & _PRINT_DEVEL && !developer.integer) { if (con_debuglog & LOG_DEVEL) /* full logging */ { va_start (argptr, fmt); q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); LOG_Print (msg); } return; } va_start (argptr, fmt); q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); Sys_PrintTerm (msg); // echo to the terminal if (con_debuglog) LOG_Print (msg); if (flags & _PRINT_TERMONLY || !con_initialized) return; // write it to the scrollable buffer Con_Print (msg); if (flags & _PRINT_SAFE) return; // safe: doesn't update the screen // update the screen immediately if the console is displayed if (cls.state != ca_active) { // protect against infinite loop if SCR_UpdateScreen // itself calls Con_Printf if (!inupdate) { inupdate = true; SCR_UpdateScreen (); inupdate = false; } } } /* ================== Con_ShowList Tyrann's ShowList ported by S.A.: Prints a given list to the console with columnized formatting ================== */ void Con_ShowList (int cnt, const char **list) { const char *s; char *line; int i, j, max_len, len, cols, rows; // Lay them out in columns max_len = 0; for (i = 0; i < cnt; ++i) { len = (int) strlen(list[i]); if (len > max_len) max_len = len; } line = (char *) Z_Malloc(con_linewidth + 1, Z_MAINZONE); cols = con_linewidth / (max_len + 2); rows = cnt / cols + 1; // Looks better if we have a few rows before spreading out if (rows < 5) { cols = cnt / 5 + 1; rows = cnt / cols + 1; } for (i = 0; i < rows; ++i) { line[0] = '\0'; for (j = 0; j < cols; ++j) { if (j * rows + i >= cnt) break; s = list[j * rows + i]; len = (int) strlen(s); q_strlcat(line, s, con_linewidth+1); if (j < cols - 1) { while (len < max_len) { q_strlcat(line, " ", con_linewidth+1); len++; } q_strlcat(line, " ", con_linewidth+1); } } if (line[0] != '\0') Con_Printf("%s\n", line); } Z_Free(line); } /* ============================================================================== DRAWING ============================================================================== */ /* ================ Con_DrawInput The input line scrolls horizontally if typing goes beyond the right edge ================ */ static void Con_DrawInput (void) { int i, y; size_t pos; char editlinecopy[MAXCMDLINE], *text; if (Key_GetDest() != key_console && !con_forcedup) return; // don't draw anything pos = q_strlcpy(editlinecopy, key_lines[edit_line], sizeof(editlinecopy)); text = editlinecopy; // fill out remainder with spaces for ( ; pos < MAXCMDLINE; ++pos) text[pos] = ' '; // add the cursor frame if ((int)(realtime * con_cursorspeed) & 1) // cursor is visible text[key_linepos] = (key_insert) ? 11 : 95; // underscore for overwrite mode, square for insert // prestep if horizontally scrolling if (key_linepos >= con_linewidth) text += 1 + key_linepos - con_linewidth; // draw it y = con_vislines - 22; for (i = 0; i < con_linewidth; i++) Draw_Character ((i + 1)<<3, y, text[i]); } /* ================ Con_DrawNotify Draws the last few lines of output transparently over the game top ================ */ void Con_DrawNotify (void) { int i, x, v; const short *text; float time; v = 0; for (i = con->current-NUM_CON_TIMES+1; i <= con->current; i++) { if (i < 0) continue; time = con_times[i % NUM_CON_TIMES]; if (time == 0) continue; time = realtime - time; if (time > con_notifytime.value) continue; text = con->text + (i % con_totallines)*con_linewidth; if (scr_viewsize.integer < 100) clearnotify = 0; scr_copytop = 1; for (x = 0; x < con_linewidth; x++) Draw_Character ((x+1)<<3, v, text[x]); v += 8; } if (Key_GetDest() == key_message) { const char *s; if (scr_viewsize.integer < 100) clearnotify = 0; scr_copytop = 1; if (chat_team) { Draw_String (8, v, "say_team:"); x = 11; } else { Draw_String (8, v, "say:"); x = 6; } s = Key_GetChatBuffer(); i = Key_GetChatMsgLen(); if (i > (vid.width>>3) - x - 1) s += i - (vid.width>>3) + x + 1; while (*s) { Draw_Character (x<<3, v, *s); s++; x++; } Draw_Character (x<<3, v, 10 + ((int)(realtime*con_cursorspeed)&1)); v += 8; } if (v > con_notifylines) con_notifylines = v; } /* ================ Con_DrawDownloadBar Draws the download progress for Con_DrawConsole ================ */ static void Con_DrawDownloadBar (void) { int i, x, y; int j, n; char dlbar[1024], *text; text = strrchr(cls.downloadname, '/'); if (text != NULL) text++; else text = cls.downloadname; // figure out width x = con_linewidth - ((con_linewidth * 7) / 40); y = x - (int)strlen(text) - 8; i = con_linewidth/3; if ((int)strlen(text) > i) { y = x - i - 11; strncpy(dlbar, text, i); dlbar[i] = 0; strcat(dlbar, "..."); } else { strcpy(dlbar, text); } strcat(dlbar, ": "); i = strlen(dlbar); dlbar[i++] = '\x80'; // where's the dot go? if (cls.downloadpercent == 0) n = 0; else n = y * cls.downloadpercent / 100; for (j = 0; j < y; j++) { if (j == n) dlbar[i++] = '\x83'; else dlbar[i++] = '\x81'; } dlbar[i++] = '\x82'; dlbar[i] = 0; sprintf(dlbar + strlen(dlbar), " %02d%%", cls.downloadpercent); // draw it y = con_vislines-22 + 8; j = (int) strlen(dlbar); for (i = 0; i < j; i++) Draw_Character ((i + 1)<<3, y, dlbar[i]); } /* ================ Con_DrawConsole Draws the console with the solid background ================ */ void Con_DrawConsole (int lines) { int i, x, y; int row, rows; const short *text; if (lines <= 0) return; // draw the background Draw_ConsoleBackground (lines); // draw the text con_vislines = lines; // changed to line things up better rows = (lines-22)>>3; // rows of text to draw y = lines - 30; // draw from the bottom up if (con->display != con->current) { // draw arrows to show the buffer is backscrolled for (x = 0; x < con_linewidth; x += 4) Draw_Character ( (x+1)<<3, y, '^'); y -= 8; rows--; } row = con->display; for (i = 0; i < rows; i++, y -= 8, row--) { if (row < 0) break; if (con->current - row >= con_totallines) break; // past scrollback wrap point text = con->text + (row % con_totallines)*con_linewidth; for (x = 0; x < con_linewidth; x++) Draw_Character ( (x+1)<<3, y, text[x]); } // draw the download bar if (cls.download) Con_DrawDownloadBar (); // draw the input prompt, user text, and cursor if desired Con_DrawInput (); } /* ================== Con_NotifyBox ================== */ void Con_NotifyBox (const char *text) { double t1, t2; // during startup for sound / cd warnings Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n"); Con_Printf ("%s", text); Con_Printf ("Press a key.\n"); Con_Printf("\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n"); key_count = -2; // wait for a key down and up Key_SetDest (key_console); do { t1 = Sys_DoubleTime (); SCR_UpdateScreen (); Sys_SendKeyEvents (); t2 = Sys_DoubleTime (); realtime += t2-t1; // make the cursor blink } while (key_count < 0); Con_Printf ("\n"); Key_SetDest (key_game); realtime = 0; // put the cursor back to invisible } engine/hexenworld/client/gl_ngraph.c000066400000000000000000000101041444734033100200640ustar00rootroot00000000000000/* * gl_ngraph.c -- net graph drawing * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" static GLuint netgraphtexture; // netgraph texture #define NET_GRAPHHEIGHT 32 #define NET_TIMINGS 256 static int packet_latency[NET_TIMINGS]; static byte ngraph_texels[NET_GRAPHHEIGHT][NET_TIMINGS]; static void R_LineGraph (int x, int h) { int i, color; if (h == 10000) color = 0x6f; // yellow else if (h == 9999) color = 0x4f; // red else if (h == 9998) color = 0xd0; // blue else color = 0xfe; // white if (h > NET_GRAPHHEIGHT) h = NET_GRAPHHEIGHT; for (i = 0 ; i < h ; i++) { if (i & 1) ngraph_texels[NET_GRAPHHEIGHT - i - 1][x] = 0xff; else ngraph_texels[NET_GRAPHHEIGHT - i - 1][x] = (byte)color; } for ( ; i < NET_GRAPHHEIGHT ; i++) ngraph_texels[NET_GRAPHHEIGHT - i - 1][x] = (byte)0xff; } #if 0 /* no users. */ extern byte *draw_chars; // 8*8 graphic characters void Draw_CharToNetGraph (int x, int y, int num) { int row, col; byte *source; int drawline; int nx; row = num >> 4; col = num & 15; source = draw_chars + (row<<10) + (col<<3); for (drawline = 8; drawline; drawline--, y++) { for (nx = 0 ; nx < 8 ; nx++) { if (source[nx] != 255) ngraph_texels[y][nx+x] = 0x60 + source[nx]; } source += 128; } } #endif /* ============== R_NetGraph ============== */ void R_NetGraph (void) { int a, x, i, y; frame_t *frame; int lost; char st[80]; unsigned int ngraph_pixels[NET_GRAPHHEIGHT][NET_TIMINGS]; for (i = cls.netchan.outgoing_sequence - UPDATE_BACKUP + 1 ; i <= cls.netchan.outgoing_sequence ; i++) { frame = &cl.frames[i&UPDATE_MASK]; if (frame->receivedtime == -1) packet_latency[i&255] = 9999; // dropped else if (frame->receivedtime == -2) packet_latency[i&255] = 10000; // choked else if (frame->invalid) packet_latency[i&255] = 9998; // invalid delta else packet_latency[i&255] = (frame->receivedtime - frame->senttime)*20; } x = 0; lost = 0; for (a = 0 ; a < NET_TIMINGS ; a++) { i = (cls.netchan.outgoing_sequence-a) & 255; if (packet_latency[i] == 9999) lost++; R_LineGraph (NET_TIMINGS-1-a, packet_latency[i]); } // now load the netgraph texture into gl and draw it for (y = 0; y < NET_GRAPHHEIGHT; y++) for (x = 0; x < NET_TIMINGS; x++) ngraph_pixels[y][x] = d_8to24table[ngraph_texels[y][x]]; x = -((vid.width - 320)>>1); y = vid.height - sb_lines - 24 - NET_GRAPHHEIGHT - 1; M_DrawTextBox (x, y, NET_TIMINGS/8, NET_GRAPHHEIGHT/8 + 1); y += 8; sprintf(st, "%3i%% packet loss", lost*100/NET_TIMINGS); Draw_String(8, y, st); y += 8; GL_Bind(netgraphtexture); glTexImage2D_fp (GL_TEXTURE_2D, 0, gl_alpha_format, NET_TIMINGS, NET_GRAPHHEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, ngraph_pixels); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); x = 8; glColor3f_fp (1,1,1); glBegin_fp (GL_QUADS); glTexCoord2f_fp (0, 0); glVertex2f_fp (x, y); glTexCoord2f_fp (1, 0); glVertex2f_fp (x+NET_TIMINGS, y); glTexCoord2f_fp (1, 1); glVertex2f_fp (x+NET_TIMINGS, y+NET_GRAPHHEIGHT); glTexCoord2f_fp (0, 1); glVertex2f_fp (x, y+NET_GRAPHHEIGHT); glEnd_fp (); } void R_InitNetgraphTexture (void) { glGenTextures_fp(1, &netgraphtexture); } engine/hexenworld/client/gl_rmain.c000066400000000000000000001465271444734033100177360ustar00rootroot00000000000000/* gl_main.c * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" entity_t r_worldentity; vec3_t modelorg, r_entorigin; int r_visframecount; // bumped when going to a new PVS int r_framecount; // used for dlight push checking mplane_t frustum[4]; int c_brush_polys, c_alias_polys; qboolean r_cache_thrash; // compatability GLuint currenttexture = GL_UNUSED_TEXTURE; // to avoid unnecessary texture sets GLuint particletexture; // little dot for particles GLuint playertextures[MAX_CLIENTS]; // up to MAX_CLIENTS color translated skins GLuint gl_extra_textures[MAX_EXTRA_TEXTURES]; // generic textures for models int mirrortexturenum; // quake texturenum, not gltexturenum qboolean mirror; mplane_t *mirror_plane; static float model_constant_alpha; static float r_time1; static float r_lasttime1 = 0; extern qmodel_t *player_models[MAX_PLAYER_CLASS]; // // view origin // vec3_t vup, vpn, vright, r_origin; float r_world_matrix[16]; float r_projection_matrix[16]; // // screen size info // refdef_t r_refdef; mleaf_t *r_viewleaf, *r_oldviewleaf; texture_t *r_notexture_mip; int d_lightstylevalue[256]; // 8.8 fraction of base light value int gl_coloredstatic; // used to store what type of static light // we loaded in Mod_LoadLighting() static qboolean AlwaysDrawModel; cvar_t r_norefresh = {"r_norefresh", "0", CVAR_NONE}; cvar_t r_drawentities = {"r_drawentities", "1", CVAR_NONE}; cvar_t r_drawviewmodel = {"r_drawviewmodel", "1", CVAR_NONE}; cvar_t r_speeds = {"r_speeds", "0", CVAR_NONE}; cvar_t r_waterwarp = {"r_waterwarp", "0", CVAR_ARCHIVE}; cvar_t r_fullbright = {"r_fullbright", "0", CVAR_NONE}; cvar_t r_lightmap = {"r_lightmap", "0", CVAR_NONE}; cvar_t r_shadows = {"r_shadows", "0", CVAR_NONE}; cvar_t r_mirroralpha = {"r_mirroralpha", "1", CVAR_NONE}; cvar_t r_wateralpha = {"r_wateralpha", "0.33", CVAR_ARCHIVE}; cvar_t r_skyalpha = {"r_skyalpha", "0.67", CVAR_ARCHIVE}; cvar_t r_dynamic = {"r_dynamic", "1", CVAR_NONE}; cvar_t r_novis = {"r_novis", "0", CVAR_NONE}; cvar_t r_wholeframe = {"r_wholeframe", "1", CVAR_ARCHIVE}; cvar_t r_clearcolor = {"r_clearcolor", "0", CVAR_ARCHIVE}; cvar_t r_texture_external = {"r_texture_external", "0", CVAR_ARCHIVE}; cvar_t r_entdistance = {"r_entdistance", "0", CVAR_ARCHIVE}; cvar_t r_netgraph = {"r_netgraph", "0", CVAR_NONE}; cvar_t r_teamcolor = {"r_teamcolor", "187", CVAR_ARCHIVE}; cvar_t gl_clear = {"gl_clear", "1", CVAR_NONE}; cvar_t gl_cull = {"gl_cull", "1", CVAR_NONE}; cvar_t gl_ztrick = {"gl_ztrick", "0", CVAR_ARCHIVE}; cvar_t gl_zfix = {"gl_zfix", "1", CVAR_ARCHIVE}; cvar_t gl_smoothmodels = {"gl_smoothmodels", "1", CVAR_NONE}; cvar_t gl_affinemodels = {"gl_affinemodels", "0", CVAR_NONE}; cvar_t gl_polyblend = {"gl_polyblend", "1", CVAR_NONE}; cvar_t gl_flashblend = {"gl_flashblend", "0", CVAR_NONE}; cvar_t gl_playermip = {"gl_playermip", "0", CVAR_NONE}; cvar_t gl_nocolors = {"gl_nocolors", "0", CVAR_NONE}; cvar_t gl_keeptjunctions = {"gl_keeptjunctions", "1", CVAR_ARCHIVE}; cvar_t gl_reporttjunctions = {"gl_reporttjunctions", "0", CVAR_NONE}; cvar_t gl_waterripple = {"gl_waterripple", "2", CVAR_ARCHIVE}; cvar_t gl_glows = {"gl_glows", "0", CVAR_ARCHIVE}; // torch glows cvar_t gl_other_glows = {"gl_other_glows", "0", CVAR_ARCHIVE}; cvar_t gl_missile_glows = {"gl_missile_glows", "1", CVAR_ARCHIVE}; cvar_t gl_coloredlight = {"gl_coloredlight", "0", CVAR_ARCHIVE}; cvar_t gl_colored_dynamic_lights = {"gl_colored_dynamic_lights", "0", CVAR_ARCHIVE}; cvar_t gl_extra_dynamic_lights = {"gl_extra_dynamic_lights", "0", CVAR_ARCHIVE}; //============================================================================= /* ================= R_CullBox Returns true if the box is completely outside the frustom ================= */ qboolean R_CullBox (vec3_t mins, vec3_t maxs) { int i; for (i = 0; i < 4; i++) { if (BoxOnPlaneSide (mins, maxs, &frustum[i]) == 2) return true; } return false; } /* ================= R_RotateForEntity ================= */ void R_RotateForEntity (entity_t *e) { glTranslatef_fp (e->origin[0], e->origin[1], e->origin[2]); glRotatef_fp (e->angles[1], 0, 0, 1); glRotatef_fp (-e->angles[0], 0, 1, 0); glRotatef_fp (e->angles[2], 1, 0, 0); //RDM: switched sign so it matches software } /* ================= R_RotateForEntity2 Same as R_RotateForEntity(), but checks for EF_ROTATE and modifies yaw appropriately. ================= */ static void R_RotateForEntity2 (entity_t *e) { float forward, yaw, pitch; vec3_t angles; glTranslatef_fp(e->origin[0], e->origin[1], e->origin[2]); if (e->model->flags & EF_FACE_VIEW) { VectorSubtract(e->origin,r_origin,angles); VectorSubtract(r_origin,e->origin,angles); VectorNormalize(angles); if (angles[1] == 0 && angles[0] == 0) { yaw = 0; if (angles[2] > 0) pitch = 90; else pitch = 270; } else { yaw = (int) (atan2(angles[1], angles[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; forward = sqrt (angles[0]*angles[0] + angles[1]*angles[1]); pitch = (int) (atan2(angles[2], forward) * 180 / M_PI); if (pitch < 0) pitch += 360; } angles[0] = pitch; angles[1] = yaw; angles[2] = 0; glRotatef_fp (-angles[0], 0, 1, 0); glRotatef_fp (angles[1], 0, 0, 1); // glRotatef_fp (-angles[2], 1, 0, 0); glRotatef_fp (-e->angles[2], 1, 0, 0); } else { if (e->model->flags & EF_ROTATE) { glRotatef_fp (anglemod((e->origin[0] + e->origin[1])*0.8 + (108*cl.time)), 0, 0, 1); } else { glRotatef_fp (e->angles[1], 0, 0, 1); } glRotatef_fp (-e->angles[0], 0, 1, 0); glRotatef_fp (-e->angles[2], 1, 0, 0); // For clientside rotation stuff glRotatef_fp (e->angleAdd[0], 0, 1, 0); glRotatef_fp (e->angleAdd[1], 0, 0, 1); glRotatef_fp (e->angleAdd[2], 1, 0, 0); } } /* ============================================================= SPRITE MODELS ============================================================= */ /* ================ R_GetSpriteFrame ================ */ static mspriteframe_t *R_GetSpriteFrame (entity_t *e) { msprite_t *psprite; mspritegroup_t *pspritegroup; mspriteframe_t *pspriteframe; int i, numframes, frame; float *pintervals, fullinterval, targettime, time; psprite = (msprite_t *) e->model->cache.data; frame = e->frame; if ((frame >= psprite->numframes) || (frame < 0)) { Con_DPrintf ("%s: no such frame %d\n", __thisfunc__, frame); frame = 0; } if (psprite->frames[frame].type == SPR_SINGLE) { pspriteframe = psprite->frames[frame].frameptr; } else { pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; pintervals = pspritegroup->intervals; numframes = pspritegroup->numframes; fullinterval = pintervals[numframes-1]; time = cl.time + e->syncbase; // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values // are positive, so we don't have to worry about division by 0 targettime = time - ((int)(time / fullinterval)) * fullinterval; for (i = 0; i < (numframes-1); i++) { if (pintervals[i] > targettime) break; } pspriteframe = pspritegroup->frames[i]; } return pspriteframe; } /* ================= R_DrawSpriteModel ================= */ typedef struct { vec3_t vup, vright, vpn; // in worldspace } spritedesc_t; static void R_DrawSpriteModel (entity_t *e) { vec3_t point; mspriteframe_t *frame; msprite_t *psprite; vec3_t tvec; float dot, angle, sr, cr; spritedesc_t r_spritedesc; int i; frame = R_GetSpriteFrame (e); psprite = (msprite_t *) e->model->cache.data; if (psprite->type == SPR_FACING_UPRIGHT) { // generate the sprite's axes, with vup straight up in worldspace, and // r_spritedesc.vright perpendicular to modelorg. // This will not work if the view direction is very close to straight up or // down, because the cross product will be between two nearly parallel // vectors and starts to approach an undefined state, so we don't draw if // the two vectors are less than 1 degree apart tvec[0] = -modelorg[0]; tvec[1] = -modelorg[1]; tvec[2] = -modelorg[2]; VectorNormalize (tvec); dot = tvec[2]; // same as DotProduct (tvec, r_spritedesc.vup) // because r_spritedesc.vup is 0, 0, 1 if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848 return; r_spritedesc.vup[0] = 0; r_spritedesc.vup[1] = 0; r_spritedesc.vup[2] = 1; r_spritedesc.vright[0] = tvec[1]; // CrossProduct (r_spritedesc.vup, -modelorg, r_spritedesc.vright[1] = -tvec[0]; // r_spritedesc.vright) r_spritedesc.vright[2] = 0; VectorNormalize (r_spritedesc.vright); r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; r_spritedesc.vpn[1] = r_spritedesc.vright[0]; r_spritedesc.vpn[2] = 0; // CrossProduct (r_spritedesc.vright, r_spritedesc.vup, // r_spritedesc.vpn) } else if (psprite->type == SPR_VP_PARALLEL) { // generate the sprite's axes, completely parallel to the viewplane. There // are no problem situations, because the sprite is always in the same // position relative to the viewer for (i = 0; i < 3; i++) { r_spritedesc.vup[i] = vup[i]; r_spritedesc.vright[i] = vright[i]; r_spritedesc.vpn[i] = vpn[i]; } } else if (psprite->type == SPR_VP_PARALLEL_UPRIGHT) { // generate the sprite's axes, with vup straight up in worldspace, and // r_spritedesc.vright parallel to the viewplane. // This will not work if the view direction is very close to straight up or // down, because the cross product will be between two nearly parallel // vectors and starts to approach an undefined state, so we don't draw if // the two vectors are less than 1 degree apart dot = vpn[2]; // same as DotProduct (vpn, r_spritedesc.vup) // because r_spritedesc.vup is 0, 0, 1 if ((dot > 0.999848) || (dot < -0.999848)) // cos(1 degree) = 0.999848 return; r_spritedesc.vup[0] = 0; r_spritedesc.vup[1] = 0; r_spritedesc.vup[2] = 1; r_spritedesc.vright[0] = vpn[1]; // CrossProduct (r_spritedesc.vup, vpn, r_spritedesc.vright[1] = -vpn[0]; // r_spritedesc.vright) r_spritedesc.vright[2] = 0; VectorNormalize (r_spritedesc.vright); r_spritedesc.vpn[0] = -r_spritedesc.vright[1]; r_spritedesc.vpn[1] = r_spritedesc.vright[0]; r_spritedesc.vpn[2] = 0; // CrossProduct (r_spritedesc.vright, r_spritedesc.vup, // r_spritedesc.vpn) } else if (psprite->type == SPR_ORIENTED) { // generate the sprite's axes, according to the sprite's world orientation AngleVectors (e->angles, r_spritedesc.vpn, r_spritedesc.vright, r_spritedesc.vup); } else if (psprite->type == SPR_VP_PARALLEL_ORIENTED) { // generate the sprite's axes, parallel to the viewplane, but rotated in // that plane around the center according to the sprite entity's roll // angle. So vpn stays the same, but vright and vup rotate angle = e->angles[ROLL] * (M_PI*2 / 360); sr = sin(angle); cr = cos(angle); for (i = 0; i < 3; i++) { r_spritedesc.vpn[i] = vpn[i]; r_spritedesc.vright[i] = vright[i] * cr + vup[i] * sr; r_spritedesc.vup[i] = vright[i] * -sr + vup[i] * cr; } } else { Sys_Error ("%s: Bad sprite type %d", __thisfunc__, psprite->type); } /* Pa3PyX: new translucency code below if (e->drawflags & DRF_TRANSLUCENT) { glEnable_fp (GL_BLEND); glColor4f_fp (1,1,1,r_wateralpha.value); } else if (e->model->flags & EF_TRANSPARENT) { glEnable_fp (GL_BLEND); glColor3f_fp (1,1,1); } else { glEnable_fp (GL_BLEND); glColor3f_fp (1,1,1); } */ /* Pa3PyX: new translucency mechanism (doesn't look as good, should work with non 3Dfx MiniGL drivers */ if ((e->drawflags & DRF_TRANSLUCENT) || (e->model->flags & EF_TRANSPARENT)) { glDisable_fp (GL_ALPHA_TEST); glEnable_fp (GL_BLEND); glTexEnvf_fp (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f_fp (1.0f, 1.0f, 1.0f, r_wateralpha.value); } else { /* pa3pyx's alpha code looks rather ugly, use the original one. glDisable_fp (GL_BLEND); glEnable_fp (GL_ALPHA_TEST); glAlphaFunc_fp (GL_GREATER, 0.632); glColor4f_fp (1.0f, 1.0f, 1.0f, 1.0f); */ /* here, we use the original alpha code */ glEnable_fp (GL_BLEND); glColor3f_fp (1,1,1); } GL_Bind(frame->gl_texturenum); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glBegin_fp (GL_QUADS); glTexCoord2f_fp (0, 1); VectorMA (e->origin, frame->down, r_spritedesc.vup, point); VectorMA (point, frame->left, r_spritedesc.vright, point); glVertex3fv_fp (point); glTexCoord2f_fp (0, 0); VectorMA (e->origin, frame->up, r_spritedesc.vup, point); VectorMA (point, frame->left, r_spritedesc.vright, point); glVertex3fv_fp (point); glTexCoord2f_fp (1, 0); VectorMA (e->origin, frame->up, r_spritedesc.vup, point); VectorMA (point, frame->right, r_spritedesc.vright, point); glVertex3fv_fp (point); glTexCoord2f_fp (1, 1); VectorMA (e->origin, frame->down, r_spritedesc.vup, point); VectorMA (point, frame->right, r_spritedesc.vright, point); glVertex3fv_fp (point); glEnd_fp (); // restore tex parms glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); /* for pa3pyx's translucency code changes above glDisable_fp (GL_BLEND); */ if ((e->drawflags & DRF_TRANSLUCENT) || (e->model->flags & EF_TRANSPARENT)) { glDisable_fp (GL_BLEND); glTexEnvf_fp (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } else { /* not using pa3pyx's alpha code (see above) glDisable_fp (GL_ALPHA_TEST); */ glDisable_fp (GL_BLEND); } } /* ============================================================= ALIAS MODELS ============================================================= */ static vec3_t shadevector; static float shadelight, ambientlight; // precalculated dot products for quantized angles #define SHADEDOT_QUANT 16 static float r_avertexnormal_dots[SHADEDOT_QUANT][256] = { #include "anorm_dots.h" }; static float *shadedots = r_avertexnormal_dots[0]; static int lastposenum; /* ============= GL_DrawAliasFrame ============= */ static void GL_DrawAliasFrame (entity_t *e, aliashdr_t *paliashdr, int posenum) { float l; trivertx_t *verts; int *order; int count; float r, g, b; byte ColorShade; char client_team[16], this_team[16]; // qboolean OnTeam = false; int i, my_team, ve_team; lastposenum = posenum; verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts += posenum * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); ColorShade = e->colorshade; i = e->scoreboard - cl.players; if (i >= 0 && i < MAX_CLIENTS) { my_team = cl.players[cl.playernum].siege_team; ve_team = cl.players[i].siege_team; if ((ambientlight+shadelight) > 50 || (cl_siege && my_team == ve_team)) cl.players[i].shownames_off = false; else cl.players[i].shownames_off = true; if (cl_siege) { if (cl.players[cl.playernum].playerclass == CLASS_DWARF && e->skinnum == 101) { ColorShade = 133; if (ambientlight < 128) shadelight += (128 - ambientlight); cl.players[i].shownames_off = false; } else if (cl.players[cl.playernum].playerclass == CLASS_DWARF && (ambientlight+shadelight) < 51) { // OOps, use darkmaps in GL ColorShade = 128 + (int)((ambientlight+shadelight)/5); shadelight += (51 - ambientlight); cl.players[i].shownames_off = false; } else if (ve_team == ST_DEFENDER) { // tint gold since we can't have seperate skins // OnTeam = true; ColorShade = 165; } } else { q_strlcpy (client_team, Info_ValueForKey(cl.players[cl.playernum].userinfo, "team"), sizeof(client_team)); if (client_team[0]) { q_strlcpy (this_team, Info_ValueForKey(cl.players[i].userinfo, "team"), sizeof(this_team)); if (q_strcasecmp(client_team, this_team) == 0) { // OnTeam = true; ColorShade = r_teamcolor.value; } } } } if (ColorShade) { r = RTint[ColorShade]; g = GTint[ColorShade]; b = BTint[ColorShade]; } else r = g = b = 1; while (1) { // get the vertex count and primitive type count = *order++; if (!count) break; // done if (count < 0) { count = -count; glBegin_fp (GL_TRIANGLE_FAN); } else glBegin_fp (GL_TRIANGLE_STRIP); do { // texture coordinates come from the draw list glTexCoord2f_fp (((float *)order)[0], ((float *)order)[1]); order += 2; // normals and vertexes come from the frame list if (gl_lightmap_format == GL_RGBA) { l = shadedots[verts->lightnormalindex]; glColor4f_fp (l * lightcolor[0], l * lightcolor[1], l * lightcolor[2], model_constant_alpha); } else { l = shadedots[verts->lightnormalindex] * shadelight; glColor4f_fp (r*l, g*l, b*l, model_constant_alpha); } glVertex3f_fp (verts->v[0], verts->v[1], verts->v[2]); verts++; } while (--count); glEnd_fp (); } } /* ============= GL_DrawAliasShadow ============= */ static void GL_DrawAliasShadow (entity_t *e, aliashdr_t *paliashdr, int posenum) { trivertx_t *verts; int *order; vec3_t point; float height, lheight; int count; lheight = e->origin[2] - lightspot[2]; height = 0; verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts += posenum * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); height = -lheight + 1.0; if (have_stencil) { glEnable_fp(GL_STENCIL_TEST); glStencilFunc_fp(GL_EQUAL,1,2); glStencilOp_fp(GL_KEEP,GL_KEEP,GL_INCR); } while (1) { // get the vertex count and primitive type count = *order++; if (!count) break; // done if (count < 0) { count = -count; glBegin_fp (GL_TRIANGLE_FAN); } else glBegin_fp (GL_TRIANGLE_STRIP); do { // texture coordinates come from the draw list // (skipped for shadows) glTexCoord2fv_fp ((float *)order); order += 2; // normals and vertexes come from the frame list point[0] = verts->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0]; point[1] = verts->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1]; point[2] = verts->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2]; point[0] -= shadevector[0]*(point[2]+lheight); point[1] -= shadevector[1]*(point[2]+lheight); point[2] = height; // height -= 0.001; glVertex3fv_fp (point); verts++; } while (--count); glEnd_fp (); } if (have_stencil) glDisable_fp(GL_STENCIL_TEST); } /* ================= R_SetupAliasFrame ================= */ static void R_SetupAliasFrame (entity_t *e, aliashdr_t *paliashdr) { int pose, numposes, frame; float interval; frame = e->frame; if ((frame >= paliashdr->numframes) || (frame < 0)) { Con_DPrintf ("%s: no such frame %d\n", __thisfunc__, frame); frame = 0; } pose = paliashdr->frames[frame].firstpose; numposes = paliashdr->frames[frame].numposes; if (numposes > 1) { interval = paliashdr->frames[frame].interval; pose += (int)(cl.time / interval) % numposes; } GL_DrawAliasFrame (e, paliashdr, pose); } /* ================= R_DrawAliasModel ================= */ static void AliasModelGetLightInfo (entity_t *e) { vec3_t adjust_origin; VectorCopy(e->origin, adjust_origin); adjust_origin[2] += (e->model->mins[2] + e->model->maxs[2]) / 2; if (gl_lightmap_format == GL_RGBA) ambientlight = R_LightPointColor (adjust_origin); else ambientlight = shadelight = R_LightPoint (adjust_origin); } static void R_DrawAliasModel (entity_t *e) { int i; int lnum; vec3_t dist; float add; qmodel_t *clmodel; vec3_t mins, maxs; aliashdr_t *paliashdr; float an; static float tmatrix[3][4]; float entScale; float xyfact = 1.0, zfact = 1.0; // avoid compiler warning int skinnum; int mls; clmodel = e->model; VectorAdd (e->origin, clmodel->mins, mins); VectorAdd (e->origin, clmodel->maxs, maxs); if (!AlwaysDrawModel && R_CullBox (mins, maxs)) return; VectorCopy (e->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); // if shadows are enabled, get lighting information here regardless // of special cases below, because R_LightPoint[Color]() calculates // lightspot for us which is used by GL_DrawAliasShadow() if (r_shadows.integer && e != &cl.viewent) AliasModelGetLightInfo (e); mls = e->drawflags & MLS_MASKIN; if (e->model->flags & EF_ROTATE) { ambientlight = shadelight = lightcolor[0] = lightcolor[1] = lightcolor[2] = 60 + 34 + sin(e->origin[0] + e->origin[1] + (cl.time*3.8)) * 34; } else if (mls == MLS_ABSLIGHT) { lightcolor[0] = lightcolor[1] = lightcolor[2] = ambientlight = shadelight = e->abslight; } else if (mls != MLS_NONE) { // Use a model light style (25-30) lightcolor[0] = lightcolor[1] = lightcolor[2] = ambientlight = shadelight = d_lightstylevalue[24+mls]/2; } else if (e != &cl.viewent) // R_DrawViewModel() already does viewmodel lighting. { if (!r_shadows.integer) AliasModelGetLightInfo (e); for (lnum = 0; lnum < MAX_DLIGHTS; lnum++) { if (cl_dlights[lnum].die >= cl.time) { VectorSubtract (e->origin, cl_dlights[lnum].origin, dist); add = cl_dlights[lnum].radius - VectorLengthFast(dist); if (add > 0) { ambientlight += add; //ZOID models should be affected by dlights as well shadelight += add; lightcolor[0] += (cl_dlights[lnum].color[0] * add); lightcolor[1] += (cl_dlights[lnum].color[1] * add); lightcolor[2] += (cl_dlights[lnum].color[2] * add); } } } // clamp lighting so it doesn't overbright as much if (ambientlight > 128) ambientlight = 128; if (ambientlight + shadelight > 192) shadelight = 192 - ambientlight; } shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; shadelight = shadelight / 200.0; VectorScale(lightcolor, 1.0f / 200.0f, lightcolor); an = e->angles[1] / 180 * M_PI; shadevector[0] = cos(-an); shadevector[1] = sin(-an); shadevector[2] = 1; VectorNormalize (shadevector); // // locate the proper data // paliashdr = (aliashdr_t *)Mod_Extradata (e->model); c_alias_polys += paliashdr->numtris; // // draw all the triangles // glPushMatrix_fp (); R_RotateForEntity2(e); if (e->scale != 0 && e->scale != 100) { entScale = (float)e->scale / 100.0f; switch (e->drawflags & SCALE_TYPE_MASKIN) { case SCALE_TYPE_UNIFORM: tmatrix[0][0] = paliashdr->scale[0]*entScale; tmatrix[1][1] = paliashdr->scale[1]*entScale; tmatrix[2][2] = paliashdr->scale[2]*entScale; xyfact = zfact = (entScale-1.0)*127.95; break; case SCALE_TYPE_XYONLY: tmatrix[0][0] = paliashdr->scale[0]*entScale; tmatrix[1][1] = paliashdr->scale[1]*entScale; tmatrix[2][2] = paliashdr->scale[2]; xyfact = (entScale-1.0)*127.95; zfact = 1.0; break; case SCALE_TYPE_ZONLY: tmatrix[0][0] = paliashdr->scale[0]; tmatrix[1][1] = paliashdr->scale[1]; tmatrix[2][2] = paliashdr->scale[2]*entScale; xyfact = 1.0; zfact = (entScale-1.0)*127.95; break; } switch (e->drawflags & SCALE_ORIGIN_MASKIN) { case SCALE_ORIGIN_CENTER: tmatrix[0][3] = paliashdr->scale_origin[0]-paliashdr->scale[0]*xyfact; tmatrix[1][3] = paliashdr->scale_origin[1]-paliashdr->scale[1]*xyfact; tmatrix[2][3] = paliashdr->scale_origin[2]-paliashdr->scale[2]*zfact; break; case SCALE_ORIGIN_BOTTOM: tmatrix[0][3] = paliashdr->scale_origin[0]-paliashdr->scale[0]*xyfact; tmatrix[1][3] = paliashdr->scale_origin[1]-paliashdr->scale[1]*xyfact; tmatrix[2][3] = paliashdr->scale_origin[2]; break; case SCALE_ORIGIN_TOP: tmatrix[0][3] = paliashdr->scale_origin[0]-paliashdr->scale[0]*xyfact; tmatrix[1][3] = paliashdr->scale_origin[1]-paliashdr->scale[1]*xyfact; tmatrix[2][3] = paliashdr->scale_origin[2]-paliashdr->scale[2]*zfact*2.0; break; } } else { tmatrix[0][0] = paliashdr->scale[0]; tmatrix[1][1] = paliashdr->scale[1]; tmatrix[2][2] = paliashdr->scale[2]; tmatrix[0][3] = paliashdr->scale_origin[0]; tmatrix[1][3] = paliashdr->scale_origin[1]; tmatrix[2][3] = paliashdr->scale_origin[2]; } if (clmodel->flags & EF_ROTATE) { // Floating motion tmatrix[2][3] += sin(e->origin[0] + e->origin[1] + (cl.time*3)) * 5.5; } if (e == &cl.viewent && scr_fov.integer > 90) /* compensate viewmodel distortion at fov>90 */ { float fovscale = tan(scr_fov.value * (0.5 * M_PI / 180)); glTranslatef_fp (tmatrix[0][3], tmatrix[1][3] * fovscale, tmatrix[2][3] * fovscale); // paliashdr->scale_origin[0..2] glScalef_fp (tmatrix[0][0], tmatrix[1][1] * fovscale, tmatrix[2][2] * fovscale); // paliashdr->scale[0..2] } else { glTranslatef_fp (tmatrix[0][3], tmatrix[1][3], tmatrix[2][3]); // paliashdr->scale_origin[0..2] glScalef_fp (tmatrix[0][0], tmatrix[1][1], tmatrix[2][2]); // paliashdr->scale[0..2] } if (e->model->flags & EF_SPECIAL_TRANS) { glEnable_fp (GL_BLEND); glBlendFunc_fp (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); // glColor3f_fp (1,1,1); model_constant_alpha = 1.0f; glDisable_fp (GL_CULL_FACE); } else if (e->drawflags & DRF_TRANSLUCENT) { glEnable_fp (GL_BLEND); // glColor4f_fp (1,1,1,r_wateralpha.value); model_constant_alpha = r_wateralpha.value; } else if (e->model->flags & EF_TRANSPARENT) { glEnable_fp (GL_BLEND); // glColor3f_fp (1,1,1); model_constant_alpha = 1.0f; } else if (e->model->flags & EF_HOLEY) { glEnable_fp (GL_BLEND); // glColor3f_fp (1,1,1); model_constant_alpha = 1.0f; } else { glColor3f_fp (1,1,1); model_constant_alpha = 1.0f; } // if (cl.players[e->scoreboard - cl.players].siege_team == ST_DEFENDER) // e->skinnum = cl.players[e->scoreboard - cl.players].playerclass + 110; skinnum = e->skinnum; if (skinnum >= 100) { if (skinnum > 255) Sys_Error ("skinnum > 255"); if (gl_extra_textures[skinnum - 100] == GL_UNUSED_TEXTURE) // Need to load it in { qpic_t *stonepic; glpic_t *gl; char temp[80]; q_snprintf (temp, sizeof(temp), "gfx/skin%d.lmp", skinnum); stonepic = Draw_CachePic(temp); gl = (glpic_t *)stonepic->data; gl_extra_textures[skinnum - 100] = gl->texnum; } GL_Bind(gl_extra_textures[skinnum - 100]); } else { int anim = (int)(cl.time*10) & 3; if ((skinnum >= paliashdr->numskins) || (skinnum < 0)) { Con_DPrintf ("%s: no such skin # %d\n", __thisfunc__, skinnum); skinnum = 0; } GL_Bind(paliashdr->gl_texturenum[skinnum][anim]); // we can't dynamically colormap textures, so they are cached // seperately for the players. Heads are just uncolored. if (e->colormap != vid.colormap && !gl_nocolors.integer) { // FIXME? What about Demoness and Dwarf? if (e->model == player_models[0] || e->model == player_models[1] || e->model == player_models[2] || e->model == player_models[3]) { i = e->scoreboard - cl.players; if (i >= 0 && i < MAX_CLIENTS) { if (!cl.players[i].Translated) R_TranslatePlayerSkin(i); GL_Bind(playertextures[i]); } } } } if (gl_smoothmodels.integer) glShadeModel_fp (GL_SMOOTH); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); #if defined(__AMIGA__) && defined(REFGL_MINIGL) if (gl_affinemodels.integer) glDisable_fp (MGL_PERSPECTIVE_MAPPING); #else if (gl_affinemodels.integer) glHint_fp (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); #endif R_SetupAliasFrame (e, paliashdr); // restore params if ((e->drawflags & DRF_TRANSLUCENT) || (e->model->flags & EF_SPECIAL_TRANS) || (e->model->flags & EF_TRANSPARENT) || (e->model->flags & EF_HOLEY) ) { glDisable_fp (GL_BLEND); } if (e->model->flags & EF_SPECIAL_TRANS) { glBlendFunc_fp (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable_fp (GL_CULL_FACE); } glTexEnvf_fp (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glShadeModel_fp (GL_FLAT); #if defined(__AMIGA__) && defined(REFGL_MINIGL) if (gl_affinemodels.integer) glEnable_fp (MGL_PERSPECTIVE_MAPPING); #else if (gl_affinemodels.integer) glHint_fp (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); #endif glPopMatrix_fp (); if (r_shadows.integer) { glPushMatrix_fp (); R_RotateForEntity2 (e); glDisable_fp (GL_TEXTURE_2D); glEnable_fp (GL_BLEND); glColor4f_fp (0,0,0,0.5); glDepthMask_fp (0); // prevent Z fighting GL_DrawAliasShadow (e, paliashdr, lastposenum); glDepthMask_fp (1); glEnable_fp (GL_TEXTURE_2D); glDisable_fp (GL_BLEND); glColor4f_fp (1,1,1,1); glPopMatrix_fp (); } } //============================================================================= typedef struct sortedent_s { entity_t *ent; vec_t len; } sortedent_t; static sortedent_t cl_transvisedicts[MAX_VISEDICTS]; static sortedent_t cl_transwateredicts[MAX_VISEDICTS]; static int cl_numtransvisedicts; static int cl_numtranswateredicts; /* ============= R_DrawEntitiesOnList ============= */ static void R_DrawEntitiesOnList (void) { int i; qboolean item_trans; mleaf_t *pLeaf; entity_t *e; vec3_t diff; int test_length, calc_length; cl_numtransvisedicts = 0; cl_numtranswateredicts = 0; if (!r_drawentities.integer) return; test_length = (r_entdistance.value <= 0) ? (9999 * 9999) : r_entdistance.value * r_entdistance.value; // draw sprites seperately, because of alpha blending for (i = 0; i < cl_numvisedicts; i++) { e = &cl_visedicts[i]; // if (e->drawflags & 5) // MLS_INVIS - but dwarf can see // if (cl.v.playerclass != CLASS_DWARF) // continue; switch (e->model->type) { case mod_alias: VectorSubtract(e->origin, r_origin, diff); calc_length = DotProduct(diff,diff); if (calc_length > test_length) continue; item_trans = ((e->drawflags & DRF_TRANSLUCENT) || (e->model->flags & (EF_TRANSPARENT|EF_HOLEY|EF_SPECIAL_TRANS))) != 0; if (!item_trans) R_DrawAliasModel (e); break; case mod_brush: item_trans = (e->drawflags & DRF_TRANSLUCENT) != 0; if (!item_trans) R_DrawBrushModel (e,false); break; case mod_sprite: VectorSubtract(e->origin, r_origin, diff); calc_length = DotProduct(diff,diff); if (calc_length > test_length) continue; item_trans = true; break; default: item_trans = false; break; } if (item_trans) { pLeaf = Mod_PointInLeaf (e->origin, cl.worldmodel); // if (pLeaf->contents == CONTENTS_EMPTY) if (pLeaf->contents != CONTENTS_WATER) cl_transvisedicts[cl_numtransvisedicts++].ent = e; else cl_transwateredicts[cl_numtranswateredicts++].ent = e; } } } /* ================ R_DrawTransEntitiesOnList Implemented by: jack ================ */ static int transCompare (const void *arg1, const void *arg2) { const sortedent_t *a1, *a2; a1 = (sortedent_t *) arg1; a2 = (sortedent_t *) arg2; return (a2->len - a1->len); // Sorted in reverse order. Neat, huh? } static void R_DrawTransEntitiesOnList (qboolean inwater) { int i; int numents; sortedent_t *theents; entity_t *e; int depthMaskWrite = 0; vec3_t result; theents = (inwater) ? cl_transwateredicts : cl_transvisedicts; numents = (inwater) ? cl_numtranswateredicts : cl_numtransvisedicts; for (i = 0; i < numents; i++) { VectorSubtract(theents[i].ent->origin, r_origin, result); // theents[i].len = VectorLength(result); theents[i].len = (result[0] * result[0]) + (result[1] * result[1]) + (result[2] * result[2]); } qsort((void *) theents, numents, sizeof(sortedent_t), transCompare); // Add in BETTER sorting here glDepthMask_fp(0); for (i = 0; i < numents; i++) { e = theents[i].ent; switch (e->model->type) { case mod_alias: if (!depthMaskWrite) { depthMaskWrite = 1; glDepthMask_fp(1); } R_DrawAliasModel (e); break; case mod_brush: if (!depthMaskWrite) { depthMaskWrite = 1; glDepthMask_fp(1); } R_DrawBrushModel (e, true); break; case mod_sprite: if (depthMaskWrite) { depthMaskWrite = 0; glDepthMask_fp(0); } R_DrawSpriteModel (e); break; } } if (!depthMaskWrite) glDepthMask_fp(1); } //============================================================================= // Glow styles. These rely on unchanged game code! #define TORCH_STYLE 1 /* Flicker */ #define MISSILE_STYLE 6 /* Flicker */ #define PULSE_STYLE 11 /* Slow pulse */ static void R_DrawGlow (entity_t *e) { qmodel_t *clmodel; clmodel = e->model; // Torches & Flames if ((gl_glows.integer && (clmodel->ex_flags & XF_TORCH_GLOW)) || (gl_missile_glows.integer && (clmodel->ex_flags & XF_MISSILE_GLOW)) || (gl_other_glows.integer && (clmodel->ex_flags & XF_GLOW)) ) { // NOTE: It would be better if we batched these up. // All those state changes are not nice. KH vec3_t lightorigin; // Origin of torch. vec3_t glow_vect; // Vector to torch. float radius; // Radius of torch flare. float distance; // Vector distance to torch. float intensity; // Intensity of torch flare. int i, j; vec3_t vp2; // NOTE: I don't think this is centered on the model. VectorCopy(e->origin, lightorigin); radius = 20.0f; // for mana, make it bit bigger if ( !q_strncasecmp(clmodel->name, "models/i_btmana", 15)) radius += 5.0f; VectorSubtract(lightorigin, r_origin, vp2); // See if view is outside the light. distance = VectorLengthFast(vp2); if (distance > radius) { VectorNormalizeFast(vp2); glPushMatrix_fp(); // Translate the glow to coincide with the flame. KH if (clmodel->ex_flags & XF_TORCH_GLOW) { if (clmodel->ex_flags & XF_TORCH_GLOW_EGYPT) // egypt torch fix glTranslatef_fp (cos(e->angles[1]/180*M_PI)*8.0f, sin(e->angles[1]/180*M_PI)*8.0f, 16.0f); else glTranslatef_fp (0.0f, 0.0f, 8.0f); } // 'floating' movement if (clmodel->flags & EF_ROTATE) glTranslatef_fp (0, 0, sin(e->origin[0] + e->origin[1] + (cl.time*3))*5.5); glBegin_fp(GL_TRIANGLE_FAN); // Diminish torch flare inversely with distance. intensity = (1024.0f - distance) / 1024.0f; // Invert (fades as you approach). intensity = (1.0f - intensity); // Clamp, but don't let the flare disappear. if (intensity > 1.0f) intensity = 1.0f; else if (intensity < 0.0f) intensity = 0.0f; // Now modulate with flicker. j = 0; // avoid compiler warning if (clmodel->ex_flags & XF_TORCH_GLOW) { i = (int)(cl.time*10); if (!cl_lightstyle[TORCH_STYLE].length) { j = 256; } else { j = i % cl_lightstyle[TORCH_STYLE].length; j = cl_lightstyle[TORCH_STYLE].map[j] - 'a'; j = j * 22; } } else if (clmodel->ex_flags & XF_MISSILE_GLOW) { i = (int)(cl.time*10); if (!cl_lightstyle[MISSILE_STYLE].length) { j = 256; } else { j = i % cl_lightstyle[MISSILE_STYLE].length; j = cl_lightstyle[MISSILE_STYLE].map[j] - 'a'; j = j * 22; } } else if (clmodel->ex_flags & XF_GLOW) { i = (int)(cl.time*10); if (!cl_lightstyle[PULSE_STYLE].length) { j = 256; } else { j = i % cl_lightstyle[PULSE_STYLE].length; j = cl_lightstyle[PULSE_STYLE].map[j] - 'a'; j = j * 22; } } intensity *= ((float)j / 255.0f); glColor4f_fp (clmodel->glow_color[0]*intensity, clmodel->glow_color[1]*intensity, clmodel->glow_color[2]*intensity, clmodel->glow_color[3]); for (i = 0; i < 3; i++) glow_vect[i] = lightorigin[i] - vp2[i]*radius; glVertex3fv_fp(glow_vect); glColor4f_fp(0.0f, 0.0f, 0.0f, 1.0f); for (i = 16; i >= 0; i--) { float a = i / 16.0f * M_PI * 2; for (j = 0; j < 3; j++) glow_vect[j] = lightorigin[j] + vright[j]*cos(a)*radius + vup[j]*sin(a)*radius; glVertex3fv_fp(glow_vect); } glEnd_fp(); glColor4f_fp (0.0f, 0.0f, 0.0f, 1.0f); // Restore previous matrix glPopMatrix_fp(); } } } static void R_DrawAllGlows (void) { int i; entity_t *e; if (!gl_glows.integer && !gl_missile_glows.integer && !gl_other_glows.integer) return; if (!r_drawentities.integer) return; glDepthMask_fp (0); glDisable_fp (GL_TEXTURE_2D); glShadeModel_fp (GL_SMOOTH); glEnable_fp (GL_BLEND); glBlendFunc_fp (GL_ONE, GL_ONE); for (i = 0; i < cl_numvisedicts; i++) { e = &cl_visedicts[i]; if (e->model->type == mod_alias) R_DrawGlow (e); } glDisable_fp (GL_BLEND); glEnable_fp (GL_TEXTURE_2D); glBlendFunc_fp (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask_fp (1); glShadeModel_fp (GL_FLAT); } //============================================================================= /* ============= R_DrawViewModel ============= */ static void R_DrawViewModel (void) { int lnum; vec3_t dist; float add; dlight_t *dl; entity_t *e; if (cl.spectator) return; e = &cl.viewent; if (!e->model) return; if (gl_lightmap_format == GL_RGBA) { ambientlight = R_LightPointColor (e->origin); if (lightcolor[0] < 24) lightcolor[0] = 24; if (lightcolor[1] < 24) lightcolor[1] = 24; if (lightcolor[2] < 24) lightcolor[2] = 24; if (ambientlight < 24) ambientlight = 24; // always give some light on gun } else { ambientlight = shadelight = R_LightPoint (e->origin); if (ambientlight < 24) ambientlight = shadelight = 24; // always give some light on gun } // add dynamic lights for (lnum = 0; lnum < MAX_DLIGHTS; lnum++) { dl = &cl_dlights[lnum]; if (!dl->radius) continue; if (dl->die < cl.time) continue; VectorSubtract (e->origin, dl->origin, dist); add = dl->radius - VectorLengthFast(dist); if (add > 0) { if (gl_lightmap_format == GL_RGBA) { lightcolor[0] += (float) (dl->color[0] * add); lightcolor[1] += (float) (dl->color[1] * add); lightcolor[2] += (float) (dl->color[2] * add); } else { shadelight += (float) add; } ambientlight += add; } } cl.light_level = ambientlight; if ((cl.v.health <= 0) || //rjr (cl.items & IT_INVISIBILITY) || (!r_drawviewmodel.integer) || (!r_drawentities.integer)) { return; } // hack the depth range to prevent view model from poking into walls glDepthRange_fp (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin)); AlwaysDrawModel = true; R_DrawAliasModel (e); AlwaysDrawModel = false; glDepthRange_fp (gldepthmin, gldepthmax); } //============================================================================= /* =============== R_MarkLeaves =============== */ static void R_MarkLeaves (void) { byte *vis; mnode_t *node; int i; byte solid[4096]; if (r_oldviewleaf == r_viewleaf && !r_novis.integer) return; if (mirror) return; r_visframecount++; r_oldviewleaf = r_viewleaf; if (r_novis.integer) { vis = solid; memset (solid, 0xff, (cl.worldmodel->numleafs+7)>>3); } else vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); for (i = 0; i < cl.worldmodel->numleafs; i++) { if ( vis[i>>3] & (1<<(i&7)) ) { node = (mnode_t *)&cl.worldmodel->leafs[i+1]; do { if (node->visframe == r_visframecount) break; node->visframe = r_visframecount; node = node->parent; } while (node); } } } //============================================================================= /* ================= GL_DrawBlendPoly Renders a polygon covering the whole screen. For fullscreen color blending and approximated gamma correction. To be called from R_PolyBlend(). ================= */ static void GL_DrawBlendPoly (void) { glBegin_fp (GL_QUADS); glVertex3f_fp (10, 100, 100); glVertex3f_fp (10, -100, 100); glVertex3f_fp (10, -100, -100); glVertex3f_fp (10, 100, -100); glEnd_fp (); } /* ================= GL_DoGamma Uses GL_DrawBlendPoly() for gamma correction. Idea originally from LordHavoc. This trick is useful if normal ways of gamma adjustment fail: In case of 3dfx Voodoo1/2/Rush, we can't use 3dfx specific extensions in unix, so this can be our friend at a cost of 4-5 fps. To be called from R_PolyBlend(). ================= */ #if 0 static void GL_DoGamma (void) { if (v_gamma.value >= 1) return; glBlendFunc_fp (GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); glColor4f_fp (1, 1, 1, v_gamma.value); GL_DrawBlendPoly (); glBlendFunc_fp (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } #endif /* ============ R_PolyBlend ============ */ static void R_PolyBlend (void) { if (!gl_polyblend.integer) return; glEnable_fp (GL_BLEND); glDisable_fp (GL_DEPTH_TEST); glDisable_fp (GL_TEXTURE_2D); glLoadIdentity_fp (); glRotatef_fp (-90, 1, 0, 0); // put Z going up glRotatef_fp (90, 0, 0, 1); // put Z going up if (v_blend[3]) { glColor4fv_fp (v_blend); GL_DrawBlendPoly (); } /*GL_DoGamma ();*/ glDisable_fp (GL_BLEND); glEnable_fp (GL_TEXTURE_2D); glEnable_fp (GL_ALPHA_TEST); } //============================================================================= static int SignbitsForPlane (mplane_t *out) { int bits, j; // for fast box on planeside test bits = 0; for (j = 0; j < 3; j++) { if (out->normal[j] < 0) bits |= 1<contents); V_CalcBlend (); r_cache_thrash = false; c_brush_polys = 0; c_alias_polys = 0; } #define NEARCLIP 4 #define FARCLIP 4096 static void GL_SetFrustum (GLdouble fovx, GLdouble fovy) { GLdouble xmax, ymax; xmax = NEARCLIP * tan(fovx * M_PI / 360.0); ymax = NEARCLIP * tan(fovy * M_PI / 360.0); glFrustum_fp (-xmax, xmax, -ymax, ymax, NEARCLIP, FARCLIP); } /* ============= R_SetupGL ============= */ static void R_SetupGL (void) { int x, x2, y2, y, w, h; // // set up viewpoint // glMatrixMode_fp(GL_PROJECTION); glLoadIdentity_fp (); x = r_refdef.vrect.x * glwidth/vid.width; x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * glwidth/vid.width; y = (vid.height - r_refdef.vrect.y) * glheight/vid.height; y2 = (vid.height - (r_refdef.vrect.y + r_refdef.vrect.height)) * glheight/vid.height; // fudge around because of frac screen scale if (x > 0) x--; if (x2 < glwidth) x2++; if (y2 < 0) y2--; if (y < glheight) y++; w = x2 - x; h = y - y2; glViewport_fp (glx + x, gly + y2, w, h); GL_SetFrustum (r_refdef.fov_x, r_refdef.fov_y); if (mirror) { if (mirror_plane->normal[2]) glScalef_fp (1, -1, 1); else glScalef_fp (-1, 1, 1); glCullFace_fp(GL_BACK); } else glCullFace_fp(GL_FRONT); glMatrixMode_fp(GL_MODELVIEW); glLoadIdentity_fp (); glRotatef_fp (-90, 1, 0, 0); // put Z going up glRotatef_fp (90, 0, 0, 1); // put Z going up glRotatef_fp (-r_refdef.viewangles[2], 1, 0, 0); glRotatef_fp (-r_refdef.viewangles[0], 0, 1, 0); glRotatef_fp (-r_refdef.viewangles[1], 0, 0, 1); glTranslatef_fp (-r_refdef.vieworg[0], -r_refdef.vieworg[1], -r_refdef.vieworg[2]); glGetFloatv_fp (GL_MODELVIEW_MATRIX, r_world_matrix); glGetFloatv_fp (GL_PROJECTION_MATRIX, r_projection_matrix); // // set drawing parms // if (gl_cull.integer) glEnable_fp(GL_CULL_FACE); else glDisable_fp(GL_CULL_FACE); glDisable_fp(GL_BLEND); glDisable_fp(GL_ALPHA_TEST); glEnable_fp(GL_DEPTH_TEST); } /* ================ R_RenderScene r_refdef must be set before the first call ================ */ static void R_RenderScene (void) { R_SetupFrame (); R_SetFrustum (); R_SetupGL (); R_MarkLeaves (); // done here so we know if we're in water R_DrawWorld (); // adds static entities to the list S_ExtraUpdate (); // don't let sound get messed up if going slow R_DrawEntitiesOnList (); R_DrawAllGlows(); R_RenderDlights (); } /* ============= R_Clear ============= */ static void R_Clear (void) { if (r_mirroralpha.value != 1.0) { if (gl_clear.integer) glClear_fp (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); else glClear_fp (GL_DEPTH_BUFFER_BIT); gldepthmin = 0; gldepthmax = 0.5; glDepthFunc_fp (GL_LEQUAL); } else if (gl_ztrick.integer) { static int trickframe; if (gl_clear.integer) glClear_fp (GL_COLOR_BUFFER_BIT); trickframe++; if (trickframe & 1) { gldepthmin = 0; gldepthmax = 0.49999; glDepthFunc_fp (GL_LEQUAL); } else { gldepthmin = 1; gldepthmax = 0.5; glDepthFunc_fp (GL_GEQUAL); } } else { if (gl_clear.integer) glClear_fp (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); else glClear_fp (GL_DEPTH_BUFFER_BIT); gldepthmin = 0; gldepthmax = 1; glDepthFunc_fp (GL_LEQUAL); } glDepthRange_fp (gldepthmin, gldepthmax); if (have_stencil && r_shadows.integer) { glClearStencil_fp(1); glClear_fp(GL_STENCIL_BUFFER_BIT); } } #if 0 /* !!! FIXME, Zoid, mirror is disabled for now */ /* ============= R_Mirror ============= */ static float r_base_world_matrix[16]; static void R_Mirror (void) { float d; msurface_t *s; entity_t *ent; if (!mirror) return; memcpy (r_base_world_matrix, r_world_matrix, sizeof(r_base_world_matrix)); d = DotProduct (r_refdef.vieworg, mirror_plane->normal) - mirror_plane->dist; VectorMA (r_refdef.vieworg, -2*d, mirror_plane->normal, r_refdef.vieworg); d = DotProduct (vpn, mirror_plane->normal); VectorMA (vpn, -2*d, mirror_plane->normal, vpn); r_refdef.viewangles[0] = -asin (vpn[2])/M_PI*180; r_refdef.viewangles[1] = atan2 (vpn[1], vpn[0])/M_PI*180; r_refdef.viewangles[2] = -r_refdef.viewangles[2]; ent = &cl_entities[cl.viewentity]; if (cl_numvisedicts < MAX_VISEDICTS) { cl_visedicts[cl_numvisedicts] = ent; cl_numvisedicts++; } gldepthmin = 0.5; gldepthmax = 1; glDepthRange_fp (gldepthmin, gldepthmax); glDepthFunc_fp (GL_LEQUAL); glDepthMask_fp(0); R_DrawParticles (); // THIS IS THE F*S*D(KCING MIRROR ROUTINE! Go down!!! R_DrawTransEntitiesOnList (true); // This restores the depth mask R_DrawWaterSurfaces (); R_DrawTransEntitiesOnList (false); gldepthmin = 0; gldepthmax = 0.5; glDepthRange_fp (gldepthmin, gldepthmax); glDepthFunc_fp (GL_LEQUAL); // blend on top glEnable_fp (GL_BLEND); glMatrixMode_fp(GL_PROJECTION); if (mirror_plane->normal[2]) glScalef_fp (1,-1,1); else glScalef_fp (-1,1,1); glCullFace_fp(GL_FRONT); glMatrixMode_fp(GL_MODELVIEW); glLoadMatrixf_fp (r_base_world_matrix); glColor4f_fp (1,1,1,r_mirroralpha.value); s = cl.worldmodel->textures[mirrortexturenum]->texturechain; for ( ; s ; s = s->texturechain) R_RenderBrushPoly (&r_worldentity, s, true); cl.worldmodel->textures[mirrortexturenum]->texturechain = NULL; glDisable_fp (GL_BLEND); glColor4f_fp (1,1,1,1); } #endif /* ============= R_PrintTimes ============= */ static void R_PrintTimes (void) { float r_time2; float ms, fps; r_lasttime1 = r_time2 = Sys_DoubleTime(); ms = 1000 * (r_time2 - r_time1); fps = 1000 / ms; Con_Printf("%3.1f fps %5.0f ms\n%4i wpoly %4i epoly %4i(%i) edicts\n", fps, ms, c_brush_polys, c_alias_polys, cl_numvisedicts, cl_numtransvisedicts+cl_numtranswateredicts); } void R_TransformModelToClip (const vec3_t src, const float *modelMatrix, const float *projectionMatrix, vec4_t eye, vec4_t dst) { int i; for (i = 0; i < 4; i++) { eye[i] = src[0] * modelMatrix[i + 0 * 4] + src[1] * modelMatrix[i + 1 * 4] + src[2] * modelMatrix[i + 2 * 4] + modelMatrix[i + 3 * 4]; } for (i = 0; i < 4; i++) { dst[i] = eye[0] * projectionMatrix[i + 0 * 4] + eye[1] * projectionMatrix[i + 1 * 4] + eye[2] * projectionMatrix[i + 2 * 4] + eye[3] * projectionMatrix[i + 3 * 4]; } } void R_TransformClipToWindow (const vec4_t clip, vec4_t normalized, vec4_t window) { normalized[0] = clip[0] / clip[3]; normalized[1] = clip[1] / clip[3]; normalized[2] = (clip[2] + clip[3]) / (2 * clip[3]); window[0] = 0.5f * (1.0f + normalized[0]) * r_refdef.vrect.width; window[1] = 0.5f * (1.0f + normalized[1]) * r_refdef.vrect.height; window[2] = normalized[2]; window[0] = (int)(window[0] + 0.5f); window[1] = (int)(window[1] + 0.5f); } qboolean R_GetScreenPosFromWorldPos (const vec3_t origin, int *u, int *v) { vec4_t eye, clip; vec4_t normalized, window; R_TransformModelToClip (origin, r_world_matrix, r_projection_matrix, eye, clip); if (eye[2] > -NEARCLIP) return false; R_TransformClipToWindow (clip, normalized, window); *u = r_refdef.vrect.x + (int)window[0]; *v = r_refdef.vrect.y + r_refdef.vrect.height - (int)window[1]; return true; } /* ============= R_DrawName ============= */ void R_DrawName (vec3_t origin, const char *name, int siegestatus) { int u, v; if (!name) return; if (!R_GetScreenPosFromWorldPos(origin, &u, &v)) return; u -= strlen(name) * 4; if (siegestatus < 0) // not siege { Draw_String (u, v, name); return; } if (siegestatus > 10) //keyholder { siegestatus -= 10; Draw_Character (u, v, 145); //key u += 8; } switch (siegestatus) { case 0: //att Draw_Character (u, v, 144); //sword Draw_String (u+8, v, name); return; case 1: //def Draw_Character (u, v, 143); //shield Draw_RedString (u+8, v, name); return; case 2: //def Draw_Character (u, v, 130); //crown Draw_RedString (u+8, v, name); return; case 3: //neither att nor def default: Draw_String (u+8, v, name); return; } } /* ================ R_RenderView r_refdef must be set before the first call ================ */ void R_RenderView (void) { if (r_norefresh.integer) return; if (!r_worldentity.model || !cl.worldmodel) Sys_Error ("%s: NULL worldmodel", __thisfunc__); if (r_speeds.integer) { glFinish_fp (); if (r_wholeframe.integer) r_time1 = r_lasttime1; else r_time1 = Sys_DoubleTime (); c_brush_polys = 0; c_alias_polys = 0; } mirror = false; // glFinish_fp (); R_Clear (); // render normal view R_RenderScene (); glDepthMask_fp(0); R_DrawParticles (); R_DrawTransEntitiesOnList (r_viewleaf->contents == CONTENTS_EMPTY); // This restores the depth mask R_DrawWaterSurfaces (); R_DrawTransEntitiesOnList (r_viewleaf->contents != CONTENTS_EMPTY); R_DrawViewModel(); // render mirror view // R_Mirror (); R_PolyBlend (); if (r_speeds.integer) R_PrintTimes (); } engine/hexenworld/client/gl_rmisc.c000066400000000000000000000277451444734033100177450ustar00rootroot00000000000000/* r_misc.c -- * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "hashindex.h" byte *playerTranslation; const int color_offsets[MAX_PLAYER_CLASS] = { 2 * 14 * 256, 0, 1 * 14 * 256, 2 * 14 * 256, 2 * 14 * 256 #if defined(H2W) , 2 * 14 * 256 #endif }; cvar_t gl_purge_maptex = {"gl_purge_maptex", "1", CVAR_ARCHIVE}; // whether or not map-specific OGL textures // are purged on map change. default == yes qboolean flush_textures; int gl_texlevel; extern int menu_numcachepics; extern cachepic_t menu_cachepics[MAX_CACHED_PICS]; extern hashindex_t hash_gltextures; extern hashindex_t hash_cachepics; extern void R_InitBubble (void); /* ================== R_InitTextures ================== */ void R_InitTextures (void) { int x, y, m; byte *dest; // create a simple checkerboard texture for the default r_notexture_mip = (texture_t *) Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, "notexture"); r_notexture_mip->width = r_notexture_mip->height = 16; r_notexture_mip->offsets[0] = sizeof(texture_t); r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16; r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8; r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4; for (m = 0; m < 4; m++) { dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[m]; for (y = 0; y < (16 >> m); y++) { for (x = 0; x < (16 >> m); x++) { if ( (y < (8 >> m)) ^ (x < (8 >> m)) ) *dest++ = 0; else *dest++ = 0xff; } } } } #define TEXSIZE 8 static byte dottexture[TEXSIZE][TEXSIZE] = { {0,1,1,0,0,0,0,0}, {1,1,1,1,0,0,0,0}, {1,1,1,1,0,0,0,0}, {0,1,1,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, }; void R_InitParticleTexture (void) { int x, y; byte data[TEXSIZE][TEXSIZE][4]; // // particle texture // for (x = 0; x < TEXSIZE; x++) { for (y = 0; y < TEXSIZE; y++) { data[y][x][0] = 255; data[y][x][1] = 255; data[y][x][2] = 255; data[y][x][3] = dottexture[x][y]*255; } } particletexture = GL_LoadTexture("", (byte *)data, TEXSIZE, TEXSIZE, TEX_ALPHA | TEX_RGBA | TEX_LINEAR); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } void R_InitExtraTextures (void) { int i; for (i = 0; i < MAX_EXTRA_TEXTURES; i++) gl_extra_textures[i] = GL_UNUSED_TEXTURE; // see R_TranslatePlayerSkin() below glGenTextures_fp(MAX_CLIENTS, playertextures); } /* ==================== R_TimeRefresh_f For program optimization ==================== */ static void R_TimeRefresh_f (void) { int i; float start, stop, time; if (cls.state != ca_active) { Con_Printf("Not connected to a server\n"); return; } start = Sys_DoubleTime (); for (i = 0; i < 128; i++) { GL_BeginRendering(&glx, &gly, &glwidth, &glheight); r_refdef.viewangles[1] = i/128.0*360.0; R_RenderView (); GL_EndRendering (); } glFinish_fp (); stop = Sys_DoubleTime (); time = stop-start; Con_Printf ("%f seconds (%f fps)\n", time, 128/time); } /* ==================== R_SetClearColor_f -- johnfitz ==================== */ static void R_SetClearColor_f (cvar_t *var) { int s = var->integer & 0xFF; byte *rgb = (byte *)(d_8to24table + s); glClearColor_fp(rgb[0]/255.0, rgb[1]/255.0, rgb[2]/255.0, 0); } /* =============== R_Init =============== */ void R_Init (void) { Cmd_AddCommand ("timerefresh", R_TimeRefresh_f); Cmd_AddCommand ("pointfile", R_ReadPointFile_f); Cvar_RegisterVariable (&r_norefresh); Cvar_RegisterVariable (&r_lightmap); Cvar_RegisterVariable (&r_fullbright); Cvar_RegisterVariable (&r_waterwarp); Cvar_RegisterVariable (&r_drawentities); Cvar_RegisterVariable (&r_drawviewmodel); Cvar_RegisterVariable (&r_shadows); Cvar_RegisterVariable (&r_mirroralpha); Cvar_RegisterVariable (&r_wateralpha); Cvar_RegisterVariable (&r_skyalpha); Cvar_RegisterVariable (&r_dynamic); Cvar_RegisterVariable (&r_novis); Cvar_RegisterVariable (&r_speeds); Cvar_RegisterVariable (&r_wholeframe); Cvar_RegisterVariable (&r_clearcolor); Cvar_SetCallback (&r_clearcolor, R_SetClearColor_f); Cvar_RegisterVariable (&r_texture_external); Cvar_RegisterVariable (&r_netgraph); Cvar_RegisterVariable (&r_entdistance); Cvar_RegisterVariable (&r_teamcolor); Cvar_RegisterVariable (&gl_clear); Cvar_RegisterVariable (&gl_cull); Cvar_RegisterVariable (&gl_smoothmodels); Cvar_RegisterVariable (&gl_affinemodels); Cvar_RegisterVariable (&gl_polyblend); Cvar_RegisterVariable (&gl_flashblend); Cvar_RegisterVariable (&gl_playermip); Cvar_RegisterVariable (&gl_nocolors); Cvar_RegisterVariable (&gl_waterripple); Cvar_RegisterVariable (&gl_ztrick); Cvar_RegisterVariable (&gl_zfix); Cvar_RegisterVariable (&gl_purge_maptex); Cvar_RegisterVariable (&gl_keeptjunctions); Cvar_RegisterVariable (&gl_reporttjunctions); Cvar_RegisterVariable (&gl_glows); Cvar_RegisterVariable (&gl_missile_glows); Cvar_RegisterVariable (&gl_other_glows); Cvar_RegisterVariable (&gl_coloredlight); Cvar_RegisterVariable (&gl_colored_dynamic_lights); Cvar_RegisterVariable (&gl_extra_dynamic_lights); R_InitBubble(); R_InitParticles (); R_InitParticleTexture (); R_InitExtraTextures (); R_InitNetgraphTexture (); flush_textures = true; R_SetClearColor_f (&r_clearcolor); playerTranslation = (byte *)FS_LoadHunkFile ("gfx/player.lmp", NULL); if (!playerTranslation) Sys_Error ("Couldn't load gfx/player.lmp"); } /* =============== R_TranslatePlayerSkin Translates a skin texture by the per-player color lookup =============== */ extern qmodel_t *player_models[MAX_PLAYER_CLASS]; extern byte player_8bit_texels[MAX_PLAYER_CLASS][620*245]; void R_TranslatePlayerSkin (int playernum) { int top, bottom; byte translate[256]; unsigned int translate32[256]; unsigned int i, j; qmodel_t *model; aliashdr_t *paliashdr; byte *original; unsigned int pixels[512*256], *out; unsigned int scaled_width, scaled_height; int inwidth, inheight; byte *inrow; unsigned int frac, fracstep; byte *sourceA, *sourceB, *colorA, *colorB; player_info_t *player; // int s; // char texname[20]; for (i = 0; i < 256; i++) translate[i] = i; player = &cl.players[playernum]; if (!player->name[0]) return; if (!player->playerclass) return; top = player->topcolor; bottom = player->bottomcolor; if (top > 10) top = 0; if (bottom > 10) bottom = 0; top -= 1; bottom -= 1; colorA = playerTranslation + 256 + color_offsets[(int)player->playerclass-1]; colorB = colorA + 256; sourceA = colorB + 256 + (top * 256); sourceB = colorB + 256 + (bottom * 256); for (i = 0; i < 256; i++, colorA++, colorB++, sourceA++, sourceB++) { if (top >= 0 && (*colorA != 255)) translate[i] = *sourceA; if (bottom >= 0 && (*colorB != 255)) translate[i] = *sourceB; } // // locate the original skin pixels // if (cl.players[playernum].modelindex <= 0) return; model = player_models[cl.players[playernum].playerclass-1]; if (!model) return; // player doesn't have a model yet if (cl.players[playernum].playerclass >= 1 && cl.players[playernum].playerclass <= MAX_PLAYER_CLASS) { original = player_8bit_texels[(int)cl.players[playernum].playerclass-1]; cl.players[playernum].Translated = true; } else original = player_8bit_texels[0]; paliashdr = (aliashdr_t *)Mod_Extradata (model); // s = paliashdr->skinwidth * paliashdr->skinheight; // if (s & 3) // Sys_Error ("%s: s&3", __thisfunc__); for (i = 0; i < 256; i++) translate32[i] = d_8to24table[translate[i]]; scaled_width = gl_max_size < 512 ? gl_max_size : 512; scaled_height = gl_max_size < 256 ? gl_max_size : 256; // allow users to crunch sizes down even more if they want scaled_width >>= gl_playermip.integer; scaled_height >>= gl_playermip.integer; inwidth = paliashdr->skinwidth; inheight = paliashdr->skinheight; out = pixels; fracstep = inwidth*0x10000/scaled_width; for (i = 0; i < scaled_height; i++, out += scaled_width) { inrow = original + inwidth*(i*inheight/scaled_height); frac = fracstep >> 1; for (j = 0; j < scaled_width; j += 4) { out[j] = translate32[inrow[frac>>16]]; frac += fracstep; out[j+1] = translate32[inrow[frac>>16]]; frac += fracstep; out[j+2] = translate32[inrow[frac>>16]]; frac += fracstep; out[j+3] = translate32[inrow[frac>>16]]; frac += fracstep; } } // playertextures doesn't like GL_LoadTexture() and its associated glDeleteTextures() // call, not sure why for now, so I have to do this the old way until I figure it out. // q_snprintf(texname, 19, "player%i", playernum); // playertextures[playernum] = GL_LoadTexture(texname, (byte *)pixels, scaled_width, scaled_height, TEX_RGBA|TEX_LINEAR); GL_Bind(playertextures[playernum]); glTexImage2D_fp(GL_TEXTURE_2D, 0, gl_solid_format, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } /* =============== R_NewMap =============== */ void R_NewMap (void) { int i; for (i = 0; i < 256; i++) d_lightstylevalue[i] = 264; // normal light value memset (&r_worldentity, 0, sizeof(r_worldentity)); r_worldentity.model = cl.worldmodel; // clear out efrags in case the level hasn't been reloaded // FIXME: is this one short? for (i = 0; i < cl.worldmodel->numleafs; i++) cl.worldmodel->leafs[i].efrags = NULL; r_viewleaf = NULL; R_ClearParticles (); GL_BuildLightmaps (); // identify sky texture skytexturenum = -1; mirrortexturenum = -1; for (i = 0; i < cl.worldmodel->numtextures; i++) { if (!cl.worldmodel->textures[i]) continue; if (!strncmp(cl.worldmodel->textures[i]->name,"sky",3) ) skytexturenum = i; if (!strncmp(cl.worldmodel->textures[i]->name,"window02_1",10) ) mirrortexturenum = i; cl.worldmodel->textures[i]->texturechain = NULL; } #ifdef QUAKE2 R_LoadSkys (); #endif } /* D_ClearOpenGLTexture this procedure (called by Host_ClearMemory/SV_SpawnServer in hexen2 on new map, or by CL_ClearState/CL_ParseServerData in HW on new connection) will purge all OpenGL textures beyond static ones (console, menu, etc, whatever was loaded at initialization time). This will save a lot of video memory, because the textures won't keep accumulating from map to map, thus bloating more and more the more time the client is running, which gets pretty nasty on 8-16-32M machines with OpenGL drivers like nVidia, which cache all textures in system memory. (Pa3PyX) */ void D_ClearOpenGLTextures (int last_tex) { int i, key; // Delete OpenGL textures for (i = last_tex; i < numgltextures; i++) { glDeleteTextures_fp(1, &(gltextures[i].texnum)); key = Hash_GenerateKeyString (&hash_gltextures, gltextures[i].identifier, true); Hash_Remove(&hash_gltextures, key, i); } memset(&(gltextures[last_tex]), 0, (numgltextures - last_tex) * sizeof(gltexture_t)); numgltextures = last_tex; if (currenttexture >= (GLuint)last_tex) currenttexture = GL_UNUSED_TEXTURE; // Clear menu pic cache memset(menu_cachepics, 0, menu_numcachepics * sizeof(cachepic_t)); menu_numcachepics = 0; Hash_Clear(&hash_cachepics); Con_DPrintf ("Purged OpenGL textures\n"); } void D_FlushCaches (void) { if (numgltextures - gl_texlevel > 0 && flush_textures && gl_purge_maptex.integer) D_ClearOpenGLTextures (gl_texlevel); } engine/hexenworld/client/gl_rsurf.c000066400000000000000000001122471444734033100177610ustar00rootroot00000000000000/* * r_surf.c -- surface-related refresh code * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" int gl_lightmap_format = GL_RGBA; cvar_t gl_lightmapfmt = {"gl_lightmapfmt", "GL_RGBA", CVAR_ARCHIVE}; int lightmap_bytes = 4; // 1, 2, or 4. default is 4 for GL_RGBA GLuint lightmap_textures[MAX_LIGHTMAPS]; static unsigned int blocklights[18*18]; static unsigned int blocklightscolor[18*18*3]; // colored light support. *3 for RGB to the definitions at the top #define BLOCK_WIDTH 128 #define BLOCK_HEIGHT 128 typedef struct glRect_s { unsigned char l,t,w,h; } glRect_t; static glpoly_t *lightmap_polys[MAX_LIGHTMAPS]; static qboolean lightmap_modified[MAX_LIGHTMAPS]; static glRect_t lightmap_rectchange[MAX_LIGHTMAPS]; static int allocated[MAX_LIGHTMAPS][BLOCK_WIDTH]; // the lightmap texture data needs to be kept in // main memory so texsubimage can update properly static byte lightmaps[4*MAX_LIGHTMAPS*BLOCK_WIDTH*BLOCK_HEIGHT]; /* =============== R_AddDynamicLights =============== */ static void R_AddDynamicLights (msurface_t *surf) { int lnum; int sd, td; float dist, rad, minlight; vec3_t impact, local; int s, t; int i; int smax, tmax; mtexinfo_t *tex; // vars for lit support float cred, cgreen, cblue, brightness; unsigned int *bl; smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; tex = surf->texinfo; for (lnum = 0; lnum < MAX_DLIGHTS; lnum++) { if ( !(surf->dlightbits & (1<plane->normal) - surf->plane->dist; rad -= fabs(dist); minlight = cl_dlights[lnum].minlight; if (rad < minlight) continue; minlight = rad - minlight; for (i = 0; i < 3; i++) { impact[i] = cl_dlights[lnum].origin[i] - surf->plane->normal[i]*dist; } local[0] = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3]; local[1] = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3]; local[0] -= surf->texturemins[0]; local[1] -= surf->texturemins[1]; // lit support (LordHavoc) bl = blocklightscolor; cred = cl_dlights[lnum].color[0] * 256.0f; cgreen = cl_dlights[lnum].color[1] * 256.0f; cblue = cl_dlights[lnum].color[2] * 256.0f; for (t = 0; t < tmax; t++) { td = local[1] - t*16; if (td < 0) td = -td; for (s = 0; s < smax; s++) { sd = local[0] - s*16; if (sd < 0) sd = -sd; if (sd > td) dist = sd + (td>>1); else dist = td + (sd>>1); if (dist < minlight) { brightness = rad - dist; if (cl_dlights[lnum].dark) { // clamp to 0 bl[0] -= (int)(((brightness * cred) < bl[0]) ? (brightness * cred) : bl[0]); bl[1] -= (int)(((brightness * cgreen) < bl[1]) ? (brightness * cgreen) : bl[1]); bl[2] -= (int)(((brightness * cblue) < bl[2]) ? (brightness * cblue) : bl[2]); } else { bl[0] += (int)(brightness * cred); bl[1] += (int)(brightness * cgreen); bl[2] += (int)(brightness * cblue); } blocklights[t*smax + s] += (rad - dist)*256; } bl += 3; } } } } /* =============== GL_SetupLightmapFmt Used to setup the lightmap_format and lightmap_bytes during init from VID_Init() and at every level change from Mod_LoadLighting(). =============== */ void GL_SetupLightmapFmt (void) { // only GL_LUMINANCE and GL_RGBA are supported if (!q_strcasecmp(gl_lightmapfmt.string, "GL_LUMINANCE")) gl_lightmap_format = GL_LUMINANCE; else if (!q_strcasecmp(gl_lightmapfmt.string, "GL_RGBA")) gl_lightmap_format = GL_RGBA; else { gl_lightmap_format = GL_RGBA; Cvar_SetQuick (&gl_lightmapfmt, "GL_RGBA"); } if (!host_initialized) // check for cmdline overrides { if (COM_CheckParm ("-lm_1")) { gl_lightmap_format = GL_LUMINANCE; Cvar_SetQuick (&gl_lightmapfmt, "GL_LUMINANCE"); } else if (COM_CheckParm ("-lm_4")) { gl_lightmap_format = GL_RGBA; Cvar_SetQuick (&gl_lightmapfmt, "GL_RGBA"); } } switch (gl_lightmap_format) { case GL_RGBA: lightmap_bytes = 4; break; case GL_LUMINANCE: lightmap_bytes = 1; break; } } /* =============== R_BuildLightMap Combine and scale multiple lightmaps into the 8.8 format in blocklights =============== */ static void R_BuildLightMap (msurface_t *surf, byte *dest, int stride) { int smax, tmax; int t, r, s, q; int i, j, size; byte *lightmap; unsigned int scale; int maps; unsigned int *bl, *blcr, *blcg, *blcb; surf->cached_dlight = (surf->dlightframe == r_framecount); smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; size = smax*tmax; lightmap = surf->samples; // set to full bright if no light data if (r_fullbright.integer || !cl.worldmodel->lightdata) { for (i = 0; i < size; i++) { if (gl_lightmap_format == GL_RGBA) blocklightscolor[i*3+0] = blocklightscolor[i*3+1] = blocklightscolor[i*3+2] = 65280; else blocklights[i] = 255*256; } goto store; } // clear to no light for (i = 0; i < size; i++) { if (gl_lightmap_format == GL_RGBA) blocklightscolor[i*3+0] = blocklightscolor[i*3+1] = blocklightscolor[i*3+2] = 0; else blocklights[i] = 0; } // add all the lightmaps if (lightmap) { for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; maps++) { scale = d_lightstylevalue[surf->styles[maps]]; surf->cached_light[maps] = scale; // 8.8 fraction if (gl_lightmap_format == GL_RGBA) { for (i = 0, j = 0; i < size; i++) { blocklightscolor[i*3+0] += lightmap[j] * scale; blocklightscolor[i*3+1] += lightmap[++j] * scale; blocklightscolor[i*3+2] += lightmap[++j] * scale; j++; } lightmap += size * 3; } else { for (i = 0; i < size; i++) blocklights[i] += lightmap[i] * scale; lightmap += size; // skip to next lightmap } } } // add all the dynamic lights if (surf->dlightframe == r_framecount) R_AddDynamicLights (surf); // bound, invert, and shift store: switch (gl_lightmap_format) { case GL_RGBA: stride -= (smax<<2); blcr = &blocklightscolor[0]; blcg = &blocklightscolor[1]; blcb = &blocklightscolor[2]; for (i = 0; i < tmax; i++, dest += stride) { for (j = 0; j < smax; j++) { q = *blcr; q >>= 7; r = *blcg; r >>= 7; s = *blcb; s >>= 7; if (q > 255) q = 255; if (r > 255) r = 255; if (s > 255) s = 255; if (gl_coloredlight.integer) { dest[0] = q; //255 - q; dest[1] = r; //255 - r; dest[2] = s; //255 - s; dest[3] = 255; //(q+r+s)/3; } else { t = (int) ((float)q * 0.33f + (float)s * 0.33f + (float)r * 0.33f); if (t > 255) t = 255; dest[0] = t; dest[1] = t; dest[2] = t; dest[3] = 255; //t; } dest += 4; blcr += 3; blcg += 3; blcb += 3; } } break; case GL_LUMINANCE: bl = blocklights; for (i = 0; i < tmax; i++, dest += stride) { for (j = 0; j < smax; j++) { t = *bl++; t >>= 7; if (t > 255) t = 255; dest[j] = 255-t; } } break; default: Sys_Error ("Bad lightmap format"); } } /* =============== R_TextureAnimation Returns the proper texture for a given time and base texture =============== */ static texture_t *R_TextureAnimation (entity_t *e, texture_t *base) { int reletive; int count; if (e->frame) { if (base->alternate_anims) base = base->alternate_anims; } if (!base->anim_total) return base; reletive = (int)(cl.time*10) % base->anim_total; count = 0; while (base->anim_min > reletive || base->anim_max <= reletive) { base = base->anim_next; if (!base) Sys_Error ("%s: broken cycle", __thisfunc__); if (++count > 100) Sys_Error ("%s: infinite cycle", __thisfunc__); } return base; } /* ============================================================= BRUSH MODELS ============================================================= */ /* ================ DrawGLWaterPoly Warp the vertex coordinates ================ */ static void DrawGLWaterPoly (glpoly_t *p) { int i; float *v; vec3_t nv; glBegin_fp (GL_TRIANGLE_FAN); v = p->verts[0]; for (i = 0; i < p->numverts; i++, v+= VERTEXSIZE) { glTexCoord2f_fp (v[3], v[4]); if (r_waterwarp.integer) { nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime); nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime); nv[2] = v[2]; glVertex3fv_fp (nv); } else { glVertex3fv_fp (v); } } glEnd_fp (); } static void DrawGLWaterPolyLightmap (glpoly_t *p) { int i; float *v; vec3_t nv; glBegin_fp (GL_TRIANGLE_FAN); v = p->verts[0]; for (i = 0; i < p->numverts; i++, v+= VERTEXSIZE) { glTexCoord2f_fp (v[5], v[6]); if (r_waterwarp.integer) { nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime); nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime); nv[2] = v[2]; glVertex3fv_fp (nv); } else { glVertex3fv_fp (v); } } glEnd_fp (); } static void DrawGLWaterPolyMTexLM (glpoly_t *p) { int i; float *v; vec3_t nv; glBegin_fp (GL_TRIANGLE_FAN); v = p->verts[0]; for (i = 0; i < p->numverts; i++, v+= VERTEXSIZE) { glMultiTexCoord2fARB_fp (GL_TEXTURE0_ARB, v[3], v[4]); glMultiTexCoord2fARB_fp (GL_TEXTURE1_ARB, v[5], v[6]); if (r_waterwarp.integer) { nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime); nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime); nv[2] = v[2]; glVertex3fv_fp (nv); } else { glVertex3fv_fp (v); } } glEnd_fp (); } /* ================ DrawGLPoly ================ */ static void DrawGLPoly (glpoly_t *p) { int i; float *v; glBegin_fp (GL_POLYGON); v = p->verts[0]; for (i = 0; i < p->numverts; i++, v+= VERTEXSIZE) { glTexCoord2f_fp (v[3], v[4]); glVertex3fv_fp (v); } glEnd_fp (); } static void DrawGLPolyMTex (glpoly_t *p) { int i; float *v; glBegin_fp (GL_POLYGON); v = p->verts[0]; for (i = 0; i < p->numverts; i++, v+= VERTEXSIZE) { glMultiTexCoord2fARB_fp (GL_TEXTURE0_ARB, v[3], v[4]); glMultiTexCoord2fARB_fp (GL_TEXTURE1_ARB, v[5], v[6]); glVertex3fv_fp (v); } glEnd_fp (); } /* ================ R_BlendLightmaps ================ */ static void R_BlendLightmaps (qboolean Translucent) { unsigned int i; int j; glpoly_t *p; float *v; glRect_t *theRect; if (r_fullbright.integer) return; if (!Translucent) glDepthMask_fp (0); // don't bother writing Z if (gl_lightmap_format == GL_RGBA) { glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f_fp (1.0f,1.0f,1.0f, 1.0f); glBlendFunc_fp (GL_ZERO, GL_SRC_COLOR); } else if (gl_lightmap_format == GL_LUMINANCE) { glBlendFunc_fp (GL_ZERO, GL_ONE_MINUS_SRC_COLOR); } if (!r_lightmap.integer) { glEnable_fp (GL_BLEND); } if (! lightmap_textures[0]) { // if lightmaps were hosed in a video mode change, make // sure we allocate new slots for lightmaps, otherwise // we'll probably overwrite some other existing textures. glGenTextures_fp(MAX_LIGHTMAPS, lightmap_textures); } for (i = 0; i < MAX_LIGHTMAPS; i++) { p = lightmap_polys[i]; if (!p) continue; // skip if no lightmap GL_Bind(lightmap_textures[i]); if (lightmap_modified[i]) { // if current lightmap was changed reload it // and mark as not changed. lightmap_modified[i] = false; theRect = &lightmap_rectchange[i]; glTexSubImage2D_fp(GL_TEXTURE_2D, 0, 0, theRect->t, BLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps + (i* BLOCK_HEIGHT + theRect->t)*BLOCK_WIDTH*lightmap_bytes); theRect->l = BLOCK_WIDTH; theRect->t = BLOCK_HEIGHT; theRect->h = 0; theRect->w = 0; } for ( ; p ; p = p->chain) { //if (p->flags & SURF_UNDERWATER) if ( ( (r_viewleaf->contents == CONTENTS_EMPTY && (p->flags & SURF_UNDERWATER)) || (r_viewleaf->contents != CONTENTS_EMPTY && !(p->flags & SURF_UNDERWATER)) ) && !(p->flags & SURF_DONTWARP) ) DrawGLWaterPolyLightmap (p); else { glBegin_fp (GL_POLYGON); v = p->verts[0]; for (j = 0; j < p->numverts; j++, v+= VERTEXSIZE) { glTexCoord2f_fp (v[5], v[6]); glVertex3fv_fp (v); } glEnd_fp (); } } } if (!r_lightmap.integer) { glDisable_fp (GL_BLEND); } if (gl_lightmap_format == GL_RGBA) { glBlendFunc_fp (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } else if (gl_lightmap_format == GL_LUMINANCE) { glBlendFunc_fp (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } if (!Translucent) glDepthMask_fp (1); // back to normal Z buffering } static void R_UpdateLightmaps (qboolean Translucent) { unsigned int i; glpoly_t *p; glRect_t *theRect; if (r_fullbright.integer) return; glActiveTextureARB_fp (GL_TEXTURE1_ARB); if (! lightmap_textures[0]) { // if lightmaps were hosed in a video mode change, make // sure we allocate new slots for lightmaps, otherwise // we'll probably overwrite some other existing textures. glGenTextures_fp(MAX_LIGHTMAPS, lightmap_textures); } for (i = 0; i < MAX_LIGHTMAPS; i++) { p = lightmap_polys[i]; if (!p) continue; // skip if no lightmap GL_Bind(lightmap_textures[i]); if (lightmap_modified[i]) { // if current lightmap was changed reload it // and mark as not changed. lightmap_modified[i] = false; theRect = &lightmap_rectchange[i]; glTexSubImage2D_fp(GL_TEXTURE_2D, 0, 0, theRect->t, BLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps + (i* BLOCK_HEIGHT + theRect->t)*BLOCK_WIDTH*lightmap_bytes); theRect->l = BLOCK_WIDTH; theRect->t = BLOCK_HEIGHT; theRect->h = 0; theRect->w = 0; } } glActiveTextureARB_fp (GL_TEXTURE0_ARB); } /* ================ R_RenderBrushPoly ================ */ void R_RenderBrushPoly (entity_t *e, msurface_t *fa, qboolean override) { texture_t *t; byte *base; int maps; glRect_t *theRect; int smax, tmax; float intensity, alpha_val; c_brush_polys++; if (gl_mtexable) glActiveTextureARB_fp(GL_TEXTURE0_ARB); intensity = 1.0f; alpha_val = 1.0f; if (e->drawflags & DRF_TRANSLUCENT) { glEnable_fp (GL_BLEND); alpha_val = r_wateralpha.value; glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } if ((e->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT) { // ent->abslight 0 - 255 glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); intensity = (float)e->abslight / 255.0f; } if (!override) glColor4f_fp(intensity, intensity, intensity, alpha_val); if (fa->flags & SURF_DRAWSKY) { // warp texture, no lightmaps EmitBothSkyLayers (fa); return; } t = R_TextureAnimation (e, fa->texinfo->texture); GL_Bind (t->gl_texturenum); if (fa->flags & SURF_DRAWTURB) { // warp texture, no lightmaps EmitWaterPolys (fa); return; } if (gl_mtexable) { if ((e->drawflags & DRF_TRANSLUCENT) || (e->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT) { if ( ( (r_viewleaf->contents == CONTENTS_EMPTY && (fa->flags & SURF_UNDERWATER)) || (r_viewleaf->contents != CONTENTS_EMPTY && !(fa->flags & SURF_UNDERWATER)) ) && !(fa->flags & SURF_DONTWARP) ) DrawGLWaterPoly (fa->polys); else DrawGLPoly (fa->polys); } else { glActiveTextureARB_fp(GL_TEXTURE1_ARB); if (gl_lightmap_format == GL_LUMINANCE) glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); else glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glEnable_fp(GL_TEXTURE_2D); GL_Bind (lightmap_textures[fa->lightmaptexturenum]); //glEnable_fp (GL_BLEND); if ( ( (r_viewleaf->contents == CONTENTS_EMPTY && (fa->flags & SURF_UNDERWATER)) || (r_viewleaf->contents != CONTENTS_EMPTY && !(fa->flags & SURF_UNDERWATER)) ) && !(fa->flags & SURF_DONTWARP) ) DrawGLWaterPolyMTexLM (fa->polys); else DrawGLPolyMTex (fa->polys); glDisable_fp(GL_TEXTURE_2D); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //glDisable_fp (GL_BLEND); glActiveTextureARB_fp(GL_TEXTURE0_ARB); } } else { if ( ( (r_viewleaf->contents == CONTENTS_EMPTY && (fa->flags & SURF_UNDERWATER)) || (r_viewleaf->contents != CONTENTS_EMPTY && !(fa->flags & SURF_UNDERWATER)) ) && !(fa->flags & SURF_DONTWARP) ) DrawGLWaterPoly (fa->polys); else DrawGLPoly (fa->polys); } // add the poly to the proper lightmap chain fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; lightmap_polys[fa->lightmaptexturenum] = fa->polys; // check for lightmap modification for (maps = 0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++) { if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps]) goto dynamic; } if (fa->dlightframe == r_framecount // dynamic this frame || fa->cached_dlight) // dynamic previously { dynamic: if (r_dynamic.integer) { lightmap_modified[fa->lightmaptexturenum] = true; theRect = &lightmap_rectchange[fa->lightmaptexturenum]; if (fa->light_t < theRect->t) { if (theRect->h) theRect->h += theRect->t - fa->light_t; theRect->t = fa->light_t; } if (fa->light_s < theRect->l) { if (theRect->w) theRect->w += theRect->l - fa->light_s; theRect->l = fa->light_s; } smax = (fa->extents[0] >> 4) + 1; tmax = (fa->extents[1] >> 4) + 1; if ((theRect->w + theRect->l) < (fa->light_s + smax)) theRect->w = (fa->light_s-theRect->l)+smax; if ((theRect->h + theRect->t) < (fa->light_t + tmax)) theRect->h = (fa->light_t-theRect->t)+tmax; base = lightmaps + fa->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes); } } if ((e->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT || (e->drawflags & DRF_TRANSLUCENT)) { glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } if (e->drawflags & DRF_TRANSLUCENT) { glDisable_fp (GL_BLEND); } } void R_RenderBrushPolyMTex (entity_t *e, msurface_t *fa, qboolean override) { texture_t *t; byte *base; int maps; glRect_t *theRect; int smax, tmax; float intensity, alpha_val; c_brush_polys++; glActiveTextureARB_fp(GL_TEXTURE0_ARB); intensity = 1.0f; alpha_val = 1.0f; if (e->drawflags & DRF_TRANSLUCENT) { glEnable_fp (GL_BLEND); alpha_val = r_wateralpha.value; glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } else { /* KIERO: Seems it's enabled when we enter here. */ glDisable_fp (GL_BLEND); } if ((e->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT) { glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); intensity = (float)e->abslight / 255.0f; } if (fa->flags & SURF_DRAWTURB) { glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable_fp (GL_BLEND); glActiveTextureARB_fp(GL_TEXTURE1_ARB); glDisable_fp(GL_TEXTURE_2D); glActiveTextureARB_fp(GL_TEXTURE0_ARB); intensity = 1.0; } if (!override) glColor4f_fp(intensity, intensity, intensity, alpha_val); if (fa->flags & SURF_DRAWSKY) { // warp texture, no lightmaps EmitBothSkyLayers (fa); return; } glActiveTextureARB_fp(GL_TEXTURE0_ARB); t = R_TextureAnimation (e, fa->texinfo->texture); GL_Bind (t->gl_texturenum); if (fa->flags & SURF_DRAWTURB) { glColor4f_fp(1.0f, 1.0f, 1.0f, 1.0f); EmitWaterPolys (fa); //return; } else { if ((e->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT) { glActiveTextureARB_fp(GL_TEXTURE0_ARB); if ( ( (r_viewleaf->contents == CONTENTS_EMPTY && (fa->flags & SURF_UNDERWATER)) || (r_viewleaf->contents != CONTENTS_EMPTY && !(fa->flags & SURF_UNDERWATER)) ) && !(fa->flags & SURF_DONTWARP) ) DrawGLWaterPoly (fa->polys); else DrawGLPoly (fa->polys); } else { glActiveTextureARB_fp(GL_TEXTURE1_ARB); GL_Bind (lightmap_textures[fa->lightmaptexturenum]); if ( ( (r_viewleaf->contents == CONTENTS_EMPTY && (fa->flags & SURF_UNDERWATER)) || (r_viewleaf->contents != CONTENTS_EMPTY && !(fa->flags & SURF_UNDERWATER)) ) && !(fa->flags & SURF_DONTWARP) ) DrawGLWaterPolyMTexLM (fa->polys); else DrawGLPolyMTex (fa->polys); } glActiveTextureARB_fp(GL_TEXTURE1_ARB); // add the poly to the proper lightmap chain fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; lightmap_polys[fa->lightmaptexturenum] = fa->polys; // check for lightmap modification for (maps = 0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++) { if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps]) goto dynamic1; } if (fa->dlightframe == r_framecount // dynamic this frame || fa->cached_dlight) // dynamic previously { dynamic1: if (r_dynamic.integer) { lightmap_modified[fa->lightmaptexturenum] = true; theRect = &lightmap_rectchange[fa->lightmaptexturenum]; if (fa->light_t < theRect->t) { if (theRect->h) theRect->h += theRect->t - fa->light_t; theRect->t = fa->light_t; } if (fa->light_s < theRect->l) { if (theRect->w) theRect->w += theRect->l - fa->light_s; theRect->l = fa->light_s; } smax = (fa->extents[0] >> 4) + 1; tmax = (fa->extents[1] >> 4) + 1; if ((theRect->w + theRect->l) < (fa->light_s + smax)) theRect->w = (fa->light_s-theRect->l)+smax; if ((theRect->h + theRect->t) < (fa->light_t + tmax)) theRect->h = (fa->light_t-theRect->t)+tmax; base = lightmaps + fa->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes); } } } glActiveTextureARB_fp(GL_TEXTURE0_ARB); if ((e->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT || (e->drawflags & DRF_TRANSLUCENT)) { glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } if (e->drawflags & DRF_TRANSLUCENT) { glDisable_fp (GL_BLEND); } glActiveTextureARB_fp(GL_TEXTURE1_ARB); } /* ================ R_MirrorChain ================ */ static void R_MirrorChain (msurface_t *s) { if (mirror) return; mirror = true; mirror_plane = s->plane; } /* ================ R_DrawWaterSurfaces ================ */ void R_DrawWaterSurfaces (void) { int i; msurface_t *s; texture_t *t; if (r_wateralpha.value > 1) r_wateralpha.value = 1; if (r_wateralpha.value == 1.0) return; glDepthMask_fp(0); // // go back to the world matrix // glLoadMatrixf_fp (r_world_matrix); glEnable_fp (GL_BLEND); glColor4f_fp (1,1,1,r_wateralpha.value); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); for (i = 0; i < cl.worldmodel->numtextures; i++) { t = cl.worldmodel->textures[i]; if (!t) continue; s = t->texturechain; if (!s) continue; if (!(s->flags & SURF_DRAWTURB)) continue; //if ((s->flags & SURF_DRAWTURB) && (s->flags & SURF_TRANSLUCENT)) if (s->flags & SURF_TRANSLUCENT) glColor4f_fp (1,1,1,r_wateralpha.value); else glColor4f_fp (1,1,1,1); // set modulate mode explicitly GL_Bind (t->gl_texturenum); for ( ; s ; s = s->texturechain) EmitWaterPolys (s); t->texturechain = NULL; } glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor4f_fp (1,1,1,1); glDisable_fp (GL_BLEND); glDepthMask_fp (1); } /* ================ DrawTextureChains ================ */ static void DrawTextureChains (entity_t *e) { int i; msurface_t *s; texture_t *t; for (i = 0; i < cl.worldmodel->numtextures; i++) { t = cl.worldmodel->textures[i]; if (!t) continue; s = t->texturechain; if (!s) continue; if (i == skytexturenum) R_DrawSkyChain (s); else if (i == mirrortexturenum && r_mirroralpha.value != 1.0) { R_MirrorChain (s); continue; } else { if ((s->flags & SURF_DRAWTURB) && r_wateralpha.value != 1.0) continue; // draw translucent water later if (((e->drawflags & DRF_TRANSLUCENT) || (e->drawflags & MLS_ABSLIGHT) == MLS_ABSLIGHT)) { for ( ; s ; s = s->texturechain) R_RenderBrushPoly (e, s, false); } else if (gl_mtexable) { glActiveTextureARB_fp(GL_TEXTURE0_ARB); glEnable_fp(GL_TEXTURE_2D); glActiveTextureARB_fp(GL_TEXTURE1_ARB); glEnable_fp(GL_TEXTURE_2D); if (gl_lightmap_format == GL_LUMINANCE) glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); else glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glEnable_fp (GL_BLEND); for ( ; s ; s = s->texturechain) R_RenderBrushPolyMTex (e, s, false); glDisable_fp(GL_TEXTURE_2D); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable_fp (GL_BLEND); glActiveTextureARB_fp(GL_TEXTURE0_ARB); } else { for ( ; s ; s = s->texturechain) R_RenderBrushPoly (e, s, false); } } t->texturechain = NULL; } } /* ================= R_DrawBrushModel ================= */ void R_DrawBrushModel (entity_t *e, qboolean Translucent) { int i, k; vec3_t mins, maxs; msurface_t *psurf; float dot; mplane_t *pplane; qmodel_t *clmodel; qboolean rotated; currenttexture = GL_UNUSED_TEXTURE; clmodel = e->model; if (e->angles[0] || e->angles[1] || e->angles[2]) { rotated = true; for (i = 0; i < 3; i++) { mins[i] = e->origin[i] - clmodel->radius; maxs[i] = e->origin[i] + clmodel->radius; } } else { rotated = false; VectorAdd (e->origin, clmodel->mins, mins); VectorAdd (e->origin, clmodel->maxs, maxs); } if (R_CullBox (mins, maxs)) return; #if 0 /* causes side effects in 16 bpp. alternative down below */ /* Get rid of Z-fighting for textures by offsetting the * drawing of entity models compared to normal polygons. * (Only works if gl_ztrick is turned off) */ if (gl_zfix.integer && !gl_ztrick.integer) { glEnable_fp(GL_POLYGON_OFFSET_FILL); glEnable_fp(GL_POLYGON_OFFSET_LINE); } #endif /* #if 0 */ glColor3f_fp (1,1,1); memset (lightmap_polys, 0, sizeof(lightmap_polys)); VectorSubtract (r_refdef.vieworg, e->origin, modelorg); if (rotated) { vec3_t temp; vec3_t forward, right, up; VectorCopy (modelorg, temp); AngleVectors (e->angles, forward, right, up); modelorg[0] = DotProduct (temp, forward); modelorg[1] = -DotProduct (temp, right); modelorg[2] = DotProduct (temp, up); } psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; // calculate dynamic lighting for bmodel if it's not an // instanced model if (clmodel->firstmodelsurface != 0 && !gl_flashblend.integer) { for (k = 0; k < MAX_DLIGHTS; k++) { if ((cl_dlights[k].die < cl.time) || (!cl_dlights[k].radius)) continue; R_MarkLights (&cl_dlights[k], 1<nodes + clmodel->hulls[0].firstclipnode); } } glPushMatrix_fp (); e->angles[0] = -e->angles[0]; // stupid quake bug /* hack the origin to prevent bmodel z-fighting * http://forums.inside3d.com/viewtopic.php?t=1350 */ if (gl_zfix.integer) { e->origin[0] -= DIST_EPSILON; e->origin[1] -= DIST_EPSILON; e->origin[2] -= DIST_EPSILON; } R_RotateForEntity (e); /* un-hack the origin */ if (gl_zfix.integer) { e->origin[0] += DIST_EPSILON; e->origin[1] += DIST_EPSILON; e->origin[2] += DIST_EPSILON; } e->angles[0] = -e->angles[0]; // stupid quake bug // // draw texture // for (i = 0; i < clmodel->nummodelsurfaces; i++, psurf++) { // find which side of the node we are on pplane = psurf->plane; dot = DotProduct (modelorg, pplane->normal) - pplane->dist; // draw the polygon if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { R_RenderBrushPoly (e, psurf, false); } } if (!Translucent && (e->drawflags & MLS_ABSLIGHT) != MLS_ABSLIGHT && !gl_mtexable) { R_BlendLightmaps (Translucent); } glPopMatrix_fp (); #if 0 /* see above... */ if (gl_zfix.integer && !gl_ztrick.integer) { glDisable_fp(GL_POLYGON_OFFSET_FILL); glDisable_fp(GL_POLYGON_OFFSET_LINE); } #endif /* #if 0 */ } /* ============================================================= WORLD MODEL ============================================================= */ /* ================ R_RecursiveWorldNode ================ */ static void R_RecursiveWorldNode (mnode_t *node) { int c, side; mplane_t *plane; msurface_t *surf, **mark; mleaf_t *pleaf; double dot; if (node->contents == CONTENTS_SOLID) return; // solid if (node->visframe != r_visframecount) return; if (R_CullBox (node->minmaxs, node->minmaxs+3)) return; // if a leaf node, draw stuff if (node->contents < 0) { pleaf = (mleaf_t *)node; mark = pleaf->firstmarksurface; c = pleaf->nummarksurfaces; if (c) { do { (*mark)->visframe = r_framecount; mark++; } while (--c); } // deal with model fragments in this leaf if (pleaf->efrags) R_StoreEfrags (&pleaf->efrags); return; } // node is just a decision point, so go down the apropriate sides // find which side of the node we are on plane = node->plane; switch (plane->type) { case PLANE_X: dot = modelorg[0] - plane->dist; break; case PLANE_Y: dot = modelorg[1] - plane->dist; break; case PLANE_Z: dot = modelorg[2] - plane->dist; break; default: dot = DotProduct (modelorg, plane->normal) - plane->dist; break; } if (dot >= 0) side = 0; else side = 1; // recurse down the children, front side first R_RecursiveWorldNode (node->children[side]); // draw stuff c = node->numsurfaces; if (c) { surf = cl.worldmodel->surfaces + node->firstsurface; if (dot < 0 -BACKFACE_EPSILON) side = SURF_PLANEBACK; else if (dot > BACKFACE_EPSILON) side = 0; for ( ; c ; c--, surf++) { if (surf->visframe != r_framecount) continue; // don't backface underwater surfaces, because they warp // if (!(surf->flags & SURF_UNDERWATER) && ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK))) if (!( ((r_viewleaf->contents == CONTENTS_EMPTY && (surf->flags & SURF_UNDERWATER)) || (r_viewleaf->contents != CONTENTS_EMPTY && !(surf->flags & SURF_UNDERWATER))) && !(surf->flags & SURF_DONTWARP)) && ((dot < 0) ^ !!(surf->flags & SURF_PLANEBACK))) continue; // wrong side // sorting by texture, just store it out if (!mirror || surf->texinfo->texture != cl.worldmodel->textures[mirrortexturenum]) { surf->texturechain = surf->texinfo->texture->texturechain; surf->texinfo->texture->texturechain = surf; } } } // recurse down the back side R_RecursiveWorldNode (node->children[!side]); } /* ============= R_DrawWorld ============= */ void R_DrawWorld (void) { VectorCopy (r_refdef.vieworg, modelorg); currenttexture = GL_UNUSED_TEXTURE; glColor4f_fp (1.0f,1.0f,1.0f,1.0f); memset (lightmap_polys, 0, sizeof(lightmap_polys)); #ifdef QUAKE2 R_ClearSkyBox (); #endif R_RecursiveWorldNode (cl.worldmodel->nodes); DrawTextureChains (&r_worldentity); // disable multitexturing - just in case if (gl_mtexable) { glActiveTextureARB_fp (GL_TEXTURE1_ARB); glDisable_fp(GL_TEXTURE_2D); glActiveTextureARB_fp (GL_TEXTURE0_ARB); glEnable_fp(GL_TEXTURE_2D); } if (!gl_mtexable) R_BlendLightmaps (false); else R_UpdateLightmaps (false); #ifdef QUAKE2 R_DrawSkyBox (); #endif } /* ============================================================================= LIGHTMAP ALLOCATION ============================================================================= */ // returns a texture number and the position inside it static unsigned int AllocBlock (int w, int h, int *x, int *y) { int i, j; int best, best2; unsigned int texnum; for (texnum = 0; texnum < MAX_LIGHTMAPS; texnum++) { best = BLOCK_HEIGHT; for (i = 0; i < BLOCK_WIDTH - w; i++) { best2 = 0; for (j = 0; j < w; j++) { if (allocated[texnum][i+j] >= best) break; if (allocated[texnum][i+j] > best2) best2 = allocated[texnum][i+j]; } if (j == w) { // this is a valid spot *x = i; *y = best = best2; } } if (best + h > BLOCK_HEIGHT) continue; for (i = 0; i < w; i++) allocated[texnum][*x + i] = best + h; return texnum; } Sys_Error ("%s: full", __thisfunc__); return -1; // shut up the compiler } #define COLINEAR_EPSILON 0.001 static mvertex_t *r_pcurrentvertbase; static qmodel_t *currentmodel; /* ================ BuildSurfaceDisplayList ================ */ static void BuildSurfaceDisplayList (msurface_t *fa) { int i, lindex, lnumverts; medge_t *pedges, *r_pedge; float *vec; float s, t; glpoly_t *poly; // reconstruct the polygon pedges = currentmodel->edges; lnumverts = fa->numedges; // // draw texture // poly = (glpoly_t *) Hunk_AllocName (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float), "poly"); poly->next = fa->polys; poly->flags = fa->flags; fa->polys = poly; poly->numverts = lnumverts; for (i = 0; i < lnumverts; i++) { lindex = currentmodel->surfedges[fa->firstedge + i]; if (lindex > 0) { r_pedge = &pedges[lindex]; vec = r_pcurrentvertbase[r_pedge->v[0]].position; } else { r_pedge = &pedges[-lindex]; vec = r_pcurrentvertbase[r_pedge->v[1]].position; } s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; s /= fa->texinfo->texture->width; t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t /= fa->texinfo->texture->height; VectorCopy (vec, poly->verts[i]); poly->verts[i][3] = s; poly->verts[i][4] = t; // // lightmap texture coordinates // s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; s -= fa->texturemins[0]; s += fa->light_s*16; s += 8; s /= BLOCK_WIDTH*16; //fa->texinfo->texture->width; t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t -= fa->texturemins[1]; t += fa->light_t*16; t += 8; t /= BLOCK_HEIGHT*16; //fa->texinfo->texture->height; poly->verts[i][5] = s; poly->verts[i][6] = t; } // // remove co-linear points - Ed // if (!gl_keeptjunctions.integer && !(fa->flags & SURF_UNDERWATER)) { for (i = 0; i < lnumverts; ++i) { vec3_t v1, v2; float *prev, *curr, *next; prev = poly->verts[(i + lnumverts - 1) % lnumverts]; curr = poly->verts[i]; next = poly->verts[(i + 1) % lnumverts]; VectorSubtract(curr, prev, v1); VectorNormalize(v1); VectorSubtract(next, prev, v2); VectorNormalize(v2); // skip co-linear points if ((fabs(v1[0] - v2[0]) <= COLINEAR_EPSILON) && (fabs(v1[1] - v2[1]) <= COLINEAR_EPSILON) && (fabs(v1[2] - v2[2]) <= COLINEAR_EPSILON)) { int j, k; for (j = i + 1; j < lnumverts; ++j) { for (k = 0; k < VERTEXSIZE; ++k) poly->verts[j - 1][k] = poly->verts[j][k]; } --lnumverts; // retry next vertex next time, which is now current vertex --i; } } } poly->numverts = lnumverts; } /* ======================== GL_CreateSurfaceLightmap ======================== */ static void GL_CreateSurfaceLightmap (msurface_t *surf) { int smax, tmax; byte *base; if (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB)) return; smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; surf->lightmaptexturenum = AllocBlock (smax, tmax, &surf->light_s, &surf->light_t); base = lightmaps + surf->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; base += (surf->light_t * BLOCK_WIDTH + surf->light_s) * lightmap_bytes; R_BuildLightMap (surf, base, BLOCK_WIDTH*lightmap_bytes); } /* ================== GL_BuildLightmaps Builds the lightmap texture with all the surfaces from all brush models ================== */ void GL_BuildLightmaps (void) { int i, j; qmodel_t *m; memset (allocated, 0, sizeof(allocated)); r_framecount = 1; // no dlightcache if (! lightmap_textures[0]) { glGenTextures_fp(MAX_LIGHTMAPS, lightmap_textures); } for (j = 1; j < MAX_MODELS; j++) { m = cl.model_precache[j]; if (!m) break; if (m->name[0] == '*') continue; r_pcurrentvertbase = m->vertexes; currentmodel = m; for (i = 0; i < m->numsurfaces; i++) { GL_CreateSurfaceLightmap (m->surfaces + i); if (m->surfaces[i].flags & SURF_DRAWTURB) continue; #ifndef QUAKE2 if (m->surfaces[i].flags & SURF_DRAWSKY) continue; #endif if (!draw_reinit) BuildSurfaceDisplayList (m->surfaces + i); } } if (gl_mtexable) glActiveTextureARB_fp (GL_TEXTURE1_ARB); // // upload all lightmaps that were filled // for (i = 0; i < MAX_LIGHTMAPS; i++) { if (!allocated[i][0]) break; // no more used lightmap_modified[i] = false; lightmap_rectchange[i].l = BLOCK_WIDTH; lightmap_rectchange[i].t = BLOCK_HEIGHT; lightmap_rectchange[i].w = 0; lightmap_rectchange[i].h = 0; GL_Bind(lightmap_textures[i]); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf_fp(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D_fp (GL_TEXTURE_2D, 0, lightmap_bytes, BLOCK_WIDTH, BLOCK_HEIGHT, 0, gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps + i*BLOCK_WIDTH*BLOCK_HEIGHT*lightmap_bytes); } if (gl_mtexable) glActiveTextureARB_fp (GL_TEXTURE0_ARB); } engine/hexenworld/client/host.h000066400000000000000000000052161444734033100171150ustar00rootroot00000000000000/* * host.h -- public host structures and functions * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #if defined(SERVERONLY) #error "this header is for hw client only" #endif /* SERVERONLY */ #ifndef HX2_HOST_H #define HX2_HOST_H // quakeparms structure specifies the base of the directory tree, the // command line parms passed to the program, and the amount of memory // available for the program to use typedef struct quakeparms_s { const char *basedir; const char *userdir; // user's directory on UNIX platforms. // if user directories are enabled, basedir // and userdir will point to different // memory locations, otherwise to the same. int argc; char **argv; void *membase; int memsize; int errstate; } quakeparms_t; extern quakeparms_t *host_parms; #define isDedicated 0 /* compatibility */ extern cvar_t sys_ticrate; extern cvar_t sys_throttle; extern cvar_t sys_nostdout; extern cvar_t developer; extern cvar_t password; extern cvar_t talksounds; extern qboolean host_initialized; // true if into command execution extern double host_frametime; extern byte *host_basepal; extern byte *host_colormap; extern int host_framecount; // incremented every frame, never reset extern double realtime; // not bounded in any way, changed at // start of every frame, never reset void Host_Init (void); void Host_InitCommands (void); void Host_Shutdown(void); /* Host_Error and Host_EndGame doesn't return either due to Sys_Error() or longjmp() */ FUNC_NORETURN void Host_Error (const char *error, ...) FUNC_PRINTF(1,2); FUNC_NORETURN void Host_EndGame (const char *message, ...) FUNC_PRINTF(1,2); #ifdef __WATCOMC__ #pragma aux Host_Error aborts; #pragma aux Host_EndGame aborts; #endif void Host_Frame (float time); void Host_Quit_f (void); void Host_ClientCommands (const char *fmt, ...) FUNC_PRINTF(1,2); void Host_ShutdownServer (qboolean crash); void Host_WriteConfiguration (const char *fname); #endif /* HX2_HOST_H */ engine/hexenworld/client/keys.c000066400000000000000000000543031444734033100171070ustar00rootroot00000000000000/* * keys.c -- key up events are sent even if in console mode * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2006-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" char key_lines[32][MAXCMDLINE]; int key_linepos; static qboolean shift_down = false; int key_lastpress; int key_insert = 1; // insert/overwrite mode toggle int edit_line = 0; static int history_line = 0; static keydest_t key_dest; static qboolean key_gamekey, prev_gamekey; int key_count; // incremented every key event char *keybindings[256]; static qboolean consolekeys[256]; // if true, can't be rebound while in console static qboolean menubound[256]; // if true, can't be rebound while in menu static int keyshift[256]; // key to map to if shift held down in console static int key_repeats[256]; // if > 1, it is autorepeating static qboolean keyreserved[256]; // hardcoded, can't be rebound by the user static qboolean keydown[256]; typedef struct { const char *name; int keynum; } keyname_t; static keyname_t keynames[] = { {"TAB", K_TAB}, {"ENTER", K_ENTER}, {"ESCAPE", K_ESCAPE}, {"SPACE", K_SPACE}, {"BACKSPACE", K_BACKSPACE}, {"UPARROW", K_UPARROW}, {"DOWNARROW", K_DOWNARROW}, {"LEFTARROW", K_LEFTARROW}, {"RIGHTARROW", K_RIGHTARROW}, {"ALT", K_ALT}, {"CTRL", K_CTRL}, {"SHIFT", K_SHIFT}, {"F1", K_F1}, {"F2", K_F2}, {"F3", K_F3}, {"F4", K_F4}, {"F5", K_F5}, {"F6", K_F6}, {"F7", K_F7}, {"F8", K_F8}, {"F9", K_F9}, {"F10", K_F10}, {"F11", K_F11}, {"F12", K_F12}, {"INS", K_INS}, {"DEL", K_DEL}, {"PGDN", K_PGDN}, {"PGUP", K_PGUP}, {"HOME", K_HOME}, {"END", K_END}, // {"KP_NUMLOCK", K_KP_NUMLOCK}, {"KP_SLASH", K_KP_SLASH}, {"KP_STAR", K_KP_STAR}, {"KP_MINUS", K_KP_MINUS}, {"KP_HOME", K_KP_HOME}, {"KP_UPARROW", K_KP_UPARROW}, {"KP_PGUP", K_KP_PGUP}, {"KP_PLUS", K_KP_PLUS}, {"KP_LEFTARROW", K_KP_LEFTARROW}, {"KP_5", K_KP_5}, {"KP_RIGHTARROW", K_KP_RIGHTARROW}, {"KP_END", K_KP_END}, {"KP_DOWNARROW", K_KP_DOWNARROW}, {"KP_PGDN", K_KP_PGDN}, {"KP_ENTER", K_KP_ENTER}, {"KP_INS", K_KP_INS}, {"KP_DEL", K_KP_DEL}, {"COMMAND", K_COMMAND}, {"MOUSE1", K_MOUSE1}, {"MOUSE2", K_MOUSE2}, {"MOUSE3", K_MOUSE3}, {"MWHEELUP", K_MWHEELUP}, {"MWHEELDOWN", K_MWHEELDOWN}, {"MOUSE4", K_MOUSE4}, {"MOUSE5", K_MOUSE5}, {"JOY1", K_JOY1}, {"JOY2", K_JOY2}, {"JOY3", K_JOY3}, {"JOY4", K_JOY4}, {"AUX1", K_AUX1}, {"AUX2", K_AUX2}, {"AUX3", K_AUX3}, {"AUX4", K_AUX4}, {"AUX5", K_AUX5}, {"AUX6", K_AUX6}, {"AUX7", K_AUX7}, {"AUX8", K_AUX8}, {"AUX9", K_AUX9}, {"AUX10", K_AUX10}, {"AUX11", K_AUX11}, {"AUX12", K_AUX12}, {"AUX13", K_AUX13}, {"AUX14", K_AUX14}, {"AUX15", K_AUX15}, {"AUX16", K_AUX16}, {"AUX17", K_AUX17}, {"AUX18", K_AUX18}, {"AUX19", K_AUX19}, {"AUX20", K_AUX20}, {"AUX21", K_AUX21}, {"AUX22", K_AUX22}, {"AUX23", K_AUX23}, {"AUX24", K_AUX24}, {"AUX25", K_AUX25}, {"AUX26", K_AUX26}, {"AUX27", K_AUX27}, {"AUX28", K_AUX28}, {"AUX29", K_AUX29}, {"AUX30", K_AUX30}, {"AUX31", K_AUX31}, {"AUX32", K_AUX32}, {"PAUSE", K_PAUSE}, {"SEMICOLON", ';'}, // because a raw semicolon seperates commands {NULL, 0} }; /* ============================================================================== LINE TYPING INTO THE CONSOLE ============================================================================== */ static qboolean CheckForCommand (void) { char command[128], *s; int i; s = key_lines[edit_line] + 1; command[0] = '\0'; for (i = 0; i < 127; i++) { if (s[i] <= ' ') break; command[i] = s[i]; } command[i] = 0; return Cmd_CheckCommand (command); } static void CompleteCommand (void) { char *matches[MAX_MATCHES]; char backup[MAXCMDLINE]; char c, *prefix, *workline; qboolean editing; int count, i; size_t len1, len2; if (key_linepos < 2) return; workline = key_lines[edit_line]; c = workline[key_linepos]; editing = (c != 0); if (editing) { // make a copy of the text starting from the // cursor position (see below) q_strlcpy(backup, workline + key_linepos, sizeof(backup)); } // complete the text only up to the cursor position: // bash style. cut off the rest for now. workline[key_linepos] = 0; prefix = workline + 1; // skip the leading whitespace and command markers while (*prefix) { if (*prefix != '\\' && *prefix != '/' && *prefix > ' ') break; ++prefix; } // if the remainder line has no length or has // spaces in it, don't bother if (!*prefix || strstr(prefix," ")) { workline[key_linepos] = c; return; } // store the length of the relevant partial len1 = len2 = strlen(prefix); // start checking for matches, finally... count = 0; count += ListCommands(prefix, (const char**)matches, count); count += ListCvars (prefix, (const char**)matches, count); count += ListAlias (prefix, (const char**)matches, count); if (count) { // do not do a full auto-complete // unless there is only one match if (count == 1) { workline[1] = '/'; q_strlcpy (workline + 2, matches[0], MAXCMDLINE-2); key_linepos = 2 + strlen(matches[0]); // q_strlcpy (workline + 1, matches[0], MAXCMDLINE-1); // key_linepos = 1 + strlen(matches[0]); workline[key_linepos] = ' '; key_linepos++; } else { // more than one match, sort and list all of them qsort (matches, count, sizeof(char *), COM_StrCompare); Con_Printf("\n"); #if 0 // plain listing for (i = 0; i < count && i < MAX_MATCHES; i++) Con_Printf ("%s\n", matches[i]); Con_Printf("\n%d matches found\n\n", count); #else // S.A.: columnize the listing. Con_Printf("%d possible completions:\n\n", count); Con_ShowList (count, (const char**) matches); Con_Printf("\n"); #endif // cycle throgh all matches and see // if there is a partial completion _search: for (i = 1; i < count && i < MAX_MATCHES; i++) { if (matches[0][len2] != matches[i][len2]) goto _check; } ++len2; goto _search; _check: if (len2 > len1) // found a partial match { workline[1] = '/'; strncpy (workline + 2, matches[0], len2); key_linepos = len2 + 2; // strncpy (workline + 1, matches[0], len2); // key_linepos = len2 + 1; } } workline[key_linepos] = 0; } // put back the remainder of the original text // which was lost after the trimming if (editing) q_strlcpy (workline + key_linepos, backup, MAXCMDLINE-key_linepos); } static void PasteToConsole (void) { char *cbd, *p, *workline; int mvlen, inslen; if (key_linepos == MAXCMDLINE - 1) return; if ((cbd = Sys_GetClipboardData()) == NULL) return; p = cbd; while (*p) { if (*p == '\n' || *p == '\r' || *p == '\b') { *p = 0; break; } p++; } inslen = (int) (p - cbd); if (inslen + key_linepos > MAXCMDLINE - 1) inslen = MAXCMDLINE - 1 - key_linepos; if (inslen <= 0) goto done; workline = key_lines[edit_line]; workline += key_linepos; mvlen = (int) strlen(workline); if (mvlen + inslen + key_linepos > MAXCMDLINE - 1) { mvlen = MAXCMDLINE - 1 - key_linepos - inslen; if (mvlen < 0) mvlen = 0; } // insert the string if (mvlen != 0) memmove (workline + inslen, workline, mvlen); memcpy (workline, cbd, inslen); key_linepos += inslen; workline[mvlen + inslen] = '\0'; done: Z_Free(cbd); } /* ==================== Key_Console Interactive line editing and console scrollback ==================== */ static void Key_Console (int key) { int history_line_last; size_t len; char *workline = key_lines[edit_line]; switch (key) { case K_ENTER: // backslash text are commands, else chat if (workline[1] == '\\' || workline[1] == '/') Cbuf_AddText (workline + 2); // skip the > else if (CheckForCommand()) Cbuf_AddText (workline + 1); // valid command else { // convert to a chat message if (cls.state >= ca_connected) Cbuf_AddText ("say "); Cbuf_AddText (workline + 1); // skip the > } Cbuf_AddText ("\n"); Con_Printf ("%s\n", workline); edit_line = (edit_line + 1) & 31; history_line = edit_line; key_lines[edit_line][0] = ']'; key_lines[edit_line][1] = 0; key_linepos = 1; if (cls.state == ca_disconnected) SCR_UpdateScreen (); // force an update, because the command // may take some time return; case K_TAB: CompleteCommand (); return; case K_LEFTARROW: if (key_linepos < 2) return; if (keydown[K_CTRL]) { /* ctrl - left, word processor style: first, * move to the ending of previous word, then * move to its beginning */ char *p = workline + key_linepos - 1; while (p != workline && *p == ' ') --p; while (p != workline) { if (*--p == ' ') break; } key_linepos = (int)(p - workline) + 1; } else /* simple cursor-to-left, only. */ { --key_linepos; } return; case K_RIGHTARROW: if (!workline[key_linepos]) return; if (keydown[K_CTRL]) { /* ctrl - right, word processor style: if * we are on a text move to its end, then * move to the beginning of the next word */ char *p = workline + key_linepos; while (*p && *p != ' ') ++p; while (*p && *p == ' ') ++p; key_linepos = (int)(p - workline); } else /* simple cursor-to-right only. */ { ++key_linepos; } return; case K_BACKSPACE: if (key_linepos > 1) { workline += key_linepos - 1; if (workline[1]) { len = strlen(workline); memmove (workline, workline + 1, len); } else *workline = 0; key_linepos--; } return; case K_DEL: workline += key_linepos; if (*workline) { if (workline[1]) { len = strlen(workline); memmove (workline, workline + 1, len); } else *workline = 0; } return; case K_UPARROW: history_line_last = history_line; do { history_line = (history_line - 1) & 31; } while (history_line != edit_line && !key_lines[history_line][1]); if (history_line == edit_line) history_line = history_line_last; len = strlen(key_lines[history_line]); memmove(workline, key_lines[history_line], len+1); key_linepos = (int)len; return; case K_DOWNARROW: if (history_line == edit_line) return; do { history_line = (history_line + 1) & 31; } while (history_line != edit_line && !key_lines[history_line][1]); if (history_line == edit_line) { workline[0] = ']'; workline[1] = 0; key_linepos = 1; } else { len = strlen(key_lines[history_line]); memmove(workline, key_lines[history_line], len+1); key_linepos = (int)len; } return; case K_PGUP: case K_MWHEELUP: con->display -= 2; return; case K_PGDN: case K_MWHEELDOWN: con->display += 2; if (con->display > con->current) con->display = con->current; return; case K_HOME: if (keydown[K_CTRL]) con->display = con->current - con_totallines + 10; else key_linepos = 1; return; case K_END: if (keydown[K_CTRL]) con->display = con->current; else key_linepos = strlen(workline); return; case K_INS: if (keydown[K_SHIFT]) /* Shift+Ins paste */ PasteToConsole(); else key_insert ^= 1; return; case 'v': case 'V': #if defined(PLATFORM_OSX) || defined(PLATFORM_MAC) if (keydown[K_COMMAND]) { /* Cmd+V paste (Mac-only) */ PasteToConsole(); return; } #endif if (keydown[K_CTRL]) { /* Ctrl+v paste */ PasteToConsole(); return; } break; case 'c': case 'C': if (keydown[K_CTRL]) { /* Ctrl+C: abort the line -- S.A */ Con_Printf ("%s\n", workline); workline[0] = ']'; workline[1] = 0; key_linepos = 1; history_line= edit_line; return; } break; } if (key < 32 || key > 127) return; // non printable if (key_linepos < MAXCMDLINE - 1) { qboolean endpos = !workline[key_linepos]; // if inserting, move the text to the right if (key_insert && !endpos) { workline[MAXCMDLINE - 2] = 0; workline += key_linepos; len = strlen(workline) + 1; memmove (workline + 1, workline, len); *workline = key; } else { workline += key_linepos; *workline = key; // null terminate if at the end if (endpos) workline[1] = 0; } key_linepos++; } } //============================================================================ qboolean chat_team = false; static char chat_buffer[MAXCMDLINE]; static int chat_bufferlen = 0; const char *Key_GetChatBuffer (void) { return chat_buffer; } int Key_GetChatMsgLen (void) { return chat_bufferlen; } void Key_EndChat (void) { Key_SetDest (key_game); chat_bufferlen = 0; chat_buffer[0] = 0; } static void Key_Message (int key) { if (key == K_ENTER) { if (chat_team) Cbuf_AddText ("say_team \""); else Cbuf_AddText ("say \""); Cbuf_AddText(chat_buffer); Cbuf_AddText("\"\n"); Key_EndChat (); return; } if (key == K_ESCAPE) { Key_EndChat (); return; } if (key == K_BACKSPACE) { if (chat_bufferlen) chat_buffer[--chat_bufferlen] = 0; return; } if (key < 32 || key > 127) return; // non printable if (chat_bufferlen == sizeof(chat_buffer) - 1) return; // all full chat_buffer[chat_bufferlen++] = key; chat_buffer[chat_bufferlen] = 0; } //============================================================================ /* =================== Key_StringToKeynum Returns a key number to be used to index keybindings[] by looking at the given string. Single ascii characters return themselves, while the K_* names are matched up. =================== */ static int Key_StringToKeynum (const char *str) { keyname_t *kn; if (!str || !str[0]) return -1; if (!str[1]) return str[0]; for (kn = keynames; kn->name; kn++) { if (!q_strcasecmp(str,kn->name)) return kn->keynum; } return -1; } /* =================== Key_KeynumToString Returns a string (either a single ascii char, or a K_* name) for the given keynum. FIXME: handle quote special (general escape sequence?) =================== */ const char *Key_KeynumToString (int keynum) { static char tinystr[2]; keyname_t *kn; if (keynum == -1) return ""; if (keynum > 32 && keynum < 127) { // printable ascii tinystr[0] = keynum; tinystr[1] = 0; return tinystr; } for (kn = keynames; kn->name; kn++) { if (keynum == kn->keynum) return kn->name; } return ""; } /* =================== Key_SetBinding =================== */ void Key_SetBinding (int keynum, const char *binding) { if (keynum == -1) return; if (keyreserved[keynum]) return; // free old bindings if (keybindings[keynum]) { Z_Free (keybindings[keynum]); keybindings[keynum] = NULL; } // allocate memory for new binding if (binding) keybindings[keynum] = Z_Strdup(binding); } /* =================== Key_Unbind_f =================== */ static void Key_Unbind_f (void) { int b; if (Cmd_Argc() != 2) { Con_Printf ("unbind : remove commands from a key\n"); return; } b = Key_StringToKeynum (Cmd_Argv(1)); if (b == -1) { Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } Key_SetBinding (b, NULL); } static void Key_Unbindall_f (void) { int i; for (i = 0; i < 256; i++) Key_SetBinding(i, NULL); } /* =================== Key_Bind_f =================== */ static void Key_Bind_f (void) { int i, c, b; char cmd[1024]; c = Cmd_Argc(); if (c != 2 && c != 3) { Con_Printf ("bind [command] : attach a command to a key\n"); return; } b = Key_StringToKeynum (Cmd_Argv(1)); if (b == -1) { Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } if (c == 2) { if (keybindings[b]) Con_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b] ); else Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) ); return; } // copy the rest of the command line cmd[0] = 0; for (i = 2; i < c; i++) { q_strlcat (cmd, Cmd_Argv(i), sizeof(cmd)); if (i != (c-1)) q_strlcat (cmd, " ", sizeof(cmd)); } Key_SetBinding (b, cmd); } /* ============ Key_WriteBindings Writes lines containing "bind key value" ============ */ void Key_WriteBindings (FILE *f) { int i; // unbindall before loading stored bindings: if (cfg_unbindall.integer) fprintf (f, "unbindall\n"); for (i = 0; i < 256; i++) { if (keybindings[i] && *keybindings[i]) fprintf (f, "bind \"%s\" \"%s\"\n", Key_KeynumToString(i), keybindings[i]); } } /* =================== Key_Init =================== */ void Key_Init (void) { int i; for (i = 0; i < 32; i++) { key_lines[i][0] = ']'; key_lines[i][1] = 0; } key_linepos = 1; memset (consolekeys, 0, sizeof(consolekeys)); memset (menubound, 0, sizeof(menubound)); memset (keyreserved, 0, sizeof(keyreserved)); // init ascii characters in console mode for (i = 32; i < 128; i++) consolekeys[i] = true; consolekeys[K_ENTER] = true; consolekeys[K_TAB] = true; consolekeys[K_LEFTARROW] = true; consolekeys[K_RIGHTARROW] = true; consolekeys[K_UPARROW] = true; consolekeys[K_DOWNARROW] = true; consolekeys[K_BACKSPACE] = true; consolekeys[K_DEL] = true; consolekeys[K_INS] = true; consolekeys[K_HOME] = true; consolekeys[K_END] = true; consolekeys[K_PGUP] = true; consolekeys[K_PGDN] = true; consolekeys[K_SHIFT] = true; consolekeys[K_MWHEELUP] = true; consolekeys[K_MWHEELDOWN] = true; consolekeys['`'] = false; consolekeys['~'] = false; for (i = 0; i < 256; i++) keyshift[i] = i; for (i = 'a'; i <= 'z'; i++) keyshift[i] = i - 'a' + 'A'; keyshift['1'] = '!'; keyshift['2'] = '@'; keyshift['3'] = '#'; keyshift['4'] = '$'; keyshift['5'] = '%'; keyshift['6'] = '^'; keyshift['7'] = '&'; keyshift['8'] = '*'; keyshift['9'] = '('; keyshift['0'] = ')'; keyshift['-'] = '_'; keyshift['='] = '+'; keyshift[','] = '<'; keyshift['.'] = '>'; keyshift['/'] = '?'; keyshift[';'] = ':'; keyshift['\''] = '"'; keyshift['['] = '{'; keyshift[']'] = '}'; keyshift['`'] = '~'; keyshift['\\'] = '|'; menubound[K_ESCAPE] = true; for (i = 0; i < 12; i++) menubound[K_F1+i] = true; memset (key_repeats, 0, sizeof(key_repeats)); // bind our reserved keys Key_SetBinding ('`', "toggleconsole"); Key_SetBinding ('~', "toggleconsole"); Key_SetBinding (K_PAUSE, "pause"); keyreserved['`'] = true; keyreserved['~'] = true; keyreserved[K_KP_NUMLOCK] = true; keyreserved[K_PAUSE] = true; // register our functions Cmd_AddCommand ("bind",Key_Bind_f); Cmd_AddCommand ("unbind",Key_Unbind_f); Cmd_AddCommand ("unbindall",Key_Unbindall_f); } /* =================== Key_Event Called by the system between frames for both key up and key down events Should NOT be called during an interrupt! =================== */ void Key_Event (int key, qboolean down) { char *kb; char cmd[1024]; keydown[key] = down; if (!down) key_repeats[key] = 0; key_lastpress = key; key_count++; if (key_count <= 0) return; // just catching keys for Con_NotifyBox // update auto-repeat status if (down) { /* Pause key doesn't generate a scancode when released, * never increment its auto-repeat status. */ if (key != K_PAUSE && key != K_KP_NUMLOCK) key_repeats[key]++; #if 0 if (key != K_BACKSPACE && key != K_PGUP && key != K_PGDN && key_repeats[key] > 1) return; // ignore most autorepeats #endif if (key_repeats[key] > 1) { if (key_dest == key_game && !con_forcedup) return; // ignore autorepeats in game mode } else if (key >= 200 && !keybindings[key]) Con_Printf ("%s is unbound, hit F4 to set.\n", Key_KeynumToString(key)); } if (key == K_SHIFT) shift_down = down; // handle escape specialy, so the user can never unbind it if (key == K_ESCAPE) { if (!down) return; switch (key_dest) { case key_message: Key_Message (key); break; case key_menu: M_Keydown (key); break; case key_menubind: M_Keybind (key); break; case key_game: case key_console: M_ToggleMenu_f (); break; default: Sys_Error ("Bad key_dest"); } return; } // key up events only generate commands if the game key binding is // a button command (leading + sign). These will occur even in console mode, // to keep the character from continuing an action started before a console // switch. Button commands include the kenum as a parameter, so multiple // downs can be matched with ups if (!down) { kb = keybindings[key]; if (kb && kb[0] == '+') { sprintf (cmd, "-%s %i\n", kb+1, key); Cbuf_AddText (cmd); } if (keyshift[key] != key) { kb = keybindings[keyshift[key]]; if (kb && kb[0] == '+') { sprintf (cmd, "-%s %i\n", kb+1, key); Cbuf_AddText (cmd); } } return; } // during demo playback, most keys bring up the main menu if (cls.demoplayback && down && consolekeys[key] && key_dest == key_game) { M_ToggleMenu_f (); return; } // if not a consolekey, send to the interpreter no matter what mode is if (((key_dest & key_menu) && menubound[key]) || (key_dest == key_console && !consolekeys[key]) || (key_dest == key_game && (!con_forcedup || !consolekeys[key]))) { kb = keybindings[key]; if (kb) { if (kb[0] == '+') { // button commands add keynum as a parm sprintf (cmd, "%s %i\n", kb, key); Cbuf_AddText (cmd); } else { Cbuf_AddText (kb); Cbuf_AddText ("\n"); } } return; } if (!down) return; // other systems only care about key down events if (shift_down) key = keyshift[key]; switch (key_dest) { case key_message: Key_Message (key); break; case key_menu: M_Keydown (key); break; case key_menubind: M_Keybind (key); break; case key_game: case key_console: Key_Console (key); break; default: Sys_Error ("Bad key_dest"); } } /* =================== Key_ClearStates =================== */ void Key_ClearStates (void) { int i; for (i = 0; i < 256; i++) { if (keydown[i]) Key_Event (i, false); } } qboolean Key_IsGameKey (void) { return ((key_dest == key_game && !con_forcedup) || (key_dest == key_menubind)); } keydest_t Key_GetDest (void) { return key_dest; } void Key_SetDest (keydest_t dest) { key_dest = dest; if ((key_gamekey = Key_IsGameKey()) != prev_gamekey) { prev_gamekey = key_gamekey; Key_ClearStates(); } } engine/hexenworld/client/menu.c000066400000000000000000001727221444734033100171060ustar00rootroot00000000000000/* * menu.c * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "bgmusic.h" #include "cdaudio.h" #include "r_shared.h" void (*vid_menudrawfn)(void); void (*vid_menukeyfn)(int key); enum m_state_e m_state; void M_Menu_Main_f (void); static void M_Menu_MultiPlayer_f (void); static void M_Menu_Setup_f (void); void M_Menu_Options_f (void); static void M_Menu_Keys_f (void); static void M_Menu_Video_f (void); static void M_Menu_Help_f (void); void M_Menu_Quit_f (void); static void M_Main_Draw (void); static void M_MultiPlayer_Draw (void); static void M_Setup_Draw (void); static void M_Options_Draw (void); static void M_Keys_Draw (void); static void M_Video_Draw (void); static void M_Help_Draw (void); static void M_Quit_Draw (void); static void M_Main_Key (int key); static void M_MultiPlayer_Key (int key); static void M_Menu_Connect_f (void); static void M_Setup_Key (int key); static void M_Options_Key (int key); static void M_Keys_Key (int key); static void M_Video_Key (int key); static void M_Help_Key (int key); static void M_Quit_Key (int key); static qboolean m_entersound; // play after drawing a frame, so caching // won't disrupt the sound static qboolean m_recursiveDraw; qboolean menu_disabled_mouse = false; static float TitlePercent = 0; static float TitleTargetPercent = 1; static float LogoPercent = 0; static float LogoTargetPercent = 1; static int setup_class, which_class; static const char *message,*message2; static double message_time; static const char *ClassNames[MAX_PLAYER_CLASS] = { "Paladin", "Crusader", "Necromancer", "Assassin", "Succubus", "Dwarf" }; //============================================================================= /* Support Routines */ /* ================ M_DrawCharacter Draws one solid graphics character ================ */ void M_DrawCharacter (int cx, int line, int num) { Draw_Character ( cx + ((vid.width - 320)>>1), line, num); } void M_Print (int cx, int cy, const char *str) { while (*str) { M_DrawCharacter (cx, cy, ((unsigned char)(*str))+256); str++; cx += 8; } } void M_PrintWhite (int cx, int cy, const char *str) { while (*str) { M_DrawCharacter (cx, cy, (unsigned char)*str); str++; cx += 8; } } void M_DrawTransPic (int x, int y, qpic_t *pic) { Draw_TransPic (x + ((vid.width - 320)>>1), y, pic); } void M_DrawPic (int x, int y, qpic_t *pic) { Draw_Pic (x + ((vid.width - 320)>>1), y, pic); } static void M_DrawTransPicCropped (int x, int y, qpic_t *pic) { Draw_TransPicCropped (x + ((vid.width - 320)>>1), y, pic); } static byte identityTable[256]; static byte translationTable[256]; static void M_BuildTranslationTable(int top, int bottom) { int j; byte *dest, *source, *sourceA, *sourceB, *colorA, *colorB; for (j = 0; j < 256; j++) identityTable[j] = j; dest = translationTable; source = identityTable; memcpy (dest, source, 256); if (top > 10) top = 0; if (bottom > 10) bottom = 0; top -= 1; bottom -= 1; colorA = playerTranslation + 256 + color_offsets[(int)which_class-1]; colorB = colorA + 256; sourceA = colorB + 256 + (top * 256); sourceB = colorB + 256 + (bottom * 256); for (j = 0; j < 256; j++, colorA++, colorB++, sourceA++, sourceB++) { if (top >= 0 && (*colorA != 255)) dest[j] = source[*sourceA]; if (bottom >= 0 && (*colorB != 255)) dest[j] = source[*sourceB]; } } void M_DrawTextBox (int x, int y, int width, int lines) { qpic_t *p; int cx, cy; int n; // draw left side cx = x; cy = y; p = Draw_CachePic ("gfx/box_tl.lmp"); M_DrawTransPic (cx, cy, p); p = Draw_CachePic ("gfx/box_ml.lmp"); for (n = 0; n < lines; n++) { cy += 8; M_DrawTransPic (cx, cy, p); } p = Draw_CachePic ("gfx/box_bl.lmp"); M_DrawTransPic (cx, cy+8, p); // draw middle cx += 8; while (width > 0) { cy = y; p = Draw_CachePic ("gfx/box_tm.lmp"); M_DrawTransPic (cx, cy, p); p = Draw_CachePic ("gfx/box_mm.lmp"); for (n = 0; n < lines; n++) { cy += 8; if (n == 1) p = Draw_CachePic ("gfx/box_mm2.lmp"); M_DrawTransPic (cx, cy, p); } p = Draw_CachePic ("gfx/box_bm.lmp"); M_DrawTransPic (cx, cy+8, p); width -= 2; cx += 16; } // draw right side cy = y; p = Draw_CachePic ("gfx/box_tr.lmp"); M_DrawTransPic (cx, cy, p); p = Draw_CachePic ("gfx/box_mr.lmp"); for (n = 0; n < lines; n++) { cy += 8; M_DrawTransPic (cx, cy, p); } p = Draw_CachePic ("gfx/box_br.lmp"); M_DrawTransPic (cx, cy+8, p); } //============================================================================= static int m_save_demonum; /* ================ M_ToggleMenu_f ================ */ void M_ToggleMenu_f (void) { keydest_t dest = Key_GetDest(); m_entersound = true; if (dest & key_menu) { if (m_state != m_main) { LogoTargetPercent = TitleTargetPercent = 1; LogoPercent = TitlePercent = 0; M_Menu_Main_f (); return; } Key_SetDest (key_game); m_state = m_none; Sbar_Changed (); return; } if (dest == key_console && cls.state == ca_active) { Con_ToggleConsole_f (); } else { LogoTargetPercent = TitleTargetPercent = 1; LogoPercent = TitlePercent = 0; M_Menu_Main_f (); } } // Note: old version of demo has bigfont.lmp, not bigfont2.lmp #define BIGCHAR_FONT_FILE0 "gfx/menu/bigfont.lmp" #define BIGCHAR_FONT_FILE "gfx/menu/bigfont2.lmp" #define BIGCHAR_WIDTH_FILE "gfx/menu/fontsize.lmp" static char BigCharWidth[27][27]; static void M_BuildBigCharWidth (void) { qpic_t *p; byte *source; int ypos, xpos; int numA, numB; int biggestX, adjustment; char After[20], Before[20]; p = (qpic_t *)FS_LoadTempFile (BIGCHAR_FONT_FILE, NULL); if (!p) p = (qpic_t *)FS_LoadTempFile (BIGCHAR_FONT_FILE0, NULL); if (!p) Sys_Error ("Failed to load %s", BIGCHAR_FONT_FILE); SwapPic(p); for (numA = 0; numA < 27; numA++) { memset (After, 20, sizeof(After)); source = p->data + ((numA % 8) * 20) + (numA / 8 * p->width * 20); biggestX = 0; for (ypos = 0; ypos < 19; ypos++) { for (xpos = 0; xpos < 19; xpos++, source++) { if (*source) { After[ypos] = xpos; if (xpos > biggestX) biggestX = xpos; } } source += (p->width - 19); } biggestX++; for (numB = 0; numB < 27; numB++) { memset (Before, 0, sizeof(Before)); source = p->data + ((numB % 8) * 20) + (numB / 8 * p->width * 20); adjustment = 0; for (ypos = 0; ypos < 19; ypos++) { for (xpos = 0; xpos < 19; xpos++, source++) { if (!(*source)) { Before[ypos]++; } else break; } source += (p->width - xpos); } while (1) { for (ypos = 0; ypos < 19; ypos++) { if (After[ypos] - Before[ypos] >= 15) break; Before[ypos]--; } if (ypos < 19) break; adjustment--; } BigCharWidth[numA][numB] = adjustment + biggestX; } } FS_CreatePath(FS_MakePath(FS_USERDIR, NULL, BIGCHAR_WIDTH_FILE)); FS_WriteFile (BIGCHAR_WIDTH_FILE, BigCharWidth, sizeof(BigCharWidth)); } static int M_DrawBigCharacter (int x, int y, int num, int numNext) { int add; if (num == ' ') return 32; if (num == '/') num = 26; else num -= 65; if (num < 0 || num >= 27) // only a-z and / return 0; if (numNext == '/') numNext = 26; else numNext -= 65; Draw_BigCharacter (x, y, num); if (numNext < 0 || numNext >= 27) return 0; add = 0; if (num == (int)'C'-65 && numNext == (int)'P'-65) add = 3; return BigCharWidth[num][numNext] + add; } static void M_DrawBigString(int x, int y, const char *string) { x += ((vid.width - 320)>>1); while (*string) { x += M_DrawBigCharacter(x, y, string[0], string[1]); ++string; } } void ScrollTitle (const char *name) { qpic_t *p; float delta; int finaly; static const char *LastName = ""; static qboolean CanSwitch = true; if (TitlePercent < TitleTargetPercent) { delta = ((TitleTargetPercent-TitlePercent)/0.5)*host_frametime; if (delta < 0.004) delta = 0.004; TitlePercent += delta; if (TitlePercent > TitleTargetPercent) { TitlePercent = TitleTargetPercent; } } else if (TitlePercent > TitleTargetPercent) { delta = ((TitlePercent-TitleTargetPercent)/0.15)*host_frametime; if (delta < 0.02) delta = 0.02; TitlePercent -= delta; if (TitlePercent <= TitleTargetPercent) { TitlePercent = TitleTargetPercent; CanSwitch = true; } } if (LogoPercent < LogoTargetPercent) { /* delta = ((LogoTargetPercent-LogoPercent)/1.1)*host_frametime; if (delta < 0.0015) delta = 0.0015; */ delta = ((LogoTargetPercent-LogoPercent)/.15)*host_frametime; if (delta < 0.02) delta = 0.02; LogoPercent += delta; if (LogoPercent > LogoTargetPercent) LogoPercent = LogoTargetPercent; } if (q_strcasecmp(LastName,name) != 0 && TitleTargetPercent != 0) TitleTargetPercent = 0; if (CanSwitch) { LastName = name; CanSwitch = false; TitleTargetPercent = 1; } p = Draw_CachePic(LastName); finaly = ((float)p->height * TitlePercent) - p->height; M_DrawTransPicCropped( (320-p->width)/2, finaly , p); if (m_state != m_keys) { p = Draw_CachePic("gfx/menu/hplaque.lmp"); finaly = ((float)p->height * LogoPercent) - p->height; M_DrawTransPicCropped(10, finaly, p); } } //============================================================================= /* MAIN MENU */ static int m_main_cursor; #define MAIN_ITEMS 4 static void BGM_RestartMusic(void); static char old_bgmtype[20]; // S.A void M_Menu_Main_f (void) { // Deactivate the mouse when the menus are drawn - S.A. menu_disabled_mouse = true; if (modestate == MS_WINDOWED) IN_DeactivateMouse (); if (!(Key_GetDest() & key_menu)) { m_save_demonum = cls.demonum; cls.demonum = -1; } Key_SetDest (key_menu); m_state = m_main; m_entersound = true; } static void M_Main_Draw (void) { int f; ScrollTitle("gfx/menu/title0.lmp"); M_DrawBigString (72, 60 + (0 * 20), "MULTIPLAYER"); M_DrawBigString (72, 60 + (1 * 20), "OPTIONS"); M_DrawBigString (72, 60 + (2 * 20), "HELP"); M_DrawBigString (72, 60 + (3 * 20), "QUIT"); f = (int)(realtime * 10)%8; M_DrawTransPic (43, 54 + m_main_cursor * 20,Draw_CachePic( va("gfx/menu/menudot%i.lmp", f+1 ) ) ); } static void M_Main_Key (int key) { switch (key) { case K_ESCAPE: // leaving the main menu, reactivate mouse - S.A. menu_disabled_mouse = false; IN_ActivateMouse (); // and check we haven't changed the music type if (old_bgmtype[0] != 0 && strcmp(old_bgmtype,bgmtype.string) != 0) BGM_RestartMusic (); old_bgmtype[0] = 0; Key_SetDest (key_game); m_state = m_none; Sbar_Changed (); cls.demonum = m_save_demonum; if (cls.demonum != -1 && !cls.demoplayback && cls.state == ca_disconnected) CL_NextDemo (); break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); if (++m_main_cursor >= MAIN_ITEMS) m_main_cursor = 0; break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); if (--m_main_cursor < 0) m_main_cursor = MAIN_ITEMS - 1; break; case K_ENTER: m_entersound = true; switch (m_main_cursor) { case 0: M_Menu_MultiPlayer_f (); break; case 1: M_Menu_Options_f (); break; case 2: M_Menu_Help_f (); break; case 3: M_Menu_Quit_f (); break; } } } //============================================================================= /* OPTIONS MENU */ #define SLIDER_RANGE 10 enum { OPT_CUSTOMIZE = 0, OPT_CONSOLE, OPT_DEFAULTS, #ifdef GLQUAKE OPT_SCALE, #endif OPT_SCRSIZE, OPT_GAMMA, OPT_MOUSESPEED, OPT_MUSICTYPE, OPT_MUSICVOL, OPT_SNDVOL, OPT_ALWAYRUN, OPT_INVMOUSE, OPT_ALWAYSMLOOK, OPT_USEMOUSE, OPT_CROSSHAIR, #ifdef GLQUAKE OPT_OPENGL, #endif OPT_VIDEO, OPTIONS_ITEMS }; #ifdef GLQUAKE // prototypes for the opengl menu static void M_Menu_OpenGL_f (void); static void M_OpenGL_Draw (void); static void M_OpenGL_Key (int k); #endif static int options_cursor; void M_Menu_Options_f (void) { Key_SetDest (key_menu); m_state = m_options; m_entersound = true; // get the current music type if (old_bgmtype[0] == 0) q_strlcpy(old_bgmtype, bgmtype.string, sizeof(old_bgmtype)); #if 0 /* change to 1 if dont want to disable mouse in fullscreen */ if ((options_cursor == OPT_USEMOUSE) && (modestate != MS_WINDOWED)) options_cursor = 0; #endif } static void M_AdjustSliders (int dir) { float f; S_LocalSound ("raven/menu3.wav"); switch (options_cursor) { #ifdef GLQUAKE case OPT_SCALE: // scale slider S.A. VID_ChangeConsize(dir); break; #endif case OPT_SCRSIZE: // screen size Cvar_SetValue ("viewsize", scr_viewsize.integer + dir * 10); break; case OPT_GAMMA: // gamma f = v_gamma.value - dir * 0.05; if (f < 0.5) f = 0.5; else if (f > 1) f = 1; Cvar_SetValue ("gamma", f); break; case OPT_MOUSESPEED: // mouse speed f = sensitivity.value + dir * 0.5; if (f > 11) f = 11; else if (f < 1) f = 1; Cvar_SetValue ("sensitivity", f); break; case OPT_MUSICTYPE: // bgm type if (q_strcasecmp(bgmtype.string,"midi") == 0) { if (bgm_extmusic.integer) { if (dir < 0) Cvar_Set("bgm_extmusic","0"); else Cvar_Set("bgmtype","cd"); } else { if (dir < 0) Cvar_Set("bgmtype","none"); else Cvar_Set("bgm_extmusic","1"); } } else if (q_strcasecmp(bgmtype.string,"cd") == 0) { if (dir < 0) { Cvar_Set("bgmtype","midi"); Cvar_Set("bgm_extmusic","1"); } else { Cvar_Set("bgmtype","none"); } } else { if (dir < 0) { Cvar_Set("bgmtype","cd"); } else { Cvar_Set("bgm_extmusic","0"); Cvar_Set("bgmtype","midi"); } } break; case OPT_MUSICVOL: // music volume f = bgmvolume.value + dir * 0.1; if (f < 0) f = 0; else if (f > 1) f = 1; Cvar_SetValue ("bgmvolume", f); break; case OPT_SNDVOL: // sfx volume f = sfxvolume.value + dir * 0.1; if (f < 0) f = 0; else if (f > 1) f = 1; Cvar_SetValue ("volume", f); break; case OPT_ALWAYRUN: // always run if (cl_forwardspeed.value > 200) { Cvar_Set ("cl_forwardspeed", "200"); Cvar_Set ("cl_backspeed", "200"); } else { Cvar_Set ("cl_forwardspeed", "400"); Cvar_Set ("cl_backspeed", "400"); } break; case OPT_INVMOUSE: // invert mouse Cvar_SetValue ("m_pitch", -m_pitch.value); break; case OPT_ALWAYSMLOOK: if (in_mlook.state & 1) Cbuf_AddText("-mlook"); else Cbuf_AddText("+mlook"); break; case OPT_CROSSHAIR: Cvar_Set ("crosshair", crosshair.integer ? "0" : "1"); break; case OPT_USEMOUSE: // _enable_mouse Cvar_Set ("_enable_mouse", _enable_mouse.integer ? "0" : "1"); break; default: break; } } static void M_DrawSlider (int x, int y, float range) { int i; if (range < 0) range = 0; else if (range > 1) range = 1; M_DrawCharacter (x-8, y, 256); for (i = 0; i < SLIDER_RANGE; i++) M_DrawCharacter (x + i*8, y, 257); M_DrawCharacter (x + i*8, y, 258); M_DrawCharacter (x + (SLIDER_RANGE-1)*8 * range, y, 259); } void M_DrawCheckbox (int x, int y, int on) { if (on) M_Print (x, y, "on"); else M_Print (x, y, "off"); } static void M_Options_Draw (void) { float r; menu_disabled_mouse = false; IN_ActivateMouse (); // we entered the customization menu ScrollTitle("gfx/menu/title3.lmp"); // we use 22 character option titles. the increment to // the x offset is: (22 - strlen(option_title)) * 8 // r goes from 0 (left) to 1 (right) M_Print (16 + (4 * 8), 60 + 8*OPT_CUSTOMIZE, "Customize controls"); M_Print (16 + (9 * 8), 60 + 8*OPT_CONSOLE, "Go to console"); M_Print (16 + (5 * 8), 60 + 8*OPT_DEFAULTS, "Reset to defaults"); #ifdef GLQUAKE M_Print (16 + (17 * 8), 60 + 8*OPT_SCALE, "Scale"); r = VID_ReportConsize(); M_DrawSlider (220, 60 + 8*OPT_SCALE, (r-1)/2); #endif M_Print (16 + (11 * 8), 60 + 8*OPT_SCRSIZE, "Screen size"); r = (scr_viewsize.value - 30.0) / (130 - 30); M_DrawSlider (220, 60 + 8*OPT_SCRSIZE, r); M_Print (16 + (12 * 8), 60 + 8*OPT_GAMMA, "Brightness"); r = (1.0 - v_gamma.value) / 0.5; M_DrawSlider (220, 60 + 8*OPT_GAMMA, r); M_Print (16 + (11 * 8), 60 + 8*OPT_MOUSESPEED, "Mouse Speed"); r = (sensitivity.value - 1) / 10; M_DrawSlider (220, 60 + 8*OPT_MOUSESPEED, r); M_Print (16 + (12 * 8), 60 + 8*OPT_MUSICTYPE, "Music Type"); if (q_strcasecmp(bgmtype.string, "midi") == 0) { if (bgm_extmusic.integer) M_Print (220, 60 + 8*OPT_MUSICTYPE, "ALL CODECS"); else M_Print (220, 60 + 8*OPT_MUSICTYPE, "MIDI ONLY"); } else if (q_strcasecmp(bgmtype.string, "cd") == 0) M_Print (220, 60 + 8*OPT_MUSICTYPE, "CD"); else M_Print (220, 60 + 8*OPT_MUSICTYPE, "None"); M_Print (16 + (10 * 8), 60 + 8*OPT_MUSICVOL, "Music Volume"); r = bgmvolume.value; M_DrawSlider (220, 60 + 8*OPT_MUSICVOL, r); M_Print (16 + (10 * 8), 60 + 8*OPT_SNDVOL, "Sound Volume"); r = sfxvolume.value; M_DrawSlider (220, 60 + 8*OPT_SNDVOL, r); M_Print (16 + (12 * 8), 60 + 8*OPT_ALWAYRUN, "Always Run"); M_DrawCheckbox (220, 60 + 8*OPT_ALWAYRUN, (cl_forwardspeed.value > 200)); M_Print (16 + (10 * 8), 60 + 8*OPT_INVMOUSE, "Invert Mouse"); M_DrawCheckbox (220, 60 + 8*OPT_INVMOUSE, m_pitch.value < 0); M_Print (16 + (12 * 8), 60 + 8*OPT_ALWAYSMLOOK, "Mouse Look"); M_DrawCheckbox (220, 60 + 8*OPT_ALWAYSMLOOK, in_mlook.state & 1); M_Print (16 + (13 * 8), 60 + 8*OPT_USEMOUSE, "Use Mouse"); M_DrawCheckbox (220, 60 + 8*OPT_USEMOUSE, _enable_mouse.integer); M_Print (16 + (8 * 8), 60 + 8*OPT_CROSSHAIR, "Show Crosshair"); M_DrawCheckbox (220, 60 + 8*OPT_CROSSHAIR, crosshair.integer); #ifdef GLQUAKE M_Print (16 + (7 * 8), 60 + 8*OPT_OPENGL, "OpenGL Features"); #endif if (vid_menudrawfn) M_Print (16 + (11 * 8), 60 + 8*OPT_VIDEO, "Video Modes"); // cursor M_DrawCharacter (200, 60 + 8*options_cursor, 12 + ((int)(realtime*4) & 1)); } static void M_Options_Key (int k) { switch (k) { case K_ESCAPE: M_Menu_Main_f (); break; case K_ENTER: m_entersound = true; switch (options_cursor) { case OPT_CUSTOMIZE: M_Menu_Keys_f (); break; case OPT_CONSOLE: //m_state = m_none; //Con_ToggleConsole_f (); Key_SetDest (key_game); m_state = m_none; break; case OPT_DEFAULTS: Cbuf_AddText ("exec default.cfg\n"); break; #ifdef GLQUAKE case OPT_OPENGL: M_Menu_OpenGL_f (); break; #endif case OPT_VIDEO: M_Menu_Video_f (); break; default: M_AdjustSliders (1); break; } return; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); options_cursor--; if (options_cursor < 0) options_cursor = OPTIONS_ITEMS-1; break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); options_cursor++; if (options_cursor >= OPTIONS_ITEMS) options_cursor = 0; break; case K_LEFTARROW: M_AdjustSliders (-1); break; case K_RIGHTARROW: M_AdjustSliders (1); break; } #if 0 /* change to 1 if dont want to disable mouse in fullscreen */ if ((options_cursor == OPT_USEMOUSE) && (modestate != MS_WINDOWED)) { if (k == K_UPARROW) options_cursor = OPT_USEMOUSE - 1; else { options_cursor = OPT_USEMOUSE + 1; if (options_cursor == OPTIONS_ITEMS) options_cursor = 0; } } #endif if (options_cursor == OPT_VIDEO && vid_menudrawfn == NULL) { if (k == K_UPARROW) options_cursor = OPT_VIDEO - 1; else options_cursor = 0; } } //============================================================================= /* OPENGL FEATURES MENU */ #ifdef GLQUAKE enum { OGL_PURGETEX, OGL_GLOW1, OGL_GLOW2, OGL_GLOW3, OGL_LIGHTMAPFMT, OGL_COLOREDLIGHT, OGL_COLOREDSTATIC, OGL_COLOREDDYNAMIC, OGL_COLOREDEXTRA, OGL_TEXFILTER, OGL_ANISOTROPY, OGL_ITEMS }; static const struct { const char *name; // legible string value const char *desc; // description for user int glenum; // opengl enum } lm_formats[] = { { "gl_luminance", "gl_luminance (8 bit)", GL_LUMINANCE }, { "gl_rgba", "gl_rgba (32 bit)", GL_RGBA }, { " ", "Unknown value (?)", 0 } }; #define MAX_LMFORMATS (sizeof(lm_formats) / sizeof(lm_formats[0])) static int tex_mode; static int lm_format; static int opengl_cursor; static void M_Menu_OpenGL_f (void) { Key_SetDest (key_menu); m_state = m_opengl; m_entersound = true; } static void M_OpenGL_Draw (void) { int i; ScrollTitle("gfx/menu/title3.lmp"); M_PrintWhite (96, 72, "OpenGL Features:"); // we use 22 character option titles. the increment to // the x offset is: (22 - strlen(option_title)) * 8 M_Print (32 + (4 * 8), 90 + 8*OGL_PURGETEX, "Purge map textures"); M_DrawCheckbox (232, 90 + 8*OGL_PURGETEX, gl_purge_maptex.integer); M_Print (32 + (9 * 8), 90 + 8*OGL_GLOW1, "missile glows"); M_DrawCheckbox (232, 90 + 8*OGL_GLOW1, gl_missile_glows.integer); M_Print (32 + (11 * 8), 90 + 8*OGL_GLOW2, "torch glows"); M_DrawCheckbox (232, 90 + 8*OGL_GLOW2, gl_glows.integer); M_Print (32 + (11 * 8), 90 + 8*OGL_GLOW3, "other glows"); M_DrawCheckbox (232, 90 + 8*OGL_GLOW3, gl_other_glows.integer); M_Print (32 + (6 * 8), 90 + 8*OGL_LIGHTMAPFMT, "Lightmap Format:"); for (i = 0; i < (int)MAX_LMFORMATS; i++) { if (!q_strcasecmp(gl_lightmapfmt.string, lm_formats[i].name)) break; } lm_format = i; M_Print (232, 90 + 8*OGL_LIGHTMAPFMT, lm_formats[lm_format].desc); M_Print (32 + (6 * 8), 90 + 8*OGL_COLOREDLIGHT, "Colored lights :"); M_Print (32 + (9 * 8), 90 + 8*OGL_COLOREDSTATIC, "static lights"); M_Print (32 + (8 * 8), 90 + 8*OGL_COLOREDDYNAMIC, "dynamic lights"); M_Print (32 + (10 * 8), 90 + 8*OGL_COLOREDEXTRA, "extra lights"); // bound the gl_coloredlight value if (gl_coloredlight.integer < 0) Cvar_Set ("gl_coloredlight", "0"); switch (gl_coloredlight.integer) { case 0: M_Print (232, 90 + 8*OGL_COLOREDSTATIC, "none (white)"); break; case 1: M_Print (232, 90 + 8*OGL_COLOREDSTATIC, "colored"); break; default: M_Print (232, 90 + 8*OGL_COLOREDSTATIC, "blend"); break; } M_DrawCheckbox (232, 90 + 8*OGL_COLOREDDYNAMIC, gl_colored_dynamic_lights.integer); M_DrawCheckbox (232, 90 + 8*OGL_COLOREDEXTRA, gl_extra_dynamic_lights.integer); M_Print (32 + (5 * 8), 90 + 8*OGL_TEXFILTER, "Texture filtering"); M_Print (232, 90 + 8*OGL_TEXFILTER, gl_texmodes[gl_filter_idx].name); M_Print (32 + (5 * 8), 90 + 8*OGL_ANISOTROPY, "Anisotropy level:"); M_Print (232, 90 + 8*OGL_ANISOTROPY, (gl_max_anisotropy < 2) ? "N/A" : Cvar_VariableString("gl_texture_anisotropy")); // cursor M_DrawCharacter (216, 90 + opengl_cursor*8, 12+((int)(realtime*4)&1)); if (opengl_cursor == OGL_LIGHTMAPFMT && gl_lightmap_format != lm_formats[lm_format].glenum) { int x = (320-25*8)/2; for (i = 0; i < (int)MAX_LMFORMATS-1; i++) { if (gl_lightmap_format == lm_formats[i].glenum) break; } M_DrawTextBox (x, 94 + 8*(OGL_LIGHTMAPFMT), 25, 5); M_Print (x+16, 98 + 8*(OGL_LIGHTMAPFMT+1), "currently running with:"); M_Print (x+16, 98 + 8*(OGL_LIGHTMAPFMT+2), lm_formats[i].desc); M_Print (x+16, 98 + 8*(OGL_LIGHTMAPFMT+3), "your selection will take"); M_Print (x+16, 98 + 8*(OGL_LIGHTMAPFMT+4), "effect upon level reload"); } else if (opengl_cursor == OGL_COLOREDSTATIC) { int x = (320-25*8)/2; if (gl_lightmap_format != GL_RGBA) { if (gl_coloredlight.integer) { M_DrawTextBox (x, 94 + 8*(OGL_COLOREDSTATIC), 25, 3); M_Print (x+16, 98 + 8*(OGL_COLOREDSTATIC+1), " rgba lightmap format "); M_Print (x+16, 98 + 8*(OGL_COLOREDSTATIC+2), " is required"); } } else if (gl_coloredlight.integer != gl_coloredstatic) { M_DrawTextBox (x, 94 + 8*(OGL_COLOREDSTATIC), 25, 3); M_Print (x+16, 98 + 8*(OGL_COLOREDSTATIC+1), "your selection will take"); M_Print (x+16, 98 + 8*(OGL_COLOREDSTATIC+2), "effect upon level reload"); } } } static void M_OpenGL_Key (int k) { switch (k) { case K_ESCAPE: M_Menu_Options_f (); break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); if (--opengl_cursor == OGL_COLOREDLIGHT) --opengl_cursor; if (opengl_cursor < 0) opengl_cursor = OGL_ITEMS-1; break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); if (++opengl_cursor == OGL_COLOREDLIGHT) ++opengl_cursor; if (opengl_cursor >= OGL_ITEMS) opengl_cursor = 0; break; case K_ENTER: case K_LEFTARROW: case K_RIGHTARROW: m_entersound = true; switch (opengl_cursor) { case OGL_PURGETEX: // purge gl textures on map change Cvar_Set ("gl_purge_maptex", gl_purge_maptex.integer ? "0" : "1"); break; case OGL_GLOW1: // glow effects, missiles Cvar_Set ("gl_missile_glows", gl_missile_glows.integer ? "0" : "1"); break; case OGL_GLOW2: // glow effects, torches Cvar_Set ("gl_glows", gl_glows.integer ? "0" : "1"); break; case OGL_GLOW3: // glow effects, other: mana, etc. Cvar_Set ("gl_other_glows", gl_other_glows.integer ? "0" : "1"); break; case OGL_LIGHTMAPFMT: // lightmap format switch (k) { case K_RIGHTARROW: if (++lm_format >= (int)MAX_LMFORMATS - 1) lm_format = MAX_LMFORMATS - 2; Cvar_Set ("gl_lightmapfmt", lm_formats[lm_format].name); break; case K_LEFTARROW: if (--lm_format < 0) lm_format = 0; Cvar_Set ("gl_lightmapfmt", lm_formats[lm_format].name); break; default: break; } break; case OGL_COLOREDSTATIC: // static colored lights switch (k) { case K_RIGHTARROW: Cvar_Set ("gl_coloredlight", (gl_coloredlight.integer >= 1) ? "2" : "1"); break; case K_LEFTARROW: Cvar_Set ("gl_coloredlight", (gl_coloredlight.integer <= 1) ? "0" : "1"); break; default: break; } break; case OGL_COLOREDDYNAMIC: // dynamic colored lights Cvar_Set ("gl_colored_dynamic_lights", gl_colored_dynamic_lights.integer ? "0" : "1"); break; case OGL_COLOREDEXTRA: // extra dynamic colored lights Cvar_Set ("gl_extra_dynamic_lights", gl_extra_dynamic_lights.integer ? "0" : "1"); break; case OGL_TEXFILTER: // texture filter tex_mode = gl_filter_idx; switch (k) { case K_LEFTARROW: if (--tex_mode < 0) tex_mode = 0; break; case K_RIGHTARROW: if (++tex_mode >= NUM_GL_FILTERS) tex_mode = NUM_GL_FILTERS-1; break; default: return; } Cvar_Set ("gl_texturemode", gl_texmodes[tex_mode].name); break; case OGL_ANISOTROPY: // anisotropic filter level if (gl_max_anisotropy < 2) return; tex_mode = Cvar_VariableValue("gl_texture_anisotropy"); switch (k) { case K_LEFTARROW: if (--tex_mode < 1) tex_mode = 1; break; case K_RIGHTARROW: if (++tex_mode > gl_max_anisotropy) tex_mode = gl_max_anisotropy; break; default: return; } Cvar_SetValue ("gl_texture_anisotropy", tex_mode); break; default: break; } default: break; } } #endif //============================================================================= /* KEYS MENU */ static const char *bindnames[][2] = { {"+attack", "attack"}, {"impulse 10", "next weapon"}, {"impulse 12", "prev.weapon"}, {"+jump", "jump / swim up"}, {"+forward", "walk forward"}, {"+back", "backpedal"}, {"+left", "turn left"}, {"+right", "turn right"}, {"+speed", "run"}, {"+moveleft", "step left"}, {"+moveright", "step right"}, {"+strafe", "sidestep"}, {"+crouch", "crouch"}, {"+lookup", "look up"}, {"+lookdown", "look down"}, {"centerview", "center view"}, {"+moveup", "swim up"}, {"+movedown", "swim down"}, {"impulse 13", "use object"}, {"invuse", "use inv item"}, {"invdrop", "drop inv item"}, {"+showinfo", "full inventory"}, {"+showdm", "info / frags"}, {"toggle_dm", "toggle frags"}, {"+shownames", "player names"}, {"invleft", "inv move left"}, {"invright", "inv move right"}, {"impulse 100", "inv:torch"}, {"impulse 101", "inv:qrtz flask"}, {"impulse 102", "inv:mystic urn"}, {"impulse 103", "inv:krater"}, {"impulse 104", "inv:chaos devc"}, {"impulse 105", "inv:tome power"}, {"impulse 106", "inv:summon stn"}, {"impulse 107", "inv:invisiblty"}, {"impulse 108", "inv:glyph"}, {"impulse 109", "inv:boots"}, {"impulse 110", "inv:repulsion"}, {"impulse 111", "inv:bo peep"}, {"impulse 112", "inv:flight"}, {"impulse 113", "inv:force cube"}, {"impulse 114", "inv:icon defn"} }; #define NUMCOMMANDS (sizeof(bindnames)/sizeof(bindnames[0])) #define KEYS_SIZE 14 static int keys_cursor; static int keys_top = 0; static void M_Menu_Keys_f (void) { Key_SetDest (key_menu); m_state = m_keys; m_entersound = true; } static void M_FindKeysForCommand (const char *command, int *twokeys) { int count; int j; int l,l2; char *b; twokeys[0] = twokeys[1] = -1; l = strlen(command); count = 0; for (j = 0; j < 256; j++) { b = keybindings[j]; if (!b) continue; if (!strncmp (b, command, l)) { l2 = strlen(b); if (l == l2) { twokeys[count] = j; count++; if (count == 2) break; } } } } static void M_UnbindCommand (const char *command) { int j; int l; char *b; l = strlen(command); for (j = 0; j < 256; j++) { b = keybindings[j]; if (!b) continue; if (!strncmp (b, command, l) ) Key_SetBinding (j, NULL); } } static void M_Keys_Draw (void) { int i, x, y; int keys[2]; const char *name; ScrollTitle("gfx/menu/title6.lmp"); if (keys_top) M_DrawCharacter (6, 80, 128); if (keys_top + KEYS_SIZE < (int)NUMCOMMANDS) M_DrawCharacter (6, 80 + ((KEYS_SIZE-1)*8), 129); // search for known bindings for (i = 0; i < KEYS_SIZE; i++) { y = 80 + 8*i; M_Print (16, y, bindnames[i+keys_top][1]); M_FindKeysForCommand (bindnames[i+keys_top][0], keys); if (keys[0] == -1) { M_Print (140, y, "???"); } else { name = Key_KeynumToString (keys[0]); M_Print (140, y, name); x = strlen(name) * 8; if (keys[1] != -1) { M_Print (140 + x + 8, y, "or"); M_Print (140 + x + 32, y, Key_KeynumToString (keys[1])); } } } if (Key_GetDest() & key_bindbit) { M_Print (12, 64, "Press a key or button for this action"); M_DrawCharacter (130, 80 + (keys_cursor-keys_top)*8, '='); } else { M_Print (18, 64, "Enter to change, backspace to clear"); M_DrawCharacter (130, 80 + (keys_cursor-keys_top)*8, 12+((int)(realtime*4)&1)); } } static void M_Keys_Key (int k) { int keys[2]; switch (k) { case K_ESCAPE: M_Menu_Options_f (); break; case K_LEFTARROW: case K_UPARROW: S_LocalSound ("raven/menu1.wav"); keys_cursor--; if (keys_cursor < 0) keys_cursor = NUMCOMMANDS-1; break; case K_DOWNARROW: case K_RIGHTARROW: S_LocalSound ("raven/menu1.wav"); keys_cursor++; if (keys_cursor >= (int)NUMCOMMANDS) keys_cursor = 0; break; case K_ENTER: // go into bind mode M_FindKeysForCommand (bindnames[keys_cursor][0], keys); S_LocalSound ("raven/menu2.wav"); if (keys[1] != -1) M_UnbindCommand (bindnames[keys_cursor][0]); Key_SetDest (key_menubind); break; case K_BACKSPACE: // delete bindings case K_DEL: // delete bindings S_LocalSound ("raven/menu2.wav"); M_UnbindCommand (bindnames[keys_cursor][0]); break; } if (keys_cursor < keys_top) keys_top = keys_cursor; else if (keys_cursor >= keys_top+KEYS_SIZE) keys_top = keys_cursor - KEYS_SIZE + 1; } //============================================================================= /* VIDEO MENU */ static void M_Menu_Video_f (void) { Key_SetDest (key_menu); m_state = m_video; m_entersound = true; } static void M_Video_Draw (void) { (*vid_menudrawfn) (); } static void M_Video_Key (int key) { (*vid_menukeyfn) (key); } //============================================================================= /* HELP MENU */ static int help_page; #define NUM_HELP_PAGES 5 #define NUM_SG_HELP_PAGES 10 /* Siege has more help */ static void M_Menu_Help_f (void) { Key_SetDest (key_menu); m_state = m_help; m_entersound = true; help_page = 0; } #if FULLSCREEN_INTERMISSIONS # ifdef GLQUAKE # define Load_HelpPic_FN(X,Y,Z) Draw_CachePicNoTrans((X)) # define Draw_HelpPic_FN(X,Y,Z) Draw_IntermissionPic((Z)) # else # define Load_HelpPic_FN(X,Y,Z) Draw_CachePicResize((X),(Y),(Z)) # define Draw_HelpPic_FN(X,Y,Z) Draw_Pic(0,0,(Z)) # endif #else # ifdef GLQUAKE # define Load_HelpPic_FN(X,Y,Z) Draw_CachePic((X)) # define Draw_HelpPic_FN(X,Y,Z) Draw_Pic((X),(Y),(Z)) # else # define Load_HelpPic_FN(X,Y,Z) Draw_CachePic((X)) # define Draw_HelpPic_FN(X,Y,Z) Draw_Pic((X),(Y),(Z)) # endif #endif static void M_Help_Draw (void) { if (cl_siege) Draw_HelpPic_FN ((vid.width - 320)>>1, 0, Load_HelpPic_FN(va("gfx/menu/sghelp%02i.lmp", help_page+1), vid.width, vid.height)); else Draw_HelpPic_FN ((vid.width - 320)>>1, 0, Load_HelpPic_FN(va("gfx/menu/help%02i.lmp", help_page+1), vid.width, vid.height)); } static void M_Help_Key (int key) { switch (key) { case K_ESCAPE: M_Menu_Main_f (); break; case K_UPARROW: case K_RIGHTARROW: m_entersound = true; if (cl_siege) { if (++help_page >= NUM_SG_HELP_PAGES) help_page = 0; } else if (++help_page >= NUM_HELP_PAGES) help_page = 0; break; case K_DOWNARROW: case K_LEFTARROW: m_entersound = true; if (--help_page < 0) { if (cl_siege) help_page = NUM_SG_HELP_PAGES-1; else help_page = NUM_HELP_PAGES-1; } break; } } //============================================================================= /* QUIT MENU */ //static int msgNumber; static enum m_state_e m_quit_prevstate; static qboolean wasInMenus; #if 0 static const char *quitMessage [] = { /* .........1.........2.... */ " Look! Behind you! ", " There's a big nasty ", " thing - shoot it! ", " ", " You can't go now, I ", " was just getting ", " warmed up. ", " ", " One more game. ", " C'mon... ", " Who's gonna know? ", " ", " What's the matter? ", " Palms too sweaty to ", " keep playing? ", " ", " Watch your local store", " for Hexen 2 ", " plush toys and ", " greeting cards! ", " Hexen 2... ", " ", " Too much is never ", " enough. ", " Sure go ahead and ", " leave. But I know ", " you'll be back. ", " ", " ", " Insert cute phrase ", " here ", " " }; #endif static float LinePos; static int LineTimes; static int MaxLines; static const char **LineText; static qboolean LineTxt2; static qboolean SoundPlayed; #define MAX_LINES 170 static const char *CreditText[MAX_LINES] = { "HexenWorld", "", "Lead Programmer: Rick Johnson", "", "Programming:", " Nathan Albury", " Ron Midthun", " Steve Sengele", " Mike Gummelt", " James Monroe", "", "Deathmatch Levels:", " Kenn Hoekstra", " Mike Renner", " Jeremy Statz", " Jon Zuk", "", "Special thanks to:", " Dave Kirsch", " William Mull", " Jack Mathews", "", "", "Hexen2", "", "Project Director: Brian Raffel", "", "Lead Programmer: Rick Johnson", "", "Programming:", " Ben Gokey", " Bob Love", " Mike Gummelt", "", "Additional Programming:", " Josh Weier", "", "Lead Design: Eric Biessman", "", "Design:", " Brian Raffel", " Brian Frank", " Tom Odell", "", "Art Director: Brian Pelletier", "", "Art:", " Shane Gurno", " Jim Sumwalt", " Mark Morgan", " Kim Lathrop", " Ted Halsted", " Rebecca Rettenmund", " Les Dorscheid", "", "Animation:", " Chaos (Mike Werckle)", " Brian Shubat", "", "Cinematics:", " Jeff Dewitt", " Jeffrey P. Lampo", "", "Music:", " Kevin Schilder", "", "Sound:", " Kevin Schilder", " Chia Chin Lee", "", "", "Activision", "", "Producer:", " Steve Stringer", "", "Localization Producer:", " Sandi Isaacs", "", "Marketing Product Manager:", " Henk Hartong", "", "European Marketing", "Product Director:", " Janine Johnson", "", "Marketing Associate:", " Kevin Kraff", "", "Senior Quality", "Assurance Lead:", " Tim Vanlaw", "", "Quality Assurance Lead:", " John Tam", "", "Quality Assurance Team:", " Steve Rosenthal, Mike Spann,", " Steve Elwell, Kelly Wand,", " Kip Stolberg, Igor Krinitskiy,", " Ian Stevens, Marilena Wahmann,", " David Baker, Winnie Lee", "", "Documentation:", " Mike Rivera, Sylvia Orzel,", " Belinda Vansickle", "", "Chronicle of Deeds written by:", " Joe Grant Bell", "", "Localization:", " Nathalie Dove, Lucy Morgan,", " Alex Wylde, Nicky Kerth", "", "Installer by:", " Steve Stringer, Adam Goldberg,", " Tanya Martino, Eric Schmidt,", " Ronnie Lane", "", "Art Assistance by:", " Carey Chico and Franz Boehm", "", "BizDev Babe:", " Jamie Bafus", "", "And...", "", "Deal Guru:", " Mitch Lasky", "", "", "Thanks to Id software:", " John Carmack", " Adrian Carmack", " Kevin Cloud", " Barrett 'Bear' Alexander", " American McGee", "", "", "Published by Id Software, Inc.", "Distributed by Activision, Inc.", "", "The Id Software Technology used", "under license in Hexen II (tm)", "(c) 1996, 1997 Id Software, Inc.", "All Rights Reserved.", "", "Hexen(r) is a registered trademark", "of Raven Software Corp.", "Hexen II (tm) and the Raven logo", "are trademarks of Raven Software", "Corp. The Id Software name and", "id logo are trademarks of", "Id Software, Inc. Activision(r)", "is a registered trademark of", "Activision, Inc. All other", "trademarks are the property of", "their respective owners.", "", "", "", "Send bug descriptions to:", " h2bugs@mail.ravensoft.com", "", "Special thanks to Gary McTaggart", "at 3dfx for his help with", "the gl version!", "", "No snails were harmed in the", #ifdef DEMOBUILD "making of this demo!" #else "making of this game!" #endif }; #define MAX_LINES2 185 static const char *Credit2Text[MAX_LINES2] = { "HexenWorld", "", "Superior Groucher:", " Rick 'Grrr' Johnson", "", "Bug Creators:", " Nathan 'Glory Code' Albury", " Ron 'Stealth' Midthun", " Steve 'Tie Dye' Sengele", " Mike 'Foos' Gummelt", " James 'Happy' Monroe", "", "Sloppy Joe Makers:", " Kenn 'I'm a broken man'", " Hoekstra", " Mike 'Outback' Renner", " Jeremy 'Under-rated' Statz", " Jon Zuk", "", "Avoid the Noid:", " Dave 'Zoid' Kirsch", " William 'Phoeb' Mull", " Jack 'Morbid' Mathews", "", "", "Hexen2", "", "Map Master: ", " 'Caffeine Buzz' Raffel", "", "Code Warrior:", " Rick 'Superfly' Johnson", "", "Grunt Boys:", " 'Judah' Ben Gokey", " Bob 'Whipped' Love", " Mike '9-Pointer' Gummelt", "", "Additional Grunting:", " Josh 'Intern' Weier", "", "Whippin' Boy:", " Eric 'Baby' Biessman", "", "Crazy Levelers:", " 'Big Daddy' Brian Raffel", " Brian 'Red' Frank", " Tom 'Texture Alignment' Odell", "", "Art Lord:", " Brian 'Mr. Calm' Pelletier", "", "Pixel Monkies:", " Shane 'Duh' Gurno", " 'Slim' Jim Sumwalt", " Mark 'Dad Gummit' Morgan", " Kim 'Toy Master' Lathrop", " 'Drop Dead' Ted Halsted", " Rebecca 'Zombie' Rettenmund", " Les 'is not more' Dorscheid", "", "Salad Shooters:", " Mike 'Chaos' Werckle", " Brian 'Mutton Chops' Shubat", "", "Last Minute Addition:", " Jeff 'Bud Bundy' Dewitt", " Jeffrey 'Misspalld' Lampo", "", "Random Notes:", " Kevin 'I Already Paid' Schilder", "", "Grunts, Groans, and Moans:", " Kevin 'I Already Paid' Schilder", " Chia 'Pet' Chin Lee", "", "", "Activision", "", "Producer:", " Steve 'Ferris' Stringer", "", "Localization Producer:", " Sandi 'Boneduster' Isaacs", "", "Marketing Product Manager:", " Henk 'A-10' Hartong", "", "European Marketing", "Product Director:", " Janine Johnson", "", "Marketing Associate:", " Kevin 'Savage' Kraff", "", "Senior Quality", "Assurance Lead:", " Tim 'Outlaw' Vanlaw", "", "Quality Assurance Lead:", " John 'Armadillo' Tam", "", "Quality Assurance Team:", " Steve 'Rhinochoadicus'", " Rosenthal,", " Mike 'Dragonhawk' Spann,", " Steve 'Zendog' Elwell,", " Kelly 'Li'l Bastard' Wand,", " Kip 'Angus' Stolberg,", " Igor 'Russo' Krinitskiy,", " Ian 'Cracker' Stevens,", " Marilena 'Raveness-X' Wahmann,", " David 'Spicegirl' Baker,", " Winnie 'Mew' Lee", "", "Documentation:", " Mike Rivera, Sylvia Orzel,", " Belinda Vansickle", "", "Chronicle of Deeds written by:", " Joe Grant Bell", "", "Localization:", " Nathalie Dove, Lucy Morgan,", " Alex Wylde, Nicky Kerth", "", "Installer by:", " Steve 'Bahh' Stringer,", " Adam Goldberg, Tanya Martino,", " Eric Schmidt, Ronnie Lane", "", "Art Assistance by:", " Carey 'Damien' Chico and", " Franz Boehm", "", "BizDev Babe:", " Jamie Bafus", "", "And...", "", "Deal Guru:", " Mitch 'I'll buy that' Lasky", "", "", "Thanks to Id software:", " John Carmack", " Adrian Carmack", " Kevin Cloud", " Barrett 'Bear' Alexander", " American McGee", "", "", "Published by Id Software, Inc.", "Distributed by Activision, Inc.", "", "The Id Software Technology used", "under license in Hexen II (tm)", "(c) 1996, 1997 Id Software, Inc.", "All Rights Reserved.", "", "Hexen(r) is a registered trademark", "of Raven Software Corp.", "Hexen II (tm) and the Raven logo", "are trademarks of Raven Software", "Corp. The Id Software name and", "id logo are trademarks of", "Id Software, Inc. Activision(r)", "is a registered trademark of", "Activision, Inc. All other", "trademarks are the property of", "their respective owners.", "", "", "", "Send bug descriptions to:", " h2bugs@mail.ravensoft.com", "", "Special thanks to Bob for", "remembering 'P' is for Polymorph", "", "", "See the next movie in the long", "awaited sequel, starring", "Bobby Love in,", " Out of Traction, Back in Action!" }; #define QUIT_SIZE 16 /* was 18. stole 2 for the two uHexen2 lines. */ void M_Menu_Quit_f (void) { if (m_state == m_quit) return; wasInMenus = !!(Key_GetDest () & key_menu); Key_SetDest (key_menu); m_quit_prevstate = m_state; m_state = m_quit; m_entersound = true; // msgNumber = rand()&7; LinePos = 0; LineTimes = 0; LineText = CreditText; MaxLines = MAX_LINES; LineTxt2 = false; SoundPlayed = false; } static void M_Quit_Key (int key) { switch (key) { case K_ESCAPE: case 'n': case 'N': if (wasInMenus) { m_state = m_quit_prevstate; m_entersound = true; } else { Key_SetDest (key_game); m_state = m_none; Sbar_Changed (); } break; case 'Y': case 'y': Key_SetDest (key_console); CL_Disconnect (); Sys_Quit (); break; default: break; } } static void M_Quit_Draw (void) { int i, x, y, place, topy; qpic_t *p; if (wasInMenus) { m_state = m_quit_prevstate; m_recursiveDraw = true; M_Draw (); m_state = m_quit; } LinePos += host_frametime*1.75; if (LinePos > MaxLines + QUIT_SIZE + 2) { LinePos = 0; SoundPlayed = false; LineTimes++; if (LineTimes >= 2) { MaxLines = MAX_LINES2; LineText = Credit2Text; LineTxt2 = true; } } y = 12; M_DrawTextBox (0, 0, 38, 23); // the increment to the x offset is for properly centering the line M_Print (16 + (6 * 8), y, "Hexen2World version " STRINGIFY(ENGINE_VERSION)); M_Print (16 + (9 * 8), y + 8, "by Raven Software"); M_PrintWhite (16 + (7 * 8), y + 16, "Hammer of Thyrion " HOT_VERSION_STR); M_PrintWhite (16 +(13 * 8), y + 24, "Source Port"); y += 40; if (LinePos > 55 && !SoundPlayed && LineTxt2) { S_LocalSound ("rj/steve.wav"); SoundPlayed = true; } topy = y; place = LinePos; y -= (LinePos - (int)LinePos) * 8; for (i = 0; i < QUIT_SIZE; i++, y += 8) { if (i + place - QUIT_SIZE >= MaxLines) break; if (i + place < QUIT_SIZE) continue; if (LineText[i + place - QUIT_SIZE][0] == ' ') M_PrintWhite(24, y, LineText[i + place - QUIT_SIZE]); else M_Print(24, y, LineText[i + place - QUIT_SIZE]); } p = Draw_CachePic ("gfx/box_mm2.lmp"); x = 24; y = topy - 8; for (i = 4; i < 36; i++, x += 8) { M_DrawTransPic (x, y, p); } p = Draw_CachePic ("gfx/box_mm2.lmp"); x = 24; y = topy + (QUIT_SIZE * 8) - 8; for (i = 4; i < 36; i++, x += 8) { M_DrawTransPic (x, y, p); } y += 8; M_PrintWhite (16 + (10 * 8), y, "Press y to exit"); /* y = 12; M_DrawTextBox (0, 0, 38, 23); M_PrintWhite (16, y, " Hexen II version 0.0 "); y += 8; M_PrintWhite (16, y, " by Raven Software "); y += 16; M_PrintWhite (16, y, "Programming Art "); y += 8; M_Print (16, y, " Ben Gokey Shane Gurno "); y += 8; M_Print (16, y, " Rick Johnson Mike Werckle "); y += 8; M_Print (16, y, " Bob Love Mark Morgan "); y += 8; M_Print (16, y, " Mike Gummelt Brian Pelletier "); y += 8; M_Print (16, y, " Kim Lathrop "); y += 8; M_PrintWhite (16, y, "Design "); M_Print (16, y, " Les Dorscheid "); y += 8; M_Print (16, y, " Brian Raffel Jim Sumwalt "); y += 8; M_Print (16, y, " Eric Biessman Brian Shubat "); y += 16; M_PrintWhite (16, y, "Sound Effects Intern "); y += 8; M_Print (16, y, " Kevin Schilder Josh Weier "); y += 16; M_PrintWhite (16, y, " Press y to exit "); y += 8; */ } //============================================================================= /* MULTIPLAYER MENU */ static int m_multiplayer_cursor; #define MULTIPLAYER_ITEMS 2 static void M_Menu_MultiPlayer_f (void) { Key_SetDest (key_menu); m_state = m_multiplayer; m_entersound = true; message = NULL; } static void M_MultiPlayer_Draw (void) { int f; ScrollTitle("gfx/menu/title4.lmp"); M_DrawBigString (72,60+(0*20),"JOIN A GAME"); M_DrawBigString (72,60+(1*20),"SETUP"); f = (int)(realtime * 10)%8; M_DrawTransPic (43, 54 + m_multiplayer_cursor * 20,Draw_CachePic( va("gfx/menu/menudot%i.lmp", f+1 ) ) ); if (message) { M_PrintWhite ((320/2) - ((27*8)/2), 168, message); M_PrintWhite ((320/2) - ((27*8)/2), 176, message2); if (realtime - 5 > message_time) message = NULL; } } static void M_MultiPlayer_Key (int key) { switch (key) { case K_ESCAPE: M_Menu_Main_f (); break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); if (++m_multiplayer_cursor >= MULTIPLAYER_ITEMS) m_multiplayer_cursor = 0; break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); if (--m_multiplayer_cursor < 0) m_multiplayer_cursor = MULTIPLAYER_ITEMS - 1; break; case K_ENTER: m_entersound = true; switch (m_multiplayer_cursor) { case 0: M_Menu_Connect_f (); break; case 1: M_Menu_Setup_f (); break; } } } //============================================================================= /* CONNECT MENU */ #define MAX_HOST_NAMES 10 #define MAX_HOST_SIZE 80 static char save_names[MAX_HOST_NAMES][MAX_HOST_SIZE]; static cvar_t hostname1 = {"host1", "", CVAR_ARCHIVE}; static cvar_t hostname2 = {"host2", "", CVAR_ARCHIVE}; static cvar_t hostname3 = {"host3", "", CVAR_ARCHIVE}; static cvar_t hostname4 = {"host4", "", CVAR_ARCHIVE}; static cvar_t hostname5 = {"host5", "", CVAR_ARCHIVE}; static cvar_t hostname6 = {"host6", "", CVAR_ARCHIVE}; static cvar_t hostname7 = {"host7", "", CVAR_ARCHIVE}; static cvar_t hostname8 = {"host8", "", CVAR_ARCHIVE}; static cvar_t hostname9 = {"host9", "", CVAR_ARCHIVE}; static cvar_t hostname10 = {"host10", "", CVAR_ARCHIVE}; static int connect_cursor = 0; #define MAX_CONNECT_CMDS 11 static const int connect_cursor_table[MAX_CONNECT_CMDS] = { 72 + 0 * 8, 72 + 1 * 8, 72 + 2 * 8, 72 + 3 * 8, 72 + 4 * 8, 72 + 5 * 8, 72 + 6 * 8, 72 + 7 * 8, 72 + 8 * 8, 72 + 9 * 8, 72 +11 * 8, }; static void M_Menu_Connect_f (void) { Key_SetDest (key_menu); m_state = m_mconnect; m_entersound = true; message = NULL; q_strlcpy(save_names[0], hostname1.string, sizeof(save_names[0])); q_strlcpy(save_names[1], hostname2.string, sizeof(save_names[0])); q_strlcpy(save_names[2], hostname3.string, sizeof(save_names[0])); q_strlcpy(save_names[3], hostname4.string, sizeof(save_names[0])); q_strlcpy(save_names[4], hostname5.string, sizeof(save_names[0])); q_strlcpy(save_names[5], hostname6.string, sizeof(save_names[0])); q_strlcpy(save_names[6], hostname7.string, sizeof(save_names[0])); q_strlcpy(save_names[7], hostname8.string, sizeof(save_names[0])); q_strlcpy(save_names[8], hostname9.string, sizeof(save_names[0])); q_strlcpy(save_names[9], hostname10.string, sizeof(save_names[0])); } static void M_Connect_Draw (void) { int length; int i,y; char temp[MAX_HOST_SIZE]; ScrollTitle("gfx/menu/title4.lmp"); if (connect_cursor < MAX_HOST_NAMES) { M_DrawTextBox (16, 48, 34, 1); q_strlcpy(temp, save_names[connect_cursor], sizeof(save_names[0])); length = strlen(temp); if (length > 33) { i = length-33; } else { i = 0; } M_Print (24, 56, &temp[i]); M_DrawCharacter (24 + 8*(length-i), 56, 10+((int)(realtime*4)&1)); } y = 72; for (i = 0; i < MAX_HOST_NAMES; i++, y += 8) { q_snprintf(temp, sizeof(temp), "%d.", i+1); if (i == connect_cursor) { M_Print(24,y,temp); } else { M_PrintWhite(24,y,temp); } q_strlcpy(temp, save_names[i], sizeof(save_names[0])); temp[30] = 0; if (i == connect_cursor) { M_Print(56, y, temp); } else { M_PrintWhite(56, y, temp); } } M_Print (24, y+8, "Save Changes"); M_DrawCharacter (8, connect_cursor_table[connect_cursor], 12+((int)(realtime*4)&1)); } static void M_Connect_Key (int k) { int l; switch (k) { case K_ESCAPE: M_Menu_MultiPlayer_f (); break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); connect_cursor--; if (connect_cursor < 0) connect_cursor = MAX_CONNECT_CMDS-1; break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); connect_cursor++; if (connect_cursor >= MAX_CONNECT_CMDS) connect_cursor = 0; break; case K_ENTER: Cvar_Set ("host1",save_names[0]); Cvar_Set ("host2",save_names[1]); Cvar_Set ("host3",save_names[2]); Cvar_Set ("host4",save_names[3]); Cvar_Set ("host5",save_names[4]); Cvar_Set ("host6",save_names[5]); Cvar_Set ("host7",save_names[6]); Cvar_Set ("host8",save_names[7]); Cvar_Set ("host9",save_names[8]); Cvar_Set ("host10",save_names[9]); if (connect_cursor < MAX_HOST_NAMES) { Key_SetDest (key_game); m_state = m_none; Cbuf_AddText ( va ("connect %s\n", save_names[connect_cursor]) ); } else { m_entersound = true; M_Menu_MultiPlayer_f (); } break; case K_BACKSPACE: if (connect_cursor < MAX_HOST_NAMES) { l = strlen(save_names[connect_cursor]); if (l) { save_names[connect_cursor][l-1] = 0; } } break; default: if (k < 32 || k > 127) break; if (connect_cursor < MAX_HOST_NAMES) { l = strlen(save_names[connect_cursor]); if (l < MAX_HOST_SIZE-1) { save_names[connect_cursor][l+1] = 0; save_names[connect_cursor][l] = k; } } } } //============================================================================= /* SETUP MENU */ static int setup_cursor = 6; static const int setup_cursor_table[] = {40, 56, 72, 88, 112, 136, 164}; static char setup_myname[16]; static int setup_oldtop; static int setup_oldbottom; static int setup_top; static int setup_bottom; static qboolean is_siege; #define NUM_SETUP_CMDS 7 extern cvar_t name; extern cvar_t topcolor; extern cvar_t bottomcolor; static void M_Menu_Setup_f (void) { Key_SetDest (key_menu); m_state = m_setup; m_entersound = true; q_strlcpy(setup_myname, name.string, sizeof(setup_myname)); setup_top = setup_oldtop = topcolor.integer; setup_bottom = setup_oldbottom = bottomcolor.integer; is_siege = (q_strcasecmp(fs_gamedir_nopath, "siege") == 0); #if ENABLE_OLD_DEMO if (gameflags & GAME_OLD_DEMO) { // FIXME: doesn't handle random class (setup_class == 0) feature. if (playerclass.integer != CLASS_PALADIN && playerclass.integer != CLASS_THEIF) Cvar_SetValue ("playerclass", CLASS_PALADIN); } #endif /* OLD_DEMO */ if (playerclass.integer == CLASS_DEMON) { if (!(gameflags & GAME_PORTALS)) Cvar_SetValue ("playerclass", CLASS_PALADIN); } if (playerclass.integer == CLASS_DWARF) { if (!is_siege) Cvar_SetValue ("playerclass", CLASS_PALADIN); } setup_class = playerclass.integer; if (setup_class < 0 || setup_class > MAX_PLAYER_CLASS) setup_class = CLASS_PALADIN; which_class = setup_class; } static void M_DrawTransPicTranslate (int x, int y, qpic_t *pic, int p_class) { Draw_TransPicTranslate (x + ((vid.width - 320)>>1), y, pic, translationTable, p_class); } static void M_Setup_Draw (void) { qpic_t *p; int i; static qboolean wait; ScrollTitle("gfx/menu/title4.lmp"); M_Print (64, 56, "Your name"); M_DrawTextBox (160, 48, 16, 1); M_PrintWhite (168, 56, setup_myname); M_Print (64, 72, "Spectator: "); if (spectator.integer) { M_PrintWhite (64 + 12*8, 72, "YES"); } else { M_PrintWhite (64 + 12*8, 72, "NO"); } M_Print (64, 88, "Current Class: "); switch (setup_class) { case 0: M_PrintWhite (88, 96, "Random"); break; case 1: case 2: case 3: case 4: case 5: case 6: M_PrintWhite (88, 96, ClassNames[setup_class-1]); break; } M_Print (64, 112, "First color patch"); M_Print (64, 136, "Second color patch"); M_DrawTextBox (64, 164-8, 14, 1); M_Print (72, 164, "Accept Changes"); if (setup_class == 0) { i = (int)(realtime * 10)%8; if ((i == 0 && !wait) || which_class == 0) { if (!(gameflags & GAME_PORTALS)) {//not succubus if (!is_siege) which_class = (rand() % CLASS_THEIF) + 1; else { which_class = (rand() % CLASS_DEMON) + 1; if (which_class == CLASS_DEMON) which_class = CLASS_DWARF; } } else { if (!is_siege) which_class = (rand() % CLASS_DEMON) + 1; else which_class = (rand() % CLASS_DWARF) + 1; } wait = true; } else if (i) { wait = false; } } else { which_class = setup_class; } p = Draw_CachePic (va("gfx/menu/netp%i.lmp",which_class)); M_BuildTranslationTable(setup_top, setup_bottom); /* garymct */ M_DrawTransPicTranslate (220, 72, p, which_class); M_DrawCharacter (56, setup_cursor_table [setup_cursor], 12+((int)(realtime*4)&1)); if (setup_cursor == 1) M_DrawCharacter (168 + 8*strlen(setup_myname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1)); } static void M_Setup_Key (int k) { int l; switch (k) { case K_ESCAPE: M_Menu_MultiPlayer_f (); break; case K_ENTER: if (setup_cursor == 0 || setup_cursor == 1) return; if (setup_cursor == 2 || setup_cursor == 3 || setup_cursor == 4 || setup_cursor == 5) goto forward; if (strcmp(name.string, setup_myname) != 0) Cbuf_AddText ( va ("name \"%s\"\n", setup_myname) ); if (setup_top != setup_oldtop || setup_bottom != setup_oldbottom) Cbuf_AddText( va ("color %i %i\n", setup_top, setup_bottom) ); Cbuf_AddText ( va ("playerclass %d\n", setup_class) ); m_entersound = true; M_Menu_MultiPlayer_f (); break; case K_BACKSPACE: if (setup_cursor == 1) { if (strlen(setup_myname)) setup_myname[strlen(setup_myname)-1] = 0; } break; case K_UPARROW: S_LocalSound ("raven/menu1.wav"); setup_cursor--; if (setup_cursor < 1) setup_cursor = NUM_SETUP_CMDS-1; break; case K_DOWNARROW: S_LocalSound ("raven/menu1.wav"); setup_cursor++; if (setup_cursor >= NUM_SETUP_CMDS) setup_cursor = 1; break; case K_LEFTARROW: if (setup_cursor < 2) return; S_LocalSound ("raven/menu3.wav"); if (setup_cursor == 2) { Cvar_Set("spectator", spectator.integer ? "0" : "1"); cl.spectator = spectator.integer; } else if (setup_cursor == 3) { #if ENABLE_OLD_DEMO if (gameflags & GAME_OLD_DEMO) { // FIXME: doesn't handle random class (setup_class == 0) feature. setup_class = (setup_class == CLASS_PALADIN) ? CLASS_THEIF : CLASS_PALADIN; break; } #endif /* OLD_DEMO */ setup_class--; if (setup_class < 0) setup_class = MAX_PLAYER_CLASS; if (!is_siege && setup_class == CLASS_DWARF) setup_class--; if (!(gameflags & GAME_PORTALS) && setup_class == CLASS_DEMON) setup_class--; } else if (setup_cursor == 4) setup_top = setup_top - 1; else if (setup_cursor == 5) setup_bottom = setup_bottom - 1; break; case K_RIGHTARROW: if (setup_cursor < 2) return; forward: S_LocalSound ("raven/menu3.wav"); if (setup_cursor == 2) { Cvar_Set("spectator", spectator.integer ? "0" : "1"); cl.spectator = spectator.integer; } else if (setup_cursor == 3) { #if ENABLE_OLD_DEMO if (gameflags & GAME_OLD_DEMO) { // FIXME: doesn't handle random class (setup_class == 0) feature. setup_class = (setup_class == CLASS_PALADIN) ? CLASS_THEIF : CLASS_PALADIN; break; } #endif /* OLD_DEMO */ setup_class++; if (!(gameflags & GAME_PORTALS) && setup_class == CLASS_DEMON) setup_class++; if (!is_siege && setup_class == CLASS_DWARF) setup_class++; if (setup_class > MAX_PLAYER_CLASS) setup_class = 0; } else if (setup_cursor == 4) setup_top = setup_top + 1; else if (setup_cursor == 5) setup_bottom = setup_bottom + 1; break; default: if (k < 32 || k > 127) break; if (setup_cursor == 1) { l = strlen(setup_myname); if (l < 15) { setup_myname[l+1] = 0; setup_myname[l] = k; } } } if (setup_top > 10) setup_top = 0; else if (setup_top < 0) setup_top = 10; if (setup_bottom > 10) setup_bottom = 0; else if (setup_bottom < 0) setup_bottom = 10; } //============================================================================= /* Menu Subsystem */ void M_Init (void) { char *ptr; ptr = (char *) FS_LoadTempFile (BIGCHAR_WIDTH_FILE, NULL); if (ptr == NULL) M_BuildBigCharWidth(); else { if (fs_filesize == (long) sizeof(BigCharWidth)) memcpy (BigCharWidth, ptr, sizeof(BigCharWidth)); else { Con_Printf ("Unexpected file size (%ld) for %s\n", fs_filesize, BIGCHAR_WIDTH_FILE); M_BuildBigCharWidth(); } } Cmd_AddCommand ("togglemenu", M_ToggleMenu_f); Cmd_AddCommand ("menu_main", M_Menu_Main_f); Cmd_AddCommand ("menu_options", M_Menu_Options_f); Cmd_AddCommand ("menu_keys", M_Menu_Keys_f); Cmd_AddCommand ("menu_video", M_Menu_Video_f); Cmd_AddCommand ("help", M_Menu_Help_f); Cmd_AddCommand ("menu_quit", M_Menu_Quit_f); Cmd_AddCommand ("menu_connect" , M_Menu_Connect_f); Cvar_RegisterVariable (&hostname1); Cvar_RegisterVariable (&hostname2); Cvar_RegisterVariable (&hostname3); Cvar_RegisterVariable (&hostname4); Cvar_RegisterVariable (&hostname5); Cvar_RegisterVariable (&hostname6); Cvar_RegisterVariable (&hostname7); Cvar_RegisterVariable (&hostname8); Cvar_RegisterVariable (&hostname9); Cvar_RegisterVariable (&hostname10); memset (old_bgmtype, 0, sizeof(old_bgmtype)); } void M_Draw (void) { if (m_state == m_none || !(Key_GetDest() & key_menu)) return; if (!m_recursiveDraw) { scr_copyeverything = 1; if (scr_con_current) { Draw_ConsoleBackground (vid.height); VID_UnlockBuffer (); S_ExtraUpdate (); VID_LockBuffer (); } else { Draw_FadeScreen (); if (scr_viewsize.integer < 110) scr_fullupdate = 0; } } else { m_recursiveDraw = false; } switch (m_state) { case m_none: break; case m_main: M_Main_Draw (); break; case m_multiplayer: M_MultiPlayer_Draw (); break; case m_setup: M_Setup_Draw (); break; case m_options: M_Options_Draw (); break; #ifdef GLQUAKE case m_opengl: M_OpenGL_Draw (); break; #endif case m_keys: M_Keys_Draw (); break; case m_video: M_Video_Draw (); break; case m_help: M_Help_Draw (); break; case m_quit: M_Quit_Draw (); break; case m_mconnect: M_Connect_Draw (); break; } if (m_entersound) { S_LocalSound ("raven/menu2.wav"); m_entersound = false; } VID_UnlockBuffer (); S_ExtraUpdate (); VID_LockBuffer (); } void M_Keybind (int key) { char cmd[80]; S_LocalSound ("raven/menu1.wav"); if (key != K_ESCAPE && key != '`') { q_snprintf (cmd, sizeof(cmd), "bind \"%s\" \"%s\"\n", Key_KeynumToString (key), bindnames[keys_cursor][0]); Cbuf_InsertText (cmd); } Key_SetDest (key_menu); } void M_Keydown (int key) { switch (m_state) { case m_none: return; case m_main: M_Main_Key (key); return; case m_multiplayer: M_MultiPlayer_Key (key); return; case m_setup: M_Setup_Key (key); return; case m_options: M_Options_Key (key); return; #ifdef GLQUAKE case m_opengl: M_OpenGL_Key (key); break; #endif case m_keys: M_Keys_Key (key); return; case m_video: M_Video_Key (key); return; case m_help: M_Help_Key (key); return; case m_quit: M_Quit_Key (key); return; case m_mconnect: M_Connect_Key (key); break; } } static void BGM_RestartMusic (void) { // called after exitting the menus and changing the music type // this is pretty crude, but doen't seem to break anything S.A if (q_strcasecmp(bgmtype.string,"midi") == 0) { CDAudio_Stop(); BGM_PlayMIDIorMusic(cl.midi_name); } else if (q_strcasecmp(bgmtype.string,"cd") == 0) { BGM_Stop(); CDAudio_Play ((byte)cl.cdtrack, true); } else { CDAudio_Stop(); BGM_Stop(); } } engine/hexenworld/client/menu.h000066400000000000000000000031741444734033100171050ustar00rootroot00000000000000/* * menu.h * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_MENU_H #define __HX2_MENU_H enum m_state_e { m_none = 0, m_main, m_multiplayer, m_setup, m_options, #ifdef GLQUAKE m_opengl, #endif m_video, m_keys, m_help, m_quit, m_mconnect }; extern enum m_state_e m_state; /* menus */ void M_Init (void); void M_Keydown (int key); void M_Keybind (int key); void M_ToggleMenu_f (void); void M_Menu_Main_f (void); void M_Menu_Options_f (void); void M_Menu_Quit_f (void); void M_Print (int cx, int cy, const char *str); void M_PrintWhite (int cx, int cy, const char *str); void M_Draw (void); void M_DrawCharacter (int cx, int line, int num); void M_DrawPic (int x, int y, qpic_t *pic); void M_DrawTransPic (int x, int y, qpic_t *pic); void M_DrawTextBox (int x, int y, int width, int lines); void M_DrawCheckbox (int x, int y, int on); void ScrollTitle (const char *name); #endif /* __HX2_MENU_H */ engine/hexenworld/client/particle.h000066400000000000000000000037121444734033100177420ustar00rootroot00000000000000/* * particle.h -- particle enums and types: note that hexen2 and * hexenworld versions of these are different !!! * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __PARTICLE_H #define __PARTICLE_H #define PARTICLE_Z_CLIP 8.0 typedef enum { pt_static, pt_grav, pt_fastgrav, pt_slowgrav, pt_fire, pt_explode, pt_explode2, pt_blob, pt_blob2, pt_rain, pt_c_explode, pt_c_explode2, pt_spit, pt_fireball, pt_ice, pt_spell, pt_test, pt_quake, pt_rd, /* rider's death */ pt_vorpal, pt_setstaff, pt_magicmissile, pt_boneshard, pt_scarab, pt_darken, pt_grensmoke, pt_redfire, pt_acidball, pt_bluestep } ptype_t; typedef enum { rt_rocket_trail = 0, rt_smoke, rt_blood, rt_tracer, rt_slight_blood, rt_tracer2, rt_voor_trail, rt_fireball, rt_ice, rt_spit, rt_spell, rt_vorpal, rt_setstaff, rt_magicmissile, rt_boneshard, rt_scarab, rt_grensmoke, rt_purify, rt_darken, rt_acidball, rt_bloodshot } rt_type_t; typedef struct particle_s { /* driver-usable fields */ vec3_t org; float color; /* drivers never touch the following fields */ struct particle_s *next; vec3_t vel; float ramp; float die; ptype_t type; } particle_t; #endif /* __PARTICLE_H */ engine/hexenworld/client/quakeinc.h000066400000000000000000000043671444734033100177460ustar00rootroot00000000000000/* * quakeinc.h -- primary header for client * FIXME: kill this in the future and make each C * file include only the necessary headers. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __QUAKEINC_H #define __QUAKEINC_H /* include the system stdc headers: */ #include "q_stdinc.h" /* include the compiler specific stuff */ #include "compiler.h" /* include the OS/arch definitions, etc */ #include "arch_def.h" /* make sure to include our compile time options first */ #include "h2config.h" /* include the quake headers */ #include "q_endian.h" #include "sys.h" #include "qsnprint.h" #include "strl_fn.h" #include "link_ops.h" #include "sizebuf.h" #include "msg_io.h" #include "printsys.h" #include "common.h" #include "quakefs.h" #include "info_str.h" #include "bspfile.h" #include "zone.h" #include "mathlib.h" #include "cvar.h" #include "protocol.h" #include "net.h" #include "cmd.h" #include "crc.h" #include "host.h" #include "host_string.h" #include "pr_comp.h" /* defs shared with qcc */ #include "progdefs.h" /* generated by program cdefs */ #include "effects.h" #include "console.h" #include "wad.h" #include "vid.h" #include "screen.h" #include "draw.h" #include "render.h" #include "view.h" #include "sbar.h" #include "q_sound.h" #include "client.h" #if defined(GLQUAKE) #include "glheader.h" #include "gl_model.h" #include "glquake.h" #else /* sw client */ #include "model.h" #include "d_iface.h" #endif #include "pmove.h" #include "r_part.h" #include "input.h" #include "keys.h" #include "menu.h" #endif /* __QUAKEINC_H */ engine/hexenworld/client/r_alias.c000066400000000000000000000701341444734033100175460ustar00rootroot00000000000000/* r_alias.c -- routines for setting up to draw alias models * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" #include "d_local.h" // FIXME: shouldn't be needed (is needed for patch // right now, but that should move) #define LIGHT_MIN 5 // lowest light value we'll allow, to avoid the // need for inner-loop light clamping mtriangle_t *ptriangles; affinetridesc_t r_affinetridesc; void *acolormap; // FIXME: should go away ASM_LINKAGE_BEGIN trivertx_t *r_apverts; // TODO: these probably will go away with optimized rasterization vec3_t r_plightvec; int r_ambientlight; float r_shadelight; ASM_LINKAGE_END aliashdr_t *paliashdr; finalvert_t *pfinalverts; auxvert_t *pauxverts; newmdl_t *pmdl; //JFM: new model fmt static qmodel_t *pmodel; static float ziscale; static vec3_t alias_forward, alias_right, alias_up; static maliasskindesc_t *pskindesc; int r_amodels_drawn; ASM_LINKAGE_BEGIN int r_anumverts; float aliastransform[3][4]; float r_avertexnormals[NUMVERTEXNORMALS][3] = { #include "anorms.h" }; ASM_LINKAGE_END typedef struct { int index0; int index1; } aedge_t; static aedge_t aedges[12] = { {0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6}, {6, 7}, {7, 4}, {0, 5}, {1, 4}, {2, 7}, {3, 6} }; #if !id386 && !id68k static void R_AliasTransformAndProjectFinalVerts (finalvert_t *fv, stvert_t *pstverts); #endif static void R_AliasSetUpTransform (int trivial_accept); #if !id68k static void R_AliasTransformVector (vec3_t in, vec3_t out); static void R_AliasTransformFinalVert (finalvert_t *fv, auxvert_t *av, trivertx_t *pverts); #endif /* ================ R_AliasCheckBBox ================ */ qboolean R_AliasCheckBBox (void) { int i, flags, frame, numv; aliashdr_t *pahdr; float zi, basepts[8][3], v0, v1, frac; finalvert_t *pv0, *pv1, viewpts[16]; auxvert_t *pa0, *pa1, viewaux[16]; maliasframedesc_t *pframedesc; qboolean zclipped, zfullyclipped; unsigned int anyclip, allclip; int minz; // expand, rotate, and translate points into worldspace currententity->trivial_accept = 0; pmodel = currententity->model; pahdr = (aliashdr_t *) Mod_Extradata (pmodel); pmdl = (newmdl_t*)((byte *)pahdr + pahdr->model); R_AliasSetUpTransform (0); // construct the base bounding box for this frame frame = currententity->frame; // TODO: don't repeat this check when drawing? if ((frame >= pmdl->numframes) || (frame < 0)) { Con_DPrintf ("No such frame %d %s\n", frame, pmodel->name); frame = 0; } pframedesc = &pahdr->frames[frame]; // x worldspace coordinates basepts[0][0] = basepts[1][0] = basepts[2][0] = basepts[3][0] = (float)pframedesc->bboxmin.v[0]; basepts[4][0] = basepts[5][0] = basepts[6][0] = basepts[7][0] = (float)pframedesc->bboxmax.v[0]; // y worldspace coordinates basepts[0][1] = basepts[3][1] = basepts[5][1] = basepts[6][1] = (float)pframedesc->bboxmin.v[1]; basepts[1][1] = basepts[2][1] = basepts[4][1] = basepts[7][1] = (float)pframedesc->bboxmax.v[1]; // z worldspace coordinates basepts[0][2] = basepts[1][2] = basepts[4][2] = basepts[5][2] = (float)pframedesc->bboxmin.v[2]; basepts[2][2] = basepts[3][2] = basepts[6][2] = basepts[7][2] = (float)pframedesc->bboxmax.v[2]; zclipped = false; zfullyclipped = true; minz = 9999; for (i = 0 ; i < 8 ; i++) { R_AliasTransformVector (&basepts[i][0], &viewaux[i].fv[0]); if (viewaux[i].fv[2] < ALIAS_Z_CLIP_PLANE) { // we must clip points that are closer than the near clip plane viewpts[i].flags = ALIAS_Z_CLIP; zclipped = true; } else { if (viewaux[i].fv[2] < minz) minz = viewaux[i].fv[2]; viewpts[i].flags = 0; zfullyclipped = false; } } if (zfullyclipped) { return false; // everything was near-z-clipped } numv = 8; if (zclipped) { // organize points by edges, use edges to get new points // (possible trivial reject) for (i = 0 ; i < 12 ; i++) { // edge endpoints pv0 = &viewpts[aedges[i].index0]; pv1 = &viewpts[aedges[i].index1]; pa0 = &viewaux[aedges[i].index0]; pa1 = &viewaux[aedges[i].index1]; // if one end is clipped and the other isn't, make a new point if (pv0->flags ^ pv1->flags) { frac = (ALIAS_Z_CLIP_PLANE - pa0->fv[2]) / (pa1->fv[2] - pa0->fv[2]); viewaux[numv].fv[0] = pa0->fv[0] + (pa1->fv[0] - pa0->fv[0]) * frac; viewaux[numv].fv[1] = pa0->fv[1] + (pa1->fv[1] - pa0->fv[1]) * frac; viewaux[numv].fv[2] = ALIAS_Z_CLIP_PLANE; viewpts[numv].flags = 0; numv++; } } } // project the vertices that remain after clipping anyclip = 0; allclip = ALIAS_XY_CLIP_MASK; // TODO: probably should do this loop in ASM, especially if we use floats for (i = 0 ; i < numv ; i++) { // we don't need to bother with vertices that were z-clipped if (viewpts[i].flags & ALIAS_Z_CLIP) continue; zi = 1.0 / viewaux[i].fv[2]; // FIXME: do with chop mode in ASM, or convert to float v0 = (viewaux[i].fv[0] * xscale * zi) + xcenter; v1 = (viewaux[i].fv[1] * yscale * zi) + ycenter; flags = 0; if (v0 < r_refdef.fvrectx) flags |= ALIAS_LEFT_CLIP; if (v1 < r_refdef.fvrecty) flags |= ALIAS_TOP_CLIP; if (v0 > r_refdef.fvrectright) flags |= ALIAS_RIGHT_CLIP; if (v1 > r_refdef.fvrectbottom) flags |= ALIAS_BOTTOM_CLIP; anyclip |= flags; allclip &= flags; } if (allclip) return false; // trivial reject off one side currententity->trivial_accept = !anyclip & !zclipped; if (currententity->trivial_accept) { if (minz > (r_aliastransition + (pmdl->size * r_resfudge))) { currententity->trivial_accept |= 2; } } return true; } #if !id68k /* ================ R_AliasTransformVector ================ */ static void R_AliasTransformVector (vec3_t in, vec3_t out) { out[0] = DotProduct(in, aliastransform[0]) + aliastransform[0][3]; out[1] = DotProduct(in, aliastransform[1]) + aliastransform[1][3]; out[2] = DotProduct(in, aliastransform[2]) + aliastransform[2][3]; } #endif /* ================ R_AliasPreparePoints General clipped case ================ */ static void R_AliasPreparePoints (void) { int i; stvert_t *pstverts; finalvert_t *fv; auxvert_t *av; mtriangle_t *ptri; finalvert_t *pfv[3]; pstverts = (stvert_t *)((byte *)paliashdr + paliashdr->stverts); r_anumverts = pmdl->numverts; fv = pfinalverts; av = pauxverts; for (i = 0 ; i < r_anumverts ; i++, fv++, av++, r_apverts++) { fv->flags = 0; R_AliasTransformFinalVert (fv, av, r_apverts); if (av->fv[2] < ALIAS_Z_CLIP_PLANE) fv->flags |= ALIAS_Z_CLIP; else { R_AliasProjectFinalVert (fv, av); if (fv->v[0] < r_refdef.aliasvrect.x) fv->flags |= ALIAS_LEFT_CLIP; if (fv->v[1] < r_refdef.aliasvrect.y) fv->flags |= ALIAS_TOP_CLIP; if (fv->v[0] > r_refdef.aliasvrectright) fv->flags |= ALIAS_RIGHT_CLIP; if (fv->v[1] > r_refdef.aliasvrectbottom) fv->flags |= ALIAS_BOTTOM_CLIP; } } // // clip and draw all triangles // r_affinetridesc.numtriangles = 1; ptri = (mtriangle_t *)((byte *)paliashdr + paliashdr->triangles); for (i = 0 ; i < pmdl->numtris ; i++, ptri++) { pfv[0] = &pfinalverts[ptri->vertindex[0]]; pfv[1] = &pfinalverts[ptri->vertindex[1]]; pfv[2] = &pfinalverts[ptri->vertindex[2]]; if ( pfv[0]->flags & pfv[1]->flags & pfv[2]->flags & (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP) ) continue; // completely clipped //jfm: fill in the triangles s and t into the finalvert pfv[0]->v[2] = pstverts[ptri->stindex[0]].s; pfv[0]->v[3] = pstverts[ptri->stindex[0]].t; pfv[0]->flags |= pstverts[ptri->stindex[0]].onseam; pfv[1]->v[2] = pstverts[ptri->stindex[1]].s; pfv[1]->v[3] = pstverts[ptri->stindex[1]].t; pfv[1]->flags |= pstverts[ptri->stindex[1]].onseam; pfv[2]->v[2] = pstverts[ptri->stindex[2]].s; pfv[2]->v[3] = pstverts[ptri->stindex[2]].t; pfv[2]->flags |= pstverts[ptri->stindex[2]].onseam; if ( ! ( (pfv[0]->flags | pfv[1]->flags | pfv[2]->flags) & (ALIAS_XY_CLIP_MASK | ALIAS_Z_CLIP) ) ) { // totally unclipped r_affinetridesc.pfinalverts = pfinalverts; r_affinetridesc.ptriangles = ptri; if (currententity->model->flags & EF_SPECIAL_TRANS) D_PolysetDrawT5 (); else if (currententity->drawflags & DRF_TRANSLUCENT) D_PolysetDrawT (); else if (currententity->model->flags & EF_TRANSPARENT) D_PolysetDrawT2 (); else if (currententity->model->flags & EF_HOLEY) D_PolysetDrawT3 (); else D_PolysetDraw (); } else { // partially clipped R_AliasClipTriangle (ptri); } } } /* ================ R_AliasSetUpTransform ================ */ static void R_AliasSetUpTransform (int trivial_accept) { int i; float rotationmatrix[3][4], t2matrix[3][4]; static float tmatrix[3][4]; static float viewmatrix[3][4]; vec3_t angles; float entScale; float xyfact = 1.0, zfact = 1.0; // avoid compiler warning float forward; float yaw, pitch; // TODO: should really be stored with the entity instead of being reconstructed // TODO: should use a look-up table // TODO: could cache lazily, stored in the entity if (currententity->model->flags & EF_FACE_VIEW) { VectorSubtract(currententity->origin,r_origin,angles); VectorSubtract(r_origin,currententity->origin,angles); VectorNormalizeFast(angles); if (angles[1] == 0 && angles[0] == 0) { yaw = 0; if (angles[2] > 0) pitch = 90; else pitch = 270; } else { yaw = (int) (atan2(angles[1], angles[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; forward = Q_sqrt (angles[0]*angles[0] + angles[1]*angles[1]); pitch = (int) (atan2(angles[2], forward) * 180 / M_PI); if (pitch < 0) pitch += 360; } angles[0] = -pitch; angles[1] = yaw; // angles[2] = 0; angles[2] = currententity->angles[ROLL]; } else { angles[ROLL] = currententity->angles[ROLL]; angles[PITCH] = -currententity->angles[PITCH]; if (currententity->model->flags & EF_ROTATE) { angles[YAW] = anglemod( (currententity->origin[0] + currententity->origin[1])*0.8 + (108*cl.time) ); } else { angles[YAW] = currententity->angles[YAW]; } } // For clientside rotation stuff angles[0] += currententity->angleAdd[0]; angles[1] += currententity->angleAdd[1]; angles[2] += currententity->angleAdd[2]; AngleVectors (angles, alias_forward, alias_right, alias_up); if (currententity->scale != 0 && currententity->scale != 100) { entScale = (float)currententity->scale/100.0; switch (currententity->drawflags & SCALE_TYPE_MASKIN) { case SCALE_TYPE_UNIFORM: tmatrix[0][0] = pmdl->scale[0]*entScale; tmatrix[1][1] = pmdl->scale[1]*entScale; tmatrix[2][2] = pmdl->scale[2]*entScale; xyfact = zfact = (entScale-1.0)*127.95; break; case SCALE_TYPE_XYONLY: tmatrix[0][0] = pmdl->scale[0]*entScale; tmatrix[1][1] = pmdl->scale[1]*entScale; tmatrix[2][2] = pmdl->scale[2]; xyfact = (entScale-1.0)*127.95; zfact = 1.0; break; case SCALE_TYPE_ZONLY: tmatrix[0][0] = pmdl->scale[0]; tmatrix[1][1] = pmdl->scale[1]; tmatrix[2][2] = pmdl->scale[2]*entScale; xyfact = 1.0; zfact = (entScale-1.0)*127.95; break; } switch (currententity->drawflags & SCALE_ORIGIN_MASKIN) { case SCALE_ORIGIN_CENTER: tmatrix[0][3] = pmdl->scale_origin[0]-pmdl->scale[0]*xyfact; tmatrix[1][3] = pmdl->scale_origin[1]-pmdl->scale[1]*xyfact; tmatrix[2][3] = pmdl->scale_origin[2]-pmdl->scale[2]*zfact; break; case SCALE_ORIGIN_BOTTOM: tmatrix[0][3] = pmdl->scale_origin[0]-pmdl->scale[0]*xyfact; tmatrix[1][3] = pmdl->scale_origin[1]-pmdl->scale[1]*xyfact; tmatrix[2][3] = pmdl->scale_origin[2]; break; case SCALE_ORIGIN_TOP: tmatrix[0][3] = pmdl->scale_origin[0]-pmdl->scale[0]*xyfact; tmatrix[1][3] = pmdl->scale_origin[1]-pmdl->scale[1]*xyfact; tmatrix[2][3] = pmdl->scale_origin[2]-pmdl->scale[2]*zfact*2.0; break; } } else { tmatrix[0][0] = pmdl->scale[0]; tmatrix[1][1] = pmdl->scale[1]; tmatrix[2][2] = pmdl->scale[2]; tmatrix[0][3] = pmdl->scale_origin[0]; tmatrix[1][3] = pmdl->scale_origin[1]; tmatrix[2][3] = pmdl->scale_origin[2]; } if (currententity->model->flags & EF_ROTATE) { // Floating motion tmatrix[2][3] += q_sinrad(currententity->origin[0] + currententity->origin[1] + (cl.time*3)) * 5.5; } // TODO: can do this with simple matrix rearrangement for (i = 0 ; i < 3 ; i++) { t2matrix[i][0] = alias_forward[i]; t2matrix[i][1] = -alias_right[i]; t2matrix[i][2] = alias_up[i]; } t2matrix[0][3] = -modelorg[0]; t2matrix[1][3] = -modelorg[1]; t2matrix[2][3] = -modelorg[2]; // FIXME: can do more efficiently than full concatenation R_ConcatTransforms (t2matrix, tmatrix, rotationmatrix); // TODO: should be global, set when vright, etc., set VectorCopy (vright, viewmatrix[0]); VectorCopy (vup, viewmatrix[1]); VectorInverse (viewmatrix[1]); VectorCopy (vpn, viewmatrix[2]); // viewmatrix[0][3] = 0; // viewmatrix[1][3] = 0; // viewmatrix[2][3] = 0; R_ConcatTransforms (viewmatrix, rotationmatrix, aliastransform); // do the scaling up of x and y to screen coordinates as part of the transform // for the unclipped case (it would mess up clipping in the clipped case). // Also scale down z, so 1/z is scaled 31 bits for free, and scale down x and y // correspondingly so the projected x and y come out right // FIXME: make this work for clipped case too? if (trivial_accept) { for (i = 0 ; i < 4 ; i++) { aliastransform[0][i] *= aliasxscale * (1.0 / ((float)0x8000 * 0x10000)); aliastransform[1][i] *= aliasyscale * (1.0 / ((float)0x8000 * 0x10000)); aliastransform[2][i] *= 1.0 / ((float)0x8000 * 0x10000); } } } #if !id68k /* ================ R_AliasTransformFinalVert ================ */ static void R_AliasTransformFinalVert (finalvert_t *fv, auxvert_t *av, trivertx_t *pverts) { int temp; float lightcos, *plightnormal; av->fv[0] = DotProduct(pverts->v, aliastransform[0]) + aliastransform[0][3]; av->fv[1] = DotProduct(pverts->v, aliastransform[1]) + aliastransform[1][3]; av->fv[2] = DotProduct(pverts->v, aliastransform[2]) + aliastransform[2][3]; // lighting plightnormal = r_avertexnormals[pverts->lightnormalindex]; lightcos = DotProduct (plightnormal, r_plightvec); temp = r_ambientlight; if (lightcos < 0) { temp += (int)(r_shadelight * lightcos); // clamp; because we limited the minimum ambient and shading light, we // don't have to clamp low light, just bright if (temp < 0) temp = 0; } fv->v[4] = temp; } #endif #if !id386 && !id68k /* ================ R_AliasTransformAndProjectFinalVerts ================ */ static void R_AliasTransformAndProjectFinalVerts (finalvert_t *fv, stvert_t *pstverts) { int i, temp; float lightcos, *plightnormal, zi; trivertx_t *pverts; pverts = r_apverts; for (i = 0 ; i < r_anumverts ; i++, fv++, pverts++, pstverts++) { // transform and project zi = 1.0 / (DotProduct(pverts->v, aliastransform[2]) + aliastransform[2][3]); // x, y, and z are scaled down by 1/2**31 in the transform, so 1/z is // scaled up by 1/2**31, and the scaling cancels out for x and y in the // projection fv->v[5] = zi; fv->v[0] = ((DotProduct(pverts->v, aliastransform[0]) + aliastransform[0][3]) * zi) + aliasxcenter; fv->v[1] = ((DotProduct(pverts->v, aliastransform[1]) + aliastransform[1][3]) * zi) + aliasycenter; fv->v[2] = pstverts->s; fv->v[3] = pstverts->t; fv->flags = pstverts->onseam; // lighting plightnormal = r_avertexnormals[pverts->lightnormalindex]; lightcos = DotProduct (plightnormal, r_plightvec); temp = r_ambientlight; if (lightcos < 0) { temp += (int)(r_shadelight * lightcos); // clamp; because we limited the minimum ambient and shading // light, we don't have to clamp low light, just bright if (temp < 0) temp = 0; } fv->v[4] = temp; } } #endif /* ================ R_AliasProjectFinalVert ================ */ void R_AliasProjectFinalVert (finalvert_t *fv, auxvert_t *av) { float zi; // project points zi = 1.0 / av->fv[2]; fv->v[5] = zi * ziscale; fv->v[0] = (av->fv[0] * aliasxscale * zi) + aliasxcenter; fv->v[1] = (av->fv[1] * aliasyscale * zi) + aliasycenter; } /* ================ R_AliasPrepareUnclippedPoints ================ */ void R_AliasPrepareUnclippedPoints (void) { stvert_t *pstverts; finalvert_t *fv; mtriangle_t *ptri; int i; pstverts = (stvert_t *)((byte *)paliashdr + paliashdr->stverts); r_anumverts = pmdl->numverts; // FIXME: just use pfinalverts directly? fv = pfinalverts; R_AliasTransformAndProjectFinalVerts (fv, pstverts); r_affinetridesc.pfinalverts = pfinalverts; r_affinetridesc.ptriangles = (mtriangle_t *) ((byte *)paliashdr + paliashdr->triangles); r_affinetridesc.numtriangles = 1;//pmdl->numtris; ptri = (mtriangle_t *)((byte *)paliashdr + paliashdr->triangles); for (i = 0 ; i < pmdl->numtris ; i++, ptri++) { finalvert_t *pfv[3]; pfv[0] = &pfinalverts[ptri->vertindex[0]]; pfv[1] = &pfinalverts[ptri->vertindex[1]]; pfv[2] = &pfinalverts[ptri->vertindex[2]]; //jfm: fill in the triangles s and t into the finalvert pfv[0]->v[2] = pstverts[ptri->stindex[0]].s; pfv[0]->v[3] = pstverts[ptri->stindex[0]].t; pfv[0]->flags = pstverts[ptri->stindex[0]].onseam; pfv[1]->v[2] = pstverts[ptri->stindex[1]].s; pfv[1]->v[3] = pstverts[ptri->stindex[1]].t; pfv[1]->flags = pstverts[ptri->stindex[1]].onseam; pfv[2]->v[2] = pstverts[ptri->stindex[2]].s; pfv[2]->v[3] = pstverts[ptri->stindex[2]].t; pfv[2]->flags = pstverts[ptri->stindex[2]].onseam; r_affinetridesc.ptriangles = ptri; if (r_affinetridesc.drawtype) { if (currententity->model->flags & EF_SPECIAL_TRANS) { D_PolysetDrawFinalVertsT5 (pfv[0],pfv[1],pfv[2]); D_PolysetDrawT5 (); } else if (currententity->drawflags & DRF_TRANSLUCENT) { D_PolysetDrawFinalVertsT (pfv[0],pfv[1],pfv[2]); D_PolysetDrawT (); } else if (currententity->model->flags & EF_TRANSPARENT) { D_PolysetDrawFinalVertsT2 (pfv[0],pfv[1],pfv[2]); D_PolysetDrawT2 (); } else if (currententity->model->flags & EF_HOLEY) { D_PolysetDrawFinalVertsT3 (pfv[0],pfv[1],pfv[2]); D_PolysetDrawT3 (); } else { D_PolysetDrawFinalVerts (pfv[0],pfv[1],pfv[2]); D_PolysetDraw (); } } else { if (currententity->model->flags & EF_SPECIAL_TRANS) D_PolysetDrawT5 (); else if (currententity->drawflags & DRF_TRANSLUCENT) D_PolysetDrawT (); else if (currententity->model->flags & EF_TRANSPARENT) D_PolysetDrawT2 (); else if (currententity->model->flags & EF_HOLEY) D_PolysetDrawT3 (); else D_PolysetDraw (); } } } /* =============== R_AliasSetupSkin =============== */ static void R_AliasSetupSkin (void) { int i, numskins, skinnum; int a_skinwidth; maliasskingroup_t *paliasskingroup; float *pskinintervals, fullskininterval; float skintargettime, skintime; qpic_t *stonepic; char temp[40]; skinnum = currententity->skinnum; if (skinnum >= 100) skinnum = 0; if ((skinnum >= pmdl->numskins) || (skinnum < 0)) { Con_DPrintf ("%s: no such skin # %d\n", __thisfunc__, skinnum); skinnum = 0; } if ( (currententity->model->flags & EF_MIP_MAP) && (currententity->model->flags & EF_MIP_MAP_FAR) && skinnum+1 < pmdl->numskins ) skinnum++; pskindesc = ((maliasskindesc_t *)((byte *)paliashdr + paliashdr->skindesc)) + skinnum; a_skinwidth = pmdl->skinwidth; if (pskindesc->type == ALIAS_SKIN_GROUP) { paliasskingroup = (maliasskingroup_t *)((byte *)paliashdr + pskindesc->skin); pskinintervals = (float *)((byte *)paliashdr + paliasskingroup->intervals); numskins = paliasskingroup->numskins; fullskininterval = pskinintervals[numskins-1]; skintime = cl.time + currententity->syncbase; // when loading in Mod_LoadAliasSkinGroup, we guaranteed all interval // values are positive, so we don't have to worry about division by 0 skintargettime = skintime - ((int)(skintime / fullskininterval)) * fullskininterval; for (i = 0 ; i < (numskins-1) ; i++) { if (pskinintervals[i] > skintargettime) break; } pskindesc = &paliasskingroup->skindescs[i]; } if (currententity->skinnum >= 100) { sprintf(temp,"gfx/skin%d.lmp",currententity->skinnum); stonepic = Draw_CachePic(temp); r_affinetridesc.pskindesc = pskindesc; r_affinetridesc.pskin = stonepic->data; r_affinetridesc.skinwidth = stonepic->width; r_affinetridesc.seamfixupX16 = (stonepic->width >> 1) << 16; r_affinetridesc.skinheight = stonepic->height; } else { r_affinetridesc.pskindesc = pskindesc; r_affinetridesc.pskin = (void *)((byte *)paliashdr + pskindesc->skin); r_affinetridesc.skinwidth = a_skinwidth; r_affinetridesc.seamfixupX16 = (a_skinwidth >> 1) << 16; r_affinetridesc.skinheight = pmdl->skinheight; } /* rjr how to handle this??? if (currententity->scoreboard) { byte *base; if (!currententity->scoreboard->skin) Skin_Find (currententity->scoreboard); base = Skin_Cache (currententity->scoreboard->skin); if (base) { r_affinetridesc.pskin = base; r_affinetridesc.skinwidth = 320; r_affinetridesc.skinheight = 200; } } */ } /* ================ R_AliasSetupLighting ================ */ static void R_AliasSetupLighting (alight_t *plighting) { // guarantee that no vertex will ever be lit below LIGHT_MIN, so we don't have // to clamp off the bottom r_ambientlight = plighting->ambientlight; if (r_ambientlight < LIGHT_MIN) r_ambientlight = LIGHT_MIN; r_ambientlight = (255 - r_ambientlight) << VID_CBITS; if (r_ambientlight < LIGHT_MIN) r_ambientlight = LIGHT_MIN; r_shadelight = plighting->shadelight; if (r_shadelight < 0) r_shadelight = 0; r_shadelight *= VID_GRADES; // rotate the lighting vector into the model's frame of reference r_plightvec[0] = DotProduct (plighting->plightvec, alias_forward); r_plightvec[1] = -DotProduct (plighting->plightvec, alias_right); r_plightvec[2] = DotProduct (plighting->plightvec, alias_up); } /* ================= R_AliasSetupFrame set r_apverts ================= */ static void R_AliasSetupFrame (void) { int i, frame, numframes; maliasgroup_t *paliasgroup; float *pintervals, fullinterval, targettime, time; frame = currententity->frame; if ((frame >= pmdl->numframes) || (frame < 0)) { Con_DPrintf ("%s: no such frame %d %s\n", __thisfunc__, frame, currententity->model->name); frame = 0; } if (paliashdr->frames[frame].type == ALIAS_SINGLE) { r_apverts = (trivertx_t *) ((byte *)paliashdr + paliashdr->frames[frame].frame); return; } paliasgroup = (maliasgroup_t *) ((byte *)paliashdr + paliashdr->frames[frame].frame); pintervals = (float *)((byte *)paliashdr + paliasgroup->intervals); numframes = paliasgroup->numframes; fullinterval = pintervals[numframes-1]; time = cl.time + currententity->syncbase; // // when loading in Mod_LoadAliasGroup, we guaranteed all interval values // are positive, so we don't have to worry about division by 0 // targettime = time - ((int)(time / fullinterval)) * fullinterval; for (i = 0 ; i < (numframes-1) ; i++) { if (pintervals[i] > targettime) break; } r_apverts = (trivertx_t *) ((byte *)paliashdr + paliasgroup->frames[i].frame); } /* ================ R_AliasDrawModel ================ */ void R_AliasDrawModel (alight_t *plighting) { int mls; int i, j; byte *dest, *source, *sourceA; auxvert_t auxverts[MAXALIASVERTS]; finalvert_t finalverts[MAXALIASVERTS + ((CACHE_SIZE - 1) / sizeof(finalvert_t)) + 1]; char client_team[16], this_team[16]; // qboolean OnTeam = false; byte ColorShade, *ColorMap, *OldColorMap; r_amodels_drawn++; // cache align pfinalverts = (finalvert_t *) (((intptr_t)&finalverts[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); pauxverts = &auxverts[0]; paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model); pmdl = (newmdl_t *)((byte *)paliashdr + paliashdr->model); R_AliasSetupSkin (); R_AliasSetUpTransform (currententity->trivial_accept); mls = currententity->drawflags & MLS_MASKIN; if (currententity->model->flags & EF_ROTATE) { plighting->ambientlight = plighting->shadelight = 60 + 34 + q_sinrad(currententity->origin[0] + currententity->origin[1] + (cl.time*3.8))*34; R_AliasSetupLighting (plighting); r_plightvec[0] = r_plightvec[1] = r_plightvec[2] = 0; } else if (mls == MLS_ABSLIGHT) { plighting->ambientlight = plighting->shadelight = currententity->abslight; R_AliasSetupLighting (plighting); r_plightvec[0] = r_plightvec[1] = r_plightvec[2] = 0; } else if (mls != MLS_NONE) { // Use a model light style (25-30) plighting->ambientlight = plighting->shadelight = d_lightstylevalue[24+mls]/2; R_AliasSetupLighting (plighting); } else R_AliasSetupLighting (plighting); R_AliasSetupFrame (); if (!currententity->colormap) Sys_Error ("%s: !currententity->colormap", __thisfunc__); r_affinetridesc.drawtype = (currententity->trivial_accept == 3) && r_recursiveaffinetriangles; ColorShade = currententity->colorshade; ColorMap = currententity->sourcecolormap; OldColorMap = currententity->colormap; i = currententity->scoreboard - cl.players; if (i >= 0 && i < MAX_CLIENTS) { if (plighting->ambientlight + plighting->shadelight > 75 || (cl_siege && cl.players[cl.playernum].siege_team == cl.players[i].siege_team)) cl.players[i].shownames_off = false; else cl.players[i].shownames_off = true; if (cl_siege && cl.players[cl.playernum].playerclass == CLASS_DWARF && currententity->skinnum == 101) { //invisible player to dwarf // OnTeam = true; ColorShade = 141; cl.players[i].shownames_off = false; } else if (cl_siege && cl.players[cl.playernum].playerclass == CLASS_DWARF && plighting->ambientlight + plighting->shadelight < 151) { // OnTeam = true; ColorShade = 138 + (int)((plighting->ambientlight + plighting->shadelight) / 30); if (currententity->sourcecolormap) ColorMap = currententity->sourcecolormap; else ColorMap = vid.colormap; currententity->colormap = globalcolormap; cl.players[i].shownames_off = false; } else { q_strlcpy (client_team, Info_ValueForKey(cl.players[cl.playernum].userinfo, "team"), sizeof(client_team)); if (client_team[0]) { q_strlcpy (this_team, Info_ValueForKey(cl.players[i].userinfo, "team"), sizeof(this_team)); if (q_strcasecmp(client_team, this_team) == 0) { // OnTeam = true; ColorShade = r_teamcolor.value; if (currententity->sourcecolormap) ColorMap = currententity->sourcecolormap; else ColorMap = vid.colormap; currententity->colormap = globalcolormap; } } } } acolormap = currententity->colormap; if (r_affinetridesc.drawtype) { D_PolysetUpdateTables (); // FIXME: precalc... } else { #if id386 if (currententity->model->flags & EF_SPECIAL_TRANS) D_Aff8PatchT5 (currententity->colormap); else if (currententity->drawflags & DRF_TRANSLUCENT) D_Aff8PatchT (currententity->colormap); else if (currententity->model->flags & EF_TRANSPARENT) D_Aff8PatchT2 (currententity->colormap); else if (currententity->model->flags & EF_HOLEY) D_Aff8PatchT3 (currententity->colormap); else D_Aff8Patch (currententity->colormap); #endif } if ( ColorShade && (ColorShade != lastglobalcolor || ColorMap != lastsourcecolormap)) { lastglobalcolor = ColorShade; lastsourcecolormap = ColorMap; dest = globalcolormap; source = ColorMap; sourceA = transTable + (((int)lastglobalcolor)*256); for (i = 0 ; i < VID_GRADES ; i++) { for (j = 0 ; j < 256 ; j++, dest++, source++) { *dest = sourceA[*source]; } } } if (currententity != &cl.viewent) ziscale = (float)0x8000 * (float)0x10000; else ziscale = (float)0x8000 * (float)0x10000 * 3.0; if (currententity->trivial_accept) R_AliasPrepareUnclippedPoints (); else R_AliasPreparePoints (); currententity->colormap = OldColorMap; } engine/hexenworld/client/r_main.c000066400000000000000000001050551444734033100174020ustar00rootroot00000000000000/* r_main.c * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" #include "d_local.h" //#define PASSAGES static vec3_t viewlightvec; static alight_t r_viewlighting = {128, 192, viewlightvec}; ASM_LINKAGE_BEGIN #if id386 void *colormap; #endif ASM_LINKAGE_END float r_time1; float r_lasttime1 = 0; int r_numallocatededges; #if 0 qboolean r_drawpolys; qboolean r_drawculledpolys; qboolean r_worldpolysbacktofront; #endif qboolean r_recursiveaffinetriangles = true; int r_pixbytes = 1; float r_aliasuvscale = 1.0; int r_outofsurfaces; int r_outofedges; byte *mainTransTable; byte *transTable; /* the particle table */ byte *playerTranslation; const int color_offsets[MAX_PLAYER_CLASS] = { 2 * 14 * 256, 0, 1 * 14 * 256, 2 * 14 * 256, 2 * 14 * 256 #if defined(H2W) , 2 * 14 * 256 #endif }; qboolean r_dowarp, r_dowarpold, r_viewchanged; int numbtofpolys; btofpoly_t *pbtofpolys; mvertex_t *r_pcurrentvertbase; int c_surf; int r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs; qboolean r_surfsonstack; int r_clipflags; byte *r_warpbuffer; static byte *r_stack_start; qboolean r_fov_greater_than_90; entity_t r_worldentity; // // view origin // vec3_t vup, base_vup; vec3_t vpn, base_vpn; vec3_t vright, base_vright; vec3_t r_origin; // // screen size info // refdef_t r_refdef; float xcenter, ycenter; float xscale, yscale; float xscaleinv, yscaleinv; float xscaleshrink, yscaleshrink; float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; int screenwidth; float pixelAspect; float screenAspect; float verticalFieldOfView; float xOrigin, yOrigin; mplane_t screenedge[4]; // // refresh flags // int r_framecount = 1; // so frame counts initialized to 0 don't match int r_visframecount; int d_spanpixcount; int r_polycount; int r_drawnpolycount; int r_wholepolycount; int *pfrustum_indexes[4]; int r_frustum_indexes[4*6]; mleaf_t *r_viewleaf, *r_oldviewleaf; texture_t *r_notexture_mip; float r_aliastransition, r_resfudge; int d_lightstylevalue[256]; // 8.8 fraction of base light value float dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2; float se_time1, se_time2, de_time1, de_time2, dv_time1, dv_time2; static edge_t *SaveEdges; static surf_t *SaveSurfaces; static int SaveEdgesCount, SaveSurfacesCount, SaveEdgesSize, SaveSurfacesSize; static qboolean AllowTranslucency; cvar_t r_draworder = {"r_draworder", "0", CVAR_NONE}; cvar_t r_speeds = {"r_speeds", "0", CVAR_NONE}; cvar_t r_timegraph = {"r_timegraph", "0", CVAR_NONE}; cvar_t r_netgraph = {"r_netgraph", "0", CVAR_NONE}; cvar_t r_zgraph = {"r_zgraph", "0", CVAR_NONE}; cvar_t r_graphheight = {"r_graphheight", "15", CVAR_NONE}; cvar_t r_clearcolor = {"r_clearcolor", "2", CVAR_NONE}; cvar_t r_waterwarp = {"r_waterwarp", "1", CVAR_ARCHIVE}; cvar_t r_fullbright = {"r_fullbright", "0", CVAR_NONE}; cvar_t r_drawentities = {"r_drawentities", "1", CVAR_NONE}; cvar_t r_drawviewmodel = {"r_drawviewmodel", "1", CVAR_NONE}; cvar_t r_aliasstats = {"r_polymodelstats", "0", CVAR_NONE}; cvar_t r_dspeeds = {"r_dspeeds", "0", CVAR_NONE}; cvar_t r_drawflat = {"r_drawflat", "0", CVAR_NONE}; cvar_t r_ambient = {"r_ambient", "0", CVAR_NONE}; cvar_t r_reportsurfout = {"r_reportsurfout", "0", CVAR_NONE}; cvar_t r_maxsurfs = {"r_maxsurfs", "0", CVAR_NONE}; cvar_t r_numsurfs = {"r_numsurfs", "0", CVAR_NONE}; cvar_t r_reportedgeout = {"r_reportedgeout", "0", CVAR_NONE}; cvar_t r_maxedges = {"r_maxedges", "0", CVAR_NONE}; cvar_t r_numedges = {"r_numedges", "0", CVAR_NONE}; cvar_t r_aliastransbase = {"r_aliastransbase", "200", CVAR_NONE}; cvar_t r_aliastransadj = {"r_aliastransadj", "100", CVAR_NONE}; cvar_t r_aliasmip = {"r_aliasmip", "80", CVAR_NONE}; cvar_t r_wholeframe = {"r_wholeframe", "1", CVAR_ARCHIVE}; cvar_t r_transwater = {"r_transwater", "1", CVAR_ARCHIVE}; cvar_t r_teamcolor = {"r_teamcolor", "187", CVAR_ARCHIVE}; cvar_t r_texture_external = {"r_texture_external", "0", CVAR_ARCHIVE}; cvar_t r_dynamic = {"r_dynamic", "1", CVAR_NONE}; extern void R_NetGraph (void); extern void R_ZGraph (void); //void CreatePassages (void); //void SetVisibilityByPassages (void); //============================================================================= /* ================== R_InitTextures ================== */ void R_InitTextures (void) { int x, y, m; byte *dest; // create a simple checkerboard texture for the default r_notexture_mip = (texture_t *) Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, "notexture"); r_notexture_mip->width = r_notexture_mip->height = 16; r_notexture_mip->offsets[0] = sizeof(texture_t); r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16; r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8; r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4; for (m = 0; m < 4; m++) { dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[m]; for (y = 0; y < (16>>m); y++) { for (x = 0; x < (16>>m); x++) { if ( (y < (8>>m)) ^ (x < (8>>m)) ) *dest++ = 0; else *dest++ = 0xff; } } } } /* ================ R_InitTurb ================ */ static void R_InitTurb (void) { int i; for (i = 0 ; i < (SIN_BUFFER_SIZE) ; i++) { sintable[i] = AMP + sin(i * 3.14159 * 2 / CYCLE) * AMP; intsintable[i] = AMP2 + sin(i * 3.14159 * 2 / CYCLE) * AMP2; // AMP2, not 20 } } /* =============== R_Init =============== */ void R_Init (void) { int dummy; // get stack position so we can guess if we are going to overflow r_stack_start = (byte *)&dummy; R_InitTurb (); Cmd_AddCommand ("timerefresh", R_TimeRefresh_f); Cmd_AddCommand ("pointfile", R_ReadPointFile_f); Cvar_RegisterVariable (&r_draworder); Cvar_RegisterVariable (&r_speeds); Cvar_RegisterVariable (&r_timegraph); Cvar_RegisterVariable (&r_netgraph); Cvar_RegisterVariable (&r_zgraph); Cvar_RegisterVariable (&r_graphheight); Cvar_RegisterVariable (&r_drawflat); Cvar_RegisterVariable (&r_ambient); Cvar_RegisterVariable (&r_clearcolor); Cvar_RegisterVariable (&r_waterwarp); Cvar_RegisterVariable (&r_fullbright); Cvar_RegisterVariable (&r_drawentities); Cvar_RegisterVariable (&r_drawviewmodel); Cvar_RegisterVariable (&r_aliasstats); Cvar_RegisterVariable (&r_dspeeds); Cvar_RegisterVariable (&r_reportsurfout); Cvar_RegisterVariable (&r_maxsurfs); Cvar_RegisterVariable (&r_numsurfs); Cvar_RegisterVariable (&r_reportedgeout); Cvar_RegisterVariable (&r_maxedges); Cvar_RegisterVariable (&r_numedges); Cvar_RegisterVariable (&r_aliastransbase); Cvar_RegisterVariable (&r_aliastransadj); Cvar_RegisterVariable (&r_aliasmip); Cvar_RegisterVariable (&r_wholeframe); Cvar_RegisterVariable (&r_transwater); Cvar_RegisterVariable (&r_teamcolor); Cvar_RegisterVariable (&r_texture_external); Cvar_RegisterVariable (&r_dynamic); Cvar_SetValueQuick (&r_maxedges, (float)NUMSTACKEDGES); Cvar_SetValueQuick (&r_maxsurfs, (float)NUMSTACKSURFACES); view_clipplanes[0].leftedge = true; view_clipplanes[1].rightedge = true; view_clipplanes[1].leftedge = view_clipplanes[2].leftedge = view_clipplanes[3].leftedge = false; view_clipplanes[0].rightedge = view_clipplanes[2].rightedge = view_clipplanes[3].rightedge = false; r_refdef.xOrigin = XCENTERING; r_refdef.yOrigin = YCENTERING; transTable = (byte *)FS_LoadHunkFile ("gfx/tinttab.lmp", NULL); if (!transTable) Sys_Error ("Couldn't load gfx/tinttab.lmp"); if (fs_filesize != 65536) Sys_Error ("Unexpected file size (%ld) for %s\n", fs_filesize, "gfx/tinttab.lmp"); mainTransTable = (byte *)FS_LoadHunkFile ("gfx/tinttab2.lmp", NULL); if (!mainTransTable) Sys_Error ("Couldn't load gfx/tinttab2.lmp"); if (fs_filesize != 65536) Sys_Error ("Unexpected file size (%ld) for %s\n", fs_filesize, "gfx/tinttab2.lmp"); playerTranslation = (byte *)FS_LoadHunkFile ("gfx/player.lmp", NULL); if (!playerTranslation) Sys_Error ("Couldn't load gfx/player.lmp"); R_InitParticles (); D_Init (); #if id386 Sys_MakeCodeWriteable ((long)R_EdgeCodeStart, (long)R_EdgeCodeEnd - (long)R_EdgeCodeStart); Sys_MakeCodeWriteable ((long)R_EdgeCodeStartT, (long)R_EdgeCodeEndT - (long)R_EdgeCodeStartT); D_Patch(); R_TranPatch1(); R_TranPatch2(); R_TranPatch6(); Sys_MakeCodeWriteable ((int)D_Draw16StartT, (int)D_Draw16EndT - (int)D_Draw16StartT); R_TranPatch3(); Sys_MakeCodeWriteable ((int)D_SpriteSpansStartT, (int)D_SpriteSpansEndT - (int)D_SpriteSpansStartT); R_TranPatch4(); Sys_MakeCodeWriteable ((int)D_SpriteSpansStartT2, (int)D_SpriteSpansEndT2 - (int)D_SpriteSpansStartT2); R_TranPatch5(); Sys_MakeCodeWriteable ((int)D_DrawTurbulent8TSpan, (int)D_DrawTurbulent8TSpanEnd - (int)D_DrawTurbulent8TSpan); R_TranPatch7(); #endif } /* =============== R_NewMap =============== */ void R_NewMap (void) { int i; memset (&r_worldentity, 0, sizeof(r_worldentity)); r_worldentity.model = cl.worldmodel; // clear out efrags in case the level hasn't been reloaded // FIXME: is this one short? for (i = 0; i < cl.worldmodel->numleafs; i++) cl.worldmodel->leafs[i].efrags = NULL; r_viewleaf = NULL; R_ClearParticles (); r_cnumsurfs = r_maxsurfs.integer; if (r_cnumsurfs <= MINSURFACES) r_cnumsurfs = MINSURFACES; if (r_cnumsurfs > NUMSTACKSURFACES) { surfaces = (surf_t *) Hunk_AllocName (r_cnumsurfs * sizeof(surf_t), "surfaces"); surface_p = surfaces; surf_max = &surfaces[r_cnumsurfs]; r_surfsonstack = false; // surface 0 doesn't really exist; it's just a dummy because index 0 // is used to indicate no edge attached to surface surfaces--; #if id386 R_SurfacePatch (); #endif } else { r_surfsonstack = true; } SaveSurfacesSize = r_cnumsurfs * sizeof(surf_t); SaveSurfaces = (surf_t *) Hunk_AllocName (SaveSurfacesSize, "surfback"); r_maxedgesseen = 0; r_maxsurfsseen = 0; r_numallocatededges = r_maxedges.integer; if (r_numallocatededges < MINEDGES) r_numallocatededges = MINEDGES; if (r_numallocatededges <= NUMSTACKEDGES) { auxedges = NULL; } else { auxedges = (edge_t *) Hunk_AllocName (r_numallocatededges * sizeof(edge_t), "edges"); } SaveEdgesSize = r_numallocatededges * sizeof(edge_t); SaveEdges = (edge_t *) Hunk_AllocName (SaveEdgesSize, "edgeback"); r_dowarpold = false; r_viewchanged = false; #ifdef PASSAGES CreatePassages (); #endif } /* =============== R_SetVrect =============== */ void R_SetVrect (vrect_t *pvrectin, vrect_t *pvrect, int lineadj) { float size; int h; qboolean full = false; if (scr_viewsize.integer >= 110) { size = 100.0; full = true; } else { size = scr_viewsize.integer; } if (cl.intermission) { size = 100.0; // intermission is always full screen full = true; lineadj = 0; } size /= 100.0; if (full) h = pvrectin->height; else h = pvrectin->height - lineadj; if (full) pvrect->width = pvrectin->width; else pvrect->width = pvrectin->width * size; if (pvrect->width < 96) { size = 96.0 / pvrectin->width; pvrect->width = 96; // min for icons } pvrect->width &= ~7; pvrect->height = h * size; if (!full) { if (pvrect->height > pvrectin->height - lineadj) pvrect->height = pvrectin->height - lineadj; } else { if (pvrect->height > pvrectin->height) pvrect->height = pvrectin->height; } pvrect->height &= ~1; pvrect->x = (pvrectin->width - pvrect->width)/2; if (full) pvrect->y = 0; else pvrect->y = (h - pvrect->height)/2; } /* =============== R_ViewChanged Called every time the vid structure or r_refdef changes. Guaranteed to be called before the first refresh =============== */ #if defined(PLATFORM_DOS) || defined(SVGAQUAKE) #define NOT_VGA_MODE (vid.aspect <= 1.10f) /* no Hor+ for weirdass VGA modes */ #elif defined(PLATFORM_AMIGAOS3) #define NOT_VGA_MODE (!vid.noadapt) /* no Hor+ for Amiga native chipset modes */ #else #define NOT_VGA_MODE true #endif #define FOV_ADAPTING (scr_fov_adapt.integer && NOT_VGA_MODE) void R_ViewChanged (float aspect) { int i; float res_scale; r_viewchanged = true; r_refdef.horizontalFieldOfView = 2.0 * tan (r_refdef.fov_x/360*M_PI); r_refdef.fvrectx = (float)r_refdef.vrect.x; r_refdef.fvrectx_adj = (float)r_refdef.vrect.x - 0.5; r_refdef.vrect_x_adj_shift20 = (r_refdef.vrect.x<<20) + (1<<19) - 1; r_refdef.fvrecty = (float)r_refdef.vrect.y; r_refdef.fvrecty_adj = (float)r_refdef.vrect.y - 0.5; r_refdef.vrectright = r_refdef.vrect.x + r_refdef.vrect.width; r_refdef.vrectright_adj_shift20 = (r_refdef.vrectright<<20) + (1<<19) - 1; r_refdef.fvrectright = (float)r_refdef.vrectright; r_refdef.fvrectright_adj = (float)r_refdef.vrectright - 0.5; r_refdef.vrectrightedge = (float)r_refdef.vrectright - 0.99; r_refdef.vrectbottom = r_refdef.vrect.y + r_refdef.vrect.height; r_refdef.fvrectbottom = (float)r_refdef.vrectbottom; r_refdef.fvrectbottom_adj = (float)r_refdef.vrectbottom - 0.5; r_refdef.aliasvrect.x = (int)(r_refdef.vrect.x * r_aliasuvscale); r_refdef.aliasvrect.y = (int)(r_refdef.vrect.y * r_aliasuvscale); r_refdef.aliasvrect.width = (int)(r_refdef.vrect.width * r_aliasuvscale); r_refdef.aliasvrect.height = (int)(r_refdef.vrect.height * r_aliasuvscale); r_refdef.aliasvrectright = r_refdef.aliasvrect.x + r_refdef.aliasvrect.width; r_refdef.aliasvrectbottom = r_refdef.aliasvrect.y + r_refdef.aliasvrect.height; xOrigin = r_refdef.xOrigin; yOrigin = r_refdef.yOrigin; // 320*200 1.0 pixelAspect = 1.6 screenAspect // 320*240 1.0 pixelAspect = 1.3333 screenAspect // proper 320*200 pixelAspect = 0.8333333 if (FOV_ADAPTING) { pixelAspect = 1; verticalFieldOfView = 2.0 * tan (r_refdef.fov_y/360*M_PI); } else { pixelAspect = aspect; screenAspect = r_refdef.vrect.width*pixelAspect / r_refdef.vrect.height; verticalFieldOfView = r_refdef.horizontalFieldOfView / screenAspect; } // values for perspective projection // if math were exact, the values would range from 0.5 to to range+0.5 // hopefully they wll be in the 0.000001 to range+.999999 and truncate // the polygon rasterization will never render in the first row or column // but will definately render in the [range] row and column, so adjust the // buffer origin to get an exact edge to edge fill xcenter = ((float)r_refdef.vrect.width * XCENTERING) + r_refdef.vrect.x - 0.5; aliasxcenter = xcenter * r_aliasuvscale; ycenter = ((float)r_refdef.vrect.height * YCENTERING) + r_refdef.vrect.y - 0.5; aliasycenter = ycenter * r_aliasuvscale; xscale = r_refdef.vrect.width / r_refdef.horizontalFieldOfView; aliasxscale = xscale * r_aliasuvscale; xscaleinv = 1.0 / xscale; yscale = (FOV_ADAPTING) ? r_refdef.vrect.height / verticalFieldOfView : xscale * pixelAspect; aliasyscale = yscale * r_aliasuvscale; yscaleinv = 1.0 / yscale; xscaleshrink = (r_refdef.vrect.width-6)/r_refdef.horizontalFieldOfView; yscaleshrink = xscaleshrink*pixelAspect; // left side clip screenedge[0].normal[0] = -1.0 / (xOrigin*r_refdef.horizontalFieldOfView); screenedge[0].normal[1] = 0; screenedge[0].normal[2] = 1; screenedge[0].type = PLANE_ANYZ; // right side clip screenedge[1].normal[0] = 1.0 / ((1.0-xOrigin)*r_refdef.horizontalFieldOfView); screenedge[1].normal[1] = 0; screenedge[1].normal[2] = 1; screenedge[1].type = PLANE_ANYZ; // top side clip screenedge[2].normal[0] = 0; screenedge[2].normal[1] = -1.0 / (yOrigin*verticalFieldOfView); screenedge[2].normal[2] = 1; screenedge[2].type = PLANE_ANYZ; // bottom side clip screenedge[3].normal[0] = 0; screenedge[3].normal[1] = 1.0 / ((1.0-yOrigin)*verticalFieldOfView); screenedge[3].normal[2] = 1; screenedge[3].type = PLANE_ANYZ; for (i = 0; i < 4; i++) VectorNormalize (screenedge[i].normal); res_scale = sqrt((double)(r_refdef.vrect.width * r_refdef.vrect.height) / (320.0 * 152.0)) * (2.0 / r_refdef.horizontalFieldOfView); r_aliastransition = r_aliastransbase.value * res_scale; r_resfudge = r_aliastransadj.value * res_scale; if (scr_fov.integer <= 90.0) r_fov_greater_than_90 = false; else r_fov_greater_than_90 = true; // TODO: collect 386-specific code in one place #if id386 if (r_pixbytes == 1) { Sys_MakeCodeWriteable ((long)R_Surf8Start, (long)R_Surf8End - (long)R_Surf8Start); colormap = vid.colormap; R_Surf8Patch (); } else { Sys_MakeCodeWriteable ((long)R_Surf16Start, (long)R_Surf16End - (long)R_Surf16Start); colormap = vid.colormap16; R_Surf16Patch (); } #endif /* id386 */ D_ViewChanged (); } #undef NOT_VGA_MODE #undef FOV_ADAPTING /* =============== R_MarkLeaves =============== */ static void R_MarkLeaves (void) { byte *vis; mnode_t *node; int i; if (r_oldviewleaf == r_viewleaf) return; r_visframecount++; r_oldviewleaf = r_viewleaf; vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); for (i = 0; i < cl.worldmodel->numleafs; i++) { if (vis[i>>3] & (1<<(i&7))) { node = (mnode_t *)&cl.worldmodel->leafs[i+1]; do { if (node->visframe == r_visframecount) break; node->visframe = r_visframecount; node = node->parent; } while (node); } } } static void R_PrepareAlias (void) { int j; int lnum; alight_t lighting; // FIXME: remove and do real lighting float lightvec[3] = {-1, 0, 0}; vec3_t dist; float add; vec3_t adjust_origin; VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); // see if the bounding box lets us trivially reject, also sets // trivial accept status if (R_AliasCheckBBox ()) { VectorCopy(currententity->origin, adjust_origin); adjust_origin[2] += (currententity->model->mins[2] + currententity->model->maxs[2]) / 2; j = R_LightPoint (adjust_origin); lighting.ambientlight = j; lighting.shadelight = j; lighting.plightvec = lightvec; if (r_dynamic.integer) { for (lnum = 0; lnum < MAX_DLIGHTS; lnum++) { if (cl_dlights[lnum].die >= cl.time) { VectorSubtract (currententity->origin, cl_dlights[lnum].origin, dist); if (cl_dlights[lnum].radius> 0) { add = cl_dlights[lnum].radius - VectorLengthFast(dist); if (add > 0) lighting.ambientlight += add; } else { add = VectorLengthFast(dist) + cl_dlights[lnum].radius; if (add < 0) lighting.ambientlight += add; } } } } // clamp lighting so it doesn't overbright as much if (lighting.ambientlight > 128) lighting.ambientlight = 128; if (lighting.ambientlight < 0) lighting.ambientlight = 0; if (lighting.ambientlight + lighting.shadelight > 192) lighting.shadelight = 192 - lighting.ambientlight; R_AliasDrawModel (&lighting); } } /* ============= R_DrawEntitiesOnList ============= */ static void R_DrawEntitiesOnList (void) { int i; if (!r_drawentities.integer) return; for (i = 0; i < cl_numvisedicts; i++) { currententity = &cl_visedicts[i]; switch (currententity->model->type) { case mod_sprite: if ((currententity->model->flags & EF_TRANSPARENT) || (currententity->drawflags & DRF_TRANSLUCENT)) { break; } VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); R_DrawSprite (); break; case mod_alias: if ((currententity->model->flags & (EF_TRANSPARENT|EF_HOLEY|EF_SPECIAL_TRANS)) || (currententity->drawflags & DRF_TRANSLUCENT)) { break; } R_PrepareAlias (); break; default: break; } } // Draw the transparent stuff for (i = 0; i < cl_numvisedicts; i++) { currententity = &cl_visedicts[i]; switch (currententity->model->type) { case mod_sprite: if ((currententity->model->flags & EF_TRANSPARENT) || (currententity->drawflags & DRF_TRANSLUCENT)) { VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); R_DrawSprite (); } break; case mod_alias: if ((currententity->model->flags & (EF_TRANSPARENT|EF_HOLEY|EF_SPECIAL_TRANS)) || (currententity->drawflags & DRF_TRANSLUCENT)) { R_PrepareAlias (); } break; default: break; } } } /* ============= R_DrawViewModel ============= */ static void R_DrawViewModel (void) { // FIXME: remove and do real lighting float lightvec[3] = {-1, 0, 0}; int j; int lnum; vec3_t dist; float add; dlight_t *dl; if (cl.spectator) return; currententity = &cl.viewent; if (!currententity->model) return; VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); VectorCopy (vup, viewlightvec); VectorInverse (viewlightvec); j = R_LightPoint (currententity->origin); if (j < 24) j = 24; // always give some light on gun r_viewlighting.ambientlight = j; r_viewlighting.shadelight = j; // add dynamic lights if (r_dynamic.integer) { for (lnum = 0; lnum < MAX_DLIGHTS; lnum++) { dl = &cl_dlights[lnum]; if (!dl->radius) continue; if (dl->die < cl.time) continue; VectorSubtract (currententity->origin, dl->origin, dist); if (dl->radius > 0) { add = dl->radius - VectorLengthFast(dist); if (add > 0) r_viewlighting.ambientlight += add; } else { add = VectorLengthFast(dist) + dl->radius; if (add < 0) r_viewlighting.ambientlight += add; } } } // clamp lighting so it doesn't overbright as much if (r_viewlighting.ambientlight > 128) r_viewlighting.ambientlight = 128; if (r_viewlighting.ambientlight < 0) r_viewlighting.ambientlight = 0; if (r_viewlighting.ambientlight + r_viewlighting.shadelight > 192) r_viewlighting.shadelight = 192 - r_viewlighting.ambientlight; r_viewlighting.plightvec = lightvec; cl.light_level = r_viewlighting.ambientlight; if ((cl.v.health <= 0) || //rjr (cl.items & IT_INVISIBILITY) || //O.S. (r_fov_greater_than_90) || (!r_drawviewmodel.integer)) { return; } R_AliasDrawModel (&r_viewlighting); } /* ============= R_BmodelCheckBBox ============= */ static int R_BmodelCheckBBox (qmodel_t *clmodel, float *minmaxs) { int i, *pindex, clipflags; vec3_t acceptpt, rejectpt; double d; clipflags = 0; if (currententity->angles[0] || currententity->angles[1] || currententity->angles[2]) { for (i = 0; i < 4; i++) { d = DotProduct (currententity->origin, view_clipplanes[i].normal); d -= view_clipplanes[i].dist; if (d <= -clmodel->radius) return BMODEL_FULLY_CLIPPED; if (d <= clmodel->radius) clipflags |= (1< tmaxs[j]) tmaxs[j] = v[j]; } } } /* ============= R_DrawBEntitiesOnList ============= */ static void R_DrawBEntitiesOnList (void) { int i, j, k, clipflags; vec3_t oldorigin; qmodel_t *clmodel; float minmaxs[6]; vec3_t mins, maxs; if (!r_drawentities.integer) return; VectorCopy (modelorg, oldorigin); insubmodel = true; r_dlightframecount = r_framecount; for (i = 0; i < cl_numvisedicts; i++) { currententity = &cl_visedicts[i]; switch (currententity->model->type) { case mod_brush: clmodel = currententity->model; // see if the bounding box lets us trivially reject, // also sets trivial accept status RotatedBBox (clmodel->mins, clmodel->maxs, currententity->angles, mins, maxs); VectorAdd (mins, currententity->origin, minmaxs); VectorAdd (maxs, currententity->origin, (minmaxs+3)); clipflags = R_BmodelCheckBBox (clmodel, minmaxs); if (clipflags != BMODEL_FULLY_CLIPPED) { VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); r_pcurrentvertbase = clmodel->vertexes; // FIXME: stop transforming twice R_RotateBmodel (); // calculate dynamic lighting for bmodel // if it's not an instanced model if (r_dynamic.integer) { if (clmodel->firstmodelsurface != 0) { for (k = 0; k < MAX_DLIGHTS; k++) { if ((cl_dlights[k].die < cl.time) || (!cl_dlights[k].radius)) { continue; } R_MarkLights (&cl_dlights[k], 1<nodes + clmodel->hulls[0].firstclipnode); } } } #if 0 // if the driver wants polygons, deliver those. Z-buffering is on // at this point, so no clipping to the world tree is needed, just // frustum clipping if (r_drawpolys | r_drawculledpolys) { R_ZDrawSubmodelPolys (clmodel); } else #endif { r_pefragtopnode = NULL; for (j = 0; j < 3; j++) { r_emins[j] = minmaxs[j]; r_emaxs[j] = minmaxs[3+j]; } R_SplitEntityOnNode2 (cl.worldmodel->nodes); if (r_pefragtopnode) { currententity->topnode = r_pefragtopnode; if (r_pefragtopnode->contents >= 0) { // not a leaf; has to be clipped to the world BSP r_clipflags = clipflags; R_DrawSolidClippedSubmodelPolygons (clmodel); } else { // falls entirely in one leaf, so we just put all the // edges in the edge list and let 1/z sorting handle // drawing order R_DrawSubmodelPolygons (clmodel, clipflags); } currententity->topnode = NULL; } } // put back world rotation and frustum clipping // FIXME: R_RotateBmodel should just work off base_vxx VectorCopy (base_vpn, vpn); VectorCopy (base_vup, vup); VectorCopy (base_vright, vright); VectorCopy (base_modelorg, modelorg); VectorCopy (oldorigin, modelorg); R_TransformFrustum (); } break; default: break; } } insubmodel = false; } /* ================ R_EdgeDrawing ================ */ static void R_EdgeDrawing (qboolean Translucent) { #if id386 # define static_in_C_ /* nothing */ #else # define static_in_C_ static #endif /* R_EdgeDrawing() is called twice by R_RenderView_(): * First with Translucent as false, where the three pointers r_edges, * surfaces and surf_max are set to the ledges[] and lsurfs[] arrays, * and then with Translucent as true in which case R_EdgeDrawing() * does *not* set the pointers and the code assumes them to be still * valid, but they pointed to variables which went out of scope by * that time. * The x86 assembler code actually handles that (R_SurfacePatch() -> * R_SurfacePatchT(), I think) and if you make those arrays static, * then you may as well get a segmentation fault. * For C-only code, however, the ledges[] and lsurfs[] arrays *must* * be static in order to keep r_edges, surfaces and surf_max pointers * valid during the second call of R_EdgeDrawing(true). */ static_in_C_ edge_t ledges[NUMSTACKEDGES + ((CACHE_SIZE - 1) / sizeof(edge_t)) + 1]; static_in_C_ surf_t lsurfs[NUMSTACKSURFACES + ((CACHE_SIZE - 1) / sizeof(surf_t)) + 1]; int EdgesSize, SurfacesSize; if (!Translucent) { if (auxedges) { r_edges = auxedges; } else { r_edges = (edge_t *) (((intptr_t)&ledges[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); } if (r_surfsonstack) { surfaces = (surf_t *) (((intptr_t)&lsurfs[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); surf_max = &surfaces[r_cnumsurfs]; // surface 0 doesn't really exist; it's just a dummy because // index 0 is used to indicate no edge attached to surface surfaces--; #if id386 R_SurfacePatch (); #endif } R_BeginEdgeFrame (); if (r_dspeeds.integer) { rw_time1 = Sys_DoubleTime (); } R_RenderWorld (); } else { memcpy(r_edges,SaveEdges,SaveEdgesCount * sizeof(edge_t)); memcpy(surfaces,SaveSurfaces,SaveSurfacesCount * sizeof(surf_t)); } #if 0 if (r_drawculledpolys) R_ScanEdges (Translucent); #endif // only the world can be drawn back to front with no z reads or compares, just // z writes, so have the driver turn z compares on now if (!Translucent) { D_TurnZOn (); if (r_dspeeds.integer) { rw_time2 = Sys_DoubleTime (); db_time1 = rw_time2; } R_DrawBEntitiesOnList (); SaveSurfacesCount = surface_p - surfaces; SurfacesSize = SaveSurfacesCount * sizeof(surf_t); SaveEdgesCount = edge_p - r_edges; EdgesSize = SaveEdgesCount * sizeof(edge_t); if (SurfacesSize > SaveSurfacesSize || EdgesSize > SaveEdgesSize) { if (!cl.spectator) Con_Printf("WARNING: Translucency disabled: %d/%d %d/%d\n",SurfacesSize,SaveSurfacesSize,EdgesSize,SaveEdgesSize); AllowTranslucency = false; } else { AllowTranslucency = true; memcpy(SaveEdges,r_edges,EdgesSize); memcpy(SaveSurfaces,surfaces,SurfacesSize); } if (r_dspeeds.integer) { db_time2 = Sys_DoubleTime (); se_time1 = db_time2; } if (!r_dspeeds.integer) { VID_UnlockBuffer (); S_ExtraUpdate (); // don't let sound get messed up if going slow VID_LockBuffer (); } } #if 0 if (!(r_drawpolys | r_drawculledpolys)) #endif R_ScanEdges (Translucent); } void R_DrawName (vec3_t origin, const char *name, int siegestatus) { vec3_t local, transformed; float zi; // byte *pdest; // short *pz; int izi, u, v; if (!name) return; // transform point VectorSubtract (origin, r_origin, local); local[2] += 16; transformed[0] = DotProduct(local, r_pright); transformed[1] = DotProduct(local, r_pup); transformed[2] = DotProduct(local, r_ppn); if (transformed[2] < PARTICLE_Z_CLIP) return; // project the point // FIXME: preadjust xcenter and ycenter zi = 1.0 / transformed[2]; u = (int)(xcenter + zi * transformed[0] + 0.5); v = (int)(ycenter - zi * transformed[1] + 0.5); if ((v > d_vrectbottom_particle) || (u > d_vrectright_particle) || (v < d_vrecty) || (u < d_vrectx)) { return; } // pz = d_pzbuffer + (d_zwidth * v) + u; // pdest = d_viewbuffer + d_scantable[v] + u; izi = (int)(zi * 0x8000); izi += 14; // if (pz[0] > izi) // return; u -= strlen(name) * 4; if (siegestatus < 0) // not siege { Draw_String (u, v, name); return; } if (siegestatus > 10) //keyholder { siegestatus -= 10; Draw_Character (u, v, 145); //key u += 8; } switch (siegestatus) { case 0: //att Draw_Character (u, v, 144); //sword Draw_String (u+8, v, name); return; case 1: //def Draw_Character (u, v, 143); //shield Draw_RedString (u+8, v, name); return; case 2: //def Draw_Character (u, v, 130); //crown Draw_RedString (u+8, v, name); return; case 3: //neither att nor def default: Draw_String (u+8, v, name); return; } } /* ================ R_RenderView r_refdef must be set before the first call ================ */ static void R_RenderView_ (void) { byte warpbuffer[WARP_WIDTH * WARP_HEIGHT]; r_warpbuffer = warpbuffer; if (r_timegraph.integer || r_speeds.integer || r_dspeeds.integer) { if (r_wholeframe.integer) r_time1 = r_lasttime1; else r_time1 = Sys_DoubleTime (); } R_SetupFrame (); #ifdef PASSAGES SetVisibilityByPassages (); #else R_MarkLeaves (); // done here so we know if we're in water #endif // make FDIV fast. This reduces timing precision after we've been running for a // while, so we don't do it globally. This also sets chop mode, and we do it // here so that setup stuff like the refresh area calculations match what's // done in screen.c Sys_LowFPPrecision (); if (!r_worldentity.model || !cl.worldmodel) Sys_Error ("%s: NULL worldmodel", __thisfunc__); if (!r_dspeeds.integer) { VID_UnlockBuffer (); S_ExtraUpdate (); // don't let sound get messed up if going slow VID_LockBuffer (); } R_EdgeDrawing (false); if (!r_dspeeds.integer) { VID_UnlockBuffer (); S_ExtraUpdate (); // don't let sound get messed up if going slow VID_LockBuffer (); } if (r_dspeeds.integer) { se_time2 = Sys_DoubleTime (); de_time1 = se_time2; } R_DrawEntitiesOnList (); if (TransCount && AllowTranslucency) R_EdgeDrawing (true); if (r_dspeeds.integer) { de_time2 = Sys_DoubleTime (); dv_time1 = de_time2; } R_DrawViewModel (); if (r_dspeeds.integer) { dv_time2 = Sys_DoubleTime (); dp_time1 = Sys_DoubleTime (); } R_DrawParticles (); if (r_dspeeds.integer) dp_time2 = Sys_DoubleTime (); if (r_dowarp) D_WarpScreen (); V_SetContentsColor (r_viewleaf->contents); if (r_timegraph.integer) R_TimeGraph (); if (r_netgraph.integer) R_NetGraph (); if (r_zgraph.integer) R_ZGraph (); if (r_aliasstats.integer) R_PrintAliasStats (); if (r_speeds.integer) R_PrintTimes (); if (r_dspeeds.integer) R_PrintDSpeeds (); if (r_reportsurfout.integer && r_outofsurfaces) Con_Printf ("Short %d surfaces\n", r_outofsurfaces); if (r_reportedgeout.integer && r_outofedges) Con_Printf ("Short roughly %d edges\n", r_outofedges * 2 / 3); // back to high floating-point precision Sys_HighFPPrecision (); } void R_RenderView (void) { /* int dummy; int delta; delta = (byte *)&dummy - r_stack_start; if (delta < -10000 || delta > 10000) Sys_Error ("%s: called without enough stack", __thisfunc__); */ if ( Hunk_LowMark() & 3 ) Sys_Error ("Hunk is missaligned"); /* if ( (intptr_t)(&dummy) & 3 ) Sys_Error ("Stack is missaligned"); */ if ( (intptr_t)(&r_warpbuffer) & 3 ) Sys_Error ("Globals are missaligned"); R_RenderView_ (); } engine/hexenworld/client/r_misc.c000066400000000000000000000320531444734033100174060ustar00rootroot00000000000000/* r_misc.c -- * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" /* =============== R_CheckVariables =============== */ static void R_CheckVariables (void) { static int oldbright; if (r_fullbright.integer != oldbright) { oldbright = r_fullbright.integer; D_FlushCaches (); // so all lighting changes } } /* ==================== R_TimeRefresh_f For program optimization ==================== */ void R_TimeRefresh_f (void) { int i; float start, stop, time; int startangle; vrect_t vr; if (cls.state != ca_active) { Con_Printf("Not connected to a server\n"); return; } startangle = r_refdef.viewangles[1]; start = Sys_DoubleTime (); for (i = 0; i < 128; i++) { r_refdef.viewangles[1] = i/128.0*360.0; VID_LockBuffer (); R_RenderView (); VID_UnlockBuffer (); vr.x = r_refdef.vrect.x; vr.y = r_refdef.vrect.y; vr.width = r_refdef.vrect.width; vr.height = r_refdef.vrect.height; vr.pnext = NULL; VID_Update (&vr); } stop = Sys_DoubleTime (); time = stop-start; Con_Printf ("%f seconds (%f fps)\n", time, 128/time); r_refdef.viewangles[1] = startangle; } /* ================ R_LineGraph Only called by R_DisplayTime ================ */ static void R_LineGraph (int x, int y, int h) { int i; byte *dest; int s; int color; // FIXME: should be disabled on no-buffer adapters, or should be in the driver // x += r_refdef.vrect.x; // y += r_refdef.vrect.y; dest = vid.buffer + vid.rowbytes*y + x; s = r_graphheight.integer; if (h == 10000) color = 175; // yellow else if (h == 9999) color = 143; // red else if (h == 9998) color = 249; // blue else color = 242; // pink if (h > s) h = s; for (i = 0; i < h; i++, dest -= vid.rowbytes*2) { dest[0] = color; // *(dest-vid.rowbytes) = 0x30; } #if 0 for ( ; i < s; i++, dest -= vid.rowbytes*2) { dest[0] = 0x30; *(dest-vid.rowbytes) = 0x30; } #endif } /* ================ R_LineGraph ================ */ static void R_LineGraph2 (int x, int y, int h, int h2, int drawType, int marker) { int i; byte *dest; int s; // FIXME: should be disabled on no-buffer adapters, or should be in the driver // drawType // 0-000: expanded / solid / background // 1-001: condensed / background // 2-010: expanded / holey / background // 3-011: condensed / background // 4-100: expanded / solid / no background // 5-101: condensed / no background // 6-110: expanded / holey / no background // 7-111: condensed / no background x += r_refdef.vrect.x; y += r_refdef.vrect.y; dest = vid.buffer + vid.rowbytes*y + x; s = r_graphheight.integer; if (s > r_refdef.vrect.height) { s = r_refdef.vrect.height; } // show actual received bytes here (h) if (h > s) { h = s; } for (i = 0; i < h; i++, dest -= vid.rowbytes) { dest[0] = ((i+1) % marker == 0) ? 143 : 255; if (!(drawType & 1)) { // Expanded if (!(drawType & 2)) { // Solid *(dest-vid.rowbytes) = 53; } dest -= vid.rowbytes; } } // show uncompressed bytes here (h2) if (h2 > s) { h2 = s; } for (i = h; i < h2; i++, dest -= vid.rowbytes) { dest[0] = ((i+1) % marker == 0) ? 130 : 244; if (!(drawType & 1)) { // Expanded if (!(drawType & 2)) { // Solid *(dest-vid.rowbytes) = 53; } dest -= vid.rowbytes; } } if (drawType & 4) { // No background return; } for ( ; i < s; i++, dest -= vid.rowbytes) { dest[0] = 53; if (!(drawType & 1)) { // Expanded if (!(drawType & 2)) { // Solid *(dest-vid.rowbytes) = 53; } dest -= vid.rowbytes; } } } /* ============== R_TimeGraph Performance monitoring tool ============== */ #define MAX_TIMINGS 100 #define GRAPH_TYPE_COUNT 2 extern int LastServerMessageSize; // uncompressed extern int LastCompMessageSize; // compressed void R_TimeGraph (void) { int a, x; int drawType, graphType; static int timex; static byte r_timings[MAX_TIMINGS]; //compressed static byte r_timings2[MAX_TIMINGS];//uncompressed static int graphMarkers[GRAPH_TYPE_COUNT] = { 10000, 10 }; graphType = r_timegraph.integer; if (graphType < 1 || graphType > GRAPH_TYPE_COUNT) { return; } drawType = (int)((r_timegraph.value-floor(r_timegraph.value)+1E-3)*10); if (graphType == 1) { // Frame times a = (Sys_DoubleTime() - r_time1) / 0.01; r_timings[timex] = a; r_timings2[timex] = a; } else { // Packet sizes a = LastCompMessageSize/10; LastCompMessageSize = 0; r_timings[timex] = a; a = LastServerMessageSize/10; LastServerMessageSize = 0; r_timings2[timex] = a; } a = timex; if (r_refdef.vrect.width <= MAX_TIMINGS) { x = r_refdef.vrect.width-1; } else { x = r_refdef.vrect.width - (r_refdef.vrect.width-MAX_TIMINGS)/2; } do { R_LineGraph2(x, r_refdef.vrect.height-2, r_timings[a], r_timings2[a], drawType, graphMarkers[graphType-1]); if (x == 0) { break; // screen too small to hold entire thing } x--; a--; if (a == -1) { a = MAX_TIMINGS-1; } } while (a != timex); timex = (timex+1) % MAX_TIMINGS; } /* ============== R_NetGraph ============== */ #define NET_TIMINGS 256 void R_NetGraph (void) { int a, x, y, y2, w, i; frame_t *frame; int lost; char st[80]; static int packet_latency[256]; if (vid.width - 16 <= NET_TIMINGS) w = vid.width - 16; else w = NET_TIMINGS; for (i = cls.netchan.outgoing_sequence-UPDATE_BACKUP+1 ; i <= cls.netchan.outgoing_sequence ; i++) { frame = &cl.frames[i&UPDATE_MASK]; if (frame->receivedtime == -1) packet_latency[i&255] = 9999; // dropped else if (frame->receivedtime == -2) packet_latency[i&255] = 10000; // choked else if (frame->invalid) packet_latency[i&255] = 9998; // invalid delta else packet_latency[i&255] = (frame->receivedtime - frame->senttime)*20; } x = -((vid.width - 320)>>1); y = vid.height - sb_lines - 24 - r_graphheight.integer*2 - 2; M_DrawTextBox (x, y, (w+7)/8, (r_graphheight.integer*2+7)/8 + 1); y2 = y + 8; y = vid.height - sb_lines - 8 - 2; x = 8; lost = 0; for (a = 0 ; a < w ; a++) { i = (cls.netchan.outgoing_sequence-a) & 255; if (packet_latency[i] == 9999) lost++; R_LineGraph (x+w-1-a, y, packet_latency[i]); } sprintf(st, "%3i%% packet loss", lost*100/NET_TIMINGS); Draw_String(8, y2, st); } /* ============== R_ZGraph ============== */ void R_ZGraph (void) { int a, x, w, i; static int height[256]; if (r_refdef.vrect.width <= 256) w = r_refdef.vrect.width; else w = 256; height[r_framecount&255] = ((int)r_origin[2]) & 31; x = 0; for (a = 0 ; a < w ; a++) { i = (r_framecount-a) & 255; R_LineGraph (x+w-1-a, r_refdef.vrect.height-2, height[i]); } } /* ============= R_PrintTimes ============= */ void R_PrintTimes(void) { float r_time2; float ms, fps; r_lasttime1 = r_time2 = Sys_DoubleTime(); ms = 1000 * (r_time2 - r_time1); fps = 1000 / ms; Con_Printf("%3.1f fps %5.0f ms\n%3i/%3i/%3i poly %3i surf\n", fps, ms, c_faceclip, r_polycount, r_drawnpolycount, c_surf); c_surf = 0; } /* ============= R_PrintDSpeeds ============= */ void R_PrintDSpeeds (void) { float ms, dp_time, r_time2, rw_time, db_time, se_time, de_time, dv_time; r_time2 = Sys_DoubleTime (); dp_time = (dp_time2 - dp_time1) * 1000; rw_time = (rw_time2 - rw_time1) * 1000; db_time = (db_time2 - db_time1) * 1000; se_time = (se_time2 - se_time1) * 1000; de_time = (de_time2 - de_time1) * 1000; dv_time = (dv_time2 - dv_time1) * 1000; ms = (r_time2 - r_time1) * 1000; Con_Printf ("%3i %4.1fp %3iw %4.1fb %3is %4.1fe %4.1fv\n", (int)ms, dp_time, (int)rw_time, db_time, (int)se_time, de_time, dv_time); } /* ============= R_PrintAliasStats ============= */ void R_PrintAliasStats (void) { Con_Printf ("%3i polygon model drawn\n", r_amodels_drawn); } #if !id68k /* =================== R_TransformFrustum =================== */ void R_TransformFrustum (void) { int i; vec3_t v, v2; for (i = 0 ; i < 4 ; i++) { v[0] = screenedge[i].normal[2]; v[1] = -screenedge[i].normal[0]; v[2] = screenedge[i].normal[1]; v2[0] = v[1]*vright[0] + v[2]*vup[0] + v[0]*vpn[0]; v2[1] = v[1]*vright[1] + v[2]*vup[1] + v[0]*vpn[1]; v2[2] = v[1]*vright[2] + v[2]*vup[2] + v[0]*vpn[2]; VectorCopy (v2, view_clipplanes[i].normal); view_clipplanes[i].dist = DotProduct (modelorg, v2); } } #endif #if !id386 && !id68k /* ================ TransformVector ================ */ void TransformVector (vec3_t in, vec3_t out) { out[0] = DotProduct(in,vright); out[1] = DotProduct(in,vup); out[2] = DotProduct(in,vpn); } #endif /* ================ R_TransformPlane ================ */ void R_TransformPlane (mplane_t *p, float *normal, float *dist) { float d; d = DotProduct (r_origin, p->normal); *dist = p->dist - d; // TODO: when we have rotating entities, this will need to use the view matrix TransformVector (p->normal, normal); } /* =============== R_SetUpFrustumIndexes =============== */ static void R_SetUpFrustumIndexes (void) { int i, j, *pindex; pindex = r_frustum_indexes; for (i = 0 ; i < 4 ; i++) { for (j = 0 ; j < 3 ; j++) { if (view_clipplanes[i].normal[j] < 0) { pindex[j] = j; pindex[j+3] = j+3; } else { pindex[j] = j+3; pindex[j+3] = j; } } // FIXME: do just once at start pfrustum_indexes[i] = pindex; pindex += 6; } } /* =============== R_SetupFrame =============== */ void R_SetupFrame (void) { int edgecount; vrect_t vrect; float w, h; // don't allow cheats in multiplayer r_draworder.integer = 0; r_fullbright.integer = 0; r_ambient.integer = 0; r_drawflat.integer = 0; if (r_numsurfs.integer) { if ((surface_p - surfaces) > r_maxsurfsseen) r_maxsurfsseen = surface_p - surfaces; Con_Printf ("Used %ld of %ld surfs; %d max\n", (long)(surface_p - surfaces), (long)(surf_max - surfaces), r_maxsurfsseen); } if (r_numedges.integer) { edgecount = edge_p - r_edges; if (edgecount > r_maxedgesseen) r_maxedgesseen = edgecount; Con_Printf ("Used %d of %d edges; %d max\n", edgecount, r_numallocatededges, r_maxedgesseen); } r_refdef.ambientlight = r_ambient.integer; if (r_refdef.ambientlight < 0) r_refdef.ambientlight = 0; // if (!sv.active) r_draworder.integer = 0; // don't let cheaters look behind walls R_CheckVariables (); R_AnimateLight (); r_framecount++; numbtofpolys = 0; #if 0 // debugging r_refdef.vieworg[0] = 80; r_refdef.vieworg[1] = 64; r_refdef.vieworg[2] = 40; r_refdef.viewangles[0] = 0; r_refdef.viewangles[1] = 46.763641357; r_refdef.viewangles[2] = 0; #endif // build the transformation matrix for the given view angles VectorCopy (r_refdef.vieworg, modelorg); VectorCopy (r_refdef.vieworg, r_origin); AngleVectors (r_refdef.viewangles, vpn, vright, vup); // current viewleaf r_oldviewleaf = r_viewleaf; r_viewleaf = Mod_PointInLeaf (r_origin, cl.worldmodel); r_dowarpold = r_dowarp; r_dowarp = r_waterwarp.integer && (r_viewleaf->contents <= CONTENTS_WATER); if ((r_dowarp != r_dowarpold) || r_viewchanged) { if (r_dowarp) { if ((vid.width <= vid.maxwarpwidth) && (vid.height <= vid.maxwarpheight)) { vrect.x = 0; vrect.y = 0; vrect.width = vid.width; vrect.height = vid.height; R_SetVrect (&vrect, &r_refdef.vrect, sb_lines); R_ViewChanged (vid.aspect); } else { w = vid.width; h = vid.height; if (w > vid.maxwarpwidth) { h *= (float)vid.maxwarpwidth / w; w = vid.maxwarpwidth; } if (h > vid.maxwarpheight) { h = vid.maxwarpheight; w *= (float)vid.maxwarpheight / h; } vrect.x = 0; vrect.y = 0; vrect.width = (int)w; vrect.height = (int)h; R_SetVrect (&vrect, &r_refdef.vrect, (int)((float)sb_lines * (h/(float)vid.height))); R_ViewChanged (vid.aspect * (h / w) * ((float)vid.width / (float)vid.height)); } } else { // scr_vrect alredy holds the original data, // therefore no need for extra R_SetVrect() r_refdef.vrect = scr_vrect; R_ViewChanged (vid.aspect); } r_viewchanged = false; } // start off with just the four screen edge clip planes R_TransformFrustum (); // save base values VectorCopy (vpn, base_vpn); VectorCopy (vright, base_vright); VectorCopy (vup, base_vup); VectorCopy (modelorg, base_modelorg); R_SetSkyFrame (); R_SetUpFrustumIndexes (); r_cache_thrash = false; // clear frame counts c_faceclip = 0; d_spanpixcount = 0; r_polycount = 0; r_drawnpolycount = 0; r_wholepolycount = 0; r_amodels_drawn = 0; r_outofsurfaces = 0; r_outofedges = 0; D_SetupFrame (); } engine/hexenworld/client/r_part.c000066400000000000000000001167541444734033100174340ustar00rootroot00000000000000/* r_part.c -- particles rendering * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_local.h" #define MAX_PARTICLES 2048 // default max # of particles at one time #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's // on the command line //============================================================================= static int ramp1[8] = { 416, 416+2, 416+4, 416+6, 416+8, 416+10, 416+12, 416+14 }; static int ramp2[8] = { 384+4, 384+6, 384+8, 384+10, 384+12, 384+13, 384+14, 384+15 }; static int ramp3[8] = { 0x6d, 0x6b, 6, 5, 4, 3 }; static int ramp4[16] = { 416, 416+1, 416+2, 416+3, 416+4, 416+5, 416+6, 416+7, 416+8, 416+9, 416+10, 416+11, 416+12, 416+13, 416+14, 416+15 }; static int ramp5[16] = { 400, 400+1, 400+2, 400+3, 400+4, 400+5, 400+6, 400+7, 400+8, 400+9, 400+10, 400+11, 400+12, 400+13, 400+14, 400+15 }; static int ramp6[16] = { 256, 256+1, 256+2, 256+3, 256+4, 256+5, 256+6, 256+7, 256+8, 256+9, 256+10, 256+11, 256+12, 256+13, 256+14, 256+15 }; static int ramp7[16] = { 384, 384+1, 384+2, 384+3, 384+4, 384+5, 384+6, 384+7, 384+8, 384+9, 384+10, 384+11, 384+12, 384+13, 384+14, 384+15 }; static int ramp8[16] = { 175, 174, 173, 172, 171, 170, 169, 168, 167, 166, 13, 14, 15, 16, 17, 18 }; static int ramp9[16] = { 416, 416+1, 416+2, 416+3, 416+4, 416+5, 416+6, 416+7, 416+8, 416+9, 416+10, 416+11, 416+12, 416+13, 416+14, 416+15 }; /* MISSION PACK */ static int ramp10[16] ={ 432, 432+1, 432+2, 432+3, 432+4, 432+5, 432+6, 432+7, 432+8, 432+9, 432+10, 432+11, 432+12, 432+13, 432+14, 432+15 }; static int ramp11[8] = { 424, 424+1, 424+2, 424+3, 424+4, 424+5, 424+6, 424+7 }; static int ramp12[8] = { 136, 137, 138, 139, 140, 141, 142, 143 }; /* HEXENWORLD */ static int ramp13[16] ={ 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159 }; //============================================================================= particle_t *active_particles, *free_particles; particle_t *particles; static int r_numparticles; vec3_t r_pright, r_pup, r_ppn; static vec3_t rider_origin; static cvar_t leak_color = {"leak_color", "251", CVAR_ARCHIVE}; static particle_t *AllocParticle (void); void R_RunParticleEffect2 (vec3_t org, vec3_t dmin, vec3_t dmax, int color, ptype_t effect, int count); void R_RunParticleEffect3 (vec3_t org, vec3_t box, int color, ptype_t effect, int count); void R_RunParticleEffect4 (vec3_t org, float radius, int color, ptype_t effect, int count); //============================================================================= /* =============== R_InitParticles =============== */ void R_InitParticles (void) { int i; i = COM_CheckParm ("-particles"); if (i && i < com_argc-1) { r_numparticles = atoi(com_argv[i+1]); if (r_numparticles < ABSOLUTE_MIN_PARTICLES) r_numparticles = ABSOLUTE_MIN_PARTICLES; } else { r_numparticles = MAX_PARTICLES; } particles = (particle_t *) Hunk_AllocName (r_numparticles * sizeof(particle_t), "particles"); Cvar_RegisterVariable (&leak_color); } void R_DarkFieldParticles (entity_t *ent) { int i, j, k; particle_t *p; float vel; vec3_t dir; vec3_t org; org[0] = ent->origin[0]; org[1] = ent->origin[1]; org[2] = ent->origin[2]; for (i = -16; i < 16; i += 8) { for (j = -16; j < 16; j += 8) { for (k = 0; k < 32; k += 8) { p = AllocParticle(); if (!p) return; p->die = cl.time + 0.2 + (rand() & 7) * 0.02; p->color = 150 + (rand() % 6); p->type = pt_slowgrav; dir[0] = j * 8; dir[1] = i * 8; dir[2] = k * 8; p->org[0] = org[0] + i + (rand() & 3); p->org[1] = org[1] + j + (rand() & 3); p->org[2] = org[2] + k + (rand() & 3); VectorNormalizeFast (dir); vel = 50 + (rand() & 63); VectorScale (dir, vel, p->vel); } } } } /* =============== AllocParticle =============== */ static particle_t *AllocParticle (void) { particle_t *p; if (!free_particles) return NULL; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; return p; } /* =============== R_ClearParticles =============== */ void R_ClearParticles (void) { int i; if (!r_numparticles) return; free_particles = &particles[0]; active_particles = NULL; for (i = 0; i < r_numparticles-1; i++) particles[i].next = &particles[i+1]; particles[r_numparticles-1].next = NULL; } void R_ReadPointFile_f (void) { FILE *f; vec3_t org; int r; int c; particle_t *p; char name[MAX_QPATH]; byte color; if (cls.state != ca_active) return; // need an active map. color = (byte)Cvar_VariableValue("leak_color"); q_snprintf (name, sizeof(name), "maps/%s.pts", cl.mapname); FS_OpenFile (name, &f, NULL); if (!f) { Con_Printf ("couldn't open %s\n", name); return; } Con_Printf ("Reading %s...\n", name); c = 0; VectorClear (org); // silence pesky compiler warnings for ( ;; ) { r = fscanf (f,"%f %f %f\n", &org[0], &org[1], &org[2]); if (r != 3) break; c++; p = AllocParticle(); if (!p) { Con_Printf ("Not enough free particles\n"); break; } p->die = 99999; p->color = color; // (-c)&15; p->type = pt_static; VectorClear (p->vel); VectorCopy (org, p->org); } fclose (f); Con_Printf ("%i points read\n", c); } #if 0 /* EF_BRIGHTFIELD used this */ /* =============== R_EntityParticles =============== */ #if defined(GLQUAKE) /* otherwise from r_alias.c (r_shared.h) */ #define NUMVERTEXNORMALS 162 static const float r_avertexnormals[NUMVERTEXNORMALS][3] = { #include "anorms.h" }; #endif static vec3_t avelocities[NUMVERTEXNORMALS]; static float beamlength = 16; void R_EntityParticles (entity_t *ent) { int i; particle_t *p; float angle, dist; float sp, sy, cp, cy; vec3_t forward; dist = 64; if (!avelocities[0][0]) { for (i = 0; i < NUMVERTEXNORMALS; i++) { avelocities[i][0] = (rand() & 255) * 0.01; avelocities[i][1] = (rand() & 255) * 0.01; avelocities[i][2] = (rand() & 255) * 0.01; } } for (i = 0; i < NUMVERTEXNORMALS; i++) { angle = cl.time * avelocities[i][0]; sy = sin(angle); cy = cos(angle); angle = cl.time * avelocities[i][1]; sp = sin(angle); cp = cos(angle); angle = cl.time * avelocities[i][2]; forward[0] = cp*cy; forward[1] = cp*sy; forward[2] = -sp; p = AllocParticle(); if (!p) return; p->die = cl.time + 0.01; p->color = 0x6f; p->type = pt_explode; p->org[0] = ent->origin[0] + r_avertexnormals[i][0]*dist + forward[0]*beamlength; p->org[1] = ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*beamlength; p->org[2] = ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*beamlength; } } #endif void R_SuccubusInvincibleParticles (entity_t *ent) { int count; particle_t *p; vec3_t ent_angles,forward,org; ent_angles[0] = ent_angles[2] = 0; ent_angles[1] = cl.time * 12; q_sincosrad(ent_angles[1], &forward[1], &forward[0]); forward[0] *= 32; forward[1] *= 32; forward[2] = 0; VectorCopy(ent->origin, org); org[2] += 28; count = 140 * host_frametime; while (count > 0) { p = AllocParticle(); if (!p) return; p->ramp = 0; p->die = cl.time + 2; p->color = 416; p->type = pt_fireball; p->org[0] = org[0] + forward[0] + (rand() % 4) - 2; p->org[1] = org[1] + forward[1] + (rand() % 4) - 2; p->org[2] = org[2] + forward[2] + (rand() % 4) - 2; p->vel[0] = (rand() % 20) - 10; p->vel[1] = (rand() % 20) - 10; p->vel[2] = (rand() % 25) + 20; count--; } count = 60 * host_frametime; while (count > 0) { p = AllocParticle(); if (!p) return; p->ramp = 0; p->die = cl.time + 2; p->color = 135; p->type = pt_redfire; p->org[0] = org[0] + forward[0] + (rand() % 4) - 2; p->org[1] = org[1] + forward[1] + (rand() % 4) - 2; p->org[2] = org[2] + forward[2] + (rand() % 4) - 2; p->vel[0] = (rand() % 20) - 10; p->vel[1] = (rand() % 20) - 10; p->vel[2] = 0; count--; } } /* =============== R_ParseParticleEffect Parse an effect out of the server message =============== */ void R_ParseParticleEffect (void) { vec3_t org, dir; int i, count, msgcount, color; for (i = 0; i < 3; i++) org[i] = MSG_ReadCoord (); for (i = 0; i < 3; i++) dir[i] = MSG_ReadChar () * (1.0/16); msgcount = MSG_ReadByte (); color = MSG_ReadByte (); if (msgcount == 255) count = 1024; else count = msgcount; R_RunParticleEffect (org, dir, color, count); } /* =============== R_ParseParticleEffect2 Parse an effect out of the server message =============== */ void R_ParseParticleEffect2 (void) { vec3_t org, dmin, dmax; int i, msgcount, color; ptype_t effect; for (i = 0; i < 3; i++) org[i] = MSG_ReadCoord (); for (i = 0; i < 3; i++) dmin[i] = MSG_ReadFloat (); for (i = 0; i < 3; i++) dmax[i] = MSG_ReadFloat (); color = MSG_ReadShort (); msgcount = MSG_ReadByte (); effect = (ptype_t) MSG_ReadByte (); R_RunParticleEffect2 (org, dmin, dmax, color, effect, msgcount); } /* =============== R_ParseParticleEffect3 Parse an effect out of the server message =============== */ void R_ParseParticleEffect3 (void) { vec3_t org, box; int i, msgcount, color; ptype_t effect; for (i = 0; i < 3; i++) org[i] = MSG_ReadCoord (); for (i = 0; i < 3; i++) box[i] = MSG_ReadByte (); color = MSG_ReadShort (); msgcount = MSG_ReadByte (); effect = (ptype_t) MSG_ReadByte (); R_RunParticleEffect3 (org, box, color, effect, msgcount); } /* =============== R_ParseParticleEffect4 Parse an effect out of the server message =============== */ void R_ParseParticleEffect4 (void) { vec3_t org; int i, msgcount, color; ptype_t effect; float radius; for (i = 0; i < 3; i++) org[i] = MSG_ReadCoord (); radius = MSG_ReadByte(); color = MSG_ReadShort (); msgcount = MSG_ReadByte (); effect = (ptype_t) MSG_ReadByte (); R_RunParticleEffect4 (org, radius, color, effect, msgcount); } void R_ParseRainEffect(void) { vec3_t org, e_size; short color, count; int x_dir, y_dir; org[0] = MSG_ReadCoord(); org[1] = MSG_ReadCoord(); org[2] = MSG_ReadCoord(); e_size[0] = MSG_ReadCoord(); e_size[1] = MSG_ReadCoord(); e_size[2] = MSG_ReadCoord(); x_dir = MSG_ReadAngle(); y_dir = MSG_ReadAngle(); color = MSG_ReadShort(); count = MSG_ReadShort(); R_RainEffect (org, e_size, x_dir, y_dir, color, count); } /* =============== R_ParticleExplosion =============== */ void R_ParticleExplosion (vec3_t org) { int i, j; particle_t *p; for (i = 0; i < 1024; i++) { p = AllocParticle(); if (!p) return; p->die = cl.time + 5; p->color = ramp1[0]; p->ramp = rand() & 3; if (i & 1) { p->type = pt_explode; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() % 32) - 16); p->vel[j] = (rand() % 512) - 256; } } else { p->type = pt_explode2; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() % 32) - 16); p->vel[j] = (rand() % 512) - 256; } } } } /* =============== R_ParticleExplosion2 color mapped explosion =============== */ void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength) { int i, j; particle_t *p; int colorMod = 0; for (i = 0; i < 512; i++) { p = AllocParticle(); if (!p) return; p->die = cl.time + 0.3; p->color = colorStart + (colorMod % colorLength); colorMod++; p->type = pt_blob; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() % 32) - 16); p->vel[j] = (rand() % 512) - 256; } } } /* =============== R_BlobExplosion tarbaby explosion =============== */ void R_BlobExplosion (vec3_t org) { int i, j; particle_t *p; for (i = 0; i < 1024; i++) { p = AllocParticle(); if (!p) return; p->die = cl.time + 1 + (rand() & 8) * 0.05; if (i & 1) { p->type = pt_blob; p->color = 66 + (rand() % 6); for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() % 32) - 16); p->vel[j] = (rand() % 512) - 256; } } else { p->type = pt_blob2; p->color = 150 + (rand() % 6); for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() % 32) - 16); p->vel[j] = (rand() % 512) - 256; } } } } /* =============== R_RunParticleEffect =============== */ void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count) { int i, j; particle_t *p; for (i = 0; i < count; i++) { p = AllocParticle(); if (!p) return; if (count == 1024) { // rocket explosion p->die = cl.time + 5; p->color = ramp1[0]; p->ramp = rand() & 3; if (i & 1) { p->type = pt_explode; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() % 32) - 16); p->vel[j] = (rand() % 512) - 256; } } else { p->type = pt_explode2; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() % 32) - 16); p->vel[j] = (rand() % 512) - 256; } } } else { p->die = cl.time + 0.1 * (rand() % 5); // p->color = (color & ~7) + (rand() & 7); // p->color = 265 + (rand() % 9); p->color = 256 + 16 + 12 + (rand() % 4); p->type = pt_slowgrav; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() & 15) - 8); p->vel[j] = dir[j] * 15;// + (rand() % 300) - 150; } } } } /* =============== R_RunParticleEffect2 =============== */ void R_RunParticleEffect2 (vec3_t org, vec3_t dmin, vec3_t dmax, int color, ptype_t effect, int count) { int i, j; particle_t *p; float num; // float num2; for (i = 0; i < count; i++) { p = AllocParticle(); if (!p) return; // p->die = cl.time + 0.1 * (rand() % 5); p->die = cl.time + 2; p->color = color; p->type = effect; p->ramp = 0; for (j = 0; j < 3; j++) { num = rand() / RAND_MAX; p->org[j] = org[j] + ((rand() & 15) - 8); p->vel[j] = dmin[j] + ((dmax[j] - dmin[j]) * num); } /* num = rand() / RAND_MAX; num2 = (int)(host_time * 20)%25 + 10; num2 = (int)(host_time * 20)%10 + 10; // num2 = rand() / RAND_MAX * 5; p->org[0] = org[0] + cos(num * 2 * M_PI)*num2; p->org[1] = org[1] + sin(num * 2 * M_PI)*num2; p->org[2] = org[2]; */ } } /* =============== R_RunParticleEffect3 =============== */ void R_RunParticleEffect3 (vec3_t org, vec3_t box, int color, ptype_t effect, int count) { int i, j; particle_t *p; float num; for (i = 0; i < count; i++) { p = AllocParticle(); if (!p) return; // p->die = cl.time + 0.1 * (rand() % 5); p->die = cl.time + 2; p->color = color; p->type = effect; p->ramp = 0; for (j = 0; j < 3; j++) { num = rand() / RAND_MAX; p->org[j] = org[j] + ((rand() & 15) - 8); p->vel[j] = (box[j] * num * 2) - box[j]; } } } /* =============== R_RunParticleEffect4 =============== */ void R_RunParticleEffect4 (vec3_t org, float radius, int color, ptype_t effect, int count) { int i, j; particle_t *p; float num; for (i = 0; i < count; i++) { p = AllocParticle(); if (!p) return; // p->die = cl.time + 0.1 * (rand() % 5); p->die = cl.time + 2; p->color = color; p->type = effect; p->ramp = 0; for (j = 0; j < 3; j++) { num = rand() / RAND_MAX; p->org[j] = org[j] + ((rand() & 15) - 8); p->vel[j] = (radius * num * 2) - radius; } } } void R_SplashParticleEffect (vec3_t org, float radius, int color, ptype_t effect, int count) { int i, j; particle_t *p; float num; for (i = 0; i < count; i++) { p = AllocParticle(); if (!p) return; p->die = cl.time + 2; p->color = color; p->type = effect; p->ramp = 0; for (j = 0; j < 3; j++) { num = rand() / RAND_MAX; if (j == 2) { p->vel[j] = (radius * num * 4) + radius; p->org[j] = org[j] + 3; } else { p->vel[j] = (radius * num * 2) - radius; p->org[j] = org[j] + ((rand() % 64) - 32); } } } } /* =============== R_LavaSplash =============== */ void R_LavaSplash (vec3_t org) { int i, j, k; particle_t *p; float vel; vec3_t dir; for (i = -16; i < 16; i++) { for (j = -16; j < 16; j++) { for (k = 0; k < 1; k++) { p = AllocParticle(); if (!p) return; p->die = cl.time + 2 + (rand() & 31) * 0.02; p->color = 224 + (rand() & 7); p->type = pt_slowgrav; dir[0] = j*8 + (rand() & 7); dir[1] = i*8 + (rand() & 7); dir[2] = 256; p->org[0] = org[0] + dir[0]; p->org[1] = org[1] + dir[1]; p->org[2] = org[2] + (rand() & 63); VectorNormalizeFast (dir); vel = 50 + (rand() & 63); VectorScale (dir, vel, p->vel); } } } } /* =============== R_TargetBallEffect =============== */ void R_TargetBallEffect (vec3_t org) { int i; particle_t *p; float vel; vec3_t dir; for (i = 0; i < 40 * host_frametime; i++) { p = AllocParticle(); if (!p) return; if (v_targDist < 60) { p->die = cl.time + (rand() & 3) * 0.02 + (.23 * (1.0 - (.23 * (v_targDist - 24.0)/36.0))); } else { p->die = cl.time + (.3 * ((256.0 - v_targDist)/256.0)) + (rand() & 7) * 0.02; } p->color = 7 + (rand() % 24); p->type = pt_slowgrav; dir[0] = (rand() & 63) - 31; dir[1] = (rand() & 63) - 31; dir[2] = 256; p->org[0] = org[0] + (rand() & 3) - 2; p->org[1] = org[1] + (rand() & 3) - 2; p->org[2] = org[2] + (rand() & 3); VectorNormalizeFast (dir); vel = 50 + (rand() & 63); VectorScale (dir, vel, p->vel); } } /* =============== R_BrightFieldSource =============== */ void R_BrightFieldSource (vec3_t org) { int i; particle_t *p; float vel; vec3_t dir; float height; height = q_cosrad(cl.time * 4.0) * 25; for (i = 0; i < 120 * host_frametime ; i++) { p = AllocParticle(); if (!p) return; p->die = cl.time + .5; p->color = 143; //p->type = pt_slowgrav; p->type = pt_quake; dir[0] = (rand() % 256) - 128; dir[1] = (rand() % 256) - 128; dir[2] = 32; p->org[0] = org[0] + (rand() & 15) - 7; p->org[1] = org[1] + (rand() & 15) - 7; // p->org[2] = org[2] + (rand() & 3); p->org[2] = org[2] + height; VectorNormalizeFast (dir); vel = 70 + (rand() & 31); VectorScale (dir, vel, p->vel); } for (i = 0; i < 120 * host_frametime; i++) { p = AllocParticle(); if (!p) return; p->die = cl.time + .5; p->color = 159; //p->type = pt_slowgrav; p->type = pt_quake; dir[0] = (rand() % 256) - 128; dir[1] = (rand() % 256) - 128; dir[2] = 32; p->org[0] = org[0] + (rand() & 15) - 7; p->org[1] = org[1] + (rand() & 15) - 7; // p->org[2] = org[2] + (rand() & 3); p->org[2] = org[2] - height; VectorNormalizeFast (dir); vel = 70 + (rand() & 31); VectorScale (dir, vel, p->vel); } } /* =============== R_TeleportSplash =============== */ void R_TeleportSplash (vec3_t org) { int i, j, k; particle_t *p; float vel; vec3_t dir; for (i = -16; i < 16; i += 4) { for (j = -16; j < 16; j += 4) { for (k = -24; k < 32; k += 4) { p = AllocParticle(); if (!p) return; p->die = cl.time + 0.2 + (rand() & 7) * 0.02; p->color = 7 + (rand() & 7); p->type = pt_slowgrav; dir[0] = j * 8; dir[1] = i * 8; dir[2] = k * 8; p->org[0] = org[0] + i + (rand() & 3); p->org[1] = org[1] + j + (rand() & 3); p->org[2] = org[2] + k + (rand() & 3); VectorNormalizeFast (dir); vel = 50 + (rand() & 63); VectorScale (dir, vel, p->vel); } } } } /* =============== R_RunQuakeEffect =============== */ void R_RunQuakeEffect (vec3_t org, float distance) { int i; particle_t *p; float num, num2; // int j; for (i = 0; i < 100; i++) { p = AllocParticle(); if (!p) return; p->die = cl.time + 0.3 * (rand() % 5); p->color = (rand() % 4) + ((rand() % 3) * 16) + (13 * 16) + 256 + 11; p->type = pt_quake; p->ramp = 0; num = rand() / RAND_MAX; num2 = distance * num; num = rand() / RAND_MAX; q_sincosrad(num * 2 * M_PI, &p->org[1], &p->org[0]); p->org[0] *= num2; p->org[1] *= num2; p->org[0] += org[0]; p->org[1] += org[1]; num = rand() / RAND_MAX; p->org[2] = org[2] + 15*num; p->org[2] = org[2]; num = rand() / RAND_MAX; p->vel[0] = (num * 40) - 20; num = rand() / RAND_MAX; p->vel[1] = (num * 40) - 20; num = rand() / RAND_MAX; p->vel[2] = 65*num + 80; /* for (j = 0; j < 2; j++) { p->vel[j] = dmin[j] + ((dmax[j] - dmin[j]) * num); } */ } } /* =============== R_SunStaffTrail =============== */ void R_SunStaffTrail(vec3_t source, vec3_t dest) { int i; particle_t *p; vec3_t vec, dist; float length, size; VectorSubtract(dest, source, vec); length = VectorNormalizeFast(vec); dist[0] = vec[0]; dist[1] = vec[1]; dist[2] = vec[2]; size = 10; while (length > 0) { length -= size; if ((p = AllocParticle()) == NULL) return; p->die = cl.time + 2; p->ramp = rand() & 3; p->color = ramp6[(int)(p->ramp)]; p->type = pt_spit; for (i = 0; i < 3; i++) { p->org[i] = source[i] + ((rand() % 4) - 2); } p->vel[0] = (rand() % 10) - 5; p->vel[1] = (rand() % 10) - 5; p->vel[2] = (rand() % 10); VectorAdd(source, dist, source); } } void RiderParticle (int count, vec3_t origin) { int i; particle_t *p; // float num, num2; float radius, angle; VectorCopy(origin, rider_origin); for (i = 0; i < count; i++) { p = AllocParticle(); if (!p) return; p->die = cl.time + 4; p->color = 256 + 16 + 15; p->type = pt_rd; p->ramp = 0; //num = rand() / RAND_MAX; angle = (rand() % 360) / (2 * M_PI); radius = 300 + (rand() % 256); q_sincosrad(angle, &p->org[0], &p->org[1]); p->org[0] *= radius; p->org[1] *= radius; p->org[0] += origin[0]; p->org[1] += origin[1]; p->org[2] = origin[2]; p->org[2] += (rand() & 255) - 30; p->vel[0] = (rand() & 255) - 127; p->vel[1] = (rand() & 255) - 127; p->vel[2] = (rand() & 255) - 127; } } /* =============== R_RocketTrail =============== */ void R_RocketTrail (vec3_t start, vec3_t end, int type) { vec3_t vec, dist; float len, size, lifetime; int j; particle_t *p; static int tracercount; VectorSubtract (end, start, vec); len = VectorNormalizeFast (vec); dist[0] = vec[0]; dist[1] = vec[1]; dist[2] = vec[2]; size = 1; lifetime = 2; switch (type) { case 9: // Spit break; case 8: // Ice size = 5 * 3; break; case rt_acidball: size = 5; lifetime = .8; break; case rt_grensmoke: size = 5; break; case rt_purify: size = 5; lifetime = .5; break; default: size = 3; break; } VectorScale(dist, size, dist); while (len > 0) { len -= size; p = AllocParticle(); if (!p) return; VectorClear (p->vel); p->die = cl.time + lifetime; switch (type) { case rt_grensmoke: //smoke trail for grenade p->color = 283 + (rand() & 3); p->type = pt_grensmoke; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() % 6) - 3); break; case rt_rocket_trail: // rocket trail p->ramp = rand() & 3; p->color = ramp3[(int)p->ramp]; p->type = pt_fire; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() % 6) - 3); break; case rt_smoke: // smoke smoke p->ramp = (rand() & 3) + 2; p->color = ramp3[(int)p->ramp]; p->type = pt_fire; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() % 6) - 3); break; case rt_blood: // blood //p->type = pt_slowgrav; //p->color = 138 + (rand() & 3); //for (j = 0; j < 3; j++) // p->org[j] = start[j] + ((rand() % 6) - 3); p->type = pt_darken; p->color = 136 + (rand() % 5); for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 3) - 2); len -= size; break; case rt_tracer:; case rt_tracer2:; // tracer p->die = cl.time + 0.5; p->type = pt_static; if (type == 3) p->color = 130 + (rand() & 6); // 243 + (rand() & 3); else p->color = 230 + ((tracercount & 4) << 1); tracercount++; VectorCopy (start, p->org); if (tracercount & 1) { p->vel[0] = 30 * vec[1]; p->vel[1] = 30 * -vec[0]; } else { p->vel[0] = 30 * -vec[1]; p->vel[1] = 30 * vec[0]; } break; case rt_slight_blood: // slight blood p->type = pt_slowgrav; p->color = 138 + (rand() & 3); for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() % 6) - 3); len -= size; break; case rt_bloodshot: // bloodshot trail p->type = pt_darken; p->color = 136 + (rand() & 5); for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 3) - 2); len -= size; break; case rt_voor_trail: // voor trail p->color = 9*16 + 8 + (rand() & 3); p->type = pt_static; p->die = cl.time + 0.3; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 15) - 8); break; case rt_fireball: // Fireball p->ramp = rand() & 3; p->color = ramp4[(int)(p->ramp)]; p->type = pt_fireball; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() % 4) - 2); p->org[2] += 2; // compensate for model p->vel[0] = (rand() % 200) - 100; p->vel[1] = (rand() % 200) - 100; p->vel[2] = (rand() % 200) - 100; break; case rt_acidball: // Acid ball p->ramp = rand() & 3; p->color = ramp10[(int)(p->ramp)]; p->type = pt_acidball; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() & 3) - 2); p->org[2] += 2; // compensate for model p->vel[0] = (rand() % 20) - 10; p->vel[1] = (rand() % 20) - 10; p->vel[2] = (rand() % 20) - 10; break; case rt_ice: // Ice p->ramp = rand() & 3; p->color = ramp5[(int)(p->ramp)]; p->type = pt_ice; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() % 4) - 2); p->org[2] += 2; // compensate for model p->vel[0] = (rand() % 16) - 8; p->vel[1] = (rand() % 16) - 8; p->vel[2] = (rand() % 20) - 40; break; case rt_spit: // Spit p->ramp = rand() & 3; p->color = ramp6[(int)(p->ramp)]; p->type = pt_spit; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() % 4) - 2); p->org[2] += 2; // compensate for model p->vel[0] = (rand() % 10) - 5; p->vel[1] = (rand() % 10) - 5; p->vel[2] = (rand() % 10); break; case rt_spell: // Spell p->ramp = rand() & 3; p->color = ramp6[(int)(p->ramp)]; p->type = pt_spell; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() % 4) - 2); p->vel[0] = (rand() % 10) - 5; p->vel[1] = (rand() % 10) - 5; p->vel[2] = (rand() % 10); p->vel[0] = vec[0] * -10; p->vel[1] = vec[1] * -10; p->vel[2] = vec[2] * -10; break; case rt_vorpal: // vorpal missile p->type = pt_vorpal; p->color = 44 + (rand() & 3) + 256; for (j = 0; j < 2; j++) p->org[j] = start[j] + ((rand() % 48) - 24); p->org[2] = start[2] + ((rand() % 16) - 8); break; case rt_setstaff: // set staff p->type = pt_setstaff; p->color = ramp9[0]; p->ramp = rand() & 3; for (j = 0; j < 2; j++) p->org[j] = start[j] + ((rand() % 6) - 3); p->org[2] = start[2] + ((rand() % 10) - 5); p->vel[0] = (rand() % 8) - 4; p->vel[1] = (rand() % 8) - 4; break; case rt_purify: // purifier p->type = pt_setstaff; p->color = ramp9[0]; p->ramp = rand() & 3; for (j = 0; j < 3; j++) p->org[j] = start[j] + ((rand() % 3) - 1); // p->org[2] = start[2] + ((rand() % 10) - 5); // p->vel[0] = (rand() % 8) - 4; // p->vel[1] = (rand() % 8) - 4; p->vel[0] = 0; p->vel[1] = 0; break; case rt_magicmissile: // magic missile p->type = pt_magicmissile; p->color = 148 + (rand() & 11); p->ramp = rand() & 3; for (j = 0; j < 2; j++) p->org[j] = start[j] + ((rand() % 48) - 24); p->org[2] = start[2] + ((rand() % 48) - 24); p->vel[2] = -((rand() % 16) + 8); break; case rt_boneshard: // bone shard p->type = pt_boneshard; p->color = 368 + (rand() & 16); for (j = 0; j < 2; j++) p->org[j] = start[j] + ((rand() % 48) - 24); p->org[2] = start[2] + ((rand() % 48) - 24); p->vel[2] = -((rand() % 16) + 8); break; case rt_scarab: // scarab staff p->type = pt_scarab; p->color = 250 + (rand() & 4); for (j = 0; j < 3; j++) p->org[j] = start[j] + (rand() % 7); p->vel[2] = -(rand() % 8); break; } VectorAdd (start, dist, start); } } /* =============== R_RainEffect =============== */ void R_RainEffect (vec3_t org, vec3_t e_size, int x_dir, int y_dir, int color, int count) { int i, holdint; particle_t *p; float z_time; for (i = 0; i < count; i++) { p = AllocParticle(); if (!p) return; p->vel[0] = x_dir; // X and Y motion p->vel[1] = y_dir; p->vel[2] = -(rand() % 956); if (p->vel[2] > -256) { p->vel[2] += -256; } z_time = -(e_size[2]/p->vel[2]); p->die = cl.time + z_time; p->color = color; p->ramp = rand() & 3; p->type = pt_rain; holdint = e_size[0]; p->org[0] = org[0] + (rand() % holdint); holdint = e_size[1]; p->org[1] = org[1] + (rand() % holdint); p->org[2] = org[2]; } } /* =============== R_RainEffect2 =============== */ void R_RainEffect2 (vec3_t org, vec3_t e_size, int x_dir, int y_dir, int color, int count) { int i, holdint; particle_t *p; float z_time; for (i = 0; i < count; i++) { p = AllocParticle(); if (!p) return; p->vel[0] = x_dir; // X and Y motion p->vel[1] = y_dir; p->vel[2] = -(rand() % 500); if (p->vel[2] > -128) { p->vel[2] += -128; } z_time = -(e_size[2]/p->vel[2]); p->die = cl.time + z_time; p->color = color; p->ramp = rand() & 3; p->type = pt_rain; holdint = e_size[0]; p->org[0] = org[0] + (rand() % holdint); holdint = e_size[1]; p->org[1] = org[1] + (rand() % holdint); p->org[2] = org[2] + (rand() % 16); } } /* =============== R_ColoredParticleExplosion =============== */ void R_ColoredParticleExplosion (vec3_t org, int color, int radius, int counter) { int i, j; particle_t *p; for (i = 0; i < counter; i++) { p = AllocParticle(); if (!p) return; p->die = cl.time + 3; p->color = color; p->ramp = rand() & 3; if (i & 1) { p->type = pt_c_explode; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() % (radius * 2)) - radius); p->vel[j] = (rand() % 512) - 256; } } else { p->type = pt_c_explode2; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((rand() % (radius * 2)) - radius); p->vel[j] = (rand() % 512) - 256; } } } } #ifdef GLQUAKE static qboolean alphaTestEnabled; static vec3_t up, right; #endif static void R_RenderParticle (particle_t *p) { #ifdef GLQUAKE int color; float scale; unsigned char *at; unsigned char theAlpha; /* hexenworld progs seems to have abandoned particleexplosion() * builtin usage, and added the following bounds check instead. * disabling: clamping the color instead (see below.) -- O.S. if (p->color < 0 || p->color > 511) { Con_Printf("Invalid color for particle type %d\n",(int)p->type); return; } */ // hack a scale up to keep particles from disapearing scale = (p->org[0] - r_origin[0])*vpn[0] + (p->org[1] - r_origin[1])*vpn[1] + (p->org[2] - r_origin[2])*vpn[2]; if (scale < 20) scale = 1; else scale = 1 + scale * 0.004; /* clamp color to 0-511: particle->type 10 and 11 (pt_c_explode * and pt_c_explode2, e.g. Crusader's ice particles hitting a * wall) lead to negative values, because R_UpdateParticles () * decrements their color against time. * (Crusader ice particles seem to have changed in HW, but..) */ color = ((int)p->color) & 0x01ff; if (color < 256) { // glColor3ubv_fp ((byte *)&d_8to24table[color]); at = (byte *)&d_8to24table[color]; if (p->type == pt_fire) theAlpha = 255 * (6 - p->ramp) / 6; // theAlpha = 192; // else if (p->type == pt_explode || p->type == pt_explode2) // theAlpha = 255 * (8 - p->ramp)/8; else theAlpha = 255; glColor4ub_fp (*at, *(at + 1), *(at + 2), theAlpha); } else { glColor4ubv_fp ((byte *)&d_8to24TranslucentTable[color-256]); } glTexCoord2f_fp (0,0); glVertex3fv_fp (p->org); glTexCoord2f_fp (1,0); glVertex3f_fp (p->org[0] + up[0]*scale, p->org[1] + up[1]*scale, p->org[2] + up[2]*scale); glTexCoord2f_fp (0,1); glVertex3f_fp (p->org[0] + right[0]*scale, p->org[1] + right[1]*scale, p->org[2] + right[2]*scale); #else /* if (p->color < 0 || p->color > 511) { Con_Printf("Invalid color for particle type %d\n",(int)p->type); return; } */ D_DrawParticle (p); #endif } /* =============== R_DrawParticles =============== */ void R_DrawParticles (void) { particle_t *p, *kill, temp_p; float grav, grav2, percent; int i; float time2, time3, time4; float time1; float dvel; float frametime; float vel0, vel1, vel2; vec3_t diff; #ifdef GLQUAKE GL_Bind(particletexture); alphaTestEnabled = glIsEnabled_fp(GL_ALPHA_TEST); if (alphaTestEnabled) glDisable_fp(GL_ALPHA_TEST); glEnable_fp (GL_BLEND); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glBegin_fp (GL_TRIANGLES); VectorScale (vup, 1.5, up); VectorScale (vright, 1.5, right); #else D_StartParticles (); VectorScale (vright, xscaleshrink, r_pright); VectorScale (vup, yscaleshrink, r_pup); VectorCopy (vpn, r_ppn); #endif frametime = host_frametime; time4 = frametime * 20; time3 = frametime * 15; time2 = frametime * 10; // 15; time1 = frametime * 5; grav = frametime * movevars.gravity * 0.05; grav2 = frametime * movevars.gravity * 0.025; dvel = 4 * frametime; percent = (frametime / HX_FRAME_TIME); /* temp_p.org[0] = -1013; temp_p.org[1] = -1863; temp_p.org[2] = 50; temp_p.color = 31; R_RenderParticle(&temp_p); */ for ( ;; ) { kill = active_particles; if (kill && kill->die < cl.time) { active_particles = kill->next; kill->next = free_particles; free_particles = kill; continue; } break; } for (p = active_particles ; p ; p = p->next) { for ( ;; ) { kill = p->next; if (kill && kill->die < cl.time) { p->next = kill->next; kill->next = free_particles; free_particles = kill; continue; } break; } if (p->type == pt_rain) { temp_p = *p; vel0 = temp_p.vel[0]*.001; vel1 = temp_p.vel[1]*.001; vel2 = temp_p.vel[2]*.001; for (i = 0; i < 4; i++) { R_RenderParticle(&temp_p); temp_p.org[0] += vel0; temp_p.org[1] += vel1; temp_p.org[2] += vel2; } } R_RenderParticle(p); p->org[0] += p->vel[0] * frametime; p->org[1] += p->vel[1] * frametime; p->org[2] += p->vel[2] * frametime; switch (p->type) { case pt_static: break; case pt_fire: p->ramp += time1; if ((int)p->ramp >= 6) p->die = -1; else p->color = ramp3[(int)p->ramp]; p->vel[2] += grav; break; case pt_explode: p->ramp += time2; if ((int)p->ramp >= 8) p->die = -1; else p->color = ramp1[(int)p->ramp]; for (i = 0; i < 3; i++) p->vel[i] += p->vel[i]*dvel; p->vel[2] -= grav; break; case pt_explode2: p->ramp += time3; if ((int)p->ramp >= 8) p->die = -1; else p->color = ramp2[(int)p->ramp]; for (i = 0; i < 3; i++) p->vel[i] -= p->vel[i] * frametime; p->vel[2] -= grav; break; case pt_c_explode: p->ramp += time2; if ((int)p->ramp >= 8 || p->color <= 0) p->die = -1; else if (time2) p->color--; for (i = 0; i < 3; i++) p->vel[i] += p->vel[i]*dvel; p->vel[2] -= grav; break; case pt_c_explode2: p->ramp += time3; if ((int)p->ramp >= 8 || p->color <= 1) p->die = -1; else if (time3) p->color -= 2; for (i = 0; i < 3; i++) p->vel[i] -= p->vel[i] * frametime; p->vel[2] -= grav; break; case pt_blob: for (i = 0; i < 3; i++) p->vel[i] += p->vel[i]*dvel; p->vel[2] -= grav; break; case pt_blob2: for (i = 0; i < 2; i++) p->vel[i] -= p->vel[i]*dvel; p->vel[2] -= grav; break; case pt_grav: #ifdef QUAKE2 p->vel[2] -= grav * 20; break; #endif case pt_slowgrav: p->vel[2] -= grav; break; case pt_grensmoke: p->vel[0] += time3 * ((rand() % 3) - 1); p->vel[1] += time3 * ((rand() % 3) - 1); p->vel[2] += time3 * ((rand() % 3) - 1); break; case pt_fastgrav: p->vel[2] -= grav * 4; break; case pt_rain: break; case pt_fireball: p->ramp += time3; if ((int)p->ramp >= 16) p->die = -1; else p->color = ramp4[(int)p->ramp]; break; case pt_acidball: p->ramp += time4 * 1.4; if ((int)p->ramp >= 23) p->die = -1; else if ((int)p->ramp >= 15) p->color = ramp11[(int)p->ramp - 15]; else p->color = ramp10[(int)p->ramp]; p->vel[2] -= grav; break; case pt_spit: p->ramp += time3; if ((int)p->ramp >= 16) p->die = -1; else p->color = ramp6[(int)p->ramp]; // p->vel[2] += grav * 2; break; case pt_ice: p->ramp += time4; if ((int)p->ramp > 15) p->die = -1; else p->color = ramp5[(int)p->ramp]; p->vel[2] -= grav; break; case pt_spell: p->ramp += time2; if ((int)p->ramp >= 16) p->die = -1; else p->color = ramp7[(int)p->ramp]; // p->vel[2] += grav * 2; break; case pt_test: p->vel[2] += 1.3; p->ramp += time3; if ((int)p->ramp >= 13 || ((int)p->ramp > 10 && p->vel[2] < 20) ) p->die = -1; else p->color = ramp8[(int)p->ramp]; break; case pt_quake: p->vel[0] *= 1.05; p->vel[1] *= 1.05; p->vel[2] -= grav * 4; if (p->color < 160 && p->color > 143) p->color = 152 + 7 * ((p->die - cl.time) * 2.0); if (p->color < 144 && p->color > 127) p->color = 136 + 7 * ((p->die - cl.time) * 2.0); break; case pt_rd: if (!frametime) break; p->ramp += percent; if ((int)p->ramp > 50) { p->ramp = 50; p->die = -1; } p->color = 256 + 16 + 16 - (p->ramp / (50/16)); VectorSubtract(rider_origin, p->org, diff); /* p->org[0] += diff[0] * p->ramp / 80; p->org[1] += diff[1] * p->ramp / 80; p->org[2] += diff[2] * p->ramp / 80; */ vel0 = 1 / (51 - p->ramp); p->org[0] += diff[0] * vel0; p->org[1] += diff[1] * vel0; p->org[2] += diff[2] * vel0; break; case pt_vorpal: --p->color; if ((int)p->color <= 37 + 256) p->die = -1; break; case pt_setstaff: p->ramp += time1; if ((int)p->ramp >= 16) p->die = -1; else p->color = ramp9[(int)p->ramp]; p->vel[0] *= 1.08 * percent; p->vel[1] *= 1.08 * percent; p->vel[2] -= grav2; break; case pt_redfire: p->ramp += frametime * 3; if ((int)p->ramp >= 8) p->die = -1; else p->color = ramp12[(int)p->ramp] + 256; p->vel[0] *= .9; p->vel[1] *= .9; p->vel[2] += grav/2; break; case pt_bluestep: p->ramp += frametime * 8; if ((int)p->ramp >= 16) p->die = -1; else p->color = ramp13[(int)p->ramp] + 256; p->vel[0] *= .9; p->vel[1] *= .9; p->vel[2] += grav; break; case pt_magicmissile: --p->color; if ((int)p->color < 149) p->color = 149; p->ramp += time1; if ((int)p->ramp > 16) p->die = -1; break; case pt_boneshard: --p->color; if ((int)p->color < 368) p->die = -1; break; case pt_scarab: --p->color; if ((int)p->color < 250) p->die = -1; break; case pt_darken: { int colindex; p->vel[2] -= grav * 2; // Also gravity if (rand() & 1) --p->color; colindex = 0; while (colindex < 224) { if (colindex == 192 || colindex == 200) colindex += 8; else colindex += 16; if (p->color == colindex) p->die = -1; } } break; } } #ifdef GLQUAKE glEnd_fp (); glDisable_fp (GL_BLEND); if (alphaTestEnabled) glEnable_fp(GL_ALPHA_TEST); glTexEnvf_fp(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); #else D_EndParticles (); #endif } engine/hexenworld/client/r_part.h000066400000000000000000000052611444734033100174270ustar00rootroot00000000000000/* r_part.h -- exported functions from r_part.c * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __R_PART_H #define __R_PART_H void R_DrawParticles (void); void R_InitParticles (void); void R_ClearParticles (void); void R_ParseParticleEffect (void); void R_ParseParticleEffect2 (void); void R_ParseParticleEffect3 (void); void R_ParseParticleEffect4 (void); void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count); void R_RunParticleEffect2 (vec3_t org, vec3_t dmin, vec3_t dmax, int color, ptype_t effect, int count); void R_RunParticleEffect3 (vec3_t org, vec3_t box, int color, ptype_t effect, int count); void R_RunParticleEffect4 (vec3_t org, float radius, int color, ptype_t effect, int count); /* for ptype_t, d_iface.h or glquake.h must be included before. */ void R_ParticleExplosion (vec3_t org); void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength); void R_ColoredParticleExplosion (vec3_t org, int color, int radius, int counter); void R_BlobExplosion (vec3_t org); void R_TargetBallEffect (vec3_t org); void R_RocketTrail (vec3_t start, vec3_t end, int type); void R_SunStaffTrail (vec3_t source, vec3_t dest); void R_LavaSplash (vec3_t org); void R_SplashParticleEffect (vec3_t org, float radius, int color, ptype_t effect, int count); void R_TeleportSplash (vec3_t org); void R_ParseRainEffect (void); void R_RainEffect (vec3_t org, vec3_t e_size, int x_dir, int y_dir, int color, int count); void R_RainEffect2(vec3_t org, vec3_t e_size, int x_dir, int y_dir, int color, int count); void R_RunQuakeEffect (vec3_t org, float distance); void RiderParticle (int count, vec3_t origin); void R_DarkFieldParticles (entity_t *ent); void R_BrightFieldSource (vec3_t org); void R_SuccubusInvincibleParticles (entity_t *ent); void R_EntityParticles (entity_t *ent); /* * NOTES: R_EntityParticles, R_ParticleExplosion2 * and R_BlobExplosion actually are not used. */ #endif /* __R_PART_H */ engine/hexenworld/client/render.h000066400000000000000000000105241444734033100174150ustar00rootroot00000000000000/* * refresh.h -- public interface to refresh functions * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef RENDER_H_ #define RENDER_H_ #define TOP_RANGE 16 // soldier uniform colors #define BOTTOM_RANGE 96 //============================================================================= typedef struct efrag_s { struct mleaf_s *leaf; struct efrag_s *leafnext; struct entity_s *entity; struct efrag_s *entnext; } efrag_t; typedef struct entity_s { int keynum; // for matching entities in different frames vec3_t origin; vec3_t angles; vec3_t angleAdd; // For clientside rotation stuff struct qmodel_s *model; // NULL = no model int frame; byte *colormap, *sourcecolormap; byte colorshade; int skinnum; // for Alias models int scale; // for Alias models int drawflags; // for Alias models int abslight; // for Alias models struct player_info_s *scoreboard; // identify player float syncbase; struct efrag_s *efrag; // linked list of efrags (FIXME) int visframe; // last frame this entity was // found in an active leaf // only used for static objects int dlightframe; // dynamic lighting int dlightbits; // FIXME: could turn these into a union int trivial_accept; struct mnode_s *topnode; // for bmodels, first world node // that splits bmodel, or NULL if // not split } entity_t; // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct { vrect_t vrect; // subwindow in video for refresh // FIXME: not need vrect next field here? vrect_t aliasvrect; // scaled Alias version int vrectright, vrectbottom; // right & bottom screen coords int aliasvrectright, aliasvrectbottom; // scaled Alias versions float vrectrightedge; // rightmost right edge we care about, // for use in edge list float fvrectx, fvrecty; // for floating-point compares float fvrectx_adj, fvrecty_adj; // left and top edges, for clamping int vrect_x_adj_shift20; // (vrect.x + 0.5 - epsilon) << 20 int vrectright_adj_shift20; // (vrectright + 0.5 - epsilon) << 20 float fvrectright_adj, fvrectbottom_adj; // right and bottom edges, for clamping float fvrectright; // rightmost edge, for Alias clamping float fvrectbottom; // bottommost edge, for Alias clamping float horizontalFieldOfView; // at Z = 1.0, this many X is visible // 2.0 = 90 degrees float xOrigin; // should probably always be 0.5 float yOrigin; // between be around 0.3 to 0.5 vec3_t vieworg; vec3_t viewangles; float fov_x, fov_y; int ambientlight; } refdef_t; // // refresh // ASM_LINKAGE_BEGIN extern refdef_t r_refdef; extern vec3_t r_origin, vpn, vright, vup; ASM_LINKAGE_END extern struct texture_s *r_notexture_mip; extern entity_t r_worldentity; void R_Init (void); void R_InitTextures (void); void R_InitEfrags (void); void R_RenderView (void); // must set r_refdef first void R_ViewChanged (float aspect); // called whenever r_refdef or vid change void R_NewMap (void); void R_InitSky (struct texture_s *mt); // called at level load void R_DrawName (vec3_t origin, const char *name, int siegestatus); void R_PushDlights (void); void R_AddEfrags (entity_t *ent); void R_RemoveEfrags (entity_t *ent); // // surface cache related // extern qboolean r_cache_thrash; // set if thrashing the surface cache int D_SurfaceCacheForRes (int width, int height); void D_FlushCaches (void); void D_DeleteSurfaceCache (void); void D_InitCaches (void *buffer, int size); void R_SetVrect (vrect_t *pvrect, vrect_t *pvrectin, int lineadj); #endif /* RENDER_H_ */ engine/hexenworld/client/sbar.c000066400000000000000000001315441444734033100170660ustar00rootroot00000000000000/* sbar.c -- Hexen II status bar * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "r_shared.h" // MACROS ------------------------------------------------------------------ #define STAT_MINUS 10 /* num frame for '-' stats digit */ #define BAR_TOP_HEIGHT 46.0 #define BAR_BOTTOM_HEIGHT 98.0 #define BAR_TOTAL_HEIGHT (BAR_TOP_HEIGHT+BAR_BOTTOM_HEIGHT) #define BAR_BUMP_HEIGHT 23.0 #define INVENTORY_DISPLAY_TIME 4 #define RING_FLIGHT 1 #define RING_WATER 2 #define RING_REGENERATION 4 #define RING_TURNING 8 /* Max number of inventory icons to display at once */ #define INV_MAX_ICON 6 /* artifact counts are relative to the cnt_torch in entvars_t struct: */ #define Inv_GetCount(artifact_num) (int)(&cl.v.cnt_torch)[(artifact_num)] // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- void Sbar_DeathmatchOverlay(void); // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- #if 0 /* not used in hexenworld */ static void Sbar_PuzzlePieceOverlay(void); #endif static void Sbar_SmallDeathmatchOverlay(void); static void Sbar_DrawPic(int x, int y, qpic_t *pic); static void Sbar_DrawSubPic(int x, int y, int h, qpic_t *pic); static void Sbar_DrawTransPic(int x, int y, qpic_t *pic); static int Sbar_itoa(int num, char *buf); static void Sbar_DrawNum(int x, int y, int number, int digits); static void Sbar_SortFrags (qboolean includespec); #if 0 /* all uses are commented out */ static void Sbar_DrawCharacter(int x, int y, int num); static void Sbar_DrawSmallCharacter(int x, int y, int num); static void Sbar_DrawString(int x, int y, const char *str); #endif static void Sbar_DrawRedString (int cx, int cy, const char *str); static void Sbar_DrawSmallString(int x, int y, const char *str); static void DrawBarArtifactNumber(int x, int y, int number); static void DrawFullScreenInfo(void); static void DrawLowerBar(void); static void DrawActiveRings(void); static void DrawActiveArtifacts(void); static int CalcAC(void); static void DrawBarArtifactIcon(int x, int y, int artifact); static qboolean SetChainPosition(float health, float maxHealth, qboolean immediate); static void ShowInfoDown_f(void); static void ShowInfoUp_f(void); static void ShowDMDown_f(void); static void ShowDMUp_f(void); static void ShowNamesDown_f(void); static void ShowNamesUp_f(void); static void ToggleDM_f(void); static void InvLeft_f(void); static void InvRight_f(void); static void InvUse_f(void); static void InvDrop_f(void); static void InvOff_f(void); static void DrawArtifactInventory(void); // PUBLIC DATA DEFINITIONS ------------------------------------------------- int sb_lines; // scan lines to draw // PRIVATE DATA DEFINITIONS ------------------------------------------------ static int sb_updates; // if >= vid.numpages, no update needed static float BarHeight; static float BarTargetHeight; static cvar_t BarSpeed = {"barspeed", "5", CVAR_NONE}; static cvar_t DMMode = {"dm_mode", "1", CVAR_ARCHIVE}; static cvar_t sbtrans = {"sbtrans", "0", CVAR_ARCHIVE}; static cvar_t dmtrans = {"dmtrans", "0", CVAR_ARCHIVE}; static qpic_t *sb_nums[11]; static qpic_t *sb_colon, *sb_slash; static int fragsort[MAX_CLIENTS]; static int scoreboardlines; static float ChainPosition = 0; static qboolean sb_ShowInfo; static qboolean sb_ShowDM; static qboolean sb_ShowNames; static qboolean inv_flg; // true - show inventory interface static float InventoryHideTime; static const char *PlayerClassNames[MAX_PLAYER_CLASS] = { "Paladin", "Crusader", "Necromancer", "Assassin", "Succubus", "Dwarf" }; static const int AmuletAC[MAX_PLAYER_CLASS] = { 8, // Paladin 4, // Crusader 2, // Necromancer 6, // Assassin 6, // Demoness 0 // Dwarf }; static const int BracerAC[MAX_PLAYER_CLASS] = { 6, // Paladin 8, // Crusader 4, // Necromancer 2, // Assassin 2, // Demoness 10 // Dwarf }; static const int BreastplateAC[MAX_PLAYER_CLASS] = { 2, // Paladin 6, // Crusader 8, // Necromancer 4, // Assassin 4, // Demoness 12 // Dwarf }; static const int HelmetAC[MAX_PLAYER_CLASS] = { 4, // Paladin 2, // Crusader 6, // Necromancer 8, // Assassin 8, // Demoness 10 // Dwarf }; static const int AbilityLineIndex[MAX_PLAYER_CLASS] = { 400, // Paladin 402, // Crusader 404, // Necromancer 406, // Assassin 589, // Demoness // was 590, should be an accidental +1 593 // Dwarf // was 594, ditto }; // CODE -------------------------------------------------------------------- //========================================================================== // // Sbar_Init // //========================================================================== void Sbar_Init(void) { int i; for (i = 0; i < 10; i++) { sb_nums[i] = Draw_PicFromWad(va("num_%i",i)); } sb_nums[10] = Draw_PicFromWad("num_minus"); sb_colon = Draw_PicFromWad("num_colon"); sb_slash = Draw_PicFromWad("num_slash"); if (draw_reinit) return; Cmd_AddCommand("+showinfo", ShowInfoDown_f); Cmd_AddCommand("-showinfo", ShowInfoUp_f); Cmd_AddCommand("+showdm", ShowDMDown_f); Cmd_AddCommand("-showdm", ShowDMUp_f); Cmd_AddCommand("+shownames", ShowNamesDown_f); Cmd_AddCommand("-shownames", ShowNamesUp_f); Cmd_AddCommand("invleft", InvLeft_f); Cmd_AddCommand("invright", InvRight_f); Cmd_AddCommand("invuse", InvUse_f); Cmd_AddCommand("invdrop", InvDrop_f); Cmd_AddCommand("invoff", InvOff_f); Cmd_AddCommand("toggle_dm", ToggleDM_f); Cvar_RegisterVariable(&DMMode); Cvar_RegisterVariable(&sbtrans); Cvar_RegisterVariable(&dmtrans); Cvar_RegisterVariable(&BarSpeed); BarHeight = BarTargetHeight = BAR_TOP_HEIGHT; } static void SB_PlacePlayerNames(void) { int i, j; entity_t *e; for (j = 0; j < cl_numvisedicts; j++) { e = &cl_visedicts[j]; i = e->scoreboard - cl.players; if (i < 0 || i >= MAX_CLIENTS) continue; if (!(cl.PIV & (1<drawflags == 5 && cl.v.playerclass != CLASS_DWARF) continue; */ if (cl.players[i].shownames_off) continue; if (!cl_siege) { R_DrawName(e->origin, cl.players[i].name, -1); } else if (cl.players[i].siege_team == ST_ATTACKER) { if (i == cl_keyholder) R_DrawName(e->origin, cl.players[i].name, 10); else R_DrawName(e->origin, cl.players[i].name, 0); } else if (cl.players[i].siege_team == ST_DEFENDER) { if (i == cl_keyholder && i == cl_doc) R_DrawName(e->origin, cl.players[i].name, 12); else if (i == cl_keyholder) R_DrawName(e->origin, cl.players[i].name, 11); else if (i == cl_doc) R_DrawName(e->origin, cl.players[i].name, 2); else R_DrawName(e->origin, cl.players[i].name, 1); } else { R_DrawName(e->origin, cl.players[i].name, 3); } } } //========================================================================== // // Sbar_Draw // //========================================================================== void Sbar_Draw(void) { float delta; char tempStr[80]; int mana, maxMana; int y; if (sb_ShowNames) { SB_PlacePlayerNames(); } if (scr_con_current == vid.height) // console is full screen return; if (cl.spectator) { if (sb_ShowDM) { //rjr if (cl.gametype == GAME_DEATHMATCH) Sbar_DeathmatchOverlay(); //rjr else //rjr Sbar_PuzzlePieceOverlay(); } else if (DMMode.integer) { Sbar_SmallDeathmatchOverlay(); } return; } DrawArtifactInventory(); DrawActiveRings(); DrawActiveArtifacts(); trans_level = 0; if (sb_ShowDM) { //rjr if (cl.gametype == GAME_DEATHMATCH) Sbar_DeathmatchOverlay(); //rjr else //rjr Sbar_PuzzlePieceOverlay(); } else if (DMMode.integer) { Sbar_SmallDeathmatchOverlay(); } trans_level = sbtrans.integer; if (trans_level < 0 || trans_level > 2) trans_level = 0; if (BarHeight < BarTargetHeight) { delta = ((BarTargetHeight-BarHeight)*BarSpeed.value) * host_frametime; if (delta < 0.5) delta = 0.5; BarHeight += delta; if (BarHeight > BarTargetHeight) BarHeight = BarTargetHeight; if (scr_viewsize.integer < 100) scr_fullupdate = 0; } else if (BarHeight > BarTargetHeight) { delta = ((BarHeight-BarTargetHeight)*BarSpeed.value) * host_frametime; if (delta < 0.5) delta = 0.5; BarHeight -= delta; if (BarHeight < BarTargetHeight) BarHeight = BarTargetHeight; if (scr_viewsize.integer < 100) scr_fullupdate = 0; } Sbar_DrawTransPic(0, -23, Draw_CachePic("gfx/topbumpl.lmp")); Sbar_DrawTransPic(138, -8, Draw_CachePic("gfx/topbumpm.lmp")); Sbar_DrawTransPic(269, -23, Draw_CachePic("gfx/topbumpr.lmp")); if (BarHeight > BAR_TOP_HEIGHT || m_state != m_none || scr_viewsize.integer > 100) sb_updates = 0; #ifndef GLQUAKE if (sb_updates >= vid.numpages) return; #endif // if (BarHeight == BarTargetHeight) // return; scr_copyeverything = 1; sb_updates++; if (BarHeight < 0 && scr_viewsize.integer <= 120) { SetChainPosition(cl.v.health, cl.v.max_health, true); DrawFullScreenInfo(); return; } Sbar_DrawPic(0, 0, Draw_CachePic("gfx/topbar1.lmp")); Sbar_DrawPic(160, 0, Draw_CachePic("gfx/topbar2.lmp")); //Sbar_DrawTransPic(0, -23, Draw_CachePic("gfx/topbumpl.lmp")); //Sbar_DrawTransPic(138, -8, Draw_CachePic("gfx/topbumpm.lmp")); //Sbar_DrawTransPic(269, -23, Draw_CachePic("gfx/topbumpr.lmp")); maxMana = (int)cl.v.max_mana; // Blue mana mana = (int)cl.v.bluemana; if (mana < 0) mana = 0; else if (mana > maxMana) mana = maxMana; sprintf(tempStr, "%03d", mana); Sbar_DrawSmallString(201, 22, tempStr); if (mana) { y = (int)((mana*18.0)/(float)maxMana+0.5); Sbar_DrawSubPic(190, 26-y, y+1, Draw_CachePic("gfx/bmana.lmp")); Sbar_DrawPic(190, 27, Draw_CachePic("gfx/bmanacov.lmp")); } // Green mana mana = (int)cl.v.greenmana; if (mana < 0) mana = 0; else if (mana > maxMana) mana = maxMana; sprintf(tempStr, "%03d", mana); Sbar_DrawSmallString(243, 22, tempStr); if (mana) { y = (int)((mana*18.0)/(float)maxMana+0.5); Sbar_DrawSubPic(232, 26-y, y+1, Draw_CachePic("gfx/gmana.lmp")); Sbar_DrawPic(232, 27, Draw_CachePic("gfx/gmanacov.lmp")); } // HP if (cl.v.health < -99) Sbar_DrawNum(58, 14, -99, 3); else Sbar_DrawNum(58, 14, cl.v.health, 3); if (SetChainPosition(cl.v.health, cl.v.max_health, false)) sb_updates = 0; Sbar_DrawTransPic(45+((int)ChainPosition&7), 38, Draw_CachePic("gfx/hpchain.lmp")); Sbar_DrawTransPic(45+(int)ChainPosition, 36, Draw_CachePic("gfx/hpgem.lmp")); Sbar_DrawPic(43, 36, Draw_CachePic("gfx/chnlcov.lmp")); Sbar_DrawPic(267, 36, Draw_CachePic("gfx/chnrcov.lmp")); // AC Sbar_DrawNum(105, 14, CalcAC(), 2); if (BarHeight > BAR_TOP_HEIGHT) { DrawLowerBar(); } // Current inventory item if (cl.inv_selected >= 0) { DrawBarArtifactIcon(144, 3, cl.inv_order[cl.inv_selected]); // Sbar_DrawTransPic(144, 3, Draw_CachePic(va("gfx/arti%02d.lmp", cl.inv_order[cl.inv_selected]))); } } //========================================================================== // // SetChainPosition // //========================================================================== static qboolean SetChainPosition(float health, float maxHealth, qboolean immediate) { float delta; float chainTargetPosition; if (health < 0) health = 0; else if (health > maxHealth) health = maxHealth; chainTargetPosition = (health*195)/maxHealth; if (fabs(ChainPosition-chainTargetPosition) < 0.1) return false; if (immediate) ChainPosition = chainTargetPosition; if (ChainPosition < chainTargetPosition) { delta = ((chainTargetPosition-ChainPosition)*5)*host_frametime; if (delta < 0.5) delta = 0.5; ChainPosition += delta; if (ChainPosition > chainTargetPosition) ChainPosition = chainTargetPosition; } else if (ChainPosition > chainTargetPosition) { delta = ((ChainPosition-chainTargetPosition)*5)*host_frametime; if (delta < 0.5) delta = 0.5; ChainPosition -= delta; if (ChainPosition < chainTargetPosition) ChainPosition = chainTargetPosition; } return true; } //========================================================================== // // DrawFullScreenInfo // //========================================================================== static void DrawFullScreenInfo(void) { int y; int mana, maxMana; char tempStr[80]; y = BarHeight-37; Sbar_DrawPic(3, y, Draw_CachePic("gfx/bmmana.lmp")); Sbar_DrawPic(3, y+18, Draw_CachePic("gfx/gmmana.lmp")); maxMana = (int)cl.v.max_mana; // Blue mana mana = (int)cl.v.bluemana; if (mana < 0) mana = 0; else if (mana > maxMana) mana = maxMana; sprintf(tempStr, "%03d", mana); Sbar_DrawSmallString(10, y+6, tempStr); // Green mana mana = (int)cl.v.greenmana; if (mana < 0) mana = 0; else if (mana > maxMana) mana = maxMana; sprintf(tempStr, "%03d", mana); Sbar_DrawSmallString(10, y+18+6, tempStr); // HP Sbar_DrawNum(38, y+18, cl.v.health, 3); // Current inventory item if (cl.inv_selected >= 0) { DrawBarArtifactIcon(288, y+7, cl.inv_order[cl.inv_selected]); } } //========================================================================== // // DrawLowerBar // //========================================================================== static void DrawLowerBar(void) { int i; // int minutes, seconds, tens, units; char tempStr[80]; int playerClass; int piece; int ringhealth; playerClass = cl.players[cl.playernum].playerclass; if (playerClass < 1 || playerClass > MAX_PLAYER_CLASS) playerClass = 1; // Default to paladin // Backdrop Sbar_DrawPic(0, 46, Draw_CachePic("gfx/btmbar1.lmp")); Sbar_DrawPic(160, 46, Draw_CachePic("gfx/btmbar2.lmp")); /* // Game time minutes = cl.time / 60; seconds = cl.time - 60*minutes; tens = seconds / 10; units = seconds - 10*tens; sprintf(tempStr, "Time :%3i:%i%i", minutes, tens, units); Sbar_DrawSmallString(116, 114, tempStr); // Map name Sbar_DrawSmallString(10, 114, cl.levelname); */ // Stats Sbar_DrawSmallString(11, 48, PlayerClassNames[playerClass-1]); Sbar_DrawSmallString(11, 58, "int"); sprintf(tempStr, "%02d", (int)cl.v.intelligence); Sbar_DrawSmallString(33, 58, tempStr); Sbar_DrawSmallString(11, 64, "wis"); sprintf(tempStr, "%02d", (int)cl.v.wisdom); Sbar_DrawSmallString(33, 64, tempStr); Sbar_DrawSmallString(11, 70, "dex"); sprintf(tempStr, "%02d", (int)cl.v.dexterity); Sbar_DrawSmallString(33, 70, tempStr); Sbar_DrawSmallString(58, 58, "str"); sprintf(tempStr, "%02d", (int)cl.v.strength); Sbar_DrawSmallString(80, 58, tempStr); Sbar_DrawSmallString(58, 64, "lvl"); sprintf(tempStr, "%02d", (int)cl.v.level); Sbar_DrawSmallString(80, 64, tempStr); Sbar_DrawSmallString(58, 70, "exp"); sprintf(tempStr, "%06d", (int)cl.v.experience); Sbar_DrawSmallString(80, 70, tempStr); // Abilities Sbar_DrawSmallString(11, 79, "abilities"); i = AbilityLineIndex[(playerClass-1)]; if (i + 1 < host_string_count) { if (((int)cl.v.flags) & FL_SPECIAL_ABILITY1) { Sbar_DrawSmallString(8, 89, Host_GetString(i)); } if (((int)cl.v.flags) & FL_SPECIAL_ABILITY2) { Sbar_DrawSmallString(8, 96, Host_GetString(i + 1)); } } // Portrait sprintf(tempStr, "gfx/cport%d.lmp", playerClass); Sbar_DrawPic(134, 50, Draw_CachePic(tempStr)); // Armor if (cl.v.armor_helmet > 0) { Sbar_DrawPic(164, 115, Draw_CachePic("gfx/armor1.lmp")); sprintf(tempStr, "+%d", (int)cl.v.armor_helmet); Sbar_DrawSmallString(168, 136, tempStr); } if (cl.v.armor_amulet > 0) { Sbar_DrawPic(205, 115, Draw_CachePic("gfx/armor2.lmp")); sprintf(tempStr, "+%d", (int)cl.v.armor_amulet); Sbar_DrawSmallString(208, 136, tempStr); } if (cl.v.armor_breastplate > 0) { Sbar_DrawPic(246, 115, Draw_CachePic("gfx/armor3.lmp")); sprintf(tempStr, "+%d", (int)cl.v.armor_breastplate); Sbar_DrawSmallString(249, 136, tempStr); } if (cl.v.armor_bracer > 0) { Sbar_DrawPic(285, 115, Draw_CachePic("gfx/armor4.lmp")); sprintf(tempStr, "+%d", (int)cl.v.armor_bracer); Sbar_DrawSmallString(288, 136, tempStr); } // Rings if (cl.v.ring_flight > 0) { Sbar_DrawTransPic(6, 119, Draw_CachePic("gfx/ring_f.lmp")); ringhealth = (int)cl.v.ring_flight; if (ringhealth > 100) ringhealth = 100; Sbar_DrawPic(35 - (int)(26 * (ringhealth/(float)100)), 142, Draw_CachePic("gfx/ringhlth.lmp")); Sbar_DrawPic(35, 142, Draw_CachePic("gfx/rhlthcvr.lmp")); } if (cl.v.ring_water > 0) { Sbar_DrawTransPic(44, 119, Draw_CachePic("gfx/ring_w.lmp")); ringhealth = (int)cl.v.ring_water; if (ringhealth > 100) ringhealth = 100; Sbar_DrawPic(73 - (int)(26 * (ringhealth/(float)100)), 142, Draw_CachePic("gfx/ringhlth.lmp")); Sbar_DrawPic(73, 142, Draw_CachePic("gfx/rhlthcvr.lmp")); } if (cl.v.ring_turning > 0) { Sbar_DrawTransPic(81, 119, Draw_CachePic("gfx/ring_t.lmp")); ringhealth = (int)cl.v.ring_turning; if (ringhealth > 100) ringhealth = 100; Sbar_DrawPic(110 - (int)(26 * (ringhealth/(float)100)), 142, Draw_CachePic("gfx/ringhlth.lmp")); Sbar_DrawPic(110, 142, Draw_CachePic("gfx/rhlthcvr.lmp")); } if (cl.v.ring_regeneration > 0) { Sbar_DrawTransPic(119, 119, Draw_CachePic("gfx/ring_r.lmp")); ringhealth = (int)cl.v.ring_regeneration; if (ringhealth > 100) ringhealth = 100; Sbar_DrawPic(148 -(int)(26 * (ringhealth/(float)100)), 142, Draw_CachePic("gfx/ringhlth.lmp")); Sbar_DrawPic(148, 142, Draw_CachePic("gfx/rhlthcv2.lmp")); } // Puzzle pieces piece = 0; for (i = 0; i < 8; i++) { if (cl.puzzle_pieces[i][0] == 0) continue; Sbar_DrawPic(194+(piece%4)*31, piece < 4 ? 51 : 82, Draw_CachePic(va("gfx/puzzle/%s.lmp", cl.puzzle_pieces[i]))); piece++; } } //========================================================================== // // CalcAC // //========================================================================== static int CalcAC(void) { int a; int playerClass; //playerClass = cl.v.playerclass; playerClass = cl.players[cl.playernum].playerclass - 1; if (playerClass < 0 || playerClass >= MAX_PLAYER_CLASS) playerClass = 1; // Default to paladin a = 0; if (cl.v.armor_amulet > 0) { a += AmuletAC[playerClass]; a += cl.v.armor_amulet/5; } if (cl.v.armor_bracer > 0) { a += BracerAC[playerClass]; a += cl.v.armor_bracer/5; } if (cl.v.armor_breastplate > 0) { a += BreastplateAC[playerClass]; a += cl.v.armor_breastplate/5; } if (cl.v.armor_helmet > 0) { a += HelmetAC[playerClass]; a += cl.v.armor_helmet/5; } return a; } //========================================================================== // // Sbar_Changed // //========================================================================== void Sbar_Changed(void) { sb_updates = 0; // Update next frame } //========================================================================== // // Sbar_itoa // //========================================================================== static int Sbar_itoa(int num, char *buf) { char *str; int pow10; int dig; str = buf; if (num < 0) { *str++ = '-'; num = -num; } for (pow10 = 10; num >= pow10; pow10 *= 10) ; do { pow10 /= 10; dig = num / pow10; *str++ = '0' + dig; num -= dig * pow10; } while (pow10 != 1); *str = 0; return str-buf; } //========================================================================== // // Sbar_DrawNum // //========================================================================== static void Sbar_DrawNum(int x, int y, int number, int digits) { char str[12]; char *ptr; int l, frame; l = Sbar_itoa(number, str); ptr = str; if (l > digits) { ptr += (l-digits); } if (l < digits) { x += ((digits-l)*13)/2; } while (*ptr) { if (*ptr == '-') frame = STAT_MINUS; else frame = *ptr -'0'; Sbar_DrawTransPic(x, y, sb_nums[frame]); x += 13; ptr++; } } //========================================================================== // // Sbar_SortFrags // //========================================================================== #define SPEC_FRAGS -9999 static void Sbar_SortFrags (qboolean includespec) { int i, j, k; // sort by frags scoreboardlines = 0; for (i = 0; i < MAX_CLIENTS; i++) { if (cl.players[i].name[0] && (!cl.players[i].spectator || includespec)) { fragsort[scoreboardlines] = i; scoreboardlines++; if (cl.players[i].spectator) cl.players[i].frags = SPEC_FRAGS; } } for (i = 0; i < scoreboardlines; i++) { for (j = 0; j < scoreboardlines-1-i; j++) { if (cl.players[fragsort[j]].frags < cl.players[fragsort[j+1]].frags) { k = fragsort[j]; fragsort[j] = fragsort[j+1]; fragsort[j+1] = k; } } } } static void FindColor (int slot, int *color1, int *color2) { int j; int top, bottom, done; byte *sourceA, *sourceB, *colorA, *colorB; if (slot > MAX_CLIENTS) Sys_Error ("%s: slot > cl.maxclients", __thisfunc__); if (cl.players[slot].playerclass <= 0 || cl.players[slot].playerclass > MAX_PLAYER_CLASS) { *color1 = *color2 = 0; return; } top = cl.players[slot].topcolor; bottom = cl.players[slot].bottomcolor; if (top > 10) top = 0; if (bottom > 10) bottom = 0; top -= 1; bottom -= 1; colorA = playerTranslation + 256 + color_offsets[(int)cl.players[slot].playerclass-1]; colorB = colorA + 256; sourceA = colorB + 256 + (top * 256); sourceB = colorB + 256 + (bottom * 256); done = 0; for (j = 0; j < 256; j++, colorA++, colorB++, sourceA++, sourceB++) { if ((*colorA != 255) && !(done & 1)) { if (top >= 0) *color1 = *sourceA; else *color1 = *colorA; // done |= 1; } if ((*colorB != 255) && !(done & 2)) { if (bottom >= 0) *color2 = *sourceB; else *color2 = *colorB; // done |= 2; } } } //========================================================================== // // Sbar_DeathmatchOverlay // //========================================================================== void Sbar_DeathmatchOverlay(void) { qpic_t *pic; int i, k, l; int top, bottom; int x, y, f; char num[40]; player_info_t *s; float total; int minutes; if (realtime - cl.last_ping_request > 2) { cl.last_ping_request = realtime; MSG_WriteByte (&cls.netchan.message, clc_stringcmd); SZ_Print (&cls.netchan.message, "pings"); } scr_copyeverything = 1; if (scr_viewsize.integer < 100) scr_fullupdate = 0; pic = Draw_CachePic ("gfx/menu/title8.lmp"); M_DrawTransPic ((320-pic->width)/2, 0, pic); // scores Sbar_SortFrags (true); // draw the text l = scoreboardlines; x = ((vid.width - 320)>>1); //was 80 + ... y = 62; Sbar_DrawRedString (x+4, y-1, "FRAG"); Sbar_DrawRedString (x+48, y-1, "PING"); Sbar_DrawRedString (x+88, y-1, "TIME"); Sbar_DrawRedString (x+128, y-1, "NAME"); y += 12; top = bottom = 0; for (i = 0; i < l; i++) { if (y+10 >= vid.height) break; k = fragsort[i]; s = &cl.players[k]; if (!s->name[0]) continue; if (s->frags != SPEC_FRAGS) { switch (s->playerclass) {//Print playerclass next to name case 0: Sbar_DrawRedString (x+128, y, "(r )"); break; case 1: Sbar_DrawRedString (x+128, y, "(P )"); break; case 2: Sbar_DrawRedString (x+128, y, "(C )"); break; case 3: Sbar_DrawRedString (x+128, y, "(N )"); break; case 4: Sbar_DrawRedString (x+128, y, "(A )"); break; case 5: Sbar_DrawRedString (x+128, y, "(S )"); break; case 6: Sbar_DrawRedString (x+128, y, "(D )"); break; default: Sbar_DrawRedString (x+128, y, "(? )"); break; } if (cl_siege) { Sbar_DrawRedString (x+160, y, "6"); if (s->siege_team == 1) Draw_Character ( x+152 , y, 143); //shield else if (s->siege_team == 2) Draw_Character ( x+152 , y, 144); //sword else Sbar_DrawRedString ( x+152 , y, "?"); //no team if (k == cl_keyholder) Draw_Character ( x+144 , y, 145); //key else Sbar_DrawRedString (x+144, y, "-"); if (k == cl_doc) Draw_Character ( x+160 , y, 130); //crown else Sbar_DrawRedString (x+160, y, "6"); } else { Sbar_DrawRedString (x+144, y, "-"); if (!s->level) Sbar_DrawRedString (x+152, y, "01"); else if ((int)s->level < 10) { Sbar_DrawRedString (x+152, y, "0"); sprintf(num, "%1d", s->level); Sbar_DrawRedString (x+160, y, num); } else { sprintf(num, "%2d", s->level); Sbar_DrawRedString (x+152, y, num); } } // draw background FindColor (k, &top, &bottom); Draw_Fill (x+8, y, 28, 4, top); //was x to 40 Draw_Fill (x+8, y+4, 28, 4, bottom); //was x to 40 // draw number f = s->frags; sprintf (num, "%3i", f); Draw_Character (x+10 , y-1, num[0]); //was 8 Draw_Character (x+18 , y-1, num[1]); //was 16 Draw_Character (x+26 , y-1, num[2]); //was 24 if (k == cl.playernum) Draw_Character ( x, y-1, 13); //rjr if (k == sv_kingofhill) //rjr Draw_Character ( x+40 , y-1, 130); sprintf(num, "%4d", s->ping); Draw_String(x+48, y, num); if (cl.intermission) total = cl.completed_time - s->entertime; else total = realtime - s->entertime; minutes = (int)total/60; sprintf (num, "%4i", minutes); Draw_String (x+88 , y, num); // draw name if (cl_siege && s->siege_team == 2) //attacker Draw_String (x+178, y, s->name); else Sbar_DrawRedString (x+178, y, s->name); } else { sprintf(num, "%4d", s->ping); Sbar_DrawRedString (x+48, y, num); if (cl.intermission) total = cl.completed_time - s->entertime; else total = realtime - s->entertime; minutes = (int)total/60; sprintf (num, "%4i", minutes); Sbar_DrawRedString ( x+88 , y, num); // draw name Sbar_DrawRedString (x+128, y, s->name); } y += 10; } } //========================================================================== // // Sbar_PuzzlePieceOverlay // //========================================================================== #if 0 /* not used in hexenworld */ static void FindPuzzlePieceName(const char *which, char *name) { const char *str = CL_FindPuzzleString (which); if (!str) str = "Unknown"; q_strlcpy (name, str, 40); /* 40 == array size in caller */ } static void Sbar_PuzzlePieceOverlay(void) { int i, y, piece; char Name[40]; scr_copyeverything = 1; if (scr_viewsize.integer < 100) scr_fullupdate = 0; piece = 0; y = 40; for (i = 0; i < 8; i++) { if (cl.puzzle_pieces[i][0] == 0) continue; if (piece == 4) y = 40; FindPuzzlePieceName(cl.puzzle_pieces[i], Name); if (piece < 4) { M_DrawPic(10, y, Draw_CachePic(va("gfx/puzzle/%s.lmp", cl.puzzle_pieces[i]))); M_PrintWhite (45, y, Name); } else { M_DrawPic(310-32, y, Draw_CachePic(va("gfx/puzzle/%s.lmp", cl.puzzle_pieces[i]))); M_PrintWhite (310-32-3-(strlen(Name)*8), 18+y, Name); } y += 32; piece++; } } #endif /* end of unused */ //========================================================================== // // Sbar_SmallDeathmatchOverlay // //========================================================================== static void DrawTime (int x, int y, int disp_time) { int show_min, show_sec; char num[40]; show_min = disp_time; show_sec = show_min%60; show_min = (show_min - show_sec)/60; sprintf(num, "%3d", show_min); Sbar_DrawRedString ( x+8 , y, num); // Draw_Character ( x+32 , y, 58);// ":" sprintf(num, "%2d", show_sec); if (show_sec >= 10) { Sbar_DrawRedString (x+40 , y, num); } else { Sbar_DrawRedString (x+40 , y, num); Sbar_DrawRedString (x+40 , y, "0"); } } static void Sbar_SmallDeathmatchOverlay(void) { int i, k, l; int top, bottom; int x, y, f; int def_frags, att_frags; char num[40]; player_info_t *s; if (!DMMode.integer) return; if (DMMode.integer > 2) DMMode.integer = 2; if (DMMode.integer == 2 && BarHeight != BAR_TOP_HEIGHT) return; trans_level = dmtrans.integer; if (trans_level < 0 || trans_level > 2) trans_level = 0; scr_copyeverything = 1; if (scr_viewsize.integer < 100) scr_fullupdate = 0; // scores Sbar_SortFrags (false); // draw the text l = scoreboardlines; x = 10; top = bottom = 0; if (DMMode.integer == 1) { i = (vid.height - 120) / 10; if (l > i) l = i; y = 46; } //else if (DMMode.integer == 2) else { if (l > 4) l = 4; y = vid.height - BAR_TOP_HEIGHT; } if (cl_siege) { y = vid.height - BAR_TOP_HEIGHT - 24; x-=4; Draw_Character (x , y, 142); //sundial Draw_Character (x+32 , y, 58); // ":" if (cl_timelimit > cl.time + cl_server_time_offset) { DrawTime(x,y,(int)(cl_timelimit - (cl.time + cl_server_time_offset))); } else if ((int)cl.time % 2) {//odd number, draw 00:00, this will make it flash every second Sbar_DrawRedString (x+16 , y, "00"); Sbar_DrawRedString (x+40 , y, "00"); } } def_frags = att_frags = 0; for (i = 0; i < l; i++) { k = fragsort[i]; s = &cl.players[k]; if (!s->name[0]) continue; if (cl_siege) { if (s->siege_team == 1) {//defender if (s->frags > 0) def_frags += s->frags; else att_frags -= s->frags; } else if (s->siege_team == 2) {//attacker if (s->frags > 0) att_frags += s->frags; else def_frags -= s->frags; } } else { // draw background FindColor (k, &top, &bottom); Draw_Fill (x, y, 28, 4, top); Draw_Fill (x, y+4, 28, 4, bottom); // draw number f = s->frags; sprintf (num, "%3i", f); /* if (k != cl.playernum) { Draw_Character (x+2 , y-1, num[0]); Draw_Character (x+10 , y-1, num[1]); Draw_Character (x+18 , y-1, num[2]); } else { Draw_Character (x-8 , y-1, 13); Draw_Character (x+2 , y-1, num[0] + 256); Draw_Character (x+10 , y-1, num[1] + 256); Draw_Character (x+18 , y-1, num[2] + 256); } */ if (k == cl.playernum) { Draw_Character (x - 8, y-1, 13); } Draw_Character (x+2 , y-1, num[0]); Draw_Character (x+10 , y-1, num[1]); Draw_Character (x+18 , y-1, num[2]); //rjr if (k == sv_kingofhill) //rjr Draw_Character ( x+28 , y-1, 130); y += 10; } } if (cl_siege) { if (cl.playernum == cl_keyholder) Draw_Character (x , y-10, 145);//key if (cl.playernum == cl_doc) Draw_Character (x+8 , y-10, 130);//crown if ((int)cl.v.artifact_active & ARTFLAG_BOOTS) Draw_Character (x+16 , y-10, 146);//boots //Print defender losses Draw_Character ( x , y+10, 143);//shield sprintf (num, "%3u", defLosses); // sprintf (num, "%3i", att_frags); Sbar_DrawRedString ( x+8 , y+10, num); Draw_Character ( x+32 , y+10, 47);// "/" sprintf (num, "%3i", cl_fraglimit); Sbar_DrawRedString ( x+40 , y+10, num); //Print attacker losses Draw_Character ( x , y+20, 144);//sword sprintf (num, "%3u", attLosses); // sprintf (num, "%3i", def_frags); Sbar_DrawRedString ( x+8 , y+20, num); Draw_Character ( x+32 , y+20, 47);// "/" sprintf (num, "%3i", cl_fraglimit*2); Sbar_DrawRedString ( x+40 , y+20, num); } trans_level = 0; } //========================================================================== // // DrawActiveRings // //========================================================================== static int art_col; static int ring_row; static void DrawActiveRings(void) { int flag; int frame; char tempStr[24]; if (scr_con_current == vid.height) return; // console is full screen ring_row = 1; flag = (int)cl.v.rings_active; if (flag & RING_TURNING) { frame = 1 + ((int)(cl.time * 16) % 16); sprintf(tempStr, "gfx/rngtrn%d.lmp", frame); Draw_TransPic(vid.width - 50, ring_row, Draw_CachePic(tempStr)); ring_row += 33; } /* if (flag & RING_REGENERATION) { frame = 1 + ((int)(cl.time * 16) % 16); sprintf(tempStr, "gfx/rngreg%d.lmp", frame); Draw_TransPic(vid.width - 50, ring_row, Draw_CachePic(tempStr)); ring_row += 33; } */ if (flag & RING_WATER) { frame = 1 + ((int)(cl.time * 16) % 16); sprintf(tempStr, "gfx/rngwtr%d.lmp", frame); Draw_TransPic(vid.width - 50, ring_row, Draw_CachePic(tempStr)); ring_row += 33; } if (flag & RING_FLIGHT) { frame = 1 + ((int)(cl.time * 16) % 16); sprintf(tempStr, "gfx/rngfly%d.lmp", frame); Draw_TransPic(vid.width - 50, ring_row, Draw_CachePic(tempStr)); ring_row += 33; } } //========================================================================== // // DrawActiveArtifacts // //========================================================================== static void DrawActiveArtifacts(void) { int flag; static int oldflags = 0; int frame; char tempStr[24]; if (scr_con_current == vid.height) return; art_col = 50; if (ring_row != 1) art_col += 50; flag = (int)cl.v.artifact_active; if (flag & ART_TOMEOFPOWER) { frame = 1 + ((int)(cl.time * 16) % 16); sprintf(tempStr, "gfx/pwrbook%d.lmp", frame); Draw_TransPic(vid.width-art_col, 1, Draw_CachePic(tempStr)); art_col += 50; scr_topupdate = 0; } else if (oldflags & ART_TOMEOFPOWER) { scr_topupdate = 0; } if (flag & ART_HASTE) { frame = 1 + ((int)(cl.time * 16) % 16); sprintf(tempStr, "gfx/durhst%d.lmp", frame); Draw_TransPic(vid.width-art_col, 1, Draw_CachePic(tempStr)); art_col += 50; scr_topupdate = 0; } else if (oldflags & ART_HASTE) { scr_topupdate = 0; } if (flag & ART_INVINCIBILITY) { frame = 1 + ((int)(cl.time * 16) % 16); sprintf(tempStr, "gfx/durshd%d.lmp", frame); Draw_TransPic(vid.width-art_col, 1, Draw_CachePic(tempStr)); art_col += 50; scr_topupdate = 0; } else if (oldflags & ART_INVINCIBILITY) { scr_topupdate = 0; } if (scr_viewsize.integer >= 100) scr_topupdate = vid.numpages; oldflags = flag; } //============================================================================ void Inv_Update(qboolean force) { if (inv_flg || force) { // Just to be safe if (cl.inv_selected >= 0 && cl.inv_count > 0) cl.v.inventory = cl.inv_order[cl.inv_selected] + 1; else cl.v.inventory = 0; if (!force) { if (scr_viewsize.integer < 100) scr_fullupdate = 0; inv_flg = false; // Toggle menu off } if (cls.state == ca_active) { // This will cause the server to set the client's edict's inventory value MSG_WriteByte (&cls.netchan.message, clc_inv_select); MSG_WriteByte (&cls.netchan.message, cl.v.inventory); } } } //========================================================================== // // DrawBarArtifactIcon // //========================================================================== static void DrawBarArtifactIcon(int x, int y, int artifact) { int count; if (artifact < 0 || artifact >= MAX_INVENTORY) return; Sbar_DrawTransPic(x, y, Draw_CachePic(va("gfx/arti%02d.lmp", artifact))); if ((count = Inv_GetCount(artifact)) > 0) { DrawBarArtifactNumber(x+20, y+21, count); } } //========================================================================== // // DrawArtifactInventory // //========================================================================== static void DrawArtifactInventory(void) { int i; int x, y; if (InventoryHideTime < cl.time) { Inv_Update(false); return; } if (!inv_flg) return; if (!cl.inv_count) { Inv_Update(false); return; } if (BarHeight < 0) y = BarHeight-34; else y = -37; // InvLeft_f and InvRight_f scrolls the inventory as needed - S.A. for (i = 0, x = 64; i < INV_MAX_ICON; i++, x += 33) { if (i >= cl.inv_count) break; if ((cl.inv_startpos + i) % cl.inv_count == cl.inv_selected) { // Highlight icon Sbar_DrawTransPic(x+9, y-12, Draw_CachePic("gfx/artisel.lmp")); } DrawBarArtifactIcon(x, y, cl.inv_order[(cl.inv_startpos + i) % cl.inv_count]); } /* //Inv_DrawArrows (x, y); // Left arrow showing there are icons to the left if (cl.inv_startpos) Draw_Fill ( x , y - 5, 3, 1, 30); // Right arrow showing there are icons to the right if (cl.inv_startpos + INV_MAX_ICON < cl.inv_count) Draw_Fill ( x + 200, y - 5 , 3, 1, 30); */ } //========================================================================== // // ShowDMDown_f // //========================================================================== static void ShowDMDown_f(void) { if (sb_ShowDM) return; sb_ShowDM = true; } //========================================================================== // // ShowDMUp_f // //========================================================================== static void ShowDMUp_f(void) { sb_ShowDM = false; } //========================================================================== // // ShowNamesDown_f // //========================================================================== static void ShowNamesDown_f(void) { if (sb_ShowNames) return; sb_ShowNames = true; } //========================================================================== // // ShowNamesUp_f // //========================================================================== static void ShowNamesUp_f(void) { sb_ShowNames = false; } //========================================================================== // // ShowInfoDown_f // //========================================================================== static void ShowInfoDown_f(void) { if (sb_ShowInfo || cl.intermission || cls.state < ca_connected) return; // if (cls.demoplayback) return; // demoplay check not needed -- O.S. S_LocalSound("misc/barmovup.wav"); BarTargetHeight = BAR_TOTAL_HEIGHT; sb_ShowInfo = true; sb_updates = 0; } //========================================================================== // // ShowInfoUp_f // //========================================================================== static void ShowInfoUp_f(void) { if (cl.intermission || (scr_viewsize.integer > 110/* && !sbtrans.integer*/)) BarTargetHeight = 0.0-BAR_BUMP_HEIGHT; else BarTargetHeight = BAR_TOP_HEIGHT; S_LocalSound("misc/barmovdn.wav"); sb_ShowInfo = false; sb_updates = 0; } //========================================================================== // // InvLeft_f // //========================================================================== static void InvLeft_f(void) { if (!cl.inv_count || cl.intermission) return; if (inv_flg) { // scroll inventory icons if we're at the left-most already if (cl.inv_selected == cl.inv_startpos) { cl.inv_startpos = (cl.inv_startpos - 1 + cl.inv_count) % cl.inv_count; cl.inv_selected = cl.inv_startpos; } else { cl.inv_selected = (cl.inv_selected - 1 + cl.inv_count) % cl.inv_count; } if (scr_viewsize.integer < 100) scr_fullupdate = 0; } else { inv_flg = true; } S_LocalSound("misc/invmove.wav"); InventoryHideTime = cl.time+INVENTORY_DISPLAY_TIME; } //========================================================================== // // InvRight_f // //========================================================================== static void InvRight_f(void) { int right_icon; if (!cl.inv_count || cl.intermission) return; if (inv_flg) { if (cl.inv_count >= INV_MAX_ICON) right_icon = (cl.inv_startpos + INV_MAX_ICON - 1) % cl.inv_count; else right_icon = (cl.inv_startpos + cl.inv_count - 1) % cl.inv_count; // scroll inventory icons if we're at the right most already if (cl.inv_selected == right_icon) cl.inv_startpos = (cl.inv_startpos + 1) % cl.inv_count; cl.inv_selected = (cl.inv_selected + 1) % cl.inv_count; if (scr_viewsize.integer < 100) scr_fullupdate = 0; } else { inv_flg = true; } S_LocalSound("misc/invmove.wav"); InventoryHideTime = cl.time+INVENTORY_DISPLAY_TIME; } //========================================================================== // // InvUse_f // //========================================================================== static void InvUse_f(void) { if (!cl.inv_count || cl.intermission) return; S_LocalSound("misc/invuse.wav"); //Inv_Update(false); Inv_Update(true); inv_flg = false; if (scr_viewsize.integer < 100) scr_fullupdate = 0; in_impulse = 23; } //========================================================================== // // InvDrop_f // //========================================================================== static void InvDrop_f(void) { if (!cl.inv_count || cl.intermission) return; S_LocalSound("misc/invuse.wav"); //Inv_Update(false); Inv_Update(true); inv_flg = false; if (scr_viewsize.integer < 100) scr_fullupdate = 0; in_impulse = 44; } //========================================================================== // // InvOff_f // //========================================================================== static void InvOff_f(void) { inv_flg = false; if (scr_viewsize.integer < 100) scr_fullupdate = 0; } //========================================================================== // // ToggleDM_f // //========================================================================== static void ToggleDM_f(void) { DMMode.integer += 1; if (DMMode.integer > 2) DMMode.integer = 0; } //========================================================================== // // SB_InvChanged // //========================================================================== void SB_InvChanged(void) { int counter, position; qboolean examined[MAX_INVENTORY]; qboolean ForceUpdate = false; memset (examined, 0, sizeof(examined)); if (cl.inv_selected >= 0 && Inv_GetCount(cl.inv_order[cl.inv_selected]) == 0) ForceUpdate = true; // removed items we no longer have from the order for (counter = position = 0; counter < cl.inv_count; counter++) { if (Inv_GetCount(cl.inv_order[counter]) > 0) { cl.inv_order[position] = cl.inv_order[counter]; examined[cl.inv_order[position]] = true; position++; } } // add in the new items for (counter = 0; counter < MAX_INVENTORY; counter++) { if (!examined[counter]) { if (Inv_GetCount(counter) > 0) { cl.inv_order[position] = counter; position++; } } } cl.inv_count = position; if (cl.inv_selected >= cl.inv_count) { cl.inv_selected = cl.inv_count-1; ForceUpdate = true; } if (cl.inv_count && cl.inv_selected < 0) { cl.inv_selected = 0; ForceUpdate = true; } if (ForceUpdate) { Inv_Update(true); } /* make sure that startpos isn't borked */ if (cl.inv_count <= 1) cl.inv_startpos = 0; else if (cl.inv_startpos >= cl.inv_count) cl.inv_startpos = cl.inv_selected; else { position = cl.inv_selected - cl.inv_startpos; if (position < 0) position += cl.inv_count; if (position >= INV_MAX_ICON/* || position < 0*/) cl.inv_startpos = cl.inv_selected; } } //========================================================================== // // SB_InvReset // //========================================================================== void SB_InvReset(void) { cl.inv_count = cl.inv_startpos = 0; cl.inv_selected = -1; inv_flg = false; if (scr_viewsize.integer < 100) scr_fullupdate = 0; } //========================================================================== // // SB_ViewSizeChanged // //========================================================================== void SB_ViewSizeChanged(void) { if (cl.intermission || (scr_viewsize.integer > 110/* && !sbtrans.integer*/)) BarHeight = BarTargetHeight = 0.0-BAR_BUMP_HEIGHT; else BarHeight = BarTargetHeight = BAR_TOP_HEIGHT; } // DRAWING FUNCTIONS ******************************************************* //========================================================================== // // Sbar_Draw**Pic // Relative to the current status bar location. // //========================================================================== static void Sbar_DrawPic(int x, int y, qpic_t *pic) { Draw_PicCropped (x+((vid.width-320)>>1), y+(vid.height-(int)BarHeight), pic); } static void Sbar_DrawSubPic(int x, int y, int h, qpic_t *pic) { Draw_SubPicCropped (x+((vid.width-320)>>1), y+(vid.height-(int)BarHeight), h, pic); } static void Sbar_DrawTransPic(int x, int y, qpic_t *pic) { Draw_TransPicCropped (x+((vid.width-320)>>1), y+(vid.height-(int)BarHeight), pic); } #if 0 /* no callers */ static void Sbar_DrawCharacter(int x, int y, int num) { Draw_Character (x+((vid.width-320)>>1)+4, y+vid.height-(int)BarHeight, num); } static void Sbar_DrawString(int x, int y, const char *str) { Draw_String (x+((vid.width-320)>>1), y+vid.height-(int)BarHeight, str); } static void Sbar_DrawSmallCharacter(int x, int y, int num) { Draw_SmallCharacter (x+((vid.width-320)>>1)+4, y+vid.height-(int)BarHeight, num); } #endif static void Sbar_DrawRedString (int cx, int cy, const char *str) { while (*str) { Draw_Character (cx, cy, ((unsigned char)(*str))+256); str++; cx += 8; } } static void Sbar_DrawSmallString(int x, int y, const char *str) { Draw_SmallString (x+((vid.width-320)>>1), y+vid.height-(int)BarHeight, str); } static void DrawBarArtifactNumber(int x, int y, int number) { static char artiNumName[18] = "gfx/artinum0.lmp"; if (number >= 10) { artiNumName[11] = '0'+(number%100)/10; Sbar_DrawTransPic(x -4, y, Draw_CachePic(artiNumName)); } artiNumName[11] = '0'+number%10; Sbar_DrawTransPic(x, y, Draw_CachePic(artiNumName)); } engine/hexenworld/client/skin.c000066400000000000000000000140371444734033100171000ustar00rootroot00000000000000/* skin.c -- skin loading * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" cvar_t baseskin = {"baseskin", "base", CVAR_NONE}; cvar_t noskins = {"noskins", "1", CVAR_NONE}; static char allskins[128]; #define MAX_CACHED_SKINS 128 static skin_t skins[MAX_CACHED_SKINS]; static int numskins; /* ================ Skin_Find Determines the best skin for the given scoreboard slot, and sets scoreboard->skin ================ */ void Skin_Find (player_info_t *sc) { skin_t *skin; int i; char name[128]; const char *s; if (allskins[0]) strcpy (name, allskins); else { s = Info_ValueForKey (sc->userinfo, "skin"); if (s[0]) strcpy (name, s); else strcpy (name, baseskin.string); } if (strstr (name, "..") || *name == '.') strcpy (name, "base"); COM_StripExtension (name, name, sizeof(name)); for (i = 0; i < numskins; i++) { if (!strcmp (name, skins[i].name)) { sc->skin = &skins[i]; Skin_Cache (sc->skin); return; } } if (numskins == MAX_CACHED_SKINS) { // ran out of spots, so flush everything Skin_Skins_f (); return; } skin = &skins[numskins]; sc->skin = skin; numskins++; memset (skin, 0, sizeof(*skin)); strcpy (skin->name, name); } /* ========== Skin_Cache Returns a pointer to the skin bitmap, or NULL to use the default ========== */ byte *Skin_Cache (skin_t *skin) { char name[1024]; byte *raw; byte *out, *pix; pcx_t *pcx; int x, y; int dataByte; int runLength; if (cls.downloadtype == dl_skin) return NULL; // use base until downloaded if (noskins.integer == 1) // JACK: So NOSKINS > 1 will show skins, but return NULL; // not download new ones. if (skin->failedload) return NULL; out = (byte *) Cache_Check (&skin->cache); if (out) return out; // // load the pic from disk // q_snprintf (name, sizeof(name), "skins/%s.pcx", skin->name); raw = FS_LoadTempFile (name, NULL); if (!raw) { Con_Printf ("Couldn't load skin %s\n", name); q_snprintf (name, sizeof(name), "skins/%s.pcx", baseskin.string); raw = FS_LoadTempFile (name, NULL); if (!raw) { skin->failedload = true; return NULL; } } // // parse the PCX file // pcx = (pcx_t *)raw; raw = &pcx->data; if (pcx->manufacturer != 0x0a || pcx->version != 5 || pcx->encoding != 1 || pcx->bits_per_pixel != 8 || pcx->xmax >= 320 || pcx->ymax >= 200) { skin->failedload = true; Con_Printf ("Bad skin %s\n", name); return NULL; } out = (byte *) Cache_Alloc (&skin->cache, 320*200, skin->name); if (!out) Sys_Error ("%s: couldn't allocate", __thisfunc__); pix = out; memset (out, 0, 320*200); for (y = 0; y < pcx->ymax; y++, pix += 320) { for (x = 0; x <= pcx->xmax ; ) { if ((raw - (byte*)pcx) > (ptrdiff_t)fs_filesize) { Cache_Free (&skin->cache); skin->failedload = true; Con_Printf ("Skin %s was malformed. You should delete it.\n", name); return NULL; } dataByte = *raw++; if ((dataByte & 0xC0) == 0xC0) { runLength = dataByte & 0x3F; if ((raw - (byte*)pcx) > (ptrdiff_t)fs_filesize) { Cache_Free (&skin->cache); skin->failedload = true; Con_Printf ("Skin %s was malformed. You should delete it.\n", name); return NULL; } dataByte = *raw++; } else runLength = 1; // skin sanity check if (runLength + x > pcx->xmax + 2) { Cache_Free (&skin->cache); skin->failedload = true; Con_Printf ("Skin %s was malformed. You should delete it.\n", name); return NULL; } while (runLength-- > 0) pix[x++] = dataByte; } } if ((raw - (byte*)pcx) > (ptrdiff_t)fs_filesize) { Cache_Free (&skin->cache); skin->failedload = true; Con_Printf ("Skin %s was malformed. You should delete it.\n", name); return NULL; } skin->failedload = false; return out; } /* ================= Skin_NextDownload ================= */ void Skin_NextDownload (void) { player_info_t *sc; int i; if (cls.downloadnumber == 0) Con_Printf ("Checking skins...\n"); cls.downloadtype = dl_skin; for ( ; cls.downloadnumber != MAX_CLIENTS; cls.downloadnumber++) { sc = &cl.players[cls.downloadnumber]; if (!sc->name[0]) continue; Skin_Find (sc); if (noskins.integer) continue; if (!CL_CheckOrDownloadFile(va("skins/%s.pcx", sc->skin->name))) return; // started a download } cls.downloadtype = dl_none; // now load them in for real for (i = 0; i < MAX_CLIENTS; i++) { sc = &cl.players[i]; if (!sc->name[0]) continue; Skin_Cache (sc->skin); #ifdef GLQUAKE sc->skin = NULL; #endif } if (cls.state != ca_active) { // get next signon phase MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, va("begin %i", cl.servercount)); Cache_Report (); // print remaining memory } } /* ========== Skin_Skins_f Refind all skins, downloading if needed. ========== */ void Skin_Skins_f (void) { int i; for (i = 0; i < numskins; i++) { if (skins[i].cache.data) Cache_Free (&skins[i].cache); } numskins = 0; if (cls.state == ca_disconnected) { Con_Printf("%s: no connection to a server.\n", __thisfunc__); return; } cls.downloadnumber = 0; cls.downloadtype = dl_skin; Skin_NextDownload (); } /* ========== Skin_AllSkins_f Sets all skins to one specific one ========== */ void Skin_AllSkins_f (void) { strcpy (allskins, Cmd_Argv(1)); Skin_Skins_f (); } engine/hexenworld/client/sys_amiga.c000066400000000000000000000446341444734033100201160ustar00rootroot00000000000000/* sys_amiga.c -- Amiga system interface code * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2012 Szilard Biro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "debuglog.h" #include #include #include #include #include #include #include #if defined(SDLQUAKE) #include "sdl_inc.h" #endif #if defined(__LP64__) #define MIN_STACK_SIZE 0x200000 /* 2 MB stack */ #elif defined(PLATFORM_AMIGAOS3) #define MIN_STACK_SIZE 0x40000 /* 256 KB stack */ #else #define MIN_STACK_SIZE 0x100000 /* 1 MB stack */ #endif #ifdef __CLIB2__ int __stack_size = MIN_STACK_SIZE; #else int __stack = MIN_STACK_SIZE; #if defined(PLATFORM_AMIGAOS3) && defined(__libnix__) /* this pulls in swapstack.o */ /* NOTE: swapstack.o was a stray object in old versions * of libnix, need manually adding to libnix20.a */ extern void __stkinit(void); void * __x = __stkinit; #endif #endif #ifdef __AROS__ #include "incstack.h" /* The problem here is that our real main never returns: the exit * point of program is either Sys_Quit() or Sys_Error(). One way * of making the real main return to the incstack.h main wrapper * is setjmp()'ing in real main and longjmp()'ing from the actual * exit points, avoiding exit(). */ #include static jmp_buf exit_buf; static int my_rc = 0; #define HAVE_AROS_MAIN_WRAPPER #endif #include /* IPTR */ #define MIN_MEM_ALLOC 0x1000000 #define STD_MEM_ALLOC 0x2000000 cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; cvar_t sys_throttle = {"sys_throttle", "0.02", CVAR_ARCHIVE}; static double starttime; static qboolean first = true; struct timerequest *timerio; struct MsgPort *timerport; #if defined(__MORPHOS__) || defined(__VBCC__) struct Library *TimerBase; #else struct Device *TimerBase; #endif #ifdef __CLIB2__ struct IntuitionBase *IntuitionBase; struct Library *IFFParseBase; #endif /* =============================================================================== FILE IO =============================================================================== */ int Sys_mkdir(const char *path, qboolean crash) { BPTR lock = CreateDir((const STRPTR) path); if (lock) { UnLock(lock); return 0; } if (IoErr() == ERROR_OBJECT_EXISTS) return 0; if (crash) Sys_Error("Unable to create directory %s", path); return -1; } int Sys_rmdir (const char *path) { if (DeleteFile((const STRPTR) path) != 0) return 0; return -1; } int Sys_unlink (const char *path) { if (DeleteFile((const STRPTR) path) != 0) return 0; return -1; } int Sys_rename (const char *oldp, const char *newp) { if (Rename((const STRPTR) oldp, (const STRPTR) newp) != 0) return 0; return -1; } long Sys_filesize (const char *path) { long size = -1; BPTR lock = Lock((const STRPTR) path, ACCESS_READ); if (lock) { struct FileInfoBlock *fib = (struct FileInfoBlock*) AllocDosObject(DOS_FIB, NULL); if (fib != NULL) { if (Examine(lock, fib)) { size = fib->fib_Size; } FreeDosObject(DOS_FIB, fib); } UnLock(lock); } return size; } int Sys_FileType (const char *path) { int type = FS_ENT_NONE; BPTR lock = Lock((const STRPTR) path, ACCESS_READ); if (lock) { struct FileInfoBlock *fib = (struct FileInfoBlock*) AllocDosObject(DOS_FIB, NULL); if (fib != NULL) { if (Examine(lock, fib)) { if (fib->fib_DirEntryType >= 0) type = FS_ENT_DIRECTORY; else type = FS_ENT_FILE; } FreeDosObject(DOS_FIB, fib); } UnLock(lock); } return type; } #define COPY_READ_BUFSIZE 8192 /* BUFSIZ */ int Sys_CopyFile (const char *frompath, const char *topath) { char buf[COPY_READ_BUFSIZE]; BPTR in, out; struct FileInfoBlock *fib; struct DateStamp stamp; LONG remaining, count; in = Open((const STRPTR) frompath, MODE_OLDFILE); if (!in) { Con_Printf ("%s: unable to open %s\n", __thisfunc__, frompath); return 1; } fib = (struct FileInfoBlock*) AllocDosObject(DOS_FIB, NULL); if (fib != NULL) { if (ExamineFH(in, fib) == 0) remaining = -1; else { remaining = fib->fib_Size; stamp = fib->fib_Date; } FreeDosObject(DOS_FIB, fib); if (remaining < 0) { Con_Printf ("%s: can't determine size for %s\n", __thisfunc__, frompath); Close(in); return 1; } } else { Con_Printf ("%s: can't allocate FileInfoBlock for %s\n", __thisfunc__, frompath); Close(in); return 1; } out = Open((const STRPTR) topath, MODE_NEWFILE); if (!out) { Con_Printf ("%s: unable to open %s\n", __thisfunc__, topath); Close(in); return 1; } while (remaining) { if (remaining < sizeof(buf)) count = remaining; else count = sizeof(buf); if (Read(in, buf, count) == -1) break; if (Write(out, buf, count) == -1) break; remaining -= count; } Close(in); Close(out); if (remaining != 0) return 1; SetFileDate(topath, &stamp); return 0; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only, not a dirent struct. this is what we presently need in this engine. ================================================= */ static struct AnchorPath apath; static BPTR oldcurrentdir; static STRPTR pattern_str; static STRPTR pattern_helper (const char *pat) { char *pdup; const char *p; int n; for (n = 0, p = pat; *p != '\0'; ++p, ++n) { if ((p = strchr (p, '*')) == NULL) break; } if (n == 0) pdup = Z_Strdup(pat); else { /* replace each "*" by "#?" */ n += (int) strlen(pat) + 1; pdup = (char *) Z_Malloc(n, Z_MAINZONE); for (n = 0, p = pat; *p != '\0'; ++p, ++n) { if (*p != '*') pdup[n] = *p; else { pdup[n] = '#'; ++n; pdup[n] = '?'; } } pdup[n] = '\0'; } return (STRPTR) pdup; } const char *Sys_FindFirstFile (const char *path, const char *pattern) { BPTR newdir; if (pattern_str) Sys_Error ("Sys_FindFirst without FindClose"); memset(&apath, 0, sizeof(apath)); newdir = Lock((const STRPTR) path, SHARED_LOCK); if (newdir) oldcurrentdir = CurrentDir(newdir); else return NULL; pattern_str = pattern_helper (pattern); if (MatchFirst((const STRPTR) pattern_str, &apath) == 0) { if (apath.ap_Info.fib_DirEntryType < 0) return (const char *) (apath.ap_Info.fib_FileName); } return Sys_FindNextFile(); } const char *Sys_FindNextFile (void) { if (!pattern_str) return NULL; while (MatchNext(&apath) == 0) { if (apath.ap_Info.fib_DirEntryType < 0) return (const char *) (apath.ap_Info.fib_FileName); } return NULL; } void Sys_FindClose (void) { if (!pattern_str) return; MatchEnd(&apath); UnLock(CurrentDir(oldcurrentdir)); oldcurrentdir = 0; Z_Free(pattern_str); pattern_str = NULL; } /* File existence check with the "Please insert volume XXX" * system requester disabled. */ qboolean Sys_PathExistsQuiet(const char *name) { struct Process *self = (struct Process *) FindTask(NULL); APTR oldwinptr = self->pr_WindowPtr; BPTR lock; self->pr_WindowPtr = (APTR) -1; lock = Lock((const STRPTR) name, ACCESS_READ); self->pr_WindowPtr = oldwinptr; if (lock) { UnLock(lock); return true; } return false; } /* =============================================================================== SYSTEM IO =============================================================================== */ /* ================ Sys_MakeCodeWriteable ================ */ #if id386 && !defined(GLQUAKE) void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) { /* Not needed on Amiga. */ } #endif /* id386, !GLQUAKE */ /* ================ Sys_Init ================ */ static void Sys_Init (void) { /*MaskExceptions ();*/ Sys_SetFPCW (); #ifdef __CLIB2__ IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 0); if (!IntuitionBase) Sys_Error ("Cannot open intuition.library!"); IFFParseBase = OpenLibrary("iffparse.library", 0); #endif if ((timerport = CreateMsgPort())) { if ((timerio = (struct timerequest *)CreateIORequest(timerport, sizeof(struct timerequest)))) { if (OpenDevice((STRPTR) TIMERNAME, UNIT_MICROHZ, (struct IORequest *) timerio, 0) == 0) { #if defined(__MORPHOS__) || defined(__VBCC__) TimerBase = (struct Library *)timerio->tr_node.io_Device; #else TimerBase = timerio->tr_node.io_Device; #endif } else { DeleteIORequest((struct IORequest *)timerio); DeleteMsgPort(timerport); } } else { DeleteMsgPort(timerport); } } if (!TimerBase) Sys_Error("Can't open timer.device"); /* 1us wait, for timer cleanup success */ timerio->tr_node.io_Command = TR_ADDREQUEST; timerio->tr_time.tv_secs = 0; timerio->tr_time.tv_micro = 1; SendIO((struct IORequest *) timerio); WaitIO((struct IORequest *) timerio); #if defined(SDLQUAKE) if (SDL_Init(0) < 0) Sys_Error("SDL failed to initialize."); #endif } static void Sys_AtExit (void) { if (TimerBase) { /* if (!CheckIO((struct IORequest *) timerio) { AbortIO((struct IORequest *) timerio); WaitIO((struct IORequest *) timerio); } */ WaitIO((struct IORequest *) timerio); CloseDevice((struct IORequest *) timerio); DeleteIORequest((struct IORequest *) timerio); DeleteMsgPort(timerport); TimerBase = NULL; } #ifdef __CLIB2__ if (IntuitionBase) { CloseLibrary((struct Library *)IntuitionBase); IntuitionBase = NULL; } if (IFFParseBase) { CloseLibrary(IFFParseBase); IFFParseBase = NULL; } #endif #if defined(SDLQUAKE) SDL_Quit(); #endif } void Sys_ErrorMessage(const char *string) { struct EasyStruct es; if (!IntuitionBase) return; es.es_StructSize = sizeof(es); es.es_Flags = 0; es.es_Title = (STRPTR) ENGINE_NAME " error"; es.es_TextFormat = (STRPTR) string; es.es_GadgetFormat = (STRPTR) "Quit"; EasyRequest(0, &es, 0, 0); } #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const unsigned char *p; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (con_debuglog) { LOG_Print (ERROR_PREFIX); LOG_Print (text); LOG_Print ("\n\n"); } Host_Shutdown (); for (p = (const unsigned char *) text2; *p; p++) putc (*p, stderr); for (p = (const unsigned char *) text ; *p; p++) putc (*p, stderr); putc ('\n', stderr); putc ('\n', stderr); Sys_ErrorMessage (text); #ifdef HAVE_AROS_MAIN_WRAPPER Sys_AtExit(); my_rc = 1; longjmp(exit_buf, 1); #else exit (1); #endif } void Sys_PrintTerm (const char *msgtxt) { const unsigned char *p; if (sys_nostdout.integer) return; for (p = (const unsigned char *) msgtxt; *p; p++) putc (*p, stdout); } void Sys_Quit (void) { Host_Shutdown(); #ifdef HAVE_AROS_MAIN_WRAPPER Sys_AtExit(); longjmp(exit_buf, 1); #else exit (0); #endif } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { struct timeval tp; double now; GetSysTime(&tp); now = tp.tv_secs + tp.tv_micro / 1e6; if (first) { first = false; starttime = now; return 0.0; } return now - starttime; } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; time_t t; struct tm *l; int val; if (!buf) buf = strbuf; t = time(NULL); l = localtime(&t); val = l->tm_mon + 1; /* tm_mon: months since January [0,11] */ buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = l->tm_mday; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = l->tm_year / 100 + 19; /* tm_year: #years since 1900. */ buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = l->tm_year % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = l->tm_hour; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = l->tm_min; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = l->tm_sec; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } void Sys_Sleep (unsigned long msecs) { timerio->tr_node.io_Command = TR_ADDREQUEST; timerio->tr_time.tv_secs = msecs / 1000; timerio->tr_time.tv_micro = (msecs * 1000) % 1000000; SendIO((struct IORequest *) timerio); WaitIO((struct IORequest *) timerio); } void Sys_SendKeyEvents (void) { IN_SendKeyEvents(); } #define MAX_CLIPBOARDTXT MAXCMDLINE /* 256 */ char *Sys_GetClipboardData (void) { struct IFFHandle *IFFHandle; struct ContextNode *cn; LONG readbytes; char *chunk_buffer = NULL; if (!IFFParseBase) { return NULL; } IFFHandle = AllocIFF(); if (IFFHandle) { IFFHandle->iff_Stream = (IPTR) OpenClipboard(0); if (IFFHandle->iff_Stream) { InitIFFasClip(IFFHandle); if (!OpenIFF(IFFHandle, IFFF_READ)) { if (!StopChunk(IFFHandle, ID_FTXT, ID_CHRS)) { if (!ParseIFF(IFFHandle, IFFPARSE_SCAN)) { cn = CurrentChunk(IFFHandle); if (cn && (cn->cn_Type == ID_FTXT) && (cn->cn_ID == ID_CHRS)) { chunk_buffer = (char *) Z_Malloc(MAX_CLIPBOARDTXT, Z_MAINZONE); readbytes = ReadChunkBytes(IFFHandle, chunk_buffer, MAX_CLIPBOARDTXT - 1); if (readbytes < 0) { readbytes = 0; } chunk_buffer[readbytes] = '\0'; } } } CloseIFF(IFFHandle); } CloseClipboard((struct ClipboardHandle *) IFFHandle->iff_Stream); } FreeIFF(IFFHandle); } return chunk_buffer; } static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { #if 1 int len = q_strlcpy(dst, "PROGDIR:", dstsize); if (len < (int)dstsize) return 0; return -1; #else if (NameFromLock(GetProgramDir(), (STRPTR) dst, dstsize) != 0) return 0; return -1; #endif } static void Sys_CheckSDL (void) { #if defined(SDLQUAKE) const SDL_version *sdl_version; sdl_version = SDL_Linked_Version(); Sys_Printf("Found SDL version %i.%i.%i\n",sdl_version->major,sdl_version->minor,sdl_version->patch); #endif } static void PrintVersion (void) { Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); Sys_Printf ("running on %s engine %4.2f (%s)\n", ENGINE_NAME, ENGINE_VERSION, PLATFORM_STRING); Sys_Printf ("More info / sending bug reports: http://uhexen2.sourceforge.net\n"); } #include "snd_sys.h" static const char *help_strings[] = { " [-v | --version] Display version information", " [-noportals] Disable the mission pack support", " [-w | -window ] Run the game windowed", " [-f | -fullscreen] Run the game fullscreen", " [-width X [-height Y]] Select screen size", #ifdef GLQUAKE " [-bpp] Depth for GL fullscreen mode", " [-g | -gllibrary] Select 3D rendering library", " [-fsaa N] Enable N sample antialiasing", " [-paltex] Enable 8-bit textures", " [-nomtex] Disable multitexture detection/usage", #endif #if !defined(_NO_SOUND) #if SOUND_NUMDRIVERS " [-s | -nosound] Run the game without sound", #endif #if (SOUND_NUMDRIVERS > 1) #if HAVE_SDL_SOUND " [-sndsdl] Use SDL sound", #endif #if HAVE_AHI_SOUND " [-sndahi] Use AHI audio system", #endif #if HAVE_PAULA_SOUND " [-sndpaula] Use Paula DMA audio", #endif #endif /* SOUND_NUMDRIVERS */ #endif /* _NO_SOUND */ " [-nomouse] Disable mouse usage", " [-heapsize Bytes] Heapsize (memory to allocate)", NULL }; static void PrintHelp (const char *name) { int i = 0; Sys_Printf ("Usage: %s [options]\n", name); while (help_strings[i]) { Sys_PrintTerm (help_strings[i]); Sys_PrintTerm ("\n"); i++; } Sys_PrintTerm ("\n"); } /* =============================================================================== MAIN =============================================================================== */ static quakeparms_t parms; static char cwd[MAX_OSPATH]; int main (int argc, char **argv) { int i; double time, oldtime, newtime; ULONG availMem; #ifdef HAVE_AROS_MAIN_WRAPPER if (setjmp(exit_buf)) return my_rc; #endif PrintVersion(); if (argc > 1) { for (i = 1; i < argc; i++) { if ( !(strcmp(argv[i], "-v")) || !(strcmp(argv[i], "-version" )) || !(strcmp(argv[i], "--version")) ) { return 0; } else if ( !(strcmp(argv[i], "-h")) || !(strcmp(argv[i], "-help" )) || !(strcmp(argv[i], "-?")) || !(strcmp(argv[i], "--help")) ) { PrintHelp(argv[0]); return 0; } } } /* initialize the host params */ memset (&parms, 0, sizeof(parms)); parms.basedir = cwd; parms.userdir = cwd; parms.argc = argc; parms.argv = argv; parms.errstate = 0; host_parms = &parms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); LOG_Init (&parms); Sys_Printf("basedir is: %s\n", parms.basedir); Sys_Printf("userdir is: %s\n", parms.userdir); COM_ValidateByteorder (); Sys_CheckSDL (); availMem = AvailMem(MEMF_ANY|MEMF_LARGEST); parms.memsize = (availMem < STD_MEM_ALLOC)? MIN_MEM_ALLOC : STD_MEM_ALLOC; i = COM_CheckParm ("-heapsize"); if (i && i < com_argc-1) parms.memsize = atoi (com_argv[i+1]) * 1024; parms.membase = malloc (parms.memsize); if (!parms.membase) Sys_Error ("Insufficient memory."); #ifndef HAVE_AROS_MAIN_WRAPPER atexit (Sys_AtExit); #endif Sys_Init (); Host_Init(); /* running from Workbench */ if (argc == 0) Cvar_SetQuick(&sys_nostdout, "1"); oldtime = Sys_DoubleTime (); /* main window message loop */ while (1) { #if defined(SDLQUAKE) /* If we have no input focus at all, sleep a bit */ if (!VID_HasMouseOrInputFocus() || cl.paused) { Sys_Sleep(16); } /* If we're minimised, sleep a bit more */ if (VID_IsMinimized()) { scr_skipupdate = 1; Sys_Sleep(32); } else { scr_skipupdate = 0; } #endif newtime = Sys_DoubleTime (); time = newtime - oldtime; Host_Frame (time); if (time < sys_throttle.value) Sys_Sleep(1); oldtime = newtime; } return 0; } engine/hexenworld/client/sys_dos.c000066400000000000000000000611561444734033100176230ustar00rootroot00000000000000/* sys_dos.c -- DOS system interface code. * from quake1 source with adaptations for uhexen2. * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include #include #include #include #include #include #include #include #include #include #include /* for _crt0_startup_flags */ #include #include #include #include "quakedef.h" #include "dosisms.h" #include "debuglog.h" #include "sys_dxe.h" #define MIN_MEM_ALLOC 0x1000000 /* minimum 16 mb */ #define STD_MEM_ALLOC 0x2000000 /* standart 32 mb */ /* 2000-07-16, DOSQuake/DJGPP mem detection fix by * Norberto Alfredo Bensa */ int _crt0_startup_flags = _CRT0_FLAG_UNIX_SBRK; #ifdef GLQUAKE /* need at least 1MB stack for 3dfx. */ unsigned int _stklen = 1048576; /* FS: FIXME TUNE. */ #endif int end_of_memory; static qboolean lockmem, lockunlockmem, unlockmem; static int win95; #define KEYBUF_SIZE 256 static unsigned char keybuf[KEYBUF_SIZE]; static int keybuf_head = 0; static int keybuf_tail = 0; static quakeparms_t quakeparms; static int sys_checksum; /* 2000-07-28, DOSQuake "time running too fast" fix * by Norberto Alfredo Bensa. Set USE_UCLOCK_TIME * to 0 if you want to use the old original code. * See Sys_DoubleTime() for information on uclock() */ #define USE_UCLOCK_TIME 1 static void Sys_InitTime (void); #if !USE_UCLOCK_TIME static double curtime = 0.0; static double lastcurtime = 0.0; static double oldtime = 0.0; #endif /* ! USE_UCLOCK_TIME */ cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; cvar_t sys_throttle = {"sys_throttle", "0.02", CVAR_ARCHIVE}; float fptest_temp; extern char start_of_memory __asm__("start"); //============================================================================= // this is totally dependent on cwsdpmi putting the stack right after tge // global data // This does evil things in a Win95 DOS box!!! #if 0 extern byte end; #define CHECKBYTE 0xed static void Sys_InitStackCheck (void) { int i; for (i = 0; i < 128*1024; i++) (&end)[i] = CHECKBYTE; } void Sys_StackCheck (void) { int i; for (i = 0; i < 128*1024; i++) { if ( (&end)[i] != CHECKBYTE ) break; } Con_Printf ("%i undisturbed stack bytes\n", i); if (end != CHECKBYTE) Sys_Error ("System stack overflow!"); } #endif //============================================================================= static byte scantokey[128] = { // 0 1 2 3 4 5 6 7 // 8 9 A B C D E F 0 , 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 0 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 13, K_CTRL, 'a', 's', // 1 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', K_SHIFT, '\\', 'z', 'x', 'c', 'v', // 2 'b', 'n', 'm', ',', '.', '/', K_SHIFT, '*', K_ALT, ' ', 0 , K_F1, K_F2, K_F3, K_F4, K_F5, // 3 K_F6, K_F7, K_F8, K_F9, K_F10, 0 , 0 , K_HOME, K_UPARROW,K_PGUP,'-',K_LEFTARROW,'5',K_RIGHTARROW,'+',K_END, // 4 K_DOWNARROW,K_PGDN,K_INS,K_DEL, 0 , 0 , 0 , K_F11, K_F12, 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 5 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 6 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 }; #if 0 /* not used */ static byte shiftscantokey[128] = { // 0 1 2 3 4 5 6 7 // 8 9 A B C D E F 0 , 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', K_BACKSPACE, 9, // 0 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', 13 , K_CTRL, 'A', 'S', // 1 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"' , '~', K_SHIFT, '|', 'Z', 'X', 'C', 'V', // 2 'B', 'N', 'M', '<', '>', '?', K_SHIFT, '*', K_ALT, ' ', 0 , K_F1, K_F2, K_F3, K_F4, K_F5, // 3 K_F6, K_F7, K_F8, K_F9, K_F10, 0 , 0 , K_HOME, K_UPARROW,K_PGUP,'_',K_LEFTARROW,'%',K_RIGHTARROW,'+',K_END, // 4 K_DOWNARROW,K_PGDN,K_INS,K_DEL, 0 , 0 , 0 , K_F11, K_F12, 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 5 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 6 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // 7 }; #endif static void TrapKey (void) { // static int ctrl = 0; keybuf[keybuf_head] = dos_inportb(0x60); dos_outportb(0x20, 0x20); /* if (scantokey[keybuf[keybuf_head] & 0x7f] == K_CTRL) ctrl = keybuf[keybuf_head] & 0x80; if (ctrl && scantokey[keybuf[keybuf_head] & 0x7f] == 'c') Sys_Error("ctrl-c hit\n"); */ keybuf_head = (keybuf_head + 1) & (KEYBUF_SIZE - 1); } static void Sys_DetectWin95 (void) { __dpmi_regs r; r.x.ax = 0x160a; /* Get Windows Version */ __dpmi_int(0x2f, &r); if (r.x.ax || r.h.bh < 4) /* Not windows or earlier than Win95 */ { win95 = 0; lockmem = true; lockunlockmem = false; unlockmem = true; } else { win95 = 1; lockunlockmem = COM_CheckParm ("-winlockunlock"); if (lockunlockmem) lockmem = true; else lockmem = COM_CheckParm ("-winlock"); unlockmem = lockmem && !lockunlockmem; } } #ifndef GLQUAKE static int minmem; static void *dos_getmaxlockedmem (int *size) { __dpmi_free_mem_info meminfo; __dpmi_meminfo info; int working_size; void *working_memory; int last_locked; int i, j, extra, allocsize; static const char msg[] = "Locking data..."; byte *x; unsigned long ul; // first lock all the current executing image so the locked count will // be accurate. It doesn't hurt to lock the memory multiple times last_locked = __djgpp_selector_limit + 1; info.size = last_locked - 4096; info.address = __djgpp_base_address + 4096; if (lockmem) { if (__dpmi_lock_linear_region(&info)) { Sys_Error ("Lock of current memory at 0x%lx for %ldKb failed!\n", info.address, info.size / 1024); } } __dpmi_get_free_memory_information(&meminfo); if (!win95) /* Not windows or earlier than Win95 */ { ul = meminfo.maximum_locked_page_allocation_in_pages * 4096; } else { ul = meminfo.largest_available_free_block_in_bytes - LEAVE_FOR_CACHE; } if (ul > 0x7fffffff) ul = 0x7fffffff; /* limit to 2GB */ working_size = (int) ul; working_size &= ~0xffff; /* Round down to 64K */ working_size += 0x10000; do { working_size -= 0x10000; /* Decrease 64K and try again */ working_memory = sbrk(working_size); } while (working_memory == (void *)-1); extra = 0xfffc - ((unsigned)sbrk(0) & 0xffff); if (extra > 0) { sbrk(extra); working_size += extra; } // now grab the memory info.address = last_locked + __djgpp_base_address; if (!win95) { info.size = __djgpp_selector_limit + 1 - last_locked; while (info.size > 0 && __dpmi_lock_linear_region(&info)) { info.size -= 0x1000; working_size -= 0x1000; sbrk(-0x1000); } } else { /* Win95 section */ j = COM_CheckParm("-winmem"); // minmem = MIN_MEM_ALLOC; minmem = STD_MEM_ALLOC; if (j && j < com_argc - 1) { allocsize = ((int)(atoi(com_argv[j + 1]))) * 0x100000 + LOCKED_FOR_MALLOC; if (allocsize < (minmem + LOCKED_FOR_MALLOC)) allocsize = minmem + LOCKED_FOR_MALLOC; } else { allocsize = minmem + LOCKED_FOR_MALLOC; } if (!lockmem) { // we won't lock, just sbrk the memory info.size = allocsize; goto UpdateSbrk; } // lock the memory down write (STDOUT_FILENO, msg, strlen (msg)); for (j = allocsize; j > (minmem + LOCKED_FOR_MALLOC); j -= 0x100000) { info.size = j; if (!__dpmi_lock_linear_region(&info)) goto Locked; write (STDOUT_FILENO, ".", 1); } // finally, try with the absolute minimum amount for (i = 0; i < 10; i++) { info.size = minmem + LOCKED_FOR_MALLOC; if (!__dpmi_lock_linear_region(&info)) goto Locked; } Sys_Error ("Can't lock memory; %lu Mb lockable RAM required. " "Try shrinking smartdrv.", info.size / 0x100000); Locked: UpdateSbrk: info.address += info.size; info.address -= __djgpp_base_address + 4; // ending point, malloc align working_size = info.address - (int)working_memory; sbrk(info.address - (int)sbrk(0)); // negative adjustment } if (lockunlockmem) { __dpmi_unlock_linear_region (&info); printf ("Locked and unlocked %d Mb data\n", working_size / 0x100000); } else if (lockmem) { printf ("Locked %d Mb data\n", working_size / 0x100000); } else { printf ("Allocated %d Mb data\n", working_size / 0x100000); } // touch all the memory to make sure it's there. The 16-page skip is to // keep Win 95 from thinking we're trying to page ourselves in (we are // doing that, of course, but there's no reason we shouldn't) x = (byte *)working_memory; for (j = 0; j < 4; j++) { for (i = 0; i < (working_size - 16 * 0x1000); i += 4) { sys_checksum += *(int *)&x[i]; sys_checksum += *(int *)&x[i + 16 * 0x1000]; } } // give some of what we locked back for malloc before returning. Done // by cheating and passing a negative value to sbrk working_size -= LOCKED_FOR_MALLOC; sbrk( -(LOCKED_FOR_MALLOC)); *size = working_size; return working_memory; } #endif int Sys_mkdir (const char *path, qboolean crash) { int rc = mkdir (path, 0777); if (rc != 0 && errno == EEXIST) rc = 0; if (rc != 0 && crash) Sys_Error("Unable to create directory %s", path); return rc; } int Sys_rmdir (const char *path) { return rmdir(path); } int Sys_unlink (const char *path) { return remove(path); } int Sys_rename (const char *oldp, const char *newp) { return rename(oldp, newp); } long Sys_filesize (const char *path) { struct ffblk f; if (findfirst(path, &f, FA_ARCH | FA_RDONLY) != 0) return -1; return (long) f.ff_fsize; } int Sys_FileType (const char *path) { int attr = _chmod(path, 0); /* Root directories on some non-local drives (e.g. CD-ROM) as well as devices may fail _chmod, but we are not interested in such cases. */ if (attr == -1) return FS_ENT_NONE; if (attr & _A_SUBDIR) return FS_ENT_DIRECTORY; if (attr & _A_VOLID) /* we shouldn't hit this! */ return FS_ENT_DIRECTORY; return FS_ENT_FILE; } #define COPY_READ_BUFSIZE 8192 /* BUFSIZ */ int Sys_CopyFile (const char *frompath, const char *topath) { char buf[COPY_READ_BUFSIZE]; int in, out; long remaining, count; struct ftime ft; in = open (frompath, O_RDONLY | O_BINARY); if (in < 0) { Con_Printf ("%s: unable to open %s\n", __thisfunc__, frompath); return 1; } remaining = filelength (in); if (remaining < 0) { Con_Printf ("%s: %s failed filelength()\n", __thisfunc__, frompath); close (in); return 1; } out = open (topath, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, 0666); if (out < 0) { Con_Printf ("%s: unable to create %s\n", __thisfunc__, topath); close (in); return 1; } while (remaining) { if (remaining < sizeof(buf)) count = remaining; else count = sizeof(buf); if (read(in, buf, count) < 0) break; if (write(out, buf, count) < 0) break; remaining -= count; } if (remaining == 0) { /* restore the file's timestamp */ if (getftime(in, &ft) == 0) setftime(out, &ft); } close (in); close (out); return remaining; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only. ================================================= */ static struct ffblk finddata; static int findhandle = -1; static char findstr[MAX_OSPATH]; const char *Sys_FindFirstFile (const char *path, const char *pattern) { if (findhandle == 0) Sys_Error ("Sys_FindFirst without FindClose"); q_snprintf (findstr, sizeof(findstr), "%s/%s", path, pattern); memset (&finddata, 0, sizeof(finddata)); findhandle = findfirst(findstr, &finddata, FA_ARCH | FA_RDONLY); if (findhandle == 0) return finddata.ff_name; return NULL; } const char *Sys_FindNextFile (void) { if (findhandle != 0) return NULL; if (findnext(&finddata) == 0) return finddata.ff_name; return NULL; } void Sys_FindClose (void) { findhandle = -1; } void Sys_Sleep (unsigned long msecs) { usleep (msecs * 1000); } static void Sys_Init (void) { MaskExceptions (); Sys_SetFPCW (); #if !USE_UCLOCK_TIME dos_outportb(0x43, 0x34); // set system timer to mode 2 dos_outportb(0x40, 0); // for Sys_DoubleTime() dos_outportb(0x40, 0); #endif /* ! USE_UCLOCK_TIME */ Sys_InitTime (); _go32_interrupt_stack_size = 4 * 1024; _go32_rmcb_stack_size = 4 * 1024; Sys_InitDXE3(); } void Sys_Shutdown (void) { if (!isDedicated) dos_restoreintr(9); if (unlockmem) { dos_unlockmem (&start_of_memory, end_of_memory - (int)&start_of_memory); dos_unlockmem (quakeparms.membase, quakeparms.memsize); } } #define SC_UPARROW 0x48 #define SC_DOWNARROW 0x50 #define SC_LEFTARROW 0x4b #define SC_RIGHTARROW 0x4d #define SC_LEFTSHIFT 0x2a #define SC_RIGHTSHIFT 0x36 void Sys_SendKeyEvents (void) { int k, next; int outkey; // get key events while (keybuf_head != keybuf_tail) { k = keybuf[keybuf_tail++]; keybuf_tail &= (KEYBUF_SIZE - 1); if (k == 0xe0) continue; // special / pause keys next = keybuf[(keybuf_tail - 2) & (KEYBUF_SIZE - 1)]; // Pause generates e1 1d 45 e1 9d c5 when pressed, and // nothing when released. e1 is generated only for the // pause key. if (next == 0xe1) continue; // pause key bullshit if (k == 0xc5 && next == 0x9d) { Key_Event (K_PAUSE, true); continue; } // extended keyboard shift key bullshit if ( (k & 0x7f) == SC_LEFTSHIFT || (k & 0x7f) == SC_RIGHTSHIFT ) { if (keybuf[(keybuf_tail - 2) & (KEYBUF_SIZE - 1)] == 0xe0) continue; k &= 0x80; k |= SC_RIGHTSHIFT; } if (k == 0xc5 && keybuf[(keybuf_tail - 2) & (KEYBUF_SIZE - 1)] == 0x9d) continue; // more pause bullshit outkey = scantokey[k & 0x7f]; if (k & 0x80) Key_Event (outkey, false); else Key_Event (outkey, true); } } char *Sys_GetClipboardData (void) { return NULL; } // ======================================================================= // General routines // ======================================================================= void Sys_PrintTerm (const char *msgtxt) { const unsigned char *p; if (sys_nostdout.integer) return; for (p = (const unsigned char *) msgtxt; *p; p++) putc (*p, stdout); } static void Sys_AtExit (void) { // shutdown only once (so Sys_Error can call this function to shutdown, then // print the error message, then call exit without exit calling this function // again) Sys_Shutdown(); } void Sys_Quit (void) { Cvar_SetROM ("sys_nostdout", "0"); // enable printing to terminal Host_Shutdown (); exit (0); } #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const unsigned char *p; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (con_debuglog) { LOG_Print (ERROR_PREFIX); LOG_Print (text); LOG_Print ("\n\n"); } Cvar_SetROM ("sys_nostdout", "0"); // enable printing to terminal Host_Shutdown (); for (p = (const unsigned char *) text2; *p; p++) putc (*p, stderr); for (p = (const unsigned char *) text ; *p; p++) putc (*p, stderr); putc ('\n', stderr); putc ('\n', stderr); // Sys_AtExit is called by exit to shutdown the system exit (1); } /* ================ Sys_MakeCodeWriteable ================ */ void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) { // it's always writeable } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { #if USE_UCLOCK_TIME /* From DJGPP uclock() man page : uclock() returns the number of uclock ticks since an arbitrary time, actually, since the first call to uclock(), which itself returns zero. The number of tics per second is UCLOCKS_PER_SEC (declared in time.h as 1193180.) uclock() is provided for very high-resulution timing. uclock_t is a 64-bit integer. It is currently accurate to better than 1 microsecond (actually about 840 nanoseconds). You cannot time across two midnights with this implementation, giving a maximum useful period of 48 hours and an effective limit of 24 hours. Casting to a 32-bit integer limits its usefulness to about an hour before 32 bits will wrap. Also note that uclock reprograms the interval timer in your PC to act as a rate generator rather than a square wave generator. I've had no problems running in this mode all the time, but if you notice strange things happening with the clock (losing time) after using uclock, check to see if this is the cause of the problem. Windows 3.X doesn't allow to reprogram the timer so the values returned by uclock() there are incorrect. DOS and Windows 9X don't have this problem. Windows NT, 2000 and XP attempt to use the rdtsc feature of newer CPUs instead of the interval timer because the timer tick and interval timer are not coordinated. During calibration the SIGILL signal handler is replaced to protect against systems which do not support or allow rdtsc. If rdtsc is available, uclock will keep the upper bits of the returned value consistent with the bios tick counter by re-calibration if needed. If rdtsc is not available, these systems fall back to interval timer usage, which may show an absolute error of 65536 uclock ticks in the values and not be monotonically increasing. */ return (double) uclock() / (double) UCLOCKS_PER_SEC; #else int r; unsigned t, tick; double ft, time; static int sametimecount; Sys_PushFPCW_SetHigh (); t = *(unsigned short*)real2ptr(0x46c) * 65536; dos_outportb(0x43, 0); // latch time r = dos_inportb(0x40); r |= dos_inportb(0x40) << 8; r = (r - 1) & 0xffff; tick = *(unsigned short*)real2ptr(0x46c) * 65536; if ((tick != t) && (r & 0x8000)) t = tick; ft = (double) (t + (65536 - r)) / 1193200.0; time = ft - oldtime; oldtime = ft; if (time < 0) { if (time > -3000.0) time = 0.0; else time += 3600.0; } curtime += time; if (curtime == lastcurtime) { sametimecount++; if (sametimecount > 100000) { curtime += 1.0; sametimecount = 0; } } else { sametimecount = 0; } lastcurtime = curtime; Sys_PopFPCW (); return curtime; #endif /* ! USE_UCLOCK_TIME */ } /* ================ Sys_InitTime ================ */ static void Sys_InitTime (void) { #if !USE_UCLOCK_TIME int j; Sys_DoubleTime (); oldtime = curtime; j = COM_CheckParm("-starttime"); if (j && j < com_argc - 1) { curtime = (double) (atof(com_argv[j+1])); } else { curtime = 0.0; } lastcurtime = curtime; #endif /* ! USE_UCLOCK_TIME */ } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; struct _dosdate_t d; struct _dostime_t t; unsigned int val; if (!buf) buf = strbuf; _dos_getdate(&d); _dos_gettime(&t); val = d.month; buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = d.day; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = d.year / 100; buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = d.year % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = t.hour; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = t.minute; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = t.second; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } /* ================ Sys_GetMemory ================ */ static void Sys_GetMemory (void) { int j, tsize; j = COM_CheckParm("-mem"); if (j && j < com_argc - 1) { quakeparms.memsize = (int) (atof(com_argv[j + 1]) * 1024 * 1024); quakeparms.membase = malloc (quakeparms.memsize); } #ifdef GLQUAKE else { /* 16 mb is usually enough. Leave rest of mem for gl driver */ quakeparms.memsize = (int) 0x1000000; quakeparms.membase = malloc (quakeparms.memsize); } #else else { quakeparms.membase = dos_getmaxlockedmem (&quakeparms.memsize); } #endif printf("malloc'd: %d\n", quakeparms.memsize); j = COM_CheckParm ("-heapsize"); if (j && j < com_argc - 1) { tsize = atoi (com_argv[j + 1]) * 1024; if (tsize < quakeparms.memsize) quakeparms.memsize = tsize; } } /* ================ Sys_PageInProgram walks the text, data, and bss to make sure it's all paged in so that the actual physical memory detected by Sys_GetMemory is correct. ================ */ static void Sys_PageInProgram (void) { int i, j; end_of_memory = (int)sbrk(0); if (lockmem) { if (dos_lockmem ((void *)&start_of_memory, end_of_memory - (int)&start_of_memory)) Sys_Error ("Couldn't lock text and data"); } if (lockunlockmem) { dos_unlockmem((void *)&start_of_memory, end_of_memory - (int)&start_of_memory); printf ("Locked and unlocked %d Mb image\n", (end_of_memory - (int)&start_of_memory) / 0x100000); } else if (lockmem) { printf ("Locked %d Mb image\n", (end_of_memory - (int)&start_of_memory) / 0x100000); } else { printf ("Loaded %d Mb image\n", (end_of_memory - (int)&start_of_memory) / 0x100000); } // touch the entire image, doing the 16-page skip so Win95 doesn't think we're // trying to page ourselves in for (j = 0; j < 4; j++) { for (i = (int)&start_of_memory; i < (end_of_memory - 16 * 0x1000); i += 4) { sys_checksum += *(int *)i; sys_checksum += *(int *)(i + 16 * 0x1000); } } } /* ================ Sys_NoFPUExceptionHandler ================ */ static void Sys_NoFPUExceptionHandler (int whatever) { const char err[] = "\nError: Hexen II requires a floating-point processor\n"; const unsigned char *p; for (p = (const unsigned char *) err; *p; p++) putc (*p, stderr); exit (1); } /* ================ Sys_DefaultExceptionHandler ================ */ static void Sys_DefaultExceptionHandler (int whatever) { } static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { char *tmp; if (getcwd(dst, dstsize - 1) == NULL) return -1; tmp = dst; while (*tmp != 0) tmp++; while (*tmp == 0 && tmp != dst) { --tmp; if (tmp != dst && (*tmp == '/' || *tmp == '\\')) *tmp = 0; } return 0; } static void PrintVersion (void) { printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); printf ("running on %s engine %4.2f (%s)\n", ENGINE_NAME, ENGINE_VERSION, PLATFORM_STRING); printf ("More info / sending bug reports: http://uhexen2.sourceforge.net\n"); } /* ================ main ================ */ static char cwd[MAX_OSPATH]; int main (int argc, char **argv) { double time, oldtime, newtime; PrintVersion(); // make sure there's an FPU signal(SIGNOFP, Sys_NoFPUExceptionHandler); signal(SIGABRT, Sys_DefaultExceptionHandler); signal(SIGALRM, Sys_DefaultExceptionHandler); signal(SIGKILL, Sys_DefaultExceptionHandler); signal(SIGQUIT, Sys_DefaultExceptionHandler); signal(SIGINT, Sys_DefaultExceptionHandler); if (fptest_temp >= 0.0) fptest_temp += 0.1; /* initialize the host params */ memset (&quakeparms, 0, sizeof(quakeparms)); quakeparms.basedir = cwd; quakeparms.userdir = cwd; quakeparms.argc = argc; quakeparms.argv = argv; quakeparms.errstate = 0; host_parms = &quakeparms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); LOG_Init (&quakeparms); COM_ValidateByteorder (); Sys_DetectWin95 (); Sys_PageInProgram (); Sys_GetMemory (); atexit (Sys_AtExit); // in case we crash Sys_Init (); dos_registerintr(9, TrapKey); // Sys_InitStackCheck (); Host_Init(); // Sys_StackCheck (); // Con_Printf ("Top of stack: 0x%x\n", &time); oldtime = Sys_DoubleTime (); while (1) { newtime = Sys_DoubleTime (); time = newtime - oldtime; Host_Frame (time); // Sys_StackCheck (); oldtime = newtime; } } engine/hexenworld/client/sys_os2.c000066400000000000000000000271271444734033100175410ustar00rootroot00000000000000/* * sys_os2.c -- OS/2 system interface code * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2016 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(SDLQUAKE) #include "sys_sdl.h" /* alternative implementations using SDL. */ #endif #include "debuglog.h" #define INCL_DOS #define INCL_DOSERRORS #include #include #include #ifdef __WATCOMC__ #include #endif #include #if defined(SDLQUAKE) #include "sdl_inc.h" #endif #define MIN_MEM_ALLOC 0x1000000 #define STD_MEM_ALLOC 0x2000000 cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; cvar_t sys_throttle = {"sys_throttle", "0.02", CVAR_ARCHIVE}; /* =============================================================================== FILE IO =============================================================================== */ int Sys_mkdir (const char *path, qboolean crash) { FILESTATUS3 fs; APIRET rc = DosCreateDir(path, NULL); if (rc == NO_ERROR) return 0; if ((DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)) == NO_ERROR) && (fs.attrFile & FILE_DIRECTORY)) { return 0; /* dir exists */ } if (crash) Sys_Error("Unable to create directory %s (ERR: %lu)", path, rc); return -1; } int Sys_rmdir (const char *path) { APIRET rc = DosDeleteDir(path); return (rc == NO_ERROR)? 0 : -1; } int Sys_unlink (const char *path) { APIRET rc = DosDelete(path); return (rc == NO_ERROR)? 0 : -1; } int Sys_rename (const char *oldp, const char *newp) { APIRET rc = DosMove(oldp, newp); return (rc == NO_ERROR)? 0 : -1; } long Sys_filesize (const char *path) { FILESTATUS3 fs; APIRET rc = DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)); if (rc != NO_ERROR) return -1; if (fs.attrFile & FILE_DIRECTORY) return -1; return (long)fs.cbFile; } int Sys_FileType (const char *path) { FILESTATUS3 fs; APIRET rc = DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)); if (rc != NO_ERROR) return FS_ENT_NONE; if (fs.attrFile & FILE_DIRECTORY) return FS_ENT_DIRECTORY; return FS_ENT_FILE; } int Sys_CopyFile (const char *frompath, const char *topath) { APIRET rc = DosCopy(frompath, topath, DCPY_EXISTING); return (rc == NO_ERROR)? 0 : -1; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only, not a dirent struct. this is what we presently need in this engine. ================================================= */ static HDIR findhandle = HDIR_CREATE; static FILEFINDBUF3 findbuffer; static char findstr[MAX_OSPATH]; const char *Sys_FindFirstFile (const char *path, const char *pattern) { ULONG cnt = 1; APIRET rc; if (findhandle != HDIR_CREATE) Sys_Error ("Sys_FindFirst without FindClose"); q_snprintf (findstr, sizeof(findstr), "%s/%s", path, pattern); findbuffer.oNextEntryOffset = 0; rc = DosFindFirst(findstr, &findhandle, FILE_NORMAL, &findbuffer, sizeof(findbuffer), &cnt, FIL_STANDARD); if (rc != NO_ERROR) { findhandle = HDIR_CREATE; findbuffer.oNextEntryOffset = 0; return NULL; } if (findbuffer.attrFile & FILE_DIRECTORY) return Sys_FindNextFile(); return findbuffer.achName; } const char *Sys_FindNextFile (void) { APIRET rc; ULONG cnt; if (findhandle == HDIR_CREATE) return NULL; while (1) { cnt = 1; rc = DosFindNext(findhandle, &findbuffer, sizeof(findbuffer), &cnt); if (rc != NO_ERROR) return NULL; if (!(findbuffer.attrFile & FILE_DIRECTORY)) return findbuffer.achName; } return NULL; } void Sys_FindClose (void) { if (findhandle != HDIR_CREATE) { DosFindClose(findhandle); findhandle = HDIR_CREATE; findbuffer.oNextEntryOffset = 0; } } /* =============================================================================== SYSTEM IO =============================================================================== */ /* ================ Sys_MakeCodeWriteable ================ */ #if id386 && !defined(GLQUAKE) void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) { APIRET rc = DosSetMem((void *)startaddr, length, PAG_READ | PAG_WRITE | PAG_EXECUTE); if (rc != NO_ERROR) Sys_Error("Protection change failed (ERR: %lu)", rc); } #endif /* id386, !GLQUAKE */ /* ================ Sys_Init ================ */ static void Sys_Init (void) { /* do we really need these with opengl ?? */ Sys_SetFPCW(); #if defined(SDLQUAKE) if (SDL_Init(0) < 0) Sys_Error("SDL failed to initialize."); #endif } static void Sys_AtExit (void) { #if defined(SDLQUAKE) SDL_Quit(); #endif } #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const unsigned char *p; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (con_debuglog) { LOG_Print (ERROR_PREFIX); LOG_Print (text); LOG_Print ("\n\n"); } Host_Shutdown (); for (p = (const unsigned char *) text2; *p; p++) putc (*p, stderr); for (p = (const unsigned char *) text ; *p; p++) putc (*p, stderr); putc ('\n', stderr); putc ('\n', stderr); #if 0 WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, text, ENGINE_NAME " Error", 0, MB_OK | MB_MOVEABLE | MB_ERROR); #endif exit (1); } void Sys_PrintTerm (const char *msgtxt) { const unsigned char *p; if (sys_nostdout.integer) return; for (p = (const unsigned char *) msgtxt; *p; p++) putc (*p, stdout); } void Sys_Quit (void) { Host_Shutdown(); exit (0); } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { union i64 { QWORD qw; long long ll; }; static qboolean first = true; static ULONG ticks_per_sec; static union i64 start; union i64 now; if (first) { first = false; DosTmrQueryFreq(&ticks_per_sec); DosTmrQueryTime(&start.qw); return 0.0; } DosTmrQueryTime(&now.qw); return (double)(now.ll - start.ll) / (double)ticks_per_sec; } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; DATETIME dt; unsigned int val; if (!buf) buf = strbuf; DosGetDateTime (&dt); val = dt.month; buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = dt.day; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = dt.year / 100; buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = dt.year % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = dt.hours; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = dt.minutes; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = dt.seconds; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } void Sys_Sleep (unsigned long msecs) { DosSleep (msecs); } void Sys_SendKeyEvents (void) { IN_SendKeyEvents(); } #if !defined(Sys_GetClipboardData) #define Sys_GetClipboardData Sys_GetClipboardData /* */ char *Sys_GetClipboardData (void) { return NULL; } #endif static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { ULONG l, drv; if (dstsize < 8) return -1; l = dstsize - 3; if (DosQueryCurrentDir(0, (PBYTE) dst + 3, &l) != NO_ERROR) return -1; DosQueryCurrentDisk(&drv, &l); dst[0] = drv + 'A' - 1; dst[1] = ':'; dst[2] = '\\'; return 0; } static void Sys_CheckSDL (void) { #if defined(SDLQUAKE) const SDL_version *sdl_version; sdl_version = SDL_Linked_Version(); Sys_Printf("Found SDL version %i.%i.%i\n",sdl_version->major,sdl_version->minor,sdl_version->patch); #endif } static void PrintVersion (void) { Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); Sys_Printf ("running on %s engine %4.2f (%s)\n", ENGINE_NAME, ENGINE_VERSION, PLATFORM_STRING); Sys_Printf ("More info / sending bug reports: http://uhexen2.sourceforge.net\n"); } #include "snd_sys.h" static const char *help_strings[] = { " [-v | --version] Display version information", " [-noportals] Disable the mission pack support", " [-width X [-height Y]] Select screen size", #ifdef GLQUAKE " [-bpp] Depth for GL fullscreen mode", " [-vsync] Enable sync with monitor refresh", " [-g | -gllibrary] Select 3D rendering library", " [-fsaa N] Enable N sample antialiasing", " [-paltex] Enable 8-bit textures", " [-nomtex] Disable multitexture detection/usage", #endif #if !defined(_NO_SOUND) #if SOUND_NUMDRIVERS " [-s | -nosound] Run the game without sound", #endif #if (SOUND_NUMDRIVERS > 1) #if HAVE_SDL_SOUND " [-sndsdl] Use SDL sound", #endif #endif /* SOUND_NUMDRIVERS */ #endif /* _NO_SOUND */ " [-nomouse] Disable mouse usage", " [-heapsize Bytes] Heapsize (memory to allocate)", NULL }; static void PrintHelp (const char *name) { int i = 0; Sys_Printf ("Usage: %s [options]\n", name); while (help_strings[i]) { Sys_PrintTerm (help_strings[i]); Sys_PrintTerm ("\n"); i++; } Sys_PrintTerm ("\n"); } /* =============================================================================== MAIN =============================================================================== */ static quakeparms_t parms; static char cwd[MAX_OSPATH]; int main (int argc, char **argv) { int i; double time, oldtime, newtime; PrintVersion(); if (argc > 1) { for (i = 1; i < argc; i++) { if ( !(strcmp(argv[i], "-v")) || !(strcmp(argv[i], "-version" )) || !(strcmp(argv[i], "--version")) ) { exit(0); } else if ( !(strcmp(argv[i], "-h")) || !(strcmp(argv[i], "-help" )) || !(strcmp(argv[i], "-?")) || !(strcmp(argv[i], "--help")) ) { PrintHelp(argv[0]); exit (0); } } } /* initialize the host params */ memset (&parms, 0, sizeof(parms)); parms.basedir = cwd; parms.userdir = cwd; parms.argc = argc; parms.argv = argv; parms.errstate = 0; host_parms = &parms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); LOG_Init (&parms); Sys_Printf("basedir is: %s\n", parms.basedir); COM_ValidateByteorder (); Sys_CheckSDL (); parms.memsize = STD_MEM_ALLOC; i = COM_CheckParm ("-heapsize"); if (i && i < com_argc-1) parms.memsize = atoi (com_argv[i+1]) * 1024; parms.membase = malloc (parms.memsize); if (!parms.membase) Sys_Error ("Insufficient memory."); Sys_Init (); atexit (Sys_AtExit); Host_Init(); oldtime = Sys_DoubleTime (); /* main window message loop */ while (1) { #if defined(SDLQUAKE) /* If we have no input focus at all, sleep a bit */ if (!VID_HasMouseOrInputFocus() || cl.paused) { DosSleep (16); } /* If we're minimised, sleep a bit more */ if (VID_IsMinimized()) { scr_skipupdate = 1; DosSleep (32); } else { scr_skipupdate = 0; } #endif newtime = Sys_DoubleTime (); time = newtime - oldtime; Host_Frame (time); if (time < sys_throttle.value) DosSleep (1); oldtime = newtime; } return 0; } engine/hexenworld/client/sys_unix.c000066400000000000000000000345441444734033100200220ustar00rootroot00000000000000/* sys_unix.c -- Unix system interface code * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2001 contributors of the Anvil of Thyrion project * Copyright (C) 2004-2005 Steven Atkinson * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #if defined(PLATFORM_OSX) #include "sys_osx.h" /* Mac OS X specifics */ #elif defined(SDLQUAKE) #include "sys_sdl.h" /* alternative implementations using SDL. */ #endif #include "userdir.h" #include "debuglog.h" #include #include #if DO_USERDIRS #include #endif #include #include #include #include #include #include #include #include #include #if defined(SDLQUAKE) #include "sdl_inc.h" #endif #define MIN_MEM_ALLOC 0x1000000 #define STD_MEM_ALLOC 0x2000000 cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; cvar_t sys_throttle = {"sys_throttle", "0.02", CVAR_ARCHIVE}; static double starttime; static qboolean first = true; /* =============================================================================== FILE IO =============================================================================== */ int Sys_mkdir (const char *path, qboolean crash) { int rc = mkdir (path, 0777); if (rc != 0 && errno == EEXIST) { struct stat st; if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) rc = 0; } if (rc != 0 && crash) { rc = errno; Sys_Error("Unable to create directory %s: %s", path, strerror(rc)); } return rc; } int Sys_rmdir (const char *path) { return rmdir(path); } int Sys_unlink (const char *path) { return unlink(path); } int Sys_rename (const char *oldp, const char *newp) { return rename(oldp, newp); } long Sys_filesize (const char *path) { struct stat st; if (stat(path, &st) != 0) return -1; if (! S_ISREG(st.st_mode)) return -1; return (long) st.st_size; } int Sys_FileType (const char *path) { /* if (access(path, R_OK) == -1) return 0; */ struct stat st; if (stat(path, &st) != 0) return FS_ENT_NONE; if (S_ISDIR(st.st_mode)) return FS_ENT_DIRECTORY; if (S_ISREG(st.st_mode)) return FS_ENT_FILE; return FS_ENT_NONE; } #define COPY_READ_BUFSIZE 8192 /* BUFSIZ */ int Sys_CopyFile (const char *frompath, const char *topath) { char buf[COPY_READ_BUFSIZE]; FILE *in, *out; struct stat st; struct utimbuf tm; /* off_t remaining, count;*/ size_t remaining, count; if (stat(frompath, &st) != 0) { Con_Printf ("%s: unable to stat %s\n", __thisfunc__, frompath); return 1; } in = fopen (frompath, "rb"); if (!in) { Con_Printf ("%s: unable to open %s\n", __thisfunc__, frompath); return 1; } out = fopen (topath, "wb"); if (!out) { Con_Printf ("%s: unable to create %s\n", __thisfunc__, topath); fclose (in); return 1; } remaining = st.st_size; while (remaining) { if (remaining < sizeof(buf)) count = remaining; else count = sizeof(buf); if (fread(buf, 1, count, in) != count) break; if (fwrite(buf, 1, count, out) != count) break; remaining -= count; } fclose (in); fclose (out); if (remaining == 0) { /* restore the file's timestamp */ tm.actime = time (NULL); tm.modtime = st.st_mtime; utime (topath, &tm); return 0; } return 1; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only, not a dirent struct. this is what we presently need in this engine. ================================================= */ static DIR *finddir; static struct dirent *finddata; static char *findpath, *findpattern; const char *Sys_FindFirstFile (const char *path, const char *pattern) { if (finddir) Sys_Error ("Sys_FindFirst without FindClose"); finddir = opendir (path); if (!finddir) return NULL; findpattern = Z_Strdup (pattern); findpath = Z_Strdup (path); return Sys_FindNextFile(); } const char *Sys_FindNextFile (void) { struct stat test; if (!finddir) return NULL; while ((finddata = readdir(finddir)) != NULL) { if (!fnmatch (findpattern, finddata->d_name, FNM_PATHNAME)) { if ( (stat(va("%s/%s", findpath, finddata->d_name), &test) == 0) && S_ISREG(test.st_mode)) return finddata->d_name; } } return NULL; } void Sys_FindClose (void) { if (finddir != NULL) { closedir(finddir); finddir = NULL; } if (findpath != NULL) { Z_Free (findpath); findpath = NULL; } if (findpattern != NULL) { Z_Free (findpattern); findpattern = NULL; } } /* =============================================================================== SYSTEM IO =============================================================================== */ /* ================ Sys_MakeCodeWriteable ================ */ #if id386 && !defined(GLQUAKE) void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) { int r; unsigned long endaddr = startaddr + length; // systems with mprotect but not getpagesize (or similar) probably don't // need to page align the arguments to mprotect (eg, QNX) #if !(defined(__QNX__) || defined(__QNXNTO__)) // int psize = getpagesize (); long psize = sysconf (_SC_PAGESIZE); startaddr &= ~(psize - 1); endaddr = (endaddr + psize - 1) & ~(psize - 1); #endif r = mprotect ((char *) startaddr, endaddr - startaddr, PROT_WRITE | PROT_READ | PROT_EXEC); if (r == -1) Sys_Error("Protection change failed\n"); } #endif /* id386, !GLQUAKE */ /* ================ Sys_Init ================ */ static void Sys_Init (void) { /* do we really need these with opengl ?? */ Sys_SetFPCW(); #if defined(SDLQUAKE) if (SDL_Init(0) < 0) Sys_Error("SDL failed to initialize."); #endif } static void Sys_AtExit (void) { #if defined(SDLQUAKE) SDL_Quit(); #endif } #if !defined(Sys_ErrorMessage) #define Sys_ErrorMessage(T) do {} while (0) #endif #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const unsigned char *p; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (con_debuglog) { LOG_Print (ERROR_PREFIX); LOG_Print (text); LOG_Print ("\n\n"); } Host_Shutdown (); for (p = (const unsigned char *) text2; *p; p++) putc (*p, stderr); for (p = (const unsigned char *) text ; *p; p++) putc (*p, stderr); putc ('\n', stderr); putc ('\n', stderr); Sys_ErrorMessage (text); exit (1); } void Sys_PrintTerm (const char *msgtxt) { const unsigned char *p; if (sys_nostdout.integer) return; for (p = (const unsigned char *) msgtxt; *p; p++) putc (*p, stdout); } void Sys_Quit (void) { Host_Shutdown(); exit (0); } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { struct timeval tp; double now; gettimeofday (&tp, NULL); now = tp.tv_sec + tp.tv_usec / 1e6; if (first) { first = false; starttime = now; return 0.0; } return now - starttime; } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; time_t t; struct tm *l; int val; if (!buf) buf = strbuf; t = time(NULL); l = localtime(&t); val = l->tm_mon + 1; /* tm_mon: months since January [0,11] */ buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = l->tm_mday; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = l->tm_year / 100 + 19; /* tm_year: #years since 1900. */ buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = l->tm_year % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = l->tm_hour; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = l->tm_min; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = l->tm_sec; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } void Sys_Sleep (unsigned long msecs) { usleep (msecs * 1000); } void Sys_SendKeyEvents (void) { IN_SendKeyEvents(); } #if !defined(Sys_GetClipboardData) #define Sys_GetClipboardData Sys_GetClipboardData /* */ char *Sys_GetClipboardData (void) { return NULL; } #endif #if !defined(Sys_GetBasedir) #define Sys_GetBasedir UNIX_GetBasedir static int UNIX_GetBasedir (char *argv0, char *dst, size_t dstsize) { char *tmp; if (getcwd(dst, dstsize - 1) == NULL) return -1; tmp = dst; while (*tmp != 0) tmp++; while (*tmp == 0 && tmp != dst) { --tmp; if (tmp != dst && *tmp == '/') *tmp = 0; } return 0; } #endif /* Sys_GetBasedir */ #if DO_USERDIRS static int Sys_GetUserdir (char *dst, size_t dstsize) { size_t n; const char *home_dir = NULL; struct passwd *pwent; pwent = getpwuid(getuid()); if (pwent == NULL) perror("getpwuid"); else home_dir = pwent->pw_dir; if (home_dir == NULL) home_dir = getenv("HOME"); if (home_dir == NULL) return 1; /* what would be a maximum path for a file in the user's directory... * $HOME/AOT_USERDIR/game_dir/dirname1/dirname2/dirname3/filename.ext * still fits in the MAX_OSPATH == 256 definition, but just in case. */ n = strlen(home_dir) + strlen(AOT_USERDIR) + 50; if (n >= dstsize) { Sys_Error ("%s: Insufficient bufsize %d. Need at least %d.", __thisfunc__, (int)dstsize, (int)n); } q_snprintf (dst, dstsize, "%s/%s", home_dir, AOT_USERDIR); return 0; } #endif /* DO_USERDIRS */ static void Sys_CheckSDL (void) { #if defined(SDLQUAKE) const SDL_version *sdl_version; sdl_version = SDL_Linked_Version(); Sys_Printf("Found SDL version %i.%i.%i\n",sdl_version->major,sdl_version->minor,sdl_version->patch); #endif } static void PrintVersion (void) { Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); Sys_Printf ("running on %s engine %4.2f (%s)\n", ENGINE_NAME, ENGINE_VERSION, PLATFORM_STRING); Sys_Printf ("More info / sending bug reports: http://uhexen2.sourceforge.net\n"); } #include "snd_sys.h" static const char *help_strings[] = { " [-v | --version] Display version information", " [-noportals] Disable the mission pack support", #ifndef SVGAQUAKE " [-w | -window ] Run the game windowed", " [-f | -fullscreen] Run the game fullscreen", #endif " [-width X [-height Y]] Select screen size", #ifdef GLQUAKE " [-bpp] Depth for GL fullscreen mode", " [-vsync] Enable sync with monitor refresh", " [-g | -gllibrary] Select 3D rendering library", " [-fsaa N] Enable N sample antialiasing", " [-paltex] Enable 8-bit textures", " [-nomtex] Disable multitexture detection/usage", #endif #if !defined(_NO_SOUND) #if SOUND_NUMDRIVERS " [-s | -nosound] Run the game without sound", #endif #if (SOUND_NUMDRIVERS > 1) #if HAVE_OSS_SOUND " [-sndoss] Use OSS sound", #endif #if HAVE_ALSA_SOUND " [-sndalsa] Use ALSA sound (alsa > 1.0.1)", #endif #if HAVE_SUN_SOUND " [-sndsun | -sndbsd] Use SUN / BSD sound", #endif #if HAVE_SDL_SOUND " [-sndsdl] Use SDL sound", #endif #endif /* SOUND_NUMDRIVERS */ #endif /* _NO_SOUND */ " [-nomouse] Disable mouse usage", " [-heapsize Bytes] Heapsize (memory to allocate)", NULL }; static void PrintHelp (const char *name) { int i = 0; Sys_Printf ("Usage: %s [options]\n", name); while (help_strings[i]) { Sys_PrintTerm (help_strings[i]); Sys_PrintTerm ("\n"); i++; } Sys_PrintTerm ("\n"); } /* =============================================================================== MAIN =============================================================================== */ static quakeparms_t parms; static char cwd[MAX_OSPATH]; #if DO_USERDIRS static char userdir[MAX_OSPATH]; #endif int main (int argc, char **argv) { int i; double time, oldtime, newtime; PrintVersion(); if (argc > 1) { for (i = 1; i < argc; i++) { if ( !(strcmp(argv[i], "-v")) || !(strcmp(argv[i], "-version" )) || !(strcmp(argv[i], "--version")) ) { exit(0); } else if ( !(strcmp(argv[i], "-h")) || !(strcmp(argv[i], "-help" )) || !(strcmp(argv[i], "-?")) || !(strcmp(argv[i], "--help")) ) { PrintHelp(argv[0]); exit (0); } } } /* initialize the host params */ memset (&parms, 0, sizeof(parms)); parms.basedir = cwd; parms.userdir = cwd; parms.argc = argc; parms.argv = argv; parms.errstate = 0; host_parms = &parms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); #if DO_USERDIRS memset (userdir, 0, sizeof(userdir)); if (Sys_GetUserdir(userdir, sizeof(userdir)) != 0) Sys_Error ("Couldn't determine userspace directory"); Sys_mkdir(userdir, true); parms.userdir = userdir; #endif LOG_Init (&parms); Sys_Printf("basedir is: %s\n", parms.basedir); Sys_Printf("userdir is: %s\n", parms.userdir); COM_ValidateByteorder (); Sys_CheckSDL (); parms.memsize = STD_MEM_ALLOC; i = COM_CheckParm ("-heapsize"); if (i && i < com_argc-1) parms.memsize = atoi (com_argv[i+1]) * 1024; parms.membase = malloc (parms.memsize); if (!parms.membase) Sys_Error ("Insufficient memory."); Sys_Init (); atexit (Sys_AtExit); Host_Init(); oldtime = Sys_DoubleTime (); /* main window message loop */ while (1) { #if defined(SDLQUAKE) /* If we have no input focus at all, sleep a bit */ if (!VID_HasMouseOrInputFocus() || cl.paused) { usleep (16000); } /* If we're minimised, sleep a bit more */ if (VID_IsMinimized()) { scr_skipupdate = 1; usleep (32000); } else { scr_skipupdate = 0; } #endif newtime = Sys_DoubleTime (); time = newtime - oldtime; Host_Frame (time); if (time < sys_throttle.value) usleep (1000); oldtime = newtime; } return 0; } engine/hexenworld/client/sys_win.c000066400000000000000000000347031444734033100176310ustar00rootroot00000000000000/* sys_win.c -- Windows system interface code * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "debuglog.h" #include "winquake.h" #include #include "resource.h" #define MIN_MEM_ALLOC 0x1000000 #define STD_MEM_ALLOC 0x2000000 #define PAUSE_SLEEP 50 /* sleep time on pause or minimization */ #define NOT_FOCUS_SLEEP 20 /* sleep time when not focus */ cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; cvar_t sys_throttle = {"sys_throttle", "0.02", CVAR_ARCHIVE}; qboolean ActiveApp, Minimized; qboolean Win95, Win95old, WinNT, WinVista; #define TIME_WRAP_VALUE (~(DWORD)0) static DWORD starttime; static HANDLE qwclsemaphore; /* =============================================================================== FILE IO =============================================================================== */ int Sys_mkdir (const char *path, qboolean crash) { if (CreateDirectory(path, NULL) != 0) return 0; if (GetLastError() == ERROR_ALREADY_EXISTS) return 0; if (crash) Sys_Error("Unable to create directory %s", path); return -1; } int Sys_rmdir (const char *path) { if (RemoveDirectory(path) != 0) return 0; return -1; } int Sys_unlink (const char *path) { if (DeleteFile(path) != 0) return 0; return -1; } int Sys_rename (const char *oldp, const char *newp) { if (MoveFile(oldp, newp) != 0) return 0; return -1; } long Sys_filesize (const char *path) { HANDLE fh; WIN32_FIND_DATA data; long size; fh = FindFirstFile(path, &data); if (fh == INVALID_HANDLE_VALUE) return -1; FindClose(fh); if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) return -1; // we're not dealing with gigabytes of files. // size should normally smaller than INT_MAX. // size = (data.nFileSizeHigh * (MAXDWORD + 1)) + data.nFileSizeLow; size = (long) data.nFileSizeLow; return size; } #ifndef INVALID_FILE_ATTRIBUTES #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #endif int Sys_FileType (const char *path) { DWORD result = GetFileAttributes(path); if (result == INVALID_FILE_ATTRIBUTES) return FS_ENT_NONE; if (result & FILE_ATTRIBUTE_DIRECTORY) return FS_ENT_DIRECTORY; return FS_ENT_FILE; } int Sys_CopyFile (const char *frompath, const char *topath) { /* 3rd param: whether to fail if 'topath' already exists */ if (CopyFile(frompath, topath, FALSE) != 0) return 0; return -1; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only, not a dirent struct. this is what we presently need in this engine. ================================================= */ static HANDLE findhandle = INVALID_HANDLE_VALUE; static WIN32_FIND_DATA finddata; static char findstr[MAX_OSPATH]; const char *Sys_FindFirstFile (const char *path, const char *pattern) { if (findhandle != INVALID_HANDLE_VALUE) Sys_Error ("Sys_FindFirst without FindClose"); q_snprintf (findstr, sizeof(findstr), "%s/%s", path, pattern); findhandle = FindFirstFile(findstr, &finddata); if (findhandle == INVALID_HANDLE_VALUE) return NULL; if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) return Sys_FindNextFile(); return finddata.cFileName; } const char *Sys_FindNextFile (void) { if (findhandle == INVALID_HANDLE_VALUE) return NULL; while (FindNextFile(findhandle, &finddata) != 0) { if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; return finddata.cFileName; } return NULL; } void Sys_FindClose (void) { if (findhandle != INVALID_HANDLE_VALUE) { FindClose(findhandle); findhandle = INVALID_HANDLE_VALUE; } } /* =============================================================================== SYSTEM IO =============================================================================== */ /* ================ Sys_MakeCodeWriteable ================ */ #if id386 && !defined(GLQUAKE) void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length) { DWORD flOldProtect; //@@@ copy on write or just read-write? if (!VirtualProtect((LPVOID)startaddr, length, PAGE_EXECUTE_READWRITE, &flOldProtect)) Sys_Error("Protection change failed\n"); } #endif /* id386, !GLQUAKE */ /* ================ Sys_SetDPIAware ================ */ typedef enum { dpi_unaware = 0, dpi_system_aware = 1, dpi_monitor_aware = 2 } dpi_awareness; typedef BOOL (WINAPI *SetProcessDPIAwareFunc)(); typedef HRESULT (WINAPI *SetProcessDPIAwarenessFunc)(dpi_awareness value); static void Sys_SetDPIAware (void) { HMODULE hUser32, hShcore; SetProcessDPIAwarenessFunc setDPIAwareness; SetProcessDPIAwareFunc setDPIAware; /* We do not handle the OS scaling our window. Call * SetProcessDpiAwareness() or SetProcessDPIAware() * to opt out of scaling. */ hShcore = LoadLibraryA ("Shcore.dll"); hUser32 = LoadLibraryA ("user32.dll"); setDPIAwareness = (SetProcessDPIAwarenessFunc) (hShcore ? GetProcAddress (hShcore, "SetProcessDpiAwareness") : NULL); setDPIAware = (SetProcessDPIAwareFunc) (hUser32 ? GetProcAddress (hUser32, "SetProcessDPIAware") : NULL); if (setDPIAwareness) /* Windows 8.1+ */ setDPIAwareness (dpi_monitor_aware); else if (setDPIAware) /* Vista, Win7 or 8.0 */ setDPIAware (); if (hShcore) FreeLibrary (hShcore); if (hUser32) FreeLibrary (hUser32); } /* ================ Sys_Init ================ */ static void Sys_Init (void) { OSVERSIONINFO vinfo; vinfo.dwOSVersionInfoSize = sizeof(vinfo); if (!GetVersionEx (&vinfo)) Sys_Error ("Couldn't get OS info"); if ((vinfo.dwMajorVersion < 4) || (vinfo.dwPlatformId == VER_PLATFORM_WIN32s)) { Sys_Error ("%s requires at least Win95 or NT 4.0", ENGINE_NAME); } if (vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT) { WinNT = true; if (vinfo.dwMajorVersion >= 6) WinVista = true; } else { WinNT = false; /* Win9x or WinME */ if ((vinfo.dwMajorVersion == 4) && (vinfo.dwMinorVersion == 0)) { Win95 = true; /* Win95-gold or Win95A can't switch bpp automatically */ if (vinfo.szCSDVersion[1] != 'C' && vinfo.szCSDVersion[1] != 'B') Win95old = true; } } Sys_SetDPIAware (); timeBeginPeriod (1); /* 1 ms timer precision */ starttime = timeGetTime (); /* allocate a named semaphore on the client so the front end can tell if it is alive mutex will fail if semephore already exists */ qwclsemaphore = CreateMutex( NULL, /* Security attributes */ 0, /* owner */ "hwcl");/* Semaphore name */ if (!qwclsemaphore) Sys_Error ("HWCL is already running on this system"); CloseHandle (qwclsemaphore); qwclsemaphore = CreateSemaphore( NULL, /* Security attributes */ 0, /* Initial count */ 1, /* Maximum count */ "hwcl");/* Semaphore name */ /* do we really need these with opengl ?? */ MaskExceptions (); Sys_SetFPCW (); } #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof (text), error, argptr); va_end (argptr); if (con_debuglog) { LOG_Print (ERROR_PREFIX); LOG_Print (text); LOG_Print ("\n\n"); } Host_Shutdown (); MessageBox(NULL, text, ENGINE_NAME " Error", MB_OK | MB_SETFOREGROUND | MB_ICONSTOP); if (qwclsemaphore) CloseHandle (qwclsemaphore); exit (1); } void Sys_PrintTerm (const char *msgtxt) { /* no stdout for win32 graphical mode */ } void Sys_Quit (void) { Host_Shutdown(); if (qwclsemaphore) CloseHandle (qwclsemaphore); exit (0); } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { DWORD now, passed; now = timeGetTime(); if (now < starttime) /* wrapped? */ { passed = TIME_WRAP_VALUE - starttime; passed += now; } else { passed = now - starttime; } return (passed == 0) ? 0.0 : (passed / 1000.0); } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; SYSTEMTIME st; int val; if (!buf) buf = strbuf; GetLocalTime(&st); val = st.wMonth; buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = st.wDay; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = st.wYear / 100; buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = st.wYear % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = st.wHour; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = st.wMinute; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = st.wSecond; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } void Sys_Sleep (unsigned long msecs) { Sleep (msecs); } void Sys_SendKeyEvents (void) { MSG msg; while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) { // we always update if there are any event, even if we're paused scr_skipupdate = 0; if (!GetMessage (&msg, NULL, 0, 0)) Sys_Quit (); TranslateMessage (&msg); DispatchMessage (&msg); } } #define MAX_CLIPBOARDTXT MAXCMDLINE /* 256 */ char *Sys_GetClipboardData (void) { char *data = NULL; char *cliptext; if (OpenClipboard(NULL) != 0) { HANDLE hClipboardData; if ((hClipboardData = GetClipboardData(CF_TEXT)) != NULL) { cliptext = (char *) GlobalLock(hClipboardData); if (cliptext != NULL) { size_t size = GlobalSize(hClipboardData) + 1; /* this is intended for simple small text copies * such as an ip address, etc: do chop the size * here, otherwise we may experience Z_Malloc() * failures and all other not-oh-so-fun stuff. */ size = q_min(MAX_CLIPBOARDTXT, size); data = (char *) Z_Malloc(size, Z_MAINZONE); q_strlcpy (data, cliptext, size); GlobalUnlock (hClipboardData); } } CloseClipboard (); } return data; } static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { char *tmp; size_t rc; rc = GetCurrentDirectory(dstsize, dst); if (rc == 0 || rc > dstsize) return -1; tmp = dst; while (*tmp != 0) tmp++; while (*tmp == 0 && tmp != dst) { --tmp; if (tmp != dst && (*tmp == '/' || *tmp == '\\')) *tmp = 0; } return 0; } /* ============================================================================== WINDOWS CRAP ============================================================================== */ /* ================== WinMain ================== */ HINSTANCE global_hInstance; int global_nCmdShow; #if !defined(NO_SPLASHES) HWND hwnd_dialog; #endif /* NO_SPLASHES */ static char *argv[MAX_NUM_ARGVS]; static char cwd[1024]; static char prog[MAX_PATH]; static quakeparms_t parms; static void Sys_CreateInitSplash (HINSTANCE hInstance) { #if !defined(NO_SPLASHES) RECT rect; hwnd_dialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, NULL); if (!hwnd_dialog) return; if (GetWindowRect (hwnd_dialog, &rect)) { if (rect.left > (rect.top * 2)) { SetWindowPos (hwnd_dialog, 0, (rect.left / 2) - ((rect.right - rect.left) / 2), rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); } } ShowWindow (hwnd_dialog, SW_SHOWDEFAULT); UpdateWindow (hwnd_dialog); SetForegroundWindow (hwnd_dialog); #endif /* NO_SPLASHES */ } int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int i; double time, oldtime, newtime; MEMORYSTATUS lpBuffer; /* previous instances do not exist in Win32 */ if (hPrevInstance) return 0; global_hInstance = hInstance; global_nCmdShow = nCmdShow; lpBuffer.dwLength = sizeof(MEMORYSTATUS); GlobalMemoryStatus (&lpBuffer); /* Limit to 2 GB to work around signed int */ if (lpBuffer.dwAvailPhys > 0x7FFFFFFF) lpBuffer.dwAvailPhys = 0x7FFFFFFF; if (lpBuffer.dwTotalPhys > 0x7FFFFFFF) lpBuffer.dwTotalPhys = 0x7FFFFFFF; memset (&parms, 0, sizeof(parms)); parms.basedir = cwd; parms.userdir = cwd; /* no userdir on win32 */ parms.errstate = 0; host_parms = &parms; /* initialize the host params */ memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(NULL, cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); parms.argc = 1; argv[0] = prog; if (GetModuleFileName(NULL, prog, sizeof(prog)) == 0) prog[0] = '\0'; else prog[MAX_PATH - 1] = '\0'; while (*lpCmdLine && (parms.argc < MAX_NUM_ARGVS)) { while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126))) lpCmdLine++; if (*lpCmdLine) { argv[parms.argc] = lpCmdLine; parms.argc++; while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126))) lpCmdLine++; if (*lpCmdLine) { *lpCmdLine = 0; lpCmdLine++; } } } parms.argv = argv; LOG_Init (&parms); Sys_Printf("basedir is: %s\n", parms.basedir); Sys_Printf("userdir is: %s\n", parms.userdir); COM_ValidateByteorder (); Sys_CreateInitSplash (global_hInstance); // take the greater of all the available memory or half the total memory, // but at least 16 Mb and no more than 32 Mb, unless explicitly requested. parms.memsize = lpBuffer.dwAvailPhys; if (parms.memsize < MIN_MEM_ALLOC) parms.memsize = MIN_MEM_ALLOC; if (parms.memsize < (int) (lpBuffer.dwTotalPhys >> 1)) parms.memsize = (int) (lpBuffer.dwTotalPhys >> 1); if (parms.memsize > STD_MEM_ALLOC) parms.memsize = STD_MEM_ALLOC; i = COM_CheckParm ("-heapsize"); if (i && i < com_argc-1) parms.memsize = atoi (com_argv[i+1]) * 1024; parms.membase = malloc (parms.memsize); if (!parms.membase) Sys_Error ("Insufficient memory."); Sys_Init (); Host_Init(); oldtime = Sys_DoubleTime (); /* main window message loop */ while (1) { /* yield the CPU for a little while when paused, minimized or not focused */ if ((cl.paused && !ActiveApp) || Minimized || block_drawing) { Sleep (PAUSE_SLEEP); scr_skipupdate = 1; /* no point in bothering to draw */ } else if (!ActiveApp) { Sleep (NOT_FOCUS_SLEEP); } newtime = Sys_DoubleTime (); time = newtime - oldtime; Host_Frame (time); if (time < sys_throttle.value) Sleep (1); oldtime = newtime; } return 0; } engine/hexenworld/client/view.c000066400000000000000000000631331444734033100171070ustar00rootroot00000000000000/* * view.c -- player eye positioning * * The view is allowed to move slightly from it's true position * for bobbing, but if it exceeds 8 pixels linear distance * (spherical, not box), the list of entities sent from the server * may not include everything in the pvs, especially when crossing * a water boudnary. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" static float v_dmg_time, v_dmg_roll, v_dmg_pitch; static cvar_t cl_bob = {"cl_bob", "0.02", CVAR_NONE}; static cvar_t cl_bobcycle = {"cl_bobcycle", "0.6", CVAR_NONE}; static cvar_t cl_bobup = {"cl_bobup", "0.5", CVAR_NONE}; static cvar_t v_kicktime = {"v_kicktime", "0.5", CVAR_NONE}; static cvar_t v_kickroll = {"v_kickroll", "0.6", CVAR_NONE}; static cvar_t v_kickpitch = {"v_kickpitch", "0.6", CVAR_NONE}; static cvar_t v_iyaw_cycle = {"v_iyaw_cycle", "2", CVAR_NONE}; static cvar_t v_iroll_cycle = {"v_iroll_cycle", "0.5", CVAR_NONE}; static cvar_t v_ipitch_cycle = {"v_ipitch_cycle", "1", CVAR_NONE}; static cvar_t v_iyaw_level = {"v_iyaw_level", "0.3", CVAR_NONE}; static cvar_t v_iroll_level = {"v_iroll_level", "0.1", CVAR_NONE}; static cvar_t v_ipitch_level = {"v_ipitch_level", "0.3", CVAR_NONE}; static cvar_t v_idlescale = {"v_idlescale", "0", CVAR_NONE}; cvar_t cl_rollspeed = {"cl_rollspeed", "200", CVAR_NONE}; cvar_t cl_rollangle = {"cl_rollangle", "2.0", CVAR_NONE}; cvar_t crosshair = {"crosshair", "0", CVAR_ARCHIVE}; cvar_t cl_crossx = {"cl_crossx", "0", CVAR_ARCHIVE}; cvar_t cl_crossy = {"cl_crossy", "0", CVAR_ARCHIVE}; cvar_t crosshaircolor = {"crosshaircolor", "75", CVAR_ARCHIVE}; // 79 seemed too bright float v_targAngle, v_targPitch, v_targDist = 0.0; frame_t *view_frame; player_state_t *view_message; //============================================================================= /* =============== V_CalcRoll =============== */ float V_CalcRoll (vec3_t angles, vec3_t velocity) { vec3_t forward, right, up; float sign; float side; float value; AngleVectors (angles, forward, right, up); side = DotProduct (velocity, right); sign = side < 0 ? -1 : 1; side = fabs(side); value = cl_rollangle.value; if (side < cl_rollspeed.value) side = side * value / cl_rollspeed.value; else side = value; return side*sign; } /* =============== V_CalcBob =============== */ static float V_CalcBob (void) { static float bob; float cycle; if (cl.spectator) return 0; // if (onground == -1) // return bob; // just use old value cycle = cl.time - (int)(cl.time/cl_bobcycle.value)*cl_bobcycle.value; cycle /= cl_bobcycle.value; if (cycle < cl_bobup.value) cycle = M_PI * cycle / cl_bobup.value; else cycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value); // bob is proportional to simulated velocity in the xy plane // (don't count Z, or jumping messes it up) bob = Q_sqrt(cl.simvel[0]*cl.simvel[0] + cl.simvel[1]*cl.simvel[1]) * cl_bob.value; bob = bob*0.3 + bob*0.7*q_sinrad(cycle); if (bob > 4) bob = 4; else if (bob < -7) bob = -7; return bob; } //============================================================================= static cvar_t v_centermove = {"v_centermove", "0.15", CVAR_NONE}; static cvar_t v_centerspeed = {"v_centerspeed", "500", CVAR_NONE}; static cvar_t v_centerrollspeed = {"v_centerrollspeed", "125", CVAR_NONE}; void V_StartPitchDrift (void) { #if 1 if (cl.laststop == cl.time) return; // something else is keeping it from drifting #endif if (cl.nodrift || !cl.pitchvel) { cl.pitchvel = v_centerspeed.value; cl.nodrift = false; cl.driftmove = 0; } } void V_StopPitchDrift (void) { cl.laststop = cl.time; cl.nodrift = true; cl.pitchvel = 0; } /* =============== V_DriftPitch Moves the client pitch angle towards cl.idealpitch sent by the server. If the user is adjusting pitch manually, either with lookup/lookdown, mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped. Drifting is enabled when the center view key is hit, mlook is released and lookspring is non 0, or when =============== */ static void V_DriftPitch (void) { float delta, move; if (view_message->onground == -1 || cls.demoplayback) { cl.driftmove = 0; cl.pitchvel = 0; return; } // don't count small mouse motion if (cl.nodrift) { if (lookspring.integer == 0 || abs(cl.frames[(cls.netchan.outgoing_sequence-1)&UPDATE_MASK].cmd.forwardmove) < (int)(cl.v.hasted*cl_forwardspeed.value)-10) cl.driftmove = 0; else cl.driftmove += host_frametime; if (cl.spectator) { cl.driftmove = 0; } if ( cl.driftmove > v_centermove.value) { if (lookspring.integer) V_StartPitchDrift (); } return; } delta = cl.idealpitch - cl.viewangles[PITCH]; if (!delta) { cl.pitchvel = 0; return; } move = host_frametime * cl.pitchvel; cl.pitchvel += host_frametime * v_centerspeed.value; if (delta > 0) { if (move > delta) { cl.pitchvel = 0; move = delta; } cl.viewangles[PITCH] += move; } else if (delta < 0) { if (move > -delta) { cl.pitchvel = 0; move = -delta; } cl.viewangles[PITCH] -= move; } } /* =============== V_DriftRoll Moves the client pitch angle towards cl.idealroll sent by the server. If the user is adjusting pitch manually, either with lookup/lookdown, mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped. =============== */ static void V_DriftRoll (void) { float delta, move; float rollspeed; if (view_message->onground == -1 || cls.demoplayback) { if (cl.v.movetype != MOVETYPE_FLY) { cl.rollvel = 0; return; } } if (cl.v.movetype != MOVETYPE_FLY) rollspeed = v_centerrollspeed.value; else rollspeed = v_centerrollspeed.value * .5; //slower roll when flying delta = cl.idealroll - cl.viewangles[ROLL]; if (!delta) { cl.rollvel = 0; return; } move = host_frametime * cl.rollvel; cl.rollvel += host_frametime * rollspeed; if (delta > 0) { if (move > delta) { cl.rollvel = 0; move = delta; } cl.viewangles[ROLL] += move; } else if (delta < 0) { if (move > -delta) { cl.rollvel = 0; move = -delta; } cl.viewangles[ROLL] -= move; } } /* =============== V_ParseTarget =============== */ void V_ParseTarget(void) { v_targAngle = MSG_ReadByte(); v_targPitch = MSG_ReadByte(); v_targDist = MSG_ReadByte(); } /* ============================================================================== PALETTE FLASHES ============================================================================== */ cshift_t cshift_empty = { {130,80,50}, 0 }; cshift_t cshift_water = { {130,80,50}, 128 }; cshift_t cshift_slime = { {0,25,5}, 150 }; cshift_t cshift_lava = { {255,80,0}, 150 }; cvar_t v_gamma = {"gamma", "1", CVAR_ARCHIVE}; byte gammatable[256]; // palette is sent through this static void BuildGammaTable (float g) { int i, inf; if (g == 1.0) { for (i = 0; i < 256; i++) gammatable[i] = i; return; } for (i = 0; i < 256; i++) { inf = 255 * pow((i + 0.5) / 255.5, g) + 0.5; if (inf < 0) inf = 0; else if (inf > 255) inf = 255; gammatable[i] = inf; } } /* =============== V_ParseDamage =============== */ void V_ParseDamage (void) { int armor, blood; vec3_t from; int i; vec3_t forward, right, up; float side; float count; armor = MSG_ReadByte (); blood = MSG_ReadByte (); for (i = 0; i < 3; i++) from[i] = MSG_ReadCoord (); count = blood*0.5 + armor*0.5; if (count < 10) count = 10; cl.faceanimtime = cl.time + 0.2; // put sbar face into pain frame cl.cshifts[CSHIFT_DAMAGE].percent += 3 * count; if (cl.cshifts[CSHIFT_DAMAGE].percent < 0) cl.cshifts[CSHIFT_DAMAGE].percent = 0; else if (cl.cshifts[CSHIFT_DAMAGE].percent > 150) cl.cshifts[CSHIFT_DAMAGE].percent = 150; if (armor > blood) { cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200; cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100; cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100; } else if (armor) { cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220; cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50; cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50; } else { cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255; cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0; cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0; } // // calculate view angle kicks // VectorSubtract (from, cl.simorg, from); VectorNormalizeFast (from); AngleVectors (cl.simangles, forward, right, up); side = DotProduct (from, right); v_dmg_roll = count*side*v_kickroll.value; side = DotProduct (from, forward); v_dmg_pitch = count*side*v_kickpitch.value; v_dmg_time = v_kicktime.value; } /* ================== V_cshift_f ================== */ static void V_cshift_f (void) { cshift_empty.destcolor[0] = atoi(Cmd_Argv(1)); cshift_empty.destcolor[1] = atoi(Cmd_Argv(2)); cshift_empty.destcolor[2] = atoi(Cmd_Argv(3)); cshift_empty.percent = atoi(Cmd_Argv(4)); } /* ================== V_BonusFlash_f When you run over an item, the server sends this command ================== */ static void V_BonusFlash_f (void) { cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215; cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186; cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69; cl.cshifts[CSHIFT_BONUS].percent = 50; } static void V_DarkFlash_f (void) { cl.cshifts[CSHIFT_BONUS].destcolor[0] = 0; cl.cshifts[CSHIFT_BONUS].destcolor[1] = 0; cl.cshifts[CSHIFT_BONUS].destcolor[2] = 0; cl.cshifts[CSHIFT_BONUS].percent = 255; } static void V_WhiteFlash_f (void) { cl.cshifts[CSHIFT_BONUS].destcolor[0] = 255; cl.cshifts[CSHIFT_BONUS].destcolor[1] = 255; cl.cshifts[CSHIFT_BONUS].destcolor[2] = 255; cl.cshifts[CSHIFT_BONUS].percent = 255; } /* ============= V_SetContentsColor Underwater, lava, etc each has a color shift ============= */ void V_SetContentsColor (int contents) { switch (contents) { case CONTENTS_EMPTY: case CONTENTS_SOLID: cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; break; case CONTENTS_LAVA: cl.cshifts[CSHIFT_CONTENTS] = cshift_lava; break; case CONTENTS_SLIME: cl.cshifts[CSHIFT_CONTENTS] = cshift_slime; break; default: cl.cshifts[CSHIFT_CONTENTS] = cshift_water; } } /* ============= V_CalcPowerupCshift ============= */ static void V_CalcPowerupCshift(void) { if ((int)cl.v.artifact_active&ARTFLAG_DIVINE_INTERVENTION) { cl.cshifts[CSHIFT_BONUS].destcolor[0] = 255; cl.cshifts[CSHIFT_BONUS].destcolor[1] = 255; cl.cshifts[CSHIFT_BONUS].destcolor[2] = 255; cl.cshifts[CSHIFT_BONUS].percent = 256; } if ((int)cl.v.artifact_active&ARTFLAG_FROZEN) { cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 20; cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 70; cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255; cl.cshifts[CSHIFT_POWERUP].percent = 65; } else if ((int)cl.v.artifact_active&ARTFLAG_STONED) { cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 205; cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 205; cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 205; //cl.cshifts[CSHIFT_POWERUP].percent = 80; cl.cshifts[CSHIFT_POWERUP].percent = 11000; } else if ((int)cl.v.artifact_active&ART_INVISIBILITY) { cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100; cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100; cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100; cl.cshifts[CSHIFT_POWERUP].percent = 100; } else if ((int)cl.v.artifact_active&ART_INVINCIBILITY) { cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255; cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; cl.cshifts[CSHIFT_POWERUP].percent = 30; } else { cl.cshifts[CSHIFT_POWERUP].percent = 0; } } /* ============= V_CalcBlend ============= */ #ifdef GLQUAKE float v_blend[4]; // rgba 0.0 - 1.0 void V_CalcBlend (void) { float r, g, b, a, a2; int j; r = 0; g = 0; b = 0; a = 0; for (j = 0; j < NUM_CSHIFTS; j++) { if (cl.cshifts[j].percent > 10000) { // Set percent for grayscale cl.cshifts[j].percent = 80; } a2 = cl.cshifts[j].percent / 255.0; if (!a2) continue; a = a + a2*(1-a); // Con_Printf ("j:%i a:%f\n", j, a); a2 = a2/a; r = r*(1-a2) + cl.cshifts[j].destcolor[0]*a2; g = g*(1-a2) + cl.cshifts[j].destcolor[1]*a2; b = b*(1-a2) + cl.cshifts[j].destcolor[2]*a2; } v_blend[0] = r / 255.0; v_blend[1] = g / 255.0; v_blend[2] = b / 255.0; v_blend[3] = a; if (v_blend[3] > 1) v_blend[3] = 1; else if (v_blend[3] < 0) v_blend[3] = 0; } #endif /* GLQUAKE */ /* ============= V_UpdatePalette ============= */ #ifdef GLQUAKE unsigned short ramps[3][256]; void V_UpdatePalette (void) { int i, j; unsigned int t; if (cls.state == ca_active) V_CalcPowerupCshift (); for (i = 0; i < NUM_CSHIFTS; i++) { if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent) cl.prev_cshifts[i].percent = cl.cshifts[i].percent; for (j = 0; j < 3; j++) { if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j]) cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j]; } } // drop the damage value cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime * 150; if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0) cl.cshifts[CSHIFT_DAMAGE].percent = 0; // drop the bonus value cl.cshifts[CSHIFT_BONUS].percent -= host_frametime * 100; if (cl.cshifts[CSHIFT_BONUS].percent <= 0) cl.cshifts[CSHIFT_BONUS].percent = 0; if (v_gamma.flags & CVAR_CHANGED) { if (v_gamma.value > 1.0 || v_gamma.value < (1.0 / GAMMA_MAX)) Cvar_SetQuick (&v_gamma, "1"); v_gamma.flags &= ~CVAR_CHANGED; vid.recalc_refdef = 1; BuildGammaTable (v_gamma.value); for (i = 0; i < 256; i++) { t = gammatable[i] << 8; ramps[0][i] = t; ramps[1][i] = t; ramps[2][i] = t; } VID_ShiftPalette(NULL); } } #else /* !GLQUAKE */ void V_UpdatePalette (void) { int i, j; qboolean is_new; byte *basepal, *newpal; byte pal[768]; int r, g, b; if (cls.state == ca_active) V_CalcPowerupCshift (); is_new = false; for (i = 0; i < NUM_CSHIFTS; i++) { if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent) { is_new = true; cl.prev_cshifts[i].percent = cl.cshifts[i].percent; } for (j = 0; j < 3; j++) { if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j]) { is_new = true; cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j]; } } } // drop the damage value cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime * 150; if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0) cl.cshifts[CSHIFT_DAMAGE].percent = 0; // drop the bonus value cl.cshifts[CSHIFT_BONUS].percent -= host_frametime * 100; if (cl.cshifts[CSHIFT_BONUS].percent <= 0) cl.cshifts[CSHIFT_BONUS].percent = 0; if (v_gamma.flags & CVAR_CHANGED) { if (v_gamma.value > 1.0 || v_gamma.value < (1.0 / GAMMA_MAX)) Cvar_SetQuick (&v_gamma, "1"); v_gamma.flags &= ~CVAR_CHANGED; BuildGammaTable (v_gamma.value); vid.recalc_refdef = 1; // force a surface cache flush is_new = true; } if (!is_new) return; basepal = host_basepal; newpal = pal; for (i = 0; i < 256; i++) { r = basepal[0]; g = basepal[1]; b = basepal[2]; basepal += 3; for (j = 0; j < NUM_CSHIFTS; j++) { if (cl.cshifts[j].percent > 10000) { // Create a grayscale r = g = b = (r*76 + g*141 + b*38) / 256; } else { r += (cl.cshifts[j].percent * (cl.cshifts[j].destcolor[0] - r)) >> 8; g += (cl.cshifts[j].percent * (cl.cshifts[j].destcolor[1] - g)) >> 8; b += (cl.cshifts[j].percent * (cl.cshifts[j].destcolor[2] - b)) >> 8; } } newpal[0] = gammatable[r]; newpal[1] = gammatable[g]; newpal[2] = gammatable[b]; newpal += 3; } VID_ShiftPalette (pal); } #endif /* !GLQUAKE */ /* ============================================================================== VIEW RENDERING ============================================================================== */ static float angledelta (float a) { a = anglemod(a); if (a > 180) a -= 360; return a; } /* ================== CalcGunAngle ================== */ static void CalcGunAngle (void) { float yaw, pitch, move; static float oldyaw = 0; static float oldpitch = 0; yaw = r_refdef.viewangles[YAW]; pitch = -r_refdef.viewangles[PITCH]; yaw = angledelta(yaw - r_refdef.viewangles[YAW]) * 0.4; if (yaw > 10) yaw = 10; else if (yaw < -10) yaw = -10; pitch = angledelta(-pitch - r_refdef.viewangles[PITCH]) * 0.4; if (pitch > 10) pitch = 10; else if (pitch < -10) pitch = -10; move = host_frametime * 20; if (yaw > oldyaw) { if (oldyaw + move < yaw) yaw = oldyaw + move; } else { if (oldyaw - move > yaw) yaw = oldyaw - move; } if (pitch > oldpitch) { if (oldpitch + move < pitch) pitch = oldpitch + move; } else { if (oldpitch - move > pitch) pitch = oldpitch - move; } oldyaw = yaw; oldpitch = pitch; cl.viewent.angles[YAW] = r_refdef.viewangles[YAW] + yaw; cl.viewent.angles[PITCH] = - (r_refdef.viewangles[PITCH] + pitch); if (v_idlescale.value == 0) return; cl.viewent.angles[ROLL] -= v_idlescale.value * q_sinrad(cl.time*v_iroll_cycle.value) * v_iroll_level.value; cl.viewent.angles[PITCH] -= v_idlescale.value * q_sinrad(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; cl.viewent.angles[YAW] -= v_idlescale.value * q_sinrad(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value; } /* ============== V_BoundOffsets ============== */ #if 0 static void V_BoundOffsets (void) { // absolutely bound refresh reletive to entity clipping hull // so the view can never be inside a solid wall if (r_refdef.vieworg[0] < cl.simorg[0] - 14) r_refdef.vieworg[0] = cl.simorg[0] - 14; else if (r_refdef.vieworg[0] > cl.simorg[0] + 14) r_refdef.vieworg[0] = cl.simorg[0] + 14; if (r_refdef.vieworg[1] < cl.simorg[1] - 14) r_refdef.vieworg[1] = cl.simorg[1] - 14; else if (r_refdef.vieworg[1] > cl.simorg[1] + 14) r_refdef.vieworg[1] = cl.simorg[1] + 14; if (r_refdef.vieworg[2] < cl.simorg[2] - 0) r_refdef.vieworg[2] = cl.simorg[2] - 0; else if (r_refdef.vieworg[2] > cl.simorg[2] + 86) r_refdef.vieworg[2] = cl.simorg[2] + 86; } #endif /* ============== V_AddIdle Idle swaying ============== */ static void V_AddIdle (void) { if (v_idlescale.value == 0) return; r_refdef.viewangles[ROLL] += v_idlescale.value * q_sinrad(cl.time*v_iroll_cycle.value) * v_iroll_level.value; r_refdef.viewangles[PITCH] += v_idlescale.value * q_sinrad(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; r_refdef.viewangles[YAW] += v_idlescale.value * q_sinrad(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value; // cl.viewent.angles[ROLL] -= v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value; // cl.viewent.angles[PITCH] -= v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; // cl.viewent.angles[YAW] -= v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value; } /* ============== V_CalcViewRoll Roll is induced by movement and damage ============== */ static void V_CalcViewRoll (void) { float side; side = V_CalcRoll (cl.simangles, cl.simvel); r_refdef.viewangles[ROLL] += side; if (v_dmg_time > 0) { r_refdef.viewangles[ROLL] += v_dmg_time / v_kicktime.value * v_dmg_roll; r_refdef.viewangles[PITCH] += v_dmg_time / v_kicktime.value * v_dmg_pitch; v_dmg_time -= host_frametime; } if (cl.v.health <= 0 && !cl.spectator) { r_refdef.viewangles[ROLL] = 80; // dead view angle return; } } /* ================== V_CalcIntermissionRefdef ================== */ static void V_CalcIntermissionRefdef (void) { entity_t *view; float old; // view is the weapon model view = &cl.viewent; VectorCopy (cl.simorg, r_refdef.vieworg); VectorCopy (cl.simangles, r_refdef.viewangles); view->model = NULL; // always idle in intermission old = v_idlescale.value; v_idlescale.value = 1; V_AddIdle (); v_idlescale.value = old; } /* ================== V_CalcRefdef ================== */ static void V_CalcRefdef (void) { entity_t *view; int i; vec3_t forward, right, up; float bob; static float oldz = 0; if (!cl.v.cameramode) { V_DriftPitch (); V_DriftRoll (); } // view is the weapon model (only visible from inside body) view = &cl.viewent; if (cl.v.movetype != MOVETYPE_FLY) bob = V_CalcBob (); else // no bobbing when you fly bob = 1; // refresh position from simulated origin VectorCopy (cl.simorg, r_refdef.vieworg); r_refdef.vieworg[2] += bob; // never let it sit exactly on a node line, because a water plane can // dissapear when viewed with the eye exactly on it. // the server protocol only specifies to 1/8 pixel, so add 1/16 in // each axis r_refdef.vieworg[0] += 1.0/32; r_refdef.vieworg[1] += 1.0/32; r_refdef.vieworg[2] += 1.0/32; VectorCopy (cl.simangles, r_refdef.viewangles); V_CalcViewRoll (); V_AddIdle (); if (cl.spectator) { r_refdef.vieworg[2] += 50; // view height } else { if (view_message->flags & PF_CROUCH) r_refdef.vieworg[2] += 24; // gib view height else if (view_message->flags & PF_DEAD) r_refdef.vieworg[2] += 8; // corpse view height else r_refdef.vieworg[2] += 50; // view height if (view_message->flags & PF_DEAD) // PF_GIB will also set PF_DEAD r_refdef.viewangles[ROLL] = 80; // dead view angle } // offsets AngleVectors (cl.simangles, forward, right, up); // set up gun position VectorCopy (cl.simangles, view->angles); CalcGunAngle (); VectorCopy (r_refdef.vieworg, view->origin); // view->origin[2] += 56; for (i = 0; i < 3; i++) { view->origin[i] += forward[i]*bob*0.4; // view->origin[i] += right[i]*bob*0.4; // view->origin[i] += up[i]*bob*0.8; } // rjr no idea why commenting this out works, as // it is in the original q1 codebase //view->origin[2] += bob; // fudge position around to keep amount of weapon visible // roughly equal with different FOV if (scr_viewsize.integer >= 110) view->origin[2] += 1; else if (scr_viewsize.integer == 100) view->origin[2] += 2; else if (scr_viewsize.integer == 90) view->origin[2] += 1; else if (scr_viewsize.integer == 80) view->origin[2] += 0.5; if (view_message->flags & (PF_DEAD) ) view->model = NULL; else view->model = cl.model_precache[cl.stats[STAT_WEAPON]]; view->frame = view_message->weaponframe; if (!view->colorshade) { view->colormap = vid.colormap; } else { #ifdef GLQUAKE view->colormap = vid.colormap; #else view->colormap = globalcolormap; #endif } // Place weapon in powered up mode if ((cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK].playerstate[cl.playernum].drawflags & MLS_MASKIN) == MLS_POWERMODE) view->drawflags = (view->drawflags & MLS_MASKOUT) | MLS_POWERMODE; else view->drawflags = (view->drawflags & MLS_MASKOUT) | 0; // set up the refresh position r_refdef.viewangles[PITCH] += cl.punchangle; // smooth out stair step ups if ( (view_message->onground != -1) && (cl.simorg[2] - oldz > 0) ) { float steptime; steptime = host_frametime; oldz += steptime * 80; if (oldz > cl.simorg[2]) oldz = cl.simorg[2]; if (cl.simorg[2] - oldz > 12) oldz = cl.simorg[2] - 12; r_refdef.vieworg[2] += oldz - cl.simorg[2]; view->origin[2] += oldz - cl.simorg[2]; } else oldz = cl.simorg[2]; } /* ============= DropPunchAngle ============= */ static void DropPunchAngle (void) { cl.punchangle -= 10*host_frametime; if (cl.punchangle < 0) cl.punchangle = 0; } /* ================== V_RenderView The player's clipping box goes from (-16 -16 -24) to (16 16 32) from the entity origin, so any view position inside that will be valid ================== */ void V_RenderView (void) { // if (cl.simangles[ROLL]) // Sys_Error ("cl.simangles[ROLL]"); // DEBUG //rjr cl.simangles[ROLL] = 0; // FIXME @@@ if (cls.state != ca_active) return; view_frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; view_message = &view_frame->playerstate[cl.playernum]; DropPunchAngle (); if (cl.intermission) { // intermission / finale rendering V_CalcIntermissionRefdef (); } else { V_CalcRefdef (); } CL_UpdateEffects (); R_PushDlights (); R_RenderView (); } //============================================================================ /* ============= V_Init ============= */ void V_Init (void) { unsigned int i; Cmd_AddCommand ("v_cshift", V_cshift_f); Cmd_AddCommand ("bf", V_BonusFlash_f); Cmd_AddCommand ("df", V_DarkFlash_f); Cmd_AddCommand ("wf", V_WhiteFlash_f); Cmd_AddCommand ("centerview", V_StartPitchDrift); Cvar_RegisterVariable (&v_centermove); Cvar_RegisterVariable (&v_centerspeed); Cvar_RegisterVariable (&v_centerrollspeed); Cvar_RegisterVariable (&v_iyaw_cycle); Cvar_RegisterVariable (&v_iroll_cycle); Cvar_RegisterVariable (&v_ipitch_cycle); Cvar_RegisterVariable (&v_iyaw_level); Cvar_RegisterVariable (&v_iroll_level); Cvar_RegisterVariable (&v_ipitch_level); Cvar_RegisterVariable (&v_idlescale); Cvar_RegisterVariable (&crosshair); Cvar_RegisterVariable (&cl_crossx); Cvar_RegisterVariable (&cl_crossy); Cvar_RegisterVariable (&crosshaircolor); Cvar_RegisterVariable (&cl_rollspeed); Cvar_RegisterVariable (&cl_rollangle); Cvar_RegisterVariable (&cl_bob); Cvar_RegisterVariable (&cl_bobcycle); Cvar_RegisterVariable (&cl_bobup); Cvar_RegisterVariable (&v_kicktime); Cvar_RegisterVariable (&v_kickroll); Cvar_RegisterVariable (&v_kickpitch); Cvar_RegisterVariable (&v_gamma); /* no gamma yet */ for (i = 0; i < 256; i++) { gammatable[i] = i; #ifdef GLQUAKE ramps[0][i] = i << 8; ramps[1][i] = i << 8; ramps[2][i] = i << 8; #endif } } engine/hexenworld/server/000077500000000000000000000000001444734033100160135ustar00rootroot00000000000000engine/hexenworld/server/Makefile000066400000000000000000000223411444734033100174550ustar00rootroot00000000000000# GNU Makefile for hexenworld server using GCC. # # Remember to "make clean" between different types of builds or targets. # # To cross-compile for Win32 on Unix: either pass the W32BUILD=1 # argument to make, or export it. Also see build_cross_win32.sh. # Requires: a mingw or mingw-w64 compiler toolchain. # # To cross-compile for Win64 on Unix: either pass the W64BUILD=1 # argument to make, or export it. Also see build_cross_win64.sh. # Requires: a mingw-w64 compiler toolchain. # # To cross-compile for MacOSX on Unix: either pass the OSXBUILD=1 # argument to make, or export it. You would also need to pass a # suitable MACH_TYPE=xxx (ppc, x86, x86_64, or ppc64) argument to # make. Also see build_cross_osx.sh. # # To (cross-)compile for DOS: either pass the DOSBUILD=1 argument # to make, or export it. Also see build_cross_dos.sh. Requires: a # djgpp compiler toolchain. # # To use a compiler other than gcc: make CC=compiler_name [other stuff] # # To build for the demo version: make DEMO=1 [other stuff] # # To build a debug version: make DEBUG=1 [other stuff] # # PATH SETTINGS: UHEXEN2_TOP:=../../.. ENGINE_TOP:=../.. HW_TOP:=.. COMMONDIR:=$(ENGINE_TOP)/h2shared COMMON_HW:=$(HW_TOP)/shared UHEXEN2_SHARED:=$(UHEXEN2_TOP)/common LIBS_DIR:=$(UHEXEN2_TOP)/libs OSLIBS:=$(UHEXEN2_TOP)/oslibs # GENERAL OPTIONS (customize as required) # use WinSock2 instead of WinSock-1.1? (disabled for w32 for compat. # with old Win95 machines.) (enabled for Win64 in the win64 section.) USE_WINSOCK2=no # include the common dirty stuff include $(UHEXEN2_TOP)/scripts/makefile.inc # Names of the binaries BINARY:=hwsv$(exe_ext) ############################################################# # Compiler flags ############################################################# ifeq ($(MACH_TYPE),x86) CPU_X86=-march=i586 endif # Overrides for the default CPUFLAGS CPUFLAGS=$(CPU_X86) CFLAGS += -Wall CFLAGS += $(CPUFLAGS) ifdef DEBUG CFLAGS += -g else # optimization flags CFLAGS += -O2 -DNDEBUG=1 -ffast-math # NOTE: -fomit-frame-pointer is broken with ancient gcc versions!! CFLAGS += -fomit-frame-pointer endif CPPFLAGS= LDFLAGS = # linkage may be sensitive to order: add SYSLIBS after all others. SYSLIBS = # compiler includes INCLUDES= -I. -I$(COMMON_HW) -I$(COMMONDIR) -I$(UHEXEN2_SHARED) # end of compiler flags ############################################################# ############################################################# # Other build flags ############################################################# CPPFLAGS+= -DH2W -DSERVERONLY ifdef DEMO CPPFLAGS+= -DDEMOBUILD endif ifdef DEBUG # This activates some extra code in hexen2/hexenworld C source CPPFLAGS+= -DDEBUG=1 -DDEBUG_BUILD=1 endif ############################################################# # DOS flags/settings ############################################################# ifeq ($(TARGET_OS),dos) INCLUDES += -I$(OSLIBS)/dos # WATT-32 is needed for DOS CPPFLAGS+= -DUSE_WATT32 -DWATT32_NO_OLDIES INCLUDES+= -I$(OSLIBS)/dos/watt32/inc LDFLAGS += -L$(OSLIBS)/dos/watt32/lib -lwatt SYSLIBS += $(OSLIBS)/dos/djtime/djtime.a -lc -lgcc #SYSLIBS += -lm endif # End of DOS settings ############################################################# ############################################################# # OS/2 flags/settings ############################################################# ifeq ($(TARGET_OS),os2) INCLUDES+= -I$(OSLIBS)/os2/emx/include CFLAGS += -Zmt LDFLAGS += -Zmt ifndef DEBUG LDFLAGS += -s # -fomit-frame-pointer/-ffast-math seems to cause trouble # with EMX at least with the old compilers I tried. CFLAGS += -fno-omit-frame-pointer endif SYSLIBS += -lsocket endif # End of OS/2 settings ############################################################# ############################################################# # Win32 flags/settings ############################################################# ifeq ($(TARGET_OS),win32) CPPFLAGS+= -DWIN32_LEAN_AND_MEAN ifeq ($(USE_WINSOCK2),yes) CPPFLAGS+=-D_USE_WINSOCK2 LIBWINSOCK=ws2_32 else LIBWINSOCK=wsock32 endif INCLUDES+= -I$(OSLIBS)/windows/misc/include CFLAGS += -m32 LDFLAGS += -m32 -mconsole LDFLAGS += -l$(LIBWINSOCK) -lwinmm endif # End of Win32 settings ############################################################# ############################################################# # Win64 flags/settings ############################################################# ifeq ($(TARGET_OS),win64) CPPFLAGS+= -DWIN32_LEAN_AND_MEAN # use winsock2 for win64 USE_WINSOCK2=yes ifeq ($(USE_WINSOCK2),yes) CPPFLAGS+=-D_USE_WINSOCK2 LIBWINSOCK=ws2_32 else LIBWINSOCK=wsock32 endif INCLUDES+= -I$(OSLIBS)/windows/misc/include CFLAGS += -m64 LDFLAGS += -m64 -mconsole LDFLAGS += -l$(LIBWINSOCK) -lwinmm endif # End of Win64 settings ############################################################# ############################################################# # Mac OS X flags/settings ############################################################# ifeq ($(TARGET_OS),darwin) CPUFLAGS= # require 10.5 for 64 bit builds ifeq ($(MACH_TYPE),x86_64) CFLAGS +=-mmacosx-version-min=10.5 LDFLAGS +=-mmacosx-version-min=10.5 endif ifeq ($(MACH_TYPE),ppc64) CFLAGS +=-mmacosx-version-min=10.5 LDFLAGS +=-mmacosx-version-min=10.5 endif endif # End of Mac OS X settings ############################################################# ############################################################# # Unix flags/settings ############################################################# ifeq ($(TARGET_OS),unix) # common unix: ifeq ($(HOST_OS),qnx) SYSLIBS += -lsocket endif ifeq ($(HOST_OS),haiku) SYSLIBS += -lnetwork endif ifeq ($(HOST_OS),sunos) SYSLIBS += -lsocket -lnsl -lresolv endif SYSLIBS += -lm endif # End of Unix settings ############################################################# ############################################################# # MorphOS flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),morphos) CFLAGS += -noixemul LDFLAGS += -noixemul SYSLIBS += -lm endif # End of MorphOS settings ############################################################# ############################################################# # AROS flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),aros) CFLAGS += -fno-common INCLUDES+= -I$(OSLIBS)/aros-$(MACH_TYPE)/misc/include endif # End of AROS settings ############################################################# ############################################################# # AmigaOS flags/settings and overrides: ############################################################# ifeq ($(TARGET_OS),amigaos) # use Bebbo's GCC6 toolchain BEBBO_TOOLCHAIN=yes # crt: libnix or clib2: USE_CLIB2=yes ifeq ($(BEBBO_TOOLCHAIN),yes) USE_CLIB2=no endif ifndef DEBUG ifneq ($(BEBBO_TOOLCHAIN),yes) # -fomit-frame-pointer / -ffast-math causes trouble with gcc2.95 CFLAGS += -fno-omit-frame-pointer else # these break the game with GCC6 CFLAGS += -fbbb=- -fno-tree-forwprop -fno-tree-ter -fno-move-loop-invariants endif endif ifeq ($(USE_CLIB2),yes) CRT_FLAGS=-mcrt=clib2 else CRT_FLAGS=-noixemul endif CFLAGS += $(CRT_FLAGS) -m68060 -m68881 LDFLAGS += $(CRT_FLAGS) -m68060 -m68881 SYSLIBS += -lm # for extra missing headers INCLUDES += -I$(OSLIBS)/amigaos/include ifneq ($(BEBBO_TOOLCHAIN),yes) # Roadshow SDK NET_INC = -I$(OSLIBS)/amigaos/netinclude endif endif # End of AmigaOS settings ############################################################# # Rules for turning source files into .o files %.o: %.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(COMMON_HW)/%.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(COMMONDIR)/%.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< %.o: $(UHEXEN2_SHARED)/%.c $(CC) -c $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -o $@ $< # Objects # Platform specific object settings ifeq ($(TARGET_OS),win32) SYSOBJ_SYS = sys_win.o endif ifeq ($(TARGET_OS),win64) SYSOBJ_SYS = sys_win.o endif ifeq ($(TARGET_OS),dos) SYSOBJ_SYS := dos_v2.o sys_dos.o endif ifeq ($(TARGET_OS),os2) SYSOBJ_SYS := sys_os2.o endif ifeq ($(TARGET_OS),unix) SYSOBJ_SYS = sys_unix.o endif ifeq ($(TARGET_OS),darwin) SYSOBJ_SYS = sys_unix.o endif ifeq ($(TARGET_OS),aros) SYSOBJ_SYS = sys_amiga.o endif ifeq ($(TARGET_OS),morphos) SYSOBJ_SYS = sys_amiga.o endif ifeq ($(TARGET_OS),amigaos) SYSOBJ_SYS = sys_amiga.o endif # Final list of objects OBJECTS = \ q_endian.o \ link_ops.o \ sizebuf.o \ strlcat.o \ strlcpy.o \ qsnprint.o \ msg_io.o \ common.o \ quakefs.o \ info_str.o \ cmd.o \ crc.o \ cvar.o \ host_string.o \ mathlib.o \ zone.o \ hashindex.o \ huffman.o \ net_udp.o \ net_chan.o \ pmove.o \ pmovetst.o \ sv_model.o \ pr_cmds.o \ pr_edict.o \ pr_exec.o \ sv_effect.o \ sv_ccmds.o \ sv_ents.o \ sv_init.o \ sv_main.o \ sv_move.o \ sv_phys.o \ sv_send.o \ sv_user.o \ world.o \ $(SYSOBJ_SYS) # Targets .PHONY: clean distclean report default: $(BINARY) all: default $(BINARY): $(OBJECTS) $(LINKER) $(OBJECTS) $(LDFLAGS) $(SYSLIBS) -o $@ ifeq ($(TARGET_OS),amigaos) # workaround stupid AmiTCP SDK mess for old aos3 net_udp.o: INCLUDES+= $(NET_INC) endif clean: rm -f *.o core distclean: clean rm -f $(BINARY) report: @echo "Host OS :" $(HOST_OS) @echo "Target OS:" $(TARGET_OS) @echo "Machine :" $(MACH_TYPE) engine/hexenworld/server/Makefile.os2000066400000000000000000000051271444734033100201620ustar00rootroot00000000000000# makefile to build hwsv.exe for OS/2 using Open Watcom: # wmake -f Makefile.os2 # PATH SETTINGS: !ifndef __UNIX__ PATH_SEP=\ UHEXEN2_TOP=..\..\.. ENGINE_TOP=..\.. HW_TOP=.. COMMONDIR=$(ENGINE_TOP)\h2shared COMMON_HW=$(HW_TOP)\shared UHEXEN2_SHARED=$(UHEXEN2_TOP)\common LIBS_DIR=$(UHEXEN2_TOP)\libs OSLIBS=$(UHEXEN2_TOP)\oslibs !else PATH_SEP=/ UHEXEN2_TOP=../../.. ENGINE_TOP=../.. HW_TOP=.. COMMONDIR=$(ENGINE_TOP)/h2shared COMMON_HW=$(HW_TOP)/shared UHEXEN2_SHARED=$(UHEXEN2_TOP)/common LIBS_DIR=$(UHEXEN2_TOP)/libs OSLIBS=$(UHEXEN2_TOP)/oslibs !endif # GENERAL OPTIONS (customize as required) # Names of the binaries BINARY=hwsv.exe ############################################################# # Compiler flags ############################################################# CFLAGS = -zq -wx -bm -bt=os2 -5s -sg -otexan -fp5 -fpi87 -ei -j -zp8 # newer OpenWatcom versions enable W303 by default CFLAGS+= -wcd=303 # compiler includes INCLUDES= -I. -I$(COMMON_HW) -I$(COMMONDIR) -I$(UHEXEN2_SHARED) # end of compiler flags ############################################################# ############################################################# # Other build flags ############################################################# CPPFLAGS+= -DH2W -DSERVERONLY !ifdef DEMO CPPFLAGS+= -DDEMOBUILD !endif !ifdef DEBUG CFLAGS += -d2 # This activates some extra code in hexen2/hexenworld C source CPPFLAGS+= -DDEBUG=1 -DDEBUG_BUILD=1 !endif ############################################################# # OS/2 flags/settings and overrides: ############################################################# ############################################################# .c: $(COMMON_HW);$(COMMONDIR);$(UHEXEN2_SHARED) .c.obj: wcc386 $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -fo=$^@ $< # Objects SYSOBJ_SYS = sys_os2.obj # Final list of objects OBJECTS = & q_endian.obj & link_ops.obj & sizebuf.obj & strlcat.obj & strlcpy.obj & qsnprint.obj & msg_io.obj & common.obj & quakefs.obj & info_str.obj & cmd.obj & crc.obj & cvar.obj & host_string.obj & mathlib.obj & zone.obj & hashindex.obj & huffman.obj & net_udp.obj & net_chan.obj & pmove.obj & pmovetst.obj & sv_model.obj & pr_cmds.obj & pr_edict.obj & pr_exec.obj & sv_effect.obj & sv_ccmds.obj & sv_ents.obj & sv_init.obj & sv_main.obj & sv_move.obj & sv_phys.obj & sv_send.obj & sv_user.obj & world.obj & $(SYSOBJ_SYS) all: $(BINARY) h2ded: $(BINARY) # 512 KB stack size. $(BINARY): $(OBJECTS) wlink N $@ SYS OS2V2 OPTION q OPTION STACK=0x80000 F {$(OBJECTS)} clean: .symbolic rm -f *.obj *.res *.err distclean: clean .symbolic rm -f $(BINARY) engine/hexenworld/server/Makefile.wat000066400000000000000000000060371444734033100202530ustar00rootroot00000000000000# makefile to build hwsv for Windows using Open Watcom: # wmake -f Makefile.wat # PATH SETTINGS: !ifndef __UNIX__ PATH_SEP=\ UHEXEN2_TOP=..\..\.. ENGINE_TOP=..\.. HW_TOP=.. COMMONDIR=$(ENGINE_TOP)\h2shared COMMON_HW=$(HW_TOP)\shared UHEXEN2_SHARED=$(UHEXEN2_TOP)\common LIBS_DIR=$(UHEXEN2_TOP)\libs OSLIBS=$(UHEXEN2_TOP)\oslibs !else PATH_SEP=/ UHEXEN2_TOP=../../.. ENGINE_TOP=../.. HW_TOP=.. COMMONDIR=$(ENGINE_TOP)/h2shared COMMON_HW=$(HW_TOP)/shared UHEXEN2_SHARED=$(UHEXEN2_TOP)/common LIBS_DIR=$(UHEXEN2_TOP)/libs OSLIBS=$(UHEXEN2_TOP)/oslibs !endif # GENERAL OPTIONS (customize as required) # use WinSock2 instead of WinSock-1.1? (disabled for w32 for compat. # with old Win95 machines.) (enabled for Win64 in the win64 section.) USE_WINSOCK2=no # Names of the binaries BINARY=hwsv.exe ############################################################# # Compiler flags ############################################################# CFLAGS = -zq -wx -bm -bt=nt -5s -sg -otexan -fp5 -fpi87 -ei -j -zp8 # newer OpenWatcom versions enable W303 by default CFLAGS+= -wcd=303 # compiler includes INCLUDES= -I. -I$(COMMON_HW) -I$(COMMONDIR) -I$(UHEXEN2_SHARED) # end of compiler flags ############################################################# ############################################################# # Other build flags ############################################################# CPPFLAGS+= -DH2W -DSERVERONLY !ifdef DEMO CPPFLAGS+= -DDEMOBUILD !endif !ifdef DEBUG CFLAGS += -d2 # This activates some extra code in hexen2/hexenworld C source CPPFLAGS+= -DDEBUG=1 -DDEBUG_BUILD=1 !endif ############################################################# # Win32 flags/settings and overrides: ############################################################# !ifndef __UNIX__ INCLUDES+= -I$(OSLIBS)\windows\misc\include !else INCLUDES+= -I$(OSLIBS)/windows/misc/include !endif !ifeq USE_WINSOCK2 yes CPPFLAGS+=-D_USE_WINSOCK2 LIBWINSOCK=ws2_32.lib !else LIBWINSOCK=wsock32.lib !endif CPPFLAGS+= -DWIN32_LEAN_AND_MEAN LIBS += $(LIBWINSOCK) winmm.lib ############################################################# .c: $(COMMON_HW);$(COMMONDIR);$(UHEXEN2_SHARED) .c.obj: wcc386 $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -fo=$^@ $< # Objects SYSOBJ_SYS = sys_win.obj # Final list of objects OBJECTS = & q_endian.obj & link_ops.obj & sizebuf.obj & strlcat.obj & strlcpy.obj & qsnprint.obj & msg_io.obj & common.obj & quakefs.obj & info_str.obj & cmd.obj & crc.obj & cvar.obj & host_string.obj & mathlib.obj & zone.obj & hashindex.obj & huffman.obj & net_udp.obj & net_chan.obj & pmove.obj & pmovetst.obj & sv_model.obj & pr_cmds.obj & pr_edict.obj & pr_exec.obj & sv_effect.obj & sv_ccmds.obj & sv_ents.obj & sv_init.obj & sv_main.obj & sv_move.obj & sv_phys.obj & sv_send.obj & sv_user.obj & world.obj & $(SYSOBJ_SYS) all: $(BINARY) h2ded: $(BINARY) # 512 KB stack. $(BINARY): $(OBJECTS) wlink N $@ SYS NT OPTION q OPTION STACK=0x80000 LIBR {$(LIBS)} F {$(OBJECTS)} clean: .symbolic rm -f *.obj *.res *.err distclean: clean .symbolic rm -f $(BINARY) engine/hexenworld/server/build_cross_amigaos.sh000077500000000000000000000005041444734033100223610ustar00rootroot00000000000000#!/bin/sh UHEXEN2_TOP=../../.. . $UHEXEN2_TOP/scripts/cross_defs.amigaos if test "$1" = "strip"; then $STRIPPER -S hwsv exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac exec $MAKE_CMD $* engine/hexenworld/server/build_cross_aros.sh000077500000000000000000000005361444734033100217120ustar00rootroot00000000000000#!/bin/sh # for x86-AROS cross-builds UHEXEN2_TOP=../../.. . $UHEXEN2_TOP/scripts/cross_defs.aros if test "$1" = "strip"; then $STRIPPER -S hwsv exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac exec $MAKE_CMD $* engine/hexenworld/server/build_cross_aros64.sh000077500000000000000000000005431444734033100220620ustar00rootroot00000000000000#!/bin/sh # for x86_64-AROS cross-builds UHEXEN2_TOP=../../.. . $UHEXEN2_TOP/scripts/cross_defs.aros64 if test "$1" = "strip"; then $STRIPPER -S hwsv exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac exec $MAKE_CMD $* engine/hexenworld/server/build_cross_morphos.sh000077500000000000000000000005041444734033100224300ustar00rootroot00000000000000#!/bin/sh UHEXEN2_TOP=../../.. . $UHEXEN2_TOP/scripts/cross_defs.morphos if test "$1" = "strip"; then $STRIPPER -S hwsv exit 0 fi HOST_OS=`uname|sed -e s/_.*//|tr '[:upper:]' '[:lower:]'` case "$HOST_OS" in freebsd|openbsd|netbsd) MAKE_CMD=gmake ;; linux) MAKE_CMD=make ;; *) MAKE_CMD=make ;; esac exec $MAKE_CMD $* engine/hexenworld/server/host.h000066400000000000000000000037451444734033100171520ustar00rootroot00000000000000/* * host.h -- public host structures and functions * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined(SERVERONLY) #error "this header is for hw server only" #endif /* SERVERONLY */ #ifndef HX2_HOST_H #define HX2_HOST_H // quakeparms structure specifies the base of the directory tree, the // command line parms passed to the program, and the amount of memory // available for the program to use typedef struct quakeparms_s { const char *basedir; const char *userdir; // user's directory on UNIX platforms. // if user directories are enabled, basedir // and userdir will point to different // memory locations, otherwise to the same. int argc; char **argv; void *membase; int memsize; int errstate; } quakeparms_t; extern quakeparms_t *host_parms; #define isDedicated 1 /* compatibility */ extern cvar_t sys_nostdout; extern cvar_t developer; extern qboolean host_initialized; // true if into command execution extern double host_frametime; extern double realtime; // not bounded in any way, changed at // start of every frame, never reset void SV_Init (void); FUNC_NORETURN void SV_Error (const char *error, ...) FUNC_PRINTF(1,2); #define Host_Error SV_Error #ifdef __WATCOMC__ #pragma aux SV_Error aborts; #endif #endif /* HX2_HOST_H */ engine/hexenworld/server/qwsvinc.h000066400000000000000000000034651444734033100176660ustar00rootroot00000000000000/* * qwsvinc.h -- primary header for server * FIXME: kill this in the future and make each C * file include only the necessary headers. * * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HWSVINC_H #define __HWSVINC_H /* include the system stdc headers: */ #include "q_stdinc.h" /* include the compiler specific stuff */ #include "compiler.h" /* include the OS/arch definitions, etc */ #include "arch_def.h" /* make sure to include our compile time options first */ #include "h2config.h" /* include the quake headers */ #include "q_endian.h" #include "sys.h" #include "qsnprint.h" #include "strl_fn.h" #include "link_ops.h" #include "sizebuf.h" #include "msg_io.h" #include "printsys.h" #include "common.h" #include "quakefs.h" #include "info_str.h" #include "bspfile.h" #include "zone.h" #include "mathlib.h" #include "cvar.h" #include "protocol.h" #include "net.h" #include "cmd.h" #include "crc.h" #include "host.h" #include "host_string.h" #include "progs.h" #include "effects.h" #include "server.h" #include "sv_model.h" #include "world.h" #include "pmove.h" #endif /* __HWSVINC_H */ engine/hexenworld/server/server.h000066400000000000000000000340571444734033100175030ustar00rootroot00000000000000/* hwsv/server.h * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __H2W_SERVER_H #define __H2W_SERVER_H #define QW_SERVER #define MAX_MASTERS 8 // max recipients for heartbeat packets #define MAX_SIGNON_BUFFERS 8 typedef enum { ss_dead, // no map loaded ss_loading, // spawning level edicts ss_active // actively running } server_state_t; // some qc commands are only valid before the server has finished // initializing (precache commands, static sounds / objects, etc) typedef struct { qboolean active; // false when server is going down server_state_t state; // precache commands are only valid during load double time; int lastcheck; // used by PF_checkclient double lastchecktime; // for monster ai double next_PIV_time; // Last Player In View time char name[64]; // map name char midi_name[128]; // midi file name byte cd_track; // cd track number int current_skill; char startspot[64]; char modelname[MAX_QPATH]; // maps/.bsp, for model_precache[0] struct qmodel_s *worldmodel; const char *model_precache[MAX_MODELS]; // NULL terminated const char *sound_precache[MAX_SOUNDS]; // NULL terminated const char *lightstyles[MAX_LIGHTSTYLES]; struct qmodel_s *models[MAX_MODELS]; struct EffectT Effects[MAX_EFFECTS]; int num_edicts; // increases towards MAX_EDICTS edict_t *edicts; // can NOT be array indexed, because // edict_t is variable sized, but can // be used to reference the world ent byte *pvs, *phs; // fully expanded and decompressed // added to every client's unreliable buffer each frame, then cleared sizebuf_t datagram; byte datagram_buf[MAX_DATAGRAM]; // added to every client's reliable buffer each frame, then cleared sizebuf_t reliable_datagram; byte reliable_datagram_buf[MAX_MSGLEN]; // the multicast buffer is used to send a message to a set of clients sizebuf_t multicast; byte multicast_buf[MAX_MSGLEN]; // the master buffer is used for building log packets sizebuf_t master; byte master_buf[MAX_DATAGRAM]; // the signon buffer will be sent to each client as they connect // includes the entity baselines, the static entities, etc // large levels will have >MAX_DATAGRAM sized signons, so // multiple signon messages are kept sizebuf_t signon; int num_signon_buffers; int signon_buffer_size[MAX_SIGNON_BUFFERS]; byte signon_buffers[MAX_SIGNON_BUFFERS][MAX_DATAGRAM]; } server_t; #define NUM_SPAWN_PARMS 16 typedef enum { cs_free, // can be reused for a new connection cs_zombie, // client has been disconnected, but don't reuse // connection for a couple seconds cs_connected, // has been assigned to a client_t, but not in game yet cs_spawned // client is fully in game } client_state_t; typedef struct { // received from client // reply double senttime; float ping_time; packet_entities_t entities; } client_frame_t; typedef struct client_s { client_state_t state; int protocol; int spectator; // non-interactive qboolean sendinfo; // at end of frame, send info to all // this prevents malicious multiple broadcasts qboolean portals; // They have portals mission pack installed int userid; // identifying number char userinfo[MAX_INFO_STRING]; // infostring usercmd_t lastcmd; // for filling in big drops and partial predictions double localtime; // of last message int oldbuttons; float maxspeed; // localized maxspeed float entgravity; // localized ent gravity edict_t *edict; // EDICT_NUM(clientnum+1) char name[32]; // for printing to other people // extracted from userinfo int playerclass; int siege_team; int next_playerclass; int messagelevel; // for filtering printed messages // the datagram is written to after every frame, but only cleared // when it is sent out to the client. overflow is tolerated. sizebuf_t datagram; byte datagram_buf[MAX_DATAGRAM]; double connection_started; // or time of disconnect for zombies qboolean send_message; // set on frames a datagram arived on // spawn parms are carried from level to level float spawn_parms[NUM_SPAWN_PARMS]; // client known data for deltas int old_frags; int stats[MAX_CL_STATS]; client_frame_t frames[UPDATE_BACKUP]; // updates can be deltad from here FILE *download; // file being downloaded int downloadsize; // total bytes int downloadcount; // bytes sent int spec_track; // entnum of player tracking double whensaid[10]; // JACK: For floodprots int whensaidhead; // Head value for floodprots double lockedtill; //===== NETWORK ============ int chokecount; int delta_sequence; // -1 = no compression netchan_t netchan; entvars_t old_v; qboolean send_all_v; unsigned int PIV, LastPIV; // people in view qboolean skipsend; // Skip sending this frame, guaranteed to send next frame } client_t; // a client can leave the server in one of four ways: // dropping properly by quiting or disconnecting // timing out if no valid messages are received for timeout.value seconds // getting kicked off by the server operator // a program error, like an overflowed reliable buffer //============================================================================= #define STATFRAMES 100 typedef struct { double active; double idle; int count; int packets; double latched_active; double latched_idle; int latched_packets; } svstats_t; typedef struct { int spawncount; // number of servers spawned since start, // used to check late spawns client_t clients[MAX_CLIENTS]; int serverflags; // episode completion information qboolean changelevel_issued; // cleared when at SV_SpawnServer double last_heartbeat; int heartbeat_sequence; svstats_t stats; char info[MAX_SERVERINFO_STRING]; // log messages are used so that fraglog processes can get stats int logsequence; // the message currently being filled double logtime; // time of last swap sizebuf_t log[2]; byte log_buf[2][MAX_DATAGRAM]; } server_static_t; //============================================================================= // // edict->movetype values // #define MOVETYPE_NONE 0 // never moves #define MOVETYPE_ANGLENOCLIP 1 #define MOVETYPE_ANGLECLIP 2 #define MOVETYPE_WALK 3 // gravity #define MOVETYPE_STEP 4 // gravity, special edge handling #define MOVETYPE_FLY 5 #define MOVETYPE_TOSS 6 // gravity #define MOVETYPE_PUSH 7 // no clip to world, push and crush #define MOVETYPE_NOCLIP 8 #define MOVETYPE_FLYMISSILE 9 // extra size to monsters #define MOVETYPE_BOUNCE 10 //#ifdef QUAKE2 #define MOVETYPE_BOUNCEMISSILE 11 // bounce w/o gravity #define MOVETYPE_FOLLOW 12 // track movement of aiment //#endif #define MOVETYPE_PUSHPULL 13 // pushable/pullable object #define MOVETYPE_SWIM 14 // should keep the object in water // // edict->solid values // #define SOLID_NOT 0 // no interaction with other objects #define SOLID_TRIGGER 1 // touch on edge, but not blocking #define SOLID_BBOX 2 // touch on edge, block #define SOLID_SLIDEBOX 3 // touch on edge, but not an onground #define SOLID_BSP 4 // bsp clip, touch on edge, block #define SOLID_PHASE 5 // won't slow down when hitting entities flagged as FL_MONSTER // // edict->deadflag values // #define DEAD_NO 0 #define DEAD_DYING 1 #define DEAD_DEAD 2 #define DAMAGE_NO 0 // Cannot be damaged #define DAMAGE_YES 1 // Can be damaged // // edict->flags // #define FL_FLY 1 #define FL_SWIM 2 #define FL_CONVEYOR 4 #define FL_CLIENT 8 #define FL_INWATER 16 #define FL_MONSTER 32 #define FL_GODMODE 64 #define FL_NOTARGET 128 #define FL_ITEM 256 #define FL_ONGROUND 512 #define FL_PARTIALGROUND 1024 // not all corners are valid #define FL_WATERJUMP 2048 // player jumping out of water #define FL_JUMPRELEASED 4096 // for jump debouncing #define FL_FLASHLIGHT 8192 #define FL_ARCHIVE_OVERRIDE 1048576 #define FL_ARTIFACTUSED 16384 #define FL_MOVECHAIN_ANGLE 32768 // when in a move chain, will update the angle #define FL_CLASS_DEPENDENT 2097152 // model will appear different to each player #define FL_SPECIAL_ABILITY1 4194304 // has 1st special ability #define FL_SPECIAL_ABILITY2 8388608 // has 2nd special ability #define FL2_CROUCHED 4096 // // Built-in Spawn Flags // #define SPAWNFLAG_NOT_PALADIN 0x00000100 #define SPAWNFLAG_NOT_CLERIC 0x00000200 #define SPAWNFLAG_NOT_NECROMANCER 0x00000400 #define SPAWNFLAG_NOT_THEIF 0x00000800 #define SPAWNFLAG_NOT_EASY 0x00001000 #define SPAWNFLAG_NOT_MEDIUM 0x00002000 #define SPAWNFLAG_NOT_HARD 0x00004000 #define SPAWNFLAG_NOT_DEATHMATCH 0x00008000 #define SPAWNFLAG_NOT_COOP 0x00010000 #define SPAWNFLAG_NOT_SINGLE 0x00020000 // // server flags // #define SFL_EPISODE_1 1 #define SFL_EPISODE_2 2 #define SFL_EPISODE_3 4 #define SFL_EPISODE_4 8 #define SFL_NEW_UNIT 16 #define SFL_NEW_EPISODE 32 #define SFL_CROSS_TRIGGERS 65280 #define MULTICAST_ALL 0 #define MULTICAST_PHS 1 #define MULTICAST_PVS 2 #define MULTICAST_ALL_R 3 #define MULTICAST_PHS_R 4 #define MULTICAST_PVS_R 5 //============================================================================ extern cvar_t sv_mintic, sv_maxtic; extern cvar_t sv_maxspeed; extern cvar_t sv_highchars; extern netadr_t master_adr[MAX_MASTERS]; // address of the master server extern cvar_t spawn; extern cvar_t teamplay; extern cvar_t skill; extern cvar_t deathmatch; extern cvar_t coop; extern cvar_t randomclass; extern cvar_t maxclients; extern cvar_t damageScale; extern cvar_t meleeDamScale; extern cvar_t shyRespawn; extern cvar_t spartanPrint; extern cvar_t manaScale; extern cvar_t tomeMode; extern cvar_t tomeRespawn; extern cvar_t w2Respawn; extern cvar_t altRespawn; extern cvar_t fixedLevel; extern cvar_t autoItems; extern cvar_t dmMode; extern cvar_t easyFourth; extern cvar_t patternRunner; extern cvar_t fraglimit; extern cvar_t timelimit; extern cvar_t noexit; extern server_static_t svs; // persistant server info extern server_t sv; // local server extern client_t *host_client; extern edict_t *sv_player; extern char localmodels[MAX_MODELS][8]; // inline model names for precache extern char localinfo[MAX_LOCALINFO_STRING+1]; extern int host_hunklevel; extern FILE *sv_logfile; extern FILE *sv_fraglogfile; extern unsigned int defLosses; // Defenders losses in Siege extern unsigned int attLosses; // Attackers Losses in Siege #define SV_PROGS_HAVE_SIEGE (sv_globals.defLosses) /* progs have siege fields: >= v0.14 */ //=========================================================== // // sv_main.c // void SV_Shutdown (void); void SV_Frame (float time); void SV_FinalMessage (const char *message); void SV_DropClient (client_t *drop); int SV_CalcPing (client_t *cl); void SV_FullClientUpdate (client_t *client, sizebuf_t *buf); int SV_ModelIndex (const char *name); qboolean SV_CheckBottom (edict_t *ent); qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink, qboolean noenemy, qboolean set_trace); void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg); void SV_MoveToGoal (void); void SV_SaveSpawnparms (void); void SV_Physics_Client (edict_t *ent); void SV_ExecuteUserCommand (const char *s); void SV_InitOperatorCommands (void); void SV_SendServerinfo (client_t *client); void SV_ExtractFromUserinfo (client_t *cl); void Master_Heartbeat (void); void Master_Packet (void); // // sv_init.c // void SV_SpawnServer (const char *server, const char *startspot); void SV_FlushSignon (void); const char *SV_GetLevelname (void); // // sv_phys.c // void SV_ProgStartFrame (void); void SV_Physics (void); void SV_CheckVelocity (edict_t *ent); void SV_AddGravity (edict_t *ent, float scale); qboolean SV_RunThink (edict_t *ent); void SV_Physics_Toss (edict_t *ent); void SV_RunNewmis (void); void SV_Impact (edict_t *e1, edict_t *e2); void SV_SetMoveVars (void); // // sv_send.c // extern unsigned int clients_multicast; void SV_SendClientMessages (void); void SV_Multicast (vec3_t origin, int to); void SV_MulticastSpecific (unsigned int clients, qboolean reliable); void SV_StartSound (edict_t *entity, int channel, const char *sample, int volume, float attenuation); void SV_StopSound (edict_t *entity, int channel); void SV_UpdateSoundPos (edict_t *entity, int channel); void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count); void SV_StartParticle2 (vec3_t org, vec3_t dmin, vec3_t dmax, int color, int effect, int count); void SV_StartParticle3 (vec3_t org, vec3_t box, int color, int effect, int count); void SV_StartParticle4 (vec3_t org, float radius, int color, int effect, int count); void SV_StartRainEffect (vec3_t org, vec3_t e_size, int x_dir, int y_dir, int color, int count); void SV_ClientPrintf (client_t *cl, int level, const char *fmt, ...) FUNC_PRINTF(3,4); void SV_BroadcastPrintf (int level, const char *fmt, ...) FUNC_PRINTF(2,3); void SV_BroadcastCommand (const char *fmt, ...) FUNC_PRINTF(1,2); void SV_SendMessagesToAll (void); void SV_FindModelNumbers (void); // // sv_user.c // void SV_ExecuteClientMessage (client_t *cl); void SV_UserInit (void); // // svonly.c // typedef enum { RD_NONE, RD_CLIENT, RD_PACKET } redirect_t; void SV_BeginRedirect (redirect_t rd); void SV_EndRedirect (void); // // sv_ccmds.c // //void SV_Status_f (void); // // sv_ents.c // void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg); void SV_WriteInventory (client_t *host_cl, edict_t *ent, sizebuf_t *msg); // // sv_effect.c // void SV_ParseEffect (sizebuf_t *sb); void SV_SendEffect (sizebuf_t *sb, int idx); void SV_SaveEffects (FILE *FH); void SV_LoadEffects (FILE *FH); #endif /* __H2W_SERVER_H */ engine/hexenworld/server/sv_ccmds.c000066400000000000000000000442311444734033100177640ustar00rootroot00000000000000/* * sv_ccmds.c -- console commands * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "filenames.h" static qboolean sv_allow_cheats; int fp_messages = 4, fp_persecond = 4, fp_secondsdead = 10; char fp_msg[255] = { 0 }; /* =============================================================================== OPERATOR CONSOLE ONLY COMMANDS These commands can only be entered from stdin or by a remote operator datagram =============================================================================== */ /* ==================== SV_SetMaster_f Make a master server current ==================== */ static void SV_SetMaster_f (void) { char data[2]; int i; memset (master_adr, 0, sizeof(master_adr)); for (i = 1; i < Cmd_Argc(); i++) { if (!strcmp(Cmd_Argv(i), "none") || !NET_StringToAdr (Cmd_Argv(i), &master_adr[i-1])) { Con_Printf ("Setting nomaster mode.\n"); return; } if (master_adr[i-1].port == 0) master_adr[i-1].port = BigShort (PORT_MASTER); Con_Printf ("Master server at %s\n", NET_AdrToString (&master_adr[i-1])); Con_Printf ("Sending a ping.\n"); data[0] = A2A_PING; data[1] = 0; NET_SendPacket (2, data, &master_adr[i-1]); } svs.last_heartbeat = -99999; } /* ================== SV_Quit_f ================== */ static void SV_Quit_f (void) { SV_FinalMessage ("server shutdown\n"); Con_Printf ("Shutting down.\n"); SV_Shutdown (); Sys_Quit (); } /* ============ SV_Logfile_f ============ */ static void SV_Logfile_f (void) { const char *name; if (sv_logfile) { Con_Printf ("File logging off.\n"); fclose (sv_logfile); sv_logfile = NULL; return; } name = FS_MakePath(FS_USERDIR, NULL, "hwsv.log"); Con_Printf ("Logging text to %s.\n", name); sv_logfile = fopen (name, "w"); if (!sv_logfile) Con_Printf ("Failed opening hwsv.log\n"); else fflush (sv_logfile); } /* ============ SV_Fraglogfile_f ============ */ static void SV_Fraglogfile_f (void) { char name[MAX_OSPATH]; int i; if (sv_fraglogfile) { Con_Printf ("Frag file logging off.\n"); fclose (sv_fraglogfile); sv_fraglogfile = NULL; return; } // find an unused name for (i = 0; i < 1000; i++) { FS_MakePath_VABUF (FS_USERDIR, NULL, name, sizeof(name), "frag_%i.log", i); sv_fraglogfile = fopen (name, "r"); if (!sv_fraglogfile) { // can't read it, so create this one sv_fraglogfile = fopen (name, "w"); if (!sv_fraglogfile) i = 1000; // give error break; } fclose (sv_fraglogfile); } if (i == 1000) { Con_Printf ("Can't open any logfiles.\n"); sv_fraglogfile = NULL; return; } Con_Printf ("Logging frags to %s.\n", name); fflush (sv_fraglogfile); } /* ================== SV_SetPlayer Sets host_client and sv_player to the player with idnum Cmd_Argv(1) ================== */ static qboolean SV_SetPlayer (void) { client_t *cl; int i; int idnum; idnum = atoi(Cmd_Argv(1)); for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (!cl->state) continue; if (cl->userid == idnum) { host_client = cl; sv_player = host_client->edict; return true; } } Con_Printf ("Userid %i is not on the server\n", idnum); return false; } /* ================== SV_God_f Sets client to godmode ================== */ static void SV_God_f (void) { if (!sv_allow_cheats) { Con_Printf ("You must run the server with -cheats to enable this command.\n"); return; } if (!SV_SetPlayer ()) return; sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE; if ( !((int)sv_player->v.flags & FL_GODMODE) ) SV_ClientPrintf (host_client, PRINT_HIGH, "godmode OFF\n"); else SV_ClientPrintf (host_client, PRINT_HIGH, "godmode ON\n"); } static void SV_Noclip_f (void) { if (!sv_allow_cheats) { Con_Printf ("You must run the server with -cheats to enable this command.\n"); return; } if (!SV_SetPlayer ()) return; if (sv_player->v.movetype != MOVETYPE_NOCLIP) { sv_player->v.movetype = MOVETYPE_NOCLIP; SV_ClientPrintf (host_client, PRINT_HIGH, "noclip ON\n"); } else { sv_player->v.movetype = MOVETYPE_WALK; SV_ClientPrintf (host_client, PRINT_HIGH, "noclip OFF\n"); } } /* ================== SV_Give_f ================== */ static void SV_Give_f (void) { const char *t; int v; if (!sv_allow_cheats) { Con_Printf ("You must run the server with -cheats to enable this command.\n"); return; } if (!SV_SetPlayer ()) return; t = Cmd_Argv(2); v = atoi (Cmd_Argv(3)); switch (t[0]) { case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': sv_player->v.items = (int)sv_player->v.items | IT_SHOTGUN<< (t[0] - '2'); break; case 's': //rjr sv_player->v.ammo_shells = v; break; case 'n': //rjr sv_player->v.ammo_nails = v; break; case 'r': //rjr sv_player->v.ammo_rockets = v; break; case 'h': sv_player->v.health = v; break; case 'c': //rjr sv_player->v.ammo_cells = v; break; } } /* ====================== SV_Map_f handle a map command from the console or progs. ====================== */ static void SV_Map_f (void) { char level[MAX_QPATH]; char expanded[MAX_QPATH]; char _startspot[MAX_QPATH]; char *startspot; if (Cmd_Argc() < 2) { Con_Printf ("map : continue game on a new level\n"); if (sv.state == ss_active) { Con_Printf ("Current level: %s [ %s ]\n", SV_GetLevelname(), sv.name); } return; } q_strlcpy (level, Cmd_Argv(1), sizeof(level)); if (Cmd_Argc() == 2) { startspot = NULL; } else { q_strlcpy (_startspot, Cmd_Argv(2), sizeof(_startspot)); startspot = _startspot; } // check to make sure the level exists q_snprintf (expanded, sizeof(expanded), "maps/%s.bsp", level); if (!FS_FileExists(expanded, NULL)) { Con_Printf ("Can't find %s\n", expanded); return; } SV_BroadcastCommand ("changing\n"); SV_SendMessagesToAll (); SV_SpawnServer (level, startspot); SV_BroadcastCommand ("reconnect\n"); } /* ================== SV_Kick_f Kick a user off of the server ================== */ static void SV_Kick_f (void) { int i; client_t *cl; int uid; uid = atoi(Cmd_Argv(1)); for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (!cl->state) continue; if (cl->userid == uid) { SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked\n", cl->name); // print directly, because the dropped client won't get the // SV_BroadcastPrintf message SV_ClientPrintf (cl, PRINT_HIGH, "You were kicked from the game\n"); SV_DropClient (cl); *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (*sv_globals.ClientKill); return; } } Con_Printf ("Couldn't find user number %i\n", uid); } /* ================== SV_Smite_f ================== */ static void SV_Smite_f (void) { int i; client_t *cl; int uid; int old_self; if (!sv_globals.SmitePlayer) /* needs in HW v0.15 */ return; uid = atoi(Cmd_Argv(1)); for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (cl->state != cs_spawned) continue; if (cl->userid == uid) { if (cl->old_v.health <= 0) { Con_Printf("%s is already dead!\n", cl->name); return; } SV_BroadcastPrintf (PRINT_HIGH, "%s was Smitten by GOD!\n", cl->name); //save this state old_self = *sv_globals.self; //call the hc SmitePlayer function *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(cl->edict); PR_ExecuteProgram (*sv_globals.SmitePlayer); //restore current state *sv_globals.self = old_self; return; } } Con_Printf ("Couldn't find user number %i\n", uid); } /* ================ SV_Status_f ================ */ extern redirect_t sv_redirected; static void SV_Status_f (void) { int i, j, l, num_min, num_sec; client_t *cl; float cpu, avg, pak, t_limit,f_limit; const char *s; cpu = (svs.stats.latched_active+svs.stats.latched_idle); if (cpu) cpu = 100 * svs.stats.latched_active / cpu; avg = 1000 * svs.stats.latched_active / STATFRAMES; pak = (float)svs.stats.latched_packets/ STATFRAMES; Con_Printf ("net address : %s\n",NET_AdrToString (&net_local_adr)); Con_Printf ("cpu utilization : %3i%%\n",(int)cpu); Con_Printf ("avg response time: %i ms\n",(int)avg); Con_Printf ("packets/frame : %5.2f\n", pak); t_limit = Cvar_VariableValue("timelimit"); f_limit = Cvar_VariableValue("fraglimit"); if (dmMode.integer == DM_SIEGE && SV_PROGS_HAVE_SIEGE) { num_min = floor((t_limit*60)-sv.time); num_sec = (int)(t_limit - num_min)%60; num_sec = floor(num_sec); num_min = floor((num_min - num_sec)/60); Con_Printf ("timeleft : %i:", num_min); Con_Printf ("%2i\n", num_sec); Con_Printf ("deflosses : %3i/%3i\n", (int)floor(*sv_globals.defLosses), (int)floor(f_limit)); Con_Printf ("attlosses : %3i/%3i\n", (int)floor(*sv_globals.attLosses), (int)floor(f_limit*2)); } else { Con_Printf ("time : %5.2f\n", sv.time); Con_Printf ("timelimit : %i\n", (int)t_limit); Con_Printf ("fraglimit : %i\n", (int)f_limit); } // min fps lat drp if (sv_redirected != RD_NONE) { // most remote clients are 40 columns // 0123456789012345678901234567890123456789 Con_Printf ("name userid frags\n"); Con_Printf (" address rate ping drop\n"); Con_Printf (" ---------------- ---- ---- -----\n"); for (i = 0, cl = svs.clients; i < MAX_CLIENTS ; i++, cl++) { if (!cl->state) continue; Con_Printf ("%-16.16s ", cl->name); Con_Printf ("%6i %5i", cl->userid, (int)cl->edict->v.frags); if (cl->spectator) Con_Printf(" (s)\n"); else Con_Printf("\n"); s = NET_BaseAdrToString (&cl->netchan.remote_address); Con_Printf (" %-16.16s", s); if (cl->state == cs_connected) { Con_Printf ("CONNECTING\n"); continue; } if (cl->state == cs_zombie) { Con_Printf ("ZOMBIE\n"); continue; } Con_Printf ("%4i %4i %5.2f\n", (int)(1000*cl->netchan.frame_rate), (int)SV_CalcPing (cl), 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence); } } else { Con_Printf ("frags userid address name rate ping drop siege\n"); Con_Printf ("----- ------ --------------- --------------- ---- ---- ----- -----\n"); for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (!cl->state) continue; Con_Printf ("%5i %6i ", (int)cl->edict->v.frags, cl->userid); s = NET_BaseAdrToString (&cl->netchan.remote_address); Con_Printf ("%s", s); l = 16 - strlen(s); for (j = 0; j < l; j++) Con_Printf (" "); Con_Printf ("%s", cl->name); l = 16 - strlen(cl->name); for (j = 0; j < l; j++) Con_Printf (" "); if (cl->state == cs_connected) { Con_Printf ("CONNECTING\n"); continue; } if (cl->state == cs_zombie) { Con_Printf ("ZOMBIE\n"); continue; } Con_Printf ("%4i %4i %5.2f", (int)(1000*cl->netchan.frame_rate), (int)SV_CalcPing (cl), 100.0*cl->netchan.drop_count / cl->netchan.incoming_sequence); if (cl->spectator) Con_Printf(" (s)\n"); else { Con_Printf(" "); switch (cl->playerclass) { case CLASS_PALADIN: Con_Printf("P"); break; case CLASS_CLERIC: Con_Printf("C"); break; case CLASS_NECROMANCER: Con_Printf("N"); break; case CLASS_THEIF: Con_Printf("A"); break; case CLASS_DEMON: Con_Printf("S"); break; case CLASS_DWARF: Con_Printf("D"); break; default: Con_Printf("?"); break; } switch (cl->siege_team) { case ST_DEFENDER: Con_Printf("D"); break; case ST_ATTACKER: Con_Printf("A"); break; default: Con_Printf("?"); break; } if ((int)cl->old_v.flags2 & 65536) //defender of crown Con_Printf("D"); else Con_Printf("-"); if ((int)cl->old_v.flags2 & 524288) //has siege key Con_Printf("K"); else Con_Printf("-"); Con_Printf("\n"); } } } Con_Printf ("\n"); } /* ================== SV_ConSay_f ================== */ static void SV_ConSay_f(void) { client_t *client; int j = 0; const char *p; char text[1024]; if (Cmd_Argc () < 2) return; if (dmMode.integer == DM_SIEGE && SV_PROGS_HAVE_SIEGE) q_strlcpy (text, "GOD SAYS: ", sizeof(text)); else q_strlcpy (text, "ServerAdmin: ", sizeof(text)); p = Cmd_Args(); if (*p == '"') { p++; j = 1; } q_strlcat (text, p, sizeof(text)); if (j == 1) // remove trailing quotes text[strlen(text)-1] = '\0'; for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { if (client->state != cs_spawned) continue; SV_ClientPrintf(client, PRINT_CHAT, "%s\n", text); } } /* ================== SV_Heartbeat_f ================== */ static void SV_Heartbeat_f (void) { svs.last_heartbeat = -9999; } /* =========== SV_Serverinfo_f Examine or change the serverinfo string =========== */ static void SV_Serverinfo_f (void) { cvar_t *var; if (Cmd_Argc() == 1) { Con_Printf ("Server info settings:\n"); Info_Print (svs.info); return; } if (Cmd_Argc() != 3) { Con_Printf ("usage: serverinfo [ ]\n"); return; } if (Cmd_Argv(1)[0] == '*') { Con_Printf ("Star variables cannot be changed.\n"); return; } Info_SetValueForKey (svs.info, Cmd_Argv(1), Cmd_Argv(2), MAX_SERVERINFO_STRING); // if this is a cvar, change it too var = Cvar_FindVar (Cmd_Argv(1)); if (var) { const char *c; Z_Free ((void *)var->string); // free the old value string c = Cmd_Argv(2); var->string = Z_Strdup (c); var->value = atof (var->string); } SV_BroadcastCommand ("fullserverinfo \"%s\"\n", svs.info); } /* =========== SV_Localinfo_f Examine or change the localinfo string =========== */ static void SV_Localinfo_f (void) { if (Cmd_Argc() == 1) { Con_Printf ("Local info settings:\n"); Info_Print (localinfo); return; } if (Cmd_Argc() != 3) { Con_Printf ("usage: localinfo [ ]\n"); return; } if (Cmd_Argv(1)[0] == '*') { Con_Printf ("Star variables cannot be changed.\n"); return; } Info_SetValueForKey (localinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_LOCALINFO_STRING); } /* =========== SV_User_f Examine a users info strings =========== */ static void SV_User_f (void) { if (Cmd_Argc() != 2) { Con_Printf ("Usage: info \n"); return; } if (!SV_SetPlayer ()) return; Info_Print (host_client->userinfo); } /* ================ SV_Gamedir Sets the fake *gamedir to a different directory. ================ */ static void SV_Gamedir (void) { const char *dir; if (Cmd_Argc() == 1) { Con_Printf ("Current *gamedir: %s\n", Info_ValueForKey (svs.info, "*gamedir")); return; } if (Cmd_Argc() != 2) { Con_Printf ("Usage: sv_gamedir \n"); return; } dir = Cmd_Argv(1); if (strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\") || strstr(dir, ":")) { Con_Printf ("gamedir should be a single directory name, not a path\n"); return; } Info_SetValueForStarKey (svs.info, "*gamedir", dir, MAX_SERVERINFO_STRING); } /* ================ SV_Floodport_f ================ */ static void SV_Floodprot_f (void) { int arg1, arg2, arg3; if (Cmd_Argc() == 1) { if (fp_messages) { Con_Printf ("Current floodprot settings: \nAfter %d msgs per %d seconds, silence for %d seconds\n", fp_messages, fp_persecond, fp_secondsdead); return; } else Con_Printf ("No floodprots enabled.\n"); } if (Cmd_Argc() != 4) { Con_Printf ("Usage: floodprot <# of messages> \n"); Con_Printf ("Use floodprotmsg to set a custom message to say to the flooder.\n"); return; } arg1 = atoi(Cmd_Argv(1)); arg2 = atoi(Cmd_Argv(2)); arg3 = atoi(Cmd_Argv(3)); if (arg1 <= 0 || arg2 <= 0 || arg3 <= 0) { Con_Printf ("All values must be positive numbers\n"); return; } if (arg1 > 10) { Con_Printf ("Can only track up to 10 messages.\n"); return; } fp_messages = arg1; fp_persecond = arg2; fp_secondsdead = arg3; } static void SV_Floodprotmsg_f (void) { if (Cmd_Argc() == 1) { Con_Printf("Current msg: %s\n", fp_msg); return; } else if (Cmd_Argc() != 2) { Con_Printf("Usage: floodprotmsg \"\"\n"); return; } q_snprintf(fp_msg, sizeof(fp_msg), "%s", Cmd_Argv(1)); } /* ================ SV_Gamedir_f Sets the gamedir and path to a different directory. ================ */ static void SV_Gamedir_f (void) { if (Cmd_Argc() == 1) { Con_Printf ("Current gamedir: %s\n", FS_GetGamedir()); return; } if (Cmd_Argc() != 2) { Con_Printf ("Usage: gamedir \n"); return; } FS_Gamedir (Cmd_Argv(1)); // also sets *gamedir } /* ================== SV_InitOperatorCommands ================== */ void SV_InitOperatorCommands (void) { if (COM_CheckParm ("-cheats")) { sv_allow_cheats = true; Info_SetValueForStarKey (svs.info, "*cheats", "ON", MAX_SERVERINFO_STRING); } Cmd_AddCommand ("logfile", SV_Logfile_f); Cmd_AddCommand ("fraglogfile", SV_Fraglogfile_f); Cmd_AddCommand ("kick", SV_Kick_f); Cmd_AddCommand ("status", SV_Status_f); Cmd_AddCommand ("smite", SV_Smite_f); Cmd_AddCommand ("map", SV_Map_f); Cmd_AddCommand ("setmaster", SV_SetMaster_f); Cmd_AddCommand ("say", SV_ConSay_f); Cmd_AddCommand ("heartbeat", SV_Heartbeat_f); Cmd_AddCommand ("quit", SV_Quit_f); Cmd_AddCommand ("god", SV_God_f); Cmd_AddCommand ("give", SV_Give_f); Cmd_AddCommand ("noclip", SV_Noclip_f); Cmd_AddCommand ("serverinfo", SV_Serverinfo_f); Cmd_AddCommand ("localinfo", SV_Localinfo_f); Cmd_AddCommand ("user", SV_User_f); Cmd_AddCommand ("gamedir", SV_Gamedir_f); Cmd_AddCommand ("sv_gamedir", SV_Gamedir); Cmd_AddCommand ("floodprot", SV_Floodprot_f); Cmd_AddCommand ("floodprotmsg", SV_Floodprotmsg_f); } engine/hexenworld/server/sv_effect.c000066400000000000000000001254001444734033100201250ustar00rootroot00000000000000/* sv_effect.c -- Client side effects. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // HEADER FILES ------------------------------------------------------------ #include "quakedef.h" // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern cvar_t sv_ce_scale; extern cvar_t sv_ce_max_size; // PUBLIC DATA DEFINITIONS ------------------------------------------------- // PRIVATE DATA DEFINITIONS ------------------------------------------------ // CODE -------------------------------------------------------------------- static void SV_ClearEffects (void) { memset(sv.Effects, 0, sizeof(sv.Effects)); } // All changes need to be in SV_SendEffect(), SV_ParseEffect(), // SV_SaveEffects(), SV_LoadEffects(), CL_ParseEffect() void SV_SendEffect (sizebuf_t *sb, int idx) { qboolean DoTest; vec3_t TestO1; // int TestDistance; /* not used. (cf. hexen2 version of the function.) */ int i; if (sv_ce_scale.value > 0) DoTest = true; else DoTest = false; VectorClear(TestO1); switch (sv.Effects[idx].type) { case CE_HWSHEEPINATOR: case CE_HWXBOWSHOOT: VectorCopy(sv.Effects[idx].ef.Xbow.origin[5], TestO1); // TestDistance = 900; break; case CE_SCARABCHAIN: VectorCopy(sv.Effects[idx].ef.Chain.origin, TestO1); // TestDistance = 900; break; case CE_TRIPMINE: VectorCopy(sv.Effects[idx].ef.Chain.origin, TestO1); // DoTest = false; break; //ACHTUNG!!!!!!! setting DoTest to false here does not insure // that effect will be sent to everyone! case CE_TRIPMINESTILL: // TestDistance = 10000; DoTest = false; break; case CE_RAIN: // TestDistance = 10000; DoTest = false; break; case CE_FOUNTAIN: // TestDistance = 10000; DoTest = false; break; case CE_QUAKE: VectorCopy(sv.Effects[idx].ef.Quake.origin, TestO1); // TestDistance = 700; break; case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK1: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: case CE_FLAMESTREAM: case CE_ACID_MUZZFL: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: case CE_RIPPLE: VectorCopy(sv.Effects[idx].ef.Smoke.origin, TestO1); // TestDistance = 250; break; case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_SM_EXPLOSION2: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_ACID_HIT: case CE_LBALL_EXPL: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_FBOOM: case CE_BRN_BOUNCE: case CE_LSHOCK: case CE_BOMB: case CE_FLOOR_EXPLOSION3: VectorCopy(sv.Effects[idx].ef.Smoke.origin, TestO1); // TestDistance = 250; break; case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_HWSPLITFLASH: case CE_RED_FLASH: VectorCopy(sv.Effects[idx].ef.Smoke.origin, TestO1); // TestDistance = 250; break; case CE_RIDER_DEATH: DoTest = false; break; case CE_TELEPORTERPUFFS: VectorCopy(sv.Effects[idx].ef.Teleporter.origin, TestO1); // TestDistance = 350; break; case CE_TELEPORTERBODY: VectorCopy(sv.Effects[idx].ef.Teleporter.origin, TestO1); // TestDistance = 350; break; case CE_DEATHBUBBLES: if (sv.Effects[idx].ef.Bubble.owner < 0 || sv.Effects[idx].ef.Bubble.owner >= sv.num_edicts) { return; } VectorCopy(PROG_TO_EDICT(sv.Effects[idx].ef.Bubble.owner)->v.origin, TestO1); // TestDistance = 400; break; case CE_HWDRILLA: case CE_BONESHARD: case CE_BONESHRAPNEL: case CE_HWBONEBALL: case CE_HWRAVENSTAFF: case CE_HWRAVENPOWER: VectorCopy(sv.Effects[idx].ef.Missile.origin, TestO1); // TestDistance = 900; break; case CE_HWMISSILESTAR: case CE_HWEIDOLONSTAR: VectorCopy(sv.Effects[idx].ef.Missile.origin, TestO1); // TestDistance = 600; break; default: PR_RunError ("%s: bad type", __thisfunc__); break; } MSG_WriteByte (&sv.multicast, svc_start_effect); MSG_WriteByte (&sv.multicast, idx); MSG_WriteByte (&sv.multicast, sv.Effects[idx].type); switch (sv.Effects[idx].type) { case CE_RAIN: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.min_org[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.min_org[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.min_org[2]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.max_org[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.max_org[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.max_org[2]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.e_size[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.e_size[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.e_size[2]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.dir[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.dir[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Rain.dir[2]); MSG_WriteShort(&sv.multicast, sv.Effects[idx].ef.Rain.color); MSG_WriteShort(&sv.multicast, sv.Effects[idx].ef.Rain.count); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Rain.wait); break; case CE_FOUNTAIN: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Fountain.pos[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Fountain.pos[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Fountain.pos[2]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Fountain.angle[0]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Fountain.angle[1]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Fountain.angle[2]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Fountain.movedir[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Fountain.movedir[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Fountain.movedir[2]); MSG_WriteShort(&sv.multicast, sv.Effects[idx].ef.Fountain.color); MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Fountain.cnt); break; case CE_QUAKE: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Quake.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Quake.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Quake.origin[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Quake.radius); break; case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK1: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: case CE_FLAMESTREAM: case CE_ACID_MUZZFL: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: case CE_RIPPLE: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Smoke.velocity[0]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Smoke.velocity[1]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Smoke.velocity[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Smoke.framelength); break; case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_SM_EXPLOSION2: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_ACID_HIT: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_LBALL_EXPL: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: case CE_FBOOM: case CE_BOMB: case CE_BRN_BOUNCE: case CE_LSHOCK: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[2]); break; case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_HWSPLITFLASH: case CE_RED_FLASH: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Smoke.origin[2]); break; case CE_RIDER_DEATH: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.RD.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.RD.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.RD.origin[2]); break; case CE_TELEPORTERPUFFS: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Teleporter.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Teleporter.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Teleporter.origin[2]); break; case CE_TELEPORTERBODY: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Teleporter.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Teleporter.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Teleporter.origin[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Teleporter.velocity[0][0]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Teleporter.velocity[0][1]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Teleporter.velocity[0][2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Teleporter.skinnum); break; case CE_BONESHRAPNEL: case CE_HWBONEBALL: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.velocity[0]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.velocity[1]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.velocity[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.angle[0]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.angle[1]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.angle[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.avelocity[0]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.avelocity[1]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.avelocity[2]); break; case CE_BONESHARD: case CE_HWRAVENSTAFF: case CE_HWMISSILESTAR: case CE_HWEIDOLONSTAR: case CE_HWRAVENPOWER: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.velocity[0]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.velocity[1]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Missile.velocity[2]); break; case CE_HWDRILLA: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Missile.origin[2]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Missile.angle[0]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Missile.angle[1]); MSG_WriteShort(&sv.multicast, sv.Effects[idx].ef.Missile.speed); break; case CE_DEATHBUBBLES: MSG_WriteShort(&sv.multicast, sv.Effects[idx].ef.Bubble.owner); MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Bubble.offset[0]); MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Bubble.offset[1]); MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Bubble.offset[2]); MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Bubble.count); break; case CE_SCARABCHAIN: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Chain.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Chain.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Chain.origin[2]); MSG_WriteShort(&sv.multicast, sv.Effects[idx].ef.Chain.owner + sv.Effects[idx].ef.Chain.material); MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Chain.tag); break; case CE_TRIPMINESTILL: case CE_TRIPMINE: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Chain.origin[0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Chain.origin[1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Chain.origin[2]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Chain.velocity[0]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Chain.velocity[1]); MSG_WriteFloat(&sv.multicast, sv.Effects[idx].ef.Chain.velocity[2]); break; case CE_HWSHEEPINATOR: MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Xbow.origin[5][0]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Xbow.origin[5][1]); MSG_WriteCoord(&sv.multicast, sv.Effects[idx].ef.Xbow.origin[5][2]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Xbow.angle[0]); MSG_WriteAngle(&sv.multicast, sv.Effects[idx].ef.Xbow.angle[1]); /* now send the guys that have turned */ MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Xbow.turnedbolts); MSG_WriteByte(&sv.multicast, sv.Effects[idx].ef.Xbow.activebolts); for (i = 0 ; i < 5 ; i++) { if ((1<= MAX_EFFECTS) { PR_RunError ("MAX_EFFECTS reached"); return; } // Con_Printf("Effect #%d\n", idx); memset(&sv.Effects[idx], 0, sizeof(struct EffectT)); sv.Effects[idx].type = effect; G_FLOAT(OFS_RETURN) = idx; switch (effect) { case CE_RAIN: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Rain.min_org); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Rain.max_org); VectorCopy(G_VECTOR(OFS_PARM3), sv.Effects[idx].ef.Rain.e_size); VectorCopy(G_VECTOR(OFS_PARM4), sv.Effects[idx].ef.Rain.dir); sv.Effects[idx].ef.Rain.color = G_FLOAT(OFS_PARM5); sv.Effects[idx].ef.Rain.count = G_FLOAT(OFS_PARM6); sv.Effects[idx].ef.Rain.wait = G_FLOAT(OFS_PARM7); sv.Effects[idx].ef.Rain.next_time = 0; break; case CE_FOUNTAIN: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Fountain.pos); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Fountain.angle); VectorCopy(G_VECTOR(OFS_PARM3), sv.Effects[idx].ef.Fountain.movedir); sv.Effects[idx].ef.Fountain.color = G_FLOAT(OFS_PARM4); sv.Effects[idx].ef.Fountain.cnt = G_FLOAT(OFS_PARM5); break; case CE_QUAKE: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Quake.origin); sv.Effects[idx].ef.Quake.radius = G_FLOAT(OFS_PARM2); break; case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK1: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: case CE_RIPPLE: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Smoke.origin); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Smoke.velocity); sv.Effects[idx].ef.Smoke.framelength = G_FLOAT(OFS_PARM3); sv.Effects[idx].expire_time = sv.time + 1; break; case CE_ACID_MUZZFL: case CE_FLAMESTREAM: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Smoke.origin); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Smoke.velocity); sv.Effects[idx].ef.Smoke.framelength = 0.05; sv.Effects[idx].ef.Smoke.frame = G_FLOAT(OFS_PARM3); sv.Effects[idx].expire_time = sv.time + 1; break; case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_SM_EXPLOSION2: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_ACID_HIT: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_LBALL_EXPL: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: case CE_FBOOM: case CE_BOMB: case CE_BRN_BOUNCE: case CE_LSHOCK: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Smoke.origin); sv.Effects[idx].expire_time = sv.time + 1; break; case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_HWSPLITFLASH: case CE_RED_FLASH: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Flash.origin); sv.Effects[idx].expire_time = sv.time + 1; break; case CE_RIDER_DEATH: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.RD.origin); break; case CE_TELEPORTERPUFFS: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Teleporter.origin); sv.Effects[idx].expire_time = sv.time + 1; break; case CE_TELEPORTERBODY: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Teleporter.origin); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Teleporter.velocity[0]); sv.Effects[idx].ef.Teleporter.skinnum = G_FLOAT(OFS_PARM3); sv.Effects[idx].expire_time = sv.time + 1; break; case CE_BONESHRAPNEL: case CE_HWBONEBALL: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Missile.origin); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Missile.velocity); VectorCopy(G_VECTOR(OFS_PARM3), sv.Effects[idx].ef.Missile.angle); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Missile.avelocity); sv.Effects[idx].expire_time = sv.time + 10; break; case CE_BONESHARD: case CE_HWRAVENSTAFF: case CE_HWMISSILESTAR: case CE_HWEIDOLONSTAR: case CE_HWRAVENPOWER: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Missile.origin); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Missile.velocity); sv.Effects[idx].expire_time = sv.time + 10; break; case CE_DEATHBUBBLES: VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Bubble.offset); sv.Effects[idx].ef.Bubble.owner = G_EDICTNUM(OFS_PARM1); sv.Effects[idx].ef.Bubble.count = G_FLOAT(OFS_PARM3); sv.Effects[idx].expire_time = sv.time + 30; break; case CE_HWDRILLA: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Missile.origin); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Missile.angle); sv.Effects[idx].ef.Missile.speed = G_FLOAT(OFS_PARM3); sv.Effects[idx].expire_time = sv.time + 10; break; case CE_TRIPMINESTILL: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Chain.origin); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Chain.velocity); sv.Effects[idx].expire_time = sv.time + 70; break; case CE_TRIPMINE: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Chain.origin); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Chain.velocity); sv.Effects[idx].expire_time = sv.time + 10; break; case CE_SCARABCHAIN: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Chain.origin); sv.Effects[idx].ef.Chain.owner = G_EDICTNUM(OFS_PARM2); sv.Effects[idx].ef.Chain.material = G_INT(OFS_PARM3); sv.Effects[idx].ef.Chain.tag = G_INT(OFS_PARM4); sv.Effects[idx].ef.Chain.state = 0; sv.Effects[idx].expire_time = sv.time + 15; break; case CE_HWSHEEPINATOR: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Xbow.origin[0]); VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Xbow.origin[1]); VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Xbow.origin[2]); VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Xbow.origin[3]); VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Xbow.origin[4]); VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Xbow.origin[5]); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Xbow.angle); sv.Effects[idx].ef.Xbow.bolts = 5; sv.Effects[idx].ef.Xbow.activebolts = 31; sv.Effects[idx].ef.Xbow.randseed = 0; sv.Effects[idx].ef.Xbow.turnedbolts = 0; sv.Effects[idx].expire_time = sv.time + 7; break; case CE_HWXBOWSHOOT: VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Xbow.origin[0]); VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Xbow.origin[1]); VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Xbow.origin[2]); VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Xbow.origin[3]); VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Xbow.origin[4]); VectorCopy(G_VECTOR(OFS_PARM1), sv.Effects[idx].ef.Xbow.origin[5]); VectorCopy(G_VECTOR(OFS_PARM2), sv.Effects[idx].ef.Xbow.angle); sv.Effects[idx].ef.Xbow.bolts = G_FLOAT(OFS_PARM3); sv.Effects[idx].ef.Xbow.randseed = G_FLOAT(OFS_PARM4); sv.Effects[idx].ef.Xbow.turnedbolts = 0; if (sv.Effects[idx].ef.Xbow.bolts == 3) { sv.Effects[idx].ef.Xbow.activebolts = 7; } else { sv.Effects[idx].ef.Xbow.activebolts = 31; } sv.Effects[idx].expire_time = sv.time + 15; break; default: PR_RunError ("%s: bad type", __thisfunc__); } SV_SendEffect(sb, idx); } /* this random generator can have its effects duplicated on the client * side by passing the randomseed over the network, as opposed to sending * all the generated values */ static unsigned int randomseed; void SV_setseed (int seed) { randomseed = seed; } float SV_seedrand (void) { randomseed = (randomseed * 877 + 573) % 9968; return (float)randomseed / 9968; } /* this will create several effects and store the ids in the array */ static float MultiEffectIds[10]; static int MultiEffectIdCount; void SV_ParseMultiEffect (sizebuf_t *sb) { int idx, count; byte effect; vec3_t orig, vel; MultiEffectIdCount = 0; effect = G_FLOAT(OFS_PARM0); switch (effect) { case CE_HWRAVENPOWER: /* need to set aside 3 effect ids */ MSG_WriteByte (sb, svc_multieffect); MSG_WriteByte (sb, effect); VectorCopy(G_VECTOR(OFS_PARM1), orig); MSG_WriteCoord(sb, orig[0]); MSG_WriteCoord(sb, orig[1]); MSG_WriteCoord(sb, orig[2]); VectorCopy(G_VECTOR(OFS_PARM2), vel); MSG_WriteCoord(sb, vel[0]); MSG_WriteCoord(sb, vel[1]); MSG_WriteCoord(sb, vel[2]); for (count = 0 ; count < 3 ; count++) { for (idx = 0 ; idx < MAX_EFFECTS ; idx++) { if (!sv.Effects[idx].type || (sv.Effects[idx].expire_time && sv.Effects[idx].expire_time <= sv.time)) break; } if (idx >= MAX_EFFECTS) { PR_RunError ("MAX_EFFECTS reached"); return; } MSG_WriteByte(sb, idx); sv.Effects[idx].type = CE_HWRAVENPOWER; VectorCopy(orig, sv.Effects[idx].ef.Missile.origin); VectorCopy(vel, sv.Effects[idx].ef.Missile.velocity); sv.Effects[idx].expire_time = sv.time + 10; MultiEffectIds[count] = idx; } break; default: PR_RunError ("%s: bad type", __thisfunc__); } } float SV_GetMultiEffectId (void) { MultiEffectIdCount++; return MultiEffectIds[MultiEffectIdCount-1]; } /* saving and loading games not supported in hexenworld yet. */ // All changes need to be in SV_SendEffect(), SV_ParseEffect(), // SV_SaveEffects(), SV_LoadEffects(), CL_ParseEffect() void SV_SaveEffects (FILE *FH) { int idx, count; for (idx = count = 0 ; idx < MAX_EFFECTS ; idx++) { if (sv.Effects[idx].type) count++; } fprintf(FH, "Effects: %d\n", count); for (idx = count = 0 ; idx < MAX_EFFECTS ; idx++) { if (!sv.Effects[idx].type) continue; fprintf(FH, "Effect: %d %d %f: ", idx, sv.Effects[idx].type, sv.Effects[idx].expire_time); switch (sv.Effects[idx].type) { case CE_RAIN: fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.min_org[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.min_org[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.min_org[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.max_org[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.max_org[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.max_org[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.e_size[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.e_size[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.e_size[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.dir[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.dir[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Rain.dir[2]); fprintf(FH, "%d ", sv.Effects[idx].ef.Rain.color); fprintf(FH, "%d ", sv.Effects[idx].ef.Rain.count); fprintf(FH, "%f\n", sv.Effects[idx].ef.Rain.wait); break; case CE_FOUNTAIN: fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.pos[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.pos[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.pos[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.angle[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.angle[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.angle[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.movedir[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.movedir[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Fountain.movedir[2]); fprintf(FH, "%d ", sv.Effects[idx].ef.Fountain.color); fprintf(FH, "%d\n", sv.Effects[idx].ef.Fountain.cnt); break; case CE_QUAKE: fprintf(FH, "%f ", sv.Effects[idx].ef.Quake.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Quake.origin[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Quake.origin[2]); fprintf(FH, "%f\n", sv.Effects[idx].ef.Quake.radius); break; case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK1: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: case CE_ACID_MUZZFL: case CE_FLAMESTREAM: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: case CE_RIPPLE: fprintf(FH, "%f ", sv.Effects[idx].ef.Smoke.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Smoke.origin[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Smoke.origin[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Smoke.velocity[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Smoke.velocity[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Smoke.velocity[2]); fprintf(FH, "%f\n", sv.Effects[idx].ef.Smoke.framelength); break; case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_SM_EXPLOSION2: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_BRN_BOUNCE: case CE_LSHOCK: case CE_ACID_HIT: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_LBALL_EXPL: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: case CE_FBOOM: case CE_BOMB: fprintf(FH, "%f ", sv.Effects[idx].ef.Smoke.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Smoke.origin[1]); fprintf(FH, "%f\n", sv.Effects[idx].ef.Smoke.origin[2]); break; case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_HWSPLITFLASH: case CE_RED_FLASH: fprintf(FH, "%f ", sv.Effects[idx].ef.Flash.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Flash.origin[1]); fprintf(FH, "%f\n", sv.Effects[idx].ef.Flash.origin[2]); break; case CE_RIDER_DEATH: fprintf(FH, "%f ", sv.Effects[idx].ef.RD.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.RD.origin[1]); fprintf(FH, "%f\n", sv.Effects[idx].ef.RD.origin[2]); break; case CE_TELEPORTERPUFFS: fprintf(FH, "%f ", sv.Effects[idx].ef.Teleporter.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Teleporter.origin[1]); fprintf(FH, "%f\n", sv.Effects[idx].ef.Teleporter.origin[2]); break; case CE_TELEPORTERBODY: fprintf(FH, "%f ", sv.Effects[idx].ef.Teleporter.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Teleporter.origin[1]); fprintf(FH, "%f\n", sv.Effects[idx].ef.Teleporter.origin[2]); break; case CE_BONESHRAPNEL: case CE_HWBONEBALL: fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.origin[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.origin[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.velocity[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.velocity[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.velocity[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.angle[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.angle[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.angle[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.avelocity[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.avelocity[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.avelocity[2]); break; case CE_BONESHARD: case CE_HWRAVENSTAFF: case CE_HWRAVENPOWER: case CE_HWMISSILESTAR: case CE_HWEIDOLONSTAR: fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.origin[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.origin[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.velocity[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.velocity[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.velocity[2]); break; case CE_DEATHBUBBLES: fprintf(FH, "%f ", sv.Effects[idx].ef.Bubble.offset[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Bubble.offset[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Bubble.offset[2]); fprintf(FH, "%d ", sv.Effects[idx].ef.Bubble.owner); fprintf(FH, "%d ", sv.Effects[idx].ef.Bubble.count); break; case CE_HWDRILLA: fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.origin[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.origin[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.angle[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.angle[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.angle[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Missile.speed); break; case CE_SCARABCHAIN: fprintf(FH, "%f ", sv.Effects[idx].ef.Chain.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Chain.origin[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Chain.origin[2]); fprintf(FH, "%d ", sv.Effects[idx].ef.Chain.owner); fprintf(FH, "%d ", sv.Effects[idx].ef.Chain.material); fprintf(FH, "%d ", sv.Effects[idx].ef.Chain.tag); break; case CE_HWSHEEPINATOR: case CE_HWXBOWSHOOT: fprintf(FH, "%f ", sv.Effects[idx].ef.Xbow.origin[5][0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Xbow.origin[5][1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Xbow.origin[5][2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Xbow.angle[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Xbow.angle[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Xbow.angle[2]); fprintf(FH, "%d ", sv.Effects[idx].ef.Xbow.bolts); fprintf(FH, "%d ", sv.Effects[idx].ef.Xbow.activebolts); fprintf(FH, "%d ", sv.Effects[idx].ef.Xbow.turnedbolts); break; case CE_TRIPMINESTILL: case CE_TRIPMINE: fprintf(FH, "%f ", sv.Effects[idx].ef.Chain.origin[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Chain.origin[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Chain.origin[2]); fprintf(FH, "%f ", sv.Effects[idx].ef.Chain.velocity[0]); fprintf(FH, "%f ", sv.Effects[idx].ef.Chain.velocity[1]); fprintf(FH, "%f ", sv.Effects[idx].ef.Chain.velocity[2]); break; default: Host_Error ("%s: bad type", __thisfunc__); break; } } } // All changes need to be in SV_SendEffect(), SV_ParseEffect(), // SV_SaveEffects(), SV_LoadEffects(), CL_ParseEffect() void SV_LoadEffects (FILE *FH) { int idx, Total, count; Total = idx = -1; /* Since the map is freshly loaded, clear out any effects as a result of the loading */ SV_ClearEffects(); fscanf(FH, "Effects: %d\n", &Total); if (Total < 0 || Total > MAX_EFFECTS) Host_Error ("%s: bad numeffects", __thisfunc__); for (count = 0 ; count < Total ; idx = -1, count++) { fscanf(FH, "Effect: %d ", &idx); if (idx < 0 || idx >= MAX_EFFECTS) Host_Error ("%s: bad index", __thisfunc__); fscanf(FH, "%d %f: ", &sv.Effects[idx].type, &sv.Effects[idx].expire_time); switch (sv.Effects[idx].type) { case CE_RAIN: fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.min_org[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.min_org[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.min_org[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.max_org[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.max_org[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.max_org[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.e_size[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.e_size[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.e_size[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.dir[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.dir[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Rain.dir[2]); fscanf(FH, "%d ", &sv.Effects[idx].ef.Rain.color); fscanf(FH, "%d ", &sv.Effects[idx].ef.Rain.count); fscanf(FH, "%f\n", &sv.Effects[idx].ef.Rain.wait); break; case CE_FOUNTAIN: fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.pos[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.pos[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.pos[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.angle[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.angle[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.angle[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.movedir[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.movedir[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Fountain.movedir[2]); fscanf(FH, "%d ", &sv.Effects[idx].ef.Fountain.color); fscanf(FH, "%d\n", &sv.Effects[idx].ef.Fountain.cnt); break; case CE_QUAKE: fscanf(FH, "%f ", &sv.Effects[idx].ef.Quake.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Quake.origin[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Quake.origin[2]); fscanf(FH, "%f\n", &sv.Effects[idx].ef.Quake.radius); break; case CE_WHITE_SMOKE: case CE_GREEN_SMOKE: case CE_GREY_SMOKE: case CE_RED_SMOKE: case CE_SLOW_WHITE_SMOKE: case CE_TELESMK1: case CE_TELESMK2: case CE_GHOST: case CE_REDCLOUD: case CE_ACID_MUZZFL: case CE_FLAMESTREAM: case CE_FLAMEWALL: case CE_FLAMEWALL2: case CE_ONFIRE: case CE_RIPPLE: fscanf(FH, "%f ", &sv.Effects[idx].ef.Smoke.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Smoke.origin[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Smoke.origin[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Smoke.velocity[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Smoke.velocity[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Smoke.velocity[2]); fscanf(FH, "%f\n", &sv.Effects[idx].ef.Smoke.framelength); break; case CE_SM_WHITE_FLASH: case CE_YELLOWRED_FLASH: case CE_BLUESPARK: case CE_YELLOWSPARK: case CE_SM_CIRCLE_EXP: case CE_BG_CIRCLE_EXP: case CE_SM_EXPLOSION: case CE_SM_EXPLOSION2: case CE_LG_EXPLOSION: case CE_FLOOR_EXPLOSION: case CE_BLUE_EXPLOSION: case CE_REDSPARK: case CE_GREENSPARK: case CE_ICEHIT: case CE_MEDUSA_HIT: case CE_MEZZO_REFLECT: case CE_FLOOR_EXPLOSION2: case CE_XBOW_EXPLOSION: case CE_NEW_EXPLOSION: case CE_MAGIC_MISSILE_EXPLOSION: case CE_BONE_EXPLOSION: case CE_BLDRN_EXPL: case CE_BRN_BOUNCE: case CE_LSHOCK: case CE_ACID_HIT: case CE_ACID_SPLAT: case CE_ACID_EXPL: case CE_LBALL_EXPL: case CE_FBOOM: case CE_FIREWALL_SMALL: case CE_FIREWALL_MEDIUM: case CE_FIREWALL_LARGE: case CE_BOMB: fscanf(FH, "%f ", &sv.Effects[idx].ef.Smoke.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Smoke.origin[1]); fscanf(FH, "%f\n", &sv.Effects[idx].ef.Smoke.origin[2]); break; case CE_WHITE_FLASH: case CE_BLUE_FLASH: case CE_SM_BLUE_FLASH: case CE_HWSPLITFLASH: case CE_RED_FLASH: fscanf(FH, "%f ", &sv.Effects[idx].ef.Flash.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Flash.origin[1]); fscanf(FH, "%f\n", &sv.Effects[idx].ef.Flash.origin[2]); break; case CE_RIDER_DEATH: fscanf(FH, "%f ", &sv.Effects[idx].ef.RD.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.RD.origin[1]); fscanf(FH, "%f\n", &sv.Effects[idx].ef.RD.origin[2]); break; case CE_TELEPORTERPUFFS: fscanf(FH, "%f ", &sv.Effects[idx].ef.Teleporter.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Teleporter.origin[1]); fscanf(FH, "%f\n", &sv.Effects[idx].ef.Teleporter.origin[2]); break; case CE_TELEPORTERBODY: fscanf(FH, "%f ", &sv.Effects[idx].ef.Teleporter.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Teleporter.origin[1]); fscanf(FH, "%f\n", &sv.Effects[idx].ef.Teleporter.origin[2]); break; case CE_BONESHRAPNEL: case CE_HWBONEBALL: fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.origin[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.origin[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.velocity[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.velocity[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.velocity[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.angle[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.angle[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.angle[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.avelocity[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.avelocity[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.avelocity[2]); break; case CE_BONESHARD: case CE_HWRAVENSTAFF: case CE_HWRAVENPOWER: case CE_HWMISSILESTAR: case CE_HWEIDOLONSTAR: fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.origin[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.origin[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.velocity[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.velocity[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.velocity[2]); break; case CE_DEATHBUBBLES: fscanf(FH, "%f ", &sv.Effects[idx].ef.Bubble.offset[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Bubble.offset[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Bubble.offset[2]); fscanf(FH, "%d ", &sv.Effects[idx].ef.Bubble.owner); fscanf(FH, "%d ", &sv.Effects[idx].ef.Bubble.count); break; case CE_HWDRILLA: fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.origin[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.origin[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.angle[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.angle[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.angle[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Missile.speed); break; case CE_SCARABCHAIN: fscanf(FH, "%f ", &sv.Effects[idx].ef.Chain.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Chain.origin[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Chain.origin[2]); fscanf(FH, "%d ", &sv.Effects[idx].ef.Chain.owner); fscanf(FH, "%d ", &sv.Effects[idx].ef.Chain.material); fscanf(FH, "%d ", &sv.Effects[idx].ef.Chain.tag); break; case CE_HWSHEEPINATOR: case CE_HWXBOWSHOOT: fscanf(FH, "%f ", &sv.Effects[idx].ef.Xbow.origin[5][0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Xbow.origin[5][1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Xbow.origin[5][2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Xbow.angle[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Xbow.angle[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Xbow.angle[2]); fscanf(FH, "%d ", &sv.Effects[idx].ef.Xbow.bolts); fscanf(FH, "%d ", &sv.Effects[idx].ef.Xbow.activebolts); fscanf(FH, "%d ", &sv.Effects[idx].ef.Xbow.turnedbolts); break; case CE_TRIPMINESTILL: case CE_TRIPMINE: fscanf(FH, "%f ", &sv.Effects[idx].ef.Chain.origin[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Chain.origin[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Chain.origin[2]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Chain.velocity[0]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Chain.velocity[1]); fscanf(FH, "%f ", &sv.Effects[idx].ef.Chain.velocity[2]); break; default: Host_Error ("%s: bad type", __thisfunc__); break; } } } engine/hexenworld/server/sv_ents.c000066400000000000000000001120261444734033100176420ustar00rootroot00000000000000/* sv_ents.c -- server entities handling * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" /* ============================================================================= The PVS must include a small area around the client to allow head bobbing or other small motion on the client side. Otherwise, a bob might cause an entity that should be visible to not show up, especially when the bob crosses a waterline. ============================================================================= */ static int fatbytes; static byte fatpvs[MAX_MAP_LEAFS/8]; static void SV_AddToFatPVS (vec3_t org, mnode_t *node) { int i; byte *pvs; mplane_t *plane; float d; while (1) { // if this is a leaf, accumulate the pvs bits if (node->contents < 0) { if (node->contents != CONTENTS_SOLID) { pvs = Mod_LeafPVS ( (mleaf_t *)node, sv.worldmodel); for (i = 0; i < fatbytes; i++) fatpvs[i] |= pvs[i]; } return; } plane = node->plane; d = DotProduct (org, plane->normal) - plane->dist; if (d > 8) node = node->children[0]; else if (d < -8) node = node->children[1]; else { // go down both SV_AddToFatPVS (org, node->children[0]); node = node->children[1]; } } } /* ============= SV_FatPVS Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the given point. ============= */ static byte *SV_FatPVS (vec3_t org) { fatbytes = (sv.worldmodel->numleafs+31)>>3; memset (fatpvs, 0, fatbytes); SV_AddToFatPVS (org, sv.worldmodel->nodes); return fatpvs; } //============================================================================= /* // because there can be a lot of nails, there is a special // network protocol for them RDM: changed to use packed missiles, this code left here in case we decide to pack missiles which require a velocity as well #define MAX_NAILS 32 edict_t *nails[MAX_NAILS]; int numnails; extern int sv_nailmodel, sv_supernailmodel, sv_playermodel[MAX_PLAYER_CLASS]; qboolean SV_AddNailUpdate (edict_t *ent) { if (ent->v.modelindex != sv_nailmodel && ent->v.modelindex != sv_supernailmodel) return false; if (numnails == MAX_NAILS) return true; nails[numnails] = ent; numnails++; return true; } void SV_EmitNailUpdate (sizebuf_t *msg) { byte bits[6]; // [48 bits] xyzpy 12 12 12 4 8 int n, i; edict_t *ent; int x, y, z, p, yaw; if (!numnails) return; MSG_WriteByte (msg, svc_nails); MSG_WriteByte (msg, numnails); for (n = 0; n < numnails; n++) { ent = nails[n]; x = (int)(ent->v.origin[0] + 4096) >> 1; y = (int)(ent->v.origin[1] + 4096) >> 1; z = (int)(ent->v.origin[2] + 4096) >> 1; p = (int)(16 * ent->v.angles[0] / 360) & 15; yaw = (int)(256 * ent->v.angles[1] / 360) & 255; bits[0] = x; bits[1] = (x>>8) | (y<<4); bits[2] = (y>>4); bits[3] = z; bits[4] = (z>>8) | (p<<4); bits[5] = yaw; for (i = 0; i < 6; i++) MSG_WriteByte (msg, bits[i]); } } */ #define MAX_MISSILES 32 static edict_t *missiles[MAX_MISSILES]; static edict_t *ravens[MAX_MISSILES]; static edict_t *raven2s[MAX_MISSILES]; static int nummissiles, numravens, numraven2s; extern int sv_magicmissmodel, sv_playermodel[MAX_PLAYER_CLASS], sv_ravenmodel, sv_raven2model; static qboolean SV_AddMissileUpdate (edict_t *ent) { if (ent->v.modelindex == sv_magicmissmodel) { if (nummissiles == MAX_MISSILES) return true; missiles[nummissiles] = ent; nummissiles++; return true; } if (ent->v.modelindex == sv_ravenmodel) { if (numravens == MAX_MISSILES) return true; ravens[numravens] = ent; numravens++; return true; } if (ent->v.modelindex == sv_raven2model) { if (numraven2s == MAX_MISSILES) return true; raven2s[numraven2s] = ent; numraven2s++; return true; } return false; } static void SV_EmitMissileUpdate (sizebuf_t *msg) { byte bits[5]; // [40 bits] xyz type 12 12 12 4 int n, i; edict_t *ent; int x, y, z, type; if (!nummissiles) return; MSG_WriteByte (msg, svc_packmissile); MSG_WriteByte (msg, nummissiles); for (n = 0; n < nummissiles; n++) { ent = missiles[n]; x = (int)(ent->v.origin[0] + 4096) >> 1; y = (int)(ent->v.origin[1] + 4096) >> 1; z = (int)(ent->v.origin[2] + 4096) >> 1; if (fabs(ent->v.scale - 0.1) < 0.05) type = 1; //assume ice mace else type = 2; //assume magic missile bits[0] = x; bits[1] = (x>>8) | (y<<4); bits[2] = (y>>4); bits[3] = z; bits[4] = (z>>8) | (type<<4); for (i = 0; i < 5; i++) MSG_WriteByte (msg, bits[i]); } } static void SV_EmitRavenUpdate (sizebuf_t *msg) { byte bits[6]; // [48 bits] xyzpy 12 12 12 4 8 int n, i; edict_t *ent; int x, y, z, p, yaw, frame; if ((!numravens) && (!numraven2s)) return; MSG_WriteByte (msg, svc_nails); //svc nails overloaded for ravens MSG_WriteByte (msg, numravens); for (n = 0; n < numravens; n++) { ent = ravens[n]; x = (int)(ent->v.origin[0] + 4096) >> 1; y = (int)(ent->v.origin[1] + 4096) >> 1; z = (int)(ent->v.origin[2] + 4096) >> 1; p = (int)(16 * ent->v.angles[0] / 360) & 15; frame = (int)(ent->v.frame) & 7; yaw = (int)(32 * ent->v.angles[1] / 360) & 31; bits[0] = x; bits[1] = (x>>8) | (y<<4); bits[2] = (y>>4); bits[3] = z; bits[4] = (z>>8) | (p<<4); bits[5] = yaw | (frame<<5); for (i = 0; i < 6; i++) MSG_WriteByte (msg, bits[i]); } MSG_WriteByte (msg, numraven2s); for (n = 0; n < numraven2s; n++) { ent = raven2s[n]; x = (int)(ent->v.origin[0] + 4096) >> 1; y = (int)(ent->v.origin[1] + 4096) >> 1; z = (int)(ent->v.origin[2] + 4096) >> 1; p = (int)(16 * ent->v.angles[0] / 360) & 15; yaw = (int)(256 * ent->v.angles[1] / 360) & 255; bits[0] = x; bits[1] = (x>>8) | (y<<4); bits[2] = (y>>4); bits[3] = z; bits[4] = (z>>8) | (p<<4); bits[5] = yaw; for (i = 0; i < 6; i++) MSG_WriteByte (msg, bits[i]); } } static void SV_EmitPackedEntities(sizebuf_t *msg) { SV_EmitMissileUpdate(msg); SV_EmitRavenUpdate(msg); } //============================================================================= /* ================== SV_WriteDelta Writes part of a packetentities message. Can delta from either a baseline or a previous packet_entity ================== */ static void SV_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean force, edict_t *ent, client_t *client) { int bits; int i; float miss; int temp_index; char NewName[MAX_QPATH]; // send an update bits = 0; for (i = 0; i < 3; i++) { miss = to->origin[i] - from->origin[i]; if ( miss < -0.1 || miss > 0.1 ) bits |= U_ORIGIN1<angles[0] != from->angles[0] ) bits |= U_ANGLE1; if ( to->angles[1] != from->angles[1] ) bits |= U_ANGLE2; if ( to->angles[2] != from->angles[2] ) bits |= U_ANGLE3; if ( to->colormap != from->colormap ) bits |= U_COLORMAP; if ( to->skinnum != from->skinnum) { bits |= U_SKIN; } if (to->drawflags != from->drawflags) bits |= U_DRAWFLAGS; if ( to->frame != from->frame ) bits |= U_FRAME; if ( to->effects != from->effects ) bits |= U_EFFECTS; temp_index = to->modelindex; if (((int)ent->v.flags & FL_CLASS_DEPENDENT) && ent->v.model) { strcpy (NewName, PR_GetString(ent->v.model)); if (client->playerclass <= 0 || client->playerclass > MAX_PLAYER_CLASS) { NewName[strlen(NewName)-5] = '1'; } else { NewName[strlen(NewName)-5] = client->playerclass + 48; } temp_index = SV_ModelIndex (NewName); } if (temp_index != from->modelindex ) { bits |= U_MODEL; if (temp_index > 255) { bits |= U_MODEL16; } } if (to->scale != from->scale) { bits |= U_SCALE; } if (to->abslight != from->abslight) { bits |= U_ABSLIGHT; } if (to->wpn_sound) { //not delta'ed, sound gets cleared after send bits |= U_SOUND; } if (bits & 0xff0000) bits |= U_MOREBITS2; if (bits & 511) bits |= U_MOREBITS; // // write the message // if (!to->number) SV_Error ("Unset entity number"); if (to->number >= 512) SV_Error ("Entity number >= 512"); if (!bits && !force) return; // nothing to send! i = to->number | (bits & ~511); if (i & U_REMOVE) Sys_Error ("U_REMOVE"); MSG_WriteShort (msg, i & 0xffff); if (bits & U_MOREBITS) MSG_WriteByte (msg, bits & 255); if (bits & U_MOREBITS2) MSG_WriteByte (msg, (bits >> 16) & 0xff); if (bits & U_MODEL) { if (bits & U_MODEL16) { MSG_WriteShort (msg, temp_index); } else { MSG_WriteByte (msg, temp_index); } } if (bits & U_FRAME) MSG_WriteByte (msg, to->frame); if (bits & U_COLORMAP) MSG_WriteByte (msg, to->colormap); if (bits & U_SKIN) MSG_WriteByte (msg, to->skinnum); if (bits & U_DRAWFLAGS) MSG_WriteByte (msg, to->drawflags); if (bits & U_EFFECTS) MSG_WriteLong (msg, to->effects); if (bits & U_ORIGIN1) MSG_WriteCoord (msg, to->origin[0]); if (bits & U_ANGLE1) MSG_WriteAngle(msg, to->angles[0]); if (bits & U_ORIGIN2) MSG_WriteCoord (msg, to->origin[1]); if (bits & U_ANGLE2) MSG_WriteAngle(msg, to->angles[1]); if (bits & U_ORIGIN3) MSG_WriteCoord (msg, to->origin[2]); if (bits & U_ANGLE3) MSG_WriteAngle(msg, to->angles[2]); if (bits & U_SCALE) MSG_WriteByte (msg, to->scale); if (bits & U_ABSLIGHT) MSG_WriteByte (msg, to->abslight); if (bits & U_SOUND) MSG_WriteShort (msg, to->wpn_sound); } /* ============= SV_EmitPacketEntities Writes a delta update of a packet_entities_t to the message. ============= */ static void SV_EmitPacketEntities (client_t *client, packet_entities_t *to, sizebuf_t *msg) { edict_t *ent; client_frame_t *fromframe; packet_entities_t *from; int oldindex, newindex; int oldnum, newnum; int oldmax; // this is the frame that we are going to delta update from if (client->delta_sequence != -1) { fromframe = &client->frames[client->delta_sequence & UPDATE_MASK]; from = &fromframe->entities; oldmax = from->num_entities; MSG_WriteByte (msg, svc_deltapacketentities); MSG_WriteByte (msg, client->delta_sequence); } else { oldmax = 0; // no delta update from = NULL; MSG_WriteByte (msg, svc_packetentities); } newindex = 0; oldindex = 0; // Con_Printf ("---%i to %i ----\n", client->delta_sequence & UPDATE_MASK, // client->netchan.outgoing_sequence & UPDATE_MASK); while (newindex < to->num_entities || oldindex < oldmax) { newnum = newindex >= to->num_entities ? 9999 : to->entities[newindex].number; oldnum = oldindex >= oldmax ? 9999 : from->entities[oldindex].number; if (newnum == oldnum) { // delta update from old position // Con_Printf ("delta %i\n", newnum); SV_WriteDelta (&from->entities[oldindex], &to->entities[newindex], msg, false, EDICT_NUM(newnum), client); oldindex++; newindex++; continue; } if (newnum < oldnum) { // this is a new entity, send it from the baseline ent = EDICT_NUM(newnum); // Con_Printf ("baseline %i\n", newnum); SV_WriteDelta (&ent->baseline, &to->entities[newindex], msg, true, ent, client); newindex++; continue; } if (newnum > oldnum) { // the old entity isn't present in the new message // Con_Printf ("remove %i\n", oldnum); MSG_WriteShort (msg, oldnum | U_REMOVE); oldindex++; continue; } } MSG_WriteShort (msg, 0); // end of packetentities } void SV_WriteInventory (client_t *host_cl, edict_t *ent, sizebuf_t *msg) { int sc1, sc2; byte test; if (host_cl->send_all_v) { sc1 = sc2 = 0xffffffff; host_cl->send_all_v = false; } else { sc1 = sc2 = 0; if (ent->v.health != host_cl->old_v.health) sc1 |= SC1_HEALTH; if (ent->v.level != host_cl->old_v.level) sc1 |= SC1_LEVEL; if (ent->v.intelligence != host_cl->old_v.intelligence) sc1 |= SC1_INTELLIGENCE; if (ent->v.wisdom != host_cl->old_v.wisdom) sc1 |= SC1_WISDOM; if (ent->v.strength != host_cl->old_v.strength) sc1 |= SC1_STRENGTH; if (ent->v.dexterity != host_cl->old_v.dexterity) sc1 |= SC1_DEXTERITY; if (ent->v.teleport_time > sv.time) { // Con_Printf ("Teleport_time>time, sending bit\n"); sc1 |= SC1_TELEPORT_TIME; // ent->v.teleport_time = 0; } // if (ent->v.weapon != host_cl->old_v.weapon) // sc1 |= SC1_WEAPON; if (ent->v.bluemana != host_cl->old_v.bluemana) sc1 |= SC1_BLUEMANA; if (ent->v.greenmana != host_cl->old_v.greenmana) sc1 |= SC1_GREENMANA; if (ent->v.experience != host_cl->old_v.experience) sc1 |= SC1_EXPERIENCE; if (ent->v.cnt_torch != host_cl->old_v.cnt_torch) sc1 |= SC1_CNT_TORCH; if (ent->v.cnt_h_boost != host_cl->old_v.cnt_h_boost) sc1 |= SC1_CNT_H_BOOST; if (ent->v.cnt_sh_boost != host_cl->old_v.cnt_sh_boost) sc1 |= SC1_CNT_SH_BOOST; if (ent->v.cnt_mana_boost != host_cl->old_v.cnt_mana_boost) sc1 |= SC1_CNT_MANA_BOOST; if (ent->v.cnt_teleport != host_cl->old_v.cnt_teleport) sc1 |= SC1_CNT_TELEPORT; if (ent->v.cnt_tome != host_cl->old_v.cnt_tome) sc1 |= SC1_CNT_TOME; if (ent->v.cnt_summon != host_cl->old_v.cnt_summon) sc1 |= SC1_CNT_SUMMON; if (ent->v.cnt_invisibility != host_cl->old_v.cnt_invisibility) sc1 |= SC1_CNT_INVISIBILITY; if (ent->v.cnt_glyph != host_cl->old_v.cnt_glyph) sc1 |= SC1_CNT_GLYPH; if (ent->v.cnt_haste != host_cl->old_v.cnt_haste) sc1 |= SC1_CNT_HASTE; if (ent->v.cnt_blast != host_cl->old_v.cnt_blast) sc1 |= SC1_CNT_BLAST; if (ent->v.cnt_polymorph != host_cl->old_v.cnt_polymorph) sc1 |= SC1_CNT_POLYMORPH; if (ent->v.cnt_flight != host_cl->old_v.cnt_flight) sc1 |= SC1_CNT_FLIGHT; if (ent->v.cnt_cubeofforce != host_cl->old_v.cnt_cubeofforce) sc1 |= SC1_CNT_CUBEOFFORCE; if (ent->v.cnt_invincibility != host_cl->old_v.cnt_invincibility) sc1 |= SC1_CNT_INVINCIBILITY; if (ent->v.artifact_active != host_cl->old_v.artifact_active) sc1 |= SC1_ARTIFACT_ACTIVE; if (ent->v.artifact_low != host_cl->old_v.artifact_low) sc1 |= SC1_ARTIFACT_LOW; if (ent->v.movetype != host_cl->old_v.movetype) sc1 |= SC1_MOVETYPE; if (ent->v.cameramode != host_cl->old_v.cameramode) sc1 |= SC1_CAMERAMODE; if (ent->v.hasted != host_cl->old_v.hasted) sc1 |= SC1_HASTED; if (ent->v.inventory != host_cl->old_v.inventory) sc1 |= SC1_INVENTORY; if (ent->v.rings_active != host_cl->old_v.rings_active) sc1 |= SC1_RINGS_ACTIVE; if (ent->v.rings_low != host_cl->old_v.rings_low) sc2 |= SC2_RINGS_LOW; if (ent->v.armor_amulet != host_cl->old_v.armor_amulet) sc2 |= SC2_AMULET; if (ent->v.armor_bracer != host_cl->old_v.armor_bracer) sc2 |= SC2_BRACER; if (ent->v.armor_breastplate != host_cl->old_v.armor_breastplate) sc2 |= SC2_BREASTPLATE; if (ent->v.armor_helmet != host_cl->old_v.armor_helmet) sc2 |= SC2_HELMET; if (ent->v.ring_flight != host_cl->old_v.ring_flight) sc2 |= SC2_FLIGHT_T; if (ent->v.ring_water != host_cl->old_v.ring_water) sc2 |= SC2_WATER_T; if (ent->v.ring_turning != host_cl->old_v.ring_turning) sc2 |= SC2_TURNING_T; if (ent->v.ring_regeneration != host_cl->old_v.ring_regeneration) sc2 |= SC2_REGEN_T; // if (ent->v.haste_time != host_cl->old_v.haste_time) // sc2 |= SC2_HASTE_T; // if (ent->v.tome_time != host_cl->old_v.tome_time) // sc2 |= SC2_TOME_T; if (ent->v.puzzle_inv1 != host_cl->old_v.puzzle_inv1) sc2 |= SC2_PUZZLE1; if (ent->v.puzzle_inv2 != host_cl->old_v.puzzle_inv2) sc2 |= SC2_PUZZLE2; if (ent->v.puzzle_inv3 != host_cl->old_v.puzzle_inv3) sc2 |= SC2_PUZZLE3; if (ent->v.puzzle_inv4 != host_cl->old_v.puzzle_inv4) sc2 |= SC2_PUZZLE4; if (ent->v.puzzle_inv5 != host_cl->old_v.puzzle_inv5) sc2 |= SC2_PUZZLE5; if (ent->v.puzzle_inv6 != host_cl->old_v.puzzle_inv6) sc2 |= SC2_PUZZLE6; if (ent->v.puzzle_inv7 != host_cl->old_v.puzzle_inv7) sc2 |= SC2_PUZZLE7; if (ent->v.puzzle_inv8 != host_cl->old_v.puzzle_inv8) sc2 |= SC2_PUZZLE8; if (ent->v.max_health != host_cl->old_v.max_health) sc2 |= SC2_MAXHEALTH; if (ent->v.max_mana != host_cl->old_v.max_mana) sc2 |= SC2_MAXMANA; if (ent->v.flags != host_cl->old_v.flags) sc2 |= SC2_FLAGS; } if (!sc1 && !sc2) goto end; MSG_WriteByte (msg, svc_update_inv); test = 0; if (sc1 & 0x000000ff) test |= 1; if (sc1 & 0x0000ff00) test |= 2; if (sc1 & 0x00ff0000) test |= 4; if (sc1 & 0xff000000) test |= 8; if (sc2 & 0x000000ff) test |= 16; if (sc2 & 0x0000ff00) test |= 32; if (sc2 & 0x00ff0000) test |= 64; if (sc2 & 0xff000000) test |= 128; MSG_WriteByte (msg, test); if (test & 1) MSG_WriteByte (msg, sc1 & 0xff); if (test & 2) MSG_WriteByte (msg, (sc1 >> 8) & 0xff); if (test & 4) MSG_WriteByte (msg, (sc1 >> 16) & 0xff); if (test & 8) MSG_WriteByte (msg, (sc1 >> 24) & 0xff); if (test & 16) MSG_WriteByte (msg, sc2 & 0xff); if (test & 32) MSG_WriteByte (msg, (sc2 >> 8) & 0xff); if (test & 64) MSG_WriteByte (msg, (sc2 >> 16) & 0xff); if (test & 128) MSG_WriteByte (msg, (sc2 >> 24) & 0xff); if (sc1 & SC1_HEALTH) MSG_WriteShort (msg, ent->v.health); if (sc1 & SC1_LEVEL) MSG_WriteByte(msg, ent->v.level); if (sc1 & SC1_INTELLIGENCE) MSG_WriteByte(msg, ent->v.intelligence); if (sc1 & SC1_WISDOM) MSG_WriteByte(msg, ent->v.wisdom); if (sc1 & SC1_STRENGTH) MSG_WriteByte(msg, ent->v.strength); if (sc1 & SC1_DEXTERITY) MSG_WriteByte(msg, ent->v.dexterity); // if (sc1 & SC1_WEAPON) // MSG_WriteByte (msg, ent->v.weapon); if (sc1 & SC1_BLUEMANA) MSG_WriteByte (msg, ent->v.bluemana); if (sc1 & SC1_GREENMANA) MSG_WriteByte (msg, ent->v.greenmana); if (sc1 & SC1_EXPERIENCE) MSG_WriteLong (msg, ent->v.experience); if (sc1 & SC1_CNT_TORCH) MSG_WriteByte (msg, ent->v.cnt_torch); if (sc1 & SC1_CNT_H_BOOST) MSG_WriteByte (msg, ent->v.cnt_h_boost); if (sc1 & SC1_CNT_SH_BOOST) MSG_WriteByte (msg, ent->v.cnt_sh_boost); if (sc1 & SC1_CNT_MANA_BOOST) MSG_WriteByte (msg, ent->v.cnt_mana_boost); if (sc1 & SC1_CNT_TELEPORT) MSG_WriteByte (msg, ent->v.cnt_teleport); if (sc1 & SC1_CNT_TOME) MSG_WriteByte (msg, ent->v.cnt_tome); if (sc1 & SC1_CNT_SUMMON) MSG_WriteByte (msg, ent->v.cnt_summon); if (sc1 & SC1_CNT_INVISIBILITY) MSG_WriteByte (msg, ent->v.cnt_invisibility); if (sc1 & SC1_CNT_GLYPH) MSG_WriteByte (msg, ent->v.cnt_glyph); if (sc1 & SC1_CNT_HASTE) MSG_WriteByte (msg, ent->v.cnt_haste); if (sc1 & SC1_CNT_BLAST) MSG_WriteByte (msg, ent->v.cnt_blast); if (sc1 & SC1_CNT_POLYMORPH) MSG_WriteByte (msg, ent->v.cnt_polymorph); if (sc1 & SC1_CNT_FLIGHT) MSG_WriteByte (msg, ent->v.cnt_flight); if (sc1 & SC1_CNT_CUBEOFFORCE) MSG_WriteByte (msg, ent->v.cnt_cubeofforce); if (sc1 & SC1_CNT_INVINCIBILITY) MSG_WriteByte (msg, ent->v.cnt_invincibility); if (sc1 & SC1_ARTIFACT_ACTIVE) MSG_WriteByte (msg, ent->v.artifact_active); if (sc1 & SC1_ARTIFACT_LOW) MSG_WriteByte (msg, ent->v.artifact_low); if (sc1 & SC1_MOVETYPE) MSG_WriteByte (msg, ent->v.movetype); if (sc1 & SC1_CAMERAMODE) MSG_WriteByte (msg, ent->v.cameramode); if (sc1 & SC1_HASTED) MSG_WriteFloat (msg, ent->v.hasted); if (sc1 & SC1_INVENTORY) MSG_WriteByte (msg, ent->v.inventory); if (sc1 & SC1_RINGS_ACTIVE) MSG_WriteByte (msg, ent->v.rings_active); if (sc2 & SC2_RINGS_LOW) MSG_WriteByte (msg, ent->v.rings_low); if (sc2 & SC2_AMULET) MSG_WriteByte(msg, ent->v.armor_amulet); if (sc2 & SC2_BRACER) MSG_WriteByte(msg, ent->v.armor_bracer); if (sc2 & SC2_BREASTPLATE) MSG_WriteByte(msg, ent->v.armor_breastplate); if (sc2 & SC2_HELMET) MSG_WriteByte(msg, ent->v.armor_helmet); if (sc2 & SC2_FLIGHT_T) MSG_WriteByte(msg, ent->v.ring_flight); if (sc2 & SC2_WATER_T) MSG_WriteByte(msg, ent->v.ring_water); if (sc2 & SC2_TURNING_T) MSG_WriteByte(msg, ent->v.ring_turning); if (sc2 & SC2_REGEN_T) MSG_WriteByte(msg, ent->v.ring_regeneration); // if (sc2 & SC2_HASTE_T) // MSG_WriteFloat(msg, ent->v.haste_time); // if (sc2 & SC2_TOME_T) // MSG_WriteFloat(msg, ent->v.tome_time); if (sc2 & SC2_PUZZLE1) MSG_WriteString(msg, PR_GetString(ent->v.puzzle_inv1)); if (sc2 & SC2_PUZZLE2) MSG_WriteString(msg, PR_GetString(ent->v.puzzle_inv2)); if (sc2 & SC2_PUZZLE3) MSG_WriteString(msg, PR_GetString(ent->v.puzzle_inv3)); if (sc2 & SC2_PUZZLE4) MSG_WriteString(msg, PR_GetString(ent->v.puzzle_inv4)); if (sc2 & SC2_PUZZLE5) MSG_WriteString(msg, PR_GetString(ent->v.puzzle_inv5)); if (sc2 & SC2_PUZZLE6) MSG_WriteString(msg, PR_GetString(ent->v.puzzle_inv6)); if (sc2 & SC2_PUZZLE7) MSG_WriteString(msg, PR_GetString(ent->v.puzzle_inv7)); if (sc2 & SC2_PUZZLE8) MSG_WriteString(msg, PR_GetString(ent->v.puzzle_inv8)); if (sc2 & SC2_MAXHEALTH) MSG_WriteShort(msg, ent->v.max_health); if (sc2 & SC2_MAXMANA) MSG_WriteByte(msg, ent->v.max_mana); if (sc2 & SC2_FLAGS) MSG_WriteFloat(msg, ent->v.flags); end: memcpy (&host_cl->old_v, &ent->v, sizeof(host_cl->old_v)); } #ifdef MGNET /* ============= float cardioid_rating (edict_t *targ , edict_t *self) Determines how important a visclient is- based on offset from forward angle and distance. Resultant pattern is a somewhat extended 3-dimensional cleaved cardioid with each point on the surface being equal in priority(0) and increasing linearly towards equal priority(1) along a straight line to the center. ============= */ static float cardioid_rating (edict_t *targ , edict_t *self) { vec3_t vec, spot1, spot2; vec3_t forward, right, up; float dot, dist; AngleVectors (self->v.v_angle,forward,right,up); VectorAdd(self->v.origin,self->v.view_ofs,spot1); VectorSubtract(targ->v.absmax,targ->v.absmin,spot2); VectorMA(targ->v.absmin,0.5,spot2,spot2); VectorSubtract(spot2,spot1,vec); dist = VectorNormalize(vec); dot = DotProduct(vec,forward); //from 1 to -1 if (dot < -0.3) //see only from -125 to 125 degrees return false; if (dot > 0) //to front of perpendicular plane to forward dot *= 31;//much more distance leniency in front, max dist = 2048 directly in front dot = (dot + 1) * 64;//64 = base distance if along the perpendicular plane, max is 2048 straight ahead if (dist >= dot)//too far away for that angle to be important return false; //from 0.000000? to almost 1 return 1 - (dist/dot);//The higher this number is, the more important it is to send this ent } #define MAX_VISCLIENTS 2 /* ============= SV_WritePlayersToClient ============= */ static void SV_WritePlayersToClient (client_t *client, edict_t *clent, byte *pvs, sizebuf_t *msg) { int i, j; client_t *cl; edict_t *ent; int msec; usercmd_t cmd; int pflags; int invis_level; qboolean playermodel = false; // vars for the cardioid_rating/MGNET code int k, l; int visclient[MAX_CLIENTS]; int forcevisclient[MAX_CLIENTS]; int cl_v_priority[MAX_CLIENTS]; int cl_v_psort[MAX_CLIENTS]; int numvc, forcevc, totalvc, num_eliminated; for (j = 0, cl = svs.clients, numvc = 0, forcevc = 0; j < MAX_CLIENTS; j++, cl++) { if (cl->state != cs_spawned) continue; ent = cl->edict; // ZOID visibility tracking invis_level = false; if (ent != clent && !(client->spec_track && client->spec_track - 1 == j)) { if ((int)ent->v.effects & EF_NODRAW) { if (dmMode.integer == DM_SIEGE && SV_PROGS_HAVE_SIEGE && clent->v.playerclass == CLASS_DWARF) invis_level = false; else invis_level = true; //still can hear } //could be invisiblenow and still sent, cull out by other methods as well if (cl->spectator) { invis_level = 2; //no vis or weaponsound } else { // ignore if not touching a PV leaf for (i = 0; i < ent->num_leafs; i++) { if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i] & 7)) ) break; } if (i == ent->num_leafs) invis_level = 2; //no vis or weaponsound } } if (invis_level == true) { //ok to send weaponsound if (ent->v.wpn_sound) { MSG_WriteByte (msg, svc_player_sound); MSG_WriteByte (msg, j); for (i = 0; i < 3; i++) MSG_WriteCoord (msg, ent->v.origin[i]); MSG_WriteShort (msg, ent->v.wpn_sound); } } if (invis_level > 0) continue; if (!cl->skipsend && ent != clent) { //don't count self visclient[numvc]=j; numvc++; } else { //Self, or Wasn't sent last time, must send this frame cl->skipsend = false; forcevisclient[forcevc] = j; forcevc++; continue; } } totalvc = numvc + forcevc; if (totalvc > MAX_VISCLIENTS) { // You have more than 5 clients in your view, cull some out // prioritize by // line of sight (20%) // distance (50%) // dot off v_forward (30%) // put this in "priority" then sort by priority // and send the highest priority // number of highest priority sent depends on how // many are forced through because they were skipped // last send. Ideally, no more than 5 are sent. for (j = 0; j < numvc && totalvc > MAX_VISCLIENTS; j++) { //priority 1 - if behind, cull out for (k = 0, cl = svs.clients; k < visclient[j]; k++, cl++); // cl = svs.clients + visclient[j]; ent = cl->edict; cl_v_priority[j] = cardioid_rating(ent, clent); if (!cl_v_priority[j]) { //% they won't be sent, l represents how many // were forced through cl->skipsend = true; totalvc--; } } if (totalvc > MAX_VISCLIENTS) { //still more than 5 inside cardioid, sort by priority //and drop those after 5 //CHECK this make sure it works for (i = 0; i < numvc; i++) { //do this as many times as there are visclients for (j = 0; j < numvc-1-i; j++) { //go through the list if (cl_v_priority[j] < cl_v_priority[j+1]) { //store lower one k = cl_v_psort[j]; //put next one in it's spot cl_v_psort[j] = cl_v_psort[j+1]; //put lower one next cl_v_psort[j+1] = k; } } } num_eliminated = 0; while (totalvc > MAX_VISCLIENTS) { //eliminate all over 5 unless not sent last time if (!cl->skipsend) { cl = svs.clients + cl_v_psort[numvc - num_eliminated]; cl->skipsend = true; num_eliminated++; totalvc--; } } } // Alternate Possibilities: ...? // priority 2 - if too many numleafs away, cull out // priority 3 - don't send those farthest away, flag for re-send next time // priority 4 - flat percentage based on how many over 5 /* if (rand() % 10 < (numvc + l - 5)) {//% they won't be sent, l represents how many were forced through cl->skipsend = true; numvc--; } */ // priority 5 - send less info on clients } for (j = 0, l = 0, k = 0, cl = svs.clients; j < MAX_CLIENTS; j++, cl++) { //priority 1 - if behind, cull out if (forcevisclient[l] == j && l <= forcevc) l++; else if (visclient[k] == j && k <= numvc) k++; //clent is always forced else continue;//not in PVS or forced if (cl->skipsend) { //still 2 bytes, but what ya gonna do? MSG_WriteByte (msg, svc_playerskipped); MSG_WriteByte (msg, j); continue; } ent = cl->edict; pflags = PF_MSEC | PF_COMMAND; if (ent->v.modelindex != sv_playermodel[0] &&//paladin ent->v.modelindex != sv_playermodel[1] &&//crusader ent->v.modelindex != sv_playermodel[2] &&//necro ent->v.modelindex != sv_playermodel[3] &&//assassin ent->v.modelindex != sv_playermodel[4] &&//succ ent->v.modelindex != sv_playermodel[5])//dwarf pflags |= PF_MODEL; else playermodel = true; for (i = 0; i < 3; i++) { if (ent->v.velocity[i]) pflags |= PF_VELOCITY1<v.effects & 0xff)) pflags |= PF_EFFECTS; if (((long)ent->v.effects & 0xff00)) pflags |= PF_EFFECTS2; if (ent->v.skin) { if (dmMode.integer == DM_SIEGE && SV_PROGS_HAVE_SIEGE && playermodel && ent->v.skin == 1); // in siege, don't send skin if 2nd skin and using // playermodel, it will know on other side- saves // us 1 byte per client per frame! else pflags |= PF_SKINNUM; } if (ent->v.health <= 0) pflags |= PF_DEAD; if (ent->v.hull == HULL_CROUCH) pflags |= PF_CROUCH; if (cl->spectator) { // only sent origin and velocity to spectators pflags &= PF_VELOCITY1 | PF_VELOCITY2 | PF_VELOCITY3; } else if (ent == clent) { // don't send a lot of data on personal entity pflags &= ~(PF_MSEC|PF_COMMAND); if (ent->v.weaponframe) pflags |= PF_WEAPONFRAME; } if (ent->v.drawflags) { pflags |= PF_DRAWFLAGS; } if (ent->v.scale != 0 && ent->v.scale != 1.0) { pflags |= PF_SCALE; } if (ent->v.abslight != 0) { pflags |= PF_ABSLIGHT; } if (ent->v.wpn_sound) { pflags |= PF_SOUND; } MSG_WriteByte (msg, svc_playerinfo); MSG_WriteByte (msg, j); MSG_WriteShort (msg, pflags); for (i = 0; i < 3; i++) MSG_WriteCoord (msg, ent->v.origin[i]); MSG_WriteByte (msg, ent->v.frame); if (pflags & PF_MSEC) { msec = 1000*(sv.time - cl->localtime); if (msec > 255) msec = 255; MSG_WriteByte (msg, msec); } if (pflags & PF_COMMAND) { cmd = cl->lastcmd; if (ent->v.health <= 0) { // don't show the corpse looking around... cmd.angles[0] = 0; cmd.angles[1] = ent->v.angles[1]; cmd.angles[0] = 0; } cmd.buttons = 0; // never send buttons cmd.impulse = 0; // never send impulses MSG_WriteUsercmd (msg, &cmd, false); } for (i = 0; i < 3; i++) { if (pflags & (PF_VELOCITY1<v.velocity[i]); } // rjr if (pflags & PF_MODEL) MSG_WriteShort (msg, ent->v.modelindex); if (pflags & PF_SKINNUM) MSG_WriteByte (msg, ent->v.skin); if (pflags & PF_EFFECTS) MSG_WriteByte (msg, ((long)ent->v.effects & 0xff)); if (pflags & PF_EFFECTS2) MSG_WriteByte(msg, ((long)ent->v.effects & 0xff00) >> 8); if (pflags & PF_WEAPONFRAME) MSG_WriteByte (msg, ent->v.weaponframe); if (pflags & PF_DRAWFLAGS) { MSG_WriteByte (msg, ent->v.drawflags); } if (pflags & PF_SCALE) { MSG_WriteByte (msg, (int)(ent->v.scale * 100.0) & 255); } if (pflags & PF_ABSLIGHT) { MSG_WriteByte (msg, (int)(ent->v.abslight * 100.0) & 255); } if (pflags & PF_SOUND) { MSG_WriteShort (msg, ent->v.wpn_sound); } } } /* End of MGNET code */ #else /* ============= SV_WritePlayersToClient ============= */ static void SV_WritePlayersToClient (client_t *client, edict_t *clent, byte *pvs, sizebuf_t *msg) { int i, j; client_t *cl; edict_t *ent; int msec; usercmd_t cmd; int pflags; int invis_level; qboolean playermodel = false; for (j = 0, cl = svs.clients; j < MAX_CLIENTS; j++, cl++) { if (cl->state != cs_spawned) continue; ent = cl->edict; // ZOID visibility tracking invis_level = false; if (ent != clent && !(client->spec_track && client->spec_track - 1 == j)) { if ((int)ent->v.effects & EF_NODRAW) { if (dmMode.integer == DM_SIEGE && SV_PROGS_HAVE_SIEGE && clent->v.playerclass == CLASS_DWARF) invis_level = false; else invis_level = true; //still can hear } else if (cl->spectator) { invis_level = 2; //no vis or weaponsound } else { // ignore if not touching a PV leaf for (i = 0; i < ent->num_leafs; i++) { if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i] & 7)) ) break; } if (i == ent->num_leafs) invis_level = 2; //no vis or weaponsound } } if (invis_level == true) { //ok to send weaponsound if (ent->v.wpn_sound) { MSG_WriteByte (msg, svc_player_sound); MSG_WriteByte (msg, j); for (i = 0; i < 3; i++) MSG_WriteCoord (msg, ent->v.origin[i]); MSG_WriteShort (msg, ent->v.wpn_sound); } } if (invis_level > 0) continue; pflags = PF_MSEC | PF_COMMAND; if (ent->v.modelindex != sv_playermodel[0] &&//paladin ent->v.modelindex != sv_playermodel[1] &&//crusader ent->v.modelindex != sv_playermodel[2] &&//necro ent->v.modelindex != sv_playermodel[3] &&//assassin ent->v.modelindex != sv_playermodel[4] &&//succ ent->v.modelindex != sv_playermodel[5])//dwarf pflags |= PF_MODEL; else playermodel = true; for (i = 0; i < 3; i++) { if (ent->v.velocity[i]) pflags |= PF_VELOCITY1<v.effects & 0xff)) pflags |= PF_EFFECTS; if (((long)ent->v.effects & 0xff00)) pflags |= PF_EFFECTS2; if (ent->v.skin) { if (dmMode.integer == DM_SIEGE && SV_PROGS_HAVE_SIEGE && playermodel && ent->v.skin == 1); // in siege, don't send skin if 2nd skin and using // playermodel, it will know on other side- saves // us 1 byte per client per frame! else pflags |= PF_SKINNUM; } if (ent->v.health <= 0) pflags |= PF_DEAD; if (ent->v.hull == HULL_CROUCH) pflags |= PF_CROUCH; if (cl->spectator) { // only sent origin and velocity to spectators pflags &= PF_VELOCITY1 | PF_VELOCITY2 | PF_VELOCITY3; } else if (ent == clent) { // don't send a lot of data on personal entity pflags &= ~(PF_MSEC|PF_COMMAND); if (ent->v.weaponframe) pflags |= PF_WEAPONFRAME; } if (ent->v.drawflags) { pflags |= PF_DRAWFLAGS; } if (ent->v.scale != 0 && ent->v.scale != 1.0) { pflags |= PF_SCALE; } if (ent->v.abslight != 0) { pflags |= PF_ABSLIGHT; } if (ent->v.wpn_sound) { pflags |= PF_SOUND; } MSG_WriteByte (msg, svc_playerinfo); MSG_WriteByte (msg, j); MSG_WriteShort (msg, pflags); for (i = 0; i < 3; i++) MSG_WriteCoord (msg, ent->v.origin[i]); MSG_WriteByte (msg, ent->v.frame); if (pflags & PF_MSEC) { msec = 1000*(sv.time - cl->localtime); if (msec > 255) msec = 255; MSG_WriteByte (msg, msec); } if (pflags & PF_COMMAND) { cmd = cl->lastcmd; if (ent->v.health <= 0) { // don't show the corpse looking around... cmd.angles[0] = 0; cmd.angles[1] = ent->v.angles[1]; cmd.angles[0] = 0; } cmd.buttons = 0; // never send buttons cmd.impulse = 0; // never send impulses MSG_WriteUsercmd (msg, &cmd, false); } for (i = 0; i < 3; i++) { if (pflags & (PF_VELOCITY1<v.velocity[i]); } // rjr if (pflags & PF_MODEL) MSG_WriteShort (msg, ent->v.modelindex); if (pflags & PF_SKINNUM) MSG_WriteByte (msg, ent->v.skin); if (pflags & PF_EFFECTS) MSG_WriteByte (msg, ((long)ent->v.effects & 0xff)); if (pflags & PF_EFFECTS2) MSG_WriteByte(msg, ((long)ent->v.effects & 0xff00) >> 8); if (pflags & PF_WEAPONFRAME) MSG_WriteByte (msg, ent->v.weaponframe); if (pflags & PF_DRAWFLAGS) { MSG_WriteByte (msg, ent->v.drawflags); } if (pflags & PF_SCALE) { MSG_WriteByte (msg, (int)(ent->v.scale * 100.0) & 255); } if (pflags & PF_ABSLIGHT) { MSG_WriteByte (msg, (int)(ent->v.abslight * 100.0) & 255); } if (pflags & PF_SOUND) { MSG_WriteShort (msg, ent->v.wpn_sound); } } } #endif /* ============= SV_WriteEntitiesToClient Encodes the current state of the world as a svc_packetentities messages and possibly a svc_nails message and svc_playerinfo messages ============= */ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg) { int e, i; byte *pvs; vec3_t org; edict_t *ent; packet_entities_t *pack; edict_t *clent; client_frame_t *frame; entity_state_t *state; // this is the frame we are creating frame = &client->frames[client->netchan.incoming_sequence & UPDATE_MASK]; // find the client's PVS clent = client->edict; VectorAdd (clent->v.origin, clent->v.view_ofs, org); pvs = SV_FatPVS (org); // send over the players in the PVS SV_WritePlayersToClient (client, clent, pvs, msg); // put other visible entities into either a packet_entities or a nails message pack = &frame->entities; pack->num_entities = 0; // numnails = 0; nummissiles = 0; numravens = 0; numraven2s = 0; for (e = MAX_CLIENTS+1, ent = EDICT_NUM(e); e < sv.num_edicts; e++, ent = NEXT_EDICT(ent)) { // ignore ents without visible models if (!ent->v.modelindex || !*PR_GetString(ent->v.model)) continue; if ((int)ent->v.effects & EF_NODRAW) { continue; } // ignore if not touching a PV leaf for (i = 0; i < ent->num_leafs; i++) { if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i] & 7)) ) break; } if (i == ent->num_leafs) continue; // not visible // if (SV_AddNailUpdate (ent)) // continue; if (SV_AddMissileUpdate (ent)) continue; // added to the special update list // add to the packetentities if (pack->num_entities == MAX_PACKET_ENTITIES) continue; // all full state = &pack->entities[pack->num_entities]; pack->num_entities++; state->number = e; state->flags = 0; VectorCopy (ent->v.origin, state->origin); VectorCopy (ent->v.angles, state->angles); state->modelindex = ent->v.modelindex; state->frame = ent->v.frame; state->colormap = ent->v.colormap; state->skinnum = ent->v.skin; state->effects = ent->v.effects; state->scale = (int)(ent->v.scale * 100.0) & 255; state->drawflags = ent->v.drawflags; state->abslight = (int)(ent->v.abslight * 255.0) & 255; //clear sound so it doesn't send twice state->wpn_sound = ent->v.wpn_sound; } // encode the packet entities as a delta from the // last packetentities acknowledged by the client SV_EmitPacketEntities (client, pack, msg); // now add the specialized nail update // SV_EmitNailUpdate (msg); SV_EmitPackedEntities (msg); } engine/hexenworld/server/sv_init.c000066400000000000000000000273571444734033100176500ustar00rootroot00000000000000/* * sv_init.c -- server spawning * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" server_static_t svs; // persistant server info server_t sv; // local server char localmodels[MAX_MODELS][8]; // inline model names for precache char localinfo[MAX_LOCALINFO_STRING+1]; // local game info /* ================ SV_ModelIndex ================ */ int SV_ModelIndex (const char *name) { int i; if (!name || !name[0]) return 0; for (i = 0; i < MAX_MODELS && sv.model_precache[i]; i++) { if (!strcmp(sv.model_precache[i], name)) return i; } if (i == MAX_MODELS || !sv.model_precache[i]) SV_Error ("%s: model %s not precached", __thisfunc__, name); return i; } /* ================ SV_FlushSignon Moves to the next signon buffer if needed ================ */ void SV_FlushSignon (void) { if (sv.signon.cursize < sv.signon.maxsize - 100) return; if (sv.num_signon_buffers == MAX_SIGNON_BUFFERS-1) SV_Error ("sv.num_signon_buffers == MAX_SIGNON_BUFFERS-1"); sv.signon_buffer_size[sv.num_signon_buffers-1] = sv.signon.cursize; sv.signon.data = sv.signon_buffers[sv.num_signon_buffers]; sv.num_signon_buffers++; sv.signon.cursize = 0; } /* ================ SV_CreateBaseline Entity baselines are used to compress the update messages to the clients -- only the fields that differ from the baseline will be transmitted ================ */ static void SV_CreateBaseline (void) { int i; edict_t *svent; int entnum; for (entnum = 0; entnum < sv.num_edicts ; entnum++) { svent = EDICT_NUM(entnum); if (svent->free) continue; // create baselines for all player slots, // and any other edict that has a visible model if (entnum > MAX_CLIENTS && !svent->v.modelindex) continue; // // create entity baseline // VectorCopy (svent->v.origin, svent->baseline.origin); VectorCopy (svent->v.angles, svent->baseline.angles); svent->baseline.frame = svent->v.frame; svent->baseline.skinnum = svent->v.skin; if (entnum > 0 && entnum <= MAX_CLIENTS) { svent->baseline.colormap = entnum; svent->baseline.modelindex = SV_ModelIndex("models/paladin.mdl"); } else { svent->baseline.colormap = 0; svent->baseline.modelindex = SV_ModelIndex(PR_GetString(svent->v.model)); } svent->baseline.scale = (int)(svent->v.scale*100.0)&255; svent->baseline.drawflags = svent->v.drawflags; svent->baseline.abslight = (int)(svent->v.abslight*255.0)&255; // // flush the signon message out to a seperate buffer if // nearly full // SV_FlushSignon (); // // add to the message // MSG_WriteByte (&sv.signon,svc_spawnbaseline); MSG_WriteShort (&sv.signon,entnum); MSG_WriteShort (&sv.signon, svent->baseline.modelindex); MSG_WriteByte (&sv.signon, svent->baseline.frame); MSG_WriteByte (&sv.signon, svent->baseline.colormap); MSG_WriteByte (&sv.signon, svent->baseline.skinnum); MSG_WriteByte (&sv.signon, svent->baseline.scale); MSG_WriteByte (&sv.signon, svent->baseline.drawflags); MSG_WriteByte (&sv.signon, svent->baseline.abslight); for (i = 0; i < 3; i++) { MSG_WriteCoord(&sv.signon, svent->baseline.origin[i]); MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]); } } } /* ================ SV_GetLevelname Return the full levelname ================ */ const char *SV_GetLevelname (void) { int idx = (int)sv.edicts->v.message; if (idx > 0 && idx <= host_string_count) return Host_GetString(idx - 1); /* return "";*/ /* Use netname on map if there is one, so they don't have to edit strings.txt */ return PR_GetString(sv.edicts->v.netname); } /* ================ SV_SaveSpawnparms Grabs the current state of the progs serverinfo flags and each client for saving across the transition to another level ================ */ void SV_SaveSpawnparms (void) { int i, j; if (!sv.state) return; // no progs loaded yet // serverflags is the only game related thing maintained svs.serverflags = *sv_globals.serverflags; for (i = 0, host_client = svs.clients; i < MAX_CLIENTS; i++, host_client++) { if (host_client->state != cs_spawned) continue; // needs to reconnect host_client->state = cs_connected; // call the progs to get default spawn parms for the new client *sv_globals.self = EDICT_TO_PROG(host_client->edict); PR_ExecuteProgram (*sv_globals.SetChangeParms); for (j = 0; j < NUM_SPAWN_PARMS; j++) host_client->spawn_parms[j] = sv_globals.parm[j]; } } /* ================ SV_CalcPHS Expands the PVS and calculates the PHS (Potentially Hearable Set) ================ */ static void SV_CalcPHS (void) { int rowbytes, rowwords; int i, j, k, l, idx, num; int bitbyte; unsigned int *dest, *src; byte *scan; int count, vcount; Con_Printf ("Building PHS...\n"); num = sv.worldmodel->numleafs; rowwords = (num+31)>>5; rowbytes = rowwords*4; sv.pvs = (byte *) Hunk_AllocName (rowbytes*num, "pvs"); scan = sv.pvs; vcount = 0; for (i = 0; i < num; i++, scan += rowbytes) { memcpy (scan, Mod_LeafPVS(sv.worldmodel->leafs+i, sv.worldmodel), rowbytes); if (i == 0) continue; for (j = 0; j < num; j++) { if ( scan[j>>3] & (1<<(j&7)) ) { vcount++; } } } sv.phs = (byte *) Hunk_AllocName (rowbytes*num, "phs"); count = 0; scan = sv.pvs; dest = (unsigned int *)sv.phs; for (i = 0; i < num; i++, dest += rowwords, scan += rowbytes) { memcpy (dest, scan, rowbytes); for (j = 0; j < rowbytes; j++) { bitbyte = scan[j]; if (!bitbyte) continue; for (k = 0; k < 8; k++) { if (! (bitbyte & (1<= num) continue; src = (unsigned int *)sv.pvs + idx*rowwords; for (l = 0; l < rowwords; l++) dest[l] |= src[l]; } } if (i == 0) continue; for (j = 0; j < num; j++) { if ( ((byte *)dest)[j>>3] & (1<<(j&7)) ) count++; } } Con_Printf ("Average leafs visible / hearable / total: %i / %i / %i\n", vcount/num, count/num, num); } /* ================ SV_SpawnServer Change the server to a new map, taking all connected clients along with it. This is only called from the SV_Map_f() function. ================ */ void SV_SpawnServer (const char *server, const char *startspot) { static char dummy[8] = { 0,0,0,0,0,0,0,0 }; edict_t *ent; int i; Con_DPrintf ("%s: %s\n", __thisfunc__, server); SV_SaveSpawnparms (); svs.spawncount++; // any partially connected client will be // restarted sv.state = ss_dead; Mod_ClearAll (); Hunk_FreeToLowMark (host_hunklevel); // wipe the entire per-level structure memset (&sv, 0, sizeof(sv)); SZ_Init (&sv.datagram, sv.datagram_buf, sizeof(sv.datagram_buf)); sv.datagram.allowoverflow = true; SZ_Init (&sv.reliable_datagram, sv.reliable_datagram_buf, sizeof(sv.reliable_datagram_buf)); SZ_Init (&sv.multicast, sv.multicast_buf, sizeof(sv.multicast_buf)); SZ_Init (&sv.master, sv.master_buf, sizeof(sv.master_buf)); SZ_Init (&sv.signon, sv.signon_buffers[0], sizeof(sv.signon_buffers[0])); sv.num_signon_buffers = 1; q_strlcpy (sv.name, server, sizeof(sv.name)); if (startspot) q_strlcpy(sv.startspot, startspot, sizeof(sv.startspot)); // load progs to get entity field count // which determines how big each edict is PR_LoadProgs (); Host_LoadStrings(); // allocate edicts sv.edicts = (edict_t *) Hunk_AllocName (MAX_EDICTS*pr_edict_size, "edicts"); // leave slots at start for clients only sv.num_edicts = MAX_CLIENTS + 1 + max_temp_edicts.integer; for (i = 0; i < MAX_CLIENTS; i++) { ent = EDICT_NUM(i+1); svs.clients[i].edict = ent; //ZOID - make sure we update frags right svs.clients[i].old_frags = 0; } sv.time = 1.0; q_strlcpy (sv.name, server, sizeof(sv.name)); q_snprintf (sv.modelname, sizeof(sv.modelname), "maps/%s.bsp", server); sv.worldmodel = Mod_ForName (sv.modelname, true); SV_CalcPHS (); // // clear physics interaction links // SV_ClearWorld (); sv.sound_precache[0] = dummy; sv.model_precache[0] = dummy; sv.model_precache[1] = sv.modelname; sv.models[1] = sv.worldmodel; for (i = 1; i < sv.worldmodel->numsubmodels; i++) { sv.model_precache[1+i] = localmodels[i]; sv.models[i+1] = Mod_ForName (localmodels[i], false); } // // spawn the rest of the entities on the map // // precache and static commands can be issued during // map initialization sv.state = ss_loading; ent = EDICT_NUM(0); ent->free = false; ent->v.model = PR_SetEngineString(sv.worldmodel->name); ent->v.modelindex = 1; // world model ent->v.solid = SOLID_BSP; ent->v.movetype = MOVETYPE_PUSH; if (coop.integer) Cvar_Set ("deathmatch", "0"); *sv_globals.coop = coop.value; *sv_globals.deathmatch = deathmatch.value; *sv_globals.randomclass = randomclass.value; *sv_globals.damageScale = damageScale.value; *sv_globals.shyRespawn = shyRespawn.value; if (sv_globals.spartanPrint) /* need v0.14 or newer */ *sv_globals.spartanPrint = spartanPrint.value; *sv_globals.meleeDamScale = meleeDamScale.value; *sv_globals.manaScale = manaScale.value; *sv_globals.tomeMode = tomeMode.value; *sv_globals.tomeRespawn = tomeRespawn.value; *sv_globals.w2Respawn = w2Respawn.value; *sv_globals.altRespawn = altRespawn.value; *sv_globals.fixedLevel = fixedLevel.value; *sv_globals.autoItems = autoItems.value; *sv_globals.dmMode = dmMode.value; *sv_globals.easyFourth = easyFourth.value; *sv_globals.patternRunner = patternRunner.value; if (sv_globals.max_players) /* need v0.14 or newer */ *sv_globals.max_players = maxclients.value; *sv_globals.startspot = PR_SetEngineString(sv.startspot); sv.current_skill = skill.integer; if (sv.current_skill < 0) sv.current_skill = 0; if (sv.current_skill > 3) sv.current_skill = 3; Cvar_SetValue ("skill", sv.current_skill); *sv_globals.mapname = PR_SetEngineString(sv.name); // serverflags are for cross level information (sigils) *sv_globals.serverflags = svs.serverflags; // run the frame start qc function to let progs check cvars SV_ProgStartFrame (); // load and spawn all other entities ED_LoadFromFile (sv.worldmodel->entities); // look up some model indexes for specialized message compression SV_FindModelNumbers (); // all spawning is completed, any further precache statements // or prog writes to the signon message are errors sv.state = ss_active; // run two frames to allow everything to settle host_frametime = HX_FRAME_TIME; // increment realtime merely by a fraction, otherwise the two // SV_Physics() calls actually return immediately without doing // anything and that used to result in the long- and well-known // server crash for the romeric5 map. -- Thomas. // FIXME: revisit the dozens of time variables some day. -- O.S. realtime += HX_FRAME_TIME; SV_Physics (); realtime += HX_FRAME_TIME; SV_Physics (); // save movement vars SV_SetMoveVars(); // create a baseline for more efficient communications SV_CreateBaseline (); sv.signon_buffer_size[sv.num_signon_buffers-1] = sv.signon.cursize; Info_SetValueForKey (svs.info, "map", sv.name, MAX_SERVERINFO_STRING); Con_DPrintf ("Server spawned.\n"); svs.changelevel_issued = false; // now safe to issue another } engine/hexenworld/server/sv_main.c000066400000000000000000001146061444734033100176230ustar00rootroot00000000000000/* * sv_main.c -- hexenworld server main program * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "huffman.h" static int sv_protocol = 0; quakeparms_t *host_parms; qboolean host_initialized; // true if into command execution (compatability) double host_frametime; double realtime; // without any filtering or bounding int host_hunklevel; netadr_t master_adr[MAX_MASTERS]; // address of group servers client_t *host_client; // current client cvar_t sv_mintic = {"sv_mintic", "0.03", CVAR_NONE}; // bound the size of the cvar_t sv_maxtic = {"sv_maxtic", "0.1", CVAR_NONE}; // physics time tic cvar_t developer = {"developer", "0", CVAR_NONE}; // show extra messages static cvar_t timeout = {"timeout", "65", CVAR_NONE}; // seconds without any message static cvar_t zombietime = {"zombietime", "2", CVAR_NONE}; // seconds to sink messages // after disconnect static cvar_t rcon_password = {"rcon_password", "", CVAR_NONE}; // password for remote server commands static cvar_t password = {"password", "", CVAR_NONE}; // password for entering the game static cvar_t spectator_password = {"spectator_password", "", CVAR_NONE}; // password for entering as a sepctator cvar_t sv_highchars = {"sv_highchars", "1", CVAR_NONE}; cvar_t sv_phs = {"sv_phs", "1", CVAR_NONE}; cvar_t sv_namedistance = {"sv_namedistance", "600", CVAR_NONE}; cvar_t allow_download = {"allow_download", "1", CVAR_NONE}; cvar_t allow_download_skins = {"allow_download_skins", "1", CVAR_NONE}; cvar_t allow_download_models = {"allow_download_models", "1", CVAR_NONE}; cvar_t allow_download_sounds = {"allow_download_sounds", "1", CVAR_NONE}; cvar_t allow_download_maps = {"allow_download_maps", "1", CVAR_NONE}; // // game rules mirrored in svs.info // cvar_t fraglimit = {"fraglimit", "0", CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t timelimit = {"timelimit", "0", CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t teamplay = {"teamplay", "0", CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t samelevel = {"samelevel", "0", CVAR_NOTIFY|CVAR_SERVERINFO}; cvar_t maxclients = {"maxclients","8", CVAR_SERVERINFO}; cvar_t maxspectators = {"maxspectators","8", CVAR_SERVERINFO}; cvar_t skill = {"skill","1", CVAR_NONE}; // 0 - 3 cvar_t coop = {"coop", "0", CVAR_SERVERINFO}; // 0 or 1 cvar_t deathmatch = {"deathmatch", "1", CVAR_SERVERINFO}; // 0, 1, or 2 cvar_t randomclass = {"randomclass", "0", CVAR_SERVERINFO}; cvar_t damageScale = {"damagescale", "1.0", CVAR_SERVERINFO}; cvar_t shyRespawn = {"shyRespawn", "0", CVAR_SERVERINFO}; cvar_t spartanPrint = {"spartanPrint", "1.0", CVAR_SERVERINFO}; cvar_t meleeDamScale = {"meleeDamScale", "0.66666", CVAR_SERVERINFO}; cvar_t manaScale = {"manascale", "1.0", CVAR_SERVERINFO}; cvar_t tomeMode = {"tomemode", "0", CVAR_SERVERINFO}; cvar_t tomeRespawn = {"tomerespawn", "0", CVAR_SERVERINFO}; cvar_t w2Respawn = {"w2respawn", "0", CVAR_SERVERINFO}; cvar_t altRespawn = {"altrespawn", "0", CVAR_SERVERINFO}; cvar_t fixedLevel = {"fixedlevel", "0", CVAR_SERVERINFO}; cvar_t autoItems = {"autoitems", "0", CVAR_SERVERINFO}; cvar_t dmMode = {"dmmode", "0", CVAR_SERVERINFO}; cvar_t easyFourth = {"easyfourth", "0", CVAR_SERVERINFO}; cvar_t patternRunner= {"patternrunner", "0", CVAR_SERVERINFO}; cvar_t spawn = {"spawn", "0", CVAR_SERVERINFO}; cvar_t hostname = {"hostname", "unnamed", CVAR_SERVERINFO}; cvar_t sv_ce_scale = {"sv_ce_scale", "1", CVAR_ARCHIVE}; cvar_t sv_ce_max_size = {"sv_ce_max_size", "0", CVAR_ARCHIVE}; cvar_t noexit = {"noexit", "0", CVAR_NOTIFY|CVAR_SERVERINFO}; FILE *sv_logfile; FILE *sv_fraglogfile; static void Master_Shutdown (void); // // external cvars // extern cvar_t sv_maxvelocity; extern cvar_t sv_gravity; extern cvar_t sv_aim; extern cvar_t sv_stopspeed; extern cvar_t sv_spectatormaxspeed; extern cvar_t sv_accelerate; extern cvar_t sv_airaccelerate; extern cvar_t sv_wateraccelerate; extern cvar_t sv_friction; extern cvar_t sv_waterfriction; //============================================================================ /* ================ SV_Shutdown Quake calls this before calling Sys_Quit or Sys_Error ================ */ void SV_Shutdown (void) { Master_Shutdown (); if (sv_logfile) { fclose (sv_logfile); sv_logfile = NULL; } if (sv_fraglogfile) { fclose (sv_fraglogfile); sv_logfile = NULL; } NET_Shutdown (); } /* ================ SV_Error Sends a datagram to all the clients informing them of the server crash, then exits ================ */ void SV_Error (const char *error, ...) { va_list argptr; static char string[1024]; static qboolean inerror = false; if (inerror) Sys_Error ("%s: recursive error! (%s)", __thisfunc__, string); inerror = true; va_start (argptr, error); q_vsnprintf (string, sizeof(string), error, argptr); va_end (argptr); Con_Printf ("%s: %s\n", __thisfunc__, string); SV_FinalMessage (va("server crashed: %s\n", string)); SV_Shutdown (); Sys_Error ("%s: %s\n", __thisfunc__, string); } /* ================== SV_FinalMessage Used by SV_Error and SV_Quit_f to send a final message to all connected clients before the server goes down. The messages are sent immediately, not just stuck on the outgoing message list, because the server is going to totally exit after returning from this function. ================== */ void SV_FinalMessage (const char *message) { int i; client_t *cl; SZ_Clear (&net_message); MSG_WriteByte (&net_message, svc_print); MSG_WriteByte (&net_message, PRINT_HIGH); MSG_WriteString (&net_message, message); MSG_WriteByte (&net_message, svc_disconnect); for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (cl->state >= cs_spawned) Netchan_Transmit (&cl->netchan, net_message.cursize, net_message.data); } } /* ===================== SV_DropClient Called when the player is totally leaving the server, either willingly or unwillingly. This is NOT called if the entire server is quiting or crashing. ===================== */ void SV_DropClient (client_t *drop) { // add the disconnect MSG_WriteByte (&drop->netchan.message, svc_disconnect); if (drop->state == cs_spawned) { if (!drop->spectator) { // call the prog function for removing a client // this will set the body to a dead frame, among other things *sv_globals.self = EDICT_TO_PROG(drop->edict); PR_ExecuteProgram (*sv_globals.ClientDisconnect); } else if (SpectatorDisconnect) { // call the prog function for removing a client // this will set the body to a dead frame, among other things *sv_globals.self = EDICT_TO_PROG(drop->edict); PR_ExecuteProgram (SpectatorDisconnect); } } else if (dmMode.integer == DM_SIEGE && SV_PROGS_HAVE_SIEGE) { if (*PR_GetString(drop->edict->v.puzzle_inv1) != '\0') { // this guy has a puzzle piece, call this function anyway // to make sure he leaves it behind Con_Printf("Client in unspawned state had puzzle piece, forcing drop\n"); *sv_globals.self = EDICT_TO_PROG(drop->edict); PR_ExecuteProgram (*sv_globals.ClientDisconnect); } } if (drop->spectator) Con_Printf ("Spectator %s removed\n",drop->name); else Con_Printf ("Client %s removed\n",drop->name); if (drop->download) { fclose (drop->download); drop->download = NULL; } drop->state = cs_zombie; // become free in a few seconds drop->connection_started = realtime; // for zombie timeout drop->old_frags = 0; drop->edict->v.frags = 0; drop->name[0] = 0; memset (drop->userinfo, 0, sizeof(drop->userinfo)); // send notification to all remaining clients SV_FullClientUpdate (drop, &sv.reliable_datagram); } //==================================================================== /* =================== SV_CalcPing =================== */ int SV_CalcPing (client_t *cl) { float ping; int i; int count; register client_frame_t *frame; ping = 0; count = 0; for (frame = cl->frames, i = 0; i < UPDATE_BACKUP; i++, frame++) { if (frame->ping_time > 0) { ping += frame->ping_time; count++; } } if (!count) return 9999; ping /= count; return ping*1000; } /* =================== SV_FullClientUpdate Writes all update values to a sizebuf =================== */ unsigned int defLosses; // Defenders losses in Siege unsigned int attLosses; // Attackers Losses in Siege void SV_FullClientUpdate (client_t *client, sizebuf_t *buf) { int i; char info[MAX_INFO_STRING]; // Con_Printf("%s\n", __thisfunc__); i = client - svs.clients; // Sys_Printf("%s: Updated frags for client %d\n", __thisfunc__, i); MSG_WriteByte (buf, svc_updatedminfo); MSG_WriteByte (buf, i); MSG_WriteShort (buf, client->old_frags); MSG_WriteByte (buf, (client->playerclass<<5)|((int)client->edict->v.level&31)); if (dmMode.integer == DM_SIEGE && SV_PROGS_HAVE_SIEGE) { MSG_WriteByte (buf, svc_updatesiegeinfo); MSG_WriteByte (buf, (int)ceil(timelimit.value)); MSG_WriteByte (buf, (int)ceil(fraglimit.value)); MSG_WriteByte (buf, svc_updatesiegeteam); MSG_WriteByte (buf, i); MSG_WriteByte (buf, client->siege_team); MSG_WriteByte (buf, svc_updatesiegelosses); MSG_WriteByte (buf, *sv_globals.defLosses); MSG_WriteByte (buf, *sv_globals.attLosses); MSG_WriteByte (buf, svc_time);//send server time upon connection MSG_WriteFloat (buf, sv.time); } MSG_WriteByte (buf, svc_updateping); MSG_WriteByte (buf, i); MSG_WriteShort (buf, SV_CalcPing (client)); MSG_WriteByte (buf, svc_updateentertime); MSG_WriteByte (buf, i); MSG_WriteFloat (buf, realtime - client->connection_started); strcpy (info, client->userinfo); Info_RemovePrefixedKeys (info, '_'); // server passwords, etc MSG_WriteByte (buf, svc_updateuserinfo); MSG_WriteByte (buf, i); MSG_WriteLong (buf, client->userid); MSG_WriteString (buf, info); } /* ============================================================================== CONNECTIONLESS COMMANDS ============================================================================== */ /* ================ SVC_Status Responds with all the info that qplug or qspy can see This message can be up to around 5k with worst case string lengths. ================ */ static void SVC_Status (void) { int i; client_t *cl; int ping; int top, bottom; Cmd_TokenizeString ("status"); SV_BeginRedirect (RD_PACKET); Con_Printf ("%s\n", svs.info); for (i = 0; i < MAX_CLIENTS; i++) { cl = &svs.clients[i]; if ((cl->state == cs_connected || cl->state == cs_spawned ) && !cl->spectator) { top = atoi(Info_ValueForKey (cl->userinfo, "topcolor")); bottom = atoi(Info_ValueForKey (cl->userinfo, "bottomcolor")); ping = SV_CalcPing (cl); Con_Printf ("%i %i %i %i \"%s\" \"%s\" %i %i\n", cl->userid, cl->old_frags, (int)(realtime - cl->connection_started)/60, ping, cl->name, Info_ValueForKey (cl->userinfo, "skin"), top, bottom); } } SV_EndRedirect (); } /* =================== SV_CheckLog =================== */ #define LOG_HIGHWATER 4096 #define LOG_FLUSH 10*60 static void SV_CheckLog (void) { sizebuf_t *sz; sz = &svs.log[svs.logsequence&1]; // bump sequence if allmost full, or ten minutes have passed and // there is something still sitting there if (sz->cursize > LOG_HIGHWATER || (realtime - svs.logtime > LOG_FLUSH && sz->cursize) ) { // swap buffers and bump sequence svs.logtime = realtime; svs.logsequence++; sz = &svs.log[svs.logsequence&1]; sz->cursize = 0; Con_Printf ("beginning fraglog sequence %i\n", svs.logsequence); } } /* ================ SVC_Log Responds with all the logged frags for ranking programs. If a sequence number is passed as a parameter and it is the same as the current sequence, an A2A_NACK will be returned instead of the data. ================ */ static void SVC_Log (void) { int seq; char data[MAX_DATAGRAM+64]; if (Cmd_Argc() == 2) seq = atoi(Cmd_Argv(1)); else seq = -1; if (seq == svs.logsequence-1 || !sv_fraglogfile) { // they already have this data, or we aren't logging frags data[0] = A2A_NACK; NET_SendPacket (1, data, &net_from); return; } Con_DPrintf ("sending log %i to %s\n", svs.logsequence-1, NET_AdrToString(&net_from)); q_snprintf(data, sizeof(data), "stdlog %i\n", svs.logsequence-1); q_strlcat (data, (char *)svs.log_buf[((svs.logsequence-1)&1)], sizeof(data)); NET_SendPacket (strlen(data)+1, data, &net_from); } /* ================ SVC_Ping Just responds with an acknowledgement ================ */ static void SVC_Ping (void) { char data; data = A2A_ACK; NET_SendPacket (1, &data, &net_from); } /* ================== SVC_DirectConnect A connection request that did not come from the master ================== */ static void SVC_DirectConnect (void) { char userinfo[1024]; static int userid; netadr_t adr; int i; client_t *cl, *newcl; client_t temp; edict_t *ent; int edictnum; const char *s; int clients, spectators; qboolean spectator; q_strlcpy (userinfo, Cmd_Argv(2), sizeof(userinfo)); // check for password or spectator_password s = Info_ValueForKey (userinfo, "spectator"); if (s[0] && strcmp(s, "0")) { if (spectator_password.string[0] && q_strcasecmp(spectator_password.string, "none") && strcmp(spectator_password.string, s) ) { // failed Con_Printf ("%s:spectator password failed\n", NET_AdrToString (&net_from)); Netchan_OutOfBandPrint (&net_from, "%c\nrequires a spectator password\n\n", A2C_PRINT); return; } Info_SetValueForStarKey (userinfo, "*spectator", "1", MAX_INFO_STRING); spectator = true; Info_RemoveKey (userinfo, "spectator"); // remove passwd } else { s = Info_ValueForKey (userinfo, "password"); if (password.string[0] && q_strcasecmp(password.string, "none") && strcmp(password.string, s) ) { Con_Printf ("%s:password failed\n", NET_AdrToString (&net_from)); Netchan_OutOfBandPrint (&net_from, "%c\nserver requires a password\n\n", A2C_PRINT); return; } spectator = false; Info_RemoveKey (userinfo, "password"); // remove passwd } adr = net_from; userid++; // so every client gets a unique id newcl = &temp; memset (newcl, 0, sizeof(client_t)); newcl->userid = userid; newcl->portals = atoi(Cmd_Argv(1)); // works properly if (!sv_highchars.integer) { byte *p, *q; for (p = (byte *)newcl->userinfo, q = (byte *)userinfo; *q && p < (byte *)newcl->userinfo + sizeof(newcl->userinfo)-1; q++) { if (*q > 31 && *q <= 127) *p++ = *q; } } else { q_strlcpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)); } // if there is already a slot for this ip, drop it for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (cl->state == cs_free) continue; if (NET_CompareAdr (&adr, &cl->netchan.remote_address)) { Con_Printf ("%s:reconnect\n", NET_AdrToString (&adr)); SV_DropClient (cl); break; } } // count up the clients and spectators clients = 0; spectators = 0; for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (cl->state == cs_free) continue; if (cl->spectator) spectators++; else clients++; } // if at server limits, refuse connection if (maxclients.integer > MAX_CLIENTS) Cvar_SetValueQuick (&maxclients, MAX_CLIENTS); if (maxspectators.integer > MAX_CLIENTS) Cvar_SetValueQuick (&maxspectators, MAX_CLIENTS); if (maxspectators.integer + maxclients.integer > MAX_CLIENTS) Cvar_SetValueQuick (&maxspectators, MAX_CLIENTS - maxspectators.integer + maxclients.integer); if ( (spectator && spectators >= maxspectators.integer) || (!spectator && clients >= maxclients.integer) ) { Con_Printf ("%s:full connect\n", NET_AdrToString (&adr)); Netchan_OutOfBandPrint (&adr, "%c\nserver is full\n\n", A2C_PRINT); return; } // find a client slot newcl = NULL; for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (cl->state == cs_free) { newcl = cl; break; } } if (!newcl) { Con_Printf ("WARNING: miscounted available clients\n"); return; } // build a new connection // accept the new client // this is the only place a client_t is ever initialized *newcl = temp; if (sv_protocol != 0) newcl->protocol = sv_protocol; else { s = Info_ValueForKey(userinfo, "*cap"); if (strstr(s, "c")) newcl->protocol = PROTOCOL_VERSION_EXT; else newcl->protocol = PROTOCOL_VERSION; } Netchan_OutOfBandPrint (&adr, "%c", S2C_CONNECTION ); edictnum = (newcl-svs.clients)+1; Netchan_Setup (&newcl->netchan, &adr); newcl->state = cs_connected; SZ_Init (&newcl->datagram, newcl->datagram_buf, sizeof(newcl->datagram_buf)); newcl->datagram.allowoverflow = true; // spectator mode can ONLY be set at join time newcl->spectator = spectator; ent = EDICT_NUM(edictnum); newcl->edict = ent; ED_ClearEdict (ent); // parse some info from the info strings SV_ExtractFromUserinfo (newcl); // JACK: Init the floodprot stuff. for (i = 0; i < 10; i++) newcl->whensaid[i] = 0.0; newcl->whensaidhead = 0; newcl->lockedtill = 0; // call the progs to get default spawn parms for the new client PR_ExecuteProgram (*sv_globals.SetNewParms); for (i = 0; i < NUM_SPAWN_PARMS; i++) newcl->spawn_parms[i] = sv_globals.parm[i]; if (newcl->spectator) Con_Printf ("Spectator %s connected\n", newcl->name); else Con_DPrintf ("Client %s connected\n", newcl->name); } static int Rcon_Validate (void) { if (rcon_password.string[0] == '\0') return 0; if (strcmp (Cmd_Argv(1), rcon_password.string) ) return 0; return 1; } /* =============== SVC_RemoteCommand A client issued an rcon command. Shift down the remaining args Redirect all printfs =============== */ static void SVC_RemoteCommand (void) { int i; char remaining[1024]; i = Rcon_Validate (); if (i == 0) { Con_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString(&net_from), net_message.data + 4); SV_BeginRedirect (RD_PACKET); Con_Printf ("Bad rcon_password.\n"); } else { Con_Printf ("Rcon from %s:\n%s\n", NET_AdrToString(&net_from), net_message.data + 4); SV_BeginRedirect (RD_PACKET); remaining[0] = 0; for (i = 2; i < Cmd_Argc(); i++) { q_strlcat (remaining, Cmd_Argv(i), sizeof(remaining)); q_strlcat (remaining, " ", sizeof(remaining)); } Cmd_ExecuteString (remaining, src_command); } SV_EndRedirect (); } /* ================= SV_ConnectionlessPacket A connectionless packet has four leading 0xff characters to distinguish it from a game channel. Clients that are in the game can still send connectionless packets. ================= */ static void SV_ConnectionlessPacket (void) { const char *s; const char *c; MSG_BeginReading (); MSG_ReadLong (); // skip the -1 marker s = MSG_ReadStringLine (); Cmd_TokenizeString (s); c = Cmd_Argv(0); if (!strcmp(c, "ping") || ( c[0] == A2A_PING && (c[1] == 0 || c[1] == '\n')) ) { SVC_Ping (); return; } if (c[0] == A2A_ACK && (c[1] == 0 || c[1] == '\n') ) { Con_Printf ("A2A_ACK from %s\n", NET_AdrToString (&net_from)); return; } else if (c[0] == A2S_ECHO) { NET_SendPacket (net_message.cursize, net_message.data, &net_from); return; } else if (!strcmp(c,"status")) { SVC_Status (); return; } else if (!strcmp(c,"log")) { SVC_Log (); return; } else if (!strcmp(c,"connect")) { SVC_DirectConnect (); return; } else if (!strcmp(c, "rcon")) SVC_RemoteCommand (); else Con_Printf ("bad connectionless packet from %s:\n%s\n", NET_AdrToString (&net_from), s); } /* ============================================================================== PACKET FILTERING You can add or remove addresses from the filter list with: addip removeip The ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire class C network with "addip 192.246.40". Removeip will only remove an address specified exactly the same way. You cannot addip a subnet, then removeip a single host. listip Prints the current list of filters. writeip Dumps "addip " commands to listip.cfg so it can be execed at a later date. The filter lists are not saved and restored by default, because I beleive it would cause too much confusion. filterban <0 or 1> If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game. This is the default setting. If 0, then only addresses matching the list will be allowed. This lets you easily set up a private game, or a game that only allows players from your local network. ============================================================================== */ typedef struct { unsigned int mask; unsigned int compare; } ipfilter_t; #define MAX_IPFILTERS 1024 static ipfilter_t ipfilters[MAX_IPFILTERS]; static int numipfilters; static cvar_t filterban = {"filterban", "1", CVAR_NONE}; /* ================= StringToFilter ================= */ static qboolean StringToFilter (const char *s, ipfilter_t *f) { char num[128]; int i, j; byte b[4]; byte m[4]; for (i = 0; i < 4; i++) { b[i] = 0; m[i] = 0; } for (i = 0; i < 4; i++) { if (*s < '0' || *s > '9') { Con_Printf ("Bad filter address: %s\n", s); return false; } j = 0; while (*s >= '0' && *s <= '9') { num[j++] = *s++; } num[j] = 0; b[i] = atoi(num); if (b[i] != 0) m[i] = 255; if (!*s) break; s++; } memcpy (&f->mask, m, 4); memcpy (&f->compare, b, 4); return true; } /* ================= SV_AddIP_f ================= */ static void SV_AddIP_f (void) { int i; for (i = 0; i < numipfilters; i++) { if (ipfilters[i].compare == 0xffffffff) break; // free spot } if (i == numipfilters) { if (numipfilters == MAX_IPFILTERS) { Con_Printf ("IP filter list is full\n"); return; } numipfilters++; } if (!StringToFilter (Cmd_Argv(1), &ipfilters[i])) ipfilters[i].compare = 0xffffffff; } /* ================= SV_RemoveIP_f ================= */ static void SV_RemoveIP_f (void) { ipfilter_t f; int i, j; if (!StringToFilter (Cmd_Argv(1), &f)) return; for (i = 0; i < numipfilters; i++) { if (ipfilters[i].mask == f.mask && ipfilters[i].compare == f.compare) { for (j = i+1; j < numipfilters; j++) ipfilters[j-1] = ipfilters[j]; numipfilters--; Con_Printf ("Removed.\n"); return; } } Con_Printf ("Didn't find %s.\n", Cmd_Argv(1)); } /* ================= SV_ListIP_f ================= */ static void SV_ListIP_f (void) { int i; byte *b; Con_Printf ("Filter list:\n"); for (i = 0; i < numipfilters; i++) { b = (byte *)&ipfilters[i].compare; Con_Printf ("%3i.%3i.%3i.%3i\n", b[0], b[1], b[2], b[3]); } } /* ================= SV_WriteIP_f ================= */ static void SV_WriteIP_f (void) { FILE *f; const char *name; byte *b; int i; name = FS_MakePath(FS_USERDIR, NULL, "listip.cfg"); Con_Printf ("Writing %s.\n", name); f = fopen (name, "wb"); if (!f) { Con_Printf ("Couldn't open %s\n", name); return; } for (i = 0; i < numipfilters; i++) { b = (byte *)&ipfilters[i].compare; fprintf (f, "addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]); } fclose (f); } /* ================= SV_SendBan ================= */ static void SV_SendBan (void) { static byte data[] = { 0xff, 0xff, 0xff, 0xff, A2C_PRINT, '\n', 'b', 'a', 'n', 'n', 'e', 'd', '.', '\n', '\0' }; NET_SendPacket (15, data, &net_from); } /* ================= SV_FilterPacket ================= */ static qboolean SV_FilterPacket (void) { int i; unsigned int in; memcpy (&in, net_from.ip, 4); for (i = 0; i < numipfilters; i++) { if ( (in & ipfilters[i].mask) == ipfilters[i].compare) return filterban.integer; } return !filterban.integer; } //============================================================================ /* ================= SV_ReadPackets ================= */ static void SV_ReadPackets (void) { int i; client_t *cl; while (NET_GetPacket ()) { if (SV_FilterPacket ()) { SV_SendBan (); // tell them we aren't listening... continue; } // check for connectionless packet (0xffffffff) first if (*(int *)net_message.data == -1) { SV_ConnectionlessPacket (); continue; } // check for packets from connected clients for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (cl->state == cs_free) continue; if (!NET_CompareAdr (&net_from, &cl->netchan.remote_address)) continue; if (Netchan_Process(&cl->netchan)) { // this is a valid, sequenced packet, so process it svs.stats.packets++; cl->send_message = true; // reply at end of frame if (cl->state != cs_zombie) SV_ExecuteClientMessage (cl); } break; } if (i != MAX_CLIENTS) continue; // packet is not from a known client // Con_Printf ("%s:sequenced packet without connection\n", NET_AdrToString(&net_from)); } } /* ================== SV_CheckTimeouts If a packet has not been received from a client in timeout.value seconds, drop the conneciton. When a client is normally dropped, the client_t goes into a zombie state for a few seconds to make sure any final reliable message gets resent if necessary ================== */ static void SV_CheckTimeouts (void) { int i; client_t *cl; float droptime; droptime = realtime - timeout.value; for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if ( (cl->state == cs_connected || cl->state == cs_spawned) && cl->netchan.last_received < droptime) { SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name); SV_DropClient (cl); cl->state = cs_free; // don't bother with zombie state } if (cl->state == cs_zombie && realtime - cl->connection_started > zombietime.value) { cl->state = cs_free; // can now be reused } } } /* =================== SV_GetConsoleCommands Add them exactly as if they had been typed at the console =================== */ static void SV_GetConsoleCommands (void) { const char *cmd; while (1) { cmd = Sys_ConsoleInput (); if (!cmd) break; Cbuf_AddText (cmd); } } /* =================== SV_CheckVars =================== */ static void SV_CheckVars (void) { int v; if (! ((password.flags | spectator_password.flags) & CVAR_CHANGED)) return; password.flags &= ~CVAR_CHANGED; spectator_password.flags &= ~CVAR_CHANGED; v = 0; if (password.string[0] && strcmp(password.string, "none")) v |= 1; if (spectator_password.string[0] && strcmp(spectator_password.string, "none")) v |= 2; Con_Printf ("Updated needpass.\n"); if (!v) Info_SetValueForKey (svs.info, "needpass", "", MAX_SERVERINFO_STRING); else Info_SetValueForKey (svs.info, "needpass", va("%i",v), MAX_SERVERINFO_STRING); } /* ================== SV_Frame ================== */ void SV_Frame (float time) { static double start, end; start = Sys_DoubleTime (); svs.stats.idle += start - end; // keep the random time dependent rand (); // decide the simulation time realtime += time; sv.time += time; // check timeouts SV_CheckTimeouts (); // toggle the log buffer if full SV_CheckLog (); // move autonomous things around if enough time has passed SV_Physics (); // get packets SV_ReadPackets (); // check for commands typed to the host SV_GetConsoleCommands (); // process console commands Cbuf_Execute (); SV_CheckVars (); // send messages back to the clients that had packets read this frame SV_SendClientMessages (); // send a heartbeat to the master if needed Master_Heartbeat (); // collect timing statistics end = Sys_DoubleTime (); svs.stats.active += end-start; if (++svs.stats.count == STATFRAMES) { svs.stats.latched_active = svs.stats.active; svs.stats.latched_idle = svs.stats.idle; svs.stats.latched_packets = svs.stats.packets; svs.stats.active = 0; svs.stats.idle = 0; svs.stats.packets = 0; svs.stats.count = 0; } } /* cvar callback functions : */ static void SV_Callback_Serverinfo (cvar_t *var) { Info_SetValueForKey (svs.info, var->name, var->string, MAX_SERVERINFO_STRING); SV_BroadcastCommand ("fullserverinfo \"%s\"\n", svs.info); } /* =============== SV_InitLocal =============== */ static void SV_InitLocal (void) { int i; SV_InitOperatorCommands (); SV_UserInit (); Cvar_RegisterVariable (&developer); if (COM_CheckParm("-developer")) { Cvar_Set ("developer", "1"); Cvar_LockVar ("developer"); } Cvar_RegisterVariable (&sys_nostdout); Cvar_RegisterVariable (&rcon_password); Cvar_RegisterVariable (&password); Cvar_RegisterVariable (&spectator_password); Cvar_RegisterVariable (&sv_mintic); Cvar_RegisterVariable (&sv_maxtic); Cvar_SetCallback (&deathmatch, SV_Callback_Serverinfo); Cvar_SetCallback (&coop, SV_Callback_Serverinfo); Cvar_SetCallback (&fraglimit, SV_Callback_Serverinfo); Cvar_SetCallback (&timelimit, SV_Callback_Serverinfo); Cvar_SetCallback (&teamplay, SV_Callback_Serverinfo); Cvar_SetCallback (&samelevel, SV_Callback_Serverinfo); Cvar_SetCallback (&maxclients, SV_Callback_Serverinfo); Cvar_SetCallback (&maxspectators, SV_Callback_Serverinfo); Cvar_SetCallback (&randomclass, SV_Callback_Serverinfo); Cvar_SetCallback (&damageScale, SV_Callback_Serverinfo); Cvar_SetCallback (­Respawn, SV_Callback_Serverinfo); Cvar_SetCallback (&spartanPrint, SV_Callback_Serverinfo); Cvar_SetCallback (&meleeDamScale, SV_Callback_Serverinfo); Cvar_SetCallback (&manaScale, SV_Callback_Serverinfo); Cvar_SetCallback (&tomeMode, SV_Callback_Serverinfo); Cvar_SetCallback (&tomeRespawn, SV_Callback_Serverinfo); Cvar_SetCallback (&w2Respawn, SV_Callback_Serverinfo); Cvar_SetCallback (&altRespawn, SV_Callback_Serverinfo); Cvar_SetCallback (&fixedLevel, SV_Callback_Serverinfo); Cvar_SetCallback (&autoItems, SV_Callback_Serverinfo); Cvar_SetCallback (&dmMode, SV_Callback_Serverinfo); Cvar_SetCallback (&easyFourth, SV_Callback_Serverinfo); Cvar_SetCallback (&patternRunner, SV_Callback_Serverinfo); Cvar_SetCallback (&spawn, SV_Callback_Serverinfo); Cvar_SetCallback (&hostname, SV_Callback_Serverinfo); Cvar_SetCallback (&noexit, SV_Callback_Serverinfo); Cvar_SetCallback (&sv_maxspeed, SV_Callback_Serverinfo); Cvar_RegisterVariable (&fraglimit); Cvar_RegisterVariable (&timelimit); Cvar_RegisterVariable (&teamplay); Cvar_RegisterVariable (&samelevel); Cvar_RegisterVariable (&maxclients); Cvar_RegisterVariable (&maxspectators); Cvar_RegisterVariable (&hostname); Cvar_RegisterVariable (&skill); Cvar_RegisterVariable (&coop); Cvar_RegisterVariable (&deathmatch); Cvar_RegisterVariable (&randomclass); Cvar_RegisterVariable (&damageScale); Cvar_RegisterVariable (&meleeDamScale); Cvar_RegisterVariable (­Respawn); Cvar_RegisterVariable (&spartanPrint); Cvar_RegisterVariable (&manaScale); Cvar_RegisterVariable (&tomeMode); Cvar_RegisterVariable (&tomeRespawn); Cvar_RegisterVariable (&w2Respawn); Cvar_RegisterVariable (&altRespawn); Cvar_RegisterVariable (&fixedLevel); Cvar_RegisterVariable (&autoItems); Cvar_RegisterVariable (&dmMode); Cvar_RegisterVariable (&easyFourth); Cvar_RegisterVariable (&patternRunner); Cvar_RegisterVariable (&spawn); Cvar_RegisterVariable (&noexit); Cvar_RegisterVariable (&timeout); Cvar_RegisterVariable (&zombietime); Cvar_RegisterVariable (&sv_maxvelocity); Cvar_RegisterVariable (&sv_gravity); Cvar_RegisterVariable (&sv_stopspeed); Cvar_RegisterVariable (&sv_maxspeed); Cvar_RegisterVariable (&sv_spectatormaxspeed); Cvar_RegisterVariable (&sv_accelerate); Cvar_RegisterVariable (&sv_airaccelerate); Cvar_RegisterVariable (&sv_wateraccelerate); Cvar_RegisterVariable (&sv_friction); Cvar_RegisterVariable (&sv_waterfriction); Cvar_RegisterVariable (&sv_aim); Cvar_RegisterVariable (&filterban); Cvar_RegisterVariable (&allow_download); Cvar_RegisterVariable (&allow_download_skins); Cvar_RegisterVariable (&allow_download_models); Cvar_RegisterVariable (&allow_download_sounds); Cvar_RegisterVariable (&allow_download_maps); Cvar_RegisterVariable (&sv_highchars); Cvar_RegisterVariable (&sv_phs); Cvar_RegisterVariable (&sv_namedistance); Cvar_RegisterVariable (&sv_ce_scale); Cvar_RegisterVariable (&sv_ce_max_size); Cmd_AddCommand ("addip", SV_AddIP_f); Cmd_AddCommand ("removeip", SV_RemoveIP_f); Cmd_AddCommand ("listip", SV_ListIP_f); Cmd_AddCommand ("writeip", SV_WriteIP_f); for (i = 0; i < MAX_MODELS; i++) sprintf (localmodels[i], "*%i", i); Info_SetValueForStarKey (svs.info, "*version", va("%4.2f", ENGINE_VERSION), MAX_SERVERINFO_STRING); // init fraglog stuff svs.logsequence = 1; svs.logtime = realtime; SZ_Init (&svs.log[0], svs.log_buf[0], sizeof(svs.log_buf[0])); SZ_Init (&svs.log[1], svs.log_buf[1], sizeof(svs.log_buf[1])); svs.log[0].allowoverflow = true; svs.log[1].allowoverflow = true; } //============================================================================ /* ================ Master_Heartbeat Send a message to the master every few minutes to let it know we are alive, and log information ================ */ #define HEARTBEAT_SECONDS 300 void Master_Heartbeat (void) { char text[32]; int i, active; if (realtime - svs.last_heartbeat < HEARTBEAT_SECONDS) return; // not time to send yet svs.last_heartbeat = realtime; // // count active users // active = 0; for (i = 0; i < MAX_CLIENTS; i++) { if (svs.clients[i].state == cs_connected || svs.clients[i].state == cs_spawned ) active++; } svs.heartbeat_sequence++; q_snprintf (text, sizeof(text), "%c\n%i\n%i\n", S2M_HEARTBEAT, svs.heartbeat_sequence, active); // send to group master for (i = 0; i < MAX_MASTERS; i++) { if (master_adr[i].port) { Con_Printf ("Sending heartbeat to %s\n", NET_AdrToString (&master_adr[i])); NET_SendPacket (strlen(text), text, &master_adr[i]); } } } /* ================= Master_Shutdown Informs all masters that this server is going down ================= */ static void Master_Shutdown (void) { static char text[] = { S2M_SHUTDOWN, '\n', '\0', '\0' }; int i; // send to group master for (i = 0; i < MAX_MASTERS; i++) { if (master_adr[i].port) { Con_Printf ("Sending heartbeat to %s\n", NET_AdrToString (&master_adr[i])); NET_SendPacket (3, text, &master_adr[i]); } } } /* ================= SV_ExtractFromUserinfo Pull specific info from a newly changed userinfo string into a more C freindly form. ================= */ void SV_ExtractFromUserinfo (client_t *cl) { const char *val; char *p, *q; int i, dupc = 1; client_t *client; char newname[80]; // 80 > 32 == sizeof(cl->name) because we // will be trimming below // name for C code val = Info_ValueForKey (cl->userinfo, "name"); // trim user name q_strlcpy(newname, val, sizeof(newname)); for (p = newname; *p == ' ' && *p; p++) ; if (p != newname && *p) { for (q = newname; *p; *q++ = *p++) ; *q = 0; } for (p = newname + strlen(newname) - 1; p != newname && *p == ' '; p--) ; p[1] = 0; if (strcmp(val, newname)) { Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING); val = Info_ValueForKey (cl->userinfo, "name"); } if (!val[0] || !q_strcasecmp(val, "console")) { Info_SetValueForKey (cl->userinfo, "name", "unnamed", MAX_INFO_STRING); val = Info_ValueForKey (cl->userinfo, "name"); } // check to see if another user by the same name exists while (1) { for (i = 0, client = svs.clients; i < MAX_CLIENTS; i++, client++) { if (client->state != cs_spawned || client == cl) continue; if (!q_strcasecmp(client->name, val)) break; } if (i != MAX_CLIENTS) { // dup name const char *ptr = val; if (val[0] == '(') { if (val[2] == ')') ptr = val + 3; else if (val[3] == ')') ptr = val + 4; } // limit to sizeof(cl->name) here to make it fit q_snprintf(newname, sizeof(cl->name), "(%d)%-.40s", dupc++, ptr); Info_SetValueForKey (cl->userinfo, "name", newname, MAX_INFO_STRING); val = Info_ValueForKey (cl->userinfo, "name"); } else break; } q_strlcpy (cl->name, val, sizeof(cl->name)); // rate command val = Info_ValueForKey (cl->userinfo, "rate"); if (*val) { i = atoi(val); if (i < 500) i = 500; if (i > 10000) i = 10000; cl->netchan.rate = 1.0/i; } // playerclass command val = Info_ValueForKey (cl->userinfo, "playerclass"); if (*val) { i = atoi(val); if (i > CLASS_DEMON && (dmMode.integer != DM_SIEGE || !SV_PROGS_HAVE_SIEGE)) i = CLASS_PALADIN; if (i < 0 || i > MAX_PLAYER_CLASS || (!cl->portals && i == CLASS_DEMON)) { i = 0; } cl->next_playerclass = cl->edict->v.next_playerclass = i; if (cl->edict->v.health > 0) { sprintf(newname,"%d",cl->playerclass); Info_SetValueForKey (cl->userinfo, "playerclass", newname, MAX_INFO_STRING); } } // msg command val = Info_ValueForKey (cl->userinfo, "msg"); if (*val) { cl->messagelevel = atoi(val); } } //============================================================================ /* ==================== SV_InitNet ==================== */ static void SV_InitNet (void) { int port; int p; port = PORT_SERVER; p = COM_CheckParm ("-port"); if (p && p < com_argc-1) { port = atoi(com_argv[p+1]); Con_Printf ("Port: %i\n", port); } NET_Init (port); Netchan_Init (); svs.last_heartbeat = -99999; // send immediately } /* ==================== SV_Init ==================== */ void SV_Init (void) { int i; Sys_Printf ("Host_Init\n"); i = COM_CheckParm ("-protocol"); if (i && i < com_argc - 1) { switch ((sv_protocol = atoi (com_argv[i + 1]))) { case PROTOCOL_VERSION_EXT: case PROTOCOL_VERSION: Sys_Printf ("Server using protocol %i\n", sv_protocol); break; default: Sys_Error ("Bad protocol version request %i. Accepted values: %i, %i.", sv_protocol, PROTOCOL_VERSION, PROTOCOL_VERSION_EXT); } } Memory_Init (host_parms->membase, host_parms->memsize); HuffInit (); Cbuf_Init (); Cmd_Init (); COM_Init (); FS_Init (); PR_Init (); Mod_Init (); SV_InitNet (); SV_InitLocal (); Pmove_Init (); Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); host_hunklevel = Hunk_LowMark (); host_initialized = true; Con_Printf ("Exe: " __TIME__ " " __DATE__ "\n"); Con_Printf ("%4.1f megabyte heap\n", host_parms->memsize/(1024*1024.0)); Con_Printf ("======== HexenWorld Initialized ========\n"); Cvar_UnlockAll (); /* unlock the early-set cvars after init */ Cbuf_InsertText ("exec server.cfg\n"); Cbuf_Execute (); Cmd_StuffCmds_f (); /* process command line arguments */ Cbuf_Execute (); if (sv.state == ss_dead) /* no map specified on the command line: spawn demo1.map */ Cmd_ExecuteString ("map demo1", src_command); if (sv.state == ss_dead) SV_Error ("Couldn't spawn a server"); } engine/hexenworld/server/sv_move.c000066400000000000000000000246451444734033100176500ustar00rootroot00000000000000/* * sv_move.c -- monster movement * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #define STEPSIZE 18 /* ============= SV_CheckBottom Returns false if any part of the bottom of the entity is off an edge that is not a staircase. ============= */ qboolean SV_CheckBottom (edict_t *ent) { vec3_t mins, maxs, start, stop; trace_t trace; int x, y; float mid, bottom; float save_hull; VectorAdd (ent->v.origin, ent->v.mins, mins); VectorAdd (ent->v.origin, ent->v.maxs, maxs); // if all of the points under the corners are solid world, don't bother // with the tougher checks // the corners must be within 16 of the midpoint start[2] = mins[2] - 1; for (x = 0; x < 2; x++) { for (y = 0; y < 2; y++) { start[0] = x ? maxs[0] : mins[0]; start[1] = y ? maxs[1] : mins[1]; if (SV_PointContents (start) != CONTENTS_SOLID) goto realcheck; } } return true; // we got out easy realcheck: // check it for real... start[2] = mins[2]; // the midpoint must be within 16 of the bottom start[0] = stop[0] = (mins[0] + maxs[0]) * 0.5; start[1] = stop[1] = (mins[1] + maxs[1]) * 0.5; stop[2] = start[2] - 2*STEPSIZE; save_hull = ent->v.hull;//temp hack so it HullForEntity doesn't calculate the wrong offset ent->v.hull = 0; trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent); ent->v.hull = save_hull; if (trace.fraction == 1.0) return false; mid = bottom = trace.endpos[2]; // the corners must be within 16 of the midpoint for (x = 0; x < 2; x++) { for (y = 0; y < 2; y++) { start[0] = stop[0] = x ? maxs[0] : mins[0]; start[1] = stop[1] = y ? maxs[1] : mins[1]; save_hull = ent->v.hull;//temp hack so it HullForEntity doesn't calculate the wrong offset ent->v.hull = 0; trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent); ent->v.hull = save_hull; if (trace.fraction != 1.0 && trace.endpos[2] > bottom) bottom = trace.endpos[2]; if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE) return false; } } return true; } static void set_move_trace(trace_t *trace) { *sv_globals.trace_allsolid = trace->allsolid; *sv_globals.trace_startsolid = trace->startsolid; *sv_globals.trace_fraction = trace->fraction; *sv_globals.trace_inwater = trace->inwater; *sv_globals.trace_inopen = trace->inopen; VectorCopy (trace->endpos, *sv_globals.trace_endpos); VectorCopy (trace->plane.normal, *sv_globals.trace_plane_normal); *sv_globals.trace_plane_dist = trace->plane.dist; if (trace->ent) *sv_globals.trace_ent = EDICT_TO_PROG(trace->ent); else *sv_globals.trace_ent = EDICT_TO_PROG(sv.edicts); } /* ============= SV_movestep Called by monster program code. The move will be adjusted for slopes and stairs, but if the move isn't possible, no move is done, false is returned, and *sv_globals.trace_normal is set to the normal of the blocking wall ============= */ qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink, qboolean noenemy, qboolean set_trace) { float dz; vec3_t oldorg, neworg, end; trace_t trace; int i; edict_t *enemy = NULL; // avoid compiler warning. // try the move VectorCopy (ent->v.origin, oldorg); VectorAdd (ent->v.origin, move, neworg); // flying monsters don't step up if ( (int)ent->v.flags & (FL_SWIM|FL_FLY) ) { // try one move with vertical motion, then one without for (i = 0; i < 2; i++) { VectorAdd (ent->v.origin, move, neworg); if (!noenemy) { enemy = PROG_TO_EDICT(ent->v.enemy); if (i == 0 && enemy != sv.edicts) { dz = ent->v.origin[2] - PROG_TO_EDICT(ent->v.enemy)->v.origin[2]; if (dz > 40) neworg[2] -= 8; else if (dz < 30) neworg[2] += 8; } } trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, neworg, false, ent); if (set_trace) { set_move_trace(&trace); } if (trace.fraction == 1) { if ( ((int)ent->v.flags & FL_SWIM) && SV_PointContents(trace.endpos) == CONTENTS_EMPTY ) return false; // swim monster left water VectorCopy (trace.endpos, ent->v.origin); if (relink) SV_LinkEdict (ent, true); return true; } if (noenemy || enemy == sv.edicts) break; } return false; } // push down from a step height above the wished position neworg[2] += STEPSIZE; VectorCopy (neworg, end); end[2] -= STEPSIZE*2; trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent); if (set_trace) { set_move_trace(&trace); } if (trace.allsolid) { return false; } if (trace.startsolid) { neworg[2] -= STEPSIZE; trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent); if (set_trace) { set_move_trace(&trace); } if (trace.allsolid || trace.startsolid) { return false; } } if (trace.fraction == 1) { // if monster had the ground pulled out, go ahead and fall if ( (int)ent->v.flags & FL_PARTIALGROUND ) { VectorAdd (ent->v.origin, move, ent->v.origin); if (relink) SV_LinkEdict (ent, true); ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; // Con_Printf ("fall down\n"); return true; } return false; // walked off an edge } // check point traces down for dangling corners VectorCopy (trace.endpos, ent->v.origin); if (!SV_CheckBottom (ent)) { if ( (int)ent->v.flags & FL_PARTIALGROUND ) { // entity had floor mostly pulled out from underneath it // and is trying to correct if (relink) SV_LinkEdict (ent, true); return true; } VectorCopy (oldorg, ent->v.origin); return false; } if ( (int)ent->v.flags & FL_PARTIALGROUND ) { // Con_Printf ("back on ground\n"); ent->v.flags = (int)ent->v.flags & ~FL_PARTIALGROUND; } ent->v.groundentity = EDICT_TO_PROG(trace.ent); // the move is ok if (relink) SV_LinkEdict (ent, true); return true; } //============================================================================ /* ====================== SV_StepDirection Turns to the movement direction, and walks the current distance if facing it. ====================== */ extern void PF_changeyaw (void); static qboolean SV_StepDirection (edict_t *ent, float yaw, float dist) { vec3_t move, oldorigin; float delta; ent->v.ideal_yaw = yaw; PF_changeyaw(); yaw = yaw * M_PI * 2 / 360; move[0] = cos(yaw) * dist; move[1] = sin(yaw) * dist; move[2] = 0; VectorCopy (ent->v.origin, oldorigin); if (SV_movestep (ent, move, false, false, false)) { delta = ent->v.angles[YAW] - ent->v.ideal_yaw; if (delta > 45 && delta < 315) { // not turned far enough, so don't take the step VectorCopy (oldorigin, ent->v.origin); } SV_LinkEdict (ent, true); return true; } SV_LinkEdict (ent, true); return false; } /* ====================== SV_FixCheckBottom ====================== */ static void SV_FixCheckBottom (edict_t *ent) { // Con_Printf ("SV_FixCheckBottom\n"); ent->v.flags = (int)ent->v.flags | FL_PARTIALGROUND; } /* ================ SV_NewChaseDir ================ */ #define DI_NODIR -1 static void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist) { float deltax, deltay; float d[3]; float tdir, olddir, turnaround; olddir = anglemod( (int)(actor->v.ideal_yaw / 45) * 45 ); turnaround = anglemod(olddir - 180); deltax = enemy->v.origin[0] - actor->v.origin[0]; deltay = enemy->v.origin[1] - actor->v.origin[1]; if (deltax > 10) d[1] = 0; else if (deltax < -10) d[1] = 180; else d[1] = DI_NODIR; if (deltay < -10) d[2] = 270; else if (deltay > 10) d[2] = 90; else d[2] = DI_NODIR; // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { if (d[1] == 0) tdir = d[2] == 90 ? 45 : 315; else tdir = d[2] == 90 ? 135 : 215; if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) return; } // try other directions if ( ((rand() & 3) & 1) || abs((int)deltay) > abs((int)deltax) ) { tdir = d[1]; d[1] = d[2]; d[2] = tdir; } if (d[1] != DI_NODIR && d[1] != turnaround && SV_StepDirection(actor, d[1], dist)) return; if (d[2] != DI_NODIR && d[2] != turnaround && SV_StepDirection(actor, d[2], dist)) return; /* there is no direct path to the player, so pick another direction */ if (olddir != DI_NODIR && SV_StepDirection(actor, olddir, dist)) return; if (rand() & 1) /* randomly determine direction of search */ { for (tdir = 0; tdir <= 315; tdir += 45) { if (tdir != turnaround && SV_StepDirection(actor, tdir, dist) ) return; } } else { for (tdir = 315; tdir >= 0; tdir -= 45) { if (tdir != turnaround && SV_StepDirection(actor, tdir, dist) ) return; } } if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) ) return; actor->v.ideal_yaw = olddir; // can't move // if a bridge was pulled out from underneath a monster, it may not have // a valid standing position at all if (!SV_CheckBottom (actor)) SV_FixCheckBottom (actor); } /* ====================== SV_CloseEnough ====================== */ static qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist) { int i; for (i = 0; i < 3; i++) { if (goal->v.absmin[i] > ent->v.absmax[i] + dist) return false; if (goal->v.absmax[i] < ent->v.absmin[i] - dist) return false; } return true; } /* ====================== SV_MoveToGoal ====================== */ void SV_MoveToGoal (void) { edict_t *ent, *goal; float dist; ent = PROG_TO_EDICT(*sv_globals.self); goal = PROG_TO_EDICT(ent->v.goalentity); dist = G_FLOAT(OFS_PARM0); if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) { G_FLOAT(OFS_RETURN) = 0; return; } // if the next step hits the enemy, return immediately if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts && SV_CloseEnough(ent, goal, dist) ) return; // bump around... if ( (rand() & 3) == 1 || !SV_StepDirection(ent, ent->v.ideal_yaw, dist) ) { SV_NewChaseDir (ent, goal, dist); } } engine/hexenworld/server/sv_phys.c000066400000000000000000001464261444734033100176670ustar00rootroot00000000000000/* * sv_phys.c -- sv physics * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" /* pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move. onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS corpses are SOLID_NOT and MOVETYPE_TOSS crates are SOLID_BBOX and MOVETYPE_TOSS walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY solid_edge items only clip against bsp models. */ cvar_t sv_maxvelocity = { "sv_maxvelocity","2000", CVAR_NONE }; //cvar_t sv_nostep = { "sv_nostep","0", CVAR_NONE }; cvar_t sv_gravity = { "sv_gravity", "800", CVAR_NONE }; cvar_t sv_stopspeed = { "sv_stopspeed", "100", CVAR_NONE }; cvar_t sv_maxspeed = { "sv_maxspeed", "360", CVAR_NOTIFY|CVAR_SERVERINFO }; cvar_t sv_spectatormaxspeed = { "sv_spectatormaxspeed", "500", CVAR_NONE }; cvar_t sv_accelerate = { "sv_accelerate", "10", CVAR_NONE }; cvar_t sv_airaccelerate = { "sv_airaccelerate", "0.7", CVAR_NONE }; cvar_t sv_wateraccelerate = { "sv_wateraccelerate", "10", CVAR_NONE }; cvar_t sv_friction = { "sv_friction", "4", CVAR_NONE }; cvar_t sv_waterfriction = { "sv_waterfriction", "1", CVAR_NONE }; cvar_t sv_flypitch = { "sv_flypitch", "20", CVAR_NONE }; cvar_t sv_walkpitch = { "sv_walkpitch", "0", CVAR_NONE }; #if 0 static vec3_t vec_origin = {0.0, 0.0, 0.0}; #endif #define MOVE_EPSILON 0.01 void SV_Physics_Toss (edict_t *ent); /* ================ SV_CheckAllEnts ================ */ #if 0 // not used static void SV_CheckAllEnts (void) { int e; edict_t *check; // see if any solid entities are inside the final position check = NEXT_EDICT(sv.edicts); for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT(check)) { if (check->free) continue; if (check->v.movetype == MOVETYPE_PUSH || check->v.movetype == MOVETYPE_NONE || check->v.movetype == MOVETYPE_FOLLOW || check->v.movetype == MOVETYPE_NOCLIP) continue; if (SV_TestEntityPosition (check)) Con_Printf ("entity in invalid position\n"); } } #endif /* ================ SV_CheckVelocity ================ */ void SV_CheckVelocity (edict_t *ent) { int i; float w; // // bound velocity // for (i = 0; i < 3; i++) { if (IS_NAN(ent->v.velocity[i])) { Con_Printf ("Got a NaN velocity on %s\n", PR_GetString(ent->v.classname)); ent->v.velocity[i] = 0; } if (IS_NAN(ent->v.origin[i])) { Con_Printf ("Got a NaN origin on %s\n", PR_GetString(ent->v.classname)); ent->v.origin[i] = 0; } } w = VectorLength(ent->v.velocity); if (w > sv_maxvelocity.value) { // sv_maxvelocity fix by Maddes VectorScale (ent->v.velocity, sv_maxvelocity.value/w, ent->v.velocity); } } /* ============= SV_RunThink Runs thinking code if time. There is some play in the exact time the think function will be called, because it is called before any movement is done in a frame. Not used for pushmove objects, because they must be exact. Returns false if the entity removed itself. ============= */ qboolean SV_RunThink (edict_t *ent) { float thinktime; thinktime = ent->v.nextthink; if (thinktime <= 0) { return true; } if (thinktime > sv.time + host_frametime) { return true; } if (thinktime < sv.time) thinktime = sv.time; // don't let things stay in the past. // it is possible to start that way // by a trigger with a local time. ent->v.nextthink = 0; *sv_globals.time = thinktime; *sv_globals.self = EDICT_TO_PROG(ent); *sv_globals.other = EDICT_TO_PROG(sv.edicts); PR_ExecuteProgram (ent->v.think); if (ent->free) { return false; } return true; } /* ================== SV_Impact Two entities have touched, so run their touch functions ================== */ void SV_Impact (edict_t *e1, edict_t *e2) { int old_self, old_other; old_self = *sv_globals.self; old_other = *sv_globals.other; *sv_globals.time = sv.time; if (e1->v.touch && e1->v.solid != SOLID_NOT) { *sv_globals.self = EDICT_TO_PROG(e1); *sv_globals.other = EDICT_TO_PROG(e2); PR_ExecuteProgram (e1->v.touch); } if (e2->v.touch && e2->v.solid != SOLID_NOT) { *sv_globals.self = EDICT_TO_PROG(e2); *sv_globals.other = EDICT_TO_PROG(e1); PR_ExecuteProgram (e2->v.touch); } *sv_globals.self = old_self; *sv_globals.other = old_other; } /* ================== ClipVelocity Slide off of the impacting object returns the blocked flags (1 = floor, 2 = step / wall) ================== */ #define STOP_EPSILON 0.1 static int ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce) { float backoff; float change; int i, blocked; blocked = 0; if (normal[2] > 0) blocked |= 1; // floor if (!normal[2]) blocked |= 2; // step backoff = DotProduct (in, normal) * overbounce; for (i = 0; i < 3; i++) { change = normal[i]*backoff; out[i] = in[i] - change; if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) out[i] = 0; } return blocked; } /* ============ SV_FlyMove The basic solid body movement clip that slides along multiple planes Returns the clipflags if the velocity was modified (hit something solid) 1 = floor 2 = wall / step 4 = dead stop If steptrace is not NULL, the trace of any vertical wall hit will be stored ============ */ #define MAX_CLIP_PLANES 5 static int SV_FlyMove (edict_t *ent, float time, trace_t *steptrace) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity, original_velocity, new_velocity; int i, j; trace_t trace; vec3_t end; float time_left; int blocked; numbumps = 4; blocked = 0; VectorCopy (ent->v.velocity, original_velocity); VectorCopy (ent->v.velocity, primal_velocity); numplanes = 0; time_left = time; for (bumpcount = 0; bumpcount < numbumps; bumpcount++) { if (!ent->v.velocity[0] && !ent->v.velocity[1] && !ent->v.velocity[2]) break; for (i = 0; i < 3; i++) end[i] = ent->v.origin[i] + time_left * ent->v.velocity[i]; trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent); if (trace.allsolid) { // entity is trapped in another solid VectorClear (ent->v.velocity); return 3; } if (trace.fraction > 0) { // actually covered some distance VectorCopy (trace.endpos, ent->v.origin); VectorCopy (ent->v.velocity, original_velocity); numplanes = 0; } if (trace.fraction == 1) break; // moved the entire distance if (!trace.ent) SV_Error ("%s: !trace.ent", __thisfunc__); if (trace.plane.normal[2] > 0.7) { blocked |= 1; // floor if (trace.ent->v.solid == SOLID_BSP) { ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = EDICT_TO_PROG(trace.ent); } } if (!trace.plane.normal[2]) { blocked |= 2; // step if (steptrace) *steptrace = trace; // save for player extrafriction } // // run the impact function // SV_Impact (ent, trace.ent); if (ent->free) break; // removed by the impact function time_left -= time_left * trace.fraction; // cliped to another plane if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen VectorClear (ent->v.velocity); return 3; } VectorCopy (trace.plane.normal, planes[numplanes]); numplanes++; // // modify original_velocity so it parallels all of the clip planes // for (i = 0; i < numplanes; i++) { ClipVelocity (original_velocity, planes[i], new_velocity, 1); for (j = 0; j < numplanes; j++) { if (j != i) { if (DotProduct (new_velocity, planes[j]) < 0) break; // not ok } } if (j == numplanes) break; } if (i != numplanes) { // go along this plane VectorCopy (new_velocity, ent->v.velocity); } else { // go along the crease if (numplanes != 2) { // Con_Printf ("clip velocity, numplanes == %i\n",numplanes); VectorClear (ent->v.velocity); return 7; } CrossProduct (planes[0], planes[1], dir); d = DotProduct (dir, ent->v.velocity); VectorScale (dir, d, ent->v.velocity); } // // if original velocity is against the original velocity, stop dead // to avoid tiny occilations in sloping corners // if (DotProduct (ent->v.velocity, primal_velocity) <= 0) { VectorClear (ent->v.velocity); return blocked; } } return blocked; } /* ============ SV_FlyExtras ============ */ #if 0 static const float hoverinc = 0.4; static void SV_FlyExtras (edict_t *ent, float time, trace_t *steptrace) { // Jumping makes you loose this flag so reset it ent->v.flags = (int) ent->v.flags | FL_ONGROUND; if ((ent->v.velocity[2] <= 6) && (ent->v.velocity[2] >= -6)) { ent->v.velocity[2] += ent->v.hoverz; if (ent->v.velocity[2] >= 6) { ent->v.hoverz = -hoverinc; ent->v.velocity[2] += ent->v.hoverz; } else if (ent->v.velocity[2] <= -6) { ent->v.hoverz = hoverinc; ent->v.velocity[2] += ent->v.hoverz; } } else // friction for upward or downward progress once key is released { ent->v.velocity[2] -= sv_player->v.velocity[2] * .1; } } #endif /* ============ SV_AddGravity ============ */ void SV_AddGravity (edict_t *ent, float scale) { ent->v.velocity[2] -= scale * movevars.gravity * host_frametime; } /* =============================================================================== PUSHMOVE =============================================================================== */ /* ============ SV_PushEntity Does not change the entities velocity at all ============ */ trace_t SV_PushEntity (edict_t *ent, vec3_t push) { trace_t trace; vec3_t start, end, impact; edict_t *impact_e; VectorCopy (ent->v.origin, start); (void) start; /* variable set but not used */ VectorAdd (ent->v.origin, push, end); // if ((int)ent->v.flags & FL_CLIENT) // Con_Printf("Player exec pushent\n"); if (ent->v.movetype == MOVETYPE_FLYMISSILE || ent->v.movetype == MOVETYPE_BOUNCEMISSILE) trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_MISSILE, ent); else if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT) // only clip against bmodels trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS, ent); else if (ent->v.movetype == MOVETYPE_SWIM) trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_WATER, ent); else trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent); if (ent->v.solid != SOLID_PHASE) { if (ent->v.movetype != MOVETYPE_BOUNCE || (trace.allsolid == 0 && trace.startsolid == 0)) { VectorCopy (trace.endpos, ent->v.origin); // Macro - watchout } else { trace.fraction = 0; return trace; } } else // Entity is PHASED so bounce off walls and other entities, go through monsters and players { if (trace.ent) { // Go through MONSTERS and PLAYERS, can't use FL_CLIENT cause rotating brushes do if (((int) trace.ent->v.flags & FL_MONSTER) || (trace.ent->v.movetype == MOVETYPE_WALK)) { VectorCopy (trace.endpos, impact); impact_e = trace.ent; trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_PHASE, ent); VectorCopy (impact, ent->v.origin); SV_Impact (ent, impact_e); VectorCopy (trace.endpos, ent->v.origin); } else { VectorCopy (trace.endpos, ent->v.origin); } } else { VectorCopy (trace.endpos, ent->v.origin); } } SV_LinkEdict (ent, true); if (trace.ent) SV_Impact (ent, trace.ent); return trace; } /* ============ SV_Push ============ */ static qboolean SV_Push (edict_t *pusher, vec3_t move) { int i, e; edict_t *check, *block; vec3_t mins, maxs; vec3_t pushorig; int num_moved; edict_t *moved_edict[MAX_EDICTS]; vec3_t moved_from[MAX_EDICTS]; for (i = 0; i < 3; i++) { mins[i] = pusher->v.absmin[i] + move[i]; maxs[i] = pusher->v.absmax[i] + move[i]; } VectorCopy (pusher->v.origin, pushorig); // move the pusher to it's final position VectorAdd (pusher->v.origin, move, pusher->v.origin); SV_LinkEdict (pusher, false); // see if any solid entities are inside the final position num_moved = 0; check = NEXT_EDICT(sv.edicts); for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT(check)) { if (check->free) continue; if (check->v.movetype == MOVETYPE_PUSH || check->v.movetype == MOVETYPE_NONE || check->v.movetype == MOVETYPE_NOCLIP) continue; pusher->v.solid = SOLID_NOT; block = SV_TestEntityPosition (check); pusher->v.solid = SOLID_BSP; if (block) continue; // if the entity is standing on the pusher, it will definately be moved if ( ! ( ((int)check->v.flags & FL_ONGROUND) && PROG_TO_EDICT(check->v.groundentity) == pusher) ) { if ( check->v.absmin[0] >= maxs[0] || check->v.absmin[1] >= maxs[1] || check->v.absmin[2] >= maxs[2] || check->v.absmax[0] <= mins[0] || check->v.absmax[1] <= mins[1] || check->v.absmax[2] <= mins[2] ) continue; // see if the ent's bbox is inside the pusher's final position if (!SV_TestEntityPosition (check)) continue; } VectorCopy (check->v.origin, moved_from[num_moved]); moved_edict[num_moved] = check; num_moved++; // try moving the contacted entity VectorAdd (check->v.origin, move, check->v.origin); block = SV_TestEntityPosition (check); if (!block) { // pushed ok SV_LinkEdict (check, false); continue; } // if it is ok to leave in the old position, do it VectorSubtract (check->v.origin, move, check->v.origin); block = SV_TestEntityPosition (check); if (!block) { num_moved--; continue; } // if it is still inside the pusher, block if (check->v.mins[0] == check->v.maxs[0]) { SV_LinkEdict (check, false); continue; } if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER) { // corpse check->v.mins[0] = check->v.mins[1] = 0; VectorCopy (check->v.mins, check->v.maxs); SV_LinkEdict (check, false); continue; } VectorCopy (pushorig, pusher->v.origin); SV_LinkEdict (pusher, false); // if the pusher has a "blocked" function, call it // otherwise, just stay in place until the obstacle is gone if (pusher->v.blocked) { *sv_globals.self = EDICT_TO_PROG(pusher); *sv_globals.other = EDICT_TO_PROG(check); PR_ExecuteProgram (pusher->v.blocked); } // move back any entities we already moved for (i = 0; i < num_moved; i++) { VectorCopy (moved_from[i], moved_edict[i]->v.origin); SV_LinkEdict (moved_edict[i], false); } return false; } return true; } /* ============ SV_PushMove ============ */ static void SV_PushMove (edict_t *pusher, float movetime, qboolean update_time) { int i; vec3_t move; if (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2]) { if (update_time) { pusher->v.ltime += movetime; } return; } for (i = 0; i < 3; i++) move[i] = pusher->v.velocity[i] * movetime; if (SV_Push (pusher, move)) { if (update_time) { pusher->v.ltime += movetime; } } } /* ============ SV_PushRotate Pre-Mission Pack fix ============ */ #if 0 // Pre-Mission Pack fix static void SV_PushRotate (edict_t *pusher, float movetime) { int i, e; edict_t *check, *block; vec3_t move, a, amove; vec3_t entorig, pushorig; int num_moved; edict_t *moved_edict[MAX_EDICTS]; vec3_t moved_from[MAX_EDICTS]; vec3_t org, org2; vec3_t forward, right, up; edict_t *ground; // edict_t *master; edict_t *slave; int slaves_moved; qboolean moveit; # if 0 Con_DPrintf("%s entity %i (time=%f)\n", __thisfunc__, NUM_FOR_EDICT(pusher), movetime); Con_DPrintf("%f %f %f (avelocity)\n", pusher->v.avelocity[0], pusher->v.avelocity[1], pusher->v.avelocity[2]); Con_DPrintf("%f %f %f\n", pusher->v.angles[0], pusher->v.angles[1], pusher->v.angles[2]); # endif for (i = 0; i < 3; i++) amove[i] = pusher->v.avelocity[i] * movetime; VectorNegate (amove, a); AngleVectors (a, forward, right, up); VectorCopy (pusher->v.angles, pushorig); // move the pusher to it's final position VectorAdd (pusher->v.angles, amove, pusher->v.angles); pusher->v.ltime += movetime; SV_LinkEdict (pusher, false); slaves_moved = 0; /* master = pusher; while (master->v.aiment) { slave = PROG_TO_EDICT(master->v.aiment); // Con_DPrintf("%f %f %f slave entity %i\n", slave->v.angles[0], slave->v.angles[1], slave->v.angles[2], NUM_FOR_EDICT(slave)); slaves_moved++; VectorCopy (slave->v.angles, moved_from[MAX_EDICTS - slaves_moved]); moved_edict[MAX_EDICTS - slaves_moved] = slave; if (slave->v.movedir[PITCH]) slave->v.angles[PITCH] = master->v.angles[PITCH]; else slave->v.angles[PITCH] += slave->v.avelocity[PITCH] * movetime; if (slave->v.movedir[YAW]) slave->v.angles[YAW] = master->v.angles[YAW]; else slave->v.angles[YAW] += slave->v.avelocity[YAW] * movetime; if (slave->v.movedir[ROLL]) slave->v.angles[ROLL] = master->v.angles[ROLL]; else slave->v.angles[ROLL] += slave->v.avelocity[ROLL] * movetime; slave->v.ltime = master->v.ltime; SV_LinkEdict (slave, false); master = slave; } */ // see if any solid entities are inside the final position num_moved = 0; check = NEXT_EDICT(sv.edicts); for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT(check)) { if (check->free) continue; if (check->v.movetype == MOVETYPE_PUSH || check->v.movetype == MOVETYPE_NONE || check->v.movetype == MOVETYPE_FOLLOW || check->v.movetype == MOVETYPE_NOCLIP) continue; // if the entity is standing on the pusher, it will definitely be moved moveit = false; ground = PROG_TO_EDICT(check->v.groundentity); if ((int)check->v.flags & FL_ONGROUND) { if (ground == pusher) { moveit = true; } else { for (i = 0; i < slaves_moved; i++) { if (ground == moved_edict[MAX_EDICTS - i - 1]) { moveit = true; break; } } } } if (!moveit) { if ( check->v.absmin[0] >= pusher->v.absmax[0] || check->v.absmin[1] >= pusher->v.absmax[1] || check->v.absmin[2] >= pusher->v.absmax[2] || check->v.absmax[0] <= pusher->v.absmin[0] || check->v.absmax[1] <= pusher->v.absmin[1] || check->v.absmax[2] <= pusher->v.absmin[2] ) { for (i = 0; i < slaves_moved; i++) { slave = moved_edict[MAX_EDICTS - i - 1]; if ( check->v.absmin[0] >= slave->v.absmax[0] || check->v.absmin[1] >= slave->v.absmax[1] || check->v.absmin[2] >= slave->v.absmax[2] || check->v.absmax[0] <= slave->v.absmin[0] || check->v.absmax[1] <= slave->v.absmin[1] || check->v.absmax[2] <= slave->v.absmin[2] ) continue; } if (i == slaves_moved) continue; } // see if the ent's bbox is inside the pusher's final position if (!SV_TestEntityPosition (check)) continue; } // remove the onground flag for non-players if (check->v.movetype != MOVETYPE_WALK) check->v.flags = (int)check->v.flags & ~FL_ONGROUND; VectorCopy (check->v.origin, entorig); VectorCopy (check->v.origin, moved_from[num_moved]); moved_edict[num_moved] = check; num_moved++; // calculate destination position VectorSubtract (check->v.origin, pusher->v.origin, org); org2[0] = DotProduct (org, forward); org2[1] = -DotProduct (org, right); org2[2] = DotProduct (org, up); VectorSubtract (org2, org, move); // try moving the contacted entity pusher->v.solid = SOLID_NOT; SV_PushEntity (check, move); //@@TODO: do we ever want to do anybody's angles? maybe just yaw??? // if (!((int)check->v.flags & (FL_CLIENT | FL_MONSTER))) // VectorAdd (check->v.angles, amove, check->v.angles); check->v.angles[YAW] += amove[YAW]; pusher->v.solid = SOLID_BSP; // if it is still inside the pusher, block block = SV_TestEntityPosition (check); if (block) { // fail the move if (check->v.mins[0] == check->v.maxs[0]) continue; if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER) { // corpse check->v.mins[0] = check->v.mins[1] = 0; VectorCopy (check->v.mins, check->v.maxs); continue; } VectorCopy (entorig, check->v.origin); SV_LinkEdict (check, true); VectorCopy (pushorig, pusher->v.angles); SV_LinkEdict (pusher, false); pusher->v.ltime -= movetime; for (i = 0; i < slaves_moved; i++) { slave = moved_edict[MAX_EDICTS - i - 1]; VectorCopy (moved_from[MAX_EDICTS - i - 1], slave->v.angles); SV_LinkEdict (slave, false); slave->v.ltime -= movetime; } // if the pusher has a "blocked" function, call it // otherwise, just stay in place until the obstacle is gone if (pusher->v.blocked) { *sv_globals.self = EDICT_TO_PROG(pusher); *sv_globals.other = EDICT_TO_PROG(check); PR_ExecuteProgram (pusher->v.blocked); } // move back any entities we already moved for (i = 0; i < num_moved; i++) { VectorCopy (moved_from[i], moved_edict[i]->v.origin); //@@TODO:: see above // if (!((int)moved_edict[i]->v.flags & (FL_CLIENT | FL_MONSTER))) // VectorSubtract (moved_edict[i]->v.angles, amove, moved_edict[i]->v.angles); moved_edict[i]->v.angles[YAW] -= amove[YAW]; SV_LinkEdict (moved_edict[i], false); } return; } } # if 0 Con_DPrintf("result:\n"); Con_DPrintf("%f %f %f\n", pusher->v.angles[0], pusher->v.angles[1], pusher->v.angles[2]); for (i = 0; i < slaves_moved; i++) { slave = moved_edict[MAX_EDICTS - i - 1]; Con_DPrintf("%f %f %f slave entity %i\n", slave->v.angles[0], slave->v.angles[1], slave->v.angles[2], NUM_FOR_EDICT(slave)); } Con_DPrintf("\n"); # endif } #endif // end of Pre-Mission Pack fix /* ============ SV_PushRotate NEW ============ */ static void SV_PushRotate (edict_t *pusher, float movetime) { int i, e, t; edict_t *check, *block; vec3_t move, a, amove, mins, maxs, move2, move3, testmove; // vec3_t amove_norm; vec3_t entorig, pushorig, pushorigangles; int num_moved; edict_t *moved_edict[MAX_EDICTS]; vec3_t moved_from[MAX_EDICTS]; vec3_t org, org2, check_center; vec3_t forward, right, up; edict_t *ground; // edict_t *master; edict_t *slave; int slaves_moved; qboolean moveit; // float turn_away, amove_mag; # if 0 Con_DPrintf("%s entity %i (time=%f)\n", __thisfunc__, NUM_FOR_EDICT(pusher), movetime); Con_DPrintf("%f %f %f (avelocity)\n", pusher->v.avelocity[0], pusher->v.avelocity[1], pusher->v.avelocity[2]); Con_DPrintf("%f %f %f\n", pusher->v.angles[0], pusher->v.angles[1], pusher->v.angles[2]); # endif for (i = 0; i < 3; i++) { amove[i] = pusher->v.avelocity[i] * movetime; move[i] = pusher->v.velocity[i] * movetime; mins[i] = pusher->v.absmin[i] + move[i]; maxs[i] = pusher->v.absmax[i] + move[i]; } VectorNegate (amove, a); AngleVectors (a, forward, right, up); VectorCopy (pusher->v.origin, pushorig); VectorCopy (pusher->v.angles, pushorigangles); // move the pusher to it's final position VectorAdd (pusher->v.origin, move, pusher->v.origin); VectorAdd (pusher->v.angles, amove, pusher->v.angles); pusher->v.ltime += movetime; SV_LinkEdict (pusher, false); slaves_moved = 0; /* master = pusher; while (master->v.aiment) { slave = PROG_TO_EDICT(master->v.aiment); // Con_DPrintf("%f %f %f slave entity %i\n", slave->v.angles[0], slave->v.angles[1], slave->v.angles[2], NUM_FOR_EDICT(slave)); slaves_moved++; VectorCopy (slave->v.angles, moved_from[MAX_EDICTS - slaves_moved]); moved_edict[MAX_EDICTS - slaves_moved] = slave; if (slave->v.movedir[PITCH]) slave->v.angles[PITCH] = master->v.angles[PITCH]; else slave->v.angles[PITCH] += slave->v.avelocity[PITCH] * movetime; if (slave->v.movedir[YAW]) slave->v.angles[YAW] = master->v.angles[YAW]; else slave->v.angles[YAW] += slave->v.avelocity[YAW] * movetime; if (slave->v.movedir[ROLL]) slave->v.angles[ROLL] = master->v.angles[ROLL]; else slave->v.angles[ROLL] += slave->v.avelocity[ROLL] * movetime; slave->v.ltime = master->v.ltime; SV_LinkEdict (slave, false); master = slave; } */ // see if any solid entities are inside the final position num_moved = 0; check = NEXT_EDICT(sv.edicts); VectorClear(testmove); // avoid compiler warning: testmove is // initialized with case 0 in the switch for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT(check)) { if (check->free) continue; if (check->v.movetype == MOVETYPE_PUSH || check->v.movetype == MOVETYPE_NONE || check->v.movetype == MOVETYPE_FOLLOW || check->v.movetype == MOVETYPE_NOCLIP) continue; // if the entity is standing on the pusher, it will definitely be moved moveit = false; ground = PROG_TO_EDICT(check->v.groundentity); if ((int)check->v.flags & FL_ONGROUND) { if (ground == pusher) { moveit = true; } else { for (i = 0; i < slaves_moved; i++) { if (ground == moved_edict[MAX_EDICTS - i - 1]) { moveit = true; break; } } } } if (!moveit) { if ( check->v.absmin[0] >= maxs[0] || check->v.absmin[1] >= maxs[1] || check->v.absmin[2] >= maxs[2] || check->v.absmax[0] <= mins[0] || check->v.absmax[1] <= mins[1] || check->v.absmax[2] <= mins[2] ) { for (i = 0; i < slaves_moved; i++) { slave = moved_edict[MAX_EDICTS - i - 1]; if ( check->v.absmin[0] >= slave->v.absmax[0] || check->v.absmin[1] >= slave->v.absmax[1] || check->v.absmin[2] >= slave->v.absmax[2] || check->v.absmax[0] <= slave->v.absmin[0] || check->v.absmax[1] <= slave->v.absmin[1] || check->v.absmax[2] <= slave->v.absmin[2] ) continue; } if (i == slaves_moved) continue; } // see if the ent's bbox is inside the pusher's final position if (!SV_TestEntityPosition (check)) continue; } // remove the onground flag for non-players if (check->v.movetype != MOVETYPE_WALK) check->v.flags = (int)check->v.flags & ~FL_ONGROUND; VectorCopy (check->v.origin, entorig); VectorCopy (check->v.origin, moved_from[num_moved]); moved_edict[num_moved] = check; num_moved++; // put check in first move spot VectorAdd (check->v.origin, move, check->v.origin); // Use center of model, like in QUAKE!!!! // Our origins are on the bottom!!! for (i = 0; i < 3; i++) check_center[i] = (check->v.absmin[i] + check->v.absmax[i]) / 2; // calculate destination position VectorSubtract (check_center, pusher->v.origin, org); // put check back VectorSubtract (check->v.origin, move, check->v.origin); org2[0] = DotProduct (org, forward); org2[1] = -DotProduct (org, right); org2[2] = DotProduct (org, up); VectorSubtract (org2, org, move2); // Con_DPrintf("%f %f %f (move2)\n", move2[0], move2[1], move2[2]); // VectorAdd (check->v.origin, move2, check->v.origin); // Add all moves together VectorAdd(move,move2,move3); // Find the angle of rotation as compared to vector from pusher // origin to check center // turn_away = DotProduct(org,a); // try moving the contacted entity for (t = 0; t < 13; t++) { switch (t) { case 0: //try x, y and z VectorCopy(move3,testmove); break; case 1: //Try xy only VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = move3[0]; testmove[1] = move3[1]; testmove[2] = 0; break; case 2: //Try z only VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = 0; testmove[1] = 0; testmove[2] = move3[2]; break; case 3: //Try none VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = 0; testmove[1] = 0; testmove[2] = 0; break; case 4: //Try xy in opposite dir testmove[0] = move3[0] * -1; testmove[1] = move3[1] * -1; testmove[2] = move3[2]; break; case 5: //Try z in opposite dir VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = move3[0]; testmove[1] = move3[1]; testmove[2] = move3[2] * -1; break; case 6: //Try xyz in opposite dir VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = move3[0] * -1; testmove[1] = move3[1] * -1; testmove[2] = move3[2] * -1; break; case 7: //Try move3 times 2 VectorSubtract(check->v.origin,testmove,check->v.origin); VectorScale(move3,2,testmove); break; case 8: //Try normalized org VectorSubtract(check->v.origin,testmove,check->v.origin); // VectorCopy(amove,amove_norm); // amove_mag = VectorNormalize(amove_norm); // //VectorNormalize(org); // VectorScale(org,amove_mag,org); // VectorNormalize(org); VectorScale(org,movetime,org);//movetime*20? VectorCopy(org,testmove); break; case 9: //Try normalized org z * 3 only VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = 0; testmove[1] = 0; testmove[2] = org[2] * 3;//was: +org[2]*(fabs(org[1])+fabs(org[2])); break; case 10: //Try normalized org xy * 2 only VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = org[0] * 2;//was: +org[0]*fabs(org[2]); testmove[1] = org[1] * 2;//was: +org[1]*fabs(org[2]); testmove[2] = 0; break; case 11: //Try xy in opposite org dir VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = org[0] * -2; testmove[1] = org[1] * -2; testmove[2] = org[2]; break; case 12: //Try z in opposite dir VectorSubtract(check->v.origin,testmove,check->v.origin); testmove[0] = org[0]; testmove[1] = org[1]; testmove[2] = org[2] * -3; break; } if (t != 3) { //THIS IS VERY BAD BAD HACK... pusher->v.solid = SOLID_NOT; SV_PushEntity (check, move3); //@@TODO: do we ever want to do anybody's angles? maybe just yaw??? // if (!((int)check->v.flags & (FL_CLIENT | FL_MONSTER))) // VectorAdd (check->v.angles, amove, check->v.angles); check->v.angles[YAW] += amove[YAW]; pusher->v.solid = SOLID_BSP; } // if it is still inside the pusher, block block = SV_TestEntityPosition (check); if (!block) break; } // Con_DPrintf("t: %i\n",t); // if (turn_away > 0) // { if (block) { // fail the move // Con_DPrintf("Check blocked\n"); if (check->v.mins[0] == check->v.maxs[0]) continue; if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER) { // corpse check->v.mins[0] = check->v.mins[1] = 0; VectorCopy (check->v.mins, check->v.maxs); continue; } VectorCopy (entorig, check->v.origin); SV_LinkEdict (check, true); VectorCopy (pushorig, pusher->v.origin); VectorCopy (pushorigangles, pusher->v.angles); SV_LinkEdict (pusher, false); pusher->v.ltime -= movetime; for (i = 0; i < slaves_moved; i++) { slave = moved_edict[MAX_EDICTS - i - 1]; VectorCopy (moved_from[MAX_EDICTS - i - 1], slave->v.angles); SV_LinkEdict (slave, false); slave->v.ltime -= movetime; } // if the pusher has a "blocked" function, call it // otherwise, just stay in place until the obstacle is gone if (pusher->v.blocked) { *sv_globals.self = EDICT_TO_PROG(pusher); *sv_globals.other = EDICT_TO_PROG(check); PR_ExecuteProgram (pusher->v.blocked); } // move back any entities we already moved for (i = 0; i < num_moved; i++) { VectorCopy (moved_from[i], moved_edict[i]->v.origin); //@@TODO:: see above // if (!((int)moved_edict[i]->v.flags & (FL_CLIENT | FL_MONSTER))) // VectorSubtract (moved_edict[i]->v.angles, amove, moved_edict[i]->v.angles); moved_edict[i]->v.angles[YAW] -= amove[YAW]; SV_LinkEdict (moved_edict[i], false); } return; } // } // else if (block) // undo last move // VectorCopy (entorig, check->v.origin); } # if 0 Con_DPrintf("result:\n"); Con_DPrintf("%f %f %f\n", pusher->v.angles[0], pusher->v.angles[1], pusher->v.angles[2]); for (i = 0; i < slaves_moved; i++) { slave = moved_edict[MAX_EDICTS - i - 1]; Con_DPrintf("%f %f %f slave entity %i\n", slave->v.angles[0], slave->v.angles[1], slave->v.angles[2], NUM_FOR_EDICT(slave)); } Con_DPrintf("\n"); # endif } /* ================ SV_Physics_Pusher ================ */ static void SV_Physics_Pusher (edict_t *ent) { float thinktime; float oldltime; float movetime; vec3_t oldorg, move; float l; oldltime = ent->v.ltime; thinktime = ent->v.nextthink; if (thinktime < ent->v.ltime + host_frametime) { movetime = thinktime - ent->v.ltime; if (movetime < 0) movetime = 0; } else movetime = host_frametime; if (movetime) { if (ent->v.avelocity[0] || ent->v.avelocity[1] || ent->v.avelocity[2]) { //SV_PushMove (ent, movetime, false); SV_PushRotate (ent, movetime); } else SV_PushMove (ent, movetime, true); // advances ent->v.ltime if not blocked } #ifdef PLATFORM_AMIGAOS3 // SV_SpawnServer race condition workaround for meso2 and romeric5 if (thinktime > oldltime && (thinktime - 0.00000001) <= ent->v.ltime) #else if (thinktime > oldltime && thinktime <= ent->v.ltime) #endif { VectorCopy (ent->v.origin, oldorg); ent->v.nextthink = 0; *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(ent); *sv_globals.other = EDICT_TO_PROG(sv.edicts); PR_ExecuteProgram (ent->v.think); if (ent->free) return; VectorSubtract (ent->v.origin, oldorg, move); l = VectorLength(move); if (l > 1.0/64) { // Con_Printf ("**** snap: %f\n", l); VectorCopy (oldorg, ent->v.origin); SV_Push (ent, move); } } } /* =============================================================================== CLIENT MOVEMENT =============================================================================== */ /* ============= SV_CheckStuck This is a big hack to try and fix the rare case of getting stuck in the world clipping hull. ============= */ #if 0 static void SV_CheckStuck (edict_t *ent) { int i, j; int z; vec3_t org; if (!SV_TestEntityPosition(ent)) { VectorCopy (ent->v.origin, ent->v.oldorigin); return; } VectorCopy (ent->v.origin, org); VectorCopy (ent->v.oldorigin, ent->v.origin); if (!SV_TestEntityPosition(ent)) { Con_DPrintf ("Unstuck.\n"); SV_LinkEdict (ent, true); return; } for (z = 0; z < 18; z++) { for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { ent->v.origin[0] = org[0] + i; ent->v.origin[1] = org[1] + j; ent->v.origin[2] = org[2] + z; if (!SV_TestEntityPosition(ent)) { Con_DPrintf ("Unstuck.\n"); SV_LinkEdict (ent, true); return; } } } } VectorCopy (org, ent->v.origin); Con_DPrintf ("player is stuck.\n"); } #endif /* ============= SV_CheckWater ============= */ #if 0 static qboolean SV_CheckWater (edict_t *ent) { vec3_t point; int cont; #ifdef QUAKE2 int truecont; #endif point[0] = ent->v.origin[0]; point[1] = ent->v.origin[1]; point[2] = ent->v.origin[2] + ent->v.mins[2] + 1; ent->v.waterlevel = 0; ent->v.watertype = CONTENTS_EMPTY; cont = SV_PointContents (point); if (cont <= CONTENTS_WATER) { #ifdef QUAKE2 truecont = SV_TruePointContents (point); #endif ent->v.watertype = cont; ent->v.waterlevel = 1; point[2] = ent->v.origin[2] + (ent->v.mins[2] + ent->v.maxs[2])*0.5; cont = SV_PointContents (point); if (cont <= CONTENTS_WATER) { ent->v.waterlevel = 2; point[2] = ent->v.origin[2] + ent->v.view_ofs[2]; cont = SV_PointContents (point); if (cont <= CONTENTS_WATER) ent->v.waterlevel = 3; } #ifdef QUAKE2 if (truecont <= CONTENTS_CURRENT_0 && truecont >= CONTENTS_CURRENT_DOWN) { static vec3_t current_table[] = { {1, 0, 0}, {0, 1, 0}, {-1, 0, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1} }; VectorMA (ent->v.basevelocity, 150.0*ent->v.waterlevel/3.0, current_table[CONTENTS_CURRENT_0 - truecont], ent->v.basevelocity); } #endif } return ent->v.waterlevel > 1; } #endif /* ============ SV_WallFriction ============ */ #if 0 static void SV_WallFriction (edict_t *ent, trace_t *trace) { vec3_t forward, right, up; float d, i; vec3_t into, side; AngleVectors (ent->v.v_angle, forward, right, up); d = DotProduct (trace->plane.normal, forward); d += 0.5; if (d >= 0) return; // cut the tangential velocity i = DotProduct (trace->plane.normal, ent->v.velocity); VectorScale (trace->plane.normal, i, into); VectorSubtract (ent->v.velocity, into, side); ent->v.velocity[0] = side[0] * (1 + d); ent->v.velocity[1] = side[1] * (1 + d); } #endif /* ===================== SV_TryUnstick Player has come to a dead stop, possibly due to the problem with limited float precision at some angle joins in the BSP hull. Try fixing by pushing one pixel in each direction. This is a hack, but in the interest of good gameplay... ====================== */ #if 0 static int SV_TryUnstick (edict_t *ent, vec3_t oldvel) { int i; vec3_t oldorg; vec3_t dir; int clip; trace_t steptrace; VectorCopy (ent->v.origin, oldorg); VectorClear (dir); for (i = 0; i < 8; i++) { // try pushing a little in an axial direction switch (i) { case 0: dir[0] = 2; dir[1] = 0; break; case 1: dir[0] = 0; dir[1] = 2; break; case 2: dir[0] = -2; dir[1] = 0; break; case 3: dir[0] = 0; dir[1] = -2; break; case 4: dir[0] = 2; dir[1] = 2; break; case 5: dir[0] = -2; dir[1] = 2; break; case 6: dir[0] = 2; dir[1] = -2; break; case 7: dir[0] = -2; dir[1] = -2; break; } SV_PushEntity (ent, dir); // retry the original move ent->v.velocity[0] = oldvel[0]; ent->v. velocity[1] = oldvel[1]; ent->v. velocity[2] = 0; clip = SV_FlyMove (ent, 0.1, &steptrace); if ( fabs(oldorg[1] - ent->v.origin[1]) > 4 || fabs(oldorg[0] - ent->v.origin[0]) > 4 ) { // Con_DPrintf ("unstuck!\n"); return clip; } // go back to the original pos and try again VectorCopy (oldorg, ent->v.origin); } VectorClear (ent->v.velocity); return 7; // still not moving } #endif /* ===================== SV_WalkMove Only used by players ====================== */ #define STEPSIZE 18 #if 0 static void SV_WalkMove (edict_t *ent) { vec3_t upmove, downmove; vec3_t oldorg, oldvel; vec3_t nosteporg, nostepvel; int clip; int oldonground; trace_t steptrace, downtrace; // // do a regular slide move unless it looks like you ran into a step // oldonground = (int)ent->v.flags & FL_ONGROUND; ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; VectorCopy (ent->v.origin, oldorg); VectorCopy (ent->v.velocity, oldvel); clip = SV_FlyMove (ent, host_frametime, &steptrace); if ( !(clip & 2) ) return; // move didn't block on a step if (!oldonground && ent->v.waterlevel == 0) return; // don't stair up while jumping if (ent->v.movetype != MOVETYPE_WALK) return; // gibbed by a trigger if (sv_nostep.integer) return; if ( (int)sv_player->v.flags & FL_WATERJUMP ) return; VectorCopy (ent->v.origin, nosteporg); VectorCopy (ent->v.velocity, nostepvel); // // try moving up and forward to go up a step // VectorCopy (oldorg, ent->v.origin); // back to start pos VectorClear (upmove); VectorClear (downmove); upmove[2] = STEPSIZE; downmove[2] = -STEPSIZE + oldvel[2]*host_frametime; // move up //Con_Printf("Calling pushent\n"); SV_PushEntity (ent, upmove); // FIXME: don't link? // move forward ent->v.velocity[0] = oldvel[0]; ent->v. velocity[1] = oldvel[1]; ent->v. velocity[2] = 0; clip = SV_FlyMove (ent, host_frametime, &steptrace); // check for stuckness, possibly due to the limited precision of floats // in the clipping hulls if (clip) { if ( fabs(oldorg[1] - ent->v.origin[1]) < 0.03125 && fabs(oldorg[0] - ent->v.origin[0]) < 0.03125 ) { // stepping up didn't make any progress clip = SV_TryUnstick (ent, oldvel); } } // extra friction based on view angle if ( clip & 2 ) SV_WallFriction (ent, &steptrace); // move down downtrace = SV_PushEntity (ent, downmove); // FIXME: don't link? if (downtrace.plane.normal[2] > 0.7) { if (ent->v.solid == SOLID_BSP) { ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = EDICT_TO_PROG(downtrace.ent); } } else { // if the push down didn't end up on good ground, use the move without // the step up. This happens near wall / slope combinations, and can // cause the player to hop up higher on a slope too steep to climb VectorCopy (nosteporg, ent->v.origin); VectorCopy (nostepvel, ent->v.velocity); } } #endif /* ================ SV_Physics_Client Player character actions ================ */ #if 0 // Note: this version requires sevenal other if 0'ed out functions void SV_Physics_Client (edict_t *ent, int num) { if ( ! svs.clients[num-1].active ) return; // unconnected slot // // call standard client pre-think // *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(ent); PR_ExecuteProgram (*sv_globals.PlayerPreThink); // // do a move // SV_CheckVelocity (ent); // // decide which move function to call // switch ((int)ent->v.movetype) { case MOVETYPE_NONE: if (!SV_RunThink (ent)) return; break; case MOVETYPE_WALK: if (!SV_RunThink (ent)) return; if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) ) SV_AddGravity (ent); SV_CheckStuck (ent); #ifdef QUAKE2 VectorAdd (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); #endif SV_WalkMove (ent); #ifdef QUAKE2 VectorSubtract (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); #endif break; case MOVETYPE_TOSS: case MOVETYPE_BOUNCE: SV_Physics_Toss (ent); break; case MOVETYPE_FLY: case MOVETYPE_SWIM: if (!SV_RunThink (ent)) return; SV_CheckWater (ent); SV_FlyMove (ent, host_frametime, NULL); SV_FlyExtras (ent, host_frametime, NULL); // Hover & friction break; case MOVETYPE_NOCLIP: if (!SV_RunThink (ent)) return; VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin); break; default: Host_Error ("%s: bad movetype %i", __thisfunc__, (int)ent->v.movetype); } // // call standard player post-think // SV_LinkEdict (ent, true); *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(ent); PR_ExecuteProgram (*sv_globals.PlayerPostThink); } #else void SV_Physics_Client (edict_t *ent) { trace_t trace; //int save_hull; // save_hull = ent->v.hull; // ent->v.hull = 1; // trace = SV_Move (ent->v.oldorigin, vec_origin, vec_origin, ent->v.origin, MOVE_NOMONSTERS, ent); trace = SV_Move (ent->v.oldorigin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NOMONSTERS, ent); // ent->v.hull = save_hull; if (trace.fraction < 1.0) return; trace = SV_Move (ent->v.oldorigin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NORMAL, ent); if (ent->v.movetype != MOVETYPE_BOUNCE || (trace.allsolid == 0 && trace.startsolid == 0)) { VectorCopy (trace.endpos, ent->v.origin); } else { trace.fraction = 0; return; } if (trace.ent) SV_Impact (ent, trace.ent); return; } #endif //============================================================================ /* ============= SV_Physics_None Non moving objects can only think ============= */ static void SV_Physics_None (edict_t *ent) { // regular thinking SV_RunThink (ent); } /* ============= SV_Physics_Noclip A moving object that doesn't obey physics ============= */ static void SV_Physics_Noclip (edict_t *ent) { // regular thinking if (!SV_RunThink (ent)) return; VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles); VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin); SV_LinkEdict (ent, false); } /* ============================================================================== TOSS / BOUNCE ============================================================================== */ /* ============= SV_CheckWaterTransition ============= */ static void SV_CheckWaterTransition (edict_t *ent) { int cont; cont = SV_PointContents (ent->v.origin); if (!ent->v.watertype) { // just spawned here ent->v.watertype = cont; ent->v.waterlevel = 1; return; } if (cont <= CONTENTS_WATER) { if (ent->v.watertype == CONTENTS_EMPTY) { // just crossed into water SV_StartSound (ent, 0, "misc/hith2o.wav", 255, 1); } ent->v.watertype = cont; ent->v.waterlevel = 1; } else { if (ent->v.watertype != CONTENTS_EMPTY) { // just crossed into water SV_StartSound (ent, 0, "misc/hith2o.wav", 255, 1); } ent->v.watertype = CONTENTS_EMPTY; ent->v.waterlevel = cont; } } /* ============= SV_Physics_Toss Toss, bounce, and fly movement. When onground, do nothing. ============= */ void SV_Physics_Toss (edict_t *ent) { trace_t trace; vec3_t move; float backoff; // regular thinking if (!SV_RunThink (ent)) return; if (ent->v.velocity[2] > 0) ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; // if onground, return without moving if ( ((int)ent->v.flags & FL_ONGROUND) ) return; SV_CheckVelocity (ent); // add gravity if (ent->v.movetype != MOVETYPE_FLY && ent->v.movetype != MOVETYPE_BOUNCEMISSILE && ent->v.movetype != MOVETYPE_FLYMISSILE && ent->v.movetype != MOVETYPE_SWIM) SV_AddGravity (ent, 1.0); // move angles VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles); // move origin VectorScale (ent->v.velocity, host_frametime, move); trace = SV_PushEntity (ent, move); if (trace.fraction == 1) return; if (ent->free) return; if (ent->v.movetype == MOVETYPE_BOUNCE) backoff = 1.5; else if (ent->v.movetype == MOVETYPE_BOUNCEMISSILE) { // Solid phased missiles don't bounce on monsters or players if ((ent->v.solid == SOLID_PHASE) && (((int)trace.ent->v.flags & FL_MONSTER) || ((int)trace.ent->v.movetype == MOVETYPE_WALK))) { return; } backoff = 2.0; } else backoff = 1; ClipVelocity (ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff); // stop if on ground if ((trace.plane.normal[2] > 0.7) && (ent->v.movetype != MOVETYPE_BOUNCEMISSILE)) { if (ent->v.velocity[2] < 60 || ent->v.movetype != MOVETYPE_BOUNCE) { ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = EDICT_TO_PROG(trace.ent); VectorClear (ent->v.velocity); VectorClear (ent->v.avelocity); } } // check for in water SV_CheckWaterTransition (ent); } /* =============================================================================== STEPPING MOVEMENT =============================================================================== */ /* ============= SV_Physics_Step Monsters freefall when they don't have a ground entity, otherwise all movement is done with discrete steps. This is also used for objects that have become still on the ground, but will fall if the floor is pulled out from under them. FIXME: is this true? ============= */ static void SV_Physics_Step (edict_t *ent) { qboolean hitsound; // freefall if not onground if ( ! ((int)ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM) ) ) { if (ent->v.velocity[2] < movevars.gravity*-0.1) hitsound = true; else hitsound = false; SV_AddGravity (ent, 1.0); SV_CheckVelocity (ent); SV_FlyMove (ent, host_frametime, NULL); SV_LinkEdict (ent, true); if (((int)ent->v.flags & FL_ONGROUND) && !((int)ent->v.flags & FL_MONSTER)) { // just hit ground if (hitsound) SV_StartSound (ent, 0, "fx/thngland.wav", 255, 1); } } // regular thinking SV_RunThink (ent); SV_CheckWaterTransition (ent); } //============================================================================ void SV_ProgStartFrame (void) { // let the progs know that a new frame has started *sv_globals.self = EDICT_TO_PROG(sv.edicts); *sv_globals.other = EDICT_TO_PROG(sv.edicts); *sv_globals.time = sv.time; PR_ExecuteProgram (*sv_globals.StartFrame); } /* ================ SV_RunEntity ================ */ static void SV_RunEntity (edict_t *ent) { int c, originMoved; edict_t *ent2; vec3_t oldOrigin, oldAngle; if (ent->v.lastruntime == (float)realtime) return; ent->v.lastruntime = (float)realtime; ent2 = PROG_TO_EDICT(ent->v.movechain); if (ent2 != sv.edicts) { VectorCopy(ent->v.origin,oldOrigin); VectorCopy(ent->v.angles,oldAngle); } else { // avoid compiler warning VectorClear(oldOrigin); VectorClear(oldAngle); } switch ( (int)ent->v.movetype) { case MOVETYPE_PUSH: SV_Physics_Pusher (ent); break; case MOVETYPE_NONE: SV_Physics_None (ent); break; case MOVETYPE_NOCLIP: SV_Physics_Noclip (ent); break; case MOVETYPE_STEP: case MOVETYPE_PUSHPULL: SV_Physics_Step (ent); break; case MOVETYPE_TOSS: case MOVETYPE_BOUNCE: case MOVETYPE_BOUNCEMISSILE: case MOVETYPE_FLY: case MOVETYPE_FLYMISSILE: case MOVETYPE_SWIM: SV_Physics_Toss (ent); break; case MOVETYPE_FOLLOW: break; case MOVETYPE_WALK: SV_RunThink (ent); break; default: SV_Error ("%s: bad movetype %i", __thisfunc__, (int)ent->v.movetype); } if (ent2 != sv.edicts) { originMoved = !VectorCompare(ent->v.origin,oldOrigin); if (originMoved || !VectorCompare(ent->v.angles,oldAngle)) { VectorSubtract(ent->v.origin,oldOrigin,oldOrigin); VectorSubtract(ent->v.angles,oldAngle,oldAngle); for (c = 0; c < 10; c++) { // chain a max of 10 objects if (ent2->free) break; VectorAdd(oldOrigin,ent2->v.origin,ent2->v.origin); if ((int)ent2->v.flags & FL_MOVECHAIN_ANGLE) { VectorAdd(oldAngle,ent2->v.angles,ent2->v.angles); } if (originMoved && ent2->v.chainmoved) { // callback function *sv_globals.self = EDICT_TO_PROG(ent2); *sv_globals.other = EDICT_TO_PROG(ent); PR_ExecuteProgram(ent2->v.chainmoved); } ent2 = PROG_TO_EDICT(ent2->v.movechain); if (ent2 == sv.edicts) break; } } } } /* ================ SV_RunNewmis ================ */ void SV_RunNewmis (void) { edict_t *ent; if (! *sv_globals.newmis) return; ent = PROG_TO_EDICT(*sv_globals.newmis); host_frametime = 0.05; *sv_globals.newmis = 0; SV_RunEntity (ent); } /* ================ SV_Physics ================ */ void SV_Physics (void) { int i; edict_t *ent; static double old_time; // don't bother running a frame if sys_ticrate seconds haven't passed host_frametime = realtime - old_time; if (host_frametime < sv_mintic.value) return; if (host_frametime > sv_maxtic.value) host_frametime = sv_maxtic.value; old_time = realtime; *sv_globals.frametime = host_frametime; SV_ProgStartFrame (); // // treat each object in turn // even the world gets a chance to think // ent = sv.edicts; for (i = 0; i < sv.num_edicts; i++, ent = NEXT_EDICT(ent)) { if (ent->free) continue; if (*sv_globals.force_retouch) SV_LinkEdict (ent, true); // force retouch even for stationary if (i > 0 && i <= MAX_CLIENTS) { // SV_Physics_Client(ent); // VectorCopy (ent->v.origin,ent->v.oldorigin); continue; // clients are run directly from packets } SV_RunEntity (ent); SV_RunNewmis (); } if (*sv_globals.force_retouch) (*sv_globals.force_retouch)--; } void SV_SetMoveVars (void) { movevars.gravity = sv_gravity.value; movevars.stopspeed = sv_stopspeed.value; movevars.maxspeed = sv_maxspeed.value; movevars.spectatormaxspeed = sv_spectatormaxspeed.value; movevars.accelerate = sv_accelerate.value; movevars.airaccelerate = sv_airaccelerate.value; movevars.wateraccelerate = sv_wateraccelerate.value; movevars.friction = sv_friction.value; movevars.waterfriction = sv_waterfriction.value; movevars.entgravity = 1.0; } engine/hexenworld/server/sv_send.c000066400000000000000000000656761444734033100176440ustar00rootroot00000000000000/* * sv_send.c -- server communication module * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" unsigned int clients_multicast; #define CHAN_AUTO 0 #define CHAN_WEAPON 1 #define CHAN_VOICE 2 #define CHAN_ITEM 3 #define CHAN_BODY 4 #define PHS_OVERRIDE_R 8 /* ============================================================================= Con_Printf redirection ============================================================================= */ static char outputbuf[8000]; redirect_t sv_redirected; extern cvar_t sv_phs; extern cvar_t sv_namedistance; extern int devlog; /* ================== SV_FlushRedirect ================== */ static void SV_FlushRedirect (void) { if (sv_redirected == RD_PACKET) { unsigned char senddata[8000 + 6]; size_t siz = strlen(outputbuf) + 1; senddata[0] = 0xff; senddata[1] = 0xff; senddata[2] = 0xff; senddata[3] = 0xff; senddata[4] = A2C_PRINT; memcpy (&senddata[5], outputbuf, siz); NET_SendPacket (siz + 5, senddata, &net_from); } else if (sv_redirected == RD_CLIENT) { MSG_WriteByte (&host_client->netchan.message, svc_print); MSG_WriteByte (&host_client->netchan.message, PRINT_HIGH); MSG_WriteString (&host_client->netchan.message, outputbuf); } // clear it outputbuf[0] = 0; } /* ================== SV_BeginRedirect Send Con_Printf data to the remote client instead of the console ================== */ void SV_BeginRedirect (redirect_t rd) { sv_redirected = rd; outputbuf[0] = 0; } void SV_EndRedirect (void) { SV_FlushRedirect (); sv_redirected = RD_NONE; } /* ================ CON_Printf Prints either to the console, or, if redirection is in effect, to the relevant client. ================ */ void CON_Printf (unsigned int flags, const char *fmt, ...) { va_list argptr; char msg[MAX_PRINTMSG]; if (flags & _PRINT_DEVEL && !developer.integer) { if (devlog && sv_logfile) /* full logging */ { va_start (argptr, fmt); q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); fprintf (sv_logfile, "%s", msg); fflush (sv_logfile); } return; } va_start (argptr, fmt); q_vsnprintf (msg, sizeof(msg), fmt, argptr); va_end (argptr); if (flags & _PRINT_TERMONLY) goto _end; // add to redirected message if (sv_redirected) { if (strlen(msg) + strlen(outputbuf) > sizeof(outputbuf) - 1) SV_FlushRedirect (); q_strlcat (outputbuf, msg, sizeof(outputbuf)); return; } _end: Sys_PrintTerm (msg); // echo to the terminal if (sv_logfile) { fprintf (sv_logfile, "%s", msg); fflush (sv_logfile); } } /* ============================================================================= EVENT MESSAGES ============================================================================= */ /* ================= SV_ClientPrintf Sends text across to be displayed if the level passes ================= */ void SV_ClientPrintf (client_t *cl, int level, const char *fmt, ...) { va_list argptr; char string[1024]; if (level < cl->messagelevel) return; va_start (argptr, fmt); q_vsnprintf (string, sizeof (string), fmt, argptr); va_end (argptr); MSG_WriteByte (&cl->netchan.message, svc_print); MSG_WriteByte (&cl->netchan.message, level); MSG_WriteString (&cl->netchan.message, string); } /* ================= SV_BroadcastPrintf Sends text to all active clients ================= */ void SV_BroadcastPrintf (int level, const char *fmt, ...) { va_list argptr; char string[1024]; client_t *cl; int i; va_start (argptr, fmt); q_vsnprintf (string, sizeof(string), fmt, argptr); va_end (argptr); Sys_Printf ("%s", string); // print to the console for (i = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) { if (level < cl->messagelevel) continue; if (!cl->state) continue; MSG_WriteByte (&cl->netchan.message, svc_print); MSG_WriteByte (&cl->netchan.message, level); MSG_WriteString (&cl->netchan.message, string); } } /* ================= SV_BroadcastCommand Sends text to all active clients ================= */ void SV_BroadcastCommand (const char *fmt, ...) { va_list argptr; char string[1024]; if (!sv.state) return; va_start (argptr, fmt); q_vsnprintf (string, sizeof (string), fmt, argptr); va_end (argptr); MSG_WriteByte (&sv.reliable_datagram, svc_stufftext); MSG_WriteString (&sv.reliable_datagram, string); } /* ================= SV_Multicast Sends the contents of sv.multicast to a subset of the clients, then clears sv.multicast. MULTICAST_ALL same as broadcast MULTICAST_PVS send to clients potentially visible from org MULTICAST_PHS send to clients potentially hearable from org ================= */ void SV_Multicast (vec3_t origin, int to) { client_t *client; byte *mask; mleaf_t *leaf; int leafnum; int j; qboolean reliable; vec3_t adjust_origin; clients_multicast = 0; leaf = Mod_PointInLeaf (origin, sv.worldmodel); if (!leaf) leafnum = 0; else leafnum = leaf - sv.worldmodel->leafs; reliable = false; switch (to) { case MULTICAST_ALL_R: reliable = true; // intentional fallthrough case MULTICAST_ALL: mask = sv.pvs; // leaf 0 is everything; break; case MULTICAST_PHS_R: reliable = true; // intentional fallthrough case MULTICAST_PHS: mask = sv.phs + leafnum * 4 * ((sv.worldmodel->numleafs + 31) >> 5); break; case MULTICAST_PVS_R: reliable = true; // intentional fallthrough case MULTICAST_PVS: mask = sv.pvs + leafnum * 4 * ((sv.worldmodel->numleafs + 31) >> 5); break; default: mask = NULL; SV_Error ("%s: bad to: %i", __thisfunc__, to); } // send the data to all relevent clients for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { if (client->state != cs_spawned) continue; VectorCopy(client->edict->v.origin, adjust_origin); adjust_origin[2] += 16; leaf = Mod_PointInLeaf (adjust_origin, sv.worldmodel); if (leaf)// && leaf != sv.worldmodel->leafs) { // -1 is because pvs rows are 1 based, not 0 based like leafs leafnum = leaf - sv.worldmodel->leafs - 1; if ( !(mask[leafnum>>3] & (1 << (leafnum & 7)) )) { // Con_Printf ("suppressed multicast\n"); if (mask == sv.pvs) Sys_Printf("suppressed multicast to all!!!\n"); continue; } } clients_multicast |= 1l << j; if (reliable) SZ_Write (&client->netchan.message, sv.multicast.data, sv.multicast.cursize); else SZ_Write (&client->datagram, sv.multicast.data, sv.multicast.cursize); } SZ_Clear (&sv.multicast); } /* ================= SV_MulticastSpecific Sends the contents of sv.multicast to a subset of the clients, then clears sv.multicast. ================= */ void SV_MulticastSpecific (unsigned int clients, qboolean reliable) { client_t *client; int j; clients_multicast = 0; // send the data to all relevent clients for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { if (client->state != cs_spawned) continue; if ((1l << j) & clients) { clients_multicast |= 1l << j; if (reliable) SZ_Write (&client->netchan.message, sv.multicast.data, sv.multicast.cursize); else SZ_Write (&client->datagram, sv.multicast.data, sv.multicast.cursize); } } SZ_Clear (&sv.multicast); } /* ================== SV_StopSound ================== */ void SV_StopSound (edict_t *entity, int channel) { int ent, i; vec3_t origin; ent = NUM_FOR_EDICT(entity); channel = (ent<<3) | channel; // use the entity origin unless it is a bmodel if (entity->v.solid == SOLID_BSP) { for (i = 0; i < 3; i++) //FIXME: This may not work- should be using (absmin + absmax)*0.5? origin[i] = entity->v.origin[i] + 0.5 * (entity->v.mins[i] + entity->v.maxs[i]); } else { VectorCopy (entity->v.origin, origin); } MSG_WriteByte (&sv.multicast, svc_stopsound); MSG_WriteShort (&sv.multicast, channel); SV_Multicast (origin, MULTICAST_ALL_R); } /* ================== SV_UpdateSoundPos ================== */ void SV_UpdateSoundPos (edict_t *entity, int channel) { int ent, i; vec3_t origin; ent = NUM_FOR_EDICT(entity); channel = (ent<<3) | channel; // use the entity origin unless it is a bmodel if (entity->v.solid == SOLID_BSP) { for (i = 0; i < 3; i++) //FIXME: This may not work- should be using (absmin + absmax)*0.5? origin[i] = entity->v.origin[i] + 0.5 * (entity->v.mins[i] + entity->v.maxs[i]); } else { VectorCopy (entity->v.origin, origin); } MSG_WriteByte (&sv.multicast, svc_sound_update_pos); MSG_WriteShort (&sv.multicast, channel); for (i = 0; i < 3; i++) MSG_WriteCoord (&sv.multicast, entity->v.origin[i] + 0.5 * (entity->v.mins[i] + entity->v.maxs[i])); SV_Multicast (origin, MULTICAST_PHS); } /* ================== SV_StartSound Each entity can have eight independant sound sources, like voice, weapon, feet, etc. Channel 0 is an auto-allocate channel, the others override anything already running on that entity/channel pair. An attenuation of 0 will play full volume everywhere in the level. Larger attenuations will drop off. (max 4 attenuation) ================== */ void SV_StartSound (edict_t *entity, int channel, const char *sample, int volume, float attenuation) { int sound_num, ent; int i; vec3_t origin; qboolean use_phs; qboolean reliable = false; if (volume < 0 || volume > 255) SV_Error ("%s: volume = %i", __thisfunc__, volume); if (attenuation < 0 || attenuation > 4) SV_Error ("%s: attenuation = %f", __thisfunc__, attenuation); if (channel < 0 || channel > 15) SV_Error ("%s: channel = %i", __thisfunc__, channel); // find precache number for sound for (sound_num = 0; sound_num < MAX_SOUNDS && sv.sound_precache[sound_num]; sound_num++) { if (!strcmp(sample, sv.sound_precache[sound_num])) break; } if (sound_num == MAX_SOUNDS || !sv.sound_precache[sound_num]) { Con_Printf ("%s: %s not precached\n", __thisfunc__, sample); return; } ent = NUM_FOR_EDICT(entity); if ((channel & PHS_OVERRIDE_R) || !sv_phs.integer) // no PHS flag { if (channel & PHS_OVERRIDE_R) //PHS_OVERRIDE_R = 8 reliable = true; // sounds that break the phs are reliable use_phs = false; channel &= 7; //clear out the PHS_OVERRIDE_R flag } else use_phs = true; // if (channel == CHAN_BODY || channel == CHAN_VOICE) // reliable = true; channel = (ent<<3) | channel; if (volume != DEFAULT_SOUND_PACKET_VOLUME) channel |= SND_VOLUME; if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION) channel |= SND_ATTENUATION; // use the entity origin unless it is a bmodel if (entity->v.solid == SOLID_BSP) { for (i = 0; i < 3; i++) //FIXME: This may not work- should be using (absmin + absmax)*0.5? origin[i] = entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i]); } else { VectorCopy (entity->v.origin, origin); } MSG_WriteByte (&sv.multicast, svc_sound); MSG_WriteShort (&sv.multicast, channel); if (channel & SND_VOLUME) MSG_WriteByte (&sv.multicast, volume); if (channel & SND_ATTENUATION) MSG_WriteByte (&sv.multicast, attenuation*32); MSG_WriteByte (&sv.multicast, sound_num); for (i = 0; i < 3; i++) MSG_WriteCoord (&sv.multicast, origin[i]); if (use_phs) SV_Multicast (origin, reliable ? MULTICAST_PHS_R : MULTICAST_PHS); else SV_Multicast (origin, reliable ? MULTICAST_ALL_R : MULTICAST_ALL); } /* ================== SV_StartParticle Make sure the event gets sent to all clients ================== */ void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count) { int i, v; MSG_WriteByte (&sv.multicast, svc_particle); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); MSG_WriteCoord (&sv.multicast, org[2]); for (i = 0; i < 3; i++) { v = dir[i] * 16; if (v > 127) v = 127; else if (v < -128) v = -128; MSG_WriteChar (&sv.multicast, v); } MSG_WriteByte (&sv.multicast, count); MSG_WriteByte (&sv.multicast, color); SV_Multicast (org, MULTICAST_PVS); } /* ================== SV_StartParticle2 Make sure the event gets sent to all clients ================== */ void SV_StartParticle2 (vec3_t org, vec3_t dmin, vec3_t dmax, int color, int effect, int count) { MSG_WriteByte (&sv.multicast, svc_particle2); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); MSG_WriteCoord (&sv.multicast, org[2]); MSG_WriteFloat (&sv.multicast, dmin[0]); MSG_WriteFloat (&sv.multicast, dmin[1]); MSG_WriteFloat (&sv.multicast, dmin[2]); MSG_WriteFloat (&sv.multicast, dmax[0]); MSG_WriteFloat (&sv.multicast, dmax[1]); MSG_WriteFloat (&sv.multicast, dmax[2]); MSG_WriteShort (&sv.multicast, color); MSG_WriteByte (&sv.multicast, count); MSG_WriteByte (&sv.multicast, effect); SV_Multicast (org, MULTICAST_PVS); } /* ================== SV_StartParticle3 Make sure the event gets sent to all clients ================== */ void SV_StartParticle3 (vec3_t org, vec3_t box, int color, int effect, int count) { MSG_WriteByte (&sv.multicast, svc_particle3); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); MSG_WriteCoord (&sv.multicast, org[2]); MSG_WriteByte (&sv.multicast, box[0]); MSG_WriteByte (&sv.multicast, box[1]); MSG_WriteByte (&sv.multicast, box[2]); MSG_WriteShort (&sv.multicast, color); MSG_WriteByte (&sv.multicast, count); MSG_WriteByte (&sv.multicast, effect); SV_Multicast (org, MULTICAST_PVS); } /* ================== SV_StartParticle4 Make sure the event gets sent to all clients ================== */ void SV_StartParticle4 (vec3_t org, float radius, int color, int effect, int count) { MSG_WriteByte (&sv.multicast, svc_particle4); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); MSG_WriteCoord (&sv.multicast, org[2]); MSG_WriteByte (&sv.multicast, radius); MSG_WriteShort (&sv.multicast, color); MSG_WriteByte (&sv.multicast, count); MSG_WriteByte (&sv.multicast, effect); SV_Multicast (org, MULTICAST_PVS); } void SV_StartRainEffect (vec3_t org, vec3_t e_size, int x_dir, int y_dir, int color, int count) { MSG_WriteByte (&sv.multicast, svc_raineffect); MSG_WriteCoord (&sv.multicast, org[0]); MSG_WriteCoord (&sv.multicast, org[1]); MSG_WriteCoord (&sv.multicast, org[2]); MSG_WriteCoord (&sv.multicast, e_size[0]); MSG_WriteCoord (&sv.multicast, e_size[1]); MSG_WriteCoord (&sv.multicast, e_size[2]); MSG_WriteAngle (&sv.multicast, x_dir); MSG_WriteAngle (&sv.multicast, y_dir); MSG_WriteShort (&sv.multicast, color); MSG_WriteShort (&sv.multicast, count); SV_Multicast (org, MULTICAST_PVS); } /* =============================================================================== FRAME UPDATES =============================================================================== */ //int sv_nailmodel, sv_supernailmodel, sv_playermodel[MAX_PLAYER_CLASS]; int sv_magicmissmodel, sv_playermodel[MAX_PLAYER_CLASS]; int sv_ravenmodel, sv_raven2model; void SV_FindModelNumbers (void) { int i; // sv_nailmodel = -1; // sv_supernailmodel = -1; sv_playermodel[0] = -1; sv_playermodel[1] = -1; sv_playermodel[2] = -1; sv_playermodel[3] = -1; sv_playermodel[4] = -1; sv_magicmissmodel = -1; sv_ravenmodel = -1; sv_raven2model = -1; for (i = 0; i < MAX_MODELS; i++) { if (!sv.model_precache[i]) break; // if (!strcmp(sv.model_precache[i],"progs/spike.mdl")) // sv_nailmodel = i; // if (!strcmp(sv.model_precache[i],"progs/s_spike.mdl")) // sv_supernailmodel = i; if (!strcmp(sv.model_precache[i],"models/paladin.mdl")) sv_playermodel[0] = i; if (!strcmp(sv.model_precache[i],"models/crusader.mdl")) sv_playermodel[1] = i; if (!strcmp(sv.model_precache[i],"models/necro.mdl")) sv_playermodel[2] = i; if (!strcmp(sv.model_precache[i],"models/assassin.mdl")) sv_playermodel[3] = i; if (!strcmp(sv.model_precache[i],"models/succubus.mdl")) sv_playermodel[4] = i; if (!strcmp(sv.model_precache[i],"models/ball.mdl")) sv_magicmissmodel = i; if (!strcmp(sv.model_precache[i],"models/ravproj.mdl")) sv_ravenmodel = i; if (!strcmp(sv.model_precache[i],"models/vindsht1.mdl")) sv_raven2model = i; } } /* ================== SV_WriteClientdataToMessage ================== */ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg) { int i; edict_t *other; edict_t *ent; ent = client->edict; // send the chokecount for r_netgraph if (client->chokecount) { MSG_WriteByte (msg, svc_chokecount); MSG_WriteByte (msg, client->chokecount); client->chokecount = 0; } // send a damage message if the player got hit this frame if (ent->v.dmg_take || ent->v.dmg_save) { other = PROG_TO_EDICT(ent->v.dmg_inflictor); MSG_WriteByte (msg, svc_damage); MSG_WriteByte (msg, ent->v.dmg_save); MSG_WriteByte (msg, ent->v.dmg_take); for (i = 0; i < 3; i++) MSG_WriteCoord (msg, other->v.origin[i] + 0.5 * (other->v.mins[i] + other->v.maxs[i])); ent->v.dmg_take = 0; ent->v.dmg_save = 0; } // a fixangle might get lost in a dropped packet. Oh well. if ( ent->v.fixangle ) { MSG_WriteByte (msg, svc_setangle); for (i = 0; i < 3; i++) MSG_WriteAngle (msg, ent->v.angles[i]); ent->v.fixangle = 0; } // if the player has a target, send its info... if (ent->v.targDist) { MSG_WriteByte(msg, svc_targetupdate); MSG_WriteByte(msg, ent->v.targAng); MSG_WriteByte(msg, ent->v.targPitch); MSG_WriteByte(msg, (ent->v.targDist < 255.0) ? (int)(ent->v.targDist) : 255); } } /* ======================= SV_UpdateClientStats Performs a delta update of the stats array. This should only be performed when a reliable message can be delivered this frame. ======================= */ static void SV_UpdateClientStats (client_t *client) { edict_t *ent; int stats[MAX_CL_STATS]; int i; ent = client->edict; memset (stats, 0, sizeof(stats)); // if we are a spectator and we are tracking a player, we get his stats // so our status bar reflects his if (client->spectator && client->spec_track > 0) ent = svs.clients[client->spec_track - 1].edict; stats[STAT_HEALTH] = 0; //ent->v.health; stats[STAT_WEAPON] = SV_ModelIndex(PR_GetString(ent->v.weaponmodel)); stats[STAT_AMMO] = 0; //ent->v.currentammo; stats[STAT_ARMOR] = 0; //ent->v.armorvalue; stats[STAT_SHELLS] = 0; //ent->v.ammo_shells; stats[STAT_NAILS] = 0; //ent->v.ammo_nails; stats[STAT_ROCKETS] = 0;//ent->v.ammo_rockets; stats[STAT_CELLS] = 0; //ent->v.ammo_cells; if (!client->spectator) stats[STAT_ACTIVEWEAPON] = 0; //ent->v.weapon; // stuff the sigil bits into the high bits of items for sbar stats[STAT_ITEMS] = 0; //(int)ent->v.items | ((int)*sv_globals.serverflags << 28); for (i = 0; i < MAX_CL_STATS; i++) { if (stats[i] != client->stats[i]) { client->stats[i] = stats[i]; if (stats[i] >=0 && stats[i] <= 255) { MSG_WriteByte (&client->netchan.message, svc_updatestat); MSG_WriteByte (&client->netchan.message, i); MSG_WriteByte (&client->netchan.message, stats[i]); } else { MSG_WriteByte (&client->netchan.message, svc_updatestatlong); MSG_WriteByte (&client->netchan.message, i); MSG_WriteLong (&client->netchan.message, stats[i]); } } } } /* ======================= SV_SendClientDatagram ======================= */ static qboolean SV_SendClientDatagram (client_t *client) { byte buf[MAX_DATAGRAM]; sizebuf_t msg; SZ_Init (&msg, buf, sizeof(buf)); msg.allowoverflow = true; // add the client specific data to the datagram SV_WriteClientdataToMessage (client, &msg); // send over all the objects that are in the PVS // this will include clients, a packetentities, and // possibly a nails update SV_WriteEntitiesToClient (client, &msg); // copy the accumulated multicast datagram // for this client out to the message if (client->datagram.overflowed) Con_Printf ("WARNING: datagram overflowed for %s\n", client->name); else SZ_Write (&msg, client->datagram.data, client->datagram.cursize); SZ_Clear (&client->datagram); // send deltas over reliable stream if (Netchan_CanReliable (&client->netchan)) SV_UpdateClientStats (client); if (msg.overflowed) { Con_Printf ("WARNING: msg overflowed for %s\n", client->name); SZ_Clear (&msg); } // send the datagram Netchan_Transmit (&client->netchan, msg.cursize, buf); return true; } static qboolean ValidToShowName(edict_t *edict) { if (edict->v.deadflag) return false; if ((int)edict->v.effects & EF_NODRAW) return false; return true; } static void UpdatePIV(void) { int i, j; client_t *client; trace_t trace; vec3_t adjust_org1, adjust_org2, distvec; float save_hull, dist; for (i = 0, host_client = svs.clients; i < MAX_CLIENTS; i++, host_client++) { host_client->PIV = 0; } for (i = 0, host_client = svs.clients; i < MAX_CLIENTS; i++, host_client++) { if (host_client->state != cs_spawned || host_client->spectator) continue; VectorCopy(host_client->edict->v.origin, adjust_org1); adjust_org1[2] += 24; save_hull = host_client->edict->v.hull; host_client->edict->v.hull = 0; for (j = i+1, client = host_client+1; j < MAX_CLIENTS; j++, client++) { if (client->state != cs_spawned || client->spectator) continue; VectorSubtract(client->edict->v.origin, host_client->edict->v.origin, distvec); dist = VectorNormalize(distvec); if (dist > sv_namedistance.value) { // Con_Printf("dist %f\n", dist); continue; } VectorCopy(client->edict->v.origin, adjust_org2); adjust_org2[2] += 24; trace = SV_Move (adjust_org1, vec3_origin, vec3_origin, adjust_org2, false, host_client->edict); if (trace.ent == client->edict) { //can see each other, check for invisible, dead if (ValidToShowName(client->edict)) host_client->PIV |= 1<edict)) client->PIV |= 1<edict->v.hull = save_hull; } } /* ======================= SV_UpdateToReliableMessages ======================= */ static void SV_UpdateToReliableMessages (void) { int i, j; client_t *client; eval_t *val; edict_t *ent; qboolean CheckPIV = false; // Con_Printf("%s\n", __thisfunc__); if (sv.time - sv.next_PIV_time >= 1) { sv.next_PIV_time = sv.time + 1; CheckPIV = true; UpdatePIV(); } // check for changes to be sent over the reliable streams to all clients for (i = 0, host_client = svs.clients; i < MAX_CLIENTS; i++, host_client++) { if (host_client->state != cs_spawned) continue; if (host_client->sendinfo) { host_client->sendinfo = false; SV_FullClientUpdate (host_client, &sv.reliable_datagram); } if (host_client->old_frags != host_client->edict->v.frags) { for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { if (client->state < cs_connected) continue; // Sys_Printf("%s: Updated frags for client %d to %d\n", __thisfunc__, i, j); MSG_WriteByte (&client->netchan.message, svc_updatedminfo); MSG_WriteByte (&client->netchan.message, i); MSG_WriteShort (&client->netchan.message, host_client->edict->v.frags); MSG_WriteByte (&client->netchan.message, (host_client->playerclass<<5)|((int)host_client->edict->v.level&31)); if (dmMode.integer == DM_SIEGE && SV_PROGS_HAVE_SIEGE) { MSG_WriteByte (&client->netchan.message, svc_updatesiegelosses); MSG_WriteByte (&client->netchan.message, *sv_globals.defLosses); MSG_WriteByte (&client->netchan.message, *sv_globals.attLosses); } } host_client->old_frags = host_client->edict->v.frags; } SV_WriteInventory(host_client, host_client->edict, &host_client->netchan.message); if (CheckPIV && host_client->PIV != host_client->LastPIV) { MSG_WriteByte (&host_client->netchan.message, svc_update_piv); MSG_WriteLong (&host_client->netchan.message, host_client->PIV); host_client->LastPIV = host_client->PIV; } // maxspeed/entgravity changes ent = host_client->edict; val = GetEdictFieldValue(ent, "gravity"); if (val && host_client->entgravity != val->_float) { host_client->entgravity = val->_float; MSG_WriteByte(&host_client->netchan.message, svc_entgravity); MSG_WriteFloat(&host_client->netchan.message, host_client->entgravity); } val = GetEdictFieldValue(ent, "maxspeed"); if (val && host_client->maxspeed != val->_float) { host_client->maxspeed = val->_float; MSG_WriteByte(&host_client->netchan.message, svc_maxspeed); MSG_WriteFloat(&host_client->netchan.message, host_client->maxspeed); } } if (sv.datagram.overflowed) SZ_Clear (&sv.datagram); // append the broadcast messages to each client messages for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { if (client->state < cs_connected) continue; // reliables go to all connected or spawned SZ_Write (&client->netchan.message, sv.reliable_datagram.data, sv.reliable_datagram.cursize); if (client->state != cs_spawned) continue; // datagrams only go to spawned SZ_Write (&client->datagram, sv.datagram.data, sv.datagram.cursize); } SZ_Clear (&sv.reliable_datagram); SZ_Clear (&sv.datagram); } /* ============= SV_CleanupEnts ============= */ static void SV_CleanupEnts (void) { int e; edict_t *ent; ent = NEXT_EDICT(sv.edicts); for (e = 1; e < sv.num_edicts; e++, ent = NEXT_EDICT(ent)) { ent->v.effects = (int)ent->v.effects & ~EF_MUZZLEFLASH; ent->v.wpn_sound = 0; } } /* ======================= SV_SendClientMessages ======================= */ void SV_SendClientMessages (void) { int i; client_t *c; // update frags, names, etc SV_UpdateToReliableMessages (); // build individual updates for (i = 0, c = svs.clients; i < MAX_CLIENTS; i++, c++) { if (!c->state) continue; // if the reliable message overflowed, // drop the client if (c->netchan.message.overflowed) { SZ_Clear (&c->netchan.message); SZ_Clear (&c->datagram); SV_BroadcastPrintf (PRINT_HIGH, "%s overflowed\n", c->name); Con_Printf ("WARNING: reliable overflow for %s\n", c->name); SV_DropClient (c); c->send_message = true; c->netchan.cleartime = 0; // don't choke this message } // only send messages if the client has sent one // and the bandwidth is not choked if (!c->send_message) continue; c->send_message = false; // try putting this after choke? if (!Netchan_CanPacket (&c->netchan)) { c->chokecount++; continue; // bandwidth choke } if (c->state == cs_spawned) SV_SendClientDatagram (c); else Netchan_Transmit (&c->netchan, 0, NULL); // just update reliable } // clear muzzle flashes & wpn_sound SV_CleanupEnts (); } /* ======================= SV_SendMessagesToAll FIXME: is this sequence right? ======================= */ void SV_SendMessagesToAll (void) { int i; client_t *c; for (i = 0, c = svs.clients; i < MAX_CLIENTS; i++, c++) { if (c->state) // FIXME: should this only send to active? c->send_message = true; } SV_SendClientMessages (); } engine/hexenworld/server/sv_user.c000066400000000000000000001103571444734033100176540ustar00rootroot00000000000000/* * sv_user.c -- server code for moving users * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "q_ctype.h" edict_t *sv_player; static usercmd_t cmd; static cvar_t cl_rollspeed = {"cl_rollspeed", "200", CVAR_NONE}; static cvar_t cl_rollangle = {"cl_rollangle", "2.0", CVAR_NONE}; static cvar_t sv_spectalk = {"sv_spectalk", "1", CVAR_NONE}; static cvar_t sv_allowtaunts = {"sv_allowtaunts", "1", CVAR_NONE}; extern cvar_t allow_download; extern cvar_t allow_download_skins; extern cvar_t allow_download_models; extern cvar_t allow_download_sounds; extern cvar_t allow_download_maps; extern vec3_t player_mins; extern int fp_messages, fp_persecond, fp_secondsdead; extern char fp_msg[]; /* ============================================================ USER STRINGCMD EXECUTION host_client and sv_player will be valid. ============================================================ */ /* ================ SV_New_f Sends the first message from the server to a connected client. This will be sent on the initial connection and upon each server load. ================ */ static void SV_New_f (void) { const char *gamedir; int playernum; if (host_client->state == cs_spawned) return; host_client->state = cs_connected; host_client->connection_started = realtime; // send the info about the new client to all connected clients // SV_FullClientUpdate (host_client, &sv.reliable_datagram); host_client->sendinfo = true; gamedir = Info_ValueForKey (svs.info, "*gamedir"); if (!gamedir[0]) gamedir = "hw"; // send the serverdata MSG_WriteByte (&host_client->netchan.message, svc_serverdata); MSG_WriteLong (&host_client->netchan.message, host_client->protocol); MSG_WriteLong (&host_client->netchan.message, svs.spawncount); MSG_WriteString (&host_client->netchan.message, gamedir); playernum = NUM_FOR_EDICT(host_client->edict)-1; if (host_client->spectator) playernum |= 128; MSG_WriteByte (&host_client->netchan.message, playernum); // send full levelname MSG_WriteString(&host_client->netchan.message, SV_GetLevelname ()); // send the movevars MSG_WriteFloat(&host_client->netchan.message, movevars.gravity); MSG_WriteFloat(&host_client->netchan.message, movevars.stopspeed); MSG_WriteFloat(&host_client->netchan.message, movevars.maxspeed); MSG_WriteFloat(&host_client->netchan.message, movevars.spectatormaxspeed); MSG_WriteFloat(&host_client->netchan.message, movevars.accelerate); MSG_WriteFloat(&host_client->netchan.message, movevars.airaccelerate); MSG_WriteFloat(&host_client->netchan.message, movevars.wateraccelerate); MSG_WriteFloat(&host_client->netchan.message, movevars.friction); MSG_WriteFloat(&host_client->netchan.message, movevars.waterfriction); MSG_WriteFloat(&host_client->netchan.message, movevars.entgravity); // send music MSG_WriteByte (&host_client->netchan.message, svc_cdtrack); MSG_WriteByte (&host_client->netchan.message, sv.cd_track); MSG_WriteByte (&host_client->netchan.message, svc_midi_name); MSG_WriteString (&host_client->netchan.message, sv.midi_name); // send server info string MSG_WriteByte (&host_client->netchan.message, svc_stufftext); MSG_WriteString (&host_client->netchan.message, va("fullserverinfo \"%s\"\n", svs.info) ); } /* ================== SV_Soundlist_f ================== */ static void SV_SoundlistChunks (void) { unsigned n; const char **s; if (host_client->state != cs_connected) { Con_Printf ("soundlist not valid -- already spawned\n"); return; } // handle the case of a level changing while a client was connecting if (atoi (Cmd_Argv (1)) != svs.spawncount) { Con_Printf ("SV_Soundlist_f from different level\n"); SV_New_f (); return; } n = atoi (Cmd_Argv (2)); if (n >= MAX_SOUNDS - 1) { Con_Printf ("SV_Soundlist_f: Invalid soundlist index\n"); SV_New_f (); return; } MSG_WriteByte (&host_client->netchan.message, svc_soundlist); MSG_WriteLong (&host_client->netchan.message, n); for (s = sv.sound_precache + 1 + n; n < MAX_SOUNDS - 1 && *s && host_client->netchan.message.cursize < (MAX_DATAGRAM / 2); s++, n++) { MSG_WriteString (&host_client->netchan.message, *s); } MSG_WriteByte (&host_client->netchan.message, 0); // next msg if (n < MAX_SOUNDS - 1 && *s) MSG_WriteLong (&host_client->netchan.message, n); else MSG_WriteLong (&host_client->netchan.message, 0); } static void SV_Soundlist_f (void) { int i; const char **s; if (host_client->protocol >= PROTOCOL_VERSION_EXT) { SV_SoundlistChunks(); return; } if (host_client->state != cs_connected) { Con_Printf ("soundlist not valid -- already spawned\n"); return; } // handle the case of a level changing while a client was connecting if ( atoi(Cmd_Argv(1)) != svs.spawncount ) { Con_Printf ("%s from different level\n", __thisfunc__); SV_New_f (); return; } MSG_WriteByte (&host_client->netchan.message, svc_soundlist); for (i = 1, s = sv.sound_precache + 1; i < MAX_SOUNDS && *s; s++) MSG_WriteString (&host_client->netchan.message, *s); MSG_WriteByte (&host_client->netchan.message, 0); } /* ================== SV_Modellist_f ================== */ static void SV_ModellistChunks (void) { unsigned n; const char **s; if (host_client->state != cs_connected) { Con_Printf ("modellist not valid -- already spawned\n"); return; } // handle the case of a level changing while a client was connecting if (atoi (Cmd_Argv (1)) != svs.spawncount) { Con_Printf ("SV_Modellist_f from different level\n"); SV_New_f (); return; } n = atoi (Cmd_Argv (2)); if (n >= MAX_MODELS - 1) { Con_Printf ("SV_Modellist_f: Invalid modellist index\n"); SV_New_f (); return; } MSG_WriteByte (&host_client->netchan.message, svc_modellist); MSG_WriteLong (&host_client->netchan.message, n); for (s = sv.model_precache + 1 + n; n < MAX_MODELS - 1 && *s && host_client->netchan.message.cursize < (MAX_DATAGRAM / 2); s++, n++) MSG_WriteString (&host_client->netchan.message, *s); MSG_WriteByte (&host_client->netchan.message, 0); // next msg if (n < MAX_MODELS - 1 && *s) MSG_WriteLong (&host_client->netchan.message, n); else MSG_WriteLong (&host_client->netchan.message, 0); } static void SV_Modellist_f (void) { int i; const char **s; if (host_client->protocol >= PROTOCOL_VERSION_EXT) { SV_ModellistChunks(); return; } if (host_client->state != cs_connected) { Con_Printf ("modellist not valid -- already spawned\n"); return; } // handle the case of a level changing while a client was connecting if ( atoi(Cmd_Argv(1)) != svs.spawncount ) { Con_Printf ("%s from different level\n", __thisfunc__); SV_New_f (); return; } MSG_WriteByte (&host_client->netchan.message, svc_modellist); for (i = 1, s = sv.model_precache + 1; i < MAX_MODELS && *s; s++) MSG_WriteString (&host_client->netchan.message, *s); MSG_WriteByte (&host_client->netchan.message, 0); } /* ================== SV_PreSpawn_f ================== */ static void SV_PreSpawn_f (void) { int buf; if (host_client->state != cs_connected) { Con_Printf ("prespawn not valid -- already spawned\n"); return; } // handle the case of a level changing while a client was connecting if ( atoi(Cmd_Argv(1)) != svs.spawncount ) { Con_Printf ("%s from different level\n", __thisfunc__); SV_New_f (); return; } buf = atoi(Cmd_Argv(2)); if (buf >= sv.num_signon_buffers) buf = 0; SZ_Write (&host_client->netchan.message, sv.signon_buffers[buf], sv.signon_buffer_size[buf]); buf++; if (buf == sv.num_signon_buffers) { // all done prespawning MSG_WriteByte (&host_client->netchan.message, svc_stufftext); MSG_WriteString (&host_client->netchan.message, va("cmd spawn %i\n",svs.spawncount) ); } else { // need to prespawn more MSG_WriteByte (&host_client->netchan.message, svc_stufftext); MSG_WriteString (&host_client->netchan.message, va("cmd prespawn %i %i\n", svs.spawncount, buf) ); } } /* ================== SV_Spawn_f ================== */ static void SV_Spawn_f (void) { int i; client_t *client; edict_t *ent; eval_t *val; // Con_Printf("%s\n", __thisfunc__); if (host_client->state != cs_connected) { Con_Printf ("Spawn not valid -- already spawned\n"); return; } // handle the case of a level changing while a client was connecting if ( atoi(Cmd_Argv(1)) != svs.spawncount ) { Con_Printf ("%s from different level\n", __thisfunc__); SV_New_f (); return; } // set up the edict ent = host_client->edict; memset (&ent->v, 0, progs->entityfields * 4); ent->v.colormap = NUM_FOR_EDICT(ent); if (dmMode.integer == DM_SIEGE && SV_PROGS_HAVE_SIEGE) ent->v.team = ent->v.siege_team; // FIXME else ent->v.team = 0; // FIXME ent->v.netname = PR_SetEngineString(host_client->name); //ent->v.playerclass = host_client->playerclass = ent->v.next_playerclass = host_client->next_playerclass; ent->v.has_portals = host_client->portals; host_client->entgravity = 1.0; val = GetEdictFieldValue(ent, "gravity"); if (val) val->_float = 1.0; host_client->maxspeed = sv_maxspeed.value; val = GetEdictFieldValue(ent, "maxspeed"); if (val) val->_float = sv_maxspeed.value; // send all current names, colors, and frag counts // FIXME: is this a good thing? SZ_Clear (&host_client->netchan.message); // send current status of all other players for (i = 0, client = svs.clients; i < MAX_CLIENTS; i++, client++) SV_FullClientUpdate (client, &host_client->netchan.message); // send all current light styles for (i = 0; i < MAX_LIGHTSTYLES; i++) { MSG_WriteByte (&host_client->netchan.message, svc_lightstyle); MSG_WriteByte (&host_client->netchan.message, (char)i); MSG_WriteString (&host_client->netchan.message, sv.lightstyles[i]); } // // force stats to be updated // memset (host_client->stats, 0, sizeof(host_client->stats)); MSG_WriteByte (&host_client->netchan.message, svc_updatestatlong); MSG_WriteByte (&host_client->netchan.message, STAT_TOTALSECRETS); MSG_WriteLong (&host_client->netchan.message, *sv_globals.total_secrets); MSG_WriteByte (&host_client->netchan.message, svc_updatestatlong); MSG_WriteByte (&host_client->netchan.message, STAT_TOTALMONSTERS); MSG_WriteLong (&host_client->netchan.message, *sv_globals.total_monsters); MSG_WriteByte (&host_client->netchan.message, svc_updatestatlong); MSG_WriteByte (&host_client->netchan.message, STAT_SECRETS); MSG_WriteLong (&host_client->netchan.message, *sv_globals.found_secrets); MSG_WriteByte (&host_client->netchan.message, svc_updatestatlong); MSG_WriteByte (&host_client->netchan.message, STAT_MONSTERS); MSG_WriteLong (&host_client->netchan.message, *sv_globals.killed_monsters); // get the client to check and download skins // when that is completed, a begin command will be issued MSG_WriteByte (&host_client->netchan.message, svc_stufftext); MSG_WriteString (&host_client->netchan.message, va("skins\n") ); } /* ================== SV_SpawnSpectator ================== */ static void SV_SpawnSpectator (void) { int i; edict_t *e; VectorClear (sv_player->v.origin); VectorClear (sv_player->v.view_ofs); sv_player->v.view_ofs[2] = 22; // search for an info_playerstart to spawn the spectator at for (i = MAX_CLIENTS-1; i < sv.num_edicts; i++) { e = EDICT_NUM(i); if (!strcmp(PR_GetString(e->v.classname), "info_player_start")) { VectorCopy (e->v.origin, sv_player->v.origin); return; } } } /* ================== SV_Begin_f ================== */ static void SV_Begin_f (void) { int i; host_client->state = cs_spawned; // handle the case of a level changing while a client was connecting if ( atoi(Cmd_Argv(1)) != svs.spawncount ) { Con_Printf ("%s from different level\n", __thisfunc__); SV_New_f (); return; } if (host_client->spectator) { SV_SpawnSpectator (); if (SpectatorConnect) { // copy spawn parms out of the client_t for (i = 0; i < NUM_SPAWN_PARMS; i++) sv_globals.parm[i] = host_client->spawn_parms[i]; // call the spawn function *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (SpectatorConnect); } } else { // copy spawn parms out of the client_t for (i = 0; i < NUM_SPAWN_PARMS; i++) sv_globals.parm[i] = host_client->spawn_parms[i]; host_client->send_all_v = true; // call the spawn function *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (*sv_globals.ClientConnect); // actually spawn the player *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (*sv_globals.PutClientInServer); } // clear the net statistics, because connecting gives a bogus picture host_client->netchan.frame_latency = 0; host_client->netchan.frame_rate = 0; host_client->netchan.drop_count = 0; host_client->netchan.good_count = 0; #if 0 // // send a fixangle over the reliable channel to make sure it gets there // Never send a roll angle, because savegames can catch the server // in a state where it is expecting the client to correct the angle // and it won't happen if the game was just loaded, so you wind up // with a permanent head tilt ent = EDICT_NUM( 1 + (host_client - svs.clients) ); MSG_WriteByte (&host_client->netchan.message, svc_setangle); for (i = 0; i < 2; i++) MSG_WriteAngle (&host_client->netchan.message, ent->v.angles[i] ); MSG_WriteAngle (&host_client->netchan.message, 0 ); #endif } //============================================================================= /* ================== SV_NextDownload_f ================== */ static void SV_NextDownload_f (void) { byte buffer[1024]; int r; int percent; int size; if (!host_client->download) return; r = host_client->downloadsize - host_client->downloadcount; if (r > 1024) r = 1024; r = fread (buffer, 1, r, host_client->download); MSG_WriteByte (&host_client->netchan.message, svc_download); MSG_WriteShort (&host_client->netchan.message, r); host_client->downloadcount += r; size = host_client->downloadsize; if (!size) size = 1; percent = host_client->downloadcount*100/size; MSG_WriteByte (&host_client->netchan.message, percent); SZ_Write (&host_client->netchan.message, buffer, r); if (host_client->downloadcount != host_client->downloadsize) return; fclose (host_client->download); host_client->download = NULL; } /* ================== SV_BeginDownload_f ================== */ static void SV_BeginDownload_f(void) { char name[MAX_OSPATH], *p; q_strlcpy (name, Cmd_Argv(1), sizeof(name)); // hacked by zoid to allow more conrol over download // first off, no .. or global allow check if (strstr (name, "..") || !allow_download.integer // leading dot is no good || *name == '.' // leading slash bad as well, must be in subdir || *name == '/' || *name == '\\' // next up, skin check || (strncmp(name, "skins/", 6) == 0 && !allow_download_skins.integer) // now models || (strncmp(name, "models/", 6) == 0 && !allow_download_models.integer) // now sounds || (strncmp(name, "sound/", 6) == 0 && !allow_download_sounds.integer) // now maps (note special case for maps, must not be in pak) || (strncmp(name, "maps/", 6) == 0 && !allow_download_maps.integer) // MUST be in a subdirectory || !strstr (name, "/") ) { MSG_WriteByte (&host_client->netchan.message, svc_download); MSG_WriteShort (&host_client->netchan.message, -1); MSG_WriteByte (&host_client->netchan.message, 0); return; } if (host_client->download) { fclose (host_client->download); host_client->download = NULL; } // lowercase the name (needed for case sensitive file systems) for (p = name; *p; p++) *p = (char)q_tolower(*p); host_client->downloadsize = FS_OpenFile (name, &host_client->download, NULL); host_client->downloadcount = 0; if (!host_client->download // special check for maps, if it came from a pak file, // don't allow download ZOID || (strncmp(name, "maps/", 5) == 0 && file_from_pak)) { if (host_client->download) { fclose(host_client->download); host_client->download = NULL; } Sys_Printf ("Couldn't download %s to %s\n", name, host_client->name); MSG_WriteByte (&host_client->netchan.message, svc_download); MSG_WriteShort (&host_client->netchan.message, -1); MSG_WriteByte (&host_client->netchan.message, 0); return; } SV_NextDownload_f (); Sys_Printf ("Downloading %s to %s\n", name, host_client->name); } //============================================================================= /* ================== SV_Say ================== */ static void SV_Say (qboolean team) { client_t *client; int j = 0, tmp; const char *p; char text[2048]; char t1[32]; const char *t2; int speaknum = -1; if (Cmd_Argc () < 2) return; if (team) q_strlcpy (t1, Info_ValueForKey(host_client->userinfo, "team"), sizeof(t1)); if (host_client->spectator && (!sv_spectalk.integer || team)) q_snprintf (text, sizeof(text), "[SPEC] %s: ", host_client->name); else if (team) q_snprintf (text, sizeof(text), "(%s): ", host_client->name); else q_snprintf (text, sizeof(text), "%s: ", host_client->name); if (fp_messages) { if (realtimelockedtill) { SV_ClientPrintf(host_client, PRINT_CHAT, "You can't talk for %d more seconds\n", (int) (host_client->lockedtill - realtime)); return; } tmp = host_client->whensaidhead - fp_messages + 1; if (tmp < 0) tmp = 10+tmp; if (host_client->whensaid[tmp] && (realtime-host_client->whensaid[tmp] < fp_persecond)) { host_client->lockedtill = realtime + fp_secondsdead; if (fp_msg[0]) SV_ClientPrintf(host_client, PRINT_CHAT, "FloodProt: %s\n", fp_msg); else SV_ClientPrintf(host_client, PRINT_CHAT, "FloodProt: You can't talk for %d seconds.\n", fp_secondsdead); return; } host_client->whensaidhead++; if (host_client->whensaidhead > 9) host_client->whensaidhead = 0; host_client->whensaid[host_client->whensaidhead] = realtime; } p = Cmd_Args(); if (*p == '"') { p++; j = 1; } if (p[0] == '`' && (!host_client->spectator && sv_allowtaunts.integer) ) { speaknum = atoi(&p[1]); if (speaknum <= 0 || speaknum > 255-PRINT_SOUND) { speaknum = -1; } else { text[strlen(text)-2] = '\0'; q_strlcat(text," speaks!\n", sizeof(text)); } } if (speaknum == -1) { q_strlcat(text, p, sizeof(text)); if (j == 1) // remove trailing quotes { j = strlen(text) - 1; if (text[j] == '"') text[j] = '\0'; } q_strlcat(text, "\n", sizeof(text)); } Sys_Printf ("%s", text); for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { if (client->state != cs_spawned) continue; if (host_client->spectator && !sv_spectalk.integer) if (!client->spectator) continue; if (team) { // the spectator team if (host_client->spectator) { if (!client->spectator) continue; } else { t2 = Info_ValueForKey (client->userinfo, "team"); if (dmMode.integer == DM_SIEGE && SV_PROGS_HAVE_SIEGE) { if ( (host_client->edict->v.skin == 102 && client->edict->v.skin != 102) || (client->edict->v.skin == 102 && host_client->edict->v.skin != 102)) // noteam players can team chat with each other, // cannot recieve team chat of other players continue; if (client->siege_team != host_client->siege_team) // on different teams continue; } else if (strcmp(t1, t2) || client->spectator) continue; // on different teams } } if (speaknum == -1) { if (dmMode.integer == DM_SIEGE && SV_PROGS_HAVE_SIEGE && host_client->siege_team != client->siege_team) //other team speaking SV_ClientPrintf(client, PRINT_CHAT, "%s", text); // FIXME: print siege else SV_ClientPrintf(client, PRINT_CHAT, "%s", text); } else { SV_ClientPrintf(client, PRINT_SOUND + speaknum-1, "%s", text); } } } /* ================== SV_Say_f ================== */ static void SV_Say_f(void) { SV_Say (false); } /* ================== SV_Say_Team_f ================== */ static void SV_Say_Team_f(void) { SV_Say (true); } //============================================================================ /* ================= SV_Pings_f The client is showing the scoreboard, so send new ping times for all clients ================= */ static void SV_Pings_f (void) { client_t *client; int j; for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { if (client->state != cs_spawned) continue; MSG_WriteByte (&host_client->netchan.message, svc_updateping); MSG_WriteByte (&host_client->netchan.message, j); MSG_WriteShort (&host_client->netchan.message, SV_CalcPing(client)); } } /* ================== SV_Kill_f ================== */ static void SV_Kill_f (void) { if (sv_player->v.health <= 0 && sv_player->v.deadflag != DEAD_NO) { SV_ClientPrintf (host_client, PRINT_HIGH, "Can't suicide -- already dead!\n"); return; } *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (*sv_globals.ClientKill); } /* ================= SV_Drop_f The client is going to disconnect, so remove the connection immediately ================= */ static void SV_Drop_f (void) { SV_EndRedirect (); if (!host_client->spectator) SV_BroadcastPrintf (PRINT_HIGH, "%s dropped\n", host_client->name); SV_DropClient (host_client); } /* ================= SV_PTrack_f Change the bandwidth estimate for a client ================= */ static void SV_PTrack_f (void) { int i; if (Cmd_Argc() != 2) { // turn off tracking host_client->spec_track = 0; return; } i = atoi(Cmd_Argv(1)); if (i < 0 || i >= MAX_CLIENTS || svs.clients[i].state != cs_spawned || svs.clients[i].spectator) { SV_ClientPrintf (host_client, PRINT_HIGH, "Invalid client to track\n"); host_client->spec_track = 0; return; } host_client->spec_track = i + 1; // now tracking } /* ================= SV_Rate_f Change the bandwidth estimate for a client ================= */ static void SV_Rate_f (void) { int rate; if (Cmd_Argc() != 2) { SV_ClientPrintf (host_client, PRINT_HIGH, "Current rate is %i\n", (int)(1.0/host_client->netchan.rate + 0.5)); return; } rate = atoi(Cmd_Argv(1)); if (rate < 500) rate = 500; if (rate > 10000) rate = 10000; SV_ClientPrintf (host_client, PRINT_HIGH, "Net rate set to %i\n", rate); host_client->netchan.rate = 1.0/rate; } /* ================= SV_Msg_f Change the message level for a client ================= */ static void SV_Msg_f (void) { if (Cmd_Argc() != 2) { SV_ClientPrintf (host_client, PRINT_HIGH, "Current msg level is %i\n", host_client->messagelevel); return; } host_client->messagelevel = atoi(Cmd_Argv(1)); SV_ClientPrintf (host_client, PRINT_HIGH, "Msg level set to %i\n", host_client->messagelevel); } /* ================== SV_SetInfo_f Allow clients to change userinfo ================== */ static void SV_SetInfo_f (void) { if (Cmd_Argc() == 1) { Con_Printf ("User info settings:\n"); Info_Print (host_client->userinfo); return; } if (Cmd_Argc() != 3) { Con_Printf ("usage: setinfo [ ]\n"); return; } if (Cmd_Argv(1)[0] == '*') return; // don't set priveledged values Info_SetValueForKey (host_client->userinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_INFO_STRING); q_strlcpy (host_client->name, Info_ValueForKey (host_client->userinfo, "name"), sizeof(host_client->name)); // SV_FullClientUpdate (host_client, &sv.reliable_datagram); host_client->sendinfo = true; // process any changed values SV_ExtractFromUserinfo (host_client); } /* ================== SV_ShowServerinfo_f Dumps the serverinfo info string ================== */ static void SV_ShowServerinfo_f (void) { Info_Print (svs.info); } /* =========================================================================== USER CMD EXECUTION =========================================================================== */ typedef struct { const char *name; void (*func) (void); } ucmd_t; static ucmd_t ucmds[] = { {"new", SV_New_f}, {"modellist", SV_Modellist_f}, {"soundlist", SV_Soundlist_f}, {"prespawn", SV_PreSpawn_f}, {"spawn", SV_Spawn_f}, {"begin", SV_Begin_f}, {"drop", SV_Drop_f}, {"pings", SV_Pings_f}, // issued by hand at client consoles {"rate", SV_Rate_f}, {"kill", SV_Kill_f}, {"msg", SV_Msg_f}, {"say", SV_Say_f}, {"say_team", SV_Say_Team_f}, {"setinfo", SV_SetInfo_f}, {"serverinfo", SV_ShowServerinfo_f}, {"download", SV_BeginDownload_f}, {"nextdl", SV_NextDownload_f}, {"ptrack", SV_PTrack_f}, //ZOID - used with autocam {NULL, NULL} }; /* ================== SV_ExecuteUserCommand ================== */ void SV_ExecuteUserCommand (const char *s) { ucmd_t *u; Cmd_TokenizeString (s); sv_player = host_client->edict; SV_BeginRedirect (RD_CLIENT); for (u = ucmds; u->name; u++) { if (!strcmp (Cmd_Argv(0), u->name) ) { u->func (); break; } } if (!u->name) Con_Printf ("Bad user command: %s\n", Cmd_Argv(0)); SV_EndRedirect (); } //============================================================================ /* =============== V_CalcRoll Used by view and sv_user =============== */ static float V_CalcRoll (vec3_t angles, vec3_t velocity) { vec3_t forward, right, up; float sign; float side; float value; AngleVectors (angles, forward, right, up); side = DotProduct (velocity, right); sign = side < 0 ? -1 : 1; side = fabs(side); value = cl_rollangle.value; if (side < cl_rollspeed.value) side = side * value / cl_rollspeed.value; else side = value; return side*sign; } //============================================================================ static vec3_t pmove_mins, pmove_maxs; /* ==================== AddLinksToPmove ==================== */ static void AddLinksToPmove (areanode_t *node) { link_t *l, *next; edict_t *check; int pl; int i; physent_t *pe; pl = EDICT_TO_PROG(sv_player); // touch linked edicts for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next) { next = l->next; check = EDICT_FROM_AREA(l); if (check->v.owner == pl) continue; // player's own missile if (check->v.solid == SOLID_BSP || check->v.solid == SOLID_BBOX || check->v.solid == SOLID_SLIDEBOX) { if (check == sv_player) continue; for (i = 0; i < 3; i++) { if (check->v.absmin[i] > pmove_maxs[i] || check->v.absmax[i] < pmove_mins[i]) break; } if (i != 3) continue; if (pmove.numphysent == MAX_PHYSENTS) return; pe = &pmove.physents[pmove.numphysent]; pmove.numphysent++; VectorCopy (check->v.origin, pe->origin); VectorCopy (check->v.angles, pe->angles); pe->info = NUM_FOR_EDICT(check); if (check->v.solid == SOLID_BSP) pe->model = sv.models[(int)(check->v.modelindex)]; else { pe->model = NULL; VectorCopy (check->v.mins, pe->mins); VectorCopy (check->v.maxs, pe->maxs); } } } // recurse down both sides if (node->axis == -1) return; if ( pmove_maxs[node->axis] > node->dist ) AddLinksToPmove ( node->children[0] ); if ( pmove_mins[node->axis] < node->dist ) AddLinksToPmove ( node->children[1] ); } /* ================ AddAllEntsToPmove For debugging ================ */ #if 0 static void AddAllEntsToPmove (void) { int e; edict_t *check; int i; physent_t *pe; int pl; pl = EDICT_TO_PROG(sv_player); check = NEXT_EDICT(sv.edicts); for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT(check)) { if (check->free) continue; if (check->v.owner == pl) continue; if (check->v.solid == SOLID_BSP || check->v.solid == SOLID_BBOX || check->v.solid == SOLID_SLIDEBOX) { if (check == sv_player) continue; for (i = 0; i < 3; i++) { if (check->v.absmin[i] > pmove_maxs[i] || check->v.absmax[i] < pmove_mins[i]) break; } if (i != 3) continue; pe = &pmove.physents[pmove.numphysent]; VectorCopy (check->v.origin, pe->origin); VectorCopy (check->v.angles, pe->angles); pmove.physents[pmove.numphysent].info = e; if (check->v.solid == SOLID_BSP) pe->model = sv.models[(int)(check->v.modelindex)]; else { pe->model = NULL; VectorCopy (check->v.mins, pe->mins); VectorCopy (check->v.maxs, pe->maxs); } if (++pmove.numphysent == MAX_PHYSENTS) break; } } } #endif /* =========== SV_PreRunCmd Done before running a player command. Clears the touch array =========== */ static byte playertouch[(MAX_EDICTS+7)/8]; static void SV_PreRunCmd(void) { memset(playertouch, 0, sizeof(playertouch)); } /* =========== SV_RunCmd =========== */ static void SV_RunCmd (usercmd_t *ucmd) { edict_t *ent; int i, n; int oldmsec; cmd = *ucmd; // chop up very long commands if (cmd.msec > 50) { oldmsec = ucmd->msec; cmd.msec = oldmsec/2; SV_RunCmd (&cmd); cmd.msec = oldmsec/2; cmd.impulse = 0; SV_RunCmd (&cmd); return; } if (!sv_player->v.fixangle) VectorCopy (ucmd->angles, sv_player->v.v_angle); sv_player->v.button0 = ucmd->buttons & 1; sv_player->v.button2 = (ucmd->buttons & 2)>>1; if (ucmd->buttons & 4 || sv_player->v.playerclass == CLASS_DWARF) // crouched? sv_player->v.flags2 = ((int)sv_player->v.flags2) | FL2_CROUCHED; else sv_player->v.flags2 = ((int)sv_player->v.flags2) & (~FL2_CROUCHED); if (ucmd->impulse) sv_player->v.impulse = ucmd->impulse; // // angles // show 1/3 the pitch angle and all the roll angle if (sv_player->v.health > 0) { if (!sv_player->v.fixangle) { sv_player->v.angles[PITCH] = -sv_player->v.v_angle[PITCH]/3; sv_player->v.angles[YAW] = sv_player->v.v_angle[YAW]; } sv_player->v.angles[ROLL] = V_CalcRoll (sv_player->v.angles, sv_player->v.velocity)*4; } host_frametime = ucmd->msec * 0.001; if (host_frametime > HX_FRAME_TIME) host_frametime = HX_FRAME_TIME; if (!host_client->spectator) { *sv_globals.frametime = host_frametime; *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (*sv_globals.PlayerPreThink); SV_RunThink (sv_player); } for (i = 0; i < 3; i++) pmove.origin[i] = sv_player->v.origin[i] + (sv_player->v.mins[i] - player_mins[i]); VectorCopy (sv_player->v.velocity, pmove.velocity); VectorCopy (sv_player->v.v_angle, pmove.angles); pmove.spectator = host_client->spectator; // pmove.waterjumptime = sv_player->v.teleport_time; pmove.numphysent = 1; pmove.physents[0].model = sv.worldmodel; pmove.cmd = *ucmd; pmove.dead = sv_player->v.health <= 0; pmove.oldbuttons = host_client->oldbuttons; pmove.hasted = sv_player->v.hasted; pmove.movetype = sv_player->v.movetype; pmove.crouched = (sv_player->v.hull == HULL_CROUCH); pmove.teleport_time = realtime + (sv_player->v.teleport_time - sv.time); // movevars.entgravity = host_client->entgravity; movevars.entgravity = sv_player->v.gravity; movevars.maxspeed = host_client->maxspeed; for (i = 0; i < 3; i++) { pmove_mins[i] = pmove.origin[i] - 256; pmove_maxs[i] = pmove.origin[i] + 256; } #if 1 AddLinksToPmove ( sv_areanodes ); #else AddAllEntsToPmove (); #endif #if 0 { int before, after; before = PM_TestPlayerPosition (pmove.origin); PlayerMove (); after = PM_TestPlayerPosition (pmove.origin); if (sv_player->v.health > 0 && before && !after ) Con_Printf ("player %s got stuck in playermove!!!!\n", host_client->name); } #else PlayerMove (); #endif host_client->oldbuttons = pmove.oldbuttons; // sv_player->v.teleport_time = pmove.waterjumptime; sv_player->v.waterlevel = waterlevel; sv_player->v.watertype = watertype; if (onground != -1) { sv_player->v.flags = (int)sv_player->v.flags | FL_ONGROUND; sv_player->v.groundentity = EDICT_TO_PROG(EDICT_NUM(pmove.physents[onground].info)); } else sv_player->v.flags = (int)sv_player->v.flags & ~FL_ONGROUND; for (i = 0; i < 3; i++) sv_player->v.origin[i] = pmove.origin[i] - (sv_player->v.mins[i] - player_mins[i]); #if 0 // truncate velocity the same way the net protocol will for (i = 0; i < 3; i++) sv_player->v.velocity[i] = (int)pmove.velocity[i]; #else VectorCopy (pmove.velocity, sv_player->v.velocity); #endif VectorCopy (pmove.angles, sv_player->v.v_angle); if (!host_client->spectator) { // link into place and touch triggers SV_LinkEdict (sv_player, true); // touch other objects for (i = 0; i < pmove.numtouch; i++) { n = pmove.physents[pmove.touchindex[i]].info; ent = EDICT_NUM(n); // Why not just do an SV_Impact here? // SV_Impact(sv_player,ent); if (sv_player->v.touch) { *sv_globals.self = EDICT_TO_PROG(sv_player); *sv_globals.other = EDICT_TO_PROG(ent); PR_ExecuteProgram (sv_player->v.touch); } if (!ent->v.touch || (playertouch[n/8]&(1<<(n%8)))) continue; *sv_globals.self = EDICT_TO_PROG(ent); *sv_globals.other = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (ent->v.touch); playertouch[n/8] |= 1 << (n%8); } } } /* =========== SV_PostRunCmd Done after running a player command. =========== */ static void SV_PostRunCmd(void) { // run post-think if (!host_client->spectator) { *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (*sv_globals.PlayerPostThink); SV_RunNewmis (); } else if (SpectatorThink) { *sv_globals.time = sv.time; *sv_globals.self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (SpectatorThink); } } /* =================== SV_ExecuteClientMessage The current net_message is parsed for the given client =================== */ void SV_ExecuteClientMessage (client_t *cl) { int c; const char *s; usercmd_t oldest, oldcmd, newcmd; client_frame_t *frame; vec3_t o; // calc ping time frame = &cl->frames[cl->netchan.incoming_acknowledged & UPDATE_MASK]; frame->ping_time = realtime - frame->senttime; // make sure the reply sequence number matches the incoming // sequence number if (cl->netchan.incoming_sequence >= cl->netchan.outgoing_sequence) cl->netchan.outgoing_sequence = cl->netchan.incoming_sequence; else cl->send_message = false; // don't reply, sequences have slipped // save time for ping calculations cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].senttime = realtime; cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].ping_time = -1; host_client = cl; sv_player = host_client->edict; // mark time so clients will know how much to predict // other players cl->localtime = sv.time; cl->delta_sequence = -1; // no delta unless requested while (1) { if (msg_badread) { Con_Printf ("%s: badread\n", __thisfunc__); SV_DropClient (cl); return; } c = MSG_ReadByte (); if (c == -1) break; switch (c) { default: Con_Printf ("%s: unknown command char\n", __thisfunc__); SV_DropClient (cl); return; case clc_nop: break; case clc_delta: cl->delta_sequence = MSG_ReadByte (); break; case clc_move: MSG_ReadUsercmd (&oldest, false); MSG_ReadUsercmd (&oldcmd, false); MSG_ReadUsercmd (&newcmd, true); if ( cl->state != cs_spawned ) break; SV_PreRunCmd(); if (net_drop < 20) { while (net_drop > 2) { SV_RunCmd (&cl->lastcmd); net_drop--; } if (net_drop > 1) SV_RunCmd (&oldest); if (net_drop > 0) SV_RunCmd (&oldcmd); } SV_RunCmd (&newcmd); SV_PostRunCmd(); cl->lastcmd = newcmd; cl->lastcmd.buttons = 0; // avoid multiple fires on lag break; case clc_stringcmd: s = MSG_ReadString (); SV_ExecuteUserCommand (s); break; case clc_tmove: o[0] = MSG_ReadCoord(); o[1] = MSG_ReadCoord(); o[2] = MSG_ReadCoord(); // only allowed by spectators if (host_client->spectator) { VectorCopy(o, sv_player->v.origin); SV_LinkEdict(sv_player, false); } break; case clc_inv_select: cl->edict->v.inventory = MSG_ReadByte(); break; case clc_get_effect: c = MSG_ReadByte(); if (sv.Effects[c].type) { Con_Printf("Getting effect %d\n",(int)c); SV_SendEffect(&host_client->netchan.message, c); } break; } } } /* ============== SV_UserInit ============== */ void SV_UserInit (void) { Cvar_RegisterVariable (&cl_rollspeed); Cvar_RegisterVariable (&cl_rollangle); Cvar_RegisterVariable (&sv_spectalk); Cvar_RegisterVariable (&sv_allowtaunts); } engine/hexenworld/server/sys_amiga.c000066400000000000000000000345111444734033100201370ustar00rootroot00000000000000/* sys_amiga.c -- Amiga system interface code * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2012 Szilard Biro * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include #include #include #include #if defined(__LP64__) #define MIN_STACK_SIZE 0x200000 /* 2 MB stack */ #elif defined(PLATFORM_AMIGAOS3) #define MIN_STACK_SIZE 0x40000 /* 256 KB stack */ #else #define MIN_STACK_SIZE 0x100000 /* 1 MB stack */ #endif #ifdef __CLIB2__ int __stack_size = MIN_STACK_SIZE; #else int __stack = MIN_STACK_SIZE; #if defined(PLATFORM_AMIGAOS3) && defined(__libnix__) /* this pulls in swapstack.o */ /* NOTE: swapstack.o was a stray object in old versions * of libnix, need manually adding to libnix20.a */ extern void __stkinit(void); void * __x = __stkinit; #endif #endif #ifdef __AROS__ #include "incstack.h" /* The problem here is that our real main never returns: the exit * point of program is either Sys_Quit() or Sys_Error(). One way * of making the real main return to the incstack.h main wrapper * is setjmp()'ing in real main and longjmp()'ing from the actual * exit points, avoiding exit(). */ #include static jmp_buf exit_buf; static int my_rc = 0; #define HAVE_AROS_MAIN_WRAPPER #endif #define MIN_MEM_ALLOC 0x0800000 #define STD_MEM_ALLOC 0x1000000 cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; int devlog; /* log the Con_DPrintf and Sys_DPrintf content when !developer.integer */ static double starttime; static qboolean first = true; static BPTR amiga_stdin, amiga_stdout; #define MODE_RAW 1 #define MODE_NORMAL 0 struct timerequest *timerio; struct MsgPort *timerport; #if defined(__MORPHOS__) || defined(__VBCC__) struct Library *TimerBase; #else struct Device *TimerBase; #endif /* =============================================================================== FILE IO =============================================================================== */ int Sys_mkdir(const char *path, qboolean crash) { BPTR lock = CreateDir((const STRPTR) path); if (lock) { UnLock(lock); return 0; } if (IoErr() == ERROR_OBJECT_EXISTS) return 0; if (crash) Sys_Error("Unable to create directory %s", path); return -1; } int Sys_rmdir (const char *path) { if (DeleteFile((const STRPTR) path) != 0) return 0; return -1; } int Sys_unlink (const char *path) { if (DeleteFile((const STRPTR) path) != 0) return 0; return -1; } int Sys_rename (const char *oldp, const char *newp) { if (Rename((const STRPTR) oldp, (const STRPTR) newp) != 0) return 0; return -1; } long Sys_filesize (const char *path) { long size = -1; BPTR lock = Lock((const STRPTR) path, ACCESS_READ); if (lock) { struct FileInfoBlock *fib = (struct FileInfoBlock*) AllocDosObject(DOS_FIB, NULL); if (fib != NULL) { if (Examine(lock, fib)) { size = fib->fib_Size; } FreeDosObject(DOS_FIB, fib); } UnLock(lock); } return size; } int Sys_FileType (const char *path) { int type = FS_ENT_NONE; BPTR lock = Lock((const STRPTR) path, ACCESS_READ); if (lock) { struct FileInfoBlock *fib = (struct FileInfoBlock*) AllocDosObject(DOS_FIB, NULL); if (fib != NULL) { if (Examine(lock, fib)) { if (fib->fib_DirEntryType >= 0) type = FS_ENT_DIRECTORY; else type = FS_ENT_FILE; } FreeDosObject(DOS_FIB, fib); } UnLock(lock); } return type; } #define COPY_READ_BUFSIZE 8192 /* BUFSIZ */ int Sys_CopyFile (const char *frompath, const char *topath) { char buf[COPY_READ_BUFSIZE]; BPTR in, out; struct FileInfoBlock *fib; struct DateStamp stamp; LONG remaining, count; in = Open((const STRPTR) frompath, MODE_OLDFILE); if (!in) { Con_Printf ("%s: unable to open %s\n", __thisfunc__, frompath); return 1; } fib = (struct FileInfoBlock*) AllocDosObject(DOS_FIB, NULL); if (fib != NULL) { if (ExamineFH(in, fib) == 0) remaining = -1; else { remaining = fib->fib_Size; stamp = fib->fib_Date; } FreeDosObject(DOS_FIB, fib); if (remaining < 0) { Con_Printf ("%s: can't determine size for %s\n", __thisfunc__, frompath); Close(in); return 1; } } else { Con_Printf ("%s: can't allocate FileInfoBlock for %s\n", __thisfunc__, frompath); Close(in); return 1; } out = Open((const STRPTR) topath, MODE_NEWFILE); if (!out) { Con_Printf ("%s: unable to open %s\n", __thisfunc__, topath); Close(in); return 1; } while (remaining) { if (remaining < sizeof(buf)) count = remaining; else count = sizeof(buf); if (Read(in, buf, count) == -1) break; if (Write(out, buf, count) == -1) break; remaining -= count; } Close(in); Close(out); if (remaining != 0) return 1; SetFileDate(topath, &stamp); return 0; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only, not a dirent struct. this is what we presently need in this engine. ================================================= */ static struct AnchorPath apath; static BPTR oldcurrentdir; static STRPTR pattern_str; static STRPTR pattern_helper (const char *pat) { char *pdup; const char *p; int n; for (n = 0, p = pat; *p != '\0'; ++p, ++n) { if ((p = strchr (p, '*')) == NULL) break; } if (n == 0) pdup = Z_Strdup(pat); else { /* replace each "*" by "#?" */ n += (int) strlen(pat) + 1; pdup = (char *) Z_Malloc(n, Z_MAINZONE); for (n = 0, p = pat; *p != '\0'; ++p, ++n) { if (*p != '*') pdup[n] = *p; else { pdup[n] = '#'; ++n; pdup[n] = '?'; } } pdup[n] = '\0'; } return (STRPTR) pdup; } const char *Sys_FindFirstFile (const char *path, const char *pattern) { BPTR newdir; if (pattern_str) Sys_Error ("Sys_FindFirst without FindClose"); memset(&apath, 0, sizeof(apath)); newdir = Lock((const STRPTR) path, SHARED_LOCK); if (newdir) oldcurrentdir = CurrentDir(newdir); else return NULL; pattern_str = pattern_helper (pattern); if (MatchFirst((const STRPTR) pattern_str, &apath) == 0) { if (apath.ap_Info.fib_DirEntryType < 0) return (const char *) (apath.ap_Info.fib_FileName); } return Sys_FindNextFile(); } const char *Sys_FindNextFile (void) { if (!pattern_str) return NULL; while (MatchNext(&apath) == 0) { if (apath.ap_Info.fib_DirEntryType < 0) return (const char *) (apath.ap_Info.fib_FileName); } return NULL; } void Sys_FindClose (void) { if (!pattern_str) return; MatchEnd(&apath); UnLock(CurrentDir(oldcurrentdir)); oldcurrentdir = 0; Z_Free(pattern_str); pattern_str = NULL; } /* =============================================================================== SYSTEM IO =============================================================================== */ /* ================ Sys_Init ================ */ static void Sys_Init (void) { if ((timerport = CreateMsgPort())) { if ((timerio = (struct timerequest *)CreateIORequest(timerport, sizeof(struct timerequest)))) { if (OpenDevice((STRPTR) TIMERNAME, UNIT_MICROHZ, (struct IORequest *) timerio, 0) == 0) { #if defined(__MORPHOS__) || defined(__VBCC__) TimerBase = (struct Library *)timerio->tr_node.io_Device; #else TimerBase = timerio->tr_node.io_Device; #endif } else { DeleteIORequest((struct IORequest *)timerio); DeleteMsgPort(timerport); } } else { DeleteMsgPort(timerport); } } if (!TimerBase) Sys_Error("Can't open timer.device"); /* 1us wait, for timer cleanup success */ timerio->tr_node.io_Command = TR_ADDREQUEST; timerio->tr_time.tv_secs = 0; timerio->tr_time.tv_micro = 1; SendIO((struct IORequest *) timerio); WaitIO((struct IORequest *) timerio); amiga_stdout = Output(); amiga_stdin = Input(); SetMode(amiga_stdin, MODE_RAW); } static void Sys_AtExit (void) { if (amiga_stdin) SetMode(amiga_stdin, MODE_NORMAL); if (TimerBase) { /* if (!CheckIO((struct IORequest *) timerio) { AbortIO((struct IORequest *) timerio); WaitIO((struct IORequest *) timerio); } */ WaitIO((struct IORequest *) timerio); CloseDevice((struct IORequest *) timerio); DeleteIORequest((struct IORequest *) timerio); DeleteMsgPort(timerport); TimerBase = NULL; } } #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const unsigned char *p; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (sv_logfile) { fprintf (sv_logfile, ERROR_PREFIX "%s\n\n", text); fflush (sv_logfile); } for (p = (const unsigned char *) text2; *p; p++) putc (*p, stderr); for (p = (const unsigned char *) text ; *p; p++) putc (*p, stderr); putc ('\n', stderr); putc ('\n', stderr); #ifdef HAVE_AROS_MAIN_WRAPPER Sys_AtExit(); my_rc = 1; longjmp(exit_buf, 1); #else exit (1); #endif } void Sys_PrintTerm (const char *msgtxt) { const unsigned char *p; if (sys_nostdout.integer) return; for (p = (const unsigned char *) msgtxt; *p; p++) putc (*p, stdout); } void Sys_Quit (void) { #ifdef HAVE_AROS_MAIN_WRAPPER Sys_AtExit(); longjmp(exit_buf, 1); #else exit (0); #endif } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { struct timeval tp; double now; GetSysTime(&tp); now = tp.tv_secs + tp.tv_micro / 1e6; if (first) { first = false; starttime = now; return 0.0; } return now - starttime; } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; time_t t; struct tm *l; int val; if (!buf) buf = strbuf; t = time(NULL); l = localtime(&t); val = l->tm_mon + 1; /* tm_mon: months since January [0,11] */ buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = l->tm_mday; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = l->tm_year / 100 + 19; /* tm_year: #years since 1900. */ buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = l->tm_year % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = l->tm_hour; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = l->tm_min; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = l->tm_sec; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } /* ================ Sys_ConsoleInput ================ */ const char *Sys_ConsoleInput (void) { static char con_text[256]; static int textlen; char c; while (WaitForChar(amiga_stdin,10)) { Read (amiga_stdin, &c, 1); if (c == '\n' || c == '\r') { Write(amiga_stdout, "\n", 1); con_text[textlen] = '\0'; textlen = 0; return con_text; } else if (c == 8) { if (textlen) { Write(amiga_stdout, "\b \b", 3); textlen--; con_text[textlen] = '\0'; } continue; } con_text[textlen] = c; textlen++; if (textlen < (int) sizeof(con_text)) { Write(amiga_stdout, &c, 1); con_text[textlen] = '\0'; } else { // buffer is full textlen = 0; con_text[0] = '\0'; Sys_PrintTerm("\nConsole input too long!\n"); break; } } return NULL; } static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { #if 1 int len = q_strlcpy(dst, "PROGDIR:", dstsize); if (len < (int)dstsize) return 0; return -1; #else if (NameFromLock(GetProgramDir(), (STRPTR) dst, dstsize) != 0) return 0; return -1; #endif } static void PrintVersion (void) { Sys_Printf ("HexenWorld server %4.2f (%s)\n", ENGINE_VERSION, PLATFORM_STRING); Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); } /* =============================================================================== MAIN =============================================================================== */ static quakeparms_t parms; static char cwd[MAX_OSPATH]; int main (int argc, char **argv) { int i; double newtime, time, oldtime; ULONG availMem; #ifdef HAVE_AROS_MAIN_WRAPPER if (setjmp(exit_buf)) return my_rc; #endif PrintVersion(); if (argc > 1) { for (i = 1; i < argc; i++) { if ( !(strcmp(argv[i], "-v")) || !(strcmp(argv[i], "-version" )) || !(strcmp(argv[i], "--version")) ) { return 0; } else if ( !(strcmp(argv[i], "-h")) || !(strcmp(argv[i], "-help" )) || !(strcmp(argv[i], "-?")) || !(strcmp(argv[i], "--help")) ) { Sys_PrintTerm ("See the documentation for details\n"); return 0; } } } /* initialize the host params */ memset (&parms, 0, sizeof(parms)); parms.basedir = cwd; parms.userdir = cwd; parms.argc = argc; parms.argv = argv; parms.errstate = 0; host_parms = &parms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); devlog = COM_CheckParm("-devlog"); Sys_Printf("basedir is: %s\n", parms.basedir); Sys_Printf("userdir is: %s\n", parms.userdir); COM_ValidateByteorder (); availMem = AvailMem(MEMF_ANY|MEMF_LARGEST); parms.memsize = (availMem < STD_MEM_ALLOC)? MIN_MEM_ALLOC : STD_MEM_ALLOC; i = COM_CheckParm ("-heapsize"); if (i && i < com_argc-1) parms.memsize = atoi (com_argv[i+1]) * 1024; parms.membase = malloc (parms.memsize); if (!parms.membase) Sys_Error ("Insufficient memory."); #ifndef HAVE_AROS_MAIN_WRAPPER atexit (Sys_AtExit); #endif Sys_Init (); SV_Init(); // report the filesystem to the user Sys_Printf("gamedir is: %s\n", FS_GetGamedir()); Sys_Printf("userdir is: %s\n", FS_GetUserdir()); // run one frame immediately for first heartbeat SV_Frame (HX_FRAME_TIME); // // main loop // oldtime = Sys_DoubleTime () - HX_FRAME_TIME; while (1) { if (NET_CheckReadTimeout(0, 10000) == -1) continue; newtime = Sys_DoubleTime (); time = newtime - oldtime; oldtime = newtime; SV_Frame (time); } return 0; } engine/hexenworld/server/sys_dos.c000066400000000000000000000504431444734033100176500ustar00rootroot00000000000000/* sys_dos.c -- DOS system interface code. * from quake1 source with adaptations for uhexen2. * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include #include #include #include #include #include #include #include #include #include #include /* for _crt0_startup_flags */ #include #include #include #include "quakedef.h" #include "dosisms.h" #define MIN_MEM_ALLOC 0x0800000 /* minimum 8 mb */ #define STD_MEM_ALLOC 0x1000000 /* standart 16 mb */ /* 2000-07-16, DOSQuake/DJGPP mem detection fix by * Norberto Alfredo Bensa */ int _crt0_startup_flags = _CRT0_FLAG_UNIX_SBRK; int end_of_memory; static qboolean lockmem, lockunlockmem, unlockmem; static int win95; static quakeparms_t quakeparms; static int sys_checksum; /* 2000-07-28, DOSQuake "time running too fast" fix * by Norberto Alfredo Bensa. Set USE_UCLOCK_TIME * to 0 if you want to use the old original code. * See Sys_DoubleTime() for information on uclock() */ #define USE_UCLOCK_TIME 1 static void Sys_InitTime (void); #if !USE_UCLOCK_TIME static double curtime = 0.0; static double lastcurtime = 0.0; static double oldtime = 0.0; #endif /* ! USE_UCLOCK_TIME */ cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; int devlog; /* log the Con_DPrintf and Sys_DPrintf content when !developer.integer */ static int minmem; float fptest_temp; extern char start_of_memory __asm__("start"); //============================================================================= // this is totally dependent on cwsdpmi putting the stack right after tge // global data // This does evil things in a Win95 DOS box!!! #if 0 extern byte end; #define CHECKBYTE 0xed static void Sys_InitStackCheck (void) { int i; for (i = 0; i < 128*1024; i++) (&end)[i] = CHECKBYTE; } void Sys_StackCheck (void) { int i; for (i = 0; i < 128*1024; i++) { if ( (&end)[i] != CHECKBYTE ) break; } Con_Printf ("%i undisturbed stack bytes\n", i); if (end != CHECKBYTE) Sys_Error ("System stack overflow!"); } #endif //============================================================================= static void Sys_DetectWin95 (void) { __dpmi_regs r; r.x.ax = 0x160a; /* Get Windows Version */ __dpmi_int(0x2f, &r); if (r.x.ax || r.h.bh < 4) /* Not windows or earlier than Win95 */ { win95 = 0; lockmem = true; lockunlockmem = false; unlockmem = true; } else { win95 = 1; lockunlockmem = COM_CheckParm ("-winlockunlock"); if (lockunlockmem) lockmem = true; else lockmem = COM_CheckParm ("-winlock"); unlockmem = lockmem && !lockunlockmem; } } static void *dos_getmaxlockedmem (int *size) { __dpmi_free_mem_info meminfo; __dpmi_meminfo info; int working_size; void *working_memory; int last_locked; int i, j, extra, allocsize; static const char msg[] = "Locking data..."; byte *x; unsigned long ul; // first lock all the current executing image so the locked count will // be accurate. It doesn't hurt to lock the memory multiple times last_locked = __djgpp_selector_limit + 1; info.size = last_locked - 4096; info.address = __djgpp_base_address + 4096; if (lockmem) { if (__dpmi_lock_linear_region(&info)) { Sys_Error ("Lock of current memory at 0x%lx for %ldKb failed!\n", info.address, info.size / 1024); } } __dpmi_get_free_memory_information(&meminfo); if (!win95) /* Not windows or earlier than Win95 */ { ul = meminfo.maximum_locked_page_allocation_in_pages * 4096; } else { ul = meminfo.largest_available_free_block_in_bytes - LEAVE_FOR_CACHE; } if (ul > 0x7fffffff) ul = 0x7fffffff; /* limit to 2GB */ working_size = (int) ul; working_size &= ~0xffff; /* Round down to 64K */ working_size += 0x10000; do { working_size -= 0x10000; /* Decrease 64K and try again */ working_memory = sbrk(working_size); } while (working_memory == (void *)-1); extra = 0xfffc - ((unsigned)sbrk(0) & 0xffff); if (extra > 0) { sbrk(extra); working_size += extra; } // now grab the memory info.address = last_locked + __djgpp_base_address; if (!win95) { info.size = __djgpp_selector_limit + 1 - last_locked; while (info.size > 0 && __dpmi_lock_linear_region(&info)) { info.size -= 0x1000; working_size -= 0x1000; sbrk(-0x1000); } } else { /* Win95 section */ j = COM_CheckParm("-winmem"); // minmem = MIN_MEM_ALLOC; minmem = STD_MEM_ALLOC; if (j && j < com_argc - 1) { allocsize = ((int)(atoi(com_argv[j + 1]))) * 0x100000 + LOCKED_FOR_MALLOC; if (allocsize < (minmem + LOCKED_FOR_MALLOC)) allocsize = minmem + LOCKED_FOR_MALLOC; } else { allocsize = minmem + LOCKED_FOR_MALLOC; } if (!lockmem) { // we won't lock, just sbrk the memory info.size = allocsize; goto UpdateSbrk; } // lock the memory down write (STDOUT_FILENO, msg, strlen (msg)); for (j = allocsize; j > (minmem + LOCKED_FOR_MALLOC); j -= 0x100000) { info.size = j; if (!__dpmi_lock_linear_region(&info)) goto Locked; write (STDOUT_FILENO, ".", 1); } // finally, try with the absolute minimum amount for (i = 0; i < 10; i++) { info.size = minmem + LOCKED_FOR_MALLOC; if (!__dpmi_lock_linear_region(&info)) goto Locked; } Sys_Error ("Can't lock memory; %lu Mb lockable RAM required. " "Try shrinking smartdrv.", info.size / 0x100000); Locked: UpdateSbrk: info.address += info.size; info.address -= __djgpp_base_address + 4; // ending point, malloc align working_size = info.address - (int)working_memory; sbrk(info.address - (int)sbrk(0)); // negative adjustment } if (lockunlockmem) { __dpmi_unlock_linear_region (&info); printf ("Locked and unlocked %d Mb data\n", working_size / 0x100000); } else if (lockmem) { printf ("Locked %d Mb data\n", working_size / 0x100000); } else { printf ("Allocated %d Mb data\n", working_size / 0x100000); } // touch all the memory to make sure it's there. The 16-page skip is to // keep Win 95 from thinking we're trying to page ourselves in (we are // doing that, of course, but there's no reason we shouldn't) x = (byte *)working_memory; for (j = 0; j < 4; j++) { for (i = 0; i < (working_size - 16 * 0x1000); i += 4) { sys_checksum += *(int *)&x[i]; sys_checksum += *(int *)&x[i + 16 * 0x1000]; } } // give some of what we locked back for malloc before returning. Done // by cheating and passing a negative value to sbrk working_size -= LOCKED_FOR_MALLOC; sbrk( -(LOCKED_FOR_MALLOC)); *size = working_size; return working_memory; } int Sys_mkdir (const char *path, qboolean crash) { int rc = mkdir (path, 0777); if (rc != 0 && errno == EEXIST) rc = 0; if (rc != 0 && crash) Sys_Error("Unable to create directory %s", path); return rc; } int Sys_rmdir (const char *path) { return rmdir(path); } int Sys_unlink (const char *path) { return remove(path); } int Sys_rename (const char *oldp, const char *newp) { return rename(oldp, newp); } long Sys_filesize (const char *path) { struct ffblk f; if (findfirst(path, &f, FA_ARCH | FA_RDONLY) != 0) return -1; return (long) f.ff_fsize; } int Sys_FileType (const char *path) { int attr = _chmod(path, 0); /* Root directories on some non-local drives (e.g. CD-ROM) as well as devices may fail _chmod, but we are not interested in such cases. */ if (attr == -1) return FS_ENT_NONE; if (attr & _A_SUBDIR) return FS_ENT_DIRECTORY; if (attr & _A_VOLID) /* we shouldn't hit this! */ return FS_ENT_DIRECTORY; return FS_ENT_FILE; } #define COPY_READ_BUFSIZE 8192 /* BUFSIZ */ int Sys_CopyFile (const char *frompath, const char *topath) { char buf[COPY_READ_BUFSIZE]; int in, out; long remaining, count; struct ftime ft; in = open (frompath, O_RDONLY | O_BINARY); if (in < 0) { Con_Printf ("%s: unable to open %s\n", __thisfunc__, frompath); return 1; } remaining = filelength (in); if (remaining < 0) { Con_Printf ("%s: %s failed filelength()\n", __thisfunc__, frompath); close (in); return 1; } out = open (topath, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, 0666); if (out < 0) { Con_Printf ("%s: unable to create %s\n", __thisfunc__, topath); close (in); return 1; } while (remaining) { if (remaining < sizeof(buf)) count = remaining; else count = sizeof(buf); if (read(in, buf, count) < 0) break; if (write(out, buf, count) < 0) break; remaining -= count; } if (remaining == 0) { /* restore the file's timestamp */ if (getftime(in, &ft) == 0) setftime(out, &ft); } close (in); close (out); return remaining; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only. ================================================= */ static struct ffblk finddata; static int findhandle = -1; static char findstr[MAX_OSPATH]; const char *Sys_FindFirstFile (const char *path, const char *pattern) { if (findhandle == 0) Sys_Error ("Sys_FindFirst without FindClose"); q_snprintf (findstr, sizeof(findstr), "%s/%s", path, pattern); memset (&finddata, 0, sizeof(finddata)); findhandle = findfirst(findstr, &finddata, FA_ARCH | FA_RDONLY); if (findhandle == 0) return finddata.ff_name; return NULL; } const char *Sys_FindNextFile (void) { if (findhandle != 0) return NULL; if (findnext(&finddata) == 0) return finddata.ff_name; return NULL; } void Sys_FindClose (void) { findhandle = -1; } const char *Sys_ConsoleInput (void) { static char con_text[256]; static int textlen = 0; char ch; if (! kbhit()) return NULL; ch = getche(); switch (ch) { case '\r': putch('\n'); if (textlen) { con_text[textlen] = '\0'; textlen = 0; return con_text; } break; case '\b': putch(' '); if (textlen) { textlen--; putch('\b'); } break; default: con_text[textlen] = ch; textlen = (textlen + 1) & 0xff; break; } return NULL; } static void Sys_Init (void) { MaskExceptions (); Sys_SetFPCW (); #if !USE_UCLOCK_TIME dos_outportb(0x43, 0x34); // set system timer to mode 2 dos_outportb(0x40, 0); // for Sys_DoubleTime() dos_outportb(0x40, 0); #endif /* ! USE_UCLOCK_TIME */ Sys_InitTime (); _go32_interrupt_stack_size = 4 * 1024; _go32_rmcb_stack_size = 4 * 1024; } void Sys_Shutdown (void) { if (unlockmem) { dos_unlockmem (&start_of_memory, end_of_memory - (int)&start_of_memory); dos_unlockmem (quakeparms.membase, quakeparms.memsize); } } // ======================================================================= // General routines // ======================================================================= void Sys_PrintTerm (const char *msgtxt) { const unsigned char *p; if (sys_nostdout.integer) return; for (p = (const unsigned char *) msgtxt; *p; p++) putc (*p, stdout); } static void Sys_AtExit (void) { // shutdown only once (so Sys_Error can call this function to shutdown, then // print the error message, then call exit without exit calling this function // again) Sys_Shutdown(); } void Sys_Quit (void) { exit (0); } #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const unsigned char *p; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (sv_logfile) { fprintf (sv_logfile, ERROR_PREFIX "%s\n\n", text); fflush (sv_logfile); } for (p = (const unsigned char *) text2; *p; p++) putc (*p, stderr); for (p = (const unsigned char *) text ; *p; p++) putc (*p, stderr); putc ('\n', stderr); putc ('\n', stderr); // Sys_AtExit is called by exit to shutdown the system exit (1); } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { #if USE_UCLOCK_TIME /* From DJGPP uclock() man page : uclock() returns the number of uclock ticks since an arbitrary time, actually, since the first call to uclock(), which itself returns zero. The number of tics per second is UCLOCKS_PER_SEC (declared in time.h as 1193180.) uclock() is provided for very high-resulution timing. uclock_t is a 64-bit integer. It is currently accurate to better than 1 microsecond (actually about 840 nanoseconds). You cannot time across two midnights with this implementation, giving a maximum useful period of 48 hours and an effective limit of 24 hours. Casting to a 32-bit integer limits its usefulness to about an hour before 32 bits will wrap. Also note that uclock reprograms the interval timer in your PC to act as a rate generator rather than a square wave generator. I've had no problems running in this mode all the time, but if you notice strange things happening with the clock (losing time) after using uclock, check to see if this is the cause of the problem. Windows 3.X doesn't allow to reprogram the timer so the values returned by uclock() there are incorrect. DOS and Windows 9X don't have this problem. Windows NT, 2000 and XP attempt to use the rdtsc feature of newer CPUs instead of the interval timer because the timer tick and interval timer are not coordinated. During calibration the SIGILL signal handler is replaced to protect against systems which do not support or allow rdtsc. If rdtsc is available, uclock will keep the upper bits of the returned value consistent with the bios tick counter by re-calibration if needed. If rdtsc is not available, these systems fall back to interval timer usage, which may show an absolute error of 65536 uclock ticks in the values and not be monotonically increasing. */ return (double) uclock() / (double) UCLOCKS_PER_SEC; #else int r; unsigned t, tick; double ft, time; static int sametimecount; Sys_PushFPCW_SetHigh (); t = *(unsigned short*)real2ptr(0x46c) * 65536; dos_outportb(0x43, 0); // latch time r = dos_inportb(0x40); r |= dos_inportb(0x40) << 8; r = (r - 1) & 0xffff; tick = *(unsigned short*)real2ptr(0x46c) * 65536; if ((tick != t) && (r & 0x8000)) t = tick; ft = (double) (t + (65536 - r)) / 1193200.0; time = ft - oldtime; oldtime = ft; if (time < 0) { if (time > -3000.0) time = 0.0; else time += 3600.0; } curtime += time; if (curtime == lastcurtime) { sametimecount++; if (sametimecount > 100000) { curtime += 1.0; sametimecount = 0; } } else { sametimecount = 0; } lastcurtime = curtime; Sys_PopFPCW (); return curtime; #endif /* ! USE_UCLOCK_TIME */ } /* ================ Sys_InitTime ================ */ static void Sys_InitTime (void) { #if !USE_UCLOCK_TIME int j; Sys_DoubleTime (); oldtime = curtime; j = COM_CheckParm("-starttime"); if (j && j < com_argc - 1) { curtime = (double) (atof(com_argv[j+1])); } else { curtime = 0.0; } lastcurtime = curtime; #endif /* ! USE_UCLOCK_TIME */ } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; struct _dosdate_t d; struct _dostime_t t; unsigned int val; if (!buf) buf = strbuf; _dos_getdate(&d); _dos_gettime(&t); val = d.month; buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = d.day; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = d.year / 100; buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = d.year % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = t.hour; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = t.minute; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = t.second; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } /* ================ Sys_GetMemory ================ */ static void Sys_GetMemory (void) { int j, tsize; j = COM_CheckParm("-mem"); if (j && j < com_argc - 1) { quakeparms.memsize = (int) (atof(com_argv[j + 1]) * 1024 * 1024); quakeparms.membase = malloc (quakeparms.memsize); } else { quakeparms.membase = dos_getmaxlockedmem (&quakeparms.memsize); } printf("malloc'd: %d\n", quakeparms.memsize); j = COM_CheckParm ("-heapsize"); if (j && j < com_argc - 1) { tsize = atoi (com_argv[j + 1]) * 1024; if (tsize < quakeparms.memsize) quakeparms.memsize = tsize; } } /* ================ Sys_PageInProgram walks the text, data, and bss to make sure it's all paged in so that the actual physical memory detected by Sys_GetMemory is correct. ================ */ static void Sys_PageInProgram (void) { int i, j; end_of_memory = (int)sbrk(0); if (lockmem) { if (dos_lockmem ((void *)&start_of_memory, end_of_memory - (int)&start_of_memory)) Sys_Error ("Couldn't lock text and data"); } if (lockunlockmem) { dos_unlockmem((void *)&start_of_memory, end_of_memory - (int)&start_of_memory); printf ("Locked and unlocked %d Mb image\n", (end_of_memory - (int)&start_of_memory) / 0x100000); } else if (lockmem) { printf ("Locked %d Mb image\n", (end_of_memory - (int)&start_of_memory) / 0x100000); } else { printf ("Loaded %d Mb image\n", (end_of_memory - (int)&start_of_memory) / 0x100000); } // touch the entire image, doing the 16-page skip so Win95 doesn't think we're // trying to page ourselves in for (j = 0; j < 4; j++) { for (i = (int)&start_of_memory; i < (end_of_memory - 16 * 0x1000); i += 4) { sys_checksum += *(int *)i; sys_checksum += *(int *)(i + 16 * 0x1000); } } } /* ================ Sys_NoFPUExceptionHandler ================ */ static void Sys_NoFPUExceptionHandler (int whatever) { const char err[] = "\nError: Hexen II requires a floating-point processor\n"; const unsigned char *p; for (p = (const unsigned char *) err; *p; p++) putc (*p, stderr); exit (1); } /* ================ Sys_DefaultExceptionHandler ================ */ static void Sys_DefaultExceptionHandler (int whatever) { } static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { char *tmp; if (getcwd(dst, dstsize - 1) == NULL) return -1; tmp = dst; while (*tmp != 0) tmp++; while (*tmp == 0 && tmp != dst) { --tmp; if (tmp != dst && (*tmp == '/' || *tmp == '\\')) *tmp = 0; } return 0; } static void PrintVersion (void) { printf ("HexenWorld server %4.2f (%s)\n", ENGINE_VERSION, PLATFORM_STRING); printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); } /* ================ main ================ */ static char cwd[MAX_OSPATH]; int main (int argc, char **argv) { double time, oldtime, newtime; PrintVersion(); // make sure there's an FPU signal(SIGNOFP, Sys_NoFPUExceptionHandler); signal(SIGABRT, Sys_DefaultExceptionHandler); signal(SIGALRM, Sys_DefaultExceptionHandler); signal(SIGKILL, Sys_DefaultExceptionHandler); signal(SIGQUIT, Sys_DefaultExceptionHandler); signal(SIGINT, Sys_DefaultExceptionHandler); if (fptest_temp >= 0.0) fptest_temp += 0.1; /* initialize the host params */ memset (&quakeparms, 0, sizeof(quakeparms)); quakeparms.basedir = cwd; quakeparms.userdir = cwd; quakeparms.argc = argc; quakeparms.argv = argv; quakeparms.errstate = 0; host_parms = &quakeparms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); devlog = COM_CheckParm("-devlog"); COM_ValidateByteorder (); Sys_DetectWin95 (); Sys_PageInProgram (); Sys_GetMemory (); atexit (Sys_AtExit); // in case we crash Sys_Init (); // Sys_InitStackCheck (); SV_Init(); // Sys_StackCheck (); // Con_Printf ("Top of stack: 0x%x\n", &time); // run one frame immediately for first heartbeat SV_Frame (HX_FRAME_TIME); oldtime = Sys_DoubleTime (); while (1) { if (NET_CheckReadTimeout(0, 10000) == -1) continue; // find time passed since last cycle newtime = Sys_DoubleTime (); time = newtime - oldtime; oldtime = newtime; SV_Frame (time); // Sys_StackCheck (); } } engine/hexenworld/server/sys_os2.c000066400000000000000000000227231444734033100175660ustar00rootroot00000000000000/* * sys_os2.c -- OS/2 system interface code * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2016 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #define INCL_DOS #define INCL_DOSERRORS #ifdef __EMX__ #define INCL_KBD #define INCL_VIO #endif #include #include #include #ifdef __WATCOMC__ #include #endif #include #define MIN_MEM_ALLOC 0x0800000 #define STD_MEM_ALLOC 0x1000000 cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; int devlog; /* log the Con_DPrintf and Sys_DPrintf content when !developer.integer */ /* =============================================================================== FILE IO =============================================================================== */ int Sys_mkdir (const char *path, qboolean crash) { FILESTATUS3 fs; APIRET rc = DosCreateDir(path, NULL); if (rc == NO_ERROR) return 0; if ((DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)) == NO_ERROR) && (fs.attrFile & FILE_DIRECTORY)) { return 0; /* dir exists */ } if (crash) Sys_Error("Unable to create directory %s (ERR: %lu)", path, rc); return -1; } int Sys_rmdir (const char *path) { APIRET rc = DosDeleteDir(path); return (rc == NO_ERROR)? 0 : -1; } int Sys_unlink (const char *path) { APIRET rc = DosDelete(path); return (rc == NO_ERROR)? 0 : -1; } int Sys_rename (const char *oldp, const char *newp) { APIRET rc = DosMove(oldp, newp); return (rc == NO_ERROR)? 0 : -1; } long Sys_filesize (const char *path) { FILESTATUS3 fs; APIRET rc = DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)); if (rc != NO_ERROR) return -1; if (fs.attrFile & FILE_DIRECTORY) return -1; return (long)fs.cbFile; } int Sys_FileType (const char *path) { FILESTATUS3 fs; APIRET rc = DosQueryPathInfo(path, FIL_STANDARD, &fs, sizeof(fs)); if (rc != NO_ERROR) return FS_ENT_NONE; if (fs.attrFile & FILE_DIRECTORY) return FS_ENT_DIRECTORY; return FS_ENT_FILE; } int Sys_CopyFile (const char *frompath, const char *topath) { APIRET rc = DosCopy(frompath, topath, DCPY_EXISTING); return (rc == NO_ERROR)? 0 : -1; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only, not a dirent struct. this is what we presently need in this engine. ================================================= */ static HDIR findhandle = HDIR_CREATE; static FILEFINDBUF3 findbuffer; static char findstr[MAX_OSPATH]; const char *Sys_FindFirstFile (const char *path, const char *pattern) { ULONG cnt = 1; APIRET rc; if (findhandle != HDIR_CREATE) Sys_Error ("Sys_FindFirst without FindClose"); q_snprintf (findstr, sizeof(findstr), "%s/%s", path, pattern); findbuffer.oNextEntryOffset = 0; rc = DosFindFirst(findstr, &findhandle, FILE_NORMAL, &findbuffer, sizeof(findbuffer), &cnt, FIL_STANDARD); if (rc != NO_ERROR) { findhandle = HDIR_CREATE; findbuffer.oNextEntryOffset = 0; return NULL; } if (findbuffer.attrFile & FILE_DIRECTORY) return Sys_FindNextFile(); return findbuffer.achName; } const char *Sys_FindNextFile (void) { APIRET rc; ULONG cnt; if (findhandle == HDIR_CREATE) return NULL; while (1) { cnt = 1; rc = DosFindNext(findhandle, &findbuffer, sizeof(findbuffer), &cnt); if (rc != NO_ERROR) return NULL; if (!(findbuffer.attrFile & FILE_DIRECTORY)) return findbuffer.achName; } return NULL; } void Sys_FindClose (void) { if (findhandle != HDIR_CREATE) { DosFindClose(findhandle); findhandle = HDIR_CREATE; findbuffer.oNextEntryOffset = 0; } } /* =============================================================================== SYSTEM IO =============================================================================== */ #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const unsigned char *p; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (sv_logfile) { fprintf (sv_logfile, ERROR_PREFIX "%s\n\n", text); fflush (sv_logfile); } for (p = (const unsigned char *) text2; *p; p++) putc (*p, stderr); for (p = (const unsigned char *) text ; *p; p++) putc (*p, stderr); putc ('\n', stderr); putc ('\n', stderr); exit (1); } void Sys_PrintTerm (const char *msgtxt) { const unsigned char *p; if (sys_nostdout.integer) return; for (p = (const unsigned char *) msgtxt; *p; p++) putc (*p, stdout); } void Sys_Quit (void) { exit (0); } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { union i64 { QWORD qw; long long ll; }; static qboolean first = true; static ULONG ticks_per_sec; static union i64 start; union i64 now; if (first) { first = false; DosTmrQueryFreq(&ticks_per_sec); DosTmrQueryTime(&start.qw); return 0.0; } DosTmrQueryTime(&now.qw); return (double)(now.ll - start.ll) / (double)ticks_per_sec; } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; DATETIME dt; unsigned int val; if (!buf) buf = strbuf; DosGetDateTime (&dt); val = dt.month; buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = dt.day; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = dt.year / 100; buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = dt.year % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = dt.hours; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = dt.minutes; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = dt.seconds; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } /* ================ Sys_ConsoleInput ================ */ #ifdef __EMX__ int putch (int c) { char ch = c; VioWrtTTY(&ch, 1, 0); return c; } int kbhit (void) { KBDKEYINFO k; if (KbdPeek(&k, 0)) return 0; return (k.fbStatus & KBDTRF_FINAL_CHAR_IN); } #endif const char *Sys_ConsoleInput (void) { static char con_text[256]; static int textlen = 0; char ch; if (! kbhit()) return NULL; ch = getche(); switch (ch) { case '\r': putch('\n'); if (textlen) { con_text[textlen] = '\0'; textlen = 0; return con_text; } break; case '\b': putch(' '); if (textlen) { textlen--; putch('\b'); } break; default: con_text[textlen] = ch; textlen = (textlen + 1) & 0xff; break; } return NULL; } static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { ULONG l, drv; if (dstsize < 8) return -1; l = dstsize - 3; if (DosQueryCurrentDir(0, (PBYTE) dst + 3, &l) != NO_ERROR) return -1; DosQueryCurrentDisk(&drv, &l); dst[0] = drv + 'A' - 1; dst[1] = ':'; dst[2] = '\\'; return 0; } static void PrintVersion (void) { Sys_Printf ("HexenWorld server %4.2f (%s)\n", ENGINE_VERSION, PLATFORM_STRING); Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); } /* =============================================================================== MAIN =============================================================================== */ static quakeparms_t parms; static char cwd[MAX_OSPATH]; int main (int argc, char **argv) { int i; double newtime, time, oldtime; PrintVersion(); if (argc > 1) { for (i = 1; i < argc; i++) { if ( !(strcmp(argv[i], "-v")) || !(strcmp(argv[i], "-version" )) || !(strcmp(argv[i], "--version")) ) { exit(0); } else if ( !(strcmp(argv[i], "-h")) || !(strcmp(argv[i], "-help" )) || !(strcmp(argv[i], "-?")) || !(strcmp(argv[i], "--help")) ) { Sys_PrintTerm ("See the documentation for details\n"); exit (0); } } } /* initialize the host params */ memset (&parms, 0, sizeof(parms)); parms.basedir = cwd; parms.userdir = cwd; parms.argc = argc; parms.argv = argv; parms.errstate = 0; host_parms = &parms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); devlog = COM_CheckParm("-devlog"); Sys_Printf("basedir is: %s\n", parms.basedir); COM_ValidateByteorder (); parms.memsize = STD_MEM_ALLOC; i = COM_CheckParm ("-heapsize"); if (i && i < com_argc-1) parms.memsize = atoi (com_argv[i+1]) * 1024; parms.membase = malloc (parms.memsize); if (!parms.membase) Sys_Error ("Insufficient memory."); SV_Init(); // report the filesystem to the user Sys_Printf("gamedir is: %s\n", FS_GetGamedir()); // run one frame immediately for first heartbeat SV_Frame (HX_FRAME_TIME); // // main loop // oldtime = Sys_DoubleTime () - HX_FRAME_TIME; while (1) { if (NET_CheckReadTimeout(0, 10000) == -1) continue; newtime = Sys_DoubleTime (); time = newtime - oldtime; oldtime = newtime; SV_Frame (time); } return 0; } engine/hexenworld/server/sys_unix.c000066400000000000000000000265321444734033100200500ustar00rootroot00000000000000/* sys_unix.c -- Unix system interface code * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2001 contributors of the Anvil of Thyrion project * Copyright (C) 2004-2005 Steven Atkinson * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "userdir.h" #include #include #if DO_USERDIRS #include #endif #include #include #include #include #include #include #define MIN_MEM_ALLOC 0x0800000 #define STD_MEM_ALLOC 0x1000000 cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; int devlog; /* log the Con_DPrintf and Sys_DPrintf content when !developer.integer */ static double starttime; static qboolean first = true; /* =============================================================================== FILE IO =============================================================================== */ int Sys_mkdir (const char *path, qboolean crash) { int rc = mkdir (path, 0777); if (rc != 0 && errno == EEXIST) { struct stat st; if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) rc = 0; } if (rc != 0 && crash) { rc = errno; Sys_Error("Unable to create directory %s: %s", path, strerror(rc)); } return rc; } int Sys_rmdir (const char *path) { return rmdir(path); } int Sys_unlink (const char *path) { return unlink(path); } int Sys_rename (const char *oldp, const char *newp) { return rename(oldp, newp); } long Sys_filesize (const char *path) { struct stat st; if (stat(path, &st) != 0) return -1; if (! S_ISREG(st.st_mode)) return -1; return (long) st.st_size; } int Sys_FileType (const char *path) { /* if (access(path, R_OK) == -1) return 0; */ struct stat st; if (stat(path, &st) != 0) return FS_ENT_NONE; if (S_ISDIR(st.st_mode)) return FS_ENT_DIRECTORY; if (S_ISREG(st.st_mode)) return FS_ENT_FILE; return FS_ENT_NONE; } #define COPY_READ_BUFSIZE 8192 /* BUFSIZ */ int Sys_CopyFile (const char *frompath, const char *topath) { char buf[COPY_READ_BUFSIZE]; FILE *in, *out; struct stat st; struct utimbuf tm; /* off_t remaining, count;*/ size_t remaining, count; if (stat(frompath, &st) != 0) { Con_Printf ("%s: unable to stat %s\n", __thisfunc__, frompath); return 1; } in = fopen (frompath, "rb"); if (!in) { Con_Printf ("%s: unable to open %s\n", __thisfunc__, frompath); return 1; } out = fopen (topath, "wb"); if (!out) { Con_Printf ("%s: unable to create %s\n", __thisfunc__, topath); fclose (in); return 1; } remaining = st.st_size; while (remaining) { if (remaining < sizeof(buf)) count = remaining; else count = sizeof(buf); if (fread(buf, 1, count, in) != count) break; if (fwrite(buf, 1, count, out) != count) break; remaining -= count; } fclose (in); fclose (out); if (remaining == 0) { /* restore the file's timestamp */ tm.actime = time (NULL); tm.modtime = st.st_mtime; utime (topath, &tm); return 0; } return 1; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only, not a dirent struct. this is what we presently need in this engine. ================================================= */ static DIR *finddir; static struct dirent *finddata; static char *findpath, *findpattern; const char *Sys_FindFirstFile (const char *path, const char *pattern) { if (finddir) Sys_Error ("Sys_FindFirst without FindClose"); finddir = opendir (path); if (!finddir) return NULL; findpattern = Z_Strdup (pattern); findpath = Z_Strdup (path); return Sys_FindNextFile(); } const char *Sys_FindNextFile (void) { struct stat test; if (!finddir) return NULL; while ((finddata = readdir(finddir)) != NULL) { if (!fnmatch (findpattern, finddata->d_name, FNM_PATHNAME)) { if ( (stat(va("%s/%s", findpath, finddata->d_name), &test) == 0) && S_ISREG(test.st_mode)) return finddata->d_name; } } return NULL; } void Sys_FindClose (void) { if (finddir != NULL) { closedir(finddir); finddir = NULL; } if (findpath != NULL) { Z_Free (findpath); findpath = NULL; } if (findpattern != NULL) { Z_Free (findpattern); findpattern = NULL; } } /* =============================================================================== SYSTEM IO =============================================================================== */ #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const unsigned char *p; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (sv_logfile) { fprintf (sv_logfile, ERROR_PREFIX "%s\n\n", text); fflush (sv_logfile); } for (p = (const unsigned char *) text2; *p; p++) putc (*p, stderr); for (p = (const unsigned char *) text ; *p; p++) putc (*p, stderr); putc ('\n', stderr); putc ('\n', stderr); exit (1); } void Sys_PrintTerm (const char *msgtxt) { const unsigned char *p; if (sys_nostdout.integer) return; for (p = (const unsigned char *) msgtxt; *p; p++) putc (*p, stdout); } void Sys_Quit (void) { exit (0); } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { struct timeval tp; double now; gettimeofday (&tp, NULL); now = tp.tv_sec + tp.tv_usec / 1e6; if (first) { first = false; starttime = now; return 0.0; } return now - starttime; } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; time_t t; struct tm *l; int val; if (!buf) buf = strbuf; t = time(NULL); l = localtime(&t); val = l->tm_mon + 1; /* tm_mon: months since January [0,11] */ buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = l->tm_mday; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = l->tm_year / 100 + 19; /* tm_year: #years since 1900. */ buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = l->tm_year % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = l->tm_hour; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = l->tm_min; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = l->tm_sec; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } /* ================ Sys_ConsoleInput ================ */ const char *Sys_ConsoleInput (void) { static char con_text[256]; static int textlen; char c; fd_set set; struct timeval timeout; FD_ZERO (&set); FD_SET (0, &set); // stdin timeout.tv_sec = 0; timeout.tv_usec = 0; while (select (1, &set, NULL, NULL, &timeout)) { read (0, &c, 1); if (c == '\n' || c == '\r') { con_text[textlen] = '\0'; textlen = 0; return con_text; } else if (c == 8) { if (textlen) { textlen--; con_text[textlen] = '\0'; } continue; } con_text[textlen] = c; textlen++; if (textlen < (int) sizeof(con_text)) con_text[textlen] = '\0'; else { // buffer is full textlen = 0; con_text[0] = '\0'; Sys_PrintTerm("\nConsole input too long!\n"); break; } } return NULL; } static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { char *tmp; if (getcwd(dst, dstsize - 1) == NULL) return -1; tmp = dst; while (*tmp != 0) tmp++; while (*tmp == 0 && tmp != dst) { --tmp; if (tmp != dst && *tmp == '/') *tmp = 0; } return 0; } #if DO_USERDIRS static int Sys_GetUserdir (char *dst, size_t dstsize) { size_t n; const char *home_dir = NULL; struct passwd *pwent; pwent = getpwuid(getuid()); if (pwent == NULL) perror("getpwuid"); else home_dir = pwent->pw_dir; if (home_dir == NULL) home_dir = getenv("HOME"); if (home_dir == NULL) return 1; /* what would be a maximum path for a file in the user's directory... * $HOME/AOT_USERDIR/game_dir/dirname1/dirname2/dirname3/filename.ext * still fits in the MAX_OSPATH == 256 definition, but just in case. */ n = strlen(home_dir) + strlen(AOT_USERDIR) + 50; if (n >= dstsize) { Sys_Error ("%s: Insufficient bufsize %d. Need at least %d.", __thisfunc__, (int)dstsize, (int)n); } q_snprintf (dst, dstsize, "%s/%s", home_dir, AOT_USERDIR); return 0; } #endif /* DO_USERDIRS */ static void PrintVersion (void) { Sys_Printf ("HexenWorld server %4.2f (%s)\n", ENGINE_VERSION, PLATFORM_STRING); Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); } /* =============================================================================== MAIN =============================================================================== */ static quakeparms_t parms; static char cwd[MAX_OSPATH]; #if DO_USERDIRS static char userdir[MAX_OSPATH]; #endif int main (int argc, char **argv) { int i; double newtime, time, oldtime; PrintVersion(); if (argc > 1) { for (i = 1; i < argc; i++) { if ( !(strcmp(argv[i], "-v")) || !(strcmp(argv[i], "-version" )) || !(strcmp(argv[i], "--version")) ) { exit(0); } else if ( !(strcmp(argv[i], "-h")) || !(strcmp(argv[i], "-help" )) || !(strcmp(argv[i], "-?")) || !(strcmp(argv[i], "--help")) ) { Sys_PrintTerm ("See the documentation for details\n"); exit (0); } } } /* initialize the host params */ memset (&parms, 0, sizeof(parms)); parms.basedir = cwd; parms.userdir = cwd; parms.argc = argc; parms.argv = argv; parms.errstate = 0; host_parms = &parms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); #if DO_USERDIRS memset (userdir, 0, sizeof(userdir)); if (Sys_GetUserdir(userdir, sizeof(userdir)) != 0) Sys_Error ("Couldn't determine userspace directory"); Sys_mkdir(userdir, true); parms.userdir = userdir; #endif devlog = COM_CheckParm("-devlog"); Sys_Printf("basedir is: %s\n", parms.basedir); Sys_Printf("userdir is: %s\n", parms.userdir); COM_ValidateByteorder (); parms.memsize = STD_MEM_ALLOC; i = COM_CheckParm ("-heapsize"); if (i && i < com_argc-1) parms.memsize = atoi (com_argv[i+1]) * 1024; parms.membase = malloc (parms.memsize); if (!parms.membase) Sys_Error ("Insufficient memory."); SV_Init(); // report the filesystem to the user Sys_Printf("gamedir is: %s\n", FS_GetGamedir()); Sys_Printf("userdir is: %s\n", FS_GetUserdir()); // run one frame immediately for first heartbeat SV_Frame (HX_FRAME_TIME); // // main loop // oldtime = Sys_DoubleTime () - HX_FRAME_TIME; while (1) { if (NET_CheckReadTimeout(0, 10000) == -1) continue; newtime = Sys_DoubleTime (); time = newtime - oldtime; oldtime = newtime; SV_Frame (time); } return 0; } engine/hexenworld/server/sys_win.c000066400000000000000000000237221444734033100176600ustar00rootroot00000000000000/* sys_win.c -- Windows system interface code * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include #include #include #include #define MIN_MEM_ALLOC 0x0800000 #define STD_MEM_ALLOC 0x1000000 cvar_t sys_nostdout = {"sys_nostdout", "0", CVAR_NONE}; int devlog; /* log the Con_DPrintf and Sys_DPrintf content when !developer.integer */ #define TIME_WRAP_VALUE (~(DWORD)0) static DWORD starttime; static HANDLE hinput, houtput; /* =============================================================================== FILE IO =============================================================================== */ int Sys_mkdir (const char *path, qboolean crash) { if (CreateDirectory(path, NULL) != 0) return 0; if (GetLastError() == ERROR_ALREADY_EXISTS) return 0; if (crash) Sys_Error("Unable to create directory %s", path); return -1; } int Sys_rmdir (const char *path) { if (RemoveDirectory(path) != 0) return 0; return -1; } int Sys_unlink (const char *path) { if (DeleteFile(path) != 0) return 0; return -1; } int Sys_rename (const char *oldp, const char *newp) { if (MoveFile(oldp, newp) != 0) return 0; return -1; } long Sys_filesize (const char *path) { HANDLE fh; WIN32_FIND_DATA data; long size; fh = FindFirstFile(path, &data); if (fh == INVALID_HANDLE_VALUE) return -1; FindClose(fh); if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) return -1; // we're not dealing with gigabytes of files. // size should normally smaller than INT_MAX. // size = (data.nFileSizeHigh * (MAXDWORD + 1)) + data.nFileSizeLow; size = (long) data.nFileSizeLow; return size; } #ifndef INVALID_FILE_ATTRIBUTES #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #endif int Sys_FileType (const char *path) { DWORD result = GetFileAttributes(path); if (result == INVALID_FILE_ATTRIBUTES) return FS_ENT_NONE; if (result & FILE_ATTRIBUTE_DIRECTORY) return FS_ENT_DIRECTORY; return FS_ENT_FILE; } int Sys_CopyFile (const char *frompath, const char *topath) { /* 3rd param: whether to fail if 'topath' already exists */ if (CopyFile(frompath, topath, FALSE) != 0) return 0; return -1; } /* ================================================= simplified findfirst/findnext implementation: Sys_FindFirstFile and Sys_FindNextFile return filenames only, not a dirent struct. this is what we presently need in this engine. ================================================= */ static HANDLE findhandle = INVALID_HANDLE_VALUE; static WIN32_FIND_DATA finddata; static char findstr[MAX_OSPATH]; const char *Sys_FindFirstFile (const char *path, const char *pattern) { if (findhandle != INVALID_HANDLE_VALUE) Sys_Error ("Sys_FindFirst without FindClose"); q_snprintf (findstr, sizeof(findstr), "%s/%s", path, pattern); findhandle = FindFirstFile(findstr, &finddata); if (findhandle == INVALID_HANDLE_VALUE) return NULL; if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) return Sys_FindNextFile(); return finddata.cFileName; } const char *Sys_FindNextFile (void) { if (findhandle == INVALID_HANDLE_VALUE) return NULL; while (FindNextFile(findhandle, &finddata) != 0) { if (finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; return finddata.cFileName; } return NULL; } void Sys_FindClose (void) { if (findhandle != INVALID_HANDLE_VALUE) { FindClose(findhandle); findhandle = INVALID_HANDLE_VALUE; } } /* =============================================================================== SYSTEM IO =============================================================================== */ #define ERROR_PREFIX "\nFATAL ERROR: " void Sys_Error (const char *error, ...) { va_list argptr; char text[MAX_PRINTMSG]; const char text2[] = ERROR_PREFIX; const char text3[] = "\n"; DWORD dummy; host_parms->errstate++; va_start (argptr, error); q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); if (sv_logfile) { fprintf (sv_logfile, ERROR_PREFIX "%s\n\n", text); fflush (sv_logfile); } WriteFile(houtput, text2, strlen(text2), &dummy, NULL); WriteFile(houtput, text, strlen(text), &dummy, NULL); WriteFile(houtput, text3, strlen(text3), &dummy, NULL); exit (1); } void Sys_PrintTerm (const char *msgtxt) { DWORD dummy; if (sys_nostdout.integer) return; WriteFile(houtput, msgtxt, strlen(msgtxt), &dummy, NULL); } void Sys_Quit (void) { exit (0); } /* ================ Sys_DoubleTime ================ */ double Sys_DoubleTime (void) { DWORD now, passed; now = timeGetTime(); if (now < starttime) /* wrapped? */ { passed = TIME_WRAP_VALUE - starttime; passed += now; } else { passed = now - starttime; } return (passed == 0) ? 0.0 : (passed / 1000.0); } char *Sys_DateTimeString (char *buf) { static char strbuf[24]; SYSTEMTIME st; int val; if (!buf) buf = strbuf; GetLocalTime(&st); val = st.wMonth; buf[0] = val / 10 + '0'; buf[1] = val % 10 + '0'; buf[2] = '/'; val = st.wDay; buf[3] = val / 10 + '0'; buf[4] = val % 10 + '0'; buf[5] = '/'; val = st.wYear / 100; buf[6] = val / 10 + '0'; buf[7] = val % 10 + '0'; val = st.wYear % 100; buf[8] = val / 10 + '0'; buf[9] = val % 10 + '0'; buf[10] = ' '; val = st.wHour; buf[11] = val / 10 + '0'; buf[12] = val % 10 + '0'; buf[13] = ':'; val = st.wMinute; buf[14] = val / 10 + '0'; buf[15] = val % 10 + '0'; buf[16] = ':'; val = st.wSecond; buf[17] = val / 10 + '0'; buf[18] = val % 10 + '0'; buf[19] = '\0'; return buf; } const char *Sys_ConsoleInput (void) { static char con_text[256]; static int textlen; INPUT_RECORD recs[1024]; int ch; DWORD dummy, numread, numevents; for ( ;; ) { if (GetNumberOfConsoleInputEvents(hinput, &numevents) == 0) Sys_Error ("Error getting # of console events"); if (! numevents) break; if (ReadConsoleInput(hinput, recs, 1, &numread) == 0) Sys_Error ("Error reading console input"); if (numread != 1) Sys_Error ("Couldn't read console input"); if (recs[0].EventType == KEY_EVENT) { if (recs[0].Event.KeyEvent.bKeyDown == FALSE) { ch = recs[0].Event.KeyEvent.uChar.AsciiChar; switch (ch) { case '\r': WriteFile(houtput, "\r\n", 2, &dummy, NULL); if (textlen != 0) { con_text[textlen] = 0; textlen = 0; return con_text; } break; case '\b': WriteFile(houtput, "\b \b", 3, &dummy, NULL); if (textlen != 0) textlen--; break; default: if (ch >= ' ') { WriteFile(houtput, &ch, 1, &dummy, NULL); con_text[textlen] = ch; textlen = (textlen + 1) & 0xff; } break; } } } } return NULL; } static int Sys_GetBasedir (char *argv0, char *dst, size_t dstsize) { char *tmp; size_t rc; rc = GetCurrentDirectory(dstsize, dst); if (rc == 0 || rc > dstsize) return -1; tmp = dst; while (*tmp != 0) tmp++; while (*tmp == 0 && tmp != dst) { --tmp; if (tmp != dst && (*tmp == '/' || *tmp == '\\')) *tmp = 0; } return 0; } static void PrintVersion (void) { Sys_Printf ("HexenWorld server %4.2f (%s)\n", ENGINE_VERSION, PLATFORM_STRING); Sys_Printf ("Hammer of Thyrion, release %s (%s)\n", HOT_VERSION_STR, HOT_VERSION_REL_DATE); } /* =============================================================================== MAIN =============================================================================== */ static quakeparms_t parms; static char cwd[MAX_OSPATH]; int main (int argc, char **argv) { int i; double newtime, time, oldtime; hinput = GetStdHandle (STD_INPUT_HANDLE); houtput = GetStdHandle (STD_OUTPUT_HANDLE); PrintVersion(); if (argc > 1) { for (i = 1; i < argc; i++) { if ( !(strcmp(argv[i], "-v")) || !(strcmp(argv[i], "-version" )) || !(strcmp(argv[i], "--version")) ) { exit(0); } else if ( !(strcmp(argv[i], "-h")) || !(strcmp(argv[i], "-help" )) || !(strcmp(argv[i], "--help")) || !(strcmp(argv[i], "-?")) ) { Sys_PrintTerm ("See the documentation for details\n"); exit (0); } } } /* initialize the host params */ memset (&parms, 0, sizeof(parms)); parms.basedir = cwd; parms.userdir = cwd; /* no userdir on win32 */ parms.argc = argc; parms.argv = argv; parms.errstate = 0; host_parms = &parms; memset (cwd, 0, sizeof(cwd)); if (Sys_GetBasedir(argv[0], cwd, sizeof(cwd)) != 0) Sys_Error ("Couldn't determine current directory"); devlog = COM_CheckParm("-devlog"); Sys_Printf("basedir is: %s\n", parms.basedir); Sys_Printf("userdir is: %s\n", parms.userdir); COM_ValidateByteorder (); parms.memsize = STD_MEM_ALLOC; i = COM_CheckParm ("-heapsize"); if (i && i < com_argc-1) parms.memsize = atoi (com_argv[i+1]) * 1024; parms.membase = malloc (parms.memsize); if (!parms.membase) Sys_Error ("Insufficient memory."); timeBeginPeriod (1); /* 1 ms timer precision */ starttime = timeGetTime (); SV_Init(); // report the filesystem to the user Sys_Printf("gamedir is: %s\n", FS_GetGamedir()); Sys_Printf("userdir is: %s\n", FS_GetUserdir()); // run one frame immediately for first heartbeat SV_Frame (HX_FRAME_TIME); // // main loop // oldtime = Sys_DoubleTime () - HX_FRAME_TIME; while (1) { if (NET_CheckReadTimeout(0, 10000) == -1) continue; newtime = Sys_DoubleTime (); time = newtime - oldtime; oldtime = newtime; SV_Frame (time); } return 0; } engine/hexenworld/server/world.c000066400000000000000000000610731444734033100173150ustar00rootroot00000000000000/* * world.c -- world query functions * * entities never clip against themselves, or their owner * line of sight checks trace->crosscontent, but bullets don't * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" typedef struct { vec3_t boxmins, boxmaxs;// enclose the test object along entire move float *mins, *maxs; // size of the moving object vec3_t mins2, maxs2; // size when clipping against mosnters float *start, *end; trace_t trace; int type; edict_t *passedict; } moveclip_t; static int SV_HullPointContents (hull_t *hull, int num, vec3_t p); /* =============================================================================== HULL BOXES =============================================================================== */ static hull_t box_hull; static mclipnode_t box_clipnodes[6]; static mplane_t box_planes[6]; static int move_type; /* =================== SV_InitBoxHull Set up the planes and clipnodes so that the six floats of a bounding box can just be stored out and get a proper hull_t structure. =================== */ static void SV_InitBoxHull (void) { int i; int side; box_hull.clipnodes = box_clipnodes; box_hull.planes = box_planes; box_hull.firstclipnode = 0; box_hull.lastclipnode = 5; for (i = 0; i < 6; i++) { box_clipnodes[i].planenum = i; side = i & 1; box_clipnodes[i].children[side] = CONTENTS_EMPTY; if (i != 5) box_clipnodes[i].children[side^1] = i + 1; else box_clipnodes[i].children[side^1] = CONTENTS_SOLID; box_planes[i].type = i>>1; box_planes[i].normal[i>>1] = 1; } } /* =================== SV_HullForBox To keep everything totally uniform, bounding boxes are turned into small BSP trees instead of being compared directly. =================== */ static hull_t *SV_HullForBox (vec3_t mins, vec3_t maxs) { box_planes[0].dist = maxs[0]; box_planes[1].dist = mins[0]; box_planes[2].dist = maxs[1]; box_planes[3].dist = mins[1]; box_planes[4].dist = maxs[2]; box_planes[5].dist = mins[2]; return &box_hull; } /* ================ SV_HullForEntity Returns a hull that can be used for testing or clipping an object of mins/maxs size. Offset is filled in to contain the adjustment that must be added to the testing object's origin to get a point to use with the returned hull. ================ */ static hull_t *SV_HullForEntity (edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t offset, edict_t *move_ent) { qmodel_t *model; vec3_t size; vec3_t hullmins, hullmaxs; hull_t *hull; int idx; // decide which clipping hull to use, based on the size if (ent->v.solid == SOLID_BSP) { // explicit hulls in the BSP model if (ent->v.movetype != MOVETYPE_PUSH) SV_Error ("SOLID_BSP without MOVETYPE_PUSH"); model = sv.models[ (int)ent->v.modelindex ]; if (!model || model->type != mod_brush) SV_Error ("SOLID_BSP with a non bsp model"); VectorSubtract (maxs, mins, size); if (move_ent->v.hull) // Entity is specifying which hull to use { idx = move_ent->v.hull-1; hull = &model->hulls[idx]; if (!hull) // Invalid hull { Con_Printf ("ERROR: hull %d is null.\n", idx); hull = &model->hulls[0]; } } else // Using the old way uses size to determine hull to use { if (size[0] < 3) // Point hull = &model->hulls[0]; else if (size[0] <= 32 && size[2] <= 28) // Half Player hull = &model->hulls[3]; else if (size[0] <= 32) // Full Player hull = &model->hulls[1]; else // Golem hull = &model->hulls[5]; } // calculate an offset value to center the origin: // FL_IGNORESIZEOFS (262144) is an abandoned Siege flag // if (!((int)move_ent->v.flags & FL_IGNORESIZEOFS)) VectorSubtract (hull->clip_mins, mins, offset); VectorAdd (offset, ent->v.origin, offset); } else { // create a temp hull from bounding box sizes VectorSubtract (ent->v.mins, maxs, hullmins); VectorSubtract (ent->v.maxs, mins, hullmaxs); hull = SV_HullForBox (hullmins, hullmaxs); VectorCopy (ent->v.origin, offset); } return hull; } /* =============================================================================== ENTITY AREA CHECKING =============================================================================== */ areanode_t sv_areanodes[AREA_NODES]; static int sv_numareanodes; /* =============== SV_CreateAreaNode =============== */ static areanode_t *SV_CreateAreaNode (int depth, vec3_t mins, vec3_t maxs) { areanode_t *anode; vec3_t size; vec3_t mins1, maxs1, mins2, maxs2; anode = &sv_areanodes[sv_numareanodes]; sv_numareanodes++; ClearLink (&anode->trigger_edicts); ClearLink (&anode->solid_edicts); if (depth == AREA_DEPTH) { anode->axis = -1; anode->children[0] = anode->children[1] = NULL; return anode; } VectorSubtract (maxs, mins, size); if (size[0] > size[1]) anode->axis = 0; else anode->axis = 1; anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]); VectorCopy (mins, mins1); VectorCopy (mins, mins2); VectorCopy (maxs, maxs1); VectorCopy (maxs, maxs2); maxs1[anode->axis] = mins2[anode->axis] = anode->dist; anode->children[0] = SV_CreateAreaNode (depth+1, mins2, maxs2); anode->children[1] = SV_CreateAreaNode (depth+1, mins1, maxs1); return anode; } /* =============== SV_ClearWorld =============== */ void SV_ClearWorld (void) { SV_InitBoxHull (); memset (sv_areanodes, 0, sizeof(sv_areanodes)); sv_numareanodes = 0; SV_CreateAreaNode (0, sv.worldmodel->mins, sv.worldmodel->maxs); } /* =============== SV_UnlinkEdict =============== */ static link_t **sv_link_next; static link_t **sv_link_prev; void SV_UnlinkEdict (edict_t *ent) { if (!ent->area.prev) return; // not linked in anywhere RemoveLink (&ent->area); if (sv_link_next && *sv_link_next == &ent->area) *sv_link_next = ent->area.next; if (sv_link_prev && *sv_link_prev == &ent->area) *sv_link_prev = ent->area.prev; ent->area.prev = ent->area.next = NULL; } /* ==================== SV_TouchLinks ==================== */ static void SV_TouchLinks (edict_t *ent, areanode_t *node) { link_t *l, *lnext; edict_t *touch; int old_self, old_other; loc0: // touch linked edicts sv_link_next = &lnext; for (l = node->trigger_edicts.next ; l != &node->trigger_edicts ; l = lnext) { if (!l) { // my area got removed out from under me! Con_Printf ("%s: encountered NULL link!\n", __thisfunc__); break; } lnext = l->next; touch = EDICT_FROM_AREA(l); if (touch == ent) continue; if (!touch->v.touch || touch->v.solid != SOLID_TRIGGER) continue; if (ent->v.absmin[0] > touch->v.absmax[0] || ent->v.absmin[1] > touch->v.absmax[1] || ent->v.absmin[2] > touch->v.absmax[2] || ent->v.absmax[0] < touch->v.absmin[0] || ent->v.absmax[1] < touch->v.absmin[1] || ent->v.absmax[2] < touch->v.absmin[2] ) continue; old_self = *sv_globals.self; old_other = *sv_globals.other; *sv_globals.self = EDICT_TO_PROG(touch); *sv_globals.other = EDICT_TO_PROG(ent); *sv_globals.time = sv.time; PR_ExecuteProgram (touch->v.touch); *sv_globals.self = old_self; *sv_globals.other = old_other; } sv_link_next = NULL; // recurse down both sides if (node->axis == -1) return; // LordHavoc: optimized recursion //if (ent->v.absmax[node->axis] > node->dist) SV_TouchLinks (ent, node->children[0]); //if (ent->v.absmin[node->axis] < node->dist) SV_TouchLinks (ent, node->children[1]); if (ent->v.absmax[node->axis] > node->dist) { if (ent->v.absmin[node->axis] < node->dist) SV_TouchLinks(ent, node->children[1]); // order reversed to reduce code node = node->children[0]; goto loc0; } else { if (ent->v.absmin[node->axis] < node->dist) { node = node->children[1]; goto loc0; } } } /* =============== SV_FindTouchedLeafs =============== */ static void SV_FindTouchedLeafs (edict_t *ent, mnode_t *node) { mplane_t *splitplane; mleaf_t *leaf; int sides; int leafnum; loc0: if (node->contents == CONTENTS_SOLID) return; // add an efrag if the node is a leaf if (node->contents < 0) { if (ent->num_leafs == MAX_ENT_LEAFS) return; leaf = (mleaf_t *)node; leafnum = leaf - sv.worldmodel->leafs - 1; ent->leafnums[ent->num_leafs] = leafnum; ent->num_leafs++; return; } // NODE_MIXED splitplane = node->plane; sides = BOX_ON_PLANE_SIDE(ent->v.absmin, ent->v.absmax, splitplane); // recurse down the contacted sides // LordHavoc: optimized recursion //if (sides & 1) SV_FindTouchedLeafs (ent, node->children[0]); //if (sides & 2) SV_FindTouchedLeafs (ent, node->children[1]); switch (sides) { case 1: node = node->children[0]; goto loc0; case 2: node = node->children[1]; goto loc0; default: // 3 if (node->children[0]->contents != CONTENTS_SOLID) SV_FindTouchedLeafs (ent, node->children[0]); node = node->children[1]; goto loc0; } } /* =============== SV_LinkEdict =============== */ void SV_LinkEdict (edict_t *ent, qboolean touch_triggers) { areanode_t *node; if (ent->area.prev) SV_UnlinkEdict (ent); // unlink from old position if (ent == sv.edicts) return; // don't add the world if (ent->free) return; // set the abs box if (ent->v.solid == SOLID_BSP && (ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) ) { // expand for rotation float v, maxv; int i; maxv = 0; for (i = 0; i < 3; i++) { v = fabs(ent->v.mins[i]); if (v > maxv) maxv = v; v = fabs(ent->v.maxs[i]); if (v > maxv) maxv = v; } for (i = 0; i < 3; i++) { ent->v.absmin[i] = ent->v.origin[i] - maxv; ent->v.absmax[i] = ent->v.origin[i] + maxv; } } else { VectorAdd (ent->v.origin, ent->v.mins, ent->v.absmin); VectorAdd (ent->v.origin, ent->v.maxs, ent->v.absmax); } // // to make items easier to pick up and allow them to be grabbed off // of shelves, the abs sizes are expanded // if ((int)ent->v.flags & FL_ITEM) { ent->v.absmin[0] -= 15; ent->v.absmin[1] -= 15; ent->v.absmax[0] += 15; ent->v.absmax[1] += 15; } else { // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch ent->v.absmin[0] -= 1; ent->v.absmin[1] -= 1; ent->v.absmin[2] -= 1; ent->v.absmax[0] += 1; ent->v.absmax[1] += 1; ent->v.absmax[2] += 1; } // link to PVS leafs ent->num_leafs = 0; if (ent->v.modelindex) SV_FindTouchedLeafs (ent, sv.worldmodel->nodes); if (ent->v.solid == SOLID_NOT) return; // find the first node that the ent's box crosses node = sv_areanodes; while (1) { if (node->axis == -1) break; if (ent->v.absmin[node->axis] > node->dist) node = node->children[0]; else if (ent->v.absmax[node->axis] < node->dist) node = node->children[1]; else break; // crosses the node } // link it in if (ent->v.solid == SOLID_TRIGGER) InsertLinkBefore (&ent->area, &node->trigger_edicts); else InsertLinkBefore (&ent->area, &node->solid_edicts); // if touch_triggers, touch all entities at this node and decend for more if (touch_triggers) SV_TouchLinks ( ent, sv_areanodes ); } /* =============================================================================== POINT TESTING IN HULLS =============================================================================== */ #if !id386 /* ================== SV_HullPointContents ================== */ static int SV_HullPointContents (hull_t *hull, int num, vec3_t p) { float d; mclipnode_t *node; mplane_t *plane; while (num >= 0) { if (num < hull->firstclipnode || num > hull->lastclipnode) SV_Error ("%s: bad node number", __thisfunc__); node = hull->clipnodes + num; plane = hull->planes + node->planenum; if (plane->type < 3) d = p[plane->type] - plane->dist; else d = DotProductDBL(plane->normal, p) - plane->dist; if (d < 0) num = node->children[1]; else num = node->children[0]; } return num; } #endif /* !id386 */ /* ================== SV_PointContents ================== */ int SV_PointContents (vec3_t p) { int cont; cont = SV_HullPointContents (&sv.worldmodel->hulls[0], 0, p); if (cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN) cont = CONTENTS_WATER; return cont; } #ifdef QUAKE2 int SV_TruePointContents (vec3_t p) { return SV_HullPointContents (&sv.worldmodel->hulls[0], 0, p); } #endif //=========================================================================== /* ============ SV_TestEntityPosition A small wrapper around SV_BoxInSolidEntity that never clips against the supplied entity. ============ */ edict_t *SV_TestEntityPosition (edict_t *ent) { trace_t trace; trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, 0, ent); if (trace.startsolid) { return sv.edicts; } return NULL; } /* =============================================================================== LINE TESTING IN HULLS =============================================================================== */ /* ================== SV_RecursiveHullCheck ================== */ qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace) { mclipnode_t *node; mplane_t *plane; float t1, t2; float frac; int i; vec3_t mid; int side; float midf; loc0: // optimized recursion // check for empty if (num < 0) { if (num != CONTENTS_SOLID) { trace->allsolid = false; if (num == CONTENTS_EMPTY) trace->inopen = true; else trace->inwater = true; } else trace->startsolid = true; return true; // empty } if (num < hull->firstclipnode || num > hull->lastclipnode) SV_Error ("%s: bad node number", __thisfunc__); // // find the point distances // node = hull->clipnodes + num; plane = hull->planes + node->planenum; if (plane->type < 3) { t1 = p1[plane->type] - plane->dist; t2 = p2[plane->type] - plane->dist; } else { t1 = DotProductDBL(plane->normal, p1) - plane->dist; t2 = DotProductDBL(plane->normal, p2) - plane->dist; } #if 1 if (t1 >= 0 && t2 >= 0) { //return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); num = node->children[0]; goto loc0; } if (t1 < 0 && t2 < 0) { //return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); num = node->children[1]; goto loc0; } #else if ( (t1 >= DIST_EPSILON && t2 >= DIST_EPSILON) || (t2 > t1 && t1 >= 0) ) return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); if ( (t1 <= -DIST_EPSILON && t2 <= -DIST_EPSILON) || (t2 < t1 && t1 <= 0) ) return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); #endif // put the crosspoint DIST_EPSILON pixels on the near side side = (t1 < 0); if (side) frac = (t1 + DIST_EPSILON)/(t1-t2); else frac = (t1 - DIST_EPSILON)/(t1-t2); if (frac < 0) frac = 0; else if (frac > 1) frac = 1; midf = p1f + (p2f - p1f)*frac; for (i = 0; i < 3; i++) mid[i] = p1[i] + frac*(p2[i] - p1[i]); // move up to the node if (!SV_RecursiveHullCheck (hull, node->children[side], p1f, midf, p1, mid, trace) ) return false; #ifdef PARANOID if (SV_HullPointContents (hull, node->children[side], mid) == CONTENTS_SOLID) { Con_Printf ("mid PointInHullSolid\n"); return false; } #endif // LordHavoc: this recursion can not be optimized because mid would need to be duplicated on a stack if (SV_HullPointContents (hull, node->children[side^1], mid) != CONTENTS_SOLID) // go past the node return SV_RecursiveHullCheck (hull, node->children[side^1], midf, p2f, mid, p2, trace); if (trace->allsolid) return false; // never got out of the solid area //================== // the other side of the node is solid, this is the impact point //================== if (!side) { VectorCopy (plane->normal, trace->plane.normal); trace->plane.dist = plane->dist; } else { VectorNegate (plane->normal, trace->plane.normal); trace->plane.dist = -plane->dist; } while (SV_HullPointContents (hull, hull->firstclipnode, mid) == CONTENTS_SOLID) { // shouldn't really happen, but does occasionally frac -= 0.1; if (frac < 0) { trace->fraction = midf; VectorCopy (mid, trace->endpos); Con_DPrintf ("backup past 0\n"); return false; } midf = p1f + (p2f - p1f)*frac; for (i = 0; i < 3; i++) mid[i] = p1[i] + frac * (p2[i] - p1[i]); } trace->fraction = midf; VectorCopy (mid, trace->endpos); return false; } /* ================== SV_ClipMoveToEntity Handles selection or creation of a clipping hull, and offseting (and eventually rotation) of the end points ================== */ static trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t *move_ent) { trace_t trace; vec3_t offset; vec3_t start_l, end_l; hull_t *hull; // fill in a default trace memset (&trace, 0, sizeof(trace_t)); trace.fraction = 1; trace.allsolid = true; VectorCopy (end, trace.endpos); // get the clipping hull hull = SV_HullForEntity (ent, mins, maxs, offset, move_ent); VectorSubtract (start, offset, start_l); VectorSubtract (end, offset, end_l); // rotate start and end into the models frame of reference if (ent->v.solid == SOLID_BSP && (fabs(ent->v.angles[0]) > 1 || fabs(ent->v.angles[1]) > 1 || fabs(ent->v.angles[2]) > 1) ) { vec3_t forward, right, up; vec3_t temp; AngleVectors (ent->v.angles, forward, right, up); VectorCopy (start_l, temp); start_l[0] = DotProduct (temp, forward); start_l[1] = -DotProduct (temp, right); start_l[2] = DotProduct (temp, up); VectorCopy (end_l, temp); end_l[0] = DotProduct (temp, forward); end_l[1] = -DotProduct (temp, right); end_l[2] = DotProduct (temp, up); } // trace a line through the apropriate clipping hull SV_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace); if (move_type == MOVE_WATER) { if (SV_PointContents (trace.endpos) != CONTENTS_WATER) { VectorCopy(start_l, trace.endpos); trace.fraction = 0; } } // rotate endpos back to world frame of reference if (ent->v.solid == SOLID_BSP && (fabs(ent->v.angles[0]) > 1 || fabs(ent->v.angles[1]) > 1 || fabs(ent->v.angles[2]) > 1) ) { vec3_t a; vec3_t forward, right, up; vec3_t temp; if (trace.fraction != 1) { VectorNegate (ent->v.angles, a); AngleVectors (a, forward, right, up); VectorCopy (trace.endpos, temp); trace.endpos[0] = DotProduct (temp, forward); trace.endpos[1] = -DotProduct (temp, right); trace.endpos[2] = DotProduct (temp, up); VectorCopy (trace.plane.normal, temp); trace.plane.normal[0] = DotProduct (temp, forward); trace.plane.normal[1] = -DotProduct (temp, right); trace.plane.normal[2] = DotProduct (temp, up); } } // fix trace up by the offset if (trace.fraction != 1) VectorAdd (trace.endpos, offset, trace.endpos); // did we clip the move? if (trace.fraction < 1 || trace.startsolid) trace.ent = ent; return trace; } //=========================================================================== /* ==================== SV_ClipToLinks Mins and maxs enclose the entire area swept by the move ==================== */ static void SV_ClipToLinks (areanode_t *node, moveclip_t *clip) { link_t *l, *next; edict_t *touch; trace_t trace; loc0: // optimized recursion // touch linked edicts for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next) { next = l->next; touch = EDICT_FROM_AREA(l); if (touch->v.solid == SOLID_NOT) continue; if (touch == clip->passedict) continue; if (touch->v.solid == SOLID_TRIGGER) SV_Error ("Trigger in clipping list"); if ((clip->type == MOVE_NOMONSTERS || clip->type == MOVE_PHASE) && touch->v.solid != SOLID_BSP) continue; if (clip->boxmins[0] > touch->v.absmax[0] || clip->boxmins[1] > touch->v.absmax[1] || clip->boxmins[2] > touch->v.absmax[2] || clip->boxmaxs[0] < touch->v.absmin[0] || clip->boxmaxs[1] < touch->v.absmin[1] || clip->boxmaxs[2] < touch->v.absmin[2] ) continue; if (clip->passedict && clip->passedict->v.size[0] && !touch->v.size[0]) continue; // points never interact // might intersect, so do an exact clip if (clip->trace.allsolid) return; if (clip->passedict) { if (PROG_TO_EDICT(touch->v.owner) == clip->passedict) continue; // don't clip against own missiles if (PROG_TO_EDICT(clip->passedict->v.owner) == touch) continue; // don't clip against owner } if ((int)touch->v.flags & FL_MONSTER) trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins2, clip->maxs2, clip->end, touch); else trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins, clip->maxs, clip->end, touch); if (trace.allsolid || trace.startsolid || trace.fraction < clip->trace.fraction) { trace.ent = touch; if (clip->trace.startsolid) { clip->trace = trace; clip->trace.startsolid = true; } else clip->trace = trace; } else if (trace.startsolid) clip->trace.startsolid = true; } // recurse down both sides if (node->axis == -1) return; if ( clip->boxmaxs[node->axis] > node->dist ) { //SV_ClipToLinks ( node->children[0], clip ); if (clip->boxmins[node->axis] < node->dist) SV_ClipToLinks(node->children[1], clip); node = node->children[0]; goto loc0; } else if ( clip->boxmins[node->axis] < node->dist ) { //SV_ClipToLinks ( node->children[1], clip ); node = node->children[1]; goto loc0; } } /* ================== SV_MoveBounds ================== */ static void SV_MoveBounds (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs) { #if 0 // debug to test against everything boxmins[0] = boxmins[1] = boxmins[2] = -9999999; //FIXME: change to FLT_MAX/-FLT_MAX boxmaxs[0] = boxmaxs[1] = boxmaxs[2] = 9999999; #else int i; for (i = 0; i < 3; i++) { if (end[i] > start[i]) { boxmins[i] = start[i] + mins[i] - 1; boxmaxs[i] = end[i] + maxs[i] + 1; } else { boxmins[i] = end[i] + mins[i] - 1; boxmaxs[i] = start[i] + maxs[i] + 1; } } #endif } /* ================== SV_Move ================== */ trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict) { moveclip_t clip; int i; memset ( &clip, 0, sizeof ( moveclip_t ) ); move_type = type; // clip to world clip.trace = SV_ClipMoveToEntity ( sv.edicts, start, mins, maxs, end, passedict ); clip.start = start; clip.end = end; clip.mins = mins; clip.maxs = maxs; clip.type = type; clip.passedict = passedict; if (type == MOVE_MISSILE || type == MOVE_PHASE) { for (i = 0; i < 3; i++) { clip.mins2[i] = -15; clip.maxs2[i] = 15; } } else { VectorCopy (mins, clip.mins2); VectorCopy (maxs, clip.maxs2); } // create the bounding box of the entire move SV_MoveBounds ( start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs ); // clip to entities SV_ClipToLinks ( sv_areanodes, &clip ); return clip.trace; } //============================================================================= /* ============ SV_TestPlayerPosition ============ */ #if 0 /* no callers */ edict_t *SV_TestPlayerPosition (edict_t *ent, vec3_t origin) { hull_t *hull; edict_t *check; vec3_t boxmins, boxmaxs; vec3_t offset; int e; // check world first hull = &sv.worldmodel->hulls[1]; if ( SV_HullPointContents (hull, hull->firstclipnode, origin) != CONTENTS_EMPTY ) return sv.edicts; // check all entities VectorAdd (origin, ent->v.mins, boxmins); VectorAdd (origin, ent->v.maxs, boxmaxs); check = NEXT_EDICT(sv.edicts); for (e = 1; e < sv.num_edicts; e++, check = NEXT_EDICT(check)) { if (check->free) continue; if (check->v.solid != SOLID_BSP && check->v.solid != SOLID_BBOX && check->v.solid != SOLID_SLIDEBOX) continue; if (boxmins[0] > check->v.absmax[0] || boxmins[1] > check->v.absmax[1] || boxmins[2] > check->v.absmax[2] || boxmaxs[0] < check->v.absmin[0] || boxmaxs[1] < check->v.absmin[1] || boxmaxs[2] < check->v.absmin[2] ) continue; if (check == ent) continue; // get the clipping hull hull = SV_HullForEntity (check, ent->v.mins, ent->v.maxs, offset, ent); VectorSubtract (origin, offset, offset); // test the point if ( SV_HullPointContents (hull, hull->firstclipnode, offset) != CONTENTS_EMPTY ) return check; } return NULL; } #endif engine/hexenworld/server/world.h000066400000000000000000000057071444734033100173240ustar00rootroot00000000000000/* world.c -- world query functions * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __HX2_WORLD_H #define __HX2_WORLD_H typedef struct { vec3_t normal; float dist; } plane_t; typedef struct { qboolean allsolid; // if true, plane is not valid qboolean startsolid; // if true, the initial point was in a solid area qboolean inopen, inwater; float fraction; // time completed, 1.0 = didn't hit anything vec3_t endpos; // final position plane_t plane; // surface normal at impact edict_t *ent; // entity the surface is on } trace_t; #define MOVE_NORMAL 0 #define MOVE_NOMONSTERS 1 #define MOVE_MISSILE 2 #define MOVE_WATER 3 #define MOVE_PHASE 4 typedef struct areanode_s { int axis; // -1 = leaf node float dist; struct areanode_s *children[2]; link_t trigger_edicts; link_t solid_edicts; } areanode_t; #define AREA_DEPTH 4 #define AREA_NODES 32 extern areanode_t sv_areanodes[AREA_NODES]; void SV_ClearWorld (void); // called after the world model has been loaded, before linking any entities void SV_UnlinkEdict (edict_t *ent); // call before removing an entity, and before trying to move one, // so it doesn't clip against itself // flags ent->v.modified void SV_LinkEdict (edict_t *ent, qboolean touch_triggers); // Needs to be called any time an entity changes origin, mins, maxs, or solid // flags ent->v.modified // sets ent->v.absmin and ent->v.absmax // if touchtriggers, calls prog functions for the intersected triggers int SV_PointContents (vec3_t p); // returns the CONTENTS_* value from the world at the given point. // does not check any entities at all edict_t *SV_TestEntityPosition (edict_t *ent); trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict); // mins and maxs are reletive // if the entire move stays in a solid volume, trace.allsolid will be set // if the starting point is in a solid, it will be allowed to move out // to an open area // nomonsters is used for line of sight or edge testing, where mosnters // shouldn't be considered solid objects // passedict is explicitly excluded from clipping checks (normally NULL) edict_t *SV_TestPlayerPosition (edict_t *ent, vec3_t origin); #endif /* __HX2_WORLD_H */ engine/hexenworld/shared/000077500000000000000000000000001444734033100157535ustar00rootroot00000000000000engine/hexenworld/shared/effects.h000066400000000000000000000147151444734033100175530ustar00rootroot00000000000000/* effects.h -- effect types and defs for Hexen2World * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __EFFECTS_H #define __EFFECTS_H #define MAX_EFFECTS 256 /* Types for various chunks */ #define THINGTYPE_GREYSTONE 1 #define THINGTYPE_WOOD 2 #define THINGTYPE_METAL 3 #define THINGTYPE_FLESH 4 #define THINGTYPE_FIRE 5 #define THINGTYPE_CLAY 6 #define THINGTYPE_LEAVES 7 #define THINGTYPE_HAY 8 #define THINGTYPE_BROWNSTONE 9 #define THINGTYPE_CLOTH 10 #define THINGTYPE_WOOD_LEAF 11 #define THINGTYPE_WOOD_METAL 12 #define THINGTYPE_WOOD_STONE 13 #define THINGTYPE_METAL_STONE 14 #define THINGTYPE_METAL_CLOTH 15 #define THINGTYPE_WEBS 16 #define THINGTYPE_GLASS 17 #define THINGTYPE_ICE 18 #define THINGTYPE_CLEARGLASS 19 #define THINGTYPE_REDGLASS 20 #define THINGTYPE_ACID 21 #define THINGTYPE_METEOR 22 #define THINGTYPE_GREENFLESH 23 #define THINGTYPE_BONE 24 #define THINGTYPE_DIRT 25 #define XBOW_IMPACT_DEFAULT 0 #define XBOW_IMPACT_GREENFLESH 2 #define XBOW_IMPACT_REDFLESH 4 #define XBOW_IMPACT_WOOD 6 #define XBOW_IMPACT_STONE 8 #define XBOW_IMPACT_METAL 10 #define XBOW_IMPACT_ICE 12 #define XBOW_IMPACT_MUMMY 14 #define CE_NONE 0 #define CE_RAIN 1 #define CE_FOUNTAIN 2 #define CE_QUAKE 3 #define CE_WHITE_SMOKE 4 /* whtsmk1.spr */ #define CE_BLUESPARK 5 /* bspark.spr */ #define CE_YELLOWSPARK 6 /* spark.spr */ #define CE_SM_CIRCLE_EXP 7 /* fcircle.spr */ #define CE_BG_CIRCLE_EXP 8 /* fcircle.spr */ #define CE_SM_WHITE_FLASH 9 /* sm_white.spr */ #define CE_WHITE_FLASH 10 /* gryspt.spr */ #define CE_YELLOWRED_FLASH 11 /* yr_flash.spr */ #define CE_BLUE_FLASH 12 /* bluflash.spr */ #define CE_SM_BLUE_FLASH 13 /* bluflash.spr */ #define CE_RED_FLASH 14 /* redspt.spr */ #define CE_SM_EXPLOSION 15 /* sm_expld.spr */ #define CE_LG_EXPLOSION 16 /* bg_expld.spr */ #define CE_FLOOR_EXPLOSION 17 /* fl_expld.spr */ #define CE_RIDER_DEATH 18 #define CE_BLUE_EXPLOSION 19 /* xpspblue.spr */ #define CE_GREEN_SMOKE 20 /* grnsmk1.spr */ #define CE_GREY_SMOKE 21 /* grysmk1.spr */ #define CE_RED_SMOKE 22 /* redsmk1.spr */ #define CE_SLOW_WHITE_SMOKE 23 /* whtsmk1.spr */ #define CE_REDSPARK 24 /* rspark.spr */ #define CE_GREENSPARK 25 /* gspark.spr */ #define CE_TELESMK1 26 /* telesmk1.spr */ #define CE_TELESMK2 27 /* telesmk2.spr */ #define CE_ICEHIT 28 /* icehit.spr */ #define CE_MEDUSA_HIT 29 /* medhit.spr */ #define CE_MEZZO_REFLECT 30 /* mezzoref.spr */ #define CE_FLOOR_EXPLOSION2 31 /* flrexpl2.spr */ #define CE_XBOW_EXPLOSION 32 /* xbowexpl.spr */ #define CE_NEW_EXPLOSION 33 /* gen_expl.spr */ #define CE_MAGIC_MISSILE_EXPLOSION 34 /* mm_expld.spr */ #define CE_GHOST 35 /* ghost.spr */ #define CE_BONE_EXPLOSION 36 #define CE_REDCLOUD 37 #define CE_TELEPORTERPUFFS 38 #define CE_TELEPORTERBODY 39 #define CE_BONESHARD 40 #define CE_BONESHRAPNEL 41 /* New for HexenWorld... */ #define CE_HWMISSILESTAR 42 #define CE_HWEIDOLONSTAR 43 #define CE_HWSHEEPINATOR 44 #define CE_TRIPMINE 45 #define CE_HWBONEBALL 46 #define CE_HWRAVENSTAFF 47 #define CE_TRIPMINESTILL 48 #define CE_SCARABCHAIN 49 #define CE_SM_EXPLOSION2 50 #define CE_HWSPLITFLASH 51 #define CE_HWXBOWSHOOT 52 #define CE_HWRAVENPOWER 53 #define CE_HWDRILLA 54 #define CE_DEATHBUBBLES 55 /* New for Mission Pack... */ #define CE_RIPPLE 56 #define CE_BLDRN_EXPL 57 #define CE_ACID_MUZZFL 58 #define CE_ACID_HIT 59 #define CE_FIREWALL_SMALL 60 #define CE_FIREWALL_MEDIUM 61 #define CE_FIREWALL_LARGE 62 #define CE_LBALL_EXPL 63 #define CE_ACID_SPLAT 64 #define CE_ACID_EXPL 65 #define CE_FBOOM 66 #define CE_BOMB 67 #define CE_BRN_BOUNCE 68 #define CE_LSHOCK 69 #define CE_FLAMEWALL 70 #define CE_FLAMEWALL2 71 #define CE_FLOOR_EXPLOSION3 72 #define CE_ONFIRE 73 #define CE_FLAMESTREAM 74 struct EffectT { int type; float expire_time; unsigned int client_list; union { struct { vec3_t e_size, dir, min_org, max_org; float next_time, wait; int color, count; } Rain; struct { vec3_t pos, angle, movedir; vec3_t vforward, vup, vright; int color, cnt; } Fountain; struct { vec3_t origin; float radius; } Quake; struct { vec3_t origin; vec3_t velocity; int entity_index; float time_amount, framelength, frame; int entity_index2; /* second is only used for telesmk1 */ } Smoke; struct { vec3_t origin; vec3_t velocity; int ent1, owner; int state, material, tag; float time_amount, height, sound_time; } Chain; struct { vec3_t origin; int entity_index; float time_amount; int reverse; /* Forward animation has been run, now go backwards */ } Flash; struct { vec3_t origin; int entity_index[13]; float time_amount; int stage; } RD; /* Rider Death */ struct { int entity_index[16]; vec3_t origin; vec3_t velocity[16]; float time_amount, framelength; float skinnum; } Teleporter; struct { vec3_t angle; vec3_t origin; vec3_t avelocity; vec3_t velocity; int entity_index; float time_amount; float speed; } Missile; struct { vec3_t angle; /* as per missile */ vec3_t origin; vec3_t avelocity; vec3_t velocity; int entity_index; float time_amount; float scale; /* star effects on top of missile */ int scaleDir; int ent1, ent2; } Star; struct { vec3_t origin[6]; vec3_t velocity; vec3_t angle; vec3_t vel[5]; int ent[5]; float gonetime[5];/* when a bolt isn't active, check here to see where in the gone process it is? not sure if that's the best way to handle it. */ int state[5]; int activebolts, turnedbolts; int bolts; float time_amount; int randseed; } Xbow; struct { vec3_t offset; int owner; int count; float time_amount; } Bubble; } ef; }; #endif /* __EFFECTS_H */ engine/hexenworld/shared/hufffreq.h000066400000000000000000000165221444734033100177400ustar00rootroot00000000000000/* hufffreq.h -- huffman freq table for use in hexenworld networking * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* 0.27588720, 0.04243389, 0.01598893, 0.00737722, 0.00557754, 0.00547342, 0.00823988, 0.00449177, 0.00986108, 0.00560728, 0.00654431, 0.00376298, 0.00498260, 0.00400095, 0.00655918, 0.00232025, 0.00504209, 0.00285570, 0.00124937, 0.00147247, 0.00226076, 0.00141298, 0.00467026, 0.00336139, 0.00123449, 0.00126424, 0.00166582, 0.00129399, 0.00114525, 0.00116013, 0.00078829, 0.00080317, 0.00639557, 0.00123449, 0.00127911, 0.00102627, 0.00182943, 0.00141298, 0.00269209, 0.00127911, 0.00224589, 0.00093703, 0.01972216, 0.00135348, 0.00477437, 0.00337627, 0.00743671, 0.00765981, 0.01951394, 0.00319779, 0.00330190, 0.00267722, 0.00235000, 0.00159146, 0.00285570, 0.00141298, 0.00151709, 0.00139810, 0.00468513, 0.00117500, 0.00202279, 0.00544367, 0.00096677, 0.00136836, 0.00913228, 0.00316804, 0.00138323, 0.00078829, 0.00942975, 0.00590475, 0.00168070, 0.00089241, 0.00095190, 0.00166582, 0.00077342, 0.00071392, 0.00086266, 0.00060981, 0.00075854, 0.00065443, 0.00126424, 0.00047595, 0.00068418, 0.00099652, 0.00065443, 0.00096677, 0.00072880, 0.00050570, 0.00071392, 0.00102627, 0.00120475, 0.00056519, 0.00281108, 0.00175506, 0.00047595, 0.00154684, 0.00160633, 0.01091710, 0.00365886, 0.00355475, 0.00937026, 0.01105096, 0.00404557, 0.00206741, 0.00389684, 0.00456614, 0.00215665, 0.00191867, 0.01072374, 0.01051551, 0.00467026, 0.00867121, 0.00542880, 0.00105601, 0.00649969, 0.01280602, 0.00510159, 0.00316804, 0.00456614, 0.00523545, 0.00157658, 0.00191867, 0.00121962, 0.00065443, 0.00080317, 0.00052057, 0.00080317, 0.00147247, 0.02236963, 0.00258798, 0.00059494, 0.00060981, 0.00038671, 0.00044620, 0.00055032, 0.00093703, 0.00072880, 0.00062468, 0.00059494, 0.00047595, 0.00044620, 0.00182943, 0.00066930, 0.00077342, 0.00114525, 0.00062468, 0.00059494, 0.00049082, 0.00059494, 0.00074367, 0.00090728, 0.00069905, 0.00075854, 0.00077342, 0.00069905, 0.00055032, 0.00075854, 0.00049082, 0.00104114, 0.00062468, 0.00397121, 0.00080317, 0.00072880, 0.00062468, 0.00163608, 0.00046108, 0.00055032, 0.00056519, 0.00102627, 0.00071392, 0.00092215, 0.00078829, 0.00660380, 0.00058006, 0.00065443, 0.00049082, 0.00118987, 0.00077342, 0.00065443, 0.00077342, 0.00151709, 0.00083291, 0.00074367, 0.00098165, 0.00108576, 0.00081804, 0.00145760, 0.00144272, 0.00394146, 0.00104114, 0.00099652, 0.00209715, 0.00502722, 0.00309367, 0.00123449, 0.00096677, 0.00090728, 0.00206741, 0.00147247, 0.00147247, 0.00129399, 0.00087753, 0.00120475, 0.00116013, 0.00145760, 0.00120475, 0.00138323, 0.00123449, 0.00166582, 0.00087753, 0.00171044, 0.00239462, 0.00156171, 0.00154684, 0.00217152, 0.00226076, 0.00211203, 0.00153196, 0.00174019, 0.00145760, 0.00168070, 0.00145760, 0.00123449, 0.00165095, 0.00188893, 0.00138323, 0.00120475, 0.00154684, 0.00138323, 0.00191867, 0.01866615, 0.00139810, 0.00153196, 0.00093703, 0.00121962, 0.00116013, 0.00075854, 0.00105601, 0.00432817, 0.00315317, 0.00407532, 0.00227563, 0.00081804, 0.00121962, 0.00110063, 0.00090728, 0.00108576, 0.00065443, 0.00096677, 0.00655918, 0.00153196, 0.00251361, 0.00312342, 0.00243924, 0.00660380, 0.01700033 */ 0.14473691, 0.01147017, 0.00167522, 0.03831121, 0.00356579, 0.03811315, 0.00178254, 0.00199644, 0.00183511, 0.00225716, 0.00211240, 0.00308829, 0.00172852, 0.00186608, 0.00215921, 0.00168891, 0.00168603, 0.00218586, 0.00284414, 0.00161833, 0.00196043, 0.00151029, 0.00173932, 0.00218370, 0.00934121, 0.00220530, 0.00381211, 0.00185456, 0.00194675, 0.00161977, 0.00186680, 0.00182071, 0.06421956, 0.00537786, 0.00514019, 0.00487155, 0.00493925, 0.00503143, 0.00514019, 0.00453520, 0.00454241, 0.00485642, 0.00422407, 0.00593387, 0.00458130, 0.00343687, 0.00342823, 0.00531592, 0.00324890, 0.00333388, 0.00308613, 0.00293776, 0.00258918, 0.00259278, 0.00377105, 0.00267488, 0.00227516, 0.00415997, 0.00248763, 0.00301555, 0.00220962, 0.00206990, 0.00270369, 0.00231694, 0.00273826, 0.00450928, 0.00384380, 0.00504728, 0.00221251, 0.00376961, 0.00232990, 0.00312574, 0.00291688, 0.00280236, 0.00252436, 0.00229461, 0.00294353, 0.00241201, 0.00366590, 0.00199860, 0.00257838, 0.00225860, 0.00260646, 0.00187256, 0.00266552, 0.00242641, 0.00219450, 0.00192082, 0.00182071, 0.02185930, 0.00157439, 0.00164353, 0.00161401, 0.00187544, 0.00186248, 0.03338637, 0.00186968, 0.00172132, 0.00148509, 0.00177749, 0.00144620, 0.00192442, 0.00169683, 0.00209439, 0.00209439, 0.00259062, 0.00194531, 0.00182359, 0.00159096, 0.00145196, 0.00128199, 0.00158376, 0.00171412, 0.00243433, 0.00345704, 0.00156359, 0.00145700, 0.00157007, 0.00232342, 0.00154198, 0.00140730, 0.00288807, 0.00152830, 0.00151246, 0.00250203, 0.00224420, 0.00161761, 0.00714383, 0.08188576, 0.00802537, 0.00119484, 0.00123805, 0.05632671, 0.00305156, 0.00105584, 0.00105368, 0.00099246, 0.00090459, 0.00109473, 0.00115379, 0.00261223, 0.00105656, 0.00124381, 0.00100326, 0.00127550, 0.00089739, 0.00162481, 0.00100830, 0.00097229, 0.00078864, 0.00107240, 0.00084409, 0.00265760, 0.00116891, 0.00073102, 0.00075695, 0.00093916, 0.00106880, 0.00086786, 0.00185600, 0.00608367, 0.00133600, 0.00075695, 0.00122077, 0.00566955, 0.00108249, 0.00259638, 0.00077063, 0.00166586, 0.00090387, 0.00087074, 0.00084914, 0.00130935, 0.00162409, 0.00085922, 0.00093340, 0.00093844, 0.00087722, 0.00108249, 0.00098598, 0.00095933, 0.00427593, 0.00496661, 0.00102775, 0.00159312, 0.00118404, 0.00114947, 0.00104936, 0.00154342, 0.00140082, 0.00115883, 0.00110769, 0.00161112, 0.00169107, 0.00107816, 0.00142747, 0.00279804, 0.00085922, 0.00116315, 0.00119484, 0.00128559, 0.00146204, 0.00130215, 0.00101551, 0.00091756, 0.00161184, 0.00236375, 0.00131872, 0.00214120, 0.00088875, 0.00138570, 0.00211960, 0.00094060, 0.00088083, 0.00094564, 0.00090243, 0.00106160, 0.00088659, 0.00114514, 0.00095861, 0.00108753, 0.00124165, 0.00427016, 0.00159384, 0.00170547, 0.00104431, 0.00091395, 0.00095789, 0.00134681, 0.00095213, 0.00105944, 0.00094132, 0.00141883, 0.00102127, 0.00101911, 0.00082105, 0.00158448, 0.00102631, 0.00087938, 0.00139290, 0.00114658, 0.00095501, 0.00161329, 0.00126542, 0.00113218, 0.00123661, 0.00101695, 0.00112930, 0.00317976, 0.00085346, 0.00101190, 0.00189849, 0.00105728, 0.00186824, 0.00092908, 0.00160896 engine/hexenworld/shared/huffman.c000066400000000000000000000151661444734033100175540ustar00rootroot00000000000000/* huffman.c -- huffman encoding/decoding for use in hexenworld networking * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include "compiler.h" #include "huffman.h" /* h2w engine includes */ #undef USE_INTEL_ASM /* use Hunk_Alloc or malloc : */ #define USE_HUNKMEM 1 #include "arch_def.h" #include "sys.h" #include "printsys.h" #if defined(PLATFORM_DOS) #undef USE_HUNKMEM #define USE_HUNKMEM 1 #endif #if USE_HUNKMEM #include "zone.h" #endif #define HuffPrintf Sys_Printf // // huffman types and vars // typedef struct huffnode_s { struct huffnode_s *zero; struct huffnode_s *one; float freq; unsigned char val; unsigned char pad[3]; } huffnode_t; typedef struct { unsigned int bits; int len; } hufftab_t; static void *HuffMemBase = NULL; static huffnode_t *HuffTree = NULL; static hufftab_t HuffLookup[256]; #ifdef _MSC_VER #pragma warning(disable:4305) /* double to float truncation */ #endif static const float HuffFreq[256] = { # include "hufffreq.h" }; //============================================================================= // // huffman debugging // #if _DEBUG_HUFFMAN static int HuffIn = 0; static int HuffOut= 0; static int freqs[256]; static void ZeroFreq (void) { memset(freqs, 0, 256 * sizeof(int)); } static void CalcFreq (const unsigned char *packet, int packetlen) { int ix; for (ix = 0; ix < packetlen; ix++) { freqs[packet[ix]]++; } } void PrintFreqs (void) { int ix; float total = 0; for (ix = 0; ix < 256; ix++) { total += freqs[ix]; } if (total > .01) { for (ix = 0; ix < 256; ix++) { HuffPrintf("\t%.8f,\n", ((float)freqs[ix])/total); } } ZeroFreq(); } #endif /* _DEBUG_HUFFMAN */ //============================================================================= // // huffman functions // static void FindTab (huffnode_t *tmp, int len, unsigned int bits) { if (!tmp) Sys_Error("no huff node"); if (tmp->zero) { if (!tmp->one) Sys_Error("no one in node"); if (len >= 32) Sys_Error("compression screwd"); FindTab (tmp->zero, len+1, bits<<1); FindTab (tmp->one, len+1, (bits<<1)|1); return; } HuffLookup[tmp->val].len = len; HuffLookup[tmp->val].bits = bits; return; } static unsigned char const Masks[8] = { 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 }; static void PutBit (unsigned char *buf, int pos, int bit) { if (bit) buf[pos/8] |= Masks[pos%8]; else buf[pos/8] &=~Masks[pos%8]; } static int GetBit (const unsigned char *buf, int pos) { if (buf[pos/8] & Masks[pos%8]) return 1; else return 0; } static void BuildTree (const float *freq) { float min1, min2; int i, j, minat1, minat2; huffnode_t *work[256]; huffnode_t *tmp; #if USE_HUNKMEM HuffMemBase = Hunk_AllocName(512 * sizeof(huffnode_t), "hufftree"); #else HuffMemBase = malloc(512 * sizeof(huffnode_t)); if (!HuffMemBase) Sys_Error("Failed allocating memory for HuffTree"); memset(HuffMemBase, 0, 512 * sizeof(huffnode_t)); #endif tmp = (huffnode_t *) HuffMemBase; for (i = 0; i < 256; tmp++, i++) { tmp->val = (unsigned char)i; tmp->freq = freq[i]; tmp->zero = NULL; tmp->one = NULL; HuffLookup[i].len = 0; work[i] = tmp; } for (i = 0; i < 255; tmp++, i++) { minat1 = -1; minat2 = -1; min1 = 1E30; min2 = 1E30; for (j = 0; j < 256; j++) { if (!work[j]) continue; if (work[j]->freq < min1) { minat2 = minat1; min2 = min1; minat1 = j; min1 = work[j]->freq; } else if (work[j]->freq < min2) { minat2 = j; min2 = work[j]->freq; } } if (minat1 < 0) Sys_Error("minat1: %d", minat1); if (minat2 < 0) Sys_Error("minat2: %d", minat2); tmp->zero = work[minat2]; tmp->one = work[minat1]; tmp->freq = work[minat2]->freq + work[minat1]->freq; tmp->val = 0xff; work[minat1] = tmp; work[minat2] = NULL; } HuffTree = --tmp; // last incrementation in the loop above wasn't used FindTab (HuffTree, 0, 0); #if _DEBUG_HUFFMAN for (i = 0; i < 256; i++) { if (!HuffLookup[i].len && HuffLookup[i].len <= 32) { // HuffPrintf("%d %d %2X\n", HuffLookup[i].len, HuffLookup[i].bits, i); Sys_Error("bad frequency table"); } } #endif /* _DEBUG_HUFFMAN */ } void HuffDecode (const unsigned char *in, unsigned char *out, int inlen, int *outlen, const int maxlen) { int bits, tbits; huffnode_t *tmp; --inlen; if (inlen < 0) { *outlen = 0; return; } if (*in == 0xff) { if (inlen > maxlen) memcpy (out, in+1, maxlen); else if (inlen) memcpy (out, in+1, inlen); *outlen = inlen; return; } tbits = inlen*8 - *in; bits = 0; *outlen = 0; while (bits < tbits) { tmp = HuffTree; do { if ( GetBit(in+1, bits) ) tmp = tmp->one; else tmp = tmp->zero; bits++; } while (tmp->zero); if ( ++(*outlen) > maxlen ) return; // out[maxlen - 1] is written already *out++ = tmp->val; } } void HuffEncode (const unsigned char *in, unsigned char *out, int inlen, int *outlen) { int i, j, bitat; unsigned int t; #if _DEBUG_HUFFMAN unsigned char *buf; int tlen; #endif /* _DEBUG_HUFFMAN */ bitat = 0; for (i = 0; i < inlen; i++) { t = HuffLookup[in[i]].bits; for (j = 0; j < HuffLookup[in[i]].len; j++) { PutBit (out+1, bitat + HuffLookup[in[i]].len-j-1, t&1); t >>= 1; } bitat += HuffLookup[in[i]].len; } *outlen = 1 + (bitat + 7)/8; *out = 8 * ((*outlen)-1) - bitat; if (*outlen >= inlen+1) { *out = 0xff; memcpy (out+1, in, inlen); *outlen = inlen+1; } #if _DEBUG_HUFFMAN HuffIn += inlen; HuffOut += *outlen; HuffPrintf("in: %d out: %d ratio: %f\n", HuffIn, HuffOut, 1-(float)HuffOut/(float)HuffIn); CalcFreq(in, inlen); buf = (unsigned char *) Z_Malloc(inlen, Z_MAINZONE); HuffDecode (out, buf, *outlen, &tlen, inlen); if (tlen != inlen) Sys_Error("bogus compression"); for (i = 0; i < inlen; i++) { if (in[i] != buf[i]) Sys_Error("bogus compression"); } Z_Free (buf); #endif /* _DEBUG_HUFFMAN */ } void HuffInit (void) { #if _DEBUG_HUFFMAN ZeroFreq (); #endif /* _DEBUG_HUFFMAN */ BuildTree(HuffFreq); } engine/hexenworld/shared/huffman.h000066400000000000000000000023671444734033100175600ustar00rootroot00000000000000/* huffman.h -- huffman encoding/decoding for use in hexenworld networking * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __H2W_HUFFMAN_H #define __H2W_HUFFMAN_H extern void HuffInit (void); extern void HuffEncode (const unsigned char *in, unsigned char *out, int inlen, int *outlen); extern void HuffDecode (const unsigned char *in, unsigned char *out, int inlen, int *outlen, const int maxlen); #define _DEBUG_HUFFMAN 0 #if _DEBUG_HUFFMAN extern void PrintFreqs (void); #endif /* _DEBUG_HUFFMAN */ #endif /* __H2W_HUFFMAN_H */ engine/hexenworld/shared/info_str.c000066400000000000000000000112561444734033100177470ustar00rootroot00000000000000/* * info_str.c -- Hexen2World info strings handling * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #include "q_ctype.h" /* =============== Info_ValueForKey Searches the string for the given key and returns the associated value, or an empty string. =============== */ const char *Info_ValueForKey (const char *s, const char *key) { char pkey[512]; static char value[4][512]; // use two buffers so compares // work without stomping on each other static int valueindex; char *o; valueindex = (valueindex + 1) % 4; if (*s == '\\') s++; while (1) { o = pkey; while (*s != '\\') { if (!*s) return ""; *o++ = *s++; } *o = 0; s++; o = value[valueindex]; while (*s != '\\' && *s) { if (!*s) return ""; *o++ = *s++; } *o = 0; if (!strcmp (key, pkey) ) return value[valueindex]; if (!*s) return ""; s++; } } void Info_RemoveKey (char *s, const char *key) { char *start; char pkey[512]; char value[512]; char *o; if (strstr (key, "\\")) { Con_Printf ("Can't use a key with a \\\n"); return; } while (1) { start = s; if (*s == '\\') s++; o = pkey; while (*s != '\\') { if (!*s) return; *o++ = *s++; } *o = 0; s++; o = value; while (*s != '\\' && *s) { if (!*s) return; *o++ = *s++; } *o = 0; if (!strcmp (key, pkey) ) { memmove(start, s, strlen(s) + 1); // remove this part return; } if (!*s) return; } } void Info_RemovePrefixedKeys (char *start, char prefix) { char *s; char pkey[512]; char value[512]; char *o; s = start; while (1) { if (*s == '\\') s++; o = pkey; while (*s != '\\') { if (!*s) return; *o++ = *s++; } *o = 0; s++; o = value; while (*s != '\\' && *s) { if (!*s) return; *o++ = *s++; } *o = 0; if (pkey[0] == prefix) { Info_RemoveKey (start, pkey); s = start; } if (!*s) return; } } void Info_SetValueForStarKey (char *s, const char *key, const char *value, size_t maxsize) { char newvalue[1024], *v; int c; if (strstr (key, "\\") || strstr (value, "\\") ) { Con_Printf ("Can't use keys or values with a \\\n"); return; } if (strstr (key, "\"") || strstr (value, "\"") ) { Con_Printf ("Can't use keys or values with a \"\n"); return; } if (strlen(key) > 63 || strlen(value) > 63) { Con_Printf ("Keys and values must be < 64 characters.\n"); return; } Info_RemoveKey (s, key); if (!*value) return; if (strlen(key) + 1 + strlen(value) + 1 + strlen(s) >= maxsize) { // >= maxsize, not > maxsize, for the null termination. // +1 for each of key and value for the added \ characters Con_Printf ("Info string length exceeded\n"); return; } sprintf (newvalue, "\\%s\\%s", key, value); // only copy ascii values s += strlen(s); v = newvalue; while (*v) { c = (unsigned char)*v++; #ifndef SERVERONLY // client only allows highbits on name if (q_strcasecmp(key, "name") != 0) { c &= 127; if (c < 32 || c > 127) continue; // auto lowercase team if (q_strcasecmp(key, "team") == 0) c = q_tolower(c); } #else if (!sv_highchars.integer) { c &= 127; if (c < 32 || c > 127) continue; } #endif // c &= 127; // strip high bits if (c > 13) // && c < 127) *s++ = c; } *s = 0; } void Info_SetValueForKey (char *s, const char *key, const char *value, size_t maxsize) { if (key[0] == '*') { Con_Printf ("Can't set * keys\n"); return; } Info_SetValueForStarKey (s, key, value, maxsize); } void Info_Print (const char *s) { char key[512]; char value[512]; char *o; int l; if (*s == '\\') s++; while (*s) { o = key; while (*s && *s != '\\') *o++ = *s++; l = o - key; if (l < 20) { memset (o, ' ', 20-l); key[20] = 0; } else *o = 0; Con_Printf ("%s", key); if (!*s) { Con_Printf ("MISSING VALUE\n"); return; } o = value; s++; while (*s && *s != '\\') *o++ = *s++; *o = 0; if (*s) s++; Con_Printf ("%s\n", value); } } engine/hexenworld/shared/info_str.h000066400000000000000000000027121444734033100177510ustar00rootroot00000000000000/* * info_str.h -- Hexen2World info strings handling * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __H2W_INFOSTR_H #define __H2W_INFOSTR_H #define MAX_INFO_STRING 196 #define MAX_SERVERINFO_STRING 512 #define MAX_LOCALINFO_STRING 32768 // none of these functions accept NULL input const char *Info_ValueForKey (const char *s, const char *key); // may return an empty string, but never NULL void Info_RemoveKey (char *s, const char *key); void Info_RemovePrefixedKeys (char *start, char prefix); void Info_SetValueForKey (char *s, const char *key, const char *value, size_t maxsize); void Info_SetValueForStarKey (char *s, const char *key, const char *value, size_t maxsize); void Info_Print (const char *s); #endif /* __H2W_INFOSTR_H */ engine/hexenworld/shared/net.h000066400000000000000000000067551444734033100167270ustar00rootroot00000000000000/* * net.h -- quake's interface to the networking layer * Copyright (C) 1996-1997 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __H2W_NET_H #define __H2W_NET_H #define PORT_ANY -1 typedef struct { byte ip[4]; unsigned short port; unsigned short pad; } netadr_t; extern netadr_t net_local_adr; extern netadr_t net_loopback_adr; extern netadr_t net_from; // address of who sent the packet extern sizebuf_t net_message; extern cvar_t hostname; void NET_Init (int port); void NET_Shutdown (void); int NET_GetPacket (void); void NET_SendPacket (int length, void *data, const netadr_t *to); int NET_CheckReadTimeout (long sec, long usec); qboolean NET_CompareAdr (const netadr_t *a, const netadr_t *b); qboolean NET_CompareBaseAdr (const netadr_t *a, const netadr_t *b); // without port const char *NET_AdrToString (const netadr_t *a); const char *NET_BaseAdrToString (const netadr_t *a); qboolean NET_StringToAdr (const char *s, netadr_t *a); //============================================================================ #define OLD_AVG 0.99 // total = oldtotal*OLD_AVG + new*(1-OLD_AVG) #define MAX_LATENT 32 typedef struct { qboolean fatal_error; float last_received; // for timeouts // the statistics are cleared at each client begin, because // the server connecting process gives a bogus picture of the data float frame_latency; // rolling average float frame_rate; int drop_count; // dropped packets, cleared each level int good_count; // cleared each level netadr_t remote_address; // bandwidth estimator double cleartime; // if realtime > nc->cleartime, free to go double rate; // seconds / byte // sequencing variables int incoming_sequence; int incoming_acknowledged; int incoming_reliable_acknowledged; // single bit int incoming_reliable_sequence; // single bit, maintained local int outgoing_sequence; int reliable_sequence; // single bit int last_reliable_sequence; // sequence number of last send // reliable staging and holding areas sizebuf_t message; // writing buffer to send to server byte message_buf[MAX_MSGLEN]; int reliable_length; byte reliable_buf[MAX_MSGLEN]; // unacked reliable message // time and size data to calculate bandwidth int outgoing_size[MAX_LATENT]; double outgoing_time[MAX_LATENT]; } netchan_t; extern int net_drop; // packets dropped before this one void Netchan_Init (void); void Netchan_Transmit (netchan_t *chan, int length, byte *data); void Netchan_OutOfBand (const netadr_t *adr, int length, byte *data); void Netchan_OutOfBandPrint (const netadr_t *adr, const char *format, ...) FUNC_PRINTF(2,3); qboolean Netchan_Process (netchan_t *chan); void Netchan_Setup (netchan_t *chan, const netadr_t *adr); qboolean Netchan_CanPacket (const netchan_t *chan); qboolean Netchan_CanReliable (const netchan_t *chan); #endif /* __H2W_NET_H */ engine/hexenworld/shared/net_chan.c000066400000000000000000000237751444734033100177140ustar00rootroot00000000000000/* * net_chan.c -- net channel * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" #define PACKET_HEADER 8 /* packet header ------------- 31 sequence 1 does this message contain a reliable payload 31 acknowledge sequence 1 acknowledge receipt of even/odd message The remote connection never knows if it missed a reliable message, the local side detects that it has been dropped by seeing a sequence acknowledge higher thatn the last reliable sequence, but without the correct evon/odd bit for the reliable set. If the sender notices that a reliable message has been dropped, it will be retransmitted. It will not be retransmitted again until a message after the retransmit has been acknowledged and the reliable still failed to get there. if the sequence number is -1, the packet should be handled without a netcon The reliable message can be added to at any time by doing MSG_Write* (&netchan->message, ). If the message buffer is overflowed, either by a single message, or by multiple frames worth piling up while the last reliable transmit goes unacknowledged, the netchan signals a fatal error. Reliable messages are always placed first in a packet, then the unreliable message is included if there is sufficient room. To the receiver, there is no distinction between the reliable and unreliable parts of the message, they are just processed out as a single larger message. Illogical packet sequence numbers cause the packet to be dropped, but do not kill the connection. This, combined with the tight window of valid reliable acknowledgement numbers provides protection against malicious address spoofing. */ int net_drop; static cvar_t showpackets = {"showpackets", "0", CVAR_NONE}; static cvar_t showdrop = {"showdrop", "0", CVAR_NONE}; #ifdef SERVERONLY #define NOT_DEMOPLAYBACK true #else #define NOT_DEMOPLAYBACK (!cls.demoplayback) #endif /* =============== Netchan_Init =============== */ void Netchan_Init (void) { Cvar_RegisterVariable (&showpackets); Cvar_RegisterVariable (&showdrop); } /* =============== Netchan_OutOfBand Sends an out-of-band datagram ================ */ void Netchan_OutOfBand (const netadr_t *adr, int length, byte *data) { sizebuf_t senddata; byte send_buf[MAX_MSGLEN + PACKET_HEADER]; // write the packet header SZ_Init (&senddata, send_buf, sizeof(send_buf)); MSG_WriteLong (&senddata, -1); // -1 sequence means out of band SZ_Write (&senddata, data, length); // send the datagram if (NOT_DEMOPLAYBACK) // zoid, no input in demo playback mode NET_SendPacket (senddata.cursize, senddata.data, adr); } /* =============== Netchan_OutOfBandPrint Sends a text message in an out-of-band datagram ================ */ void Netchan_OutOfBandPrint (const netadr_t *adr, const char *format, ...) { va_list argptr; static char string[8192]; va_start (argptr, format); q_vsnprintf (string, sizeof (string), format, argptr); va_end (argptr); Netchan_OutOfBand (adr, strlen(string), (byte *)string); } /* ============== Netchan_Setup called to open a channel to a remote system ============== */ void Netchan_Setup (netchan_t *chan, const netadr_t *adr) { memset (chan, 0, sizeof(*chan)); chan->remote_address = *adr; chan->last_received = realtime; SZ_Init (&chan->message, chan->message_buf, sizeof(chan->message_buf)); chan->message.allowoverflow = true; chan->rate = 1.0/2500; } /* =============== Netchan_CanPacket Returns true if the bandwidth choke isn't active ================ */ #define MAX_BACKUP 200 qboolean Netchan_CanPacket (const netchan_t *chan) { if (chan->cleartime < realtime + MAX_BACKUP*chan->rate) return true; return false; } /* =============== Netchan_CanReliable Returns true if the bandwidth choke isn't ================ */ qboolean Netchan_CanReliable (const netchan_t *chan) { if (chan->reliable_length) return false; // waiting for ack return Netchan_CanPacket (chan); } /* =============== Netchan_Transmit tries to send an unreliable message to a connection, and handles the transmition / retransmition of the reliable messages. A 0 length will still generate a packet and deal with the reliable messages. ================ */ void Netchan_Transmit (netchan_t *chan, int length, byte *data) { sizebuf_t senddata; byte send_buf[MAX_MSGLEN + PACKET_HEADER]; qboolean send_reliable; unsigned int w1, w2; int i; // check for message overflow if (chan->message.overflowed) { chan->fatal_error = true; Con_Printf ("%s:Outgoing message overflow\n", NET_AdrToString (&chan->remote_address)); return; } // if the remote side dropped the last reliable message, resend it send_reliable = false; if (chan->incoming_acknowledged > chan->last_reliable_sequence && chan->incoming_reliable_acknowledged != chan->reliable_sequence) send_reliable = true; // if the reliable transmit buffer is empty, copy the current message out if (!chan->reliable_length && chan->message.cursize) { memcpy (chan->reliable_buf, chan->message_buf, chan->message.cursize); chan->reliable_length = chan->message.cursize; chan->message.cursize = 0; chan->reliable_sequence ^= 1; send_reliable = true; } // write the packet header SZ_Init (&senddata, send_buf, sizeof(send_buf)); w1 = chan->outgoing_sequence | (send_reliable<<31); w2 = chan->incoming_sequence | (chan->incoming_reliable_sequence<<31); chan->outgoing_sequence++; MSG_WriteLong (&senddata, w1); MSG_WriteLong (&senddata, w2); // copy the reliable message to the packet first if (send_reliable) { SZ_Write (&senddata, chan->reliable_buf, chan->reliable_length); chan->last_reliable_sequence = chan->outgoing_sequence; } // add the unreliable part if space is available if (senddata.maxsize - senddata.cursize >= length) SZ_Write (&senddata, data, length); // send the datagram i = chan->outgoing_sequence & (MAX_LATENT-1); chan->outgoing_size[i] = senddata.cursize; chan->outgoing_time[i] = realtime; if (NOT_DEMOPLAYBACK) // zoid, no input in demo playback mode NET_SendPacket (senddata.cursize, senddata.data, &chan->remote_address); if (chan->cleartime < realtime) chan->cleartime = realtime + senddata.cursize*chan->rate; else chan->cleartime += senddata.cursize*chan->rate; if (showpackets.integer) { Con_Printf ("--> s=%i(%i) a=%i(%i) %i\n", chan->outgoing_sequence, send_reliable, chan->incoming_sequence, chan->incoming_reliable_sequence, senddata.cursize); } } /* ================= Netchan_Process called when the current net_message is from remote_address modifies net_message so that it points to the packet payload ================= */ qboolean Netchan_Process (netchan_t *chan) { unsigned int sequence, sequence_ack; int reliable_ack, reliable_message; if (NOT_DEMOPLAYBACK && !NET_CompareAdr (&net_from, &chan->remote_address)) { return false; } // get sequence numbers MSG_BeginReading (); sequence = MSG_ReadLong (); sequence_ack = MSG_ReadLong (); reliable_message = (int)(sequence >> 31); reliable_ack = (int)(sequence_ack >> 31); sequence &= ~(1<<31); sequence_ack &= ~(1<<31); if (showpackets.integer) { Con_Printf ("<-- s=%u(%i) a=%u(%i) %i\n", sequence, reliable_message, sequence_ack, reliable_ack, net_message.cursize); } // get a rate estimation #if 0 if (chan->outgoing_sequence - sequence_ack < MAX_LATENT) { int i; double time, rate; i = sequence_ack & (MAX_LATENT - 1); time = realtime - chan->outgoing_time[i]; time -= 0.1; // subtract 100 ms if (time <= 0) { // gotta be a digital link for <100 ms ping if (chan->rate > 1.0/5000) chan->rate = 1.0/5000; } else { if (chan->outgoing_size[i] < 512) { // only deal with small messages rate = chan->outgoing_size[i]/time; if (rate > 5000) rate = 5000; rate = 1.0/rate; if (chan->rate > rate) chan->rate = rate; } } } #endif // // discard stale or duplicated packets // if (sequence <= (unsigned int) chan->incoming_sequence) { if (showdrop.integer) { Con_Printf ("%s:Out of order packet %u at %i\n", NET_AdrToString (&chan->remote_address), sequence, chan->incoming_sequence); } return false; } // // dropped packets don't keep the message from being used // net_drop = sequence - (chan->incoming_sequence+1); if (net_drop > 0) { chan->drop_count += 1; if (showdrop.integer) { Con_Printf ("%s:Dropped %u packets at %u\n", NET_AdrToString (&chan->remote_address), sequence - (unsigned int)(chan->incoming_sequence + 1), sequence); } } // // if the current outgoing reliable message has been acknowledged // clear the buffer to make way for the next // if (reliable_ack == chan->reliable_sequence) chan->reliable_length = 0; // it has been received // // if this message contains a reliable message, bump incoming_reliable_sequence // chan->incoming_sequence = sequence; chan->incoming_acknowledged = sequence_ack; chan->incoming_reliable_acknowledged = reliable_ack; if (reliable_message) chan->incoming_reliable_sequence ^= 1; // // the message can now be read from the current message pointer // update statistics counters // chan->frame_latency = chan->frame_latency*OLD_AVG + (chan->outgoing_sequence-sequence_ack)*(1.0-OLD_AVG); chan->frame_rate = chan->frame_rate*OLD_AVG + (realtime-chan->last_received)*(1.0-OLD_AVG); chan->good_count += 1; chan->last_received = realtime; return true; } engine/hexenworld/shared/net_udp.c000066400000000000000000000227751444734033100175720ustar00rootroot00000000000000/* net_udp.c -- network UDP driver * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_stdinc.h" #include "arch_def.h" #include "net_sys.h" #include "quakedef.h" #include "huffman.h" //============================================================================= int LastCompMessageSize = 0; netadr_t net_local_adr; netadr_t net_loopback_adr; netadr_t net_from; sizebuf_t net_message; static sys_socket_t net_socket = INVALID_SOCKET; #if defined(PLATFORM_AMIGA) struct Library *SocketBase; #endif #ifdef PLATFORM_WINDOWS #include "wsaerror.h" static WSADATA winsockdata; #endif #define MAX_UDP_PACKET (MAX_MSGLEN + 9) /* one more than msg + header */ static byte net_message_buffer[MAX_UDP_PACKET]; //============================================================================= static void NetadrToSockadr (const netadr_t *a, struct sockaddr_in *s) { memset (s, 0, sizeof(*s)); s->sin_family = AF_INET; memcpy (&s->sin_addr, a->ip, 4); s->sin_port = a->port; } static void SockadrToNetadr (const struct sockaddr_in *s, netadr_t *a) { memcpy (a->ip, &s->sin_addr, 4); a->port = s->sin_port; } qboolean NET_CompareBaseAdr (const netadr_t *a, const netadr_t *b) { if (a->ip[0] == b->ip[0] && a->ip[1] == b->ip[1] && a->ip[2] == b->ip[2] && a->ip[3] == b->ip[3]) { return true; } return false; } qboolean NET_CompareAdr (const netadr_t *a, const netadr_t *b) { if (a->ip[0] == b->ip[0] && a->ip[1] == b->ip[1] && a->ip[2] == b->ip[2] && a->ip[3] == b->ip[3] && a->port == b->port) { return true; } return false; } const char *NET_AdrToString (const netadr_t *a) { static char s[64]; sprintf (s, "%i.%i.%i.%i:%i", a->ip[0], a->ip[1], a->ip[2], a->ip[3], ntohs(a->port)); return s; } const char *NET_BaseAdrToString (const netadr_t *a) { static char s[64]; sprintf (s, "%i.%i.%i.%i", a->ip[0], a->ip[1], a->ip[2], a->ip[3]); return s; } /* ============= NET_StringToAdr ============= */ qboolean NET_StringToAdr (const char *s, netadr_t *a) { struct hostent *h; struct sockaddr_in sadr; char *colon; char copy[128]; memset (&sadr, 0, sizeof(sadr)); sadr.sin_family = AF_INET; sadr.sin_port = 0; strncpy (copy, s, sizeof(copy) - 1); copy[sizeof(copy) - 1] = '\0'; // strip off a trailing :port if present for (colon = copy ; *colon ; colon++) { if (*colon == ':') { *colon = 0; sadr.sin_port = htons((short)atoi(colon+1)); } } if (copy[0] >= '0' && copy[0] <= '9') { sadr.sin_addr.s_addr = inet_addr(copy); } else { h = gethostbyname (copy); if (!h) return false; sadr.sin_addr.s_addr = *(in_addr_t *)h->h_addr_list[0]; } SockadrToNetadr (&sadr, a); return true; } //============================================================================= static unsigned char huffbuff[65536]; int NET_GetPacket (void) { int ret; struct sockaddr_in from; socklen_t fromlen; fromlen = sizeof(from); ret = recvfrom(net_socket, (char *)huffbuff, sizeof(net_message_buffer), 0, (struct sockaddr *)&from, &fromlen); if (ret == SOCKET_ERROR) { int err = SOCKETERRNO; if (err == NET_EWOULDBLOCK) return 0; if (err == NET_ECONNREFUSED) { Con_Printf ("%s: Connection refused\n", __thisfunc__); return 0; } # ifdef PLATFORM_WINDOWS if (err == WSAEMSGSIZE) { Con_Printf ("Oversize packet from %s\n", NET_AdrToString (&net_from)); return 0; } if (err == WSAECONNRESET) { Con_Printf ("Connection reset by peer %s\n", NET_AdrToString (&net_from)); return 0; } # endif /* _WINDOWS */ Sys_Error ("%s: %s", __thisfunc__, socketerror(err)); } SockadrToNetadr (&from, &net_from); if (ret == (int) sizeof(net_message_buffer)) { Con_Printf ("Oversize packet from %s\n", NET_AdrToString (&net_from)); return 0; } LastCompMessageSize += ret; /* debug: bytes actually received */ HuffDecode(huffbuff, net_message_buffer, ret, &ret, sizeof(net_message_buffer)); if (ret > (int) sizeof(net_message_buffer)) { Con_Printf ("Oversize compressed data from %s\n", NET_AdrToString (&net_from)); return 0; } net_message.cursize = ret; return ret; } //============================================================================= void NET_SendPacket (int length, void *data, const netadr_t *to) { int ret, outlen; struct sockaddr_in addr; NetadrToSockadr (to, &addr); HuffEncode((unsigned char *)data, huffbuff, length, &outlen); ret = sendto (net_socket, (char *) huffbuff, outlen, 0, (struct sockaddr *)&addr, sizeof(addr) ); if (ret == SOCKET_ERROR) { int err = SOCKETERRNO; if (err == NET_EWOULDBLOCK) return; if (err == NET_ECONNREFUSED) { Con_Printf ("%s: Connection refused\n", __thisfunc__); return; } Con_Printf ("%s ERROR: %s\n", __thisfunc__, socketerror(err)); } } //============================================================================= int NET_CheckReadTimeout (long sec, long usec) { fd_set readfds; struct timeval timeout; FD_ZERO (&readfds); FD_SET (net_socket, &readfds); timeout.tv_sec = sec; timeout.tv_usec = usec; return selectsocket(net_socket + 1, &readfds, NULL, NULL, &timeout); } //============================================================================= static sys_socket_t UDP_OpenSocket (int port) { int i; sys_socket_t newsocket; struct sockaddr_in address; #if defined(PLATFORM_WINDOWS) || defined(PLATFORM_DOS) u_long _true = 1; #else int _true = 1; #endif int err; newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (newsocket == INVALID_SOCKET) { err = SOCKETERRNO; Sys_Error ("%s: socket: %s", __thisfunc__, socketerror(err)); } if (ioctlsocket (newsocket, FIONBIO, IOCTLARG_P(&_true)) == SOCKET_ERROR) { err = SOCKETERRNO; Sys_Error ("%s: ioctl FIONBIO: %s", __thisfunc__, socketerror(err)); } memset(&address, 0, sizeof(struct sockaddr_in)); address.sin_family = AF_INET; //ZOID -- check for interface binding option i = COM_CheckParm("-ip"); if (!i) i = COM_CheckParm("-bindip"); if (i && i < com_argc-1) { address.sin_addr.s_addr = inet_addr(com_argv[i+1]); if (address.sin_addr.s_addr == INADDR_NONE) Sys_Error ("%s is not a valid IP address", com_argv[i+1]); Con_Printf("Binding to IP Interface Address of %s\n", inet_ntoa(address.sin_addr)); } else { address.sin_addr.s_addr = INADDR_ANY; } if (port == PORT_ANY) address.sin_port = 0; else address.sin_port = htons((short)port); if (bind(newsocket, (struct sockaddr *)&address, sizeof(address)) == SOCKET_ERROR) { err = SOCKETERRNO; Sys_Error ("%s: bind: %s", __thisfunc__, socketerror(err)); } return newsocket; } static void NET_GetLocalAddress (void) { char buff[MAXHOSTNAMELEN]; struct sockaddr_in address; socklen_t namelen; int err; if (gethostname(buff, MAXHOSTNAMELEN) == SOCKET_ERROR) { err = SOCKETERRNO; Sys_Error ("%s: gethostname: %s", __thisfunc__, socketerror(err)); } buff[MAXHOSTNAMELEN-1] = 0; NET_StringToAdr (buff, &net_local_adr); namelen = sizeof(address); if (getsockname (net_socket, (struct sockaddr *)&address, &namelen) == SOCKET_ERROR) { err = SOCKETERRNO; Sys_Error ("%s: getsockname: %s", __thisfunc__, socketerror(err)); } net_local_adr.port = address.sin_port; Con_SafePrintf("IP address %s\n", NET_AdrToString(&net_local_adr)); } /* ==================== NET_Init ==================== */ void NET_Init (int port) { in_addr_t a = htonl(INADDR_LOOPBACK); #ifdef PLATFORM_WINDOWS int err = WSAStartup(MAKEWORD(1,1), &winsockdata); if (err != 0) Sys_Error ("Winsock initialization failed (%s)", socketerror(err)); #endif #ifdef PLATFORM_AMIGA SocketBase = OpenLibrary("bsdsocket.library", 0); if (!SocketBase) Sys_Error ("Can't open bsdsocket.library."); #endif #if defined(PLATFORM_OS2) && !defined(__EMX__) if (sock_init() < 0) Sys_Error ("Can't initialize IBM OS/2 sockets"); #endif /* OS/2 */ #if defined(PLATFORM_DOS) && defined(USE_WATT32) int i, err; /* dbug_init();*/ i = _watt_do_exit; _watt_do_exit = 0; err = sock_init(); _watt_do_exit = i; if (err != 0) Sys_Error ("WATTCP initialization failed (%s)", sock_init_err(err)); #endif /* WatTCP */ // open the single socket to be used for all communications net_socket = UDP_OpenSocket (port); // init the message buffer SZ_Init (&net_message, net_message_buffer, sizeof(net_message_buffer)); // determine my name & address NET_GetLocalAddress (); memset (&net_loopback_adr, 0, sizeof(netadr_t)); memcpy (net_loopback_adr.ip, &a, 4); Con_SafePrintf("UDP Initialized\n"); } /* ==================== NET_Shutdown ==================== */ void NET_Shutdown (void) { if (net_socket != INVALID_SOCKET) { closesocket (net_socket); net_socket = INVALID_SOCKET; } #ifdef PLATFORM_WINDOWS WSACleanup (); #endif #ifdef PLATFORM_AMIGA if (SocketBase) { CloseLibrary(SocketBase); SocketBase = NULL; } #endif } engine/hexenworld/shared/pmove.c000066400000000000000000000544361444734033100172610ustar00rootroot00000000000000/* * pmove.c -- player movement * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" movevars_t movevars; playermove_t pmove; int onground; int waterlevel; int watertype; static float frametime; static vec3_t forward, right, up; vec3_t player_mins = {-16, -16, 0}; vec3_t player_maxs = {16, 16, 56}; vec3_t player_maxs_crouch = {16, 16, 28}; vec3_t beast_mins = {-48, -48, 0}; vec3_t beast_maxs = {48, 48, 100}; // #define PM_GRAVITY 800 // #define PM_STOPSPEED 100 // #define PM_MAXSPEED 320 // #define PM_SPECTATORMAXSPEED 500 // #define PM_ACCELERATE 10 // #define PM_AIRACCELERATE 0.7 // #define PM_WATERACCELERATE 10 // #define PM_FRICTION 6 // #define PM_WATERFRICTION 1 extern void PM_InitBoxHull (void); void Pmove_Init (void) { PM_InitBoxHull (); } #define STEPSIZE 18 #define BUTTON_JUMP 2 /* ================== PM_ClipVelocity Slide off of the impacting object returns the blocked flags (1 = floor, 2 = step / wall) ================== */ #define STOP_EPSILON 0.1 static int PM_ClipVelocity (const vec3_t in, const vec3_t normal, vec3_t out, float overbounce) { float backoff; float change; int i, blocked; blocked = 0; if (normal[2] > 0) blocked |= 1; // floor if (!normal[2]) blocked |= 2; // step backoff = DotProduct (in, normal) * overbounce; for (i = 0; i < 3; i++) { change = normal[i]*backoff; out[i] = in[i] - change; if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) out[i] = 0; } return blocked; } /* ============ PM_FlyMove The basic solid body movement clip that slides along multiple planes ============ */ #define MAX_CLIP_PLANES 5 static int PM_FlyMove (void) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity, original_velocity; int i, j; pmtrace_t trace; vec3_t end; float time_left; int blocked; numbumps = 4; blocked = 0; VectorCopy (pmove.velocity, original_velocity); VectorCopy (pmove.velocity, primal_velocity); numplanes = 0; time_left = frametime; for (bumpcount = 0; bumpcount < numbumps; bumpcount++) { for (i = 0; i < 3; i++) end[i] = pmove.origin[i] + time_left * pmove.velocity[i]; trace = PM_PlayerMove (pmove.origin, end); if (trace.startsolid || trace.allsolid) { // entity is trapped in another solid VectorClear (pmove.velocity); return 3; } if (trace.fraction > 0) { // actually covered some distance VectorCopy (trace.endpos, pmove.origin); numplanes = 0; } if (trace.fraction == 1) break; // moved the entire distance // save entity for contact pmove.touchindex[pmove.numtouch] = trace.ent; pmove.numtouch++; if (trace.plane.normal[2] > 0.7) { blocked |= 1; // floor } if (!trace.plane.normal[2]) { blocked |= 2; // step } time_left -= time_left * trace.fraction; // cliped to another plane if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen VectorClear (pmove.velocity); break; } VectorCopy (trace.plane.normal, planes[numplanes]); numplanes++; // // modify original_velocity so it parallels all of the clip planes // for (i = 0; i < numplanes; i++) { PM_ClipVelocity (original_velocity, planes[i], pmove.velocity, 1); for (j = 0; j < numplanes; j++) { if (j != i) { if (DotProduct (pmove.velocity, planes[j]) < 0) break; // not ok } } if (j == numplanes) break; } if (i != numplanes) { // go along this plane } else { // go along the crease if (numplanes != 2) { // Con_Printf ("clip velocity, numplanes == %i\n",numplanes); VectorClear (pmove.velocity); break; } CrossProduct (planes[0], planes[1], dir); d = DotProduct (dir, pmove.velocity); VectorScale (dir, d, pmove.velocity); } // // if original velocity is against the original velocity, stop dead // to avoid tiny occilations in sloping corners // if (DotProduct (pmove.velocity, primal_velocity) <= 0) { VectorClear (pmove.velocity); break; } } if (pmove.waterjumptime) { VectorCopy (primal_velocity, pmove.velocity); } return blocked; } /* ============= PM_GroundMove Player is on ground, with no upwards velocity ============= */ static void PM_GroundMove (void) { vec3_t start, dest; pmtrace_t trace; vec3_t original, originalvel, down, downvel; //vec3_t up; float downdist, updist; pmove.velocity[2] = 0; if (!pmove.velocity[0] && !pmove.velocity[1] && !pmove.velocity[2]) return; // first try just moving to the destination dest[0] = pmove.origin[0] + pmove.velocity[0]*frametime; dest[1] = pmove.origin[1] + pmove.velocity[1]*frametime; dest[2] = pmove.origin[2]; // first try moving directly to the next spot VectorCopy (dest, start); (void) start; /* variable set but not used */ trace = PM_PlayerMove (pmove.origin, dest); if (trace.fraction == 1) { VectorCopy (trace.endpos, pmove.origin); return; } // try sliding forward both on ground and up 16 pixels // take the move that goes farthest VectorCopy (pmove.origin, original); VectorCopy (pmove.velocity, originalvel); // slide move PM_FlyMove (); VectorCopy (pmove.origin, down); VectorCopy (pmove.velocity, downvel); VectorCopy (original, pmove.origin); VectorCopy (originalvel, pmove.velocity); // move up a stair height VectorCopy (pmove.origin, dest); dest[2] += STEPSIZE; trace = PM_PlayerMove (pmove.origin, dest); if (!trace.startsolid && !trace.allsolid) { VectorCopy (trace.endpos, pmove.origin); } // slide move PM_FlyMove (); // press down the stepheight VectorCopy (pmove.origin, dest); dest[2] -= STEPSIZE; trace = PM_PlayerMove (pmove.origin, dest); if ( trace.plane.normal[2] < 0.7) goto usedown; if (!trace.startsolid && !trace.allsolid) { VectorCopy (trace.endpos, pmove.origin); } VectorCopy (pmove.origin, up); // decide which one went farther downdist = (down[0] - original[0])*(down[0] - original[0]) + (down[1] - original[1])*(down[1] - original[1]); updist = (up[0] - original[0])*(up[0] - original[0]) + (up[1] - original[1])*(up[1] - original[1]); if (downdist > updist) { usedown: VectorCopy (down, pmove.origin); VectorCopy (downvel, pmove.velocity); } else // copy z value from slide move pmove.velocity[2] = downvel[2]; // if at a dead stop, retry the move with nudges to get around lips } /* ================== PM_Friction Handles both ground friction and water friction ================== */ static void PM_Friction (void) { float *vel; float speed, newspeed, control; float friction; float drop; if (pmove.waterjumptime) return; vel = pmove.velocity; speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1] + vel[2]*vel[2]); if (speed < 1) { vel[0] = 0; vel[1] = 0; return; } drop = 0; // apply ground friction if ((onground != -1) || (pmove.movetype == MOVETYPE_FLY)) { friction = movevars.friction; control = speed < movevars.stopspeed ? movevars.stopspeed : speed; drop += control*friction*frametime; } // apply water friction if (waterlevel) drop += speed*movevars.waterfriction*waterlevel*frametime; // scale the velocity newspeed = speed - drop; if (newspeed < 0) newspeed = 0; newspeed /= speed; vel[0] = vel[0] * newspeed; vel[1] = vel[1] * newspeed; vel[2] = vel[2] * newspeed; } /* ============== PM_Accelerate ============== */ static void PM_Accelerate (const vec3_t wishdir, float wishspeed, float accel) { int i; float addspeed, accelspeed, currentspeed; if (pmove.dead) return; if (pmove.waterjumptime) return; currentspeed = DotProduct (pmove.velocity, wishdir); addspeed = wishspeed - currentspeed; if (addspeed <= 0) return; accelspeed = accel*frametime*wishspeed; if (accelspeed > addspeed) accelspeed = addspeed; for (i = 0; i < 3; i++) pmove.velocity[i] += accelspeed*wishdir[i]; } static void PM_AirAccelerate (const vec3_t wishdir, float wishspeed, float accel) { int i; float addspeed, accelspeed, currentspeed, wishspd = wishspeed; if (pmove.dead) return; if (pmove.waterjumptime) return; if (wishspd > 30) wishspd = 30; currentspeed = DotProduct (pmove.velocity, wishdir); addspeed = wishspd - currentspeed; if (addspeed <= 0) return; accelspeed = accel * wishspeed * frametime; if (accelspeed > addspeed) accelspeed = addspeed; for (i = 0; i < 3; i++) pmove.velocity[i] += accelspeed*wishdir[i]; } /* =================== PM_WaterMove =================== */ static void PM_WaterMove (void) { int i; vec3_t wishvel; float wishspeed; vec3_t wishdir; vec3_t start, dest; pmtrace_t trace; float fmove, smove, clamp; // // user intentions // fmove = pmove.cmd.forwardmove; smove = pmove.cmd.sidemove; // client 400 (assasin running) is scaled to maxspeed // clamp is tested seperately so strafe running works clamp = movevars.maxspeed * pmove.hasted; if (fabs(fmove) > clamp) { if (fmove < 0) fmove = -clamp; else fmove = clamp; } if (fabs(smove) > clamp) { if (smove < 0) smove = -clamp; else smove = clamp; } if (pmove.crouched) { fmove = fmove / 600 * movevars.maxspeed; smove = smove / 600 * movevars.maxspeed; } else { fmove = fmove / 400 * movevars.maxspeed; smove = smove / 400 * movevars.maxspeed; } for (i = 0; i < 3; i++) wishvel[i] = forward[i]*fmove + right[i]*smove; if ((!pmove.cmd.forwardmove && !pmove.cmd.sidemove && !pmove.cmd.upmove)&&pmove.crouched) wishvel[2] -= 120; // drift towards bottom else if ((!pmove.cmd.forwardmove && !pmove.cmd.sidemove && !pmove.cmd.upmove)||pmove.crouched) wishvel[2] -= 60; // drift towards bottom else wishvel[2] += pmove.cmd.upmove; VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); if (wishspeed > (movevars.maxspeed * pmove.hasted)) { VectorScale (wishvel, (movevars.maxspeed * pmove.hasted)/wishspeed, wishvel); wishspeed = (movevars.maxspeed * pmove.hasted); } wishspeed *= 0.7; if (wishspeed > (movevars.maxspeed * pmove.hasted)) { VectorScale (wishvel, (movevars.maxspeed * pmove.hasted)/wishspeed, wishvel); wishspeed = (movevars.maxspeed * pmove.hasted); } // // water acceleration // // if (pmove.waterjumptime) // Con_Printf ("wm->%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); PM_Accelerate (wishdir, wishspeed, movevars.wateraccelerate); // assume it is a stair or a slope, so press down from stepheight above VectorMA (pmove.origin, frametime, pmove.velocity, dest); VectorCopy (dest, start); start[2] += STEPSIZE + 1; trace = PM_PlayerMove (start, dest); if (!trace.startsolid && !trace.allsolid) // FIXME: check steep slope? { // walked up the step VectorCopy (trace.endpos, pmove.origin); return; } PM_FlyMove (); // if (pmove.waterjumptime) // Con_Printf ("<-wm%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); } /* =================== PM_AirMove =================== */ static void PM_AirMove (void) { int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed, clamp; if (pmove.teleport_time < realtime) { //no Mario Bros. stop in mid air! fmove = pmove.cmd.forwardmove; smove = pmove.cmd.sidemove; } else fmove = smove = 0; // client 400 (assasin running) is scaled to maxspeed // clamp is tested seperately so strafe running works clamp = movevars.maxspeed * pmove.hasted; if (fabs(fmove) > clamp) { if (fmove < 0) fmove = -clamp; else fmove = clamp; } if (fabs(smove) > clamp) { if (smove < 0) smove = -clamp; else smove = clamp; } fmove = fmove / 400 * movevars.maxspeed; smove = smove / 400 * movevars.maxspeed; // Con_Printf("fmove %f\n", fmove); forward[2] = 0; right[2] = 0; VectorNormalize (forward); VectorNormalize (right); for (i = 0; i < 2; i++) wishvel[i] = forward[i]*fmove*pmove.hasted + right[i]*smove*pmove.hasted; wishvel[2] = 0; VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); // // clamp to server defined max speed // if (wishspeed > (movevars.maxspeed * pmove.hasted)) { VectorScale (wishvel, (movevars.maxspeed * pmove.hasted)/wishspeed, wishvel); wishspeed = (movevars.maxspeed * pmove.hasted); } // if (pmove.waterjumptime) // Con_Printf ("am->%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); if ( onground != -1) { pmove.velocity[2] = 0; PM_Accelerate (wishdir, wishspeed, movevars.accelerate); PM_GroundMove (); } else { // not on ground, so little effect on velocity PM_AirAccelerate (wishdir, wishspeed, movevars.accelerate); PM_FlyMove (); // add gravity pmove.velocity[2] -= movevars.entgravity * movevars.gravity * frametime; } // Con_Printf("airmove:vec: %4.2f %4.2f %4.2f\n", // pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); // if (pmove.waterjumptime) // Con_Printf ("<-am%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); } /* =================== PM_FlyingMove - for when you are using the ring of flight =================== */ static void PM_FlyingMove (void) { int i; vec3_t wishvel; float fmove, smove, umove; vec3_t wishdir; float wishspeed, clamp; fmove = pmove.cmd.forwardmove; smove = pmove.cmd.sidemove; umove = pmove.cmd.upmove; // client 400 (assasin running) is scaled to maxspeed // clamp is tested seperately so strafe running works clamp = movevars.maxspeed * pmove.hasted; if (fabs(fmove) > clamp) { if (fmove < 0) fmove = -clamp; else fmove = clamp; } if (fabs(smove) > clamp) { if (smove < 0) smove = -clamp; else smove = clamp; } if (fabs(umove) > clamp) { if (umove < 0) umove = -clamp; else umove = clamp; } fmove = fmove / 400 * movevars.maxspeed; smove = smove / 400 * movevars.maxspeed; umove = umove / 400 * movevars.maxspeed; // Con_Printf("fmove %f\n", fmove); VectorNormalize (forward); VectorNormalize (right); VectorNormalize (up); for (i = 0; i < 3; i++) wishvel[i] = forward[i]*fmove + right[i]*smove + up[i]*umove; VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); // // clamp to server defined max speed // /* if (wishspeed > (movevars.maxspeed * pmove.hasted)) { VectorScale (wishvel, (movevars.maxspeed * pmove.hasted)/wishspeed, wishvel); wishspeed = (movevars.maxspeed * pmove.hasted); } */ // if (pmove.waterjumptime) // Con_Printf ("am->%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); PM_Accelerate (wishdir, wishspeed, movevars.accelerate); PM_FlyMove (); // Con_Printf("airmove:vec: %4.2f %4.2f %4.2f\n", // pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); // if (pmove.waterjumptime) // Con_Printf ("<-am%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); } /* ============= PM_CatagorizePosition ============= */ static void PM_CatagorizePosition (void) { vec3_t point; int cont; pmtrace_t tr; // if the player hull point one unit down is solid, the player // is on ground // see if standing on something solid point[0] = pmove.origin[0]; point[1] = pmove.origin[1]; point[2] = pmove.origin[2] - 1; if (pmove.velocity[2] > 180) { onground = -1; } else { tr = PM_PlayerMove (pmove.origin, point); if ( tr.plane.normal[2] < 0.7) onground = -1; // too steep else onground = tr.ent; if (onground != -1) { pmove.waterjumptime = 0; if (!tr.startsolid && !tr.allsolid) VectorCopy (tr.endpos, pmove.origin); } // standing on an entity other than the world if (tr.ent > 0) { pmove.touchindex[pmove.numtouch] = tr.ent; pmove.numtouch++; } } // // get waterlevel // waterlevel = 0; watertype = CONTENTS_EMPTY; point[2] = pmove.origin[2] + player_mins[2] + 1; cont = PM_PointContents (point); if (cont <= CONTENTS_WATER) { watertype = cont; waterlevel = 1; point[2] += 26; cont = PM_PointContents (point); if (cont <= CONTENTS_WATER) { waterlevel = 2; point[2] += 22; cont = PM_PointContents (point); if (cont <= CONTENTS_WATER) waterlevel = 3; } } } /* ============= JumpButton ============= */ static void JumpButton (void) { if (pmove.dead) { pmove.oldbuttons |= BUTTON_JUMP; // don't jump again until released return; } if (pmove.waterjumptime) { pmove.waterjumptime -= frametime; if (pmove.waterjumptime < 0) pmove.waterjumptime = 0; return; } if (waterlevel >= 2) { // swimming, not jumping onground = -1; if (watertype == CONTENTS_WATER) pmove.velocity[2] = 100; else if (watertype == CONTENTS_SLIME) pmove.velocity[2] = 80; else pmove.velocity[2] = 50; return; } if (onground == -1) return; // in air, so no effect if ( pmove.oldbuttons & BUTTON_JUMP ) return; // don't pogo stick onground = -1; pmove.velocity[2] += 270; pmove.oldbuttons |= BUTTON_JUMP; // don't jump again until released } /* ============= CheckWaterJump ============= */ static void CheckWaterJump (void) { vec3_t spot; int cont; vec3_t flatforward; if (pmove.waterjumptime) return; // Con_Printf("wjvel: %4.2f %4.2f %4.2f: ", // pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]); // ZOID, don't hop out if we just jumped in if (pmove.velocity[2] < -180) { //Con_Printf("downcut\n"); return; // only hop out if we are moving up } // see if near an edge flatforward[0] = forward[0]; flatforward[1] = forward[1]; flatforward[2] = 0; VectorNormalize (flatforward); VectorMA (pmove.origin, 24, flatforward, spot); spot[2] += 32; cont = PM_PointContents (spot); if (cont != CONTENTS_SOLID) { //Con_Printf("notsolid\n"); return; } spot[2] += 24; cont = PM_PointContents (spot); if (cont != CONTENTS_EMPTY) { //Con_Printf("notempty\n"); return; } // jump out of water VectorScale (forward, 200, pmove.velocity); pmove.velocity[2] = 275; pmove.waterjumptime = 2; // safety net pmove.oldbuttons |= BUTTON_JUMP; // don't jump again until released // Con_Printf ("Waterjump (%6.2f,%6.2f,%6.2f) (%6.2f,%6.2f,%6.2f)\n", // pmove.origin[0], pmove.origin[1], pmove.origin[2], // pmove.velocity[0],pmove.velocity[1],pmove.velocity[2]); } /* ================= NudgePosition If pmove.origin is in a solid position, try nudging slightly on all axis to allow for the cut precision of the net coordinates ================= */ static void NudgePosition (void) { vec3_t base; int x, y, z; int i; VectorCopy (pmove.origin, base); for (i = 0; i < 3; i++) pmove.origin[i] = ((int)(pmove.origin[i]*8)) * 0.125; pmove.origin[2] += 0.124; // pmove.origin[2] += 1.124; // if (pmove.dead) // return; // might be a squished point, so don'y bother if (PM_TestPlayerPosition (pmove.origin) ) return; for (z = -1; z <= 1; z++) { for (x = -1; x <= 1; x++) { for (y = -1; y <= 1; y++) { pmove.origin[0] = base[0] + x * 1.0/8; pmove.origin[1] = base[1] + y * 1.0/8; pmove.origin[2] = base[2] + z * 1.0/8; if (PM_TestPlayerPosition (pmove.origin)) return; } } } VectorCopy (base, pmove.origin); // Con_DPrintf ("NudgePosition: stuck\n"); } /* =============== SpectatorMove =============== */ static void SpectatorMove (void) { float speed, drop, friction, control, newspeed; float currentspeed, addspeed, accelspeed; int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed; // friction speed = VectorLength (pmove.velocity); if (speed < 1) { VectorClear (pmove.velocity); } else { drop = 0; friction = movevars.friction*1.5; // extra friction control = speed < movevars.stopspeed ? movevars.stopspeed : speed; drop += control*friction*frametime; // scale the velocity newspeed = speed - drop; if (newspeed < 0) newspeed = 0; newspeed /= speed; VectorScale (pmove.velocity, newspeed, pmove.velocity); } // accelerate fmove = pmove.cmd.forwardmove; smove = pmove.cmd.sidemove; VectorNormalize (forward); VectorNormalize (right); for (i = 0; i < 3; i++) wishvel[i] = forward[i]*fmove + right[i]*smove; wishvel[2] += pmove.cmd.upmove; VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); // // clamp to server defined max speed // if (wishspeed > movevars.spectatormaxspeed) { VectorScale (wishvel, movevars.spectatormaxspeed/wishspeed, wishvel); wishspeed = movevars.spectatormaxspeed; } currentspeed = DotProduct(pmove.velocity, wishdir); addspeed = wishspeed - currentspeed; if (addspeed <= 0) return; accelspeed = movevars.accelerate*frametime*wishspeed; if (accelspeed > addspeed) accelspeed = addspeed; for (i = 0; i < 3; i++) pmove.velocity[i] += accelspeed*wishdir[i]; // move VectorMA (pmove.origin, frametime, pmove.velocity, pmove.origin); } /* ============= PlayerMove Returns with origin, angles, and velocity modified in place. Numtouch and touchindex[] will be set if any of the physents were contacted during the move. ============= */ void PlayerMove (void) { frametime = pmove.cmd.msec * 0.001; pmove.numtouch = 0; if (pmove.movetype == MOVETYPE_NONE) { pmove.oldbuttons = 0; pmove.cmd.buttons = 0; return; } AngleVectors (pmove.angles, forward, right, up); if (pmove.spectator) { SpectatorMove (); return; } NudgePosition (); // take angles directly from command VectorCopy (pmove.cmd.angles, pmove.angles); // set onground, watertype, and waterlevel PM_CatagorizePosition (); if (waterlevel == 1) CheckWaterJump (); if (pmove.velocity[2] < 0 && pmove.waterjumptime) { pmove.waterjumptime = 0; // Con_Printf ("Waterjump *OVER*\n"); } if (pmove.cmd.buttons & BUTTON_JUMP) JumpButton (); else pmove.oldbuttons &= ~BUTTON_JUMP; PM_Friction (); if (waterlevel >= 2) { PM_WaterMove (); } else { if (pmove.movetype == MOVETYPE_FLY) PM_FlyingMove(); else PM_AirMove (); // else//WHY THE FUCK DO I KEEP MOVING ANYWAY?!!!! WORKED IN MP!!! // Con_Printf("No Air move, teleport_time > realtime\n"); } // set onground, watertype, and waterlevel for final spot PM_CatagorizePosition (); } engine/hexenworld/shared/pmove.h000066400000000000000000000051561444734033100172610ustar00rootroot00000000000000/* * pmove.h -- player movement * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __PLAYERMOVE_H #define __PLAYERMOVE_H typedef struct { vec3_t normal; float dist; } pmplane_t; typedef struct { qboolean allsolid; // if true, plane is not valid qboolean startsolid; // if true, the initial point was in a solid area qboolean inopen, inwater; float fraction; // time completed, 1.0 = didn't hit anything vec3_t endpos; // final position pmplane_t plane; // surface normal at impact int ent; // entity the surface is on } pmtrace_t; #define MAX_PHYSENTS 64 typedef struct { vec3_t origin; qmodel_t *model; // only for bsp models vec3_t mins, maxs; // only for non-bsp models vec3_t angles; int info; // for client or server to identify } physent_t; typedef struct { int sequence; // just for debugging prints // player state vec3_t origin; vec3_t angles; vec3_t velocity; int oldbuttons; float waterjumptime; float teleport_time; qboolean dead; int spectator; float hasted; int movetype; qboolean crouched; // world state int numphysent; physent_t physents[MAX_PHYSENTS]; // 0 should be the world // input usercmd_t cmd; // results int numtouch; int touchindex[MAX_PHYSENTS]; } playermove_t; typedef struct { float gravity; float stopspeed; float maxspeed; float spectatormaxspeed; float accelerate; float airaccelerate; float wateraccelerate; float friction; float waterfriction; float entgravity; } movevars_t; extern movevars_t movevars; extern playermove_t pmove; extern int onground; extern int waterlevel; extern int watertype; void PlayerMove (void); void Pmove_Init (void); int PM_PointContents (vec3_t point); int PM_HullPointContents (hull_t *hull, int num, vec3_t p); qboolean PM_TestPlayerPosition (vec3_t point); pmtrace_t PM_PlayerMove (vec3_t start, vec3_t stop); #endif /* __PLAYERMOVE_H */ engine/hexenworld/shared/pmovetst.c000066400000000000000000000273311444734033100200060ustar00rootroot00000000000000/* * pmovetst.c -- player movement testing * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "quakedef.h" static hull_t box_hull; static mclipnode_t box_clipnodes[6]; static mplane_t box_planes[6]; extern vec3_t player_mins; extern vec3_t player_maxs; extern vec3_t player_maxs_crouch; extern vec3_t beast_mins; extern vec3_t beast_maxs; /* =================== PM_InitBoxHull Set up the planes and clipnodes so that the six floats of a bounding box can just be stored out and get a proper hull_t structure. =================== */ void PM_InitBoxHull (void) { int i; int side; box_hull.clipnodes = box_clipnodes; box_hull.planes = box_planes; box_hull.firstclipnode = 0; box_hull.lastclipnode = 5; for (i = 0; i < 6; i++) { box_clipnodes[i].planenum = i; side = i & 1; box_clipnodes[i].children[side] = CONTENTS_EMPTY; if (i != 5) box_clipnodes[i].children[side^1] = i + 1; else box_clipnodes[i].children[side^1] = CONTENTS_SOLID; box_planes[i].type = i>>1; box_planes[i].normal[i>>1] = 1; } } /* =================== PM_HullForBox To keep everything totally uniform, bounding boxes are turned into small BSP trees instead of being compared directly. =================== */ static hull_t *PM_HullForBox (vec3_t mins, vec3_t maxs) { box_planes[0].dist = maxs[0]; box_planes[1].dist = mins[0]; box_planes[2].dist = maxs[1]; box_planes[3].dist = mins[1]; box_planes[4].dist = maxs[2]; box_planes[5].dist = mins[2]; return &box_hull; } /* ================== PM_HullPointContents ================== */ int PM_HullPointContents (hull_t *hull, int num, vec3_t p) { float d; mclipnode_t *node; mplane_t *plane; while (num >= 0) { if (num < hull->firstclipnode || num > hull->lastclipnode) Sys_Error ("%s: bad node number", __thisfunc__); node = hull->clipnodes + num; plane = hull->planes + node->planenum; if (plane->type < 3) d = p[plane->type] - plane->dist; else d = DotProduct (plane->normal, p) - plane->dist; if (d < 0) num = node->children[1]; else num = node->children[0]; } return num; } /* ================== PM_PointContents ================== */ int PM_PointContents (vec3_t p) { float d; mclipnode_t *node; mplane_t *plane; hull_t *hull; int num; hull = &pmove.physents[0].model->hulls[0]; num = hull->firstclipnode; while (num >= 0) { if (num < hull->firstclipnode || num > hull->lastclipnode) Sys_Error ("%s: bad node number", __thisfunc__); node = hull->clipnodes + num; plane = hull->planes + node->planenum; if (plane->type < 3) d = p[plane->type] - plane->dist; else d = DotProduct (plane->normal, p) - plane->dist; if (d < 0) num = node->children[1]; else num = node->children[0]; } return num; } /* =============================================================================== LINE TESTING IN HULLS =============================================================================== */ /* ================== PM_RecursiveHullCheck ================== */ static qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace) { mclipnode_t *node; mplane_t *plane; float t1, t2; float frac; int i; vec3_t mid; int side; float midf; // check for empty if (num < 0) { if (num != CONTENTS_SOLID) { trace->allsolid = false; if (num == CONTENTS_EMPTY) trace->inopen = true; else trace->inwater = true; } else trace->startsolid = true; return true; // empty } if (num < hull->firstclipnode || num > hull->lastclipnode) Sys_Error ("%s: bad node number", __thisfunc__); // // find the point distances // node = hull->clipnodes + num; plane = hull->planes + node->planenum; if (plane->type < 3) { t1 = p1[plane->type] - plane->dist; t2 = p2[plane->type] - plane->dist; } else { t1 = DotProduct (plane->normal, p1) - plane->dist; t2 = DotProduct (plane->normal, p2) - plane->dist; } #if 1 if (t1 >= 0 && t2 >= 0) return PM_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); if (t1 < 0 && t2 < 0) return PM_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); #else if ( (t1 >= DIST_EPSILON && t2 >= DIST_EPSILON) || (t2 > t1 && t1 >= 0) ) return PM_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); if ( (t1 <= -DIST_EPSILON && t2 <= -DIST_EPSILON) || (t2 < t1 && t1 <= 0) ) return PM_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); #endif // put the crosspoint DIST_EPSILON pixels on the near side if (t1 < 0) frac = (t1 + DIST_EPSILON)/(t1-t2); else frac = (t1 - DIST_EPSILON)/(t1-t2); if (frac < 0) frac = 0; if (frac > 1) frac = 1; midf = p1f + (p2f - p1f)*frac; for (i = 0; i < 3; i++) mid[i] = p1[i] + frac*(p2[i] - p1[i]); side = (t1 < 0); // move up to the node if (!PM_RecursiveHullCheck (hull, node->children[side], p1f, midf, p1, mid, trace) ) return false; #ifdef PARANOID if (PM_HullPointContents (hull, node->children[side], mid) == CONTENTS_SOLID) { Con_Printf ("mid PointInHullSolid\n"); return false; } #endif if (PM_HullPointContents (hull, node->children[side^1], mid) != CONTENTS_SOLID) // go past the node return PM_RecursiveHullCheck (hull, node->children[side^1], midf, p2f, mid, p2, trace); if (trace->allsolid) return false; // never got out of the solid area //================== // the other side of the node is solid, this is the impact point //================== if (!side) { VectorCopy (plane->normal, trace->plane.normal); trace->plane.dist = plane->dist; } else { VectorNegate (plane->normal, trace->plane.normal); trace->plane.dist = -plane->dist; } while (PM_HullPointContents (hull, hull->firstclipnode, mid) == CONTENTS_SOLID) { // shouldn't really happen, but does occasionally frac -= 0.1; if (frac < 0) { trace->fraction = midf; VectorCopy (mid, trace->endpos); Con_DPrintf ("backup past 0\n"); return false; } midf = p1f + (p2f - p1f)*frac; for (i = 0; i < 3; i++) mid[i] = p1[i] + frac*(p2[i] - p1[i]); } trace->fraction = midf; VectorCopy (mid, trace->endpos); return false; } /* ================ PM_TestPlayerPosition Returns false if the given player position is not valid (in solid) ================ */ qboolean PM_TestPlayerPosition (vec3_t pos) { #if 0 /* FIXME: Unreachable code. See also: NudgePosition(). Compare QW and HW codes someday. */ int i; physent_t *pe; vec3_t mins, maxs, test; hull_t *hull; #endif /* Unreachable code */ pmtrace_t trace; trace = PM_PlayerMove (pos, pos); if (trace.allsolid || trace.startsolid) { return false; } return true; #if 0 /* Unreachable code: see above. */ for (i = 0; i < pmove.numphysent; i++) { pe = &pmove.physents[i]; // get the clipping hull if (0){}/*shitbox pmove.hasted == 1.666) //hacky- beast speed { VectorCopy (beast_maxs, maxs); VectorCopy (beast_mins, mins); hull = &pmove.physents[i].model->hulls[5]; }*/ else if (pe->model) { if (pmove.crouched) { hull = &pmove.physents[i].model->hulls[3]; } else { hull = &pmove.physents[i].model->hulls[1]; } } else { if (pmove.crouched) { VectorSubtract (pe->mins, player_maxs_crouch, mins); } else { VectorSubtract (pe->mins, player_maxs, mins); } VectorSubtract (pe->maxs, player_mins, maxs); hull = PM_HullForBox (mins, maxs); } VectorSubtract (pos, pe->origin, test); // rjr will need to adjust for player when going into different hulls test[2] -= hull->clip_mins[2]; if (PM_HullPointContents (hull, hull->firstclipnode, test) == CONTENTS_SOLID) return false; } return true; #endif /* Unreachable code */ } /* ================ PM_PlayerMove ================ */ pmtrace_t PM_PlayerMove (vec3_t start, vec3_t end) { pmtrace_t trace, total; vec3_t offset; vec3_t start_l, end_l; hull_t *hull; int i; physent_t *pe; vec3_t mins, maxs; // fill in a default trace memset (&total, 0, sizeof(pmtrace_t)); total.fraction = 1; total.ent = -1; VectorCopy (end, total.endpos); for (i = 0; i < pmove.numphysent; i++) { pe = &pmove.physents[i]; // get the clipping hull if (0){}/*shitbox pmove.hasted == 1.666) //hacky- beast speed { VectorCopy (beast_maxs, maxs); VectorCopy (beast_mins, mins); hull = &pmove.physents[i].model->hulls[5]; }*/ else if (pe->model) { if (pmove.crouched) { hull = &pmove.physents[i].model->hulls[3]; } else { hull = &pmove.physents[i].model->hulls[1]; } } else { if (pmove.crouched) { VectorSubtract (pe->mins, player_maxs_crouch, mins); } else { VectorSubtract (pe->mins, player_maxs, mins); } VectorSubtract (pe->maxs, player_mins, maxs); hull = PM_HullForBox (mins, maxs); } // PM_HullForEntity (ent, mins, maxs, offset); VectorCopy (pe->origin, offset); VectorSubtract (start, offset, start_l); VectorSubtract (end, offset, end_l); if (pe->model && (fabs(pe->angles[0]) > 1 || fabs(pe->angles[1]) > 1 || fabs(pe->angles[2]) > 1) ) { vec3_t forward, right, up; vec3_t temp; AngleVectors (pe->angles, forward, right, up); VectorCopy (start_l, temp); start_l[0] = DotProduct (temp, forward); start_l[1] = -DotProduct (temp, right); start_l[2] = DotProduct (temp, up); VectorCopy (end_l, temp); end_l[0] = DotProduct (temp, forward); end_l[1] = -DotProduct (temp, right); end_l[2] = DotProduct (temp, up); } // rjr will need to adjust for player when going into different hulls start_l[2] -= hull->clip_mins[2]; end_l[2] -= hull->clip_mins[2]; // fill in a default trace memset (&trace, 0, sizeof(pmtrace_t)); trace.fraction = 1; trace.allsolid = true; //trace.startsolid = true; VectorCopy (end, trace.endpos); // rjr will need to adjust for player when going into different hulls trace.endpos[2] -= hull->clip_mins[2]; // trace a line through the apropriate clipping hull PM_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace); // rjr will need to adjust for player when going into different hulls trace.endpos[2] += hull->clip_mins[2]; if (pe->model && (fabs(pe->angles[0]) > 1 || fabs(pe->angles[1]) > 1 || fabs(pe->angles[2]) > 1) ) { vec3_t a; vec3_t forward, right, up; vec3_t temp; if (trace.fraction != 1) { VectorNegate (pe->angles, a); AngleVectors (a, forward, right, up); VectorCopy (trace.endpos, temp); trace.endpos[0] = DotProduct (temp, forward); trace.endpos[1] = -DotProduct (temp, right); trace.endpos[2] = DotProduct (temp, up); VectorCopy (trace.plane.normal, temp); trace.plane.normal[0] = DotProduct (temp, forward); trace.plane.normal[1] = -DotProduct (temp, right); trace.plane.normal[2] = DotProduct (temp, up); } } if (trace.allsolid) trace.startsolid = true; if (trace.startsolid) trace.fraction = 0; // did we clip the move? if (trace.fraction < total.fraction) { // fix trace up by the offset VectorAdd (trace.endpos, offset, trace.endpos); total = trace; total.ent = i; } } return total; } engine/hexenworld/shared/progdefs.h000066400000000000000000000335321444734033100177430ustar00rootroot00000000000000/* * progdefs.h -- PROGS structures: Generated by HCC. Do not * modify unless you know what you are doing. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * Copyright (C) 2005-2012 O.Sezer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __PROGDEFS_H #define __PROGDEFS_H /* progs from hw releases older than v0.14 (0.12?) do NOT have Siege * support, including the lack of the "siege_team" member in * entvars_t, therefore siege_team must be accessed carefully. * * progs from hw-0.09 (AFAIK, the initial hw release) is ancient. * there is no hwprogs.dat but progs.dat and progs2.dat. it is * much more close to h2-v1.11 progs and has no praevus mission * pack support either, therefore four classes only. it has no * has_portals and gravity members in its entvars_t. */ /* hexenworld v0.09 progdefs: */ typedef struct { int pad[28]; int self; int other; int world; float time; float frametime; int newmis; float force_retouch; string_t mapname; string_t startspot; float deathmatch; float randomclass; float damageScale; /* meleeDamScale and shyRespawn * not in v0.09. */ /* spartanPrint not in v0.11 */ float manaScale; float tomeMode; float tomeRespawn; float w2Respawn; float altRespawn; float fixedLevel; float autoItems; float dmMode; /* easyFourth and patternRunner * not in v0.09. */ float coop; float teamplay; float serverflags; float total_secrets; float total_monsters; float found_secrets; float killed_monsters; float chunk_cnt; float done_precache; float parm1; float parm2; float parm4; float parm5; float parm6; float parm7; float parm8; float parm9; float parm10; float parm11; float parm12; float parm13; float parm14; float parm15; float parm16; string_t parm3; vec3_t v_forward; vec3_t v_up; vec3_t v_right; float trace_allsolid; float trace_startsolid; float trace_fraction; vec3_t trace_endpos; vec3_t trace_plane_normal; float trace_plane_dist; int trace_ent; float trace_inopen; float trace_inwater; int msg_entity; float cycle_wrapped; float crouch_cnt; float modelindex_assassin; float modelindex_crusader; float modelindex_paladin; float modelindex_necromancer; float modelindex_sheep; float num_players; float exp_mult; /* max_players, defLosses and attLosses * are for Siege: not in v0.11. */ func_t main; func_t StartFrame; func_t PlayerPreThink; func_t PlayerPostThink; func_t ClientKill; func_t ClientConnect; func_t PutClientInServer; func_t ClientReEnter; func_t ClientDisconnect; func_t ClassChangeWeapon; func_t SetNewParms; func_t SetChangeParms; /* SmitePlayer only in v0.15 */ } globalvars_v009_t; /* hexenworld v0.11 progdefs: */ typedef struct { int pad[28]; int self; int other; int world; float time; float frametime; int newmis; float force_retouch; string_t mapname; string_t startspot; float deathmatch; float randomclass; float damageScale; float meleeDamScale; float shyRespawn; /* spartanPrint not in v0.11 */ float manaScale; float tomeMode; float tomeRespawn; float w2Respawn; float altRespawn; float fixedLevel; float autoItems; float dmMode; float easyFourth; float patternRunner; float coop; float teamplay; float serverflags; float total_secrets; float total_monsters; float found_secrets; float killed_monsters; float chunk_cnt; float done_precache; float parm1; float parm2; float parm4; float parm5; float parm6; float parm7; float parm8; float parm9; float parm10; float parm11; float parm12; float parm13; float parm14; float parm15; float parm16; string_t parm3; vec3_t v_forward; vec3_t v_up; vec3_t v_right; float trace_allsolid; float trace_startsolid; float trace_fraction; vec3_t trace_endpos; vec3_t trace_plane_normal; float trace_plane_dist; int trace_ent; float trace_inopen; float trace_inwater; int msg_entity; float cycle_wrapped; float crouch_cnt; float modelindex_assassin; float modelindex_crusader; float modelindex_paladin; float modelindex_necromancer; float modelindex_sheep; float num_players; float exp_mult; /* max_players, defLosses and attLosses * are for Siege: not in v0.11. */ func_t main; func_t StartFrame; func_t PlayerPreThink; func_t PlayerPostThink; func_t ClientKill; func_t ClientConnect; func_t PutClientInServer; func_t ClientReEnter; func_t ClientDisconnect; func_t ClassChangeWeapon; func_t SetNewParms; func_t SetChangeParms; /* SmitePlayer only in v0.15 */ } globalvars_v011_t; /* hexenworld v0.14 progdefs: */ typedef struct { int pad[28]; int self; int other; int world; float time; float frametime; int newmis; float force_retouch; string_t mapname; string_t startspot; float deathmatch; float randomclass; float damageScale; float meleeDamScale; float shyRespawn; float spartanPrint; /* in v0.14 and newer */ float manaScale; float tomeMode; float tomeRespawn; float w2Respawn; float altRespawn; float fixedLevel; float autoItems; float dmMode; float easyFourth; float patternRunner; float coop; float teamplay; float serverflags; float total_secrets; float total_monsters; float found_secrets; float killed_monsters; float chunk_cnt; float done_precache; float parm1; float parm2; float parm4; float parm5; float parm6; float parm7; float parm8; float parm9; float parm10; float parm11; float parm12; float parm13; float parm14; float parm15; float parm16; string_t parm3; vec3_t v_forward; vec3_t v_up; vec3_t v_right; float trace_allsolid; float trace_startsolid; float trace_fraction; vec3_t trace_endpos; vec3_t trace_plane_normal; float trace_plane_dist; int trace_ent; float trace_inopen; float trace_inwater; int msg_entity; float cycle_wrapped; float crouch_cnt; float modelindex_assassin; float modelindex_crusader; float modelindex_paladin; float modelindex_necromancer; float modelindex_sheep; float num_players; float exp_mult; /* max_players, defLosses and attLosses * are for Siege: in v0.14 and newer. */ float max_players; float defLosses; float attLosses; func_t main; func_t StartFrame; func_t PlayerPreThink; func_t PlayerPostThink; func_t ClientKill; func_t ClientConnect; func_t PutClientInServer; func_t ClientReEnter; func_t ClientDisconnect; func_t ClassChangeWeapon; func_t SetNewParms; func_t SetChangeParms; /* SmitePlayer only in v0.15 */ } globalvars_v014_t; /* hexenworld v0.15 progdefs: */ typedef struct { int pad[28]; int self; int other; int world; float time; float frametime; int newmis; float force_retouch; string_t mapname; string_t startspot; float deathmatch; float randomclass; float damageScale; float meleeDamScale; float shyRespawn; float spartanPrint; /* in v0.14 and newer */ float manaScale; float tomeMode; float tomeRespawn; float w2Respawn; float altRespawn; float fixedLevel; float autoItems; float dmMode; float easyFourth; float patternRunner; float coop; float teamplay; float serverflags; float total_secrets; float total_monsters; float found_secrets; float killed_monsters; float chunk_cnt; float done_precache; float parm1; float parm2; float parm4; float parm5; float parm6; float parm7; float parm8; float parm9; float parm10; float parm11; float parm12; float parm13; float parm14; float parm15; float parm16; string_t parm3; vec3_t v_forward; vec3_t v_up; vec3_t v_right; float trace_allsolid; float trace_startsolid; float trace_fraction; vec3_t trace_endpos; vec3_t trace_plane_normal; float trace_plane_dist; int trace_ent; float trace_inopen; float trace_inwater; int msg_entity; float cycle_wrapped; float crouch_cnt; float modelindex_assassin; float modelindex_crusader; float modelindex_paladin; float modelindex_necromancer; float modelindex_sheep; float num_players; float exp_mult; /* max_players, defLosses and attLosses * are for Siege: in v0.14 and newer. */ float max_players; float defLosses; float attLosses; func_t main; func_t StartFrame; func_t PlayerPreThink; func_t PlayerPostThink; func_t ClientKill; func_t ClientConnect; func_t PutClientInServer; func_t ClientReEnter; func_t ClientDisconnect; func_t ClassChangeWeapon; func_t SetNewParms; func_t SetChangeParms; func_t SmitePlayer; /* in v0.15 */ } globalvars_v015_t; typedef struct { int *self; /* @ offset 28. */ int *other; int *world; float *time; float *frametime; int *newmis; float *force_retouch; string_t *mapname; string_t *startspot; float *deathmatch; float *randomclass; float *damageScale; float *meleeDamScale; float *shyRespawn; float *spartanPrint; float *manaScale; float *tomeMode; float *tomeRespawn; float *w2Respawn; float *altRespawn; float *fixedLevel; float *autoItems; float *dmMode; float *easyFourth; float *patternRunner; float *coop; float *teamplay; float *serverflags; float *total_secrets; float *total_monsters; float *found_secrets; float *killed_monsters; float *chunk_cnt; float *done_precache; /* parm1-16 form an array. Unlike Quake, Hexen II progs has them * in order parms1..2 parms4..16 as float and parm3 as string_t */ float *parm; vec3_t *v_forward; vec3_t *v_up; vec3_t *v_right; float *trace_allsolid; float *trace_startsolid; float *trace_fraction; vec3_t *trace_endpos; vec3_t *trace_plane_normal; float *trace_plane_dist; int *trace_ent; float *trace_inopen; float *trace_inwater; int *msg_entity; float *cycle_wrapped; float *crouch_cnt; float *modelindex_assassin; float *modelindex_crusader; float *modelindex_paladin; float *modelindex_necromancer; float *modelindex_sheep; float *num_players; float *exp_mult; /* max_players, defLosses and attLosses * are for Siege: in v0.14 and newer. */ float *max_players; float *defLosses; float *attLosses; /* functions */ func_t *main; func_t *StartFrame; func_t *PlayerPreThink; func_t *PlayerPostThink; func_t *ClientKill; func_t *ClientConnect; func_t *PutClientInServer; func_t *ClientReEnter; func_t *ClientDisconnect; func_t *ClassChangeWeapon; func_t *SetNewParms; func_t *SetChangeParms; func_t *SmitePlayer; } sv_globals_t; typedef struct { float modelindex; vec3_t absmin; vec3_t absmax; float ltime; float lastruntime; float movetype; float solid; vec3_t origin; vec3_t oldorigin; vec3_t velocity; vec3_t angles; vec3_t avelocity; vec3_t punchangle; string_t classname; string_t model; float frame; float skin; float effects; float scale; float drawflags; float abslight; vec3_t mins; vec3_t maxs; vec3_t size; float hull; func_t touch; func_t use; func_t think; func_t blocked; float nextthink; int groundentity; float stats_restored; float frags; float weapon; string_t weaponmodel; float weaponframe; float health; float max_health; float playerclass; float next_playerclass; float has_portals; /* has_portals is NOT in v0.09 */ float bluemana; float greenmana; float max_mana; float armor_amulet; float armor_bracer; float armor_breastplate; float armor_helmet; float level; float intelligence; float wisdom; float dexterity; float strength; float experience; float ring_flight; float ring_water; float ring_turning; float ring_regeneration; float haste_time; float tome_time; string_t puzzle_inv1; string_t puzzle_inv2; string_t puzzle_inv3; string_t puzzle_inv4; string_t puzzle_inv5; string_t puzzle_inv6; string_t puzzle_inv7; string_t puzzle_inv8; float experience_value; float items; float takedamage; int chain; float deadflag; vec3_t view_ofs; float button0; float button1; float button2; float impulse; float fixangle; vec3_t v_angle; float idealpitch; float idealroll; float hoverz; string_t netname; int enemy; float flags; float flags2; float artifact_flags; float colormap; float team; float light_level; float wpn_sound; float targAng; float targPitch; float targDist; float teleport_time; float armortype; float armorvalue; float waterlevel; float watertype; float friction; float ideal_yaw; float yaw_speed; int goalentity; float spawnflags; string_t target; string_t targetname; float dmg_take; float dmg_save; int dmg_inflictor; int owner; vec3_t movedir; float message; float soundtype; string_t noise; string_t noise1; string_t noise2; string_t noise3; float rings; float rings_active; float rings_low; float artifacts; float artifact_active; float artifact_low; float hasted; float inventory; float cnt_torch; float cnt_h_boost; float cnt_sh_boost; float cnt_mana_boost; float cnt_teleport; float cnt_tome; float cnt_summon; float cnt_invisibility; float cnt_glyph; float cnt_haste; float cnt_blast; float cnt_polymorph; float cnt_flight; float cnt_cubeofforce; float cnt_invincibility; int cameramode; int movechain; func_t chainmoved; float string_index; float gravity; /* gravity is not in v0.09 */ float siege_team; /* siege support: in v0.14 (0.12?) and newer */ } entvars_t; /* crc for HexenWorld v0.09 hwprogs.dat headers */ #define PROGS_V009_CRC 27922 /* crc for HexenWorld v0.11 hwprogs.dat headers */ #define PROGS_V011_CRC 48691 /* crc for HexenWorld v0.14 hwprogs.dat headers */ #define PROGS_V014_CRC 61593 /* crc for HexenWorld v0.15 hwprogs.dat headers */ #define PROGS_V015_CRC 25203 /* the default valid crc: */ #define PROGHEADER_CRC (PROGS_V015_CRC) #endif /* __PROGDEFS_H */ engine/hexenworld/shared/protocol.h000066400000000000000000000344721444734033100177770ustar00rootroot00000000000000/* protocol.h -- communications protocols * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __H2W_PROTOCOL_H #define __H2W_PROTOCOL_H #define OLD_PROTOCOL_VERSION 24 #define PROTOCOL_VERSION 25 #define PROTOCOL_VERSION_EXT 26 //========================================= #define PORT_CLIENT 26901 //27001 #define PORT_MASTER 26900 //27000 #define PORT_SERVER 26950 //27500 //========================================= // out of band message id bytes // M = master, S = server, C = client, A = any // the second character will always be \n if the message isn't a single // byte long (?? not true anymore?) #define A2S_ECHO 'g' // echo back a message #define S2C_CHALLENGE 'c' #define S2C_CONNECTION 'j' #define A2A_PING 'k' // respond with an A2A_ACK #define A2A_ACK 'l' // general acknowledgement without info #define A2A_NACK 'm' // [+ comment] general failure #define A2C_PRINT 'n' // print a message on client #define S2M_HEARTBEAT 'a' // + serverinfo + userlist + fraglist #define A2C_CLIENT_COMMAND 'B' // + command line #define S2M_SHUTDOWN 'C' #define M2C_SERVERLST 'd' // + \n + hw server port list //================== // note that constant.hc may mirror to some of these numbers // also related to svc_strings[] in cl_parse //================== // // server to client // #define svc_bad 0 #define svc_nop 1 #define svc_disconnect 2 #define svc_updatestat 3 // [byte] [byte] // svc_version 4 // [long] server version #define svc_setview 5 // [short] entity number #define svc_sound 6 // #define svc_time 7 // [float] server time #define svc_print 8 // [byte] id [string] null terminated string #define svc_stufftext 9 // [string] stuffed into client's console buffer // the string should be \n terminated #define svc_setangle 10 // [angle3] set the view angle to this absolute value #define svc_serverdata 11 // [long] protocol ... #define svc_lightstyle 12 // [byte] [string] // svc_updatename 13 // [byte] [string] #define svc_updatefrags 14 // [byte] [short] // svc_clientdata 15 // #define svc_stopsound 16 // // svc_updatecolors 17 // [byte] [byte] [byte] #define svc_particle 18 // [vec3] #define svc_damage 19 #define svc_spawnstatic 20 // svc_spawnbinary 21 #define svc_spawnbaseline 22 #define svc_temp_entity 23 // variable // svc_setpause 24 // [byte] on / off // svc_signonnum 25 // [byte] used for the signon sequence #define svc_centerprint 26 // [string] to put in center of the screen #define svc_killedmonster 27 #define svc_foundsecret 28 #define svc_spawnstaticsound 29 // [coord3] [byte] samp [byte] vol [byte] aten #define svc_intermission 30 // [vec3_t] origin [vec3_t] angle #define svc_finale 31 // [string] text #define svc_cdtrack 32 // [byte] track #define svc_sellscreen 33 #define svc_smallkick 34 // set client punchangle to 2 #define svc_bigkick 35 // set client punchangle to 4 #define svc_updateping 36 // [byte] [short] #define svc_updateentertime 37 // [byte] [float] #define svc_updatestatlong 38 // [byte] [long] #define svc_muzzleflash 39 // [short] entity #define svc_updateuserinfo 40 // [byte] slot [long] uid // [string] userinfo #define svc_download 41 // [short] size [size bytes] #define svc_playerinfo 42 // variable #define svc_nails 43 // [byte] num [48 bits] xyzpy 12 12 12 4 8 #define svc_chokecount 44 // [byte] packets choked #define svc_modellist 45 // [strings] #define svc_soundlist 46 // [strings] #define svc_packetentities 47 // [...] #define svc_deltapacketentities 48 // [...] #define svc_maxspeed 49 // maxspeed change, for prediction #define svc_entgravity 50 // gravity change, for prediction // Hexen2 specifics #define svc_plaque 51 #define svc_particle_explosion 52 #define svc_set_view_tint 53 #define svc_start_effect 54 #define svc_end_effect 55 #define svc_set_view_flags 56 #define svc_clear_view_flags 57 #define svc_update_inv 58 #define svc_particle2 59 #define svc_particle3 60 #define svc_particle4 61 #define svc_turn_effect 62 #define svc_update_effect 63 #define svc_multieffect 64 #define svc_midi_name 65 #define svc_raineffect 66 #define svc_packmissile 67 // [byte] num [40 bits] xyz type 12 12 12 4 #define svc_indexed_print 68 // same as svc_print, but sends an index in strings.txt instead of string #define svc_targetupdate 69 // [byte] angle [byte] pitch [byte] dist/4 - Hey, I got number 69! Woo hoo! #define svc_name_print 70 // print player's name #define svc_sound_update_pos 71 // [short] ent+channel [coord3] pos #define svc_update_piv 72 // update players in view #define svc_player_sound 73 // sends weapon sound for invisible player #define svc_updatepclass 74 // [byte] [byte] #define svc_updatedminfo 75 // [byte] [short] [byte] #define svc_updatesiegeinfo 76 // [byte] [byte] #define svc_updatesiegeteam 77 // [byte] [byte] #define svc_updatesiegelosses 78 // [byte] [byte] #define svc_haskey 79 // [byte] [byte] #define svc_nonehaskey 80 // [byte] [byte] #define svc_isdoc 81 // [byte] [byte] #define svc_nodoc 82 // [byte] [byte] #define svc_playerskipped 83 // [byte] //============================================== // // client to server // #define clc_bad 0 #define clc_nop 1 // clc_doublemove 2 #define clc_move 3 // [[usercmd_t] #define clc_stringcmd 4 // [string] message #define clc_delta 5 // [byte] sequence number, requests delta compression of message #define clc_tmove 6 // teleport request, spectator only #define clc_inv_select 7 #define clc_get_effect 8 // [byte] effect id //============================================== // playerinfo flags from server // playerinfo always sends: playernum, flags, origin[] and framenumber #define PF_MSEC (1<<0) #define PF_COMMAND (1<<1) #define PF_VELOCITY1 (1<<2) #define PF_VELOCITY2 (1<<3) #define PF_VELOCITY3 (1<<4) #define PF_MODEL (1<<5) #define PF_SKINNUM (1<<6) #define PF_EFFECTS (1<<7) #define PF_WEAPONFRAME (1<<8) // only sent for view player #define PF_DEAD (1<<9) // don't block movement any more #define PF_CROUCH (1<<10) // offset the view height differently //#define PF_NOGRAV (1<<11) // don't apply gravity for prediction #define PF_EFFECTS2 (1<<11) // player has high byte of effects set... #define PF_DRAWFLAGS (1<<12) #define PF_SCALE (1<<13) #define PF_ABSLIGHT (1<<14) #define PF_SOUND (1<<15) // play a sound in the weapon channel //============================================== // if the high bit of the client to server byte is set, the low bits are // client move cmd bits // ms and angle2 are always sent, the others are optional #define CM_ANGLE1 (1<<0) #define CM_ANGLE3 (1<<1) #define CM_FORWARD (1<<2) #define CM_SIDE (1<<3) #define CM_UP (1<<4) #define CM_BUTTONS (1<<5) #define CM_IMPULSE (1<<6) #define CM_MSEC (1<<7) //============================================== // the first 16 bits of a packetentities update holds 9 bits // of entity number and 7 bits of flags #define U_ORIGIN1 (1<<9) #define U_ORIGIN2 (1<<10) #define U_ORIGIN3 (1<<11) #define U_ANGLE2 (1<<12) #define U_FRAME (1<<13) #define U_REMOVE (1<<14) // REMOVE this entity, don't add it #define U_MOREBITS (1<<15) // if MOREBITS is set, these additional flags are read in next #define U_ANGLE1 (1<<0) #define U_ANGLE3 (1<<1) #define U_SCALE (1<<2) #define U_COLORMAP (1<<3) #define U_SKIN (1<<4) #define U_EFFECTS (1<<5) #define U_MODEL16 (1<<6) #define U_MOREBITS2 (1<<7) // if MOREBITS2 is set, then send the 3rd byte #define U_MODEL (1<<16) #define U_SOUND (1<<17) #define U_DRAWFLAGS (1<<18) #define U_ABSLIGHT (1<<19) //============================================== // Bits to help send server info about the client's edict variables #define SC1_HEALTH (1<<0) // changes stat bar #define SC1_LEVEL (1<<1) // changes stat bar #define SC1_INTELLIGENCE (1<<2) // changes stat bar #define SC1_WISDOM (1<<3) // changes stat bar #define SC1_STRENGTH (1<<4) // changes stat bar #define SC1_DEXTERITY (1<<5) // changes stat bar //#define SC1_WEAPON (1<<6) // changes stat bar #define SC1_TELEPORT_TIME (1<<6) // can't airmove for 2 seconds #define SC1_BLUEMANA (1<<7) // changes stat bar #define SC1_GREENMANA (1<<8) // changes stat bar #define SC1_EXPERIENCE (1<<9) // changes stat bar #define SC1_CNT_TORCH (1<<10) // changes stat bar #define SC1_CNT_H_BOOST (1<<11) // changes stat bar #define SC1_CNT_SH_BOOST (1<<12) // changes stat bar #define SC1_CNT_MANA_BOOST (1<<13) // changes stat bar #define SC1_CNT_TELEPORT (1<<14) // changes stat bar #define SC1_CNT_TOME (1<<15) // changes stat bar #define SC1_CNT_SUMMON (1<<16) // changes stat bar #define SC1_CNT_INVISIBILITY (1<<17) // changes stat bar #define SC1_CNT_GLYPH (1<<18) // changes stat bar #define SC1_CNT_HASTE (1<<19) // changes stat bar #define SC1_CNT_BLAST (1<<20) // changes stat bar #define SC1_CNT_POLYMORPH (1<<21) // changes stat bar #define SC1_CNT_FLIGHT (1<<22) // changes stat bar #define SC1_CNT_CUBEOFFORCE (1<<23) // changes stat bar #define SC1_CNT_INVINCIBILITY (1<<24) // changes stat bar #define SC1_ARTIFACT_ACTIVE (1<<25) #define SC1_ARTIFACT_LOW (1<<26) #define SC1_MOVETYPE (1<<27) #define SC1_CAMERAMODE (1<<28) #define SC1_HASTED (1<<29) #define SC1_INVENTORY (1<<30) #define SC1_RINGS_ACTIVE (1<<31) #define SC2_RINGS_LOW (1<<0) #define SC2_AMULET (1<<1) #define SC2_BRACER (1<<2) #define SC2_BREASTPLATE (1<<3) #define SC2_HELMET (1<<4) #define SC2_FLIGHT_T (1<<5) #define SC2_WATER_T (1<<6) #define SC2_TURNING_T (1<<7) #define SC2_REGEN_T (1<<8) #define SC2_HASTE_T (1<<9) #define SC2_TOME_T (1<<10) #define SC2_PUZZLE1 (1<<11) #define SC2_PUZZLE2 (1<<12) #define SC2_PUZZLE3 (1<<13) #define SC2_PUZZLE4 (1<<14) #define SC2_PUZZLE5 (1<<15) #define SC2_PUZZLE6 (1<<16) #define SC2_PUZZLE7 (1<<17) #define SC2_PUZZLE8 (1<<18) #define SC2_MAXHEALTH (1<<19) #define SC2_MAXMANA (1<<20) #define SC2_FLAGS (1<<21) // This is to mask out those items that need to generate a stat bar change #define SC1_STAT_BAR 0x01ffffff #define SC2_STAT_BAR 0x0000001e // This is to mask out those items in the inventory (for inventory changes) #define SC1_INV 0x01fffc00 #define SC2_INV 0x00000000 //============================================== // a sound with no channel is a local only sound // the sound field has bits 0-2: channel, 3-12: entity #define SND_VOLUME (1<<15) #define SND_ATTENUATION (1<<14) #define DEFAULT_SOUND_PACKET_VOLUME 255 #define DEFAULT_SOUND_PACKET_ATTENUATION 1.0 // svc_print messages have an id, so messages can be filtered #define PRINT_LOW 0 #define PRINT_MEDIUM 1 #define PRINT_HIGH 2 #define PRINT_CHAT 3 // also go to chat buffer // // temp entity events // #define TE_SPIKE 0 #define TE_SUPERSPIKE 1 #define TE_GUNSHOT 2 #define TE_EXPLOSION 3 #define TE_TAREXPLOSION 4 #define TE_LIGHTNING1 5 #define TE_LIGHTNING2 6 #define TE_WIZSPIKE 7 #define TE_KNIGHTSPIKE 8 #define TE_LIGHTNING3 9 #define TE_LAVASPLASH 10 #define TE_TELEPORT 11 #define TE_BLOOD 12 #define TE_LIGHTNINGBLOOD 13 // hexen 2 #define TE_STREAM_CHAIN 25 #define TE_STREAM_SUNSTAFF1 26 #define TE_STREAM_SUNSTAFF2 27 #define TE_STREAM_LIGHTNING 28 #define TE_STREAM_COLORBEAM 29 #define TE_STREAM_ICECHUNKS 30 #define TE_STREAM_GAZE 31 #define TE_STREAM_FAMINE 32 #define TE_BIGGRENADE 33 #define TE_CHUNK 34 #define TE_HWBONEPOWER 35 #define TE_HWBONEPOWER2 36 #define TE_METEORHIT 37 #define TE_HWRAVENDIE 38 #define TE_HWRAVENEXPLODE 39 #define TE_XBOWHIT 40 #define TE_CHUNK2 41 #define TE_ICEHIT 42 #define TE_ICESTORM 43 #define TE_HWMISSILEFLASH 44 #define TE_SUNSTAFF_CHEAP 45 #define TE_LIGHTNING_HAMMER 46 #define TE_DRILLA_EXPLODE 47 #define TE_DRILLA_DRILL 48 #define TE_HWTELEPORT 49 #define TE_SWORD_EXPLOSION 50 #define TE_AXE_BOUNCE 51 #define TE_AXE_EXPLODE 52 #define TE_TIME_BOMB 53 #define TE_FIREBALL 54 #define TE_SUNSTAFF_POWER 55 #define TE_PURIFY2_EXPLODE 56 #define TE_PLAYER_DEATH 57 #define TE_PURIFY1_EFFECT 58 #define TE_TELEPORT_LINGER 59 #define TE_LINE_EXPLOSION 60 #define TE_METEOR_CRUSH 61 //MISSION PACK #define TE_STREAM_LIGHTNING_SMALL 62 #define TE_ACIDBALL 63 #define TE_ACIDBLOB 64 #define TE_FIREWALL 65 #define TE_FIREWALL_IMPACT 66 #define TE_HWBONERIC 67 #define TE_POWERFLAME 68 #define TE_BLOODRAIN 69 #define TE_AXE 70 #define TE_PURIFY2_MISSILE 71 #define TE_SWORD_SHOT 72 #define TE_ICESHOT 73 #define TE_METEOR 74 #define TE_LIGHTNINGBALL 75 #define TE_MEGAMETEOR 76 #define TE_CUBEBEAM 77 #define TE_LIGHTNINGEXPLODE 78 #define TE_ACID_BALL_FLY 79 #define TE_ACID_BLOB_FLY 80 #define TE_CHAINLIGHTNING 81 /* ========================================================== ELEMENTS COMMUNICATED ACROSS THE NET ========================================================== */ #define MAX_CLIENTS 32 #define UPDATE_BACKUP 64 // copies of entity_state_t to keep buffered // must be power of two #define UPDATE_MASK (UPDATE_BACKUP-1) // entity_state_t is the information conveyed from the server // in an update message typedef struct { int number; // edict index int flags; // nolerp, etc vec3_t origin; vec3_t angles; int modelindex; int frame; int colormap; int skinnum; int effects; int scale; // for Alias models int drawflags; // for Alias models int abslight; // for Alias models int wpn_sound; // for cheap playing of sounds } entity_state_t; #define MAX_PACKET_ENTITIES 64 // doesn't count nails typedef struct { int num_entities; entity_state_t entities[MAX_PACKET_ENTITIES]; } packet_entities_t; typedef struct usercmd_s { byte msec; vec3_t angles; short forwardmove, sidemove, upmove; byte buttons; byte impulse; byte light_level; } usercmd_t; #endif /* __H2W_PROTOCOL_H */ engine/hexenworld/shared/quakedef.h000066400000000000000000000151211444734033100177110ustar00rootroot00000000000000/* quakedef.h -- common definitions for client and server. * * Copyright (C) 1996-1997 Id Software, Inc. * Copyright (C) 1997-1998 Raven Software Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __QUAKEDEFS_H #define __QUAKEDEFS_H #define __STRINGIFY(x) #x #define STRINGIFY(x) __STRINGIFY(x) #define HOT_VERSION_MAJ 1 #define HOT_VERSION_MID 5 #define HOT_VERSION_MIN 10 #define HOT_VERSION_REL_DATE "2022-10-25" #define HOT_VERSION_STR STRINGIFY(HOT_VERSION_MAJ) "." STRINGIFY(HOT_VERSION_MID) "." STRINGIFY(HOT_VERSION_MIN) #define GLQUAKE_VERSION 1.00 #define ENGINE_VERSION 0.29 #define ENGINE_NAME "HexenWorld" #define MAX_QPATH 64 // max length of a quake game pathname #define QUAKE_GAME // as opposed to utilities // !!! if this is changed, it must be changed in d_ifacea.h too !!! #define CACHE_SIZE 32 // used to align key data structures #define Q_UNUSED(x) (x = x) // for pesky compiler / lint warnings #define MINIMUM_MEMORY 0x550000 #define MAX_NUM_ARGVS 50 // up / down #define PITCH 0 // left / right #define YAW 1 // fall over #define ROLL 2 // // Timing macros // #define HX_FRAME_TIME 0.05 #define HX_FPS 20 #define ON_EPSILON 0.1 // point on plane side epsilon #define DIST_EPSILON (0.03125) // 1/32 epsilon to keep floating point happy (moved from world.h) #define MAX_MSGLEN 7500 // max length of a reliable message #define MAX_DATAGRAM 1400 // max length of unreliable message #define MAX_PRINTMSG 4096 // maximum allowed print message length // // per-level limits // #define MAX_EDICTS 768 // FIXME: ouch! ouch! ouch! #define MAX_LIGHTSTYLES 64 #define MAX_MODELS 512 /* Sent over the net as a word */ #define MAX_SOUNDS 256 /* Sent over the net as a byte */ #define MAX_STYLESTRING 64 // // stats are integers communicated to the client by the server // #define MAX_CL_STATS 32 #define STAT_HEALTH 0 //define STAT_FRAGS 1 #define STAT_WEAPON 2 #define STAT_AMMO 3 #define STAT_ARMOR 4 //define STAT_WEAPONFRAME 5 #define STAT_SHELLS 6 #define STAT_NAILS 7 #define STAT_ROCKETS 8 #define STAT_CELLS 9 #define STAT_ACTIVEWEAPON 10 #define STAT_TOTALSECRETS 11 #define STAT_TOTALMONSTERS 12 #define STAT_SECRETS 13 // bumped on client side by svc_foundsecret #define STAT_MONSTERS 14 // bumped by svc_killedmonster #define STAT_ITEMS 15 //define STAT_VIEWHEIGHT 16 #define MAX_INVENTORY 15 /* Max inventory array size */ /* the number of cnt_ members in the entvars_t struct: from cnt_torch to cnt_invincibility: 15 total (see in progdefs.h). */ #define SAVEGAME_VERSION 5 #define SAVEGAME_COMMENT_LENGTH 39 // 0-19: level name, 21-rest: save time #define MAX_SAVEGAMES 12 // Max number of savegames for the menu listing // // quake item flags // #define IT_SHOTGUN (1 << 0 ) #define IT_SUPER_SHOTGUN (1 << 1 ) #define IT_NAILGUN (1 << 2 ) #define IT_SUPER_NAILGUN (1 << 3 ) #define IT_GRENADE_LAUNCHER (1 << 4 ) #define IT_ROCKET_LAUNCHER (1 << 5 ) #define IT_LIGHTNING (1 << 6 ) #define IT_SUPER_LIGHTNING (1 << 7 ) #define IT_SHELLS (1 << 8 ) #define IT_NAILS (1 << 9 ) #define IT_ROCKETS (1 << 10) #define IT_CELLS (1 << 11) #define IT_AXE (1 << 12) #define IT_ARMOR1 (1 << 13) #define IT_ARMOR2 (1 << 14) #define IT_ARMOR3 (1 << 15) #define IT_SUPERHEALTH (1 << 16) #define IT_KEY1 (1 << 17) #define IT_KEY2 (1 << 18) #define IT_INVISIBILITY (1 << 19) #define IT_INVULNERABILITY (1 << 20) #define IT_SUIT (1 << 21) #define IT_QUAD (1 << 22) #define IT_SIGIL1 (1 << 28) #define IT_SIGIL2 (1 << 29) #define IT_SIGIL3 (1 << 30) #define IT_SIGIL4 (1 << 31) // // hexen2 artifact flags // #define ART_HASTE (1 << 0) #define ART_INVINCIBILITY (1 << 1) #define ART_TOMEOFPOWER (1 << 2) #define ART_INVISIBILITY (1 << 3) /* hexen2 and hexenworld versions of these flags are different !!! */ #define ARTFLAG_FROZEN (1 << 4) #define ARTFLAG_STONED (1 << 5) #define ARTFLAG_DIVINE_INTERVENTION (1 << 6) #define ARTFLAG_BOOTS (1 << 7) /* ART_CLIMB in siege gamecode */ // // edict->drawflags // #define MLS_MASKIN 7 // MLS: Model Light Style #define MLS_MASKOUT 248 #define MLS_NONE 0 #define MLS_FULLBRIGHT 1 #define MLS_POWERMODE 2 #define MLS_TORCH 3 #define MLS_TOTALDARK 4 #define MLS_ABSLIGHT 7 #define SCALE_TYPE_MASKIN 24 #define SCALE_TYPE_MASKOUT 231 #define SCALE_TYPE_UNIFORM 0 // Scale X, Y, and Z #define SCALE_TYPE_XYONLY 8 // Scale X and Y #define SCALE_TYPE_ZONLY 16 // Scale Z #define SCALE_ORIGIN_MASKIN 96 #define SCALE_ORIGIN_MASKOUT 159 #define SCALE_ORIGIN_CENTER 0 // Scaling origin at object center #define SCALE_ORIGIN_BOTTOM 32 // Scaling origin at object bottom #define SCALE_ORIGIN_TOP 64 // Scaling origin at object top #define DRF_TRANSLUCENT 128 // // Player Classes // #define MAX_PLAYER_CLASS 6 /* total number of available player classes */ #define ABILITIES_STR_INDEX 400 /* starting number of class ability lines in strings.txt - 1 */ #define CLASS_PALADIN 1 #define CLASS_CLERIC 2 #define CLASS_CRUSADER CLASS_CLERIC /* alias, the progs actually use this one */ #define CLASS_NECROMANCER 3 #define CLASS_THEIF 4 #define CLASS_THIEF CLASS_THEIF /* for those who type correctly ;) */ #define CLASS_ASSASSIN CLASS_THEIF /* another alias, progs actually use this one */ #define CLASS_DEMON 5 #define CLASS_SUCCUBUS CLASS_DEMON /* alias, the h2w progs actually use this one */ #define CLASS_DWARF 6 // // Siege teams // #define ST_DEFENDER 1 #define ST_ATTACKER 2 // // Dm Modes // #define DM_CAPTURE_THE_TOKEN 1 #define DM_HUNTER 2 #define DM_SIEGE 3 // // print flags // #define PRINT_LOW 0 // pickup messages #define PRINT_MEDIUM 1 // death messages #define PRINT_HIGH 2 // critical messages #define PRINT_CHAT 3 // chat messages #define PRINT_SOUND 4 // says a sound /* include our common header file */ /* FIXME: kill this in the future and make each C file include only the necessary headers. */ #if defined(SERVERONLY) #include "qwsvinc.h" #else #include "quakeinc.h" #endif #endif /* __QUAKEDEFS_H */ engine/resource/000077500000000000000000000000001444734033100141555ustar00rootroot00000000000000engine/resource/h2_os2.ico000066400000000000000000000017621444734033100157530ustar00rootroot00000000000000BA\CINò@ @ÿÿÿCINò@ ÿ€ÿÿÿ?ÿÿÿÿþÿ—ÿþÿÊÿýÿå?ýÿòŸóÿýoãÿþ“çÿþmïÿÿ0ÏÿÿŠÿÿåÿÿòÿÿøÿÿþÿÿþÿÿüÿÿüÃÿÿøáÿÿõðÿÿåü?ÿëþÿÓÿÿóÿÏÿçÿçÿÃÿûþ ÿýÿ‡ÿÿÿÏÿÿÿ?ÿÿÿÿÿÿ !" "  " "  " "   !"" "  " """!" """ "" """ """"""""" "" """ !"" ""!" " "engine/resource/h2mp.ico000066400000000000000000000013761444734033100155260ustar00rootroot00000000000000 è( @ÿ€ !" "  " "  " "   !"" "  " """!" """ "" """ """"""""" "" """ !"" ""!" " "ÿÿÿ?ÿÿÿÿþÿ—ÿþÿÊÿýÿå?ýÿòŸóÿýoãÿþ“çßþmïßÿ0ÏßÿŠßÿåÿòßÿøßÿþßÿþßÿüÿÿüÃÿÿøáÿÿõðÿÿåü?ÿëþÿÓÿÿóÿÏÿçÿçÿÃÿûþ ÿýÿ‡ÿÿÿÏÿÿÿ?ÿÿÿÿÿÿengine/resource/h2mp.xbm000066400000000000000000000017011444734033100155320ustar00rootroot00000000000000/* uHexen2 xbm format icon - Hexen II Mission Pack (H2MP) */ #define hx2icon_width 32 #define hx2icon_height 32 static const unsigned char hx2icon_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x80, 0x2d, 0x00, 0x40, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, 0x20, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x00, 0x80, 0x07, 0x00, 0x58, 0xc0, 0x03, 0x00, 0x50, 0xf0, 0x00, 0x00, 0xe0, 0x78, 0x00, 0x00, 0xc0, 0x3c, 0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x04, 0x00, 0x80, 0x1f, 0x04, 0x00, 0xe0, 0x0f, 0x04, 0x00, 0x10, 0x07, 0x04, 0x00, 0x10, 0x87, 0x3f, 0x00, 0x8a, 0x03, 0x04, 0x00, 0xe1, 0x0c, 0x04, 0x80, 0x40, 0x00, 0x04, 0x80, 0x36, 0x18, 0x04, 0x40, 0x08, 0x38, 0x00, 0x30, 0x06, 0x20, 0x00, 0x18, 0x03, 0x40, 0x00, 0x8c, 0x00, 0x40, 0x00, 0x06, 0x00, 0x80, 0x00, 0x0e, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; engine/resource/hexen2.bmp000066400000000000000000001200461444734033100160510ustar00rootroot00000000000000BM& æ(ôP@œœœìì3f™Ìÿ333f3™3Ì3ÿ3f3fff™fÌfÿf™3™f™™™Ì™ÿ™Ì3ÌfÌ™ÌÌÌÿÌÿ3ÿfÿ™ÿÌÿÿÿ333f3™3Ì3ÿ333333f33™33Ì33ÿ33f33f3ff3™f3Ìf3ÿf3™33™3f™3™™3Ì™3ÿ™3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌ3ÿ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿ3f3fff™fÌfÿf3f33ff3f™3fÌ3fÿ3fff3fffff™ffÌffÿff™f3™ff™f™™fÌ™fÿ™fÌf3ÌffÌf™ÌfÌÌfÿÌfÿf3ÿffÿf™ÿfÌÿfÿÿf™3™f™™™Ì™ÿ™3™33™f3™™3™Ì3™ÿ3™f™3f™ff™™f™Ìf™ÿf™™™3™™f™™™™™Ì™™ÿ™™Ì™3Ì™fÌ™™Ì™ÌÌ™ÿÌ™ÿ™3ÿ™fÿ™™ÿ™Ìÿ™ÿÿ™Ì3ÌfÌ™ÌÌÌÿÌ3Ì33Ìf3Ì™3ÌÌ3Ìÿ3ÌfÌ3fÌffÌ™fÌÌfÌÿfÌ™Ì3™Ìf™Ì™™ÌÌ™Ìÿ™ÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÌÿÌ3ÿÌfÿÌ™ÿÌÌÿÌÿÿÌÿ3ÿfÿ™ÿÌÿÿÿ3ÿ33ÿf3ÿ™3ÿÌ3ÿÿ3ÿfÿ3fÿffÿ™fÿÌfÿÿfÿ™ÿ3™ÿf™ÿ™™ÿÌ™ÿÿ™ÿÌÿ3ÌÿfÌÿ™ÌÿÌÌÿÿÌÿÿÿ3ÿÿfÿÿ™ÿÿÌÿÿÿÿÿ (((555CCCPPP]]]kkkxxx†††“““¡¡¡®®®»»»ÉÉÉÖÖÖäääñññÿÿÿ++€+Ú*€zU*U€äz*UऀUO*y€¤yO+OU*Nz¤€z+¤€U$U€¤€U*¤U$*y¤€€OUz$€€y*$Uz€€U+V€z*€¤U*ÙNV€¤U*UOÛU€U$*U€z+$*U€€y**€€UO*yV+Uã€*$*O€y*Ny€€U++[€NU€N*$Ny¤*Ûyz€UU€UUz€O**U¤N*O€zz*€*T€€O*¤yy€€N**z€z1Oy€NU€*z€N*UTT€¤UN*U€€Oz€U0€ßUz€N$*HU€€y**U€€U€UNU+*Ü€âU$+*U£€UN*O€€UUyTOU*Ozz$**y€¤zO*Nz€y+€yyV€á*$ÚT€€€O$*y€àO*Oy¤€UOz€UNÚNUy€UÛ*y€€OUyyOÛ*U€€O$$Uz€€y*ÚT€€U*U€UO%ÛzÛ*O€€U$$O€€yx*$O€zU*y€€OzÝÛy¤z$Ú+Û*y£€OOÛ*y†UO**€€zO€Uz€ÜÚ¤U*ÝßU+y€£O$+NU¤€O**ကOUNÜyUOPy€z €¥*U£€y+$+U€O$*Uz+U€UÚऀUU€€N¤¤z*yOU€z*U¤y+$+s€U**U€€UÚU€€ÜÛ*z¤yz€OÚ$+$%Ú*V¤UÛ¥OU¤€UÙUONU$N€€yO*$U¤£U*$UzâOÛÛUz€zÝÛOOÛz€VÛ+y¤OUUVUUOUOUz€yUyyUyUUUàyVzUOy€¤yyz€zzzy€zUO$U€€UNyOO*OÛNzU*$yyzO*$O€£U**O€€O**Ty¤UÛÛ€€€yyUU+UO**y€zÛÚOy£xÛNU€¤€¤€£€¤¤€¤¤€£¤£€£€¤€€¤€¤€¤¤ª€¢¢££¤¤£†££€ª€¤€€y€¤O*€N$*U¤U€¤y*¤U€¤zUN++U€O**N€y+$Þz€Ty£U*ÛÛÚ+**y¤€£€£€yyUO+++%%+$O€¤y*yy€*ÛUyTy£yUက€¤€¤€y€£€£€yTTyTUy€[yzyy£y££€¤€€€€zUz¤NÛU¤[$*y€yVUy*¤TTyTy¤¤€£zU€£€U$ÚNy€€yO*N€£yO*ÞáU0*Ty£££¤¤¤¤€£zyzO*Vz€z€y**O€yyzUUyy£££z*VzzàUONz€¤z¤€szyày€€£y¤¤U***N£zUUU€z[UTOUU*ON*Uy*ÛU€zO€N£¤£y£x£yOy€¤UNÚ*z¤€yOÚN££yO€¤€€€€€£y£y~£yy£¤U**NTOU€z€z€££U*yUy£y£yyUUONVyN€yT€NT€£€*ÛO€¤yU€O$yª€Ty¤¤¤¤££££Ty€€yÜ$*z€€yTO£¤U*ßÞOUUz€yzy££££y££y£££yyUUy€¤€žyUU£x£UUTy€x£yTz+ÜÚO€y$¤€UÛUy£zUO£UU¤*Ùy€€O*T¤£yOzz¤€£€yUOUy€yU$*y€£€£O$%$ÛNUy¤y£y¤¤¤z€£€££yyz€€€yOUON*y€yx£yUyU€yxTU€O€y*€€z$U¤£€NNy€¤UU¤U$UyOÚy¤y$*ÛOUyzz€€¤€¤€O*Uy€€€O*$y€£€y££y+ÚO¤zyUyzUUy€zy€yyUz€zy£yUUyx€yUy€yyUzN€y*y€€*Uyy¤yUÛ£¤U€€NN¤*€¤¤N*Û*NUOV€€€€V*U€y*N£y££ž€zOO€yU*Ú*U¤£yy*Uxy€Uy¤yN€y**á¤**£€y*UzTy£¤€sz€ONz€NNz¤Ux£€Ty¤UNOU€zO€yNy£~y€U*y€UUy££yO*yUy€UUz¤xN€O*U¤N*¤€z*¤*TyVOz€zU+*y€£U€¤£€$££**OÙO¤x£££xNU¤U*U€yy€£y¤UOOUÚÜz¤y£TyzÜO€y*¤€U€NT£U€VU[€£¤UN%ÛÛy£€€yU£¤£€£z€£*ÛÛOT£œ£y£NNU¤UUO*Oy€££€UUyTy€UÛÝ€£yTO*£TN¤y¤€U€yz*Uy¤¤£€zOÛÙÚÚy€£y%U€yyTy€yO¥¤UÛN££y£¤U*y€U*y¤UUU+%Uyy£zNÛNáyOyÛUyTyz%$z£€T€£NOU€€¤€yÛÛÛy£€U+ÛU¤¤N€yÛz¤¤¤€*ÛN£¢£££y£UNy€U££¤€yUUOÚÚO££¤U**yUN€T%ÛUyyT€UO…*y£€O*U¤OÛ+OUy€€¤€yy££¤yOU€xy¤U€£OÛy£x££¤yNU¥z€£¤£¤¤¤€€zUUVN€y£y€ON[OTzOO€y¤€UyNy¤Uy€€yÛÛÚ*Tz€¤€yy€£££yOyT€€O*y££€U*Üy£¢£~£€yNUOz€£yyOyz£yz€€€¤€€€Uyyy€y¤UTyOU€UÛU€€£yy$y£¤*y€zÝ***U€¤€¤€££££yyUUzzPÛ*U£Ty€+N¤NOÛNz£y£y£¢£OO£yyO$U¤€y*N*Tà€UO*UUNTzyyNU€UzUy€UO*U£N€£yzUÚN€¤zÛ%Û$*O€€€£¤££££©€O*$NyUU*y¤yyO*+ÛÛO££y£y££€yNÛUyU**+*U+ÛÜzÜyzTyxNU€T%N£UOÚU€y££€NN€€y+$UUy£££y££~yUyU¤UN*€¤££UÛ+U££yyyy£y£yN*UUNÛÞ€áOO€~£Ty€**$T¤yy£€y*U¤¤y€ž££££~¢£¤¤¤£¤zOOz€z¤££€yyUO$N£€yO*z€Uyy£££z+Û$U£U*UÛO¤xyO€€y*UOU€yT£zO*Nyy€zÛU¤££~£¤yT¤€U*y£€yÚÞᤣy£~€£€¤€€€yOUUUU€€¥€€zU€£y£O*€yUx£y+U€NÛÛ*y££U*U€€yy£y€*€yN**+£££¤UNzyO€¤zÛ$*€¤££NNUUUU€€€€¤€¤€¤€€UO€€yU€y£UO€y$++Û*Oy£y+Üy€¤€OU¤y€OU€£xN£££yzO$**1N€£yNÚU¤yy$Û*NOTU€UUUUU*€€y£yO+Uyy£zyÛyz£¤zyUOO+*ÚO€UÛUUy€UÛU£€€¤N*O*z£~£yy¤£yUz¤£¤Ü+%++%Oz€Uy£yTÚ€£zzO+U1U€€€y££¤¤€€€yUUTUUN***Oà€£¤y+Û$U€£yO$N€£€ÞO*NyNy££yO€£¤UUUy¤¤O%Oá€U$T¤Uy€€£££€z*Üy€€€y¤€yyy£y€¤€€¤€€€€z€€¤z%ÛÞy€¤y£€OUz€y*Ûy€U$ݤ¤yy€£Uy€£Ny¤à+U€z*NzyN%Oyyy££¤yÜ*****ßzUNy€€yNOUUyz€z€z€€€zUy€Uyy€£€Oy£€Uy€UÜz€£€OU££~¤yO£€Oy¤€U€z*$U€zOUyx£yy¤O***Û*UN+N*Ny€N$O*U¤yy€y+Ny€Oy€yU¤€Ny£~££€OU¤yU£UO€€OÚs£y**Üy¤y£yyUO*ÛU€€ÜÛOÛNU€UU€y€€y*U€yO€€€y£££€O*y£¤Û+U¤Uy€OÚOy£U+U€¤xy¤***ÛOzUÜN€UOUzUU€yU**z¤U*zàNz£~£¢y+*€¤yU€€€y+€U**y€£yUÛUz¤yyUOÜÛ$+z€UUz*V*z€€U$U¤Ü$+$*z€y+€€£~£€N*yy¤U+U¤¤U€zÜ*O€£yÜÛÛÛUz€y¤€UÛ*€V++€€€O+*OOO+OUOUy€yUOz$*$U¤yU¥€OÚ*y£¢£€O¤TN¤€Û*€ž¤NU€U$Üy£UO€£¤€yÜz€U€€€€y£€€¤y¤£££€€¤€UOy€¤€£Tx£yyy€€€yyUO**y£€U€Uy¤O$y€£¤y**O*%€y**U£yOy£y€zO€z*߀VV€yz€£€£€€£~UNyU¤€¤€€€€£y££€€yyy€£€¤€€€«€zU£€yU*¤€*€O+Uy££yyTUyUyUUOO*%y£Uy€y£TOO+ÚOOÜOOUOÝO€€zUNy€yNÚOyUOUy€€€€€z€y€€yzUzUOÝUÜy€¤€y**[¤TT¤N€¤£¤£¤€¤€¤z¤UO$*OOÛU¤yy**ÛÚOÝÚOã¤àOÛÚÚÙO*NONONOO*$ÚÛ*ÚÛÚÚßUUzÛO€y*€Û1y¤££zUz€€yy€ßOÜÛO££U%ÛOÞ¤¤y*ÛÛO+ÜÜÜÛ%Û**O€€N¤UÚNUy¤y£€yNÜy€€€VÚ+yUOÝOy€¤€OU¤U*€€*ÛÛO¤€UÛÛÛÜO€¤Ú%Oz€€U*€€*O¤O*U¤€yOÚU€£yN%ÛÝU€OU¤UÙz**U€zÙ**y£U%ÛO€U+O€z*NOUyÜÛNU£€zUzz€€OU¤U¤O*+y€+%ÜUy€€€££¤yNU€z¤U+*U¤y€U*OãV+त€¤€y¤NyyN*UUU€yUU¤NN€€Nz+UU€£€yN¤y*U€OOV1N€U*€U1+*UU¤yN£yU*zz%O€y$y†*U€NU£Ny€Ü€¤ÜU€O*yyU€UzåUÜ***O£xUz€+*ÛÛ*O€z¤€OÛ*€zUNVzÛ*U€à*ÚÛÛ+߀€*ÛÛÛ++ÝVÝÚ+ÜÜÛengine/resource/hexen2.icns000066400000000000000000000747401444734033100162400ustar00rootroot00000000000000icnsyàis328QZOB%‚ Å^Qÿf}Pš¥QP|Ÿ~ÿÄ­‘=BGº™ʨP€ ©|‡g;¥µ€ Î2‘‹$‘¹©N€ m†·Äɽbg%€í]ÜôÊ^R@‰J»€lœ×–"xL’Pu·ÿ•‡~“–`XD‘>“¦ÿ–xrUBž}=7m*=yutT´ˆK/|hmd!ÿlÿ€gi[²F‚K}|W3 M!߯H7dA€ ,Ô. éˆ ˆ‰  ˆ ‰½ÿûs8mkDD“ô1uD ôÉ;¯~›Û>S㨄î…%‹ä½6&ÃßJI×ÿØ.&4Ïåíÿ¯®ZL‰€ÿÿÌAŸ¬§£HÄÿøg «¼¸¥fØý÷ñ?²¨­¦ ƒÙñÑx˜w £‘ [ÝöËb1·t˜w™faÒñÏZ€Î­H~<mÙð·Fs¡+RÒ¬HS#til32x„EU–‹j‡Cth[;Œ£€ÿ€ n€ˆœoˆmi8…¾ÓmfK‡†µ‡# >z²œeˆª¾·M/€ M:5Ãö~†ÿÿ©µ\„:‚žð¤ƒ4ÃÃT„ ¡a4¬ÓExn€ ¯øÖ<€†yk?Ê¥€–0µã«X-ˆÁ†am¾Y‚NZ¬è£‹ÿˆ`Œš a’…½ð’Œ@ƒyÇ„L¯ Ðö›„‡e€ •ÝÔ„¨¿×ô“€8# ˆz¬½ó÷éîä„ P6 ˜\‰&·äóûß„@AmIÁwˆY¢€¥ÏãòšWU(‹nÆt‡ußl¾‰õú‘96…b,Çq† ‰ÿ•ʼnoŠµí€ €GvL(Çq„Aª€ ²ÂyTžf[ÒÎd€Qf:"Àd„²ÿ·¼gDšqlYcç{€SX,!¤PƒI€źQA•cTb”„[LD _X «PO]LF »pd;T4€ lT’…Fd`WA‚çJ,-v\ŒxBhlf<ƒ‡¢Œ8 € i{^gyts2…y…… xtZ„}js)ƒ„€¥_‡ŽrXo\_„&€Ÿ”I…‹{{R,D€ª‚6ÿ„`…€•uAƒN5„~†Wƒˆ² =‚eHˆÿzÿ„6€,˜ÿÿ÷•€ • – &”&” '–'– %– ” ”  – šÿýÿÿÿÿÿÿÿïl8mkE% ƒ²{:-ËÿßiZQ8¡Ôè÷ÙKsñ B¼ôôîõôsqÿäZ+TP]Êÿôn}öäf Eûÿ‘ÿõr =c·ÿÀ&¢ÿÿäX?¼µçûtvÉÿÿÏ~)\ÔÎûÊ1WÆÿÿõ¨?zÜæùŒšòÿÿý¸:‡ìÿé_5¾ÿÿÿëÉÆX$š;ƒøÿÞâÿÿÿõŒi츑ÿN ªÿÿÿÿÿù´%:øèAØÿI_ÿÿÿü÷íš=øü_OôÿK|ÿÿýûõÉZCúÿqaüÿN£èõÿÿÿÒ[FþÿobüÿP1Æþéìôÿþ˜Kÿÿd`üÿTEÚøÔêàâÿÿ¶NÿôS[üÿUbãêÄâäçÓµýòDMÿç=TüÿF™÷Õ¹ÛÛÚÕwsð‹LÿÙ)Qüç0uÿ×»Ø×Ô¸_ÉìLFÿÃPûÂ.»ÇÁàÚܳL¦ÿ¼Bû­RøŸ 5¶ÀÀãåã¯COþÿŠ`ë’Oñ}>ÁßÏÛçí§:ˆìè¶Î_Jâ^G¼ÙèîéèŸ/5²ÿ§/…/MÅÙÝðîÒ}#/à¹#UÚöîÉ“†PwäX¬ÿø´K_9MÝÛ'‹šQ ih32¹ˆG ãC§ f5«Q‘>p0 “36ˆ &L7Ô¿…A•ˆ t3VÀ,Å"@êPŒ†Q€•è(clZŸ”?Õž‰ !d¿íûÌ–Xg3ÿ2ÛЇ 1ZI78n«îøŒ’0Ôæ† ÿƒkÿÿ¢ŽHVáÒ°ÿºŒ€gçž’Œ €Nëÿ7 ‰ ÿpôõb ed‰ÿª‡2€ŠëÿÿÜ'n§Öÿ;ƒ$ÿ¨áåÊfŽ ÿlŸJÿÅ„ ÀH¸ðø²’„“‰ÿX€ ªWx<Òîõ¯“c”}¬ê 'Œ¨X׿þ—•©}î“‚ ´¹lÝáÿ„ÿ »xÉÿd ù‹åæÿ˜›Ûb‰>ƉŒo¯5ãºyÊ?ˆT€EØ.‡Jÿs¬+®©’£Ž‰À‰²Æ›{|ŽD»}ˆ ÿº%dØi @ EŒ‰CUºË0‚‘< (‰qÓo 8”‹ ·©dŽ ¦/ª4«ÿÿÿÿÿÿü¤ ƒ!¤ 1¤4¤,€3¤-€!3¤+#2¤)"2¤#"2¤ 3¤ +£ !£ £  ¥¥¥‚ ªÿÿÿÿºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛh8mk  ʇ1XÿÃÀu/gÿÿÿÑ_AB w¡ÿÕÿÚb NüšR)ÆÿÇÞã×ÿ³2cÿ÷ºS5¾ÿÿÿÿÛöÖÿÎO^ÿÿÓ‰T–®¸¼ÌÿþÿþÌLZÿÿá”. ´ÿýÿÍL‡ÿÿÞš6íÿÿÖ\ÿøÍ‰.qÿÿñ}«ÿþöÛa 6Òÿÿ¾BÅÿÿÿÿå€eÿ°ÿÿì)qÝÿûÿøÐ‹;ˆÿ”°ÿÿÌIŽòÿÿýÿà‘@µÿÚÿöŽ YÚùüþþÿׇ*Óí–çÿÔ]³ÿýüüÿüÒ|$#ìèµþý®1(ãÿøüüÿÿØÑhQ 7ôý÷ÿüŒLûÿúýüÿþÌ•øÀS|ÿ:0Óÿûÿä±ÿÿüüûÿÿÔr[øö”0ýÿXÖÿüÿÿýþûûÿþÖzMøÿÁ<¡ÿÿZ‹ÿúþÿÿýüÿøÕ„"R÷ÿÞ^ïÿÿ]?ÿýûÿýýÿðÓî‰6Sùÿï ?ÿüÿ_ÖÿüûýÿîÞúÍŠ(Vûÿÿšfÿÿÿa:âÿûúÿúêÿÓ.Xýÿÿ£sÿÿÿdÀÿóìÿüüþþÒ„ Uÿÿÿ¤tÿÿÿf'ÚÿêÔùÿÿûÿã˜<VÿÿütÿÿÿiHýÿé¼ðÿåúýý×ebÿÿõ tÿÿÿlbÿÿܰìÿÕ¯÷ÿýÿ§ hÿÿîpÿÿÿpÿÿПêÿØÇõÞöÿÿÕ2eÿÿælmÿÿÿh­ÿýÅ–ìÿÎÁÿ蜋ýÿî_cÿÿÜWhÿÿýKMÝÿó·îÿ·ûÛ¡YWÿóœ `ÿÿÐDeÿÿç3uÿÿñ©“óù·µôÂH wÿÄS^ÿÿ¿4aÿÿÇ•ÿ꣟üó®Ëú¶~B"ÿù§4]ÿÿª(]ÿÿ§2ôÒŽ´ÿí®×û¯u-ùÿõŽZÿúYÿÿƒ<ýÐ}Äÿä¶çõ³r'Ãÿÿçi Uÿô{UÿûaHÿÌ~ÉÿÚ¾õë¬mJÿûÿÓTVÿå`Gÿë9VÿÑ”ÎÿÈÅÿé©f°ÿûÿ®söÌA0ÿ×dÿÁÚôý»Êÿà¤W €{úó£ó$ÿ¨qÿ¹žÿøÅàÿÛ˜Q sÿÒÚDÍSƒÿ´Ÿëò¿òöº€3¨ÿµB‰ÿ²»üó¿ùß b"úëˆ ”ÿÎÞÿ祂oVB ‹ÿ¡?d÷ðüÿÞ—Q Œo8ÓÿòÂ~2 ÎÿËH­ö¢x,E6Eit32!O˜aíÿ‚ÿ†<;ZZF$ïG3‚%?ò+:@@+ø =R^W5ø9Ynrb4÷ Op‚ƒe' ‚#*¼'7¤_€ `ƒ’†_40QhgC€ ½"Oÿ›@0 l‰“ƒ†—ž’oB¸-hÿ„'›=ÿ `‡ ¹Í϶˜rP¹Jš‚ŸL1‚ P“Íóî³}w’Œ\€´09bÞ‚" 6(ƒ b¶éé«]T޲›S±U!Tÿ/@A8œ+0e† o¾Øµ^-b±Å‘C ²9LƒItwjL™ 03zÿ!`iK v¶»}10¿¶}2€¯ ÿ…Y“«•Dzª›\ ;À¯i¯:.ƒ0€ºØÅˆ6˜B]…K•ÈäÏŠ*-z ‰=U®Ä‘>­2][‚@}¶Üâ²_˜ƒlS^pžÒüÿÿÍv9…•a*€®”N €­8ƒ4}·åìÆw'™#,€$N{”§¸Öõ€ÿ÷µYTˆ†U6W€|L€«/uk‚9z·åùÔ….™‚%7Uz»Ïàèíòöñ× X :­›kVdeA€­fÿ2y²çýå™>€” .@Xs¥±³ª¤£®¼ÆÀ™e^“ÌЮ‰yb4 €©> ‡ÿ‚7y´èÿòªO €”3EXfnoke\VT_v‘ªªŸ¡ÅìõêÑ­u.€ªg‚,x³åÿ÷·] ”5GOH6#*;b…¯Ôöÿ؇,§0F|ƒ4z´éÿô¸c€•€(Y2!‰Kžà‚ÿë-¥ó7z‚:¹âýì°_˜ ²ŽËýÿé’1¦I_ƒRŒÁíúæ¨W€˜Šc¦Ùù€ÿê™;€¤$V„ ešÊèôØ›N —! 8Ne{ŠŒ…ƒ€‚~°ÿ€1r¯â€ÿéžB€£Vi‚ d¢ÎìçņB €¨ÿ 9ŒÉ÷ÿÿó°X€ ŸGÿ‚f¤Øîä°l*ƒ¿¨DSƒp¬ß€ÿÉw*€ Ÿy΂ a£Ùÿô»g!€¨ÿ„B„¿óÿÿí§Q œb?z„ j¡Øÿÿïª],­ ƒM›Õ€ÿÓ{" š )o‚ 6u¬ØûÿÿîÆšxR3²=<3" u³èÿÿðžC€˜)T†8~°ÜüÿúçņF ­C^efV1ƒÈþÿÿÊx,€ ”J€.388„Uˆ¶ÙóƒÿôÊ‚<­ ;rŠ“@€ X¬èÿÿò¨O “=TG†Z”»×çòû€ÿý廉L® #v›¬›d~ÃøÿÿØ}'’0Hn=€"nŸÆÚÞÞèòòßͳ’a7¯ ‚[¨¥„E€F˜Úÿÿö¦O €Ž (Sˆ(mªÍááÝâì寕pR9‚«‚L…žžy3€\®ìÿÿÚ‰4‚ ‹@NC ÿ' *n¯Úææàäîã¶y;„« +‚Fˆ¦Ÿt)~Åüÿÿº\ ‹YÿƒbD%'m´ßñìéò÷ê·o.ƒ­ ,²‚_•©Ÿn7Ýÿÿè‘=‡3+a…WfJ*2r½ìôïí÷ÿë·h† ®.;³ÿi™®œc O­ñÿÿÈo ‚€E€ ^ÿ‚Inl\?4DÄòøéèúÿî¯c„².;c?p¨”Wi»üÿ÷¢B „>¨„R€…gI8UÐõúéâïÿë°\„´> ‚%yž©ŒJoÊÿÿ×{*€†°‚q“•WF[˜×ú÷êãîñÞ¡W‚ ·ÇY>€¢£ƒ:„ØÿüµX‚ÿƒ6§«ŒcJc›ÔðñäèôûÚN»]/‚U¤¡}7+äÿäŽ0€ÿ$ÿ]²·ŸqYl¤×ïêãçùüß—I ºÿ€Ô`¦¥€2;©î÷½`€ ÿƒr©ÃÀŸv`xªÖåäßëÿÿážJ €¿}glš³¬}._Ãýï¤E‚#f‚)y²ÔΦwg„·Þêâáïÿÿá˜I€‹·%r¢¶¯….!Œðÿà).‚0¹ØÒ©wj‹¼Þäãç÷ÿÿæH €“«!¯ÿ-{§Â»}+pàÿÿÃo'€‚ @‡½ÜÔ¦zr—Çâæàè€ÿá›I™ª0†6¯Â©v[‰Õÿÿó´b„LŽÂÞÓ§z|¢ÎåâàèýÿÿéœL €–­+½‚I‚£©¥¦Àßùÿÿê¥O ^™ÈßÔ¦…°ÙçãÞê€ÿâ›I ˜±Ag‚ ,eˆ®ÏèððòÿÿÛ@€h§ÏãÓªˆ’¸ÛãÛÚèþÿÿã—I‘ ±B[}‚J‹½ãõñßãóöÇv" [£ÕâÓ©ŽšÁÞßÏÌÞ€ÿãœI,1# ²5G¹ƒ!BƒºßêÚÑãüï¶mF^ÎâÏ®—¥ÇßÝÐÉÚòÿÿã™J ‹Gf]3 ²cƒ.…¾ßáÕ×ëù潤°ÒâÓ²£³ÒâØÆËâþÿÿÞ›J€ #‹1v}7´DÚƒS™ÌééÜÝôÿûóô÷êʸÅãòáÇÁÝ€ÿÔG €.E<Š^¬È3¹ÿÿ€-|¿òÿèÒáûÿñÜÚéøñÚËÚúÿÿΆ:€<`X,‰5ŠÕ؉-·$ÿ¶d´óÿöÖ×ì‚ÿ úøòêâãëüÿñÀx0‚BtxMˆW´øâƒ)¶%€6 ^°îÿÿãÖÙëûÿùãÖÞûÿÿç¯h! ‚>zŽl,ˆmÎÿá{&¹,5 M¤ÜþÿõÞÑÝôÿìØÛðÿÿç¤Z"96€ ‚8€©•K ‡&‚æÿäw$¸A+:‘ÀæÿÿêÐÔìÿÿþñåáõÿÿÞšI Id_D€ ‚1‚¾·h‚6“ôÿât"ÀiÏüÿõØÕãòõîèîûÿÿÕŠ;!_‡„a3€ ‚.‡ÓÜ,‚E¤ÿÿár!¶A8t€?‚¼òÿÿèÝÙÜÞáæùÿÿ܉,5uŸŸF ‚,ˆÞ÷°J€€ O­ÿÿßq ·9 †„i¹öÿÿòßÍÇÎàòÿÿè›;E“³¬‚L€„-ˆßÿÅd€€T±ÿÿÞq¹eÿƒU¸€ÿïÙļÇäÿÿ÷°PI™Å·†Eƒ.‰ßÿÎp €€[·ÿÿÜpµ:–…uÅ€ÿäÕÈÁÉæÿÿá|DŸÆ¹> €ƒ0ŠÞÿÈm€€aºÿÿÜp¶‘‚.^t‹§ÃÜãàÝáÞÔÏáÿÿÒl!4¼³{4‚ ƒ2âþÀa€€$h¿ÿÿÛo³'I¤ƒI|¨ÊÔÇ¤ŠŒ®ÛùúéÖÜöÿØ•hy—›l- …3åý·U€€)lÁÿÿÚo¯€GÿNˆµÖêܬ`/BßÿÿöÛÙçùîÓ²~U‚´=…4“èü¯J€€,oÃÿÿÙn°dÿƒX“Äêñܤ]F ñÿÿþà×ÜõÿýÑ“J(‰4•ìüªB‚€+nÂÿÿÙm®(DBÿ‚b›ÏïúÙ”B L‘Ðú€ÿêÛØðýî¯_‚Mˆ5˜ïû¢8‚€)kÁÿÿÙl®(¨ƒlªÚüù×0J®çöéëöÿøäÞëã¸h …!‚6šñù›/‚€&i¿ÿÿÙl¬3h•ƒ5w°ãü÷Ƀ2!—õÿ妙¾ïÿñèæÈˆ3…'9Fÿ‚8œïñŽ#‚€#f¾ÿÿÙk®ZÞ‚=»èÿì¼w+m×ÿú¨K:vÆõúñâ»z(€!()€L]^‹ƒ:œê。€!d¼ÿÿØkª=}ÿƒ$JÅõÿæ¨adÅÿÿ¼X7¡îÿÿåÂŽI˜ÕÁd‚€ ]¸ÿÿÖ` ¦-Z‚6w­ÛþøÄw)€ kÆÿÿØ‚&Dsš·Ùóÿä­{SC7#H€†>•̵\‚€ [¶ÿÿÆO¤]ãƒJ†¸æýõ¼j€sÈÿÿÒ}""k Šp†¼ð€ÿù²`?“Æ­V ‚€[¶ÿÿ´?€  cÿ RÂæûç¯`€vÊþÿÌw4€·ÃX)wÇø€ÿ´R‚ˆ?½ M ‚€[¶ú÷œ/€  =*Ú„^œÌòøß V€#~ÌûùÄl7‘ÉÔ²m?’Ѐÿ¿Z ˆ?·˜G‚ €\µóç‹'€ ž<ktƒ e¢ÖñôБG €%Íøò¼h3ŒËܺv)€ WžÜÿÿÚ~*‰@‰­Œ?‚€\´êÕx€ žBÿƒ $p­ÞüñÅ€=€-…Ìóë³\ 5‹ÄÒµq'‚j«àýé£K€‰@‡§ƒ9‚5€\³äÈk€ œ66tl‚ D…¹çûí¶p+0ˆÍïâ©W5…¹Ç¨i"„%n¨ÙçÄpˆA„Ÿx2†]²Ý·X € œCC‚ Bl•Çñÿæ©^!9ŽÎêÚ M€4±º_`¨ØÙ–2‡B‚—m*‡]±Ö¦F€ œff c޵ÜüÿæPE—ÐçÑ“A€6€¦¬T u¾Ú¯P‡Be$‡ _³Ñ™8 ¡ HqŸËðÿÿבAS¡ÖäÉ‹8€:} ŸI Ÿ‚4žÏÃ+€†B|‡Y‡ ^°È‡%¡_˜ÊûÿÿÔ‚6c¬ÚãÂ})F‚›“q<€=‚½Ë­d…D{Q‡ ]«¾x a¥ÝÿÿÓ-r·ßáºv"U‹œe1‚†_ªÏÕ£N€ „ExyE ‡ Wªd œ`‚S£Ùñσ//ÃäÞ±fb˜¤ŠZ#……€W˜Éê׊/€ƒFvr=‡ PŽ˜X ™ &ƒ‚^¢Ã¾„47ÌéÝ©\ iž©V‚ƒJq€G‡¿÷ÿÈi‚Bkb/ˆ G{J ˜ oã‚T¯ª9D–ÔíÚ M€+v¦­ŠN€.n€B|·óÿï¤Q € ;]S$ˆ ?lrB—F4ÿ‚T««9L ÛïØ™D€3}«¯‹I‚…]€ n±çÿóÍ”K.G>ˆ 7\`7—~Ó‚YŽ­ªˆC€X©áòÕ7I‹°°†C€  ‚Mf±ØÜÑÏÁŒ9F€!3,ˆ 1OR/•WJƒ\ª«…?€ _°èôÒŠ.\”³­ƒ>‚ˆ0 W«ËÄ«¬À¸€7€€ ˆ (>?$—Es‚^‘ª§…C€ g¶éôЄ(h£¹¬{7Š :˜½¾š•¾¹|(€ ˆ -,“--[Yƒf”¯¬…<€n»ëïÆx/€¯¼§s0‚Œ‚ nªÂ§o`šÍºhˆ•I‘ d•­¬„@€r½éé¿o<Š»Â¢j&Úl€ ˆº»ˆSk´Õ©X€Œ  €‘2Uÿ‚n—¯¨4€u¾äà®^KÆÃœ]€)* \¢Ã±xY€ÃÒ›A‘SG‚$l˜­§x.€x¾ßÔ¡P€W¥ÎÇ—T‚­( ‚ q³È§fT’Ó΄/‹ƒ‘59ÿ‚1wš°§y,€"z¾ÚÉŽ7jµÕÅH %„ 9•¹v9U¬Ý¼l€‹—,FO‚=w¬¤u8!s¹Ô¿€/w½ÙƉ?‚”iu p ¡j$"xÊÛ«W –9ÿ‚C€Ÿ°¤p& /Oc¬Ç°l;ŒÉÜ¿€5œ BfiH?×Ö–:¢7lj‚L€¡¬¡qs¬ÁÀµ™\ OšÎÙ¹v*ƒ–&*ÿ‚  m¾âÆw!€¤’ÿO„Ÿ¬m!TµóùÑŒA]¨ÑÕ®j!‚ ˜ U«† 6”ÐÛ­Z :‚›‚YŒ¥¬œl0œçÿêœ8!v±ÍÅ›Z™ 8^÷†_©ÒÌ“4Ÿˆÿ\Œ¦«™i! yÒõá™:1¸É¶†D ‚.Uï…xºÓ½r€œ:?}¼‚b“©¯—ad¶ãÖ/A’¼¿¡m2‚3‚ÿ 9ÀΨU–G¸t‚c‘ª¬—_i¯ÒÈŽ/K“½¹S‚ ¬S¡ÏÊ9˜A_ƒgš®¯R.ºÔÂ…6Q—¶ª|<€¦ÿæ€y¹Îµq#5\i˜³±“P€@ŒÂÖÉ3E„©¡n*ƒ®©¬ÓA•À̧X ›?ƒj£º¸ŽCUŸËÓ¸~61kŽ‹` ª„d¨ÒÇ"šB‚iž¾¼–Lm­ÏϪn*@huSƒ¬ ª-—ÎË0šm=~«¾¹ŽIJÁ×Å•W€;I< € ­„ ‡°§y2šÿ€4~«Å½ŽL!;z²Õ×»„?  ƒG¯!0‚q…xZ.œW‰¬°X‚Ä?l¥ëQ ›/°ÈÛY„ ë1rž»·ŒG ì&aŠ£§‚;„î>l‹s3 ï?e{g.„ð9GE‚ò*ƒø øÿôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ø åŒ  ဋ  ဠŠ!& å Š )*æ ˆ#0,æ ˆ(4,æ !‡-7,æ %#‡ /8,æ )+‡ 29,æ+0"†"38+æ+2&…"27+æ +2(…$37+æ +2'…$37*æ ,1%…%37*æ ,1#…%37*æ -1"…&37*æ .1! †%37*æ /1 †%37*æ /1 †%37*æ //†%37*æ .,†%37*æ -*†$37+æ +'†$38*æ *%†#38*æ (#†#36&€ã &!‚€#24# €ã $‚€#10 €ã # ‚€#/-€ã ! ‚€#.)€ã   ƒ €",'€ã  ‡"+$€ã ‡") ã ‡") ã ‡"'ã ‡!%ã  ‡!ä  ‡ä  ˆ ä ˆ  å  ˆ  å ˆ  åˆ €á€‰€ôø€øùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿt8mk@ % DfcD# X™³ tL2& FšÚìÊ”bG1&iÀþÿÛ¥€lk[G( 'uÑÿÿá½°¸²ŸxX‚ÆÿÿÿæÄ¶ÇæøîÔÉ×òÿÿëÇ’d5#c²íÿÿþäʶ–kA9i™ÑõÿÿÿÿêÐÈÕìóãÎÊÞ÷ÿôÕ¥xE$b­îÿÿÿìÙ݉Z1/V}žËëÿÿÿÿÿÿÿíØÑàòðÚË×òÿøÞ´ˆR, `±íÿÿÿôßϵšoAGƒ»âöÿÿÿÿÿÿÿÿÿÿëÛáñ÷éÛàñÿöà¹X0^¯ðÿÿÿ÷æÏ½œzL%-wºíÿÿÿÿÿÿÿÿÿÿÿÿþñíôúöññ÷þñÞ·W/ \¦êÿÿÿþìÜ©€Z/ 4r£ÂÍÎÕÖ×ÕÖ×Úßçòúýûûýÿÿÿþýïܵ‹U- Z¬èÿÿÿþðÚǦ‡Z6 N¦ôÿÿÿÿÿÿòຒ\2 FšÜÿÿÿúîãÒ»¤_:" kÉüÿÿÿÿÿõåÁšd9 MžçÿÿÿüîßÓ¼žW7 8—ãÿÿÿÿÿøëʤn@KŸáÿÿÿùîçÛϲŠd;!  ^·üÿÿÿÿýõØ´~O" S¦éÿÿÿÿúòðèÛµƒW- 0ÕþÿÿÿÿþæÆ“d4d°ñÿÿÿÿýûøüøë‹]3IkY0 T¨éÿÿÿÿÿðÖ§zG$ (i¹ïÿÿÿÿþÿÿÿÿÿùÑžrE( 0‡Å¼†E/BÊüÿÿÿÿùèÁ˜b9 5~Ãýÿÿÿÿþþÿÿÿÿÿñ΢zK+ <¢öÿÖaPo¬çÿÿÿÿýôÔ­wJ!G†ÍøÿÿÿþÿÿÿÿÿÿÿôྖqD$ /ˆåÿÿÕ tlÄõÿÿÿÿÿçÇ”f7(;\žØÿÿÿÿýýÿÿÿÿüòçй˜sR-M£íÿÿÖ›vw¡Ûÿÿÿÿÿñت}J! :^x®ßþÿÿüùýÿÿÿþôèÒ¿¤mL2 V«ôÿüÄ‘s…¶èÿÿÿÿûìÇg7;b‘°ÊÞðÿÿÿþüýÿÿüñæÑ»›ƒhW>&b½ýÿíº‡{–Éöÿÿÿÿ÷Ú´€M( $U‘¸×ãòþÿÿÿûúþÿÿüñÜÈ©iR;. tÌÿÿè­‡„©ÛþÿÿÿÿíÏŸk@Cz«ÜòÿýÿÿÿÿþýÿÿÿûêØ¹wY8% +€Úÿÿاˆ’»éÿÿÿÿù弉\.5mŸÏèüÿÿÿÿÿÿýýÿÿÿúéϵ‘pI/ 7”åÿÿÓ£ŒÉòÿÿÿÿñСtB""ZÀßøúûùýÿÿþüýÿÿÿùêѰ’kL*DŸîÿøÊ§Òöÿÿÿü体`87w¹âúüÿüýýÿÿýüüþÿÿúåÏ­ˆgA'V²ôÿõÄŸ–®×øÿÿÿñЫwK "XÊöÿÿøö÷üþþüüýÿÿÿóåɯŠcC$d¹øÿìÞ›µÜúÿÿþ寕i9:v¸åÿÿÿÿùúüýýüüþÿÿýôßË«g@$ (rÈÿÿñ¤¢½ãýÿÿòÙ­‚P- @ŠÆöÿÿÿ÷õøüýüûüþÿÿÿõèм›{R.+€Óÿÿéè°Ïðÿÿÿï̦sJ#U›ÝÿÿÿÿüùúüýüüüþÿÿýôãÚÎÄ£|N+,&>’ÞÿÿîȼËçýÿÿüäÄ‘e7Z«âÿÿÿú÷÷úüüüüýÿÿÿÿôâÐÑØÛ½‘_:$!\{g5 HšéÿÿëÚØèúÿÿÿú幌X3$n·õÿÿÿÿúûüüüüüþÿÿÿÿõÞÄ´ÂÝñݳV: \®Ô°aS©êÿÿÿòóùÿÿÿÿùׯxI/sÁôÿÿÿøøúüýüüüþÿÿÿÿú寍œ³ÛûóÑ¢uQ+?›ìÿÙ)Y£çÿÿÿÿüýýÿÿÿóÔ¡mC4L†Éþÿÿÿüúüýýüüýþÿÿÿÿ÷çÈ¢‚~ ÓüÿëÄ–m@" *€Ûÿÿì”:N˜ÏøÿÿÿýûÿÿÿÿîÈ›|¤Øÿÿÿÿûûüýüûûüýÿÿÿÿ÷ãʤz\c’ÐýÿûÞ³‰W3d¾ÿÿÿñŸED{¿íÿÿÿüüýÿÿúãǹÅãÿÿÿÿüüüýüúúüþÿÿÿÿøéÌ®ƒW>QŒÐþÿÿëÆžkB D¡êÿÿÿî¡J %j¯ìÿÿÿûûÿÿÿøïïùÿÿÿÿüüýüûúúûüÿÿÿÿøãÍ©…X0"DˆÒþÿÿöØ´T(&}Üÿÿÿÿð£M &h¹îÿÿüûüÿÿÿÿÿÿÿÿþýýýýûúúüþÿÿÿÿõç˯†a7AŠÕýÿÿúáÁa1C¢÷ÿÿÿÿð¢M #zËÿÿÿþûýÿÿÿÿÿÿÿþþþþüúùûþÿÿÿüóÝȧ†[9 @Œ×ûÿÿÿìÑ¡q=jÂÿÿÿÿÿð£N V³ùÿÿÿûüþÿÿÿÿþþþÿþýûúûþÿÿÿüîßéƒb; DØúÿÿÿñت{F% 4‹Õÿÿÿÿÿï£N ?ìÿÿÿûûýÿÿÿÿÿÿþþýüüüþÿÿÿöìÜίa@ E‘Úúÿÿÿ÷â·ŠT0S´ñÿÿÿÿÿï£O8ŽàÿÿÿüûûýþÿÿÿÿþüûüþÿÿÿþõãÚÕѰ„R6 G“Ûúÿÿÿùç¿”_8 jÐÿÿÿÿÿÿï¤P!lÄÿÿÿýûûüþÿÿÿþýûûýÿÿÿüòâÒÔÜÞ¹…R;( G”ÝûÿÿÿüïË£nB„éÿÿÿÿÿÿï¤R F¢ðÿÿÿýûûýÿÿþýüüýÿÿÿûïáÕÑàíëÓfN4 G•ÞüÿÿÿþóÔ­xI1’ïÿÿÿþÿÿï¥RwÖÿÿÿÿûûüýýýýýýÿÿÿøíàרáñòã¼™tW3 H–ßýÿÿÿÿúÞº„O  CŸñÿÿÿÿÿÿï¥SUºøÿÿÿüûûûüüüþÿÿÿùíáÙÝèòõãË«•tS) I—áþÿÿÿÿþçÅŽU"Q§ïÿÿÿÿÿÿï¥TB¦íÿÿÿüüúúúüýÿÿÿùïäàäðúùîÒ¸š„bB J™âþÿÿÿÿÿìË“W# Z«ìÿÿÿÿÿÿî¥UM¨éÿÿÿüûùùúüþÿÿýóèãêõþùèÖº¢}_=' LšãþÿþÿÿÿïÏ—Z$ "b°ìÿÿÿÿÿÿî¦V6{ÃñÿÿÿüûúùúüÿÿÿùðéìùÿþêÏ»¡‡[9 L›äÿÿþÿÿÿïÏ™\% #d²îÿÿÿÿÿÿï§V!Ak¾åøÿÿþüüüûûüþÿþøòò÷ÿùëÏ­’oR. JœæÿÿþÿÿÿïЛ]% #e´ïÿÿÿÿÿÿï§W*jŸÌâôúöóôùýþþýûûýþýûùýÿüéÕ¹“k=# H›çÿÿþÿÿÿîЛ^& "d´ñÿÿÿÿÿÿï¨X:zÂòÿÿÿùéãéõþÿÿþûûüýýÿÿÿüîÒ½ŸrBGœéÿÿþÿÿÿíÏš^& "eµñÿÿÿÿÿÿï¨Y >ˆÆùÿÿÿüèÛÜèöÿÿÿþûûûýþÿÿÿòßÁ«ˆY,Gœêÿÿþÿÿÿë͘\% "dµñÿÿÿÿÿÿï©ZT˜Úþÿÿÿ÷娨ãñûÿÿÿÿüûûýÿÿþ÷â̪f;HžëÿÿþÿÿþèÉ•Z# "eµñÿÿÿÿÿÿïª[Y©áÿÿÿþîÙÌÎßòþÿÿÿÿÿþüûýþÿ÷ëдlG(H îÿÿþÿÿüæÆ’W! "eµñÿÿÿÿÿÿðª\"l¶óÿÿÿÿñÛÈÄÓìÿÿÿûûþÿÿýûýþþòãÅ¢pI*L¥ñÿÿþÿÿúâÁT "eµñÿÿÿÿÿÿð«^+wÇûÿÿÿÿðÚŽÈàøÿÿùíêï÷ýþüýýýðåÉ¥k=  R«õÿÿþÿÿ÷Ý»ˆP!d´ñÿÿÿÿÿÿñ¬_5{ÉÿÿÿÿýìÖ¹ÄÛôÿÿúçÔÎÙíýÿÿýüüòïßÁF  X°øÿÿþÿþöÙ¶„M c´ñÿÿÿÿÿÿñ®`AÐÿÿÿÿúçмµÁÙóÿÿÿëк¶Éæýÿÿýüü÷þûå¢XZ²úÿÿþÿþóÔ°}Ha³ñÿÿÿÿÿÿñ¯bO’Ùÿÿÿÿ÷ãÉ´¯½Ùôÿÿÿñ×¼­³Íìÿÿÿÿþþûÿÿù¶h%Y±ùÿÿþÿýñЫwC`²ñÿÿÿÿÿÿò°d $\§áÿÿÿÿöß쨹×óÿÿÿñÛÅ·¸ÈßñúÿÿÿÿÿÿÿÿþÄ}: W°øÿÿþÿüîË£o=_±ñÿÿÿÿÿÿð®c $i¬êÿÿÿÿòܾ¦¢´ÕôÿÿÿïÙÇÁÇØëóðìòÿÿÿÿÿÿÿûЖR W¯÷ÿÿþÿûìÇi8]±ñÿÿÿÿÿÿî¬`6wÀñÿÿÿÿñغ¡±ÔóÿÿÿíÖÅÁÎâ÷ÿòÝÍÔëÿÿÿÿÿÿþä´k)  W®öÿÿþÿúèÀ”`1 [°òÿÿÿÿÿÿê¤W9„ÅøÿÿÿüíÔ¶š¯ÔõÿÿÿêÒÀ¾ÍçýÿþåDZ²ËíÿÿÿÿÿÿíÃy4W­õÿÿþÿù廎Z,Y®òÿÿÿÿÿÿæœMP•Øÿÿÿÿùéѱ™–®Óõÿÿüèλ¸ÉäýÿþèÌ®‘ƒ·æÿÿÿÿÿòÍŒM) U¬ôÿÿþÿ÷á´…Q%W­òÿÿÿÿÿÿÞ@[¦áÿÿÿÿôäÊ­•”­Ô÷ÿÿûäË·´Åãûÿúçθœ{[Tq®æÿÿÿÿðÑžg@ TªóÿÿÿÿöÞ¯~J V¬òÿÿÿÿÿüÔƒ7Hx·ïÿÿÿÿó߯§‘«Ô÷ÿÿ÷ádz°ÁÞøÿõßʲ ]4'\£ãÿÿÿóØ³ƒX) R¨ñÿÿÿÿôÚ¨vAT«òÿÿÿÿÿôÆu.4lªÕûÿÿÿÿîÛÀ¢«Ô÷ÿÿõÜ®ª½ÛôûìÖ½¬”€]=P¡áÿÿùßÀ•j8 R§ðÿÿÿÿñÔ m:Rªòÿÿÿÿÿêµe& :„ÀòÿÿÿÿûíÖ»žŒ­×ùÿÿî×½«©ºÛôøéϺ¥–z`<$ [«îÿÿçÈžuD" Q¦ïÿÿÿÿîЛh6Qªòÿÿÿÿÿá¨Z J’ÛÿÿÿÿÿüéÒµ™Š‘°Úûÿÿíѹ©§º×ðôàÆ«‡uT8 d½öÿ÷Ù²\7 Q¥îÿÿÿÿêÉ’`0O©òÿÿÿÿÿÕ–J-~Ìÿÿÿÿÿùéϱ–‰”µÞþÿÿç̵©®ÁÝñòݾ¨•‰oV4 /ŒÞÿÿëÇ£sK" Q¤íÿÿÿþæÃŒZ, N¨ñÿÿÿÿüˉ><•àÿÿÿÿúçÍ®”Š—¹âÿÿþæÈ±©±Éãôï×·›ziJ/`ÃÿÿÿãÅ—n>!  P£ëÿÿÿûຂR& L¦ñÿÿÿÿ÷¾w. -ˆ×ÿÿÿöäʬ•ŽÀçÿÿúàᩏÑíúòÖ¶›‡|dL,P¶ÿÿÿõÞ¶ŒY5 O¢êÿÿÿùÛ³{M# J¥ñÿÿÿÿôµi#7”ßÿÿõÚÁ§•“¥ÇëÿÿúÞ­«º×ñýðÔ°—ƒo]@'H­üÿÿÿöس€W- N éÿÿÿõÓ¨qE I¥ñÿÿÿÿí§Xh¿óÿîÒ²’—®ÐòÿÿõÚ¾­­ÁÝ÷þñÓ´–†rY@$L®ûÿÿÿÿíРtD%  N èÿÿÿòÌ i@ H¤ñÿÿÿÿèžM k¼õþëĦŒšµØ÷ÿÿõÙ¿®±Äâøþíѯ—nS5 D¢ñÿÿÿÿþîÆ›hA LŸèÿÿÿíÄ–`8E£ñÿÿÿÿÝAkÂøÿêÛ††š¼àýÿÿðÕ¼°·Ìçûüìγ—…kT5 8ãÿÿÿÿÿýὈ]2 Kžèÿÿÿê¾Y3D£ñÿÿÿùÒƒ9#pÇÿÿ€ƒœÁæÿÿÿðÔ½´¼Óìûûçͯš€kJ0 jÅûÿÿüÿÿùᲄR/ Içÿÿÿ渆P,B¢ñÿÿÿðÂs0'yÊÿÿîׂÅëÿÿÿíÓ½·ÂØñüöäǯ“€bH(>œèÿÿøûÿÿúدzO& Hšåÿÿþà¯}G% @ ñÿÿÿå±b&+xÍþÿì½—€‚ŸÉïÿÿþèϾ¼ÊâõÿöàÉ®›gE*tÏÿÿýùÿÿÿðОp@G–áÿÿüÜ©vA <žñÿÿÿÞ¤V0ƒÐÿÿêÁ˜€‚ŸÉðÿÿýä̽½ÏæúþôÝÁ¬’~]B"CžêÿÿüúÿÿÿïÈ›g:+K’Ûÿÿ÷Òj74˜ðÿÿÿÒ’E5‚Óýÿຘƒ… ËñÿÿõÝÆ»ÁÕîþÿóÝÄ«™}c?%jÄÿÿÿþÿÿÿÿ⼉W>S‘ÕÿÿôË’`0,’îÿÿýÊ…8:Öÿÿ澞‰Š¢ËòÿÿïÖÁ¹ÁØðÿÿñØÂªzX< 0åÿÿþýÿÿÿùÞ±~\c”Ñþÿë»Q%"Šëÿÿø½r'>ŒÚÿþâÈ®œ–©ÎòÿýèÌ»·ÅÝöÿÿðÚÁ¯–y\9  [»úÿÿøöÿÿÿòÊ›vr˜Îûÿä¯sE „éÿÿô³dD–ÛÿüäÍÈÁ¹ºÑòÿüâȶ¶ÆàøÿÿîÔ¿§”uS5 $u¾êãÖÑçÿÿÿæ¾—‡ Íø÷Ô™^5~äÿÿæ Q I•àþùÖÁÂÕáâæóÿøÞÁ³¶ËåýÿÿìÖ¼«‘yU36u¨ª Âíÿÿ÷Õ¯—¨ÑøðăL*|åÿÿÛE NŸâÿøÖ²±ÈèüÿÿÿößÄ´¹Îéþÿþëм¢ŽnQ/+QXVY„½ôÿÿðɬµÖöá¨c3wßÿÿÄw3R£èÿÿÖ¯›¬Òöÿÿÿøßƹ¿ÓíÿÿûäͲ „kG-AzÆûÿÿÞ¿ÁÙîÍŠEmÑÿé£X X£çÿ÷׬•˜·ãÿÿÿõÞÉÁÉÞóÿÿöãÈ´™…eI(8‹ÚÿÿóÒÉÔܲo- Z°Ù¿|:]­êÿüÔ¯•’¨ÎôÿÿïÚÉÅÑåùÿüî×Á¥‘u]<%C™çÿÿë×ÍÔY"7oŠwGb­ìÿòЫ—–¨ÉëÿÿëÔÅÉÚñÿÿ÷çѸ¤ˆrR:V´üÿÿæË°S% .=5 &f¶ïÿñͬšœ¯ÌëÿüéÎÀÅÛóÿþïØÄ¬“~`G*pÍÿÿø×°ƒ^5"kµïýíŧœ¥»ØñÿÿëÓÄÌãüÿÿéѸ§u]>(;–åÿÿêÛxM&+oÀôÿëÉ©¢­ÆáùÿþëÐÀÈáùÿôÝÁ¬“‚hL3 W²ûÿýÛ¹•h<%pºóþíÆ­§¸Óíÿÿÿëϼ¿Øð÷é͵wcF, ,†âÿÿíÍ«Q, ,rÂôÿíÓº·ÄÜõÿÿûé̯¥°Ç;©”ˆwjP:  U·ùÿõÔ´c;,u½õÿòÓÅÆ×ëüÿÿöåͳ–‘˜‹sebeZJ1 .Šàÿÿݵ”kD1yÄõÿúçÕ×åöÿÿÿòÛÈ®–vdWN=-.6>5&R±ùÿÚ­ŒiH -tÁöÿÿïååïþÿÿþêÕº¦ŠsQ9! "pºÑº•y_D >ŒÐñóõðôûÿÿÿþëѺ‡hP1 1dqbP:1p¯×è÷ûÿÿÿÿóãÊ«“t\=('9AB:) ;ƒÅìÿÿÿÿøë×ĨˆnO8'$  @×ûÿÿøìÚǬ•wX@% <„Íúÿþïßи¢„kM3!A˜ÞÿÿñÝι§ŠqR;#B™îÿÿõÖ¾±š„cJ. 9ãÿÿëǰ›ŽrY8#i¾ñøÚ¹Ÿ“oP7ž¦¬§™ˆzq]F*pfYQTXVL7",#*68- engine/resource/hexen2.ico000066400000000000000000000013761444734033100160510ustar00rootroot00000000000000 è( @ÿ€ !" "  " "  " "   !"" "  " """!" """ "" """ """"""""" "" """ !"" ""!" " "ÿÿÿ?ÿÿÿÿþÿ—ÿþÿÊÿýÿå?ýÿòŸóÿýoãÿþ“çÿþmïÿÿ0ÏÿÿŠÿÿåÿÿòÿÿøÿÿþÿÿþÿÿüÿÿüÃÿÿøáÿÿõðÿÿåü?ÿëþÿÓÿÿóÿÏÿçÿçÿÃÿûþ ÿýÿ‡ÿÿÿÏÿÿÿ?ÿÿÿÿÿÿengine/resource/hexen2.png000066400000000000000000000006511444734033100160560ustar00rootroot00000000000000‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  ÒÝ~ütIMEÕ *$ˆØ 6IDATxœÍVÛà Kwöß;œ=LÎ,µ è.yêj!!8øwòk Ù<Yb:ã<¸ˆ ÈnÈ­  4‡»KÊ -?»ˆ˜ø{$Ál%0Xß–âº{ †*Øß^‘·ªB{çí¡%f#Æí𪞅Ü&ÏÁ=À…ÄŠÔ_©&© ¢ä@°½Är Ø‚ 1SS1å@!ŽÎµdnq©°c{„<%€åtÔô˜  PÂz²ïÆåo»ÖË)¦dz.°¼ÓõAŽ&†°²\Éu$—uµ]Å,»-Îo9NÀSÅ=xñ~è@Ýçú™JÕ™ÖZ4ý®ßõbˆÉÛ3;›Í?um›%·¹–G‰SZä+àq8Œ˜½¯Ï€Ž)öIÜ€ó]ý›xÒæ¤tH¦ñƒIEND®B`‚engine/resource/hexen2.xbm000066400000000000000000000016551444734033100160650ustar00rootroot00000000000000/* uHexen2 xbm format icon - Hexen II */ #define hx2icon_width 32 #define hx2icon_height 32 static const unsigned char hx2icon_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x80, 0x2d, 0x00, 0x40, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, 0x20, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x00, 0x80, 0x07, 0x00, 0x58, 0xc0, 0x03, 0x00, 0x50, 0xf0, 0x00, 0x00, 0xe0, 0x78, 0x00, 0x00, 0xc0, 0x3c, 0x00, 0x00, 0xc0, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0x10, 0x07, 0x00, 0x00, 0x10, 0x07, 0x00, 0x00, 0x8a, 0x03, 0x00, 0x00, 0xe1, 0x0c, 0x00, 0x80, 0x40, 0x00, 0x00, 0x80, 0x36, 0x18, 0x00, 0x40, 0x08, 0x38, 0x00, 0x30, 0x06, 0x20, 0x00, 0x18, 0x03, 0x40, 0x00, 0x8c, 0x00, 0x40, 0x00, 0x06, 0x00, 0x80, 0x00, 0x0e, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; engine/resource/hexen2n.png000066400000000000000000000116671444734033100162450ustar00rootroot00000000000000‰PNG  IHDRFFq.â„ pHYsII¹(ïtIMEÝ  ÷gŽuVIDATxÚåÜy¸•e¹?ðÏ»ÖÚ[A@T˜p qÊì(–câ©Y’zÙéduÄóXÚÉ~G‡òX©”Ú`hVZšÃOqHq@Q”Idܰ7ÃÞ{ Ïùãy.·X‚°«u]ïu±7k½ë}¾Ï}ïûþÞ÷³³‚öxeY–!Ÿ~¬„*þ_Y{3,˾VbÕMà¬@#Ê¡½ß̯B{ܤž–¹¶¯>Æí˜×ÑeYeTþQ€j`žæ–:öûO¾ÔD§ù¼²7=ßàÑÙ<–¬gVfYÖúa'Qi÷²v䘺Orß÷8r!ŃÙúŸXô'îÀs˜„hþ0“eY.¾z4£(„Ð.2tý / c ;1w!áDšrü_ÀتÊo[úêÅîûð*ÅØù\{ºå ®øßżaèŪ3yón¶9Œãq$†b»š¶Å\gÿ,ûÄÙüåÓtÄ‚®ÈÚËR¶:šû¦Qþ8/uç¶ëYùCJýxdMã©ôd®Çaè¼¥¬ÙAœýcÂ)¼_ãbôGáƒRןϞÊS ©Âk”÷bN/ž}‚–áÌÀÓ¯Žc5þ‚ó°+ò[ ”ÛY×ÑÔ‰gq'>ßè›Ì7¾Áĉ´Bíu7å]˜9”9ã »ñìWh>’U1ˆùvGasƒr0gŒfQ *ºU¶Qáº#¶ed+u7³¸ÄVBF~Wê¡îvú¾Hq~ÊõV„jN³¹9å@N9‡ëÎa‡Åø*Ëæð&&â)ÑÕ[Ba£€™ÃçððƒÑúcûj¤A]^ßbä#É:*}®%þ¨Iø6(ƒ9i$?>çP9哘šRˆÇSBººZÊl0!„eY3æayŠÿÕ—•È¥ërÎí^ó¹m"0¹ô½¹ÍJ~87~)ra·ç)žÇ’Ɉ—Ä2f"V„ʵ‹ü œK¡·íÕù«¼RË=§³Ïãr콩9ùጙE)~Åš~Ñ]ÂqvHõŽùv-ÄW%„Pn{aÕÓ\3.f’ÄD¡.£º¥pß„–R÷IÆŒæÔ¾ä¿Çª¯³`V̼§à LNÿ®b7·‰*‡º—øÝÕÁŠÏ³ªB˜E©j)3¨ Œ.úBå”XÚm¸0¶9*ÚÜ@N¹ƒ¥w±ªã¶fôÿ$¢Åš|,ྒྷBø;ˆ¹#¸~,„Xu Íð­Õ¬v&•”—ðG\˜„§.ËRjJ”l³SMÍä¢GXu7«{óü&B‘pDt©ßcdÚáºä:Žãþ™”E®Zû ¥F*ÕȳûEPžKuÏR\…“îS·>‹I–ô | 6;0ÕHõqn˜Hëý¬Jc•* ‰©ùÍ8;ìÊžŸãÅFÂtJ£y:•Y”«®³ É}Æ%—›*ø§R:pðú\4¾ßM0ê†qÕ I=«SÞ9†ökzóí‹™U&Gá.O`äì¢Ò»N%E¤IséTS4Ÿ6j­;×ËÊï,¨³M*;¼O€J ™b:šõ ›À©2ãl*S)÷'ß—ü­”G²l:/÷™ò$±r_ëíÏ:æÑUHQ§z& Y–ÕãøOuQŸ#>N[mh‹Ó¹åÁ˜¥êK~Ö»uà04ɯÿAéb62û|ö> VåÝ’‘u®YK¾.“Ü5ºlª‹v¨ÆöëS¯ûà­#•›ÅÀLž½7ÖQ’˜”›M¹zõM»8’âuÌíÊšëÙw?ꟈ¤»‹ª—Ötòu‡w–ØQHÜÒ-i/9ôÛ±Îêêw–ß¶˜Ü‡ÆbÄy‘iÕoGna á¡ Êh*cX4.÷±{#å+y~qœÁ™ ¶?*5›Ÿ¯Oû~P'Ð!E¡ª`¿s÷¼–êw¦…¶SØ`¤‘‹öæøžQS)ïD®Ö¥n¢e:þ‘î7Ðx3Ï.ÑæñÔ(kN¡9ߘ†òºEjþ¿nÛüÛ|R‰“µ5’–åPÎ?…ÿÁÛâ_YÞ‹­/¡Cu˪®t8u‡ÒõbVa\s´k1?møÚB%˲H¸PŸÅ_\\LVk soƒð¡à˜,Ërç¿.âÊ Ù¡Ò¿0ÿO,nŠÂô»^×R¸—ò¯™ÚÌB±ÏüV²×$P²š^U¾.­jQI–Þ÷Ž_¨ ò´RØŒ t8’;¯æØý©KñË,œÇÒ_1`õ‹’ÞØ—üxJÛþ‹ò”"(ãSîRN Î(ÕE•QWH³$B]L®V¬t1¼Ý®&¿Ù-fû,Ûùž¼“ûSÿZÎaÁT¦JÔ¿FùP–Ý Ù2*¯Rj‘g^â•*§”.Ý»J¡˜ˆ¶C}Æ[E–—ªaÍÕ÷KnL¶Y£R–eÙ¾Y6øLþÿo9`;\Äê¯1{6“£ßoèµ&Î…,É+W3¥!E¨¥Q·­ëÅNŸäÓ—²Ç]¬ !´\NpY•Ë¢;åSH®Ï‘Íi­ænšÅ\©5áP*VÞ¶ŒrX¿Å6%(Ãq*דÞ8ƒÆ‡™Vbæì{tŽ¿_ñ Ó:Ò0¾¹” ¥04Vw=þBë:½€)Y–»œpÙ;e•ª»”ËŠëÜ¥E¬«š“Å”ZÃÛ,*nFŽÉ²,û_ÿ—žÎvoP9ƒ†ç£b?í“r7ýó8‰åðúntú û f›aL½œ>gD×Ð;ꔫçÄqµ×Òh¹:hYÓIÌ¡Rªo•ÖÕœ¦F›ÓÏå*0¯5³ ¸¼bíhJaSDž£¸ö»œwÆR¼€%Sâ‚^?Ž#ưOclò/~’){±í­|d; g2u“¾Ç‚Þr([uâIÇ9q°1Ÿšto¥©‹¬f-•b×Íá¬IÀTë©ÖbfòÚuÝœj®b»sL_;ÏC7qÁatº•µ#™;%{“N䈻Ùg¥ãxóI&îNÝÙg+rg2ubœ |cW~›1/ÆÝtP¤.õ†z¢.˲|›¾s•Ö°.T—0+0”ZÇLi^—ç,ÁòÚ1\{YÉa\|)ÏüŽá{ÿ«¿Åsy9ǤÏpìöyŠæÏðÆ&ïFîÔDù ŒŸåƒ×ÒÌÊ„§ù× øÙc¬ù u=bj¿OªêÛLe­J*!…ê²8÷×”Ü% Xªq¥­æ¨™ÊhWʲ¬n8wÞĈ=É·âLš~Ïô^ïļÏsÚìöKš.cæ\f÷&÷†Í åzÆÍcQ,ü5³ågùæ%t{œÏ¦ËÃQ>Ø#½wíz&³*ŰŽT«Ä»º&\—‹)ú¼¡h­Ž˜µ+0‡rÕ/9©'¹FœLã£qZiòŽ4]Àç.e—ÿfÅÕL]§8{|—ÁÏÐt;¬ˆ0-eµÓÅ\NÀ'òÃ{9ágô<šS#ˆ³Ð˜eÙÊwLBQ.!¹R•x×Ôè5Å– k,35B±Vgmi×Lj¥ÿ±×3÷1šö!ö§3§LXB8šåïÁs·§–ÓX\…ê[p.¤Q²\;µx;¤ñŽƒ¸îµ8AÑÜ#rÞà œ Ò¼ÍÖmæmêhó“kwÛ Á!ñ\Ïi·2ÿ~÷޾ûó]¹çZ–ÚëC˜ö,-OÒr3qO¬OO¦¼u{€Ò¶ÿÙá-Ή!ûÑ”•”Òüz>{%nLuïd‡0òNÝÍŠ=x¿èÅï¯fñb*‹aî‘á,X%ƆÞñ,Òí)ò•vµƒMx’ YGvýo4†Äçº5™õ\$é¢ë{²^`ÆWïcé4ô‹dvû~Œ»Õó(Äô<|Ž¥Ó©œÀÜ¿Ãc7ÄÐÔý+Ø Çû á¼§Y}kºÆòâûé9¶Þ¨â¶_p8>BÃݬìÃyþ0œ¹3)?AËàX Þý1¦ÝÌÊA1êܘdö¬Nlæñ’ü™ü5DÞ›-žq<+YÍ?K¡9êR¾[¦pqŒ2ý.fçËér=M?bÚ›1²LYÂÊKxli›™¢@qsŸ«!”÷β=ÏÐB ßY›ñʆÞPÁ‘ŒËŠq¬éÍÜÓX9JáL–wŒ–Qä…|Û¦aÂ^5Ó[ì<5òGó癋+Òüð6â\Ôº“úÇdÙÝ»²Óƒìó:OÝhŠÿËÒ ±y=ÍÏNL…ךšfxx¯ã-[ '^—ÒƒÉJ^Åâõæ*ë^ÕµdYÖã8þp‡¬ŠijÓ“L_›D§ÔœžªØ–ë¯HàÔW󦪬¹¡Ï›…ì’e½çøÈU,Í‚7c4-e­SRB´Æ?ÀŸ>IàdµÓm|t>‹Goä€ó™{/[¢Î1123¥ÐÅô?–³!¯Âá\?‚ŸæÅ'cż&)j—¬ý0ðÇf·ºA|}3–ÆD¨k²iIakùg¤Ö•²ë»§¯L’`ùŸ”uäÛfÆ$ü3qÉ{½þ‰Þbüí²YIEND®B`‚engine/resource/hexen2n.xcf000066400000000000000000000166731444734033100162430ustar00rootroot00000000000000gimp xcf fileFFB¹ÆŽB¹ÆŽgimp-image-grid(style solid) (fgcolor (color-rgba 0.000000 0.000000 0.000000 1.000000)) (bgcolor (color-rgba 1.000000 1.000000 1.000000 1.000000)) (xspacing 10.000000) (yspacing 10.000000) (spacing-unit inches) (xoffset 0.000000) (yoffset 0.000000) (offset-unit inches) ƒFFhx2.pngÿ     /FFG¯FFc à§ÿ:ÿý518ÿü;„B4ÿø@Â5B 4ÿøFÄ«äšM3ÿøUû¿çO2ÿö0mõ6Aú?ÿý0ÿÿõHÚ>…!rÌÿü8“ÿ ÿó&Tçÿ×'ÁD,ØKÿü8¡ÿ ÿû9tÐÿÿø«4Ì"kCÿú9ÿÂÿ ÿðHnjfef‚¼¼‰ÿºÀ6ÿù7›ÿé/ÿ ÿýzÿÿþ/ÿø2—ÿà8ÿ ÿý9íÿÿþ;ÿ÷D«ÿÉ-ÿ ÿþmÿÿþXÿùDßÿ¤!ÿø2ÆÿÿÿùFáÿ}ÿúQýÿø/ÿ÷Oçÿÿž[,ÿõ>% ”ÿÿƒÿûd÷ÿÿýí.ÿõ ž´9ëÿø2ÿö.ÿÑåÿþ«4ÿô2´fiÿÿ• ÿ"÷¢ÿÄÌøs1 ÿõ<ÕD­ÿþA ÿõ8&ÃÿËèùQÿöIÜ34éÿ¹ÿô&ab'áÿËÿòCÿ÷aÇ#JúÿSÿõD¯W9ôòËèæ7ÿõ"ˆ©cÿÜÿó6sï@]þßËÿÜ.ÿõ.¢ª ÿrÿóB¿ò5Œÿ¾ßÿÝ,ÿö7»«3ÿûBôKÝé3¸ò¾ÿÿß-ÿþÿÿö>е4ïïç+ó[òÛ<ÛßËÿÿà.ÿÿê6‘ÿïïÿºxýÊRòÜÑÿÿâ/ÿÿìqÿÎÎÿ–ÿºtý˾åÿã0þ ûÿ\ÿÿî&¡ÿËòÿßÿµÿȾÿÿÛ/þLûÿåÿÿúaÿòËÿÿ÷ùÿòÙßÿ¼%ýüÿ†ÿÿúHþÿßßÿÿ÷ßËÿÿ› ý˜nþÍÿÿú#¶ÿÿ¾ÿÿ÷ßÿÿzfUýÀþÿÿûgÿÿßßÿù_ #ž“ü¢ÿ.þ6ÿÿðCÿÿòÆÎÿÿœ7Õü¨ÿfþHÿÿñ([ÿÿËß¾ÿÿ2#çxü®ÿUþWÿÿï;ŽÞíg„þÿßßÿq¬_þÿü´ÿ?þdÿÿï>®ÿÍ.wþÿßßÿÿ]þÿü¹ÿ(þcÿÿïCÎÿ¾' ¯ÿèÿÿËòy ü½ÿþ]ÿÿïSêÿ¥ FÿÖ#ŸÿßßN€ùÿÃçþXÿÿç)nûýr=ìõ:*îòËŸF|ªÿ€ÿÿýÅÈþSÿÿï6ŠÿñJBïüU Xðÿÿùш~u€ÿÿýÁ²þMÿÿé@§ÿÝ6FñôM"¿&¦ÿÿïþÿý½šüGÿÖÿÿèEÂÿ¾*Kôæ>'ÃÖ0$ÄÿÿUüÿÿý¸„üBÿ©ÿÿïLÛÿ›OöÓ1#ºÊ0û;Ìÿÿý²mü;ÿÿÿí=|øÿxUø¹("°­(û8½âÿý¯Xü7ÿVÿÿíDÚÿùY hüŸ¥Œü7÷FÿýªAü2ÿ.ÿÿî;çÿñCƒÿ‰,©oÿú¨ß'þÿý¥*ü+ñÿÿï+Œï?% þr :ÈSÿú€ÿ¡ý ü'Àÿ ÿðMÞ?.¹û^OÕ?ÿùuÿÿfý{ü!–ÿ ÿðRÚ>8ÓôNmÔ4ÿúGþžúEþGúÿmÿ ÿð ZÛ:BæêA •Ä-ÿù"Öžoê1þúÿCÿ ÿðcÛ7IóÓ6/¾°'ÿø`ý¸Æ!úÿÿ ÿðlÕ3Jô±)?ßš ÿ÷(¬Ëë™úÿÿÿð-wÏ0 IõŒWõ„ ÿøCïKIÿfÿÿï1„È-wAñf yýo ÿø?' ‹õ7ÿÿï4¾*XýëH$¡òVÿû0×µÿÿð8œµ(Ëî?3ÈÂ5ÿüMûbÿÿð:§«$‹ê<Dæ€ÿú ~ò3ÿÿð=²¢!-²ä8[ÛMÿú3Å­ÿñ?»«DÝÁ2<š0ÿûZÿHÿñ@¼¹#%{û‹'ÿü'ÉLÿöC¿ÇVäóZ ÿüH7ÿøHd³ÿœ1ÿÿùKáÕG %ÿÿÿ:ÿ8ÿ 4ÿ 4ÿ 3ÿ 2ÿ ÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿ ÿÿ ÿÿÿÿ ÿÿ ÿÿ ÿÿ!ÿþÿÿ ÿÿ ÿÿþûÿÿÿþûÿ,ÿÿýûÿ2ÿÿýý(2ÿÿý%ü22ÿÿü 2 ü 22ÿÿü!2ü22ÿÿü"2ü22ÿÿþÿü#2 ü22ÿÿþÿü%2ü22ÿÿü&2ü22ÿÿúÿ'-ü22ÿÿúÿÿÿ'ü22ÿÿÿý'"ü22ÿÿþÿý%ü2*ÿÿüÿÿý$ü 2!ÿÿÿý#ü 2ÿÿÿý"ü 2ÿÿÿý! ü 2 ÿÿÿþÿý ü /ÿÿÿ ýü%ÿ ÿÿ þüÿ ÿÿ þ úÿÿ ÿÿ þúÿ ÿ ÿÿúÿÿ ÿ ÿúÿÿÿ ÿÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ %ÿÿÿ:ÿ8ÿ 4ÿ 4ÿ 3ÿ 2ÿ ÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿ ÿ ÿ ÿ ÿ ÿ ÿ ÿÿ ÿÿ ÿÿÿÿ ÿÿ ÿÿ ÿÿ!ÿþÿÿ ÿÿ ÿÿ þÿÿþÿÿþÿÿ ÿ ÿ ÿ ÿ"ÿþÿ ÿþÿ ÿ%ÿþÿ ÿúÿÿÿ ÿÿ ÿþÿ ÿüÿÿ ÿÿ ÿÿ ÿÿ ÿÿþÿ ÿÿÿÿ ÿÿþÿ ÿÿþÿ ÿÿþÿ ÿ ÿþÿ ÿ ÿÿÿÿ ÿÿÿ ÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ %ÿÿû9ønb"7ôgðÅfB 3ó”ûФ¼y7)3ó·úõÿþç‰R# 2òÎÿÿâÿó¦^$1ò”DãÿÖâÿò¡Xý@ï²ýΠñü»óþß= ü_æ íWÃþÿýáÈùäÅÿõ·dû_ëÿ ûtÐúÿÿôýÝáÿÈøôÅv#úYéÿú ôQÔÙäëïòøýýøÿÿúòÄt"ùTèÿÿç õ 'MbijoŒìÿÿúóÃq øRçÿýëÀ ö  œÿÿúôÅu"÷—îÿú彋 ýý Ôÿÿú÷Ê€(ö©üÿöß¹…Bõ`÷ÿÿüÕ‘5õ¬ýÿóÜÅFû ½ÿÿúð«Rô¿ýÿÿü÷ëˆAò ¼h5IéÿÿûË€-ü*Òÿÿûì¥^ù*ðó¢pªÿÿúï©S ðTßÿûýÿÿýòÁ‹Cðlñè pÜÿÿûσ1  íÿúûÿúïËwBñ–üà‘òÿÿó¯\ìƒçáùÿûýÿøÖ©rB&ð½þÔŠ»þÿýÚŽ: íJËéêþÿûÿÿ÷Ïž_* ñÖùlj×ÿÿô¶j íªïëðÿþûýÿöÊ—V" ñ5ãóÄâÿþá˜DícÛÿðôÿüûÿþóË—SÝSìôÇíÿöÃy. òÿôøÿúüÿþóΰq3ésôõÊÓÿÿô®` µüþ÷üþúÿÿöóÉÐì‰Oþë‘ú÷òþþÿîšM%Íÿüøþýûÿÿõóǘ¿ÿÃ9ý}ì‰ëÿþþÿýàŒwßÿüúÿýûÿÿïôÇ’\¹ÿë²a 6ùÚEàÿûûÿûåôÿýüÿûúýÿÿôÇ‘M3ºÿúÒˆ5  ÐúøDîÿûþÿÿþçÿûúÿÿþôÆ’J&¿úÿæ¦KIüþúºÿþûÿÿçþüüÿýñÄM %ÂøýôºaŸöÿúŸÿÿüüÿÿæýûÿÿüêÃÐa7%ÆøüûËy+ëüÿú:õÿÿúÿÿæüÿÿûãÊëårW %ÉùýÿÛ8 3ýÿÿûÉÿÿüüÿèúÝÙôñ¬J %Ìùÿþí B TòÿÿÞ£ÿÿþúûÿÿýãëüñ»œ\-%Ïùÿþõ©HnëÿÿÜfÔÿÿûüúÿÿöìþð¸ž\ %Òùÿþô­KsîÿÿÛgéÿÿí÷ÿÿüüÿûüó·š( Öúÿþó®LsðÿÿÚˆïÿûä×óÿÿüüÿÿùÌžb Ùúÿÿï«Jsñÿÿ÷¯ùÿúáÍùÿÿåûþûß³j, Þûÿÿê¥EsðÿÿØÊÿÿøàºáÿüìúÿüü÷Ò—*$#æûÿÿãA sðÿÿÖ2ÖÿÿôسÛþÿèÀÐÿþûüëåN-ìüþÿÛ“; oñÿÿðQáÿÿñ˨Ùÿÿï¯àÿÿúùYñ(ëûýþÔ‡3 kòÿÿípìÿýìœÖÿÿíÅÍïûØ÷ÿÿéî¥0*éúýüÌy+hóÿÿÓôÿúå¼”Öÿþè¾ÍøüÕ¦¾øÿÿ÷É3)çùüúÂk"bôÿÿÑ»üÿöܶÖÿûá¸ÃöúÕª„F¡øÿúÎu2&åùý÷¸\^õÿÿÐyÜÿÿôÔ®‡×ÿ÷Ù´ºóóÆ¥A ‘õÿמD %âøýô­OYöÿÿϬüÿÿòÍ¥‰Ýÿòϯ±ð쳜x<¢ÿå¬h %àøýí¡DV÷ÿÿÏŽýÿÿñÉœäÿïȬÀòå ’l4'ïýÚ•G%Ý÷þå“: QøÿÿêMìÿìÆ™ëÿíéÒûß–Ša- èÜÿøÏ‚6 %Ú÷ÿÙ„1 Mùýÿ ê ¿þÚ°“°òÿ뽬àýÙ—…V% èÓÿÿ÷»n)%×ùÿÊt(Iûûþ êÃýÏ™ŠÀùÿ躷éûÒ›ƒKé¬ÿûÿó¦[ %Õúü¾eCýûü êËüÍ–ˆÌýþæ¹ÅñøÌ EéRüöøÿé–K'Òþø³W@þü÷ êÑû˘ˆÎÿüà¸ÔøôÆ¢}?éÉÿ÷üýÛ‰D0Çÿð£H4ÿþï ë)ÖùȉÎÿõÕ¶ßýñ¢x; éGïûøÿúÊM»ÿãŒ7 ÿÿåë3ÛøÆ½™ÕÿïÅ·çÿï¿¢r6 ê¤ÿåïÿô²z´ÿÍn&ûýÔê?ßõÁÌöâÿë·¼íÿ컟k1  ë ˜•ðÿ䤳ÿ§N íö­êJâó¾ŸáÿÿîºÉòÿê·›c, ë,úøËºÿl. ÚåxêVåò»ž­ùÿèÁÚùùÝ­ŽX& ó½ÿí½ÑEûˆ”+ê`é𸟮êþÞ»æÿïÅ¡}EöÞÿÞœSüêmìï´¡ÇõýÛ¶ìþ⬓h4ödõöÁ4 ëqïñ¶¯âýùØ«Úñ¿”P% ÷Äÿà¥\ësðôÀ×ñÿǫ̀~œZ[a9øVøÔ¦r#ìrðöâñþÿn6 ø´â„j$î«Äíúÿúæ¶ŽY) ø2FDóÃýýðË£v@$ùÿÿý)ý|wýÿSý þþÿÿÿÿÿþÿÿþÿÿþÿ!ÿÿÿÿþÿþKÿþIÿþFÿþEÿþCÿþ@ÿþ>ÿþ<ÿþ:ÿþ8ÿþ6ÿþ4ÿþ1ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿ-ÿÿÿ'þÿÿÿÿÿþÿÿþÿÿþÿ!ÿÿÿÿþÿþÿþÿþÿþ ÿþ ÿþ ÿþ ÿþ ÿþ ÿþ ÿþ ÿþ ÿþ ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿ-ÿÿÿ'þÿÿÿÿÿþÿÿþÿÿþÿ!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿÿþÿ-ÿÍ#Õ;7ÚÞpj'ÿÞ¥x&óȧ]Û·y/ ¾‡<GHüýþþþ!üüŸnüÿÇ–ÿæ+þì2ÿë5ÿë7ÿê:ÿê<ÿé>ÿè@ÿéBÿéDÿéFÿéIÿêKÿêN ÿêP ÿãKÿÛ5þÆ#üú§üô†üëd üàDüÒ)ý¶ý”ýrýQ ý0þþþ-ùÿf÷€'ÿÿú\ðV4ÿü?¬?5ÿý07ÿ9ÿ:ÿþÿ 'ÿÿ 4ÿ 5ÿ7ÿ9ÿ:ÿþÿ 'ÿÿ 4ÿ 5ÿ7ÿ9ÿ:ÿòÔÿíÓ­‡S)&ûô Êÿᱜl83õöÌ”ŠY( 4÷oKUeB6ø 8û9#ÿ#ÿ#ÿ###engine/resource/hexenworld.bmp000066400000000000000000001177261444734033100170520ustar00rootroot00000000000000BMÖŸ–(ôP@œÄÄØØ3f™Ìÿ3333f3™3Ì3ÿff3fff™fÌfÿ™™3™f™™™Ì™ÿÌÌ3ÌfÌ™ÌÌÌÿÿÿ3ÿfÿ™ÿÌÿÿ3333f3™3Ì3ÿ3333333f33™33Ì33ÿ3f3f33ff3f™3fÌ3fÿ3™3™33™f3™™3™Ì3™ÿ3Ì3Ì33Ìf3Ì™3ÌÌ3Ìÿ3ÿ3ÿ33ÿf3ÿ™3ÿÌ3ÿÿff3fff™fÌfÿf3f33f3ff3™f3Ìf3ÿffff3fffff™ffÌffÿf™f™3f™ff™™f™Ìf™ÿfÌfÌ3fÌffÌ™fÌÌfÌÿfÿfÿ3fÿffÿ™fÿÌfÿÿ™™3™f™™™Ì™ÿ™3™33™3f™3™™3Ì™3ÿ™f™f3™ff™f™™fÌ™fÿ™™™™3™™f™™™™™Ì™™ÿ™Ì™Ì3™Ìf™Ì™™ÌÌ™Ìÿ™ÿ™ÿ3™ÿf™ÿ™™ÿÌ™ÿÿÌÌ3ÌfÌ™ÌÌÌÿÌ3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌfÌf3ÌffÌf™ÌfÌÌfÿ̙̙3Ì™fÌ™™Ì™ÌÌ™ÿÌÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÿÌÿ3ÌÿfÌÿ™ÌÿÌÌÿÿÿÿ3ÿfÿ™ÿÌÿÿÿ3ÿ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿfÿf3ÿffÿf™ÿfÌÿfÿÿ™ÿ™3ÿ™fÿ™™ÿ™Ìÿ™ÿÿÌÿÌ3ÿÌfÿÌ™ÿÌÌÿÌÿÿÿÿÿ3ÿÿfÿÿ™ÿÿÌÿÿÿ+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++%+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++VV++++++++++++++++++++++++++++++++++¬V¬+¬V¬V+××+¬VV+¬VVV+V¬+++¬¬VV+¬V¬VV++¬VV¬+V¬+¬¬+V¬V+VVV¬+׬+V¬+¬VV¬+VVV¬V¬¬¬+V¬V2V,¬+¬,+++¬+¬++VV¬++VV¬+++¬++++++++++++++++++¬+¬¬+V¬+¬¬+¬¬¬++¬+++++++VV¬¬+V+¬¬+VV+++¬V¬+++++V×+V+¬+¬¬+++VV++¬V¬++¬V+¬¬¬¬+++¬+++¬+¬¬¬+¬+++¬+¬+¬,++¬¬++¬++¬+¬V+V+++++V+++++++++++++++++++++++V++¬+¬+¬¬¬++¬+¬VV+¬V++++++V++¬¬+¬¬¬+¬V+¬¬++VV+¬++¬+V+VVV¬++V¬++++¬++VV+V++V¬¬++¬++¬+¬V+V++¬++¬V¬¬¬+V¬¬¬+V+¬VV¬¬¬,¬++¬¬,+V+¬V++V¬++V++++++++++++++++++++++¬¬+¬¬¬V+VV¬¬V¬VV+¬V++V++++++V¬¬++¬+¬+++++++++¬++V+++¬+VV+׬+¬¬+++++¬+¬+¬+¬V++++¬+¬++¬¬¬¬¬++++¬+++V¬++¬+¬+++V++V¬¬¬++++¬+++¬+++¬¬+VV++++V+++++++++++++++++++++++++¬+¬¬+V¬V¬V+¬¬¬++¬+++++¬V¬V¬¬V+¬++¬V¬¬VV++¬V+¬++VV+¬++¬++¬VVV¬+¬V++V+V¬VVVV¬V¬V¬++++++++++V+VV¬¬¬+V¬+¬V+V+¬¬,,‚+¬+¬¬VV+¬+V+V¬+++++++++++++++++++++¬V¬+¬V¬¬¬V¬V¬+VV+V++++++++V+V¬+V¬+++++++++++++×V++¬V++++++VV++++++++++++++++++++++++++++++VV++++++++++++++++++++++++++++++¬V%++++++++++++++++++++++++++++++++++++++$++++++++++++++++++++++V++¬V+++++V+++++++++++++++++++++++++++++++V+++++++++++$++++++++++++++++V++V¬¬++++++V++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++VV+++¬W+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++VV++++V¬¬++++++++++++++++++++++++++++++V++++++++++++++++++++++++++++++++++++++++VV+++++¬¬W+++++++++++++++++++++++++++++++V+++++++++++++++++$+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++V++++V¬¬2+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++V++++++¬]+++++++++++++++++++++++++++++++++$+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++V++++++¬++++++++++++++++++++++++++%+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++2+++++%+]¬++++++++++++++++++++++++V++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++V+++++++++++++++++++++W++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++V++++++++++++++++V+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++V+++++++++++++++++++++++++++++++++++%++++++++++++++++++++++++++++++++++++++++++++V+++++++++++++++++++++++++++++++++++++++++$+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++%++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++%++++++++++$+$++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++$++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++$+++++++++++.+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++$+++++....++++++....++++..+++++++..+++...+++++...++++++..++++++.++++..++++..++++++...++++++++++.........++++...+++++++...+++++++..++++++++++++++++++++++++++$+++++++V+++....++++++....++++..++++++++..++++...+++++++...++++++..+++++++..+++++....+++++...++++...++++++++++.........+++++...+++++++...++++++...++++++++++++++++++++++++$%++++++++++V+++++....++++++....++++....+++++++++++++++++....++++...+++++++++++++++...++++++..++++++...++++++.....++++++..++++...+++++..........+++++...++++++...++++++...+++++2,++++++++++++++++++++%$+++++++++++++++....++++++....++++....+++++++++++++++++++..+++++...+++++++++++++++++...++++++..++++++..+++++++......+++++++...++++....+...........++++++...+++++...++++++....++++V,+++++++++++++++++++V++++++++++++++++++++....++++++....++++....+++++++++++++++++....+++++...++++++++++++++++...++++++..++++++...+++++++......++++++++...+++...............++++++...+++++...++++++....++++V2++++++++++++++++++VVV+++++++++++++,+$+++....++++++++++....++++....++++++++++..++++++...++++++++...++++++..++++++...+++++++.......++++++...+++..............++++++...+++++....++++++....+++VV2,++++++++++++++++VVVVV+++++++++++++$$++++$++++....+++++++++....++++....++++++++...++++++....++++++...++++++..+++++++....++++++.......++++++...+++...................++++++....+++++....++++++++....+++VV++,+++++++++++++VVVVVVV,++++++++++++$$+++++++++++....+++++++++....++++....+++++++...+++++++....++++++...++++++..+++++++...++++++.......++++++...+++........................-+++++....+++++....+++++++....+++++V++++++++++++++,2VVV+VVVV+++++$++++%+++++++%+++........++++.....+++++++...+++++++.....++++++...++++++..++++++...++++++.......++++++...++.....................++++....++++++.....+++++....+++*,2+++++++++++++++PV+VVVVVV++++$++++$$++VV+++++........++++.....+++++++...+++++++.....++++++...++++++..++++++....++++++.....++++++...++..................+++....++++++.....+++....+++*,.-++++++++++++++++V+++++V++++++++$$+OVV++++XXXXXXXX++++.XX.+++++++....++++++.XX.++++++XXX++++++.X++++++.XX.+++++XXXX.++++++.XX.++.XX..XXX.XXX.X..++.XX.++++++.XXX..XXX.+++$-X.++,++++++,%++$%%%++++$$++V¥¦V+++XXXX..XXXX++++.XX.++++++.....++++++.XX.++++++XXX+++++++.X++++++.XX.++++++XXXX.++++++.XX.++.XXX.XXXXXX.XXX++.XX.+++++++.XXXXXXXX.+++-,-+,+++%....$$++%+V¬¬¬++++YYYYY.................YYYYY++++.YY.+++++++++X..+++++.YY.+++++++++YYY+++++++.Y++++++.YY.++++++.YYY.++++++.YY.++.YYY+++.YYXYYY.YY.++.YY.++++++.YYYY.YYYY.+++-++++,++-X--XX++++++O+VVV+++YYYY..YYYY++++.YY.++++++++++++++++.X..++++.YY.++++++++++++++++YYY++++++.Y+++++YYY.++++++.YYY.++++++.YY.+.YYY.++++++.YY.YYY+.YYX+.YY.+++++++.YYYY...YYYYY++++-,VV++,,++++,,X.$++V+++YYYY..YYYY++++.YY.++++++++++++++++++Y..Y++++XYY.+++++++++++++++++YYY++++XY+++++.YYY.++++++XYYY.+++++.YYY+YYY.++++++++++XYY.YYY+++YYY.+.YYX++++++.YYYX.....-.YYYX+++++$%%++++++++++++,]VVV++V2+++++..+++YYYYYYYY++++.YY.+++++++++++++.Y.Y.++++XYY.+++++++++++++YYY+XY++++.YYY.+++++.YYY.+++++.YYY+.YY.++++++++++YYY.YYY+++XYY.+.YYX+++++++....YYYY.++++++++++++++++++++++WVVVVVVV,+++++++-.+++YYYYYYYY++++.YY.++++++++++YY+++++.Y+++YYY.++++++++++YYY.YY++++.YYY.+++++.YYY.+++++YYY.++.YYY+++++++++.YYYYYY++++YYY.+.YYY+++++++.YYYY+V++++++++++++++++++++++VVVVVVV,,++++++++++++++++++++YYYYYYYY++++.YY.+++++++++.YX+++++++.Y.+++YYY.+++++++++YYY.YYY+++.YYY.+++++.YYY.+++++YYY.++YYY.++++++++.YY.YYY+++YYY.+.YYY++++++++.YYY.++V++++++++++++++++++++++WVVVVVV++++++++++++++++++++++++YYYY+++++++++YYYY++++.YY.+++++++YY.+++++++YY++YYY.+++++++YYY.YYYYY++.YYY.+++++.YYY.++++++YYY.++.YY.+++++++YYYYYY++YYY+.YYY++++++++YYYY.+VV+++++++++++++++++++++++VVVVV]V++++++++++++++++++++++++YYYY+++++++++YYYY++++.YY.++++++YY.+++++++YY.+YYY.++++++.+YYY.YYYYYYYY.++.YYY.+++++.YYY.++++++YYYX+++YYY++++++.YY.YYY+.YYY+.YYY++++++++++++.YYY.++VV++++++++,VVVVV]]V++++VWWV]]V,+,++++++++++++++++++++ƒƒƒƒ++++++ƒƒƒƒ++++.ƒƒ.++.ƒƒ.+++++++ƒƒYYƒƒ.+++.ƒƒƒ.XYƒƒƒƒƒY.Yƒƒƒ.++++++Yƒƒƒ.+++++++ƒƒƒY+++.ƒƒ.+++YƒYƒƒƒ.ƒƒX+.ƒƒY++++++++++++++.ƒƒƒY+VV++++++++++++++,VVVVWVVVVVWWW]VVV2,++++++++++++++++++ƒƒƒƒ++++++ƒƒƒƒ++.ƒƒ....ƒƒ.++++++ƒƒYYƒƒ..ƒƒƒY.YƒƒƒƒƒY.++Yƒƒƒ.+++++++Yƒƒƒ.++++++++ƒƒƒY++++Xƒƒ.Xƒƒ.ƒƒƒ.ƒƒƒ..ƒƒY+++++++++++++Yƒƒƒ.++V+++++++++++++++++++++++++V]WVWWVVVV,+++++++++++++++$++++++++ƒƒƒƒ++++++ƒƒƒƒYƒƒX..XƒƒX+++ƒƒƒƒƒƒ.Y..ƒƒƒƒYƒƒƒƒƒƒX.++Yƒƒƒ.+++++++YƒƒƒX++++++++ƒƒƒƒ+++++YƒY.ƒƒ..ƒƒƒ.ƒƒƒ..ƒƒƒ+++++++++.ƒƒƒ.+++++++++++++++]WWVVVVVV,+++++++++++++++++++++++++++++ƒƒƒƒ++++++ƒƒƒƒƒƒƒY.ƒYYƒƒƒ.ƒƒƒ..ƒƒƒ.Yƒ..ƒƒƒƒƒƒƒƒY.+++Yƒƒƒ.++++++ƒƒƒƒY+++++++ƒƒƒƒ+++++.ƒY.ƒY.Xƒƒƒ..ƒƒY.Yƒƒƒ.+++++++++++.ƒƒƒX+++++]W]VVVVV,++++++++++++++++++++++++++%+++ƒƒƒƒ++++++ƒƒƒƒƒƒƒƒƒY..YƒƒƒYƒƒƒƒY.ƒƒƒƒY..ƒƒƒƒƒ...YƒƒY.ƒƒƒƒƒƒƒ..+++++++++Yƒƒƒ.+++++.ƒƒƒƒƒ.++++++ƒƒƒƒ++++++Xƒ.Yƒ.Xƒƒƒƒƒ...YY..Yƒƒƒƒƒ.+++++++++++Yƒƒ.++++VWW¬WVVV,,,+++++++++++++++++++++++++++++++++++ƒƒƒƒ++++++ƒƒƒƒ..................++++++++++++Yƒƒƒ.++++.ƒƒƒƒƒƒƒ.+++++ƒƒƒƒ+++++++.XX...Y..+++++++++++ƒƒƒ.+++++WV¬VWVVVV,+++++++++++++++++++++++++++++++++++++ƒƒƒƒ++++++ƒƒƒƒ+++++++++++++++Yƒƒƒ.++++++...............+++++ƒƒƒƒ+++++++++++++++++++ƒƒY++++V]]]VVVVVV,++++++++++++++++++++++++++++++++++++++++++++ƒƒƒƒ++++++++ƒƒƒƒ++++++++++++ƒƒƒƒ.+++++++++++++ƒƒƒƒ+++++++++++++++++++++++++ƒY.+++++]¬]VVVVV,+++++++++++++++++++++++,,2+++++++++++++++++++++ƒƒƒƒ+++++++++ƒƒƒƒ++++++++++++++++ƒƒƒƒ.++++++++++++++ƒƒƒƒ+++++++++++++++++++++++++++++++..+++++2‚VVVVVV,,++++++++++++++++++++VVVVVVV++++++++++++++++++++ƒƒƒƒ+++++++++ƒƒƒƒ+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ƒƒƒƒ.++++++++++++++ƒƒƒƒ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.+++++++]‚WVVVVVV2+++++++++++++++++++++2VVVV+++++++++++++++++++.ƒƒƒƒ.+++++.ƒƒƒƒ.++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ƒƒƒƒX+++++++++++++++.ƒƒƒƒ.+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++2V]]VVVVVVVV++++++++++++++VV]WV+++++++++++++++$++++XƒƒƒƒX+++XƒƒƒƒX++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.ƒƒƒƒY+++++++++++++++++++++++++++++.ƒƒƒƒ.+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++VVVVV2++$+++++++++VV]VV+++++++++++++++++.ƒƒƒƒƒƒ.+.ƒƒƒƒƒƒ.+++++++Yƒƒƒƒƒ.+++++++++++++++++++++++++++ƒƒƒƒƒƒ+++++++++++++++++++++++++++++++++++++++VVVV++++++++++++++++++.ƒƒƒƒƒƒƒƒ..ƒƒƒƒƒƒƒƒ.++++++Yƒƒƒƒƒƒƒ.++++++ƒƒƒƒƒƒƒƒ++++++++++++++++++++++++++++++++,,,++++++++++++*++++++++++..............+++++..............+++++++++++++++++++++++++++%%+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++%+++,,++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,++++++++++++++++++++++++++++++++++++++++++++++,V,+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++%+++++++%+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++%+++++++++V++++V+++++++$+++++++++++++++engine/resource/hexenworld.icns000066400000000000000000002014271444734033100172200ustar00rootroot00000000000000icnsis32N &P2P2P2P2‚bÿ¹¹&’&€€J€-zJJ€2ÂÂ2€>Jz&–ÿP€2ÎÎ2€Pÿ–2ÿÿ&ÿ&ÿ2€ 2ÿÿPsÿPPÿ&ÿ>€ 2ÿ¹sPÿPs¹&ÿ2€ &ÿÿs>ÿ>Pÿ&ÿ&€ >ÿÿV>ÿ>Pÿÿ>€ n¹P¹sPÿPP¹P¹n s–€P¹––P€€s‚ –P–€P–ƒ2PPJJPP…>J€b2’ &2222‚bŽ==&’&€J€-zJJ2ÂÂ2>Jz&1k2ÎÎ2k12ŽH&H&k2€ 2kH%ŽH&k>€ 2k=%k%=&H2€ &H%%>H>%&H&€ >%%V>H>%%>€n€%%n‘†ƒ2JJ…>J€b2’ &2222‚bŽ&’&€J€-zJJ2ÂÂ2>Jz&k2ÎÎ2k2ŽH&H&k2€ 2kHŽH&k>€2k€k€&H2€ &H%>H>%&H&€ >%%V>H>%%>€n‚%‚n‘†ƒ2JJ…>J€b2’s8mkÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿil32$£V…&&…>‰PP‚–P€PP–PP&ˆ&PÿPPV&PÿP&‰V&ÿÿb&z&–ÿÿ>>Pÿÿ–„–ÿÿPV‹zPÿÿ–ƒVPÿÿP&†ƒzVÿÿ–&‚VPÿÿP&‚zV†ÿÿb&‚V–ÿb… >–ÿbÿÿ–Pbÿ–ƒ&Pÿ&P–ÿbÿb–P&z–ÿ¶ÿPÿÿ–&€>&Pÿ––€ÿP>€Pÿÿ–VPÿÿ… PÿÿP&Pÿÿ–z€ Vÿÿ–Pÿ&€P &PPÿÿPPÿÿP& bÿÿ>&Pÿ&–ÿ –PbÿPÿÿ–>bÿÿ–€ÿÿ–ÿÿ–PÿÿPPÿÿP&Vÿÿ–Pÿÿ–PÿÿP&PÿÿPPÿÿP>Vÿÿ–Pÿÿ–PÿÿPzPÿÿPÿÿP>bÿÿ–€ÿÿ–Pÿÿ–z–ÿÿPPÿÿ>Vÿÿ–&Pÿÿ–Pÿÿ–zPÿÿPPÿÿ>ƒ–ÿ–VPÿÿPPÿÿPzPÿÿPPÿ–zƒPÿ–&P–ÿP&–ÿÿ–>VPÿÿP–ÿ–z‚ >PÿÿPÿÿ––€ÿ –ÿÿP–ÿP&ƒ Vÿÿ–ÿÿP–€ÿ€ÿP–ÿ&…PÿP€PPÿ––ÿP€Pÿ–z„V––&PÿP––&PÿPˆPÿP&&ÿPPÿP>ÿ–z†z––€P–P&&––€Pÿ&ˆV–PPÿP€&–PP–P‹>–€P‚&Pz‹VPP„&PPzb&>…z&>¦£V…&&…>œ&ˆ&%ŽŽk€ V&kŽŽ%&‰V&ŽŽb&z&ŽŽ>>kކŽŽV‹zkŽ„VŽŽ&†ƒzVkŽ&‚VŽŽ&‚zV†kŽb&‚VŽb…>€%b%%€bŽ„ &k&%b%b&z€޶k€%k&€>&kŽŽ%>kŽVk%† kk&ŽŽz€VkŽ%&&kk€kŽ&bkŽ>&%&%Žkk€b%€kŽ>bkŽ%%€ŽŽ%%€kŽ&VkŽ%%€kŽ&%%€kŽ>V%k%%€kkz%%€kk>b%k%%€%kz%%€%%> V%k&%%€kkz%%€k%>„kV%%€%kz%%€kz„%&%&%%>V%%€%z‚>%%€%%€€%€%%€%&ƒV%%€%%€€%€%€%&†%„%%„%z„V‚&€%ƒ&€%Š%&&%%€>%z†z†&&„%&ˆV‚%&’>‡&ƒz‹V‡&zb&>…z&>¦£V…&&…>œ&ˆ&%ŽŽk€ V&kŽŽ%&‰V&ŽŽb&z&ŽŽ>>kކŽŽV‹zkŽ„VŽŽ&†ƒzVkŽ&‚VŽŽ&‚zV†kŽb&‚VŽb…>€%b%%€bŽ„ &k&%b%b&z€޶k€%k&€>&kŽŽ%>kŽVk%† kk&ŽŽz€VkŽ%&&kk€kŽ&bkŽ>&%&%Žkk€b%€kŽ>bkŽ%%€ŽŽ%%€kŽ&VkŽ%%€kŽ&%%€kŽ>V%k%%€kkz%%€kk>b%k%%€%kz%%€%%> V%k&%%€kkz%%€k%>„kV%%€%kz%%€kz„%&%&%%>V%%€%z‚>%%€%%€€%€%%€%&ƒV%%€%%€€%€%€%&†%„%%„%z„V‚&€%ƒ&€%Š%&&%%€>%z†z†&&„%&ˆV‚%&’>‡&ƒz‹V‡&zb&>…z&>¦l8mkÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿih32‚ýÿñ0‰ÿ6‰–ÿˆcb1ˆ-šE‡/Pÿˆ$=J>/""#2>ÿˆ$"+?^uqhjvwgC%7MdqjluiM5//*ˆ$ #U¦ãâÕÜß¹…T9}ÁãÙÛèÇ~8 #!ˆ 4…à€ÿ è’L80.=<gÎÿ¾^(‰;€M;(%ƒòÿÿ¥>Gcˆ©&,“ôÿÿÉaq„yz…«C VÞÿþ†! a§ÿgÛÿÿ¤% "Hƒzz^I†\37lÞÿÿ“/† \Èÿÿ£+>„BS‚ÿ ÎjZ|Üÿÿ¥C‚S79U°ÿÿºJ.Šÿÿÿƒÿ‘„„‰‡ƒ†”b=\Ðÿÿ¡I „Y\FM¡ÿÿ¸H!;†¥‹…‡ˆ‘ÿ4.@FIOLCBJS+AÓÿÿ’H 5?XQeµÿø&CTMIPSC:B%4+")-,- .½ÿú†=ƒ(LTÑì§C #)# "$!8! ?td7Cs„fGƒìù”:„k3>xÐÏeUt`>FQ?085+.% P´È›¤ãÿÌx'kÚÿ°EZ[dv/V¾Ïhi±¾¨ºÍ´…c@&(R!~×êèÿÿÑh"£ÿÿ²:(:;:4Fºë§= &vÈò€ÿ®V*#/—I!—úÿöËy*Zåÿýw D¼ÿï{(“ùÿÿáy(%;E açÿÿ¦< ?†èÿ¬*  -³ÿÿ¬>zéÿÿ—4@ƒÿ˜,DËÿÿ“-^¡Ï´9@RA,*EH"ÿÿ´bnÑÿðu'Pÿ€+®G8Ãÿúx)M¥Å‰5‘¼¼§œª±™X >Åì°qSÁÿø‡D$©L :Äÿôq' ¶:ŒäÿוO ?²à¯sI½ÿý‘N)+™E ?Åÿÿ‰3TÃÿ±1÷ÿÿù< 3‡íðª`jÌÿó‚<#’?>Âÿÿ >€ZÇÿÿmJÅÿÿÅVA½ÿÿ¥T/ƒ×ÿço/0+ˆ8>Ãÿÿ¡>4‚Ðÿÿ¬5+©ÿÿ¦C=µÿÿ¥YjÍÿês7)>+‰9>Ãÿÿ›95…Íÿñ»u<£ÿÿ¦YPKK©ÿÿ§aHÁÿìr9.E‘@?Åÿÿœ;€^¿ÿ캠K¡ûÿºxq‰eºÿþ¦_SÉÿßV!%T+C BËÿÿœDP¹ÿîºN¢÷ÿÒ“}ybÁÿý¥WsÙÿÕ> `+<@ÆÿÿžK nËÿê´ŒEŸ÷ÿÏzng±ÿþ¤Z)}ÙÿÈ5!l+ù%-œÿÿ¤FG…ÝÿÜ—XL ûÿ¶rp‡D ÿÿ¥\ }Òÿ¡,E„+dÖû©9%IvÊÿÒve±ÿÿ«`]o6GŸÿÿ¨K-Üåx 4g¯+I¾û¹>4[©ñÝy "tÊÿÿÅrLB6O¦ÿÿ­86¦óÞm6n¾"€B E³ÿå`a¹ÿù‘+oÙÿÿú£9 cÃÿÿ²18´ÿÏ_)cÿDt=-@¨ÿÿ{ˆôÿÿš& `Ø€ÿÄ&•ýÿÿº74·ý°> VÿW,%†ÿÿŠ2¦ÿÿô{ jÖ€ÿÑ&º€ÿ ­/8Àô#"€y]€WvEÌÿ¢1ƒ¸¿’5@§ÞØèÿÙLŒÃÄ©i]ÞÝ`)YÿzþnѶG8OE)uäâŒx¹Í|/LK8 ø¶/=‚*YLv*)Â"—í¿Cb¶¬H <Ãöˆ=ÿ‚-VaÿETÀ¿S T»Ãg!—Ô “ç¹I#,XNc€ÿ€¹ 4¢ÞŽ,€Î”'nØÄNUÜãd,dÿxzc€d:-^±¬RJœ¨b 6“Á€€"Œâ¡ .¤€Ø‚V€!Çb/(\—s L§­f*#& :„–MV¦ I€Yˆ ´B#W}ZyÅ’2" :qf-=|‡K€pz„‰€ u3)WqWIisI‚+BIIYcC%0U·Qyƒyº%€ Q.'2J`]F$„ @_W6"1W”ÿt&‡U?€wA/-BP8 ÿ "8OF#L‘ÿ€ÿ‡VMwÿˆcŒb5†-ˆA‡2Nÿˆ=V~™t4.EA  =wš“d'€-‰;€YG-=‰­¥s.>b‹«-e¦ªs-m„yz…´K 'n›‰D k¦ÿ.F™¡_'Eƒzz^I† ŠA!Vw!‚ ÿB—šI$J„BS‚ÿ øn5NpƒYI$E—šD ;°ÿÿÿƒÿš‹‹ŒŒ’œn2Qp(ƒYj9B•œG Ož¦‹Š‹™ÿ|*5R[XSVY\^_BS|;35ÿj: D“–B :XZXVVXR77C (*# %$ Y‘W2"ƒe3Dˆƒ6(& )* 8%€ ‚Y‘‹K ƒK$?m\0(€(S]K3€F~t*g{YbfE! :S0€ (MQH@!-Q*€*v¦j+5eX7@=>;+;N& /x™}G5M€=n±¤U7R9 ;aH!|±†'2HL#_œ!3%9D"‚‚#8vq6m¨}Z–ÿŽ9T‹n0*5 €:|u9 Z’qhÿ€ ŸX,OŒ~: '/GPI. :kT"€QŒo%$V œ]/M‹‚C'[ŒŒj:.L4€Q‹n&%MT*V“~0+ Y¢¡_€1$ €Ts% LˆN'Y–w *€A•šK$"U‘t& M}I%G‚l *€8ƒˆ?#$#Y‘n$+]|N( )a] )€/jo6$Ii '%MzY3bƒW.MR ) €O[/*`Ì-%%2L44ZY1NP) €LY/*^Ã-%%'3 3QT+VV!-+€&^h3)Z² &%=C:ZÏMKU C! *€#Ye3*]À%%*OK .Y‚…*4C;+ ! `ÿzþ+  €€  €!P˜‚YKg< €  Jÿ‚Vaÿ` .# € 2( !=[Nc€ÿ€(ÿ3 #   ++ €pz„ˆ€ŠG(‚. ƒ 3]¬Pyƒyº%€fF2Š3‚ 5džÿt&‡U?€‘Y7  ÿ ,)$$]žÿ€ÿ‡V*#%"‚B"€\U=(!=Ÿÿ€Š>MwÿˆcŒb5†-ˆA‡2Nÿˆ=V~™t4.EA  =wš“d'€-‰;€YG-=‰­¥s.>b‹«-e¦ªs-m„yz…´K 'n›‰D k¦ÿ.F™¡_'Eƒzz^I† ŠA!Vw!‚ ÿB—šI$J„BS‚ÿ øn5NpƒYI$E—šD ;°ÿÿÿƒÿš‹‹ŒŒ’œn2Qp(ƒYj9B•œG Ož¦‹Š‹™ÿ|*5R[XSVY\^_BS|;35ÿj: D“–B :XZXVVXR77C (*# %$ Y‘W2"ƒe3Dˆƒ6(& )* 8%€ ‚Y‘‹K ƒK$?m\0(€(S]K3€F~t*g{YbfE! :S0€ (MQH@!-Q*€*v¦j+5eX7@=>;+;N& /x™}G5M€=n±¤U7R9 ;aH!|±†'2HL#_œ!3%9D"‚‚#8vq6m¨}Z–ÿŽ9T‹n0*5 €:|u9 Z’qhÿ€ ŸX,OŒ~: '/GPI. :kT"€QŒo%$V œ]/M‹‚C'[ŒŒj:.L4€Q‹n&%MT*V“~0+ Y¢¡_€1$ €Ts% LˆN'Y–w *€A•šK$"U‘t& M}I%G‚l *€8ƒˆ?#$#Y‘n$+]|N( )a] )€/jo6$Ii '%MzY3bƒW.MR ) €O[/*`Ì-%%2L44ZY1NP) €LY/*^Ã-%%'3 3QT+VV!-+€&^h3)Z² &%=C:ZÏMKU C! *€#Ye3*]À%%*OK .Y‚…*4C;+ ! `ÿzþ+  €€  €!P˜‚YKg< €  Jÿ‚Vaÿ` .# € 2( !=[Nc€ÿ€(ÿ3 #   ++ €pz„ˆ€ŠG(‚. ƒ 3]¬Pyƒyº%€fF2Š3‚ 5džÿt&‡U?€‘Y7  ÿ ,)$$]žÿ€ÿ‡V*#%"‚B"€\U=(!=Ÿÿ€Š>HQ^øƒ@•Nƒ ÿk_UL@6+  ’ #+3CHNPPOMLLNPRRPKE>71-,,-.01‚0ƒ0•„' "%+08CQ]is|~|wtommpuy}|}|wmaSB/#  &/5:1/..*.4;?=5) $=d‹²Öö†ÿüèÇ¥\6  !-Bÿƒ#•#„"&#'>Xx›Áßø„ÿ"íЪ‡gL5-,//4:>>CIPSOA." +NužÆé…ÿýèΪ‡cA $:b„$•.„/3350*##$5OršÁäƒÿ$÷Ø´ŒfF-'.:ENQXaksmR3% 8[…¯Õó„ÿêέ†bA& ‚+Wÿ„%•>… ]SLC90+ ,Lv£Ïõ‚ÿêÄšqJ+ -@P]do}•Àö™;+ € $Dk–¿ãƒÿõײ‹b>#ƒD†(Œƒzyyzzyxv_†‚ë|cQA8'3]ŽÂð‚ÿ幋a:-G]o{Š£ñB!3Xƒ®×û‚ÿñËŸrE"  &A‚ËH‡€zwzy€z‚zy€zŠ!„ÿžlPC- (P‚·é‚ÿÞ°W2+Lj‚“¨×ÿ‚7'JtŸËó‚ÿêÀ_1 ",>„†‡z” ¯`K3 ,P³ä‚ÿÜ­V1#Q~ŸºÙÿƒÿ #Bj”Áë‚ÿ溈V( €"'2AeŽƒzy€z•€T:)€'9Zƒ´ã‚ÿÞ±ƒ[7 X¶þ€ÿ„"‚$Ae¹ä‚ÿ幇V* € ,3AWêŽzy^\WN;– _C6/118He‹¸å‚ÿä·Ša=”*Cbˆ³ß‚ÿ纉Y/ 1:Lp 4EKQNÿÃËÿ– ¢^NFGFKXq“¼æ‚ÿé¿“jF(‘  $1E`‚«×þÿî‘c;  &:FgßÿÜÜÞœ ô‚h[YUXbx—¾åþÿîÅšrN/Šƒ +#"$08F\{¢ÎøÿöË›mG,/H_¦ÿÿ‚ Þ™{jb\[cw•»âüÿðÈžwT6‰&:ƒ XA74:=FWs˜ÅñÿþÓ¢uP6& ';\„ïÿÿˆÿÿÿàÈ¿€»½¾¿€¾¿¼ÅÚÿ üÇ ƒm`UQWiˆ¯Ù÷ÿïÇžxU8ˆ#ƒ ƒYH>?>BPi޼ëÿ þÒ¡sP7*&&4Ox«ù€ÿ å³ÂÀ»·´³·ÁÝÿÿŠ$ÿù¹¤™”’’“”–——––•“•˜œ¤«ª¤‘{fWIDI^ª×ùÿîÅœxV;$ ˆ>„ …cQFEACOfЏé‚ÿÓ¡sO6($%4Nq’¶ÆÀµ£•••””“”•™£·ôÿ‰$Õ–…~ywvvwxz||{yvtsstty€„‡}kVF719Qw§ØýÿëÁ˜vW>*†\ƒ ¸}eVKIEGRiŒºéÿûΜnI/!+B]q…ŽŽ‰€yuwxy{|}}~‡¤ƒÿa^€_^_acdec`]YWVXY^dinfVA1"(Fq¦Ú‚ÿ 滓rV?. „ÿ8rƒ osbVNMKO[q”¿ëÿíÀb>$2GUckmkfa]^`be€heaZN8ƒ)‘†mG*'08?DFHIKLNQRSQNJFDDEFKOSWO?+ ;l¤Ü‚ÿ âµnS@1 ƒK5R4‚ Qk^UOPQWf}ŸÇî€ÿ ÷Ô¨zO- '9DOVXWSOLMNQTWWUPH<,€3LocP8$%,269:€;<=>=:7432459=?C;+ 3g¡Ú‚ÿ ݯ†hO=1 „#iƒH1eYROSWbs‹­ÑñþÿöܶŒb< ,4;@A@=:889<@BCA<4*&9FYN?-!).122/-+*€+ *))*++.014, €,`šÒ‚ÿ ÙªcJ9.W`SONT\j˜¹ÚòõïÛ¼”mH* '*-..-+)(')*-/0/,'+8AF>1#'/453-'! #'*-..--,,.%€&UŒÃñÿ ש`F5) \NJKR]n†¡ÂßñëÜ¿›sN1 "),*'$"… !€"+!$-6<7/% -:CEB:/& !%-7>DGE?;620' €Hz®Üýÿÿüת^C/" ŽXLDBELXl†£Åàìàɦ}U4"/5=@?7/)$$&)++)&" #%%$$&).37+$  &;P^d_UD5++1>N]hmkbXNE?2#€9e–Ãèþÿüܲ‡dE. XD:8;BPe‚¡ÃÞç׺‘e?!$8KV`b]QD8115;@A>70(&'+/11/,*+,02#  .Kj€Š‡|gRDBJ[q†—Ÿž‘ƒscUC0€ *S­Õñþþ従nJ/l:/,.5CZxš¾Ùâѱ…X3 /Jcuƒˆ‚ueVMMS\debZOC>=?BB>91,**,."  4V­®¦yhfm€™²ÈÔÔIJœ„mT;$!GsŸÉêýÿîË yR1†ÿ ¯ÿ‚Y/# '5Ll¶ÓÞϯƒV1 5SqЧ¦}tv}ˆ’”‘‰|ka\ZVPH>2+((),)" 3Wƒ¦½ÅŶ¢“‘—¨ÁÚðýýëÕº›|\># €ErœÆèüÿòѨ€W4€\=‡¦’we]_ep€„uM,($<^…­ÎÜѵ‹^; 2Opާ¸½»±§¡¥®¹ÃÆÃ¹ª”†zpdXK>/(&'(+5.# *Ly ½ÏÙÒŸ¶ºÇÛòÿę̈‚];€%O}¨ÒóÿÿúÙ¯…Y4\ /Pa\WROPRTVTJ9&  1T}¨ËÝØÁ›pL* '@_´ÃÌÌÈÇÍÖáëîìãÒº¦’€jXG9)#$'(,G?1!7`ˆ«ÆÜâÞ×ÖÖÝëýÿîÌ¥|U3€4b”Àèÿܰ„V/€&;GFFED&CA9, +Oy¥Ëáâѱˆd= )Cb‚ ¹ÎÙßäíö‚ÿ%ïÔºŸƒeO=/" #)*/]SD/?d‹¯Ðãìííëíóþ€ÿøÞ»”kF&€H|±ÞÿþÖ¨yL& € -77‚9863,"€(Mx¥ÌçîäÊ¥U/ &@`¢ÁÙêö„ÿüÞ¿œzX@0'!&-/4tkY?! €2Xƒ°Ùúƒÿíͧ~X;$!+1>JRMIáøùºL„>lÍôÿüåɨ„`?$  "5On™Èõÿà¬vE…€ƒCq£Óýÿâ²€P-€ )Nw¤Îò‚ÿôÔ®†_>)&5@Panj\ÿ‡2\Áï‚ÿíÉ wP.1Hb~£Êï€ÿ컇S(‚€‚ ‚6fšÎüÿðÃ’dA *NtŸÊð‚ÿè¿”jF+-BSm¹ÿ„ 4  *N}²å‚ÿïÆ˜mF$ €$>Yt‹ªÉä÷úèÑ_2€   'VŒÃõÿóÈœrQ.,Oq™Áç‚ÿÙ¬~U4 3Mf‘ßÿ… A(%Do¥Û‚ÿîÁ‘g@ € $Gg‚–¯ÆÙâÛÀ–d6€ ",23-'   '-.+(Cw°åÿïÈ¡{^<-Om’¸Þþ€ÿùÏŸqK.!7UxÎÿ† ´cA,$"=e›Óûÿ鹉a= JqŸ³ÄÏо›l:€ !8DQWXTMD:4,'%%,7BKQRME2€-_—Íý€ÿèÅ¢iG(Jgаָ€ÿ öË›nK1""5V‡ÿ‡ ÛzR7*4]”Ðüÿà®~W7‚ Fx–§¹ÆËŬL6Tfv‚‚|sic[VTU\fpuywnaJ/E{²åÿÿüãĤ†qM€;[¨Ðõ€ÿ øÌrR:+$+F¢ˆ Ç€[@0 2Z‘ÍûÿפuR4 ‚ 4p’¥·Ãƾ¡r9 Eiƒ˜¥«¯ª¡—‘Š…„†–€ž˜Œy_A# € /a—ÊìøðÛ¿£ˆuP .Qx¡Ëñ€ÿ üÒ¤|]G7." #ˆ ÿ²€_D4#3Z‘Íû€ÿþÏœnM3 ‚ ^…œ±¿Ä½¡r9 -!Ks“¯ÂÍÖÓ˾·´´¶½ÄÇÁº¯†kM/"O‚´ØéåÓ¼¢ˆvP"IrœÆîÿ ج†iRB7( ‰ ÿ¦}_F6& 5\’Îû€ÿúÊ—kJ2!‚ Eq¦¹Ã¬‚L,Dn•¶ÐáðòîçåàÞßáåçäÖȶ „jN4  Ix¨ÍàßϹ¡ˆvO‚Dn™Äíÿ ݳqZI<,!‰ ÿ´ƒbH8& 5[’Îû€ÿûÊ–iH0 ‚ 0^}š²ÄÌÀžm<€3^‰°Ðèý†ÿþóÜÆ®”w^G2  +P|ªÎßßл£‹xRAl—Ãíÿ ܲ‹oXG;+!‰ ÿ®_F7&!7^”Îû€ÿýÏœoM4" ‚Gi‹«ÆÙÙ˜f3 Al–¼Üö‡ÿðÒ´–x[D2%&@d޹×åáѺ¡ˆsM#IrœÇïÿ Ú¯‰kTC7( ˆ ÿ¨z\C5&#9`•ÎúÿÖ¥xU9%‚3W~¤ÈåòèÆ•])"Hq›Áâú…ÿû໘uU9%+=[©ÑéðçÓ¹žƒlE€5W}¤Íò€ÿ þÖª‚dL<1#ˆ ÿ¢vXA3%#:a•Îøÿß°„^A)ƒ'LuŸÊïÿÿðÁ‡L%KtžÄæ…ÿèÇžwQ1"9Tx ÈíÿþîÖ¸š}e>€ IgŠ®Óö€ÿ ùÏ¢z[C4+&ˆ ÿ¢uX@2$"9`”Ìöÿ軎gG-„#GqÊõ€ÿæ®q:€ *R}§ÑõƒÿöÕ¯ƒZ5 %Dg‘¼ä€ÿôØ·—x_97\u•¶Ùù€ÿ òǘoP:-&!*ˆ ÿžrT=0"!9`”ËõÿïÖnL/„+NuŸÍùÿÌV*€4^йäƒÿ⽓hA‚ $Hq Ï÷€ÿùÚ¶”tZ5Df}›»Üû€ÿ ï“jK6*$$0ˆ ÿ™mP:-  9`”ËõÿòÇšsO0ƒ=\¦Ðúÿà©qBGr¤ÔûÿôЧ|T0ƒ"Hu¦×ÿüÛ¶“sY4Df}›»Üû€ÿ îÁ’iJ6+&!)5ˆ ÿ”iM7+9`”ËõÿóÈ›tO/ƒ0Rm‹­Óùÿ뼉\-€9c–Èòÿê—mH+‚Es¤ÕÿþÛ¶“sZ67\u•¶Ùú€ÿ ï“kM9.*#&-:ˆ ÿ‘gK5)8`”ÌõÿòÇšsO.‚Fd|–´ÕøÿòÌ¢xH€ 3]Âîÿ 龑hG/ €DpŸÎ÷€ÿþÛ¶”v]9€ IgНո€ÿ ðÕmP=3/(+3@ˆ ÿfJ4(8`”ÌöÿðÄ—qM-‚#Sp†¸ÖöÿîЯŽa0 5]½éÿçºgK9/)# Dm™Äì€ÿýÚ·–xa>€4V}¦Ïö€ÿ óÆ—oS@62,/6Cˆ ÿfJ4)8`•Ìöÿî•nK+‚$Tq‡ž¸Ôô€ÿûæÏ¶žyH;aºãÿ溎jSF@?>7)&Hn•½ã€ÿüÙ·—{eA#HsžËô€ÿ ôÆ—oTA74.18F‡ ÿ“hL6*8`”ÌöÿíÁ”mI* ‚Hg€˜³Ñò€ÿóÞ˸ª`%Dg·Þÿç¼’r]SQSWUH/3St—»ß€ÿûØ·˜|gC@lšÉó€ÿ óÓkO>52-2:I‡ ÿ˜lO8,8_“Êôÿî•mI*ƒ2Tq¬Íó€ÿòÝ˼´ v+Gh´Ùú€ÿêÀ—xf]]aiokR34Kgƒ¡Áá€ÿû×¶˜|fCAnœÊõ€ÿ 캈_E5..,3?RF‡ ÿšoR;.!!9`”Ìöÿî”mJ,ƒ?`€£Èñ€ÿðÚǹ¶©ˆ6'Nm³×÷€ÿïÈ¢…sjimu€‡yXTdz©Åâ€ÿúÖµ—|eA"Gr Îø€ÿ é´€U:*%''0?Wg‡ ÿšpT>1#";b—ÐùÿîÁ”mK.„,RvœÃï€ÿïÙÅ·µ«Ž;-Tp‘³Õõ€ÿõÑ®’€vsu{„Ž‹smy‰›±Êå€ÿùÕµ–{c?€0Q{¦Óü€ÿ ç¯wK.!-?[†‡ ÿ˜qU?3%$2$$=eœÕ‚ÿìÀ•rT;% ƒ(Pu›Ãï€ÿòÛǸ´¦ƒ4*Qp‘²Óòÿ䯫˜‹‚}yujTI[sˆš±Ëæ€ÿùÖµ•v\78\t•ºâÿ ÜŸc3*Ck߇ ÿ–mR[w—¹ÜÿéϹ§žŒb#MlްÓó€ÿ8ùضšˆ}xxzzk38\z™ºÜþÿÿû×µ•w`=@dy˜¹ÜûÿÿñÀ†Q+ +@[„ÿˆ ß{H( -O|­×ü€ÿìÆyW<(#7Mež¿âÿäȯ›ŽxI€&PmްÔõ€ÿóΪ{rpt{‚{C)Op’µÙüÿÿü×µ•xa>€=bx—¸Ùõÿÿã²zJ).:Oi‘ÿˆ ÿ B "BkšÄì€ÿëÆwS6"#1BTižÀâ€ÿýßÁ¥Œ{^-€/Uq‘³Öø€ÿíÅž€ngho{‡‡Y#GiŒ±ÖûÿÿýÙ¶•w`<€;by˜¸×ñýôÔ£nB'+KYi‚¡ÄæúýðØ·’mM* .QqŠ©ÈçÿêÜ{cXUX_c`Q>4:Mj´Üÿ 侕mK$It‘³ÐæñìÓªzK+-AWi˜¼ÿ‚Áˆ.0T}¬ØöúïÓ©|M'*6BO^v”¶Ùï÷ïܼ•mL* 5Vw“²Òïÿî˧†k[SQRSOF<7>Pk·ßÿéÁ•iD€Kx™½ÛïöìÏ¥vI*-BXk‚œÁÿ‚ň.-Ow¦ÓôüôÛ³†U, -9GVnŒ­ÐéôñâÄsP.7Xz™»ÚõÿöÙ¸—ycSJEA=857@Sn“»äÿ îÄ•e>"M| Çå÷ûìÌ¡tI+,@Wj‚ŸÈÿ‚$̇/*Hm›ÊïüûçÄ—e8"2BTlŠ«ÌäòóèË£xS2"9Y~ ÄâúÿþéίoVB4,((+3AWs—Àçÿ ïÄ’`7%Q©ÑîýûæÂ–mF, +>Ui…§Ýÿ‚$‡-IlšÊóÿÿ÷Õ¨sA€+AWt”¶ÖîýÿõÙ±ƒ\73Sz¡Ççƒÿû䯡{W9$"1Gb¦ÎóÿõÉ•a7%P¬Öôÿý佑iD+%7Oc„´ÿƒ&&'„ ##2Km›Íú€ÿè¹J‚)Eaƒ§Êéÿ 辎d< +JsÇê„ÿùÛ³…W/2Oq“ºá‚ÿûÏše9#M€¬Ø÷ÿýඉb?(-DX†ßÿƒ320ƒ =.%!&*5Kk™ÏÿùÉŽR‚ *Mp—¾â‚ÿ òÇ–i= !Ak™Åê…ÿëÁV%€5\…«Ó÷ƒÿÔ j= K~¬ÙøÿúÙ¬~W7!5D™ÿ„€BƒSVB5€-3DaŽÆüÿØšZ ‚ /W~©Òö‚ÿ ñÅ’e9 =h—Äé…ÿöË“V 9i™ÁèƒÿüÒ k> L€®Û÷ýïÉškF) %ƒ€Lƒ ŒjQ@41,/=X…¿úÿã¢^ ‚8d»åƒÿ ïÁ_39f–Ãè…ÿþÒ—V Ay¯ØýƒÿûÒ¡m?K¯ÛöúéÀ]9 ƒÿ9zz€Rƒ Ì|\G71('2Kx´òÿë¨a"‚Bp›Çïƒÿ 䵂T* -1Fi¯¼À³˜qI% €  !'%! ‚0bšËíýëÃ]0%Dd§ÿÿ„‚€ZMvƒV<*!>f®À¿¬Ša8€" ƒ4a‘¾ÞíëϤqF"$Hrš°¿½ª‰a8‚‚I|¯Ùðöܯ|O+ $Beù†Ÿ‚VUVSYƒó{P3" $Iu›¹Å½¢|R- ‚#Kx¡ÃØÜЯ‚P''S€ ºÄ»£}S+ ‚ ƒ:jšÄáëäÁ’c?%*Fy‡ÿ‚€V\=ƒÿ£c;&8c³Ê͹–kB! €‚ 5`°ÉÒɳŽa4 ?n•¸ËÌ»˜k?ƒ -YжØçâΤvN4$!+9Rƒ¯9€?ƒ€Vj„ÿïzB*-U€¬ÌÙÍ®„X3 Gu¡¾Íʵ–nC 0^гÐÙѱ„U, ‚"  Hy«ÑçèÓ²„Y;+%(/4@Qqƒ­[€u€€V€©…ÿ°H0 'Ir ÇÝÛšnF'  +V‡²ÈÎÀ¢|R- %P|ªÍßàÅšj?‚ & 5c—Éèñ⾓c>)#&0=GYp£ƒÀo€Š†ÿ‡i=" ;`Ž»ÚàÏ­„Z8 €‚ 7e•¼Í˵e;ƒ =i™ÃÝæÓ­R) ‚ #J}³ßöñÓ¤qC##5K\y¦ÿƒ~wzyz‡€-„Y9)"4Qy¥ÉØÒ¹•lH)‡"Gsž¾È¿¥~U/‚ 0Uƒ®Îß׺‘e<ƒ3_”Çìøç½‡T* ":Vn—ÝÿƒW‚€z‡€QMYƒ„WC4-''0Bbˆ®Ä˾¡|W6…" 8[£¹º¬lH* $Ah´ÌÏ¿ŸxP+ … #Ft¦ÒìîÑ¡j9 !>aÀÿÿ„–€z‡€fcmƒ¸x]J?4--6Kj¨¹¹¨‰fC"ƒ%!8Uu’¦®¦“wY='  .Lo’¯¾»¨ˆc>ƒ4Zˆ³ÔàØ³M#@lžÿÿ…ð€y†VUVh8ƒÿ©|`N=0))5Lk‰¢¯ª’sP,ƒ%$>[x“¤¨ v\C.  /Mnަ±«”uQ.ƒ (Ho˜ºÍ̺‘_0 BŒÿ‡ÿŠ€Vw„ÿÕ™u^I8+%)6Mgƒ™ ”}]:‚':]œ®±¥w[C0"$$  1Mj‡œ£™‚b?!‚  <]¡¶º­•mC‚Dÿƒ‚$‹€V“„ÿÿ¾oU@0&#(6Jd}ŽŽ‚jK.' ,PzŸ¸Â·ž~`D."%26-"  2Jg‚“”ˆoO1 2PpŒ¢© ‹pN-ƒZ„†X€sŠV€ÿ…Tÿÿ³‚bJ7*#!'4H`vt\C& :b³ÆÇ³‘mL0 $+hƒQ5 2Je{†„tZ@$-Eb}š”‚iP6!‰{€‘Žÿ‡#ÿ·xU=.##2G_r}|n[?& &Hp›»È§‚[:'}- 2JbsztbN5 '>Vp…Ž|dK6%†y{zyz€$D„!ÿdG7)#3G]oxvkV?+!#4Qt–­²§lJ+ˆ&O   "3GXcf_SA1&$*8Latt`I5' !&.:P†$r›€z€spv„ ÿƒVC2%&5I]mtsgUD97BVo…‘‘„nS8ˆ&ÿ4 "/=IPRPH@:8=HVenpiZF4("$*3>Md—„9%M€z€‘Ž“…ÿsQ<-" *:K]jpmdXNJNXenpk_N:&Š%š/#&/8@FIIJKOT[bb\QC4)$%-8FVk‰ÕƒNU=&‚yŽzyz…u‡gB/##/=L[dihd]XWXZXSJ=.Œ$ÿ. $/:DMTX[]\[TJ=1(#$*5DVlˆ¶ÿ„T&%&“€z—s/†ÿU:.($"$*2„ÐR@81-*+-28@JS[_^YOC5& ‘"Ú;$ %5ES]_[QF7*" $-9GXjÂÿÿ„€&’y‚djlq™„ÿeOC;40-,.3;EOVXTK=- ‰ÿ@„ õ9& #1@MVYTJ=.!'5EWk€—Âÿÿ…ÿ€&–VUV^S‡sWH=60,+-3HPRNE9*'9Me~œÇÿ‡ÿš€VjQ"†ÿ‡aNA82.,/55)-B[y™Áÿ„\€$›€VH;8L„ÿ’ePD;51026:>@>80#‡ )&…+5;=??>==>??=92*# $5PošÄÿÿƒi€sšV‚HLYL…ÿdSH@9522468974.#…$AH3)…c\ZVRKE@;97531.++.En €ÿ„Œ›€‘Ÿ>=>L8†ÿ€eWLD<731123441(†ÿ"0+#† ÿ“}qf[PE<51€/0132ŠsŒzyzŸ€>H;"Y„Ƀl^QF<4.,,.39Ca„ çÿ+"0.$Ö„ÿ¶“}kZJ<1+)+/7CgŠ8s“€zŸ>=>HQSdKƒÿ½ŒucRC6,&%)2Bkÿ„ "0$$#U„ÿÿÄš~eO<.%#'1BjˆiŒssv€zž>‚j^eM„ ÿМ~dM9)!1Wÿ…‡.+E „ ÿÿÊ›uW=)"4Xÿ‡\›Œ“‚y¢VUVeP‡ÿ·d.•&%&=‡ÿéy6$s‘zyz§€VgQ*–ÿ9‡&%$š$s‘€zŒ€×-9€/–ȃ ÿÿûÝÀ}Z6‚",57/' —   — !)2:BLÃ4•$ƒ …6.("˜€ €  ˜ &.5 „ (06;==;72*!„   ƒ)18<>>=:5.& ‚  $).4rƒ%•ƒ< ƒ #2@MV\``]XPE7( ƒ  &++& ‚ )9FQY]`_[TK?1# ‚ !'-4{ƒ#•ƒ E ‚ 0EXiu}~wl^K7#‚ ",4760&‚ /BUeqz€|tgXF3 ‚  '.8¨ƒ#•„€ € 8Rj~—››—Ž€nX@(€ !)3>;5.'  …41d`VOF;1#7UrŒ©®¬¥—„lT9 $7IXcp€—Áô¡I0& 3RrŽ£¯°¨—€eI0  'K†(Œƒzyyzzyxvc‚‚(ÿ†o]M?/ 0Lj…˜¥«¨Ÿv\D,/F[lzŠ£ï=1)! $Dhˆ ¯±¨–}_B( !,M‚ÈL‡€zwzy€z‚zy€zŠ&„ÿ¨w\I7& '@]yœ¢ž“~dI2 € !8Um“§Óÿ‚G5(ƒ8]€™©¬¢tU7   '0>„†‡z”½oS?.  5Pl‚’™”‡nR6  #@f‡ »ÖÿƒÿJ0 ƒ0Uz“¤¦œ‡jI+&,5?YŽƒzy€z•´bK:-$+Day‹“Ž~bC'!H‹Ïû€ÿ„º<%‚ .Rw¡£—a? €#08CQªŽzy^\WN;–`K<1%&>\v‹”Ž|^<ƒj‹£7 €0Ty“£¥—~\8 +:ETn 4EKQNÿÃËÿ–ÿ‚bN?/""8Wr‰’yX5ƒ R.  3Uy“¤¥–|X4 3GYwàÿÜÜÞœÿ¤y_K9( 5Tqˆ’ŒvU1ŽƒuD/!5Vy“¤¥–{W2'>Z|Åÿÿ‚ÿ±ˆlU?,5Tpˆ’‹uS/)=ƒŽYB1(!6Ux“¤¦—|W2 0Lqª€ÿˆÿÿÿη®«¬®³·º¹·µ²¬±ÀÞá€ÿÏ«ŒqXA-4Tpˆ‘ŠsQ-ƒ…#ƒ¯pS@2&!4Sv‘£¤–{V2!9Z…Áÿ Û¸¨¶µ³°¯®±¸ÎÿÿŠ ÿü¼¦œ—•”•–—˜—–˜›¡œ¨µ¸¬œ†nW?+ 6Urˆ’‹tS1(…>„#¦w\H:+!"4Ru‘£¦—}X4  &?_„ªÅɼ¶¤—‘€–•””–›¥ºûÿ‰ ᢒ‹‡…„ƒ‚€€'‚‚„†ˆ…Š‘•’‰yeO8%!9Ws‰’‹wX8 1iƒ\ƒ$ÿ›x`M=.##5Ru’¤§™~Z6  '@[w‹——‘ˆ~€€€ƒ…Š’£æƒÿ`hmpr€s-qomlkklmnoprtruz|{uhVB.";Yt‰’{_B) $5M‚ÿ8rƒ$ÿ“vaM>/$$6Sv’¤§™}Z6 %;ReqwwtuqmkmnopqrqpjQ‚:€b:"'7EOW[^^][YWWXY[\]^_`_acdd^RC2 ";Xr‡~fL5&!)5;‚K5R4‚#ù’u`M=/$%7Tu ¢“wU22ESZ^_^^\[YIZZ[[ZWQH91GnaL4!"-8AGKMLIGDCCDEFFGIKJLNNMG<.  #=Zt‡‘…pXC5*%#"$)2ƒ#iƒIÿ˜u^J;-#%7TuŽžŽsQ/ '7BGIIHHGEDEEFGIIHFA:/#(7DYN?-#,27::8520/."/13468871'%@\v‰“’‹ycN@4,($$&*¦u[H9+"%8TsŠ˜—ˆlK+€X )2566431//01468863-$+7@G?4& #'('&$!!""!(B^x‹••~iTE7.("¹tXD5( %7Rp„~cD&>#&'&#! $'))&  +6=82*     ‚)D_x‹”•~iSC4*#«kP>0%$6Oj{„€pV: ‚"   +49,("‚  €€†(C_x‹••|dM:+  Ž `G6* #5Lcqwq`H/ƒ (16%" ƒ %'$ ˆ%@\v‰““‹v[B- |O;,!"3I]giaP9"ƒ!!!#" &.3$! „*5:;71*" † :Vp„ŽŽ…mP4€3‚ÿ ¯ÿ‚¨U<, !2FW][Q?)„(042/+**-00-"‚ $,1*& ƒ$6FRXYUOG>6-# † 1Ley„ƒyaD'‚# 2[‚‰‹sc[^bkxnl[E7) 1DQTOB/ …$5BKNNKGDCA=6)‚ #,161)ƒ)?TfrxxtkaUG7'… *C\p{zoW9‚# !1ESY]WROPRUWQMD7, 1COOG8%…*?Raikid]WOF:+‚ %-3G@6( ƒ)C]t‡’–”Œo\G2… %=TgqpdM0ƒ!.:CGKIGEEFHHEA9/% € 1CNNE5!†,E^r€†…~seWH8' (05[SF5"ƒ %A_{“¤¬«¢“gM5 ƒ #9N_geYC)‚(18;>=<;;<==:71( 1DOPH8$…*Fc~‘›š‘‚nXB/ € %/59qhXC+ƒ8Y{—¬··­šdG. €  #6HV\XL8! ‚").1€3€2330-(! 0DQTM?+† #Ab‚›¨©žŠoQ5 €!*28=>‰€nT5„#1Sw•¬¸·¬•xX:! (8HRTOA/ ‚ "$€&%%€&%# ‚0EU\YM;' =`‚ž®°£ŒlI(€+4HMJ?/‚ €  ‚-F]mtoaL5! €  5Wy—©¬Ÿ†a9 #9LZfmor]ÿU"€ $Cf‚— ™†fA€*3/(!%0;DFB6% Š‚ƒ+E^r|ylX@*€  3St‘¥©ƒ^6*D\p…™³ÿ„L- $@`{–{Z4,72) $-7?A<1!*E`wƒt`G0  1Mlˆœ¡–~Z4€/Kg„ªéÿ…ƒK6' #=\u‰ˆtS0‚*7/$(18;7.Œ‰+Gbz…ƒvaI1‚ -Gd“™yW4 0Mm™êÿ†£dJ9-!#;Xq…†sU4#/$!*142+ ‰   † -Hbzƒp[D. ƒ *C]xŒ“‹uV5 0Ko´ÿ‡+Ä{^I:+#9Vp…ŽŠy]># ! !'++& ƒ "&**)%„ -G`u{ueQ;'ƒ'?Ytˆ‘ŠtU6 1Jvÿˆ²€fRB2$#8Uo…eI0    "'(& ‚ (18=ACC@;3(ƒ .G]nriXD0ƒ&, „ '7ENKA3# …%Xsˆ‘‹vX:"*:R¾‡ÿ–xcPA1$'=\w—“‚fF+ € $*+(!ƒ'FhˆŸ««žˆlO2†   )//*" ƒ'>Yt‰“wY:" (8Q›‡ÿ–wbPA1$'>]y˜’a?" ‡ $)+( ƒ =`‚œ«¬Ÿ†gE'‡  '(%  „&=Wr‡‘‹vX:"(7O«‡ÿ‘s_M>/"'?_z™’~]:‡ $)+( ƒ 4Wy•¥§š€`=‚  $&%  „&>Xt‰“xY;")9Q°‡ÿŒo[J<-!&>]yŽ—{Z7‡ $)*( „ .Oqž “zY7  #&&"ƒ(@ZvŒ–yZ;#,IOI1 %)($ƒ)?Wo€…|fJ0%/=Nhð‡ÿk[M?0! +BWeh_K2‡ $))%ƒ$9NesukW@-#$-1"(2=I[Þ‡ÿ†seWI9(  0EUZSC. ˆ %)($„"4HW\WH7)$)6Jd†³åßF $()$ƒ *4::3''0:EVÒ‡ÿ†seWI9(  0ETXRB- ‡  $(($„"3HV\VH6)$(5Ic„®ÙÍE $()$ƒ (041) (1;FUÒ‡ÿ„qcUH8'  4IW[TC/‚ %))$„ &9N\a[K8*$(4Ga¥È´7 $()$ƒ$-572( (1;ETчÿ„pbTF6&  7LZ^VE0  &*)%„ ,?UdiaO;+$(4F_| ¿¦* $)($ƒ !,6>>8* !+4=HWÙ‡ÿ‡p`QC3" 7M\`YG2  !./€ &**%„ 0D[jofS=,%(4F_| À¥ %)($ƒ *6AHH>-  $0:DO_ï‡-ÿ“uaO?. 2HX^XH3 (7;'  &+*&„0E[jofS=-%)5G`}£È¯ %)($ƒ!1>IPM@,+9DO\mÿˆ,¶€bK9'-DU\WH3  ,PYUF1 ,=D;- "()%„ ) ->GF:(%5<:1$  #!  (;HNJ>/"!-@Xsš†L€ #()%ƒ 04'(:Ph|ƒsL&  "')&ƒ (3:=3! € 6Odx‰š°ÿ‚ÁŠ)12* @ (//)  (263+ "2G[jnbH+!'*'ƒ  )/1(€ 8PfzŒžµÿ‚ÅŠ  '(# @%&!    #+-*$*;LWZQ?*  '*( ƒ $&€ 7Oe{Ž¡»ÿ‚$Ì… €@     $&# -9AC>3%  '*( ƒ  € !7Nd|’©Ìÿ‚$„-       € #  $$"!)/1.'  "(+)!ƒ   3H^z•·òƒ&&'ƒ„8.'   €€  #" €' "&&%" %+-*"ƒ ,?UuŸåÿƒ320ƒL@7/' "#! „ "'(&  €&%)*)'#  ")-.+#ƒ  $2FoÑÿ„€BƒÇ_QF=2( %(&… !(,-)"ƒ%$(*+*($  &,/.*"ƒ &3qƒ€Lƒåm]QE9-" !),+#… $,0/+$„%*-..,)$ ‚ #)-/-)"ƒ ƒÿ9zz€RƒÿzfXK=/" "+/.%ƒ $+/.*#…$)-€/,'  ‚ %),,*& ƒ "#ƒãFyz€Vƒÿ‡o]M=.  !*0/&ƒ  &**&„!&),-.,(" ‚ #&''%!ƒ #*/34ƒñKzz€[ƒÿ }fQ=,%,,& „  "" ƒ  #&()&"‚   ‚  (09@GOƒòSzy€]„ÑlP9&  !')# „  ‚ € #%$ ‚  ‚   $0;GR]lÿƒVzz€\„ÿÅwN2  !# „   ‚   !‚ € ‚ &6ETbp„ÿƒ[zz€[†¥J,  … ƒ    „ € %9L`r„œÿƒd€—‡`,!  …„  ƒ … !$(‚"   ",33,!   …  '6:5+!    !.9EOZlƒ­[€u€€V€©…ÿÝoB,€&  ",45-#     $9@:0%  *:HXfv•ƒÀo€Š†ÿ‡1ÁO8*   "&'"    €‚+53+"1DWn†§ÿƒ~wzyz‡€/„ bJ=2&  ‚ *       ƒ #%"!5Jb€£ÛÿƒW‚€z‡€QNXƒ€aRF9-!  €     € ƒ  "6Mh’Ëÿÿ„–€z‡€fboƒ ¥|iZJ=/ € "#  €„  !4Lm­ÿÿ…ð€y†VUVh8ƒ ÿª‰s]L<+ ƒ‚  $+-)" ‹‚ !4P‚ÿ‡ÿŠ€Vw„ ÿÓ¥ˆlYG5$   *483*! ‘   0Nǃ'€$‹€V“„ ÿÿÉžydQ?. 0DKA3' ‘€  *F„†Y€sŠV€ÿ… ÿÿÇn[I7)‹  ‚=Š£d@.$ …Œ &…žy€‘Žÿ‡ ÿµ…jVC5' Š  ‚ }=,$ ƒ‰!(0:‡…y{zyz€$G„ ÿ¤yaL>1$ ‰   ‰ V5*# ’(4@Me†$r›€z€spu„ ÿ•nUG:." ‰ ‰ ÿD3)$ -:IXk˜„:%M€z€‘Œ”… ÿ‡\NB7,"ˆ‰ C0*% Ž />L^p‡ÃƒOT=&‚yŽzyz…u‡ v[MB7.& ‰Š ÿC5-'   0AQax´ÿ„T&%&“€z—s/† ñhWMA:3* €Š ­E6.%  ‹  0BRctŽ©Óÿ„h€&“€zeRNL>„ ´h[MG?7+  Ž‹ ·F5*$ ˆ .?Rdu†¦Çÿÿ„€&’y‚djiq³„õx]UMC7-" ‹ÿ5„¸@-($   );Ncu‡šÉÿÿ…ÿ€&–VUV^S‡qaTF;0% Š"‡4-*)'%#  3I^xަÊÿƒ€ÿš€VjQ"„ ÿœwcSH=3(  ‡ +„*03€52-(" '=Vn§Çÿ„`€$›€VH;8M„ÿžv`TJ@6-&!†*'…7CGIHD>7/'"!0If‚¬Îýÿƒi‚€sšV‚HLWM„ ÿ–oaVLB90*&%%&€'%ŠDD1*„Tda`]WNE:1*%"!!"&.<\ˆ´€ÿ„š€‘Ÿ>=>L8†ÿ˜yh\PE<4/--/26H;"t„ß‘yi\PE<6347>GZ–„¤‚"0.$ä„ÿ½š„raQC9324;FZšŠ8s“€zŸ>=>HQSeJƒÿ¸Œxi[OE=:;@K_‘ÿ„!‚"0$$#U„ÿÿœƒmZK?99>I]ˆissv€zž>‚j^bN„ ÿ²Ž{i[OFBDM`Žÿ…‡.+D!„ ÿÿÄœgUGABK^ˆÿ†`‚šŒ“‚y¢VUVeP‡ÿÿÁŒ‹¨ÿˆ †&%&=‡ÿÿÖ¥Œ†œðÿ‰$s‘zyz§€VgQ*–ÿ‡&%$š$s‘€zŒ€×-9€/–ȃ ÿÿûÝÀ}Z6‚",57/' —   — !)2:BLÃ4•$ƒ …6.("˜€ €  ˜ &.5 „ (06;==;72*!„   ƒ)18<>>=:5.& ‚  $).4rƒ%•ƒ< ƒ #2@MV\``]XPE7( ƒ  &++& ‚ )9FQY]`_[TK?1# ‚ !'-4{ƒ#•ƒ E ‚ 0EXiu}~wl^K7#‚ ",4760&‚ /BUeqz€|tgXF3 ‚  '.8¨ƒ#•„€ € 8Rj~—››—Ž€nX@(€ !)3>;5.'  …41d`VOF;1#7UrŒ©®¬¥—„lT9 $7IXcp€—Áô¡I0& 3RrŽ£¯°¨—€eI0  'K†(Œƒzyyzzyxvc‚‚(ÿ†o]M?/ 0Lj…˜¥«¨Ÿv\D,/F[lzŠ£ï=1)! $Dhˆ ¯±¨–}_B( !,M‚ÈL‡€zwzy€z‚zy€zŠ&„ÿ¨w\I7& '@]yœ¢ž“~dI2 € !8Um“§Óÿ‚G5(ƒ8]€™©¬¢tU7   '0>„†‡z”½oS?.  5Pl‚’™”‡nR6  #@f‡ »ÖÿƒÿJ0 ƒ0Uz“¤¦œ‡jI+&,5?YŽƒzy€z•´bK:-$+Day‹“Ž~bC'!H‹Ïû€ÿ„º<%‚ .Rw¡£—a? €#08CQªŽzy^\WN;–`K<1%&>\v‹”Ž|^<ƒj‹£7 €0Ty“£¥—~\8 +:ETn 4EKQNÿÃËÿ–ÿ‚bN?/""8Wr‰’yX5ƒ R.  3Uy“¤¥–|X4 3GYwàÿÜÜÞœÿ¤y_K9( 5Tqˆ’ŒvU1ŽƒuD/!5Vy“¤¥–{W2'>Z|Åÿÿ‚ÿ±ˆlU?,5Tpˆ’‹uS/)=ƒŽYB1(!6Ux“¤¦—|W2 0Lqª€ÿˆÿÿÿη®«¬®³·º¹·µ²¬±ÀÞá€ÿÏ«ŒqXA-4Tpˆ‘ŠsQ-ƒ…#ƒ¯pS@2&!4Sv‘£¤–{V2!9Z…Áÿ Û¸¨¶µ³°¯®±¸ÎÿÿŠ ÿü¼¦œ—•”•–—˜—–˜›¡œ¨µ¸¬œ†nW?+ 6Urˆ’‹tS1(…>„#¦w\H:+!"4Ru‘£¦—}X4  &?_„ªÅɼ¶¤—‘€–•””–›¥ºûÿ‰ ᢒ‹‡…„ƒ‚€€'‚‚„†ˆ…Š‘•’‰yeO8%!9Ws‰’‹wX8 1iƒ\ƒ$ÿ›x`M=.##5Ru’¤§™~Z6  '@[w‹——‘ˆ~€€€ƒ…Š’£æƒÿ`hmpr€s-qomlkklmnoprtruz|{uhVB.";Yt‰’{_B) $5M‚ÿ8rƒ$ÿ“vaM>/$$6Sv’¤§™}Z6 %;ReqwwtuqmkmnopqrqpjQ‚:€b:"'7EOW[^^][YWWXY[\]^_`_acdd^RC2 ";Xr‡~fL5&!)5;‚K5R4‚#ù’u`M=/$%7Tu ¢“wU22ESZ^_^^\[YIZZ[[ZWQH91GnaL4!"-8AGKMLIGDCCDEFFGIKJLNNMG<.  #=Zt‡‘…pXC5*%#"$)2ƒ#iƒIÿ˜u^J;-#%7TuŽžŽsQ/ '7BGIIHHGEDEEFGIIHFA:/#(7DYN?-#,27::8520/."/13468871'%@\v‰“’‹ycN@4,($$&*¦u[H9+"%8TsŠ˜—ˆlK+€X )2566431//01468863-$+7@G?4& #'('&$!!""!(B^x‹••~iTE7.("¹tXD5( %7Rp„~cD&>#&'&#! $'))&  +6=82*     ‚)D_x‹”•~iSC4*#«kP>0%$6Oj{„€pV: ‚"   +49,("‚  €€†(C_x‹••|dM:+  Ž `G6* #5Lcqwq`H/ƒ (16%" ƒ %'$ ˆ%@\v‰““‹v[B- |O;,!"3I]giaP9"ƒ!!!#" &.3$! „*5:;71*" † :Vp„ŽŽ…mP4€3‚ÿ ¯ÿ‚¨U<, !2FW][Q?)„(042/+**-00-"‚ $,1*& ƒ$6FRXYUOG>6-# † 1Ley„ƒyaD'‚# 2[‚‰‹sc[^bkxnl[E7) 1DQTOB/ …$5BKNNKGDCA=6)‚ #,161)ƒ)?TfrxxtkaUG7'… *C\p{zoW9‚# !1ESY]WROPRUWQMD7, 1COOG8%…*?Raikid]WOF:+‚ %-3G@6( ƒ)C]t‡’–”Œo\G2… %=TgqpdM0ƒ!.:CGKIGEEFHHEA9/% € 1CNNE5!†,E^r€†…~seWH8' (05[SF5"ƒ %A_{“¤¬«¢“gM5 ƒ #9N_geYC)‚(18;>=<;;<==:71( 1DOPH8$…*Fc~‘›š‘‚nXB/ € %/59qhXC+ƒ8Y{—¬··­šdG. €  #6HV\XL8! ‚").1€3€2330-(! 0DQTM?+† #Ab‚›¨©žŠoQ5 €!*28=>‰€nT5„#1Sw•¬¸·¬•xX:! (8HRTOA/ ‚ "$€&%%€&%# ‚0EU\YM;' =`‚ž®°£ŒlI(€+4HMJ?/‚ €  ‚-F]mtoaL5! €  5Wy—©¬Ÿ†a9 #9LZfmor]ÿU"€ $Cf‚— ™†fA€*3/(!%0;DFB6% Š‚ƒ+E^r|ylX@*€  3St‘¥©ƒ^6*D\p…™³ÿ„L- $@`{–{Z4,72) $-7?A<1!*E`wƒt`G0  1Mlˆœ¡–~Z4€/Kg„ªéÿ…ƒK6' #=\u‰ˆtS0‚*7/$(18;7.Œ‰+Gbz…ƒvaI1‚ -Gd“™yW4 0Mm™êÿ†£dJ9-!#;Xq…†sU4#/$!*142+ ‰   † -Hbzƒp[D. ƒ *C]xŒ“‹uV5 0Ko´ÿ‡+Ä{^I:+#9Vp…ŽŠy]># ! !'++& ƒ "&**)%„ -G`u{ueQ;'ƒ'?Ytˆ‘ŠtU6 1Jvÿˆ²€fRB2$#8Uo…eI0    "'(& ‚ (18=ACC@;3(ƒ .G]nriXD0ƒ&, „ '7ENKA3# …%Xsˆ‘‹vX:"*:R¾‡ÿ–xcPA1$'=\w—“‚fF+ € $*+(!ƒ'FhˆŸ««žˆlO2†   )//*" ƒ'>Yt‰“wY:" (8Q›‡ÿ–wbPA1$'>]y˜’a?" ‡ $)+( ƒ =`‚œ«¬Ÿ†gE'‡  '(%  „&=Wr‡‘‹vX:"(7O«‡ÿ‘s_M>/"'?_z™’~]:‡ $)+( ƒ 4Wy•¥§š€`=‚  $&%  „&>Xt‰“xY;")9Q°‡ÿŒo[J<-!&>]yŽ—{Z7‡ $)*( „ .Oqž “zY7  #&&"ƒ(@ZvŒ–yZ;#,IOI1 %)($ƒ)?Wo€…|fJ0%/=Nhð‡ÿk[M?0! +BWeh_K2‡ $))%ƒ$9NesukW@-#$-1"(2=I[Þ‡ÿ†seWI9(  0EUZSC. ˆ %)($„"4HW\WH7)$)6Jd†³åßF $()$ƒ *4::3''0:EVÒ‡ÿ†seWI9(  0ETXRB- ‡  $(($„"3HV\VH6)$(5Ic„®ÙÍE $()$ƒ (041) (1;FUÒ‡ÿ„qcUH8'  4IW[TC/‚ %))$„ &9N\a[K8*$(4Ga¥È´7 $()$ƒ$-572( (1;ETчÿ„pbTF6&  7LZ^VE0  &*)%„ ,?UdiaO;+$(4F_| ¿¦* $)($ƒ !,6>>8* !+4=HWÙ‡ÿ‡p`QC3" 7M\`YG2  !./€ &**%„ 0D[jofS=,%(4F_| À¥ %)($ƒ *6AHH>-  $0:DO_ï‡-ÿ“uaO?. 2HX^XH3 (7;'  &+*&„0E[jofS=-%)5G`}£È¯ %)($ƒ!1>IPM@,+9DO\mÿˆ,¶€bK9'-DU\WH3  ,PYUF1 ,=D;- "()%„ ) ->GF:(%5<:1$  #!  (;HNJ>/"!-@Xsš†L€ #()%ƒ 04'(:Ph|ƒsL&  "')&ƒ (3:=3! € 6Odx‰š°ÿ‚ÁŠ)12* @ (//)  (263+ "2G[jnbH+!'*'ƒ  )/1(€ 8PfzŒžµÿ‚ÅŠ  '(# @%&!    #+-*$*;LWZQ?*  '*( ƒ $&€ 7Oe{Ž¡»ÿ‚$Ì… €@     $&# -9AC>3%  '*( ƒ  € !7Nd|’©Ìÿ‚$„-       € #  $$"!)/1.'  "(+)!ƒ   3H^z•·òƒ&&'ƒ„8.'   €€  #" €' "&&%" %+-*"ƒ ,?UuŸåÿƒ320ƒL@7/' "#! „ "'(&  €&%)*)'#  ")-.+#ƒ  $2FoÑÿ„€BƒÇ_QF=2( %(&… !(,-)"ƒ%$(*+*($  &,/.*"ƒ &3qƒ€Lƒåm]QE9-" !),+#… $,0/+$„%*-..,)$ ‚ #)-/-)"ƒ ƒÿ9zz€RƒÿzfXK=/" "+/.%ƒ $+/.*#…$)-€/,'  ‚ %),,*& ƒ "#ƒãFyz€Vƒÿ‡o]M=.  !*0/&ƒ  &**&„!&),-.,(" ‚ #&''%!ƒ #*/34ƒñKzz€[ƒÿ }fQ=,%,,& „  "" ƒ  #&()&"‚   ‚  (09@GOƒòSzy€]„ÑlP9&  !')# „  ‚ € #%$ ‚  ‚   $0;GR]lÿƒVzz€\„ÿÅwN2  !# „   ‚   !‚ € ‚ &6ETbp„ÿƒ[zz€[†¥J,  … ƒ    „ € %9L`r„œÿƒd€—‡`,!  …„  ƒ … !$(‚"   ",33,!   …  '6:5+!    !.9EOZlƒ­[€u€€V€©…ÿÝoB,€&  ",45-#     $9@:0%  *:HXfv•ƒÀo€Š†ÿ‡1ÁO8*   "&'"    €‚+53+"1DWn†§ÿƒ~wzyz‡€/„ bJ=2&  ‚ *       ƒ #%"!5Jb€£ÛÿƒW‚€z‡€QNXƒ€aRF9-!  €     € ƒ  "6Mh’Ëÿÿ„–€z‡€fboƒ ¥|iZJ=/ € "#  €„  !4Lm­ÿÿ…ð€y†VUVh8ƒ ÿª‰s]L<+ ƒ‚  $+-)" ‹‚ !4P‚ÿ‡ÿŠ€Vw„ ÿÓ¥ˆlYG5$   *483*! ‘   0Nǃ'€$‹€V“„ ÿÿÉžydQ?. 0DKA3' ‘€  *F„†Y€sŠV€ÿ… ÿÿÇn[I7)‹  ‚=Š£d@.$ …Œ &…žy€‘Žÿ‡ ÿµ…jVC5' Š  ‚ }=,$ ƒ‰!(0:‡…y{zyz€$G„ ÿ¤yaL>1$ ‰   ‰ V5*# ’(4@Me†$r›€z€spu„ ÿ•nUG:." ‰ ‰ ÿD3)$ -:IXk˜„:%M€z€‘Œ”… ÿ‡\NB7,"ˆ‰ C0*% Ž />L^p‡ÃƒOT=&‚yŽzyz…u‡ v[MB7.& ‰Š ÿC5-'   0AQax´ÿ„T&%&“€z—s/† ñhWMA:3* €Š ­E6.%  ‹  0BRctŽ©Óÿ„h€&“€zeRNL>„ ´h[MG?7+  Ž‹ ·F5*$ ˆ .?Rdu†¦Çÿÿ„€&’y‚djiq³„õx]UMC7-" ‹ÿ5„¸@-($   );Ncu‡šÉÿÿ…ÿ€&–VUV^S‡qaTF;0% Š"‡4-*)'%#  3I^xަÊÿƒ€ÿš€VjQ"„ ÿœwcSH=3(  ‡ +„*03€52-(" '=Vn§Çÿ„`€$›€VH;8M„ÿžv`TJ@6-&!†*'…7CGIHD>7/'"!0If‚¬Îýÿƒi‚€sšV‚HLWM„ ÿ–oaVLB90*&%%&€'%ŠDD1*„Tda`]WNE:1*%"!!"&.<\ˆ´€ÿ„š€‘Ÿ>=>L8†ÿ˜yh\PE<4/--/26H;"t„ß‘yi\PE<6347>GZ–„¤‚"0.$ä„ÿ½š„raQC9324;FZšŠ8s“€zŸ>=>HQSeJƒÿ¸Œxi[OE=:;@K_‘ÿ„!‚"0$$#U„ÿÿœƒmZK?99>I]ˆissv€zž>‚j^bN„ ÿ²Ž{i[OFBDM`Žÿ…‡.+D!„ ÿÿÄœgUGABK^ˆÿ†`‚šŒ“‚y¢VUVeP‡ÿÿÁŒ‹¨ÿˆ †&%&=‡ÿÿÖ¥Œ†œðÿ‰$s‘zyz§€VgQ*–ÿ‡&%$š$s‘€zt8mk@          *38:::75455555555555555555555458<><6,,6<><85455555555555555555555457:::83*  (=PblponjfceeeeeeeeeeeeeeeeeeeecfkqtrhV?%%?VhrtqkfceeeeeeeeeeeeeeeeeeeecfjnoplbP=( =\y”¢¨§¦ ™–™™™™™™™™™™™™™™™™™™™™–𠍬©œ„eD((De„œ©¬¨ š–™™™™™™™™™™™™™™™™™™™™–™ ¦§¨¢”y\= *PyŸÃÖÝÜÛÒÊÅÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÅÊÒÚßÜͲŽhI66Ih޲ÍÜßÚÒÊÅÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÅÊÒÛÜÝÖßyP*2`‘¿ëÿÿÿÿÿøò÷÷ööööööööööööööööööó÷ÿÿÿÿúß»”s`_p·Û÷ÿÿÿÿøó÷÷öööööööööööööööö÷÷òøÿÿÿÿÿë¿‘`29l£×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÛ»Ÿ ¼Ýüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×£l9 >u°æÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôÝËÂÄÑæþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæ°u>  Az¶íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùñïôýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿí¶zA  B{¶ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿí¶zA  ?w±æÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæ±w?  9m¤ØÿÿÿÿÿÿÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþùÿÿÿÿÿÿÿÖ¤n: /]Âòÿÿÿÿÿÿýýúø÷øùúúúúúúúúùøøøøûþÿÿÿÿÿÿÿÿÿÿÿÿÿüúûþÿÿÿÿÿÿýúø÷øùúúúúúúúúúúúúúûöüÿÿÿÿÿì¿_2"Hu£Õ÷ÿÿÿÿÿÿÿÿüûüýþÿþþþþÿþýüüüýÿÿÿÿÿÿÿÿÿÿÿôãÕËÈÍ×ç÷ÿÿÿÿÿÿÿüûüýþÿþþþþþÿÿÿþÿÿÿþÿÿÿÿúéÌ uK%0U±Øöÿÿÿÿÿÿÿýúüýþÿþþþþÿþýüüüýÿÿÿÿÿÿÿÿÿÿìΰ™‹‹–¬ÈåþÿÿÿÿÿÿýúüýþÿþþþþÿþþþüÿÿÿÿÿÿÿùæÌª|U23W‡±ÖôÿÿÿÿÿÿþúýþþÿþþþþÿþþýýýýÿÿÿÿÿÿÿÿÿðÌ¢{[JK]|£ËîÿÿÿÿÿÿþúýþþÿþþþþÿþþýúþÿÿÿÿÿÿñÓ¯†V3/[…®Ó÷ÿÿÿÿÿÿüÿÿÿþþþþþþÿÿÿÿÿÿþùüÿÿÿÿÿïÏ¥vI&'Ky¨Òòÿÿÿÿÿÿüÿÿÿþþþþþÿþýýøýÿÿÿÿÿÿê”d3 0U~§ÒîÿÿÿÿÿÿÿÿÿþþþþþþÿÿÿÿÿÿýðêçåÝÙоŸxL Gw¦Îëýÿÿÿÿÿÿÿÿþþþþþÿþýü÷ýÿÿÿÿÿÿâ²|H &Kv¦Ëæùÿÿÿÿÿÿÿýþþþþýÿÿÿÿÿÿûæ×ɽ¬£™ŠoP+!N|©Ìçùÿÿÿÿÿÿÿýþþþþÿþýü÷þÿÿÿÿÿÿÖ f/Bv£Êèÿÿÿÿÿÿÿûþþþþüÿÿÿÿÿÿúܨ‘vh^S?+(R«Îêÿÿÿÿÿÿÿüþþþþÿþþþùÿÿÿÿÿÿ÷ŠPI|­×þÿÿÿÿÿÿúþþþþûÿÿÿÿÿÿøÓ®‰fC0&  -]жÜÿÿÿÿÿÿÿûþþþþþÿÿÿüÿÿÿÿÿüܦo9%]•Éùÿÿÿÿÿÿúþþþþúÿÿÿÿÿÿ÷Êœl> :lŸÎùÿÿÿÿÿÿúþþþþýÿÿÿÿÿÿÿÿöݸ‚P!K‡ÀõÿÿÿÿÿÿùþþþþúÿÿÿÿÿÿöÆ’[(([’Åõÿÿÿÿÿÿúþþþþüÿÿÿÿÿÿÿóܼ“_4J…¾óÿÿÿÿÿÿùþþþþùÿÿÿÿÿÿöÄŽU!TŒÁóÿÿÿÿÿÿùþþþþûÿÿÿÿÿÿÿåÅŸuE!   *ZÃóÿÿÿÿÿÿúþþþþùÿÿÿÿÿÿöÄV #UŒÁóÿÿÿÿÿÿùþþþþûÿÿÿÿÿÿýÚ¶Že:  %3:=<<8545567;6,!*Nw£ÍõÿÿÿÿÿÿúþþþþùÿÿÿÿÿÿöÅW!$VÂôÿÿÿÿÿÿùþþþþûÿÿÿÿÿÿùײ‹eB.%$$,38358<<=:3%2Kamrqqkfceeffif_VSPTb}›¼ÛøÿÿÿÿÿÿûþþþþúÿÿÿÿÿÿöÇ”^+,^“ÆõÿÿÿÿÿÿúþþþþûÿÿÿÿÿÿøÙ¹˜y^QNRV_fjdgkpqrmaK23Uu“¤ª©¨ š–™™™™›˜”Ž‘›­ÀÕéûÿÿÿÿÿÿýþþþþúÿÿÿÿÿÿöɘd44d˜Éöÿÿÿÿÿÿúþþþþüÿÿÿÿÿÿøáɰ›ˆ‚‡•›˜› §©ª¤“uU30U| ÃÕÝÛÚÒÊÅÉÉÉÉÉÈÇÅÅÆÉÎØáëõýÿÿÿÿÿÿþþþþþúÿÿÿÿÿÿöÊ›h9  9h›Ê÷ÿÿÿÿÿÿúþþþþýÿÿÿÿÿÿúìÝθ¶¸½ÁÇËÍÈËÒÚÛÝÕà|U00)#!);^‚©Îðÿÿÿÿÿ÷óööööööööööö÷øúûýþÿÿÿÿÿÿþþþþþúÿÿÿÿÿÿöÊ›h9   9h›ÊöÿÿÿÿÿÿúþþþþþÿÿÿÿÿÿüöðëçæçêïóöøùõøÿÿÿÿÿðΩ‚^;)!#$,a[UTZk‰©Ëëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýýþþÿþþþþúÿÿÿÿÿÿöɘe44e˜ÉöÿÿÿÿÿÿúþþþþÿþþþþþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëË©‰kZTUV^•‘Œ‘³ÌåýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüüüýþÿþþþþúÿÿÿÿÿÿöÇ”^,,^”Çöÿÿÿÿÿÿúþþþþÿþýüüüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýå̳‘Œ“ÇÄÂÁÄËÙèøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüüüýþÿþþþþùÿÿÿÿÿÿöÄX##XÄöÿÿÿÿÿÿùþþþþÿþýüüüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøèÙËÄÁÂÂÆñïîîïóùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüüýýþÿþþþþùÿÿÿÿÿÿöÄW""WÄöÿÿÿÿÿÿùþþþþÿþýýüüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýöóððïðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýýýþþÿþþþþùÿÿÿÿÿÿöÄŽV!!VŽÄöÿÿÿÿÿÿùþþþþÿþþýýýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþüüýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþþþÿþþþþúÿÿÿÿÿÿöÆ’\((\’Æöÿÿÿÿÿÿúþþþþÿþþþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû÷õõõøúûúúúúúúúùùùúúûüýþþÿÿÿÿÿÿþþþþþúÿÿÿÿÿÿ÷Ëœj; ;jœË÷ÿÿÿÿÿÿúþþþþþÿÿÿÿÿÿþþýüûúúùùùúúúúúù÷ø÷÷ùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüúúúüþÿþþþþþÿÿÿÿÿÿþýüûûûûüýþÿÿÿÿÿÿþûÿÿÿÿÿÿøÔ®…_;(!"#,4994,#"!(;_…®ÔøÿÿÿÿÿÿûþÿÿÿÿÿÿþýüûûûûüýþÿÿÿÿÿÿþÿþþýýýüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüûûûýþÿþþþþÿþþþþþþÿÿÿÿÿÿÿÿÿÿþþþþþþÿüÿÿÿÿÿÿúÞÁ¢…j\VWX^ehhe^XWV\j…¢ÁÞúÿÿÿÿÿÿüÿþþþþþþÿÿÿÿÿÿÿÿÿÿþþþþþþÿÿþþýþýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýýþþÿþþþþÿþþýýýþÿÿÿÿÿÿÿÿÿÿþýýýþþÿýÿÿÿÿÿÿûéÖÁ®œ’Ž”˜››˜”Ž’œ®ÁÖéûÿÿÿÿÿÿýÿþþýýýþÿÿÿÿÿÿÿÿÿÿþýýýþþÿÿþþýýýýþÿÿÿÿÿÿÿÿÿÿññððñòô÷ùüþÿÿÿÿÿÿþþþþþÿþýýüüþÿÿÿÿÿÿÿÿÿÿþüüýýþÿþÿÿÿÿÿÿýóéÞÔËÆÄÄÄÇÉÊÊÉÇÄÄÄÆËÔÞéóýÿÿÿÿÿÿþÿþýýüüþÿÿÿÿÿÿÿÿÿÿþüüýýþÿÿþþýüýÿÿÿÿÿÿÿÿÿúôðÈÆÄÄÆËÔÞéóýÿÿÿÿÿÿþþþþþÿþýüüüþÿÿÿÿÿÿÿÿÿÿþüüüýþÿþÿÿÿÿÿÿþýûúø÷öööööööööööööö÷øúûýþÿÿÿÿÿÿþÿþýüüüþÿÿÿÿÿÿÿÿÿÿþüüüýþÿÿþýüûüÿÿÿÿÿÿÿùêÜÎÅ–’Ž’œ®ÁÖéûÿÿÿÿÿÿýþþþþÿþýüüüþÿÿÿÿÿÿÿÿÿÿþüüüýþÿÿþþýýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýýýýþþÿÿþýüüüþÿÿÿÿÿÿÿÿÿÿþüüüýþÿÿþýüúýÿÿÿÿÿÿýçζŸ’a\WV\j…¢ÁÞúÿÿÿÿÿÿüþþþþÿþþýýýþÿÿÿÿÿÿÿÿÿÿþýýýþþÿÿþýüüüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüüüýþÿÿþþýýýþÿÿÿÿÿÿÿÿÿÿþýýýþþÿÿþþýúþÿÿÿÿÿÿëÌ«Šl[0("!(;_…®ÔøÿÿÿÿÿÿûþþþþþÿÿÿÿÿÿþüúùøøùúüþÿÿÿÿÿÿþÿþýüüüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüüüýþÿþÿÿÿÿÿÿþüúùøøùúüþÿÿÿÿÿÿþþÿÿÿüÿÿÿÿÿÿòΩ]:(;jœË÷ÿÿÿÿÿÿúþþþþþÿÿÿÿÿÿøîäÛÖÖÛäîøÿÿÿÿÿÿþÿþýýüüýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüüýýþÿþÿÿÿÿÿÿøîäÛÖÖÛäîøÿÿÿÿÿÿþþÿÿÿÿÿÿÿÿýë΢xO+ (\’ÆöÿÿÿÿÿÿúþþþþýÿÿÿÿÿÿòàÌ»±±»ÌàòÿÿÿÿÿÿýÿþþýýýþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþýýýþþÿýÿÿÿÿÿÿòàÌ»±±»ÌàòÿÿÿÿÿÿýýÿÿÿÿÿÿÿùçÌ©xO) !VŽÄöÿÿÿÿÿÿùþþþþüÿÿÿÿÿÿìв™ŠŠ™²Ðìÿÿÿÿÿÿüÿþþþþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþþþþÿüÿÿÿÿÿÿìв™ŠŠ™²ÐìÿÿÿÿÿÿüüÿÿÿÿÿÿÿêΫO) "WÄöÿÿÿÿÿÿùþþþþûÿÿÿÿÿÿæÁšyffyšÁæÿÿÿÿÿÿûþÿÿÿÿÿÿþþýüûúúùùùúúúúúúùùùúúûüýþþÿÿÿÿÿÿþûÿÿÿÿÿÿæÁšyffyšÁæÿÿÿÿÿÿûûÿÿÿÿÿÿÿܶŠ]+ #XÄöÿÿÿÿÿÿùþþþþúÿÿÿÿÿÿ᳃ZCCZƒ³áÿÿÿÿÿÿúþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþúÿÿÿÿÿÿ᳃ZCCZƒ³áÿÿÿÿÿÿúúÿÿÿÿÿÿùΟl: ,^”ÇöÿÿÿÿÿÿúþþþþúÿÿÿÿÿÿÞ¬wJ11Jw¬ÞÿÿÿÿÿÿúþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþúÿÿÿÿÿÿÞ¬wJ11Jw¬ÞÿÿÿÿÿÿúúÿÿÿÿÿÿõÅ’[(4e˜ÉöÿÿÿÿÿÿúþþþþùÿÿÿÿÿÿÝ©rD**Dr©ÝÿÿÿÿÿÿùþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþùÿÿÿÿÿÿÝ©rD**Dr©ÝÿÿÿÿÿÿùùÿÿÿÿÿÿóÁŒT! 9h›ÊöÿÿÿÿÿÿúþþþþùÿÿÿÿÿÿݪsE++EsªÝÿÿÿÿÿÿùþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþùÿÿÿÿÿÿݪsE++EsªÝÿÿÿÿÿÿùùÿÿÿÿÿÿóÁŒU#5e™ÉöÿÿÿÿÿÿúþþþþùÿÿÿÿÿÿݪtF,,FtªÝÿÿÿÿÿÿúÿÿÿþýüûûûûüýþÿÿÿÿÿÿþþÿÿÿÿÿÿþýüûûûûüýþÿÿÿúÿÿÿÿÿÿݪtF,,FtªÝÿÿÿÿÿÿùùÿÿÿÿÿÿôÂV$5e™Éöÿÿÿÿÿÿúþþþþúÿÿÿÿÿÿß­yM55My­ßÿÿÿÿÿÿùþþþÿÿÿÿÿÿÿÿÿÿþþþþþþÿÿþþþþþþÿÿÿÿÿÿÿÿÿÿþþþùÿÿÿÿÿÿß­yM55My­ßÿÿÿÿÿÿúúÿÿÿÿÿÿõÆ“^,5e™Éöÿÿÿÿÿÿúþþþþúÿÿÿÿÿÿà±~T<>>222&&&ðûÿ¤  €€€ÿÿÿÿÿÿÿÿÿÿÿÿñðîòò--ðð--ôô+-+-+ô,+-*++-,í*-* !'!-*-*íñ'õ'ïñ&ñ!'ô'ñó&ô' ñ&ñ!'ó&óò%ô -%- ó&òò%ô&! $!!&ó%ñò$õ&õõóõõ&ó%òó%-òååò-%ôìððõòææòõñðìî$óêóðó!ò!õòõ!ò!òÿÿÿÿóŸÿÿáÿÿÀÿÿÀÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿ€ÿÿÿÿÿÿÀÿÿÀÿÿÿÿÿÿ( @€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿp€€p€pppp€€p‘‘ €™™™‘ ™‘‘‘p™‘‘‘p€™‘ˆ‘‘€€™‘ˆ‘™€™€™ ‘ˆ‘™€™€™ ‘ˆ‘™€™€™‘‘™€™€‘‘‘™€™€™™ ‘™€™€ ‘™™€™™€€™€™™€™pp™ˆˆˆ€™€ˆˆ€€™‘™€p‘x™‘p ™‘€™™pÿÿÿÿÿ‡ðÿÿÀþ€?üøððàààÀÀÀÀÀÀÀÀÀ€Àà€àÿà?ÿÀ?ü€üüüÿÿÿÿ( @€€€€€€€€€€ÀÀÀÀÜÀðʦÔðÿ±âÿŽÔÿkÆÿH¸ÿ%ªÿªÿ’Üz¹b–Js2PÔãÿ±ÇÿŽ«ÿkÿHsÿ%WÿUÿIÜ=¹1–%sPÔÔÿ±±ÿŽŽÿkkÿHHÿ%%ÿþܹ–sPãÔÿDZÿ«ŽÿkÿsHÿW%ÿUÿIÜ=¹1–%sPðÔÿâ±ÿÔŽÿÆkÿ¸Hÿª%ÿªÿ’Üz¹b–Js2PÿÔÿÿ±ÿÿŽÿÿkÿÿHÿÿ%ÿþþÜܹ¹––ssPPÿÔðÿ±âÿŽÔÿkÆÿH¸ÿ%ªÿªÜ’¹z–bsJP2ÿÔãÿ±ÇÿŽ«ÿkÿHsÿ%WÿUÜI¹=–1s%PÿÔÔÿ±±ÿŽŽÿkkÿHHÿ%%þܹ–sPÿãÔÿDZÿ«ŽÿkÿsHÿW%ÿUÜI¹=–1s%PÿðÔÿâ±ÿÔŽÿÆkÿ¸Hÿª%ÿªÜ’¹z–bsJP2ÿÿÔÿÿ±ÿÿŽÿÿkÿÿHÿÿ%þþÜܹ¹––ssPPðÿÔâÿ±ÔÿŽÆÿk¸ÿHªÿ%ªÿ’Üz¹b–Js2PãÿÔÇÿ±«ÿŽÿksÿHWÿ%UÿIÜ=¹1–%sPÔÿÔ±ÿ±ŽÿŽkÿkHÿH%ÿ%þܹ–sPÔÿã±ÿÇŽÿ«kÿHÿs%ÿWÿUÜI¹=–1s%PÔÿð±ÿâŽÿÔkÿÆHÿ¸%ÿªÿªÜ’¹z–bsJP2Ôÿÿ±ÿÿŽÿÿkÿÿHÿÿ%ÿÿþþÜܹ¹––ssPPòòòæææÚÚÚÎÎζ¶¶ªªªžžž’’’†††zzznnnbbbVVVJJJ>>>222&&&ðûÿ¤  €€€ÿÿÿÿÿÿÿÿÿÿÿÿîóñìóñï--ó--ìñ+---ó----ìï+--'-ó+--+-ì++-+-óó++-'ó-'-óó'--'-ñ'+ìï++ó-'-++ó-'--'-----'++'----'+ìï''+''-+''''''-+'óñ-''-''++'''+''-+'-ó-'+ó-+'-ó+''+ñï-''-+'+ì+%+ï-''--'%-ì-''--%+ìï'%+ó-''+-%%+ì-''--%'ñî'%+''+-'%+ì+''--''ñï'%+-''+-%%-ì-''-%%-ñï%$+-''+-%$-ó-''--%$-ñî%$+''+$$+-''--%$-óî%$ñó-'ó+'$%%+-î'-%$+ñï%$+-'ó---ó--%%--%$-ó-%$+ï-%'-%%-ó-$$+ìì+$ç%-'%+óñññó-%++$$'-ñ+'î''+-î$+ó-%ó-+'î'î+-óï%$îóï+$îñììììììï%$+óï-$$-óìììììì-%$+ï-$$-óñ-%$++$$-ïïó$$îóìó+$$ñó-'$$%--ïó-%$$'-ó--+++++---++++--óïóóñÿÿÿÿÿñÿÿàþÀ?ü€?øø ððààààƒÀ„ƒÀ„ƒÀ„ƒÀ€ƒÀ€ƒÀ€ƒÀ€ƒÀààÀàÿàÿà?þÀ?üüü€ÿÿÿÿengine/resource/hexenworld.png000066400000000000000000000015101444734033100170370ustar00rootroot00000000000000‰PNG  IHDR szzôIDATxœÍW=Nä0þQr€¡F~{-Q¶‚:²¶›Ž2 œŽP-õ* SQ¤„9ÕJx˜ž Mé-âçyqìÑ6|’•Œýò½Û|Uh­-€ÑADÜstdYfczf#6ØÜ½œG[Kñ;%Ûºç_"cÆôõÁ^倵JY»Xt£,mîæX­µíÉ–åNV)/ÇÑ q2À3ÓZc àÛlgøåv‹¥ó¨ª*4M33ÆÌˆKw··^öÇñ±Ò^ÞKdYÖó¬Þå5[>2J)þd§§§“ÕZûœ3ˆMÓ$½?ä—ªª¢V¾½½õŠI1“ ‹qßÜÜÌØêO["EQ$ JkíSPŤÖD?Ÿžð+ xµ3 ›_¯q''¸»¾ö²9€ßÏÏ=®\¼·pÅçà#ðññ‘²¥GP ¢0¬S!#0Ü ÛÖ`·³9Ù‹‹‹þ¼À2•HÖ@” „˳<‚cGwβm;àM‡5¬°¶¡ºð,“íÀív·^ûEc “¬§ÀE ©4sËnP­ ºí54Œˆ¬÷§mq`µZ ŒuèdÛ fú³_©Þ®'çj¹½º9)+çó`ŽwF©³ù|-8™½Ïü¸R*¹–,æÞã\D@Þy½¼‹ÉBÈBœIðý€É™D^½C÷‘„þ‡áý? Œ*qGí~Þ3øŽzÆÊj÷$"ïmJ– 4u‹JB¦"Ì}NiD1TÊî#å#„òFÊN} ‰1å)#þ[9ƒo©?˜UUM–ýøE„–|íöEIEND®B`‚engine/resource/hexenworld.xbm000066400000000000000000000016601444734033100170470ustar00rootroot00000000000000/* uHexen2 xbm format icon - Hexen2World */ #define hx2icon_width 32 #define hx2icon_height 32 static const unsigned char hx2icon_bits[] = { 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xfe, 0x07, 0xc0, 0xff, 0xff, 0x07, 0xc0, 0xeb, 0x9e, 0x07, 0x80, 0x73, 0xdc, 0x03, 0x00, 0x37, 0x98, 0x03, 0x00, 0x3a, 0xd8, 0x01, 0x54, 0x37, 0x98, 0x2b, 0xff, 0x2b, 0xd8, 0xff, 0xef, 0x37, 0xd8, 0xeb, 0xce, 0xff, 0xff, 0xfb, 0xdc, 0xfa, 0xbf, 0x33, 0xec, 0xfe, 0xde, 0x36, 0xd8, 0x7a, 0xdd, 0x36, 0xec, 0x7e, 0xff, 0x3a, 0xcc, 0xfe, 0xfe, 0x36, 0xfc, 0x56, 0xb3, 0x1a, 0xd8, 0xde, 0xf6, 0x3e, 0xfc, 0x5e, 0xf3, 0x3a, 0xd8, 0xde, 0xf6, 0x1a, 0xf8, 0xff, 0xdb, 0x3f, 0xf8, 0xfb, 0xff, 0x1f, 0xf0, 0xff, 0xff, 0x1f, 0xf0, 0xbf, 0xff, 0x0f, 0xf0, 0xff, 0xdf, 0x0d, 0xe0, 0xfb, 0xdf, 0x0f, 0xc0, 0xff, 0xfe, 0x07, 0x80, 0x7f, 0xfe, 0x03, 0x80, 0x3f, 0xfc, 0x01, 0x00, 0x1e, 0xf8, 0x01, 0x00, 0x0a, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00 }; engine/resource/hw_os2.ico000066400000000000000000000127101444734033100160530ustar00rootroot00000000000000BA\òCINH @ ÿÿÿCINÈ @€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿBA\¤CINH @ ÿÿÿCINÈ @€€€€€€€€€ÀÀÀÀÜÀðʦÔðÿ±âÿŽÔÿkÆÿH¸ÿ%ªÿªÿ’Üz¹b–Js2PÔãÿ±ÇÿŽ«ÿkÿHsÿ%WÿUÿIÜ=¹1–%sPÔÔÿ±±ÿŽŽÿkkÿHHÿ%%ÿþܹ–sPãÔÿDZÿ«ŽÿkÿsHÿW%ÿUÿIÜ=¹1–%sPðÔÿâ±ÿÔŽÿÆkÿ¸Hÿª%ÿªÿ’Üz¹b–Js2PÿÔÿÿ±ÿÿŽÿÿkÿÿHÿÿ%ÿþþÜܹ¹––ssPPÿÔðÿ±âÿŽÔÿkÆÿH¸ÿ%ªÿªÜ’¹z–bsJP2ÿÔãÿ±ÇÿŽ«ÿkÿHsÿ%WÿUÜI¹=–1s%PÿÔÔÿ±±ÿŽŽÿkkÿHHÿ%%þܹ–sPÿãÔÿDZÿ«ŽÿkÿsHÿW%ÿUÜI¹=–1s%PÿðÔÿâ±ÿÔŽÿÆkÿ¸Hÿª%ÿªÜ’¹z–bsJP2ÿÿÔÿÿ±ÿÿŽÿÿkÿÿHÿÿ%þþÜܹ¹––ssPPðÿÔâÿ±ÔÿŽÆÿk¸ÿHªÿ%ªÿ’Üz¹b–Js2PãÿÔÇÿ±«ÿŽÿksÿHWÿ%UÿIÜ=¹1–%sPÔÿÔ±ÿ±ŽÿŽkÿkHÿH%ÿ%þܹ–sPÔÿã±ÿÇŽÿ«kÿHÿs%ÿWÿUÜI¹=–1s%PÔÿð±ÿâŽÿÔkÿÆHÿ¸%ÿªÿªÜ’¹z–bsJP2Ôÿÿ±ÿÿŽÿÿkÿÿHÿÿ%ÿÿþþÜܹ¹––ssPPòòòæææÚÚÚÎÎζ¶¶ªªªžžž’’’†††zzznnnbbbVVVJJJ>>>222&&&ðûÿ¤  €€€ÿÿÿÿÿÿÿÿÿÿÿÿBA\–CINÈ @ @ÿÿÿCINÈ@ €€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿBA\CINÈ@ @ÿÿÿCINÈ@ €€€€€€€€€ÀÀÀÀÜÀðʦÔðÿ±âÿŽÔÿkÆÿH¸ÿ%ªÿªÿ’Üz¹b–Js2PÔãÿ±ÇÿŽ«ÿkÿHsÿ%WÿUÿIÜ=¹1–%sPÔÔÿ±±ÿŽŽÿkkÿHHÿ%%ÿþܹ–sPãÔÿDZÿ«ŽÿkÿsHÿW%ÿUÿIÜ=¹1–%sPðÔÿâ±ÿÔŽÿÆkÿ¸Hÿª%ÿªÿ’Üz¹b–Js2PÿÔÿÿ±ÿÿŽÿÿkÿÿHÿÿ%ÿþþÜܹ¹––ssPPÿÔðÿ±âÿŽÔÿkÆÿH¸ÿ%ªÿªÜ’¹z–bsJP2ÿÔãÿ±ÇÿŽ«ÿkÿHsÿ%WÿUÜI¹=–1s%PÿÔÔÿ±±ÿŽŽÿkkÿHHÿ%%þܹ–sPÿãÔÿDZÿ«ŽÿkÿsHÿW%ÿUÜI¹=–1s%PÿðÔÿâ±ÿÔŽÿÆkÿ¸Hÿª%ÿªÜ’¹z–bsJP2ÿÿÔÿÿ±ÿÿŽÿÿkÿÿHÿÿ%þþÜܹ¹––ssPPðÿÔâÿ±ÔÿŽÆÿk¸ÿHªÿ%ªÿ’Üz¹b–Js2PãÿÔÇÿ±«ÿŽÿksÿHWÿ%UÿIÜ=¹1–%sPÔÿÔ±ÿ±ŽÿŽkÿkHÿH%ÿ%þܹ–sPÔÿã±ÿÇŽÿ«kÿHÿs%ÿWÿUÜI¹=–1s%PÔÿð±ÿâŽÿÔkÿÆHÿ¸%ÿªÿªÜ’¹z–bsJP2Ôÿÿ±ÿÿŽÿÿkÿÿHÿÿ%ÿÿþþÜܹ¹––ssPPòòòæææÚÚÚÎÎζ¶¶ªªªžžž’’’†††zzznnnbbbVVVJJJ>>>222&&&ðûÿ¤  €€€ÿÿÿÿÿÿÿÿÿÿÿÿñà‡ÀÀ€€€€€€€ÀÀÀ€pp™‘™‘€ ‘ €‘ ‘ ‘ ‘ ‘ ™ ‘ ‘ p‘ ‘ x €  x€ ™‘™‘€€€ÿÿóŸáÀÀ€€€€€€ÀÀÿÿñðîòò--ðð--ôô+-+-+ô,+-*++-,í*-* !'!-*-*íñ'õ'ïñ&ñ!'ô'ñó&ô' ñ&ñ!'ó&óò%ô -%- ó&òò%ô&! $!!&ó%ñò$õ&õõóõõ&ó%òó%-òååò-%ôìððõòææòõñðìî$óêóðó!ò!õòõ!ò!òÿÿÿÿÿ‡ðÿÿÀþ€?üøððàààÀÀÀÀÀÀÀÀÀ€Àà€àÿà?ÿÀ?ü€üüüÿÿÿÿp€€p€pppp€€p‘‘ €™™™‘ ™‘‘‘p™‘‘‘p€™‘ˆ‘‘€€™‘ˆ‘™€™€™ ‘ˆ‘™€™€™ ‘ˆ‘™€™€™‘‘™€™€‘‘‘™€™€™™ ‘™€™€ ‘™™€™™€€™€™™€™pp™ˆˆˆ€™€ˆˆ€€™‘™€p‘x™‘p ™‘€™™pÿÿÿÿÿñÿÿàþÀ?ü€?øø ððààààƒÀ„ƒÀ„ƒÀ„ƒÀ€ƒÀ€ƒÀ€ƒÀ€ƒÀààÀàÿàÿà?þÀ?üüü€ÿÿÿÿîóñìóñï--ó--ìñ+---ó----ìï+--'-ó+--+-ì++-+-óó++-'ó-'-óó'--'-ñ'+ìï++ó-'-++ó-'--'-----'++'----'+ìï''+''-+''''''-+'óñ-''-''++'''+''-+'-ó-'+ó-+'-ó+''+ñï-''-+'+ì+%+ï-''--'%-ì-''--%+ìï'%+ó-''+-%%+ì-''--%'ñî'%+''+-'%+ì+''--''ñï'%+-''+-%%-ì-''-%%-ñï%$+-''+-%$-ó-''--%$-ñî%$+''+$$+-''--%$-óî%$ñó-'ó+'$%%+-î'-%$+ñï%$+-'ó---ó--%%--%$-ó-%$+ï-%'-%%-ó-$$+ìì+$ç%-'%+óñññó-%++$$'-ñ+'î''+-î$+ó-%ó-+'î'î+-óï%$îóï+$îñììììììï%$+óï-$$-óìììììì-%$+ï-$$-óñ-%$++$$-ïïó$$îóìó+$$ñó-'$$%--ïó-%$$'-ó--+++++---++++--óïóóñengine/resource/mp_os2.ico000066400000000000000000000017621444734033100160560ustar00rootroot00000000000000BA\CINò@ @ÿÿÿCINò@ ÿ€ÿÿÿ?ÿÿÿÿþÿ—ÿþÿÊÿýÿå?ýÿòŸóÿýoãÿþ“çßþmïßÿ0ÏßÿŠßÿåÿòßÿøßÿþßÿþßÿüÿÿüÃÿÿøáÿÿõðÿÿåü?ÿëþÿÓÿÿóÿÏÿçÿçÿÃÿûþ ÿýÿ‡ÿÿÿÏÿÿÿ?ÿÿÿÿÿÿ !" "  " "  " "   !"" "  " """!" """ "" """ """"""""" "" """ !"" ""!" " "gamecode/000077500000000000000000000000001444734033100126255ustar00rootroot00000000000000gamecode/COMPILE000066400000000000000000000013201444734033100136340ustar00rootroot00000000000000In order to compile the hcode (the HexenC *.hc files) into the progs.dat files, you need the hcc program (the hcode compiler). You can find the sources for this utility either in released uhexen2 sources, or in the uhexen2 svn repository. In order to compile the original hexen2 hcode, don't use the -oi and -on command line switches of hcc for compatibility with old saved games. If you use the -oi and -on optimizations, loading old saves make the engine to emit warnings at runtime. They are actually harmless, but annoying... $ hcc -os # this compiles progs.dat $ hcc -os -name progs2.src # this compiles progs2.dat In order to compile the mission pack, hexenworld or siege hcode, do: $ hcc -os -oi -on gamecode/README000066400000000000000000000425461444734033100135200ustar00rootroot00000000000000Hexen II: Hammer of Thyrion (uHexen2) - GAMECODE ------------------------------------------------ Changes ------- 2022-06-12 (1.29c): =================== - object.hc(ballista_think): break early from loop if find() returns world also change the while(){} into a loop{} . - cache the pointcontents() result in a local var. - portals: fix possible 'sticky fingers' issue (99999999999....): replaced those two with 99999999: trigger_stop_use is used by the wheel of time in tibet5.bsp: it used to set its nextthink to (time + -727379968.0), it's now (time + 100000000). HomeThink() used to set the t_width field of Succubus' lightning ball to time + 276447232.0, it now sets it to time + 100000000.0. - break if constructs checking SFL_CROSS_TRIGGER_x into small parts. also use SFL_CROSS_TRIGGERS instead of OR'ing the individual constants at runtime. - items.hc, triggers.hc: break the if contruct in client_has_piece() into smaller parts and use SFL_CROSS_TRIGGERS in hub_intermission_use() - impulse.hc (ImpulseCommands): early return if self.impulse == 0. also remove the 'else' cases where the parent 'if' does return. - commented out a lot of unused constants, also the unused attck_cnt. - objects.hc: bitset / bitclear clean-ups for FL_ONGROUND, DRF_TRANSLUCENT, MLS_ABSLIGHT and SCALE_ORIGIN_BOTTOM. - check_monsterspawn_ok(): changed FL_SUMMONED bitset from += to (+) - client.hc (ClientObituary): return from every attacker.classname match. - multiple removal of 'else' cases where the parent 'if' does return. 2018-02-20 (1.29b): =================== - soul.hc (necro_soul_touch): Necromancer soul devour no longer prints a stray number (bug #65.) Thanks to Raymond Martineau for reporting the issue. 2016-05-30 (1.29a): =================== - rider_death(), rider_pestilence(), rider_war(): set max_health along with health. these bosses rely on the multiplayer_health() function to set health, and without max_health set, it used to set health to 0 - multiplayer_health(): made sure that torncount doesn't go negative. 2015-10-31 (1.29): =================== - change lots of statements like x=x|y; into x(+)y; so that it's simply done with the BITSET op. also changed a few statements like x=x-(x&y); into x(-)y; so that it's simply done with the BITCLR op. other minor tidy-ups. - meteor.hc (tornato_spin): fixed typo in pain_finished equality check: if(self.pain_finished==-1), not if(self.pain_finished=-1). issue was present since at least v0.42. - stats.hc (AwardExperience): eliminated the duplicated IsPlayer check. Crusader partial healing at certain experience thresholds now starts at level 3 to be consistent with the manual. - specials.hc (CheckAbilities): fixed missing parens around two OR'ed constants in the drawflags bittest. (issue was present since at least v1.03.) - ravenai.hc: commented out all functions except for LocateTarget() and CheckMonsterAttack(). all of them are unused. - fangel.hc (fangel_deathframes): removed an unused local. - fablacde.hc: commented out unused frame_BLADE - client.hc (changelevel_touch): fixed parens around noxit cvar checks (typo was introduced in v1.15.) in siege version, remove the start map check (no such map in hexen2/hexen2world.) - allplay.hc (DeathBubbles,DeathSound), mummy.hc (mummy_die), rat.hc (rat_touch), triggers.hc (hurt_touch): removed the useless return statements from the end of the functions. - MG_AI.hc (CheckJump): changed several else if statements to simple ifs whose parent if already always return. - triggers.hc (quake_shake_next): remove useless initialization of local entity variable to world. - portals/damage.hc (T_Damage): make sure target is a player (FL_CLIENT) before checking its camera_time and return: spider.spiderActiveCount and player.camera_time overlap in entity union and can make spiders invincible, otherwise. issue has been there since Raven's v1.12. - siege/client.hc: in Climb(), changed a bitwise and to logical and to better match the code to v0.15 (was a typo from KS's changes.) in PlayerPostThink(), fixed a misplaced else case for the landing sound code to better match the code to v0.15 (was a typo from KS's changes.) in ClientObituary(), changed targ.deathtype check from falling to fall to match the v0.15.x code elsewhere in client.hc. - siege: removed again raven, snake, spider, scorpion, skullwiz, medusa, archer, hydra, faspell, fablade and fangel hc files from build. they were added to build along with KS's siege changes back in 2005, but they aren't really used, and they weren't in siege-v0.15 either... - combat.hc (FireMelee): broke the long if condition for the assassin's backstab into smaller 3 nested ifs ordered in the likelihood of truth. in siege version, changed some of the if conditions into else if (the player class checks.) - ravenstf.hc (create_raven_shot2, hw and siege versions): removed the DAMAGE_YES assignment to missile.solid, just like we did for the h2 and portals versions years ago. (must have been there by mistake as noticed by Pa3PyX; used to cause missiles to be of SOLID_TRIGGER type and collide with each other.) - ravenstf.hc (missle_straight, hw and siege versions): removed useless return statement from the end of the function. 2013-03-08 (1.28): =================== - Map cycling: abandoned the old community-invented (Kor Skarn) method of abusing strings.txt: it was intrusive and unsafe and ugly. The hw and siege hcode now use the standart quakeworld method of localinfo variables: see client.hc:NextLevel() for details. docs/README.hwsv is updated for brief user instructions on setting up a map cycling. 2012-10-01 (1.27): =================== - client.hc, invntory.hc: artifact_active/artifact_low setting/clearing of ART_TOMEOFPOWER, ART_INVINCIBILITY, ART_INVISIBILITY and ART_HASTE are now done simply by using BITSET and BITCLR ops. - h2/cube.hc (cube_find_target): fixed parens in the if construct. don't choose a monster whose controller is the same as the cube's controller i.e. the player's summoned imp. assign self as the cube's controller in UseCubeOfForce(). fixes backported from the mission pack version. - breakabl.hc,client.hc,doors.hc,subs.hc,weapons.hc,world.hc: replaced several while(1){} and do{}while(1) loops by loop{} which is shorter. - weapons.hc (CycleWeaponCommand, CycleWeaponReverseCommand): removed an unnecessary local variable and changed the loop into a while(1) loop and simplified a bit. made them to return in in an impossible case of weapon not being one of IT_WEAPON1..4. changed the portals version to use switch statements. - h2/items.hc, portals/items.hc (weapon_touch): IT_WEAPON4 must be OR'ed to new items, not added to. fix copied over from hexenworld version. - h2/items.hc (weapon_touch) : commented out other.oldweapon assignment to other.weapon. was causing weapon switching to get stuck if several weapons were picked up too fast. see: http://sourceforge.net/projects/uhexen2/forums/forum/425206/topic/5635367 - h2/weapons.hc (W_DeselectWeapon): added else cases for the necromancer for self.oldweapon == IT_WEAPON2 && self.oldweapon != IT_WEAPON3, and for self.oldweapon == IT_WEAPON3 && self.oldweapon != IT_WEAPON2, simply calling W_SetCurrentAmmo() same as the portals and hw versions do. - setstaff.hc (scarab_die): fixed a typo in chain removal code. the bug was at least as old as v0.42 of hexen2. noticed the fix in hexenworld. - commented out several dprints, several whitespace adjustments. - Bumped version number to 1.27. 2012-05-22 (1.26): =================== - fish.hc (fish_follow): Proceed only if the goalentity is a valid one. Also made it to return if the function puts the fish into bored state. - Bumped version number to 1.26. 2012-01-15 (1.25): =================== - h2/axe.hc (axetail_run): if no owner, remove self and return (just in case. merged from the mission pack.) - h2/weapons.hc: Fixed occasional crashes with Paladin's axe (H2MP 1.12a patch to the rescue...) - some minor clean up - Bumped version number to 1.25. 2011-07-03 (1.20): =================== - Bumped version number to 1.20. - Commented out unreferenced create_swarm() from h2 version of pstboar.hc (already disabled or removed in all other versions.) - Reset skull wizard's .enemy and .goalentity fields to world. Fixes bug #3314808 where, if there were a summoned imp around, a blinked away wizard wouldn't reappear even after imp went away. From Thomas Freundt. - Fixed bug where the summoned imp didn't pick the Egypt snake boss as an enemy. (Set the snake's ALIVE flag. From Thomas Freundt.) - Soul spheres: Made the soul sphere in sync with the skull or the cross. Changed the model lighting style to fullbright so now the skull and the cross are well discernible in dark places. The soul sphere can now gain momentum during spawning and fly around instead of hovering over the dead body and sometimes got stuck. Scaled down the soul at the end of its lifetime. Added the demoness among the list of bad people in the portals version in parallel to the hw version. Corrected minor errors and some inconsistencies. From Thomas Freundt. Also, in case of rider bosses, don't spawn a soul sphere because it won't be accessible anyway and may confuse the player. - Now that we added a modified ent file for the Cathedral level, removed the ugly hack of reducing the teleport push speed to 225 for this level from triggers.hc::teleport_touch. - Made sure that gaze attack sound doesn't persist when medusa is dead. - When an imp lord dies, clear the ALIVE flag before removing, otherwise eidolon might get confused and rendered idle. From Thomas Freundt. - Fixed mezzoman.hc bug which would prevent yakman from appearing during the 'Trial of Strength' in the Temple of Phurbu (tibet7) level of the mission pack, rendering the level not completable (bug #1112533). From Thomas Freundt. - Fixed a mission pack T_Damage() bug where a pentacle monster in tibet1 became invulnerable when it got crushed by a door. The design flaw in the union in entity.hc is documented. - Fixed a bug where werepanthers became "undead" when an assassin killed them by her bombs. From Thomas Freundt. - Fixed a bug which might prevent Eidolon to land. From Thomas Freundt. - Fixed Eidolon bugs which might prevent the finale screen to trigger. From Thomas Freundt. - Fixed a bug which used to prevent the finale screen to trigger if Praevus were killed too quickly in buddha_die() and buddha_run(). From Thomas Freundt. - Fixed trigger_crosslevel() with spawnflag 8, i.e. the clash with SPAWNFLAG_ACTIVATED, which used to prevent one of the prizes in Temple of Mars to appear. We now just set inactive property back to FALSE after calling InitTrigger(). From Keith Rozett. - Added a "mapfixes" section. Added entity fixes for cath, demo2, egypt4, egypt5, romeric5 and tower maps of the original game and tibet2 and tibet9 maps of the expansion pack among the mapfixes. - strings.txt: Fixed egypt2 wheel of ages message to report 360 degrees instead of 30 when it "points to a jackal". From Sander van Dijk. 2008-01-01 (1.19a): =================== - Updated the patch shell scripts for much better compatibility with BSD and other systems. No other source code or binary data change. 2006-03-18 (1.19): =================== - Origin fixes for tomed fires of vorpal sword and purifier: paladin used to fire them always from the non-crouched eye position, and it was even the worst when he was looking up (and down). You won't be shooting your back when standing just in front of a wall and fire your tomed purifier to the sky, from now on ;) 2006-12-01 (1.16a): =================== - Fixed an obscure bug where the assasin with her 4th weapon uses the tome of power and can't fire it. The bug was reported by Mathias Schlueter to Jacques 'Korax' Krige who forwarded the report to us. (The bug seems to have been introduced in gamecode-1.12g when a patch from Pa3PyX's sources was merged. Pa3PyX's own progs as part of his 1.15 package also has the same bug.) 2006-10-30 (1.16) : =================== - Fixed the dreaded mezzoman bug causing the following error: SV_StartSound: fangel/deflect.wav not precached ADDRESS -14227(?) 537(last_attack).last_attack -14226(?) mezzoman.hc : mezzo_reflect_trig_touch assignment to world entity Host_Error: Program error - Merged a probably better fix for the above bug. (from Steven). - Disabled impulse 11 (ServerflagsCommand): it is a dev command, can be issued from any client and may screw up the server pretty bad.. - Made the hexen2 progs to work with demo version 1.11: the original demo version excluded the necromancer and crusader classes, but the later one didn't care and included them. changing some precache_model3 and precache_sound3 to precache_model and precache_sound solves it. the pak file of 1.11 demo version already contains the necessary stuff. - Made the hexenworld progs to work with demo version 1.11: Raven actually did all of the changes, but they seem to have missed changing the precache_model2 calls to precache_model in wp_art.hc which prevented crusader and necromancer classes to be played with. - Ported the demo-1.11 precache compatibility changes from hexen2 to the mission pack. the mission pack actually has nothing to do with the demo version, but this is for the sake of the two trees being in sync as much as possible. - Ported the demo-1.11 precache compatibility changes from hexenworld to siege. siege actually doesn't have much to do with the demo, but this is for the sake of the two trees being in sync as much as possible. - Cleaned up and tweaked hexenworld and siege map cycling. - Fixed hexen2 and hexenworld server crashes upon deathmatch level change in cases of a custom map being run and there is no map cycling. mission pack actually didn't used to crash but it always failed changing into a new level. it is now fixed, as well. - Since version 1.11 of hexen2 demo already contains the demo3 level and it also has necessary stuff for running the hexenworld dm maps, added demo3 and the hw dm maps to FindDMLevel(). Similarly changed the hexen2 and the mission pack versions. 2006-03-20 (1.15) : =================== - hw: fixed the server crash when the server is spawned with dmmode 1. solution found in the ghost mod. - hw: properly notify users when dmmode is changed. also remove the brightfield from the icon holder when dmmode 1 is abandoned. code found in the the ghost mode. - hw / siege: broadcast the names of the current map and the next map in cycle to all clients - noexit is supposed to matter only for deathmatch, not for coop and singleplayer - CheckRules (timelimit and fraglimit) is supposed to matter only for deathmatch, not for coop and singleplayer - fixed those double semicolons. - hw: killed the irritating Poisong_die dprint - fixed those famous spelling errors. 2005-08-09 (1.14) : =================== - Disallowed cycling to a weapon without proper mana (S.A) - Enabled impulse 34, (puzzle piece inventory list) (S.A) - Map cycling: minor cleanups - Bumped the teleport-push speed to a highest safe value of 225 (250 is still safe, but just in case) and restricted that to the Cathedral map when not in deathmatch. Otherwise, Raven's original value (300) is used. - added Pa3PyX' fire delay bits for paladin's staff 2005-03-17 (1.12h): =================== Merged the actual H2MP 1.12-1.12a patch and it has been missing here in the CVS for ages. That no one told me about it is a bit embarassing.... 2005-02-23 (1.12g): =================== Merged a bunch of fixes/adjustments from Pa3PyX' sources: - fixed ravenstaff bug where splitting missiles would collide with each other - setstaff tomed and untomed fire rates now fps independent - crossbow fire rate fps now independent (also makes fire rate slightly faster, but I liked it ;) - no more 20 tornadoes per second from meteor staff at 200 fps - framerate independent Flame Orb fire procedure - fix demoness abilities according to the manual - assasin backstab starts at level 6 - crusader holy strength starts at level 6 - necromancer sickle steal adjustments to make it really useful - assasin cloak ability re-write - start crusader full-mana ability at level3 2005-02-15 (1.12f): =================== - added an optional patch file which allows compiling boss maps into hwprogs.dat. this is not of much use, because coop seems broken.. - updated the hexenworld boss files according to H2MP. although coop is broken as of now, maybe someone messes with these one day... - added stubs for the hexenworld missing boss-map functions. hwsv no longer crashes on maps rider1a, rider2c, romeric6, meso9 and eidolon. - Applied Kor Skarn's further changes to the Siege hcode: this is the code to the hwprogs.dat in "sgcycle.zip" (seems to be first intended for the Rival Kingdoms mod?) - Applied map cycling patch for Siege (similar to the one below) - Added the hcode for Siege. - Applied map cycling for HexenWorld (by Kor Skarn). 1.12d Reduce the speed of the teleporting process' pushing you forward. Fixes the unreachable Cathedral balcony issue for me (for both Hexen2 and H2MP) (2005-01-06). 1.12c fixed eidolon losing his hostility upon destruction of the orb. (2005-01-05, adaptation of eidolon.hc from H2MP) 1.12b fixed eidolon not jumping off the ledge (hc/h2/triggers.hc) (2004-12-09) 1.12a Raven's original 1.12a hcode for the mission pack (initial) 1.11 Raven's original 1.11 hcode for original Hexen2 (initial) gamecode/devel/000077500000000000000000000000001444734033100137245ustar00rootroot00000000000000gamecode/devel/buddha.txt000066400000000000000000000206051444734033100157170ustar00rootroot00000000000000buddha_die() cleaned up and fixed by Thomas Freundt: From: "Ozkan Sezer" [...] While browsing the forums, I ran into this bug report: http://quakeone.com/forums/quake-talk/other-games/5783-hexen-ii-community-work-discussion-59.html ... and I actually reproduced it. On the console, do playerclass 2 (to make it crusader) map tibet10 impulse 43 (to get everything) god ... then go and face praevus. Always use the ice mace in tome of power mode and always keep a summoned imp around, and then spam praevus with a lot of blizzard. Result: the finale screen doesn't trigger. [...] From: "Thomas Freundt" To: "Ozkan Sezer" Subject: Re: praevus bug?? Date: Mon, 18 Apr 2011 09:07:02 +0200 Hi Ozkan I think I found the reason for the bug. The following entity is assigned to Praevus: { "classname" "monster_buddha" "target" "t22" "targetname" "t21" "netname" "t51" "angle" "0" "origin" "-2720 1248 1456" } In 'buddha.hc' there is a function called 'buddha_run': if (self.health < self.max_health * (1 / 5) && self.dmgtime == 3) { self.dmgtime+=1; SUB_UseTargets(); self.target = self.netname; self.think = buddha_recharge; thinktime self : 0.1; return; } else if (self.health < self.max_health * (2 / 5) && self.dmgtime == 2) { self.dmgtime+=1; SUB_UseTargets(); self.target = self.targetname; self.think = buddha_recharge; thinktime self : 0.1; return; } When Praevus' health drops to less than 40% SUB_UseTargets() fires the standard "target" being "t22" which leads to the destruction of the first pillar; then self.target is latched to self.targetname which resolves to "target" "t21". Hence, when health drops to less than 20% the second pillar is destroyed and self.target is latched to self.netname which is equivalent to "target" "t51". When Praevus dies 'monster_death_use' issues another call to SUB_UseTargets() which activates the trigger_once entity with "targetname" "51" and thereby initiates the final sequence with the flying skulls: { "classname" "trigger_once" "target" "t52" "targetname" "t51" "delay" "6.5" "model" "*13" } This is how it should work. However, only one function of Praevus' behavioral repertoire can be active at a time and inserting debug messages into 'buddha_run' reveals that this function is only active during certain time slots; in the meantime Praevus can take considerable damage or even die. This is nicely demonstrated when one quickly drains his health level to near-death, then ceases fire, and only watches the scenery. The lag causes the first pillar to fall asunder, invoke a recharge sequence, followed by the second pillar being destroyed and yet another recharge sequence. If Praevus dies without execution of the <40% health if clause, the default "target" "t22" is triggered by 'monster_death_use'; in other words, the first pillar is always destroyed. The assignment 'self.target = self.netname' is also found in 'buddha_die', but this function is called *after* 'monster_death_use' so the flying skulls sequence never triggers. To fix this bug one only has to make another call to SUB_UseTargets() before Praevus' entity removes itself in 'buddha_die': doWhiteFlash(); SUB_UseTargets(); remove(self); Better still we remove 'self.target = self.targetname' from 'buddha_run' not only to ascertain the function is called only once (calling it twice shouldn't display any ill side effects, though; then again, I prefer not to rely on assumptions :-), but also to have more accurate control of timing. The default delay of 6.5s is now too long. To remedy this issue we can either reduce delay to <1s by altering tibet10.ent or call SUB_UseTargets about 6s before Praevus is removed: if (self.count >= 4 && self.count < 4.1) SUB_UseTargets(); if (self.count >= 10) { ... (There seems to be some rounding up with floating point, so 'if (self.count == 4)' is never executed) ~ ThF [...] After having completed the MP by defeating by far the toughest adversary, waiting more than 6s without anything happening seems somewhat irritating to me. For obvious reasons altering the entity file isn't really an option. As soon as 'buddha_die' sets self.count to 0, the function is called another 100 times with 0.1s intervals. So the perfect point in time to trigger the final sequence with its inherent 6.5s delay would be when self.count is 3.5. I now use a prog.dat that has both original 'self.target = self.netname' statements commented out and instead inserted if (self.count >= 3.5 && self.target != self.netname) { self.target = self.netname; SUB_UseTargets(); } before the statement originally at line 1087 if (self.count >= 10) { ... This gets rid of floating point uncertainty and ensures that it is only activated once with the right timing. I have tested this many times, it always worked. [...] > I see. To confirm, it also is fine if one plays "decently", ie. > without the Crusader's ice spam, yes? The only error that can still occur with this amendment is that the second pillar never breaks; but I find this rather unimportant. > And do you mean that if you do the above changes, the SUB_UseTargets() > call just after doWhiteFlash() is not needed? Exactly. > BTW, the if (self.count >= 10) {...} construct contains some weird > unnecessary if checks, such as the if (self.count == 3) check, as > well as if (self.count == 0) check after the self.count = 0 > assignment. I guess they can use a good cleanup while we are there. I think so, too. It looks like a duplicate pasted mistakenly. --- buddha.hc.orig +++ buddha.hc @@ -958,7 +958,7 @@ { self.dmgtime+=1; SUB_UseTargets(); - self.target = self.netname; + // self.target = self.netname; // see notes by Thomas Freundt below self.think = buddha_recharge; thinktime self : 0.1; return; @@ -1050,7 +1223,7 @@ entity found; vector new_origin; - self.target = self.netname; +// self.target = self.netname; if (self.think != buddha_die) { @@ -1065,7 +1238,7 @@ if (self.cnt == 0) { - sound(self, CHAN_VOICE, "buddha/die.wav", 1, ATTN_NONE); + sound(self, CHAN_VOICE, "buddha/die.wav", 1, ATTN_NONE); found=find(world,classname,"player"); while(found) {//Give them all the exp @@ -1081,59 +1254,37 @@ self.velocity='0 0 0'; } + if (self.count >= 3.5 && self.target != self.netname) + { + self.target = self.netname; + SUB_UseTargets(); + } + //thinktime self : self.rider_death_speed; self.rider_death_speed += 0.1; - if (self.count >= 10) + if (self.count >= 10) { - if (self.count == 3) - { - beam = spawn(); + self.count = 1; + self.effects (-) EF_BRIGHTLIGHT; + self.effects (+) EF_NODRAW; + if (self.movechain != world) + self.movechain.effects (+) EF_NODRAW; - new_origin = self.origin + '0 0 50'; + //thinktime self : 0.05; - setmodel(beam,"models/boss/circle.mdl"); - setorigin(beam,new_origin); + entity search; - setsize (beam, '0 0 0', '0 0 0'); - beam.owner = self; - beam.movetype = MOVETYPE_FLYMISSILE; - beam.solid = SOLID_NOT; - beam.drawflags = SCALE_TYPE_UNIFORM; - beam.scale = .1; - beam.skin = 0; - beam.avelocity = '0 0 300'; - beam.think = circle_think; - thinktime beam : HX_FRAME_TIME; - //self.count = 13; - } - - self.count = 0; - - if (self.count == 0) + search = find(world, classname, "rider_temp"); + while (search != world) { - self.count = 1; - self.effects (-) EF_BRIGHTLIGHT; - self.effects (+) EF_NODRAW; - if(self.movechain!=world) - self.movechain.effects (+) EF_NODRAW; - - //thinktime self : 0.05; - - entity search; - - search = find(world, classname, "rider_temp"); - while (search != world) - { - remove(search); - search = find(search, classname, "rider_temp"); - } + remove(search); + search = find(search, classname, "rider_temp"); + } - doWhiteFlash(); - remove(self); + doWhiteFlash(); + remove(self); - return; - } return; } else @@ -1152,7 +1303,7 @@ setmodel(beam,"models/boss/circle.mdl"); setorigin(beam,new_origin); - setsize (beam, '0 0 0', '0 0 0'); + setsize (beam, '0 0 0', '0 0 0'); beam.owner = self; beam.movetype = MOVETYPE_FLYMISSILE; beam.solid = SOLID_NOT; @@ -1174,7 +1325,7 @@ setmodel(beam,"models/boss/shaft.mdl"); setorigin(beam,new_origin); - setsize (beam, '0 0 0', '0 0 0'); + setsize (beam, '0 0 0', '0 0 0'); beam.owner = self; beam.drawflags = SCALE_ORIGIN_BOTTOM | SCALE_TYPE_XYONLY; beam.movetype = MOVETYPE_NOCLIP; gamecode/devel/eidolon.txt000066400000000000000000000101761444734033100161230ustar00rootroot00000000000000From: "Thomas Freundt" To: "Ozkan Sezer" Subject: Re: eidobug Date: Mon, 2 May 2011 14:43:39 +0200 [...] When a player or monster gets hurt, 'T_Damage' is called and after that another function, the reference to which is stored in self.th_pain, which in our case is 'eidolon_check_fake'. In the first part of the showdown this function restores Eidolon's health to 3000 and keeps track of the inflicted damage through the separate variable self.dmg: self.dmg+=self.max_health-self.health; self.health=self.max_health; When self.dmg is >= 2000 Eidolon has to be struck once more after at least 3+skill seconds: self.pain_finished=time+3+skill; Then, 'eidolon_check_fake' deactivates itself (temporarily) by the statement self.th_pain=SUB_Null; and invokes the chain 'eidolon_fake_die' -> 'eidolon_darken_sky' -> 'eidolon_ready_grow' -> 'eidolon_grow' -> 'eidolon_ready_roar' -> 'eidolon_roar' Until 'eidolon_ready_roar' is called, deactivation of 'eidolon_check_fake' is compensated by self.health=self.max_health; assignments every animation frame. Even with four players, there is no way of inflicting so much damage as to kill Eidolon during one think interval (0.1s) . In 'T_Damage', however, there is an if clause that only applies to damage inflicted by monsters; so when an Imp Lord supports the player and hits Eidolon the following chain is triggered: 'T_Damage' -> 'monster_pissed' -> 'FoundTarget' -> 'HuntTarget' -> self.think = self.th_run; Hence, an Imp Lord can break the chain beginning with 'eidolon_fake_die' in any one of the functions involved by replacing it with self.th_run which is either 'eidolon_run' or 'eidolon_guarding'. As 'eidolon_check_fake' is only restored in 'eidolon_roar', Eidolon can take damage by either an Imp Lord or a player while he isn't supposed to and eventually dies: 'eidolon_die' -> 'eidolon_shake' -> 'eidolon_explode' -> 'rider_die' -> 'rider_eol' -> WriteByte (MSG_ALL, 6); In 'eidobug.dem' you can hear the sound "eidolon/fakedie.wav" which is the moment when Eidolon starts to loose health. Once 'eidolon_die' is initiated 'T_Damage' returns before 'monster_pissed' might be called because health is <= 0. Hurting the orb, however, invokes the chain 'orb_pain' -> 'eidolon_orb_pain' (self.owner.think) -> 'eidolon_run' thereby interrupting 'rider_die' and as such effectively inhibiting completion of the game. As an entity, Eidolon isn't removed, only his model gets replaced with "models/null.spr"; therefore, his bounding box can still be hurt and in developer mode debug print spits out messages like "R_GetSpriteFrame: no such frame 201". There are at least two more bugs in the eidolon map. Eidolon starts off with MOVETYPE_STEP (4) which is typical of monsters and required for his initial jump to work; after that, movetype is set to MOVETYPE_NONE (0). During 'eidolon_darken_sky' movetype is set to MOVETYPE_NOCLIP (8). The first condition checked in 'eidolon_check_attack' is if(self.movetype) return FALSE; If an Imp Lord hits Eidolon before the first roaring sequence initiated by self.th_jump is completed, detection of any attacker whatsoever is inhibited because only when the last animation frame of 'eidolon_roar' is reached movetype is set to MOVETYPE_NONE. The third quirk is caused by the game's assumption that Eidolon always lands on the arena's ground after his initial jump and assigns the FL_ONGROUND flag to this z-level; if an Imp Lord attacks Eidolon in a way that the latter's jump is either blocked or makes him land on the Imp Lord, Eidolon keeps walking in mid air. For both bugs see attachment "eidobug2+3.dem". As far as I can tell, the only way to safely prevent interruption of chained sequences by an Imp without fiddling with "damage.hc" is to set the DAMAGE_NO flag during the vulnerable phases - the downside to this is Eidolon being immune to damage during the first roaring sequence (and during 'eidolon_grow', but then his health is restored anyhow). [...] What I forgot: while the DAMAGE_NO flag is set, hitting Eidolon sounds like hitting stone. Using FL_GODMODE instead not only makes hitting Eidolon sound like flesh again [...] gamecode/devel/mezzoman.txt000066400000000000000000000073401444734033100163310ustar00rootroot00000000000000mezzoman.hc: fixed HexenC bug which would prevent a yakman from appearing during the 'Trial of Strength' in the 'Temple of Phurbu' (tibet7) level of the mission pack, rendering the level not completable. Fix by Thomas Freundt; quoting his analysis: "The problem originates in the Werepanther's shield. [...] behavioral function mezzoman.hc::spawn_reflect() 'Picks up enemy missile as shield!'. The pointer to the missile entity is stored in monster_mezzoman's .shield property and is initiated as usual with 0 being the equivalent to entity world. There are 3 functions in mezzoman.hc that deactivate the missile by if(self.shield) remove(self.shield); ... and a think function SUB_Remove(). When another function of the above mentioned ones already has removed the entity, the pointer is freed, so no problems arise with freeing the entity again, although I regard this as sloppy programming. It starts to get out of hand when another entity has been allocated to the slot: it's removed and that's it. Yakman may meet the same fate, although this case is more likely to happen than random because the last entity removed may be a reflected missile. The mezzoman chain acts like this: T_Damage Killed SUB_UseTargets func_monsterspawner_mp mezzo_die ... Yakman is spawned before mezzo_die() is called and in latter function it is removed before being properly initiated by monster_yakman, so it stays invisible during its short existence. The design flaw also explains why sometimes missiles disappear for no apparent reason. Eventually, any temporary entity might be the victim. There are many, many debug messages commented out and I think this is not coincidental. The best way to go, of course, were to reset the .shield pointer to 0 after the entity is removed: self.shield = world; ... but during runtime an error is issued: "assignment to world entity". So not only do we have to check whether the entity to be removed is indeed of classname 'mezzo_reflect' but also if it is owned by self, our dying monster_mezzoman." --- mezzoman.hc.orig +++ mezzoman.hc @@ -856,7 +856,8 @@ void mezzo_die () [++ $death1 .. $death16] { if(self.shield) - remove(self.shield); + if(self.shield.classname=="mezzo_reflect" && self.shield.owner==self) + remove(self.shield); if (self.health < -40) { chunk_death(); @@ -893,7 +894,8 @@ self.monster_awake=TRUE; if(self.shield) - remove(self.shield); + if(self.shield.classname=="mezzo_reflect" && self.shield.owner==self) + remove(self.shield); if(self.health<=100) { @@ -1071,7 +1073,8 @@ { float r; if(self.shield) - remove(self.shield); + if(self.shield.classname=="mezzo_reflect" && self.shield.owner==self) + remove(self.shield); r=vlen(self.enemy.origin-self.origin); if(infront(self.enemy)&&r<100) { @@ -1120,16 +1123,22 @@ self.think=mezzo_roll_forward; thinktime self : 0; } - self.shield.oldthink=self.shield.think; - self.shield.think=SUB_Remove; - thinktime self.shield : 0.2; + if(self.shield) + if(self.shield.classname=="mezzo_reflect" && self.shield.owner==self) + { + self.shield.oldthink=self.shield.think; + self.shield.think=SUB_Remove; + thinktime self.shield : 0.2; + } // dprint("checking defense from block\n"); mezzo_check_defense(); if(self.think==mezzo_block_wait) - { - self.shield.think=self.shield.oldthink; - thinktime self.shield : 0; - } + if(self.shield) + if(self.shield.classname=="mezzo_reflect" && self.shield.owner==self) + { + self.shield.think=self.shield.oldthink; + thinktime self.shield : 0; + } // else // dprint("wigging!\n"); } This fix properly closes our more that six year old bug #1112533, see http://sourceforge.net/tracker/?func=detail&aid=1112533&group_id=124987&atid=701006 gamecode/devel/objectives_sorted_by_index.txt000066400000000000000000000366061444734033100220760ustar00rootroot00000000000000 "keep1.txt" { "origin" "1312 1448 -200" "model" "*74" "targetname" "t49" "frags" "8" "spawnflags" "32769" "classname" "trigger_objective" "model" "*81" "targetname" "t11" "wait" "-1" "origin" "-826 384 -40" } { "frags" "9" "origin" "648 224 280" "targetname" "t50" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "2264 -1440 232" "targetname" "t23" "spawnflags" "49154" "frags" "9" "classname" "trigger_objective" } { "origin" "-2488 104 248" "targetname" "t52" "spawnflags" "32770" "frags" "8" "classname" "trigger_objective" } { "origin" "672 224 280" "targetname" "t50" "frags" "10" "spawnflags" "49154" "classname" "trigger_objective" "movedir" "1 1 2" } { "origin" "2248 -1464 232" "targetname" "t53" "frags" "10" "spawnflags" "49153" "classname" "trigger_objective" } { "classname" "trigger_objective" "spawnflags" "49153" "frags" "11" "targetname" "t52" "origin" "-2496 40 248" "abslight" ".4" } "keep2.txt" { "targetname" "t208" "origin" "-48 1336 312" "frags" "11" "spawnflags" "32770" "classname" "trigger_objective" "model" "*31" } { "targetname" "t211" "origin" "-328 448 312" "frags" "11" "spawnflags" "32770" "classname" "trigger_objective" } { "targetname" "t205" "model" "*41" "classname" "trigger_objective" "spawnflags" "32770" "frags" "11" "origin" "-624 1824 312" "soundtype" "5" "abslight" ".3" "angle" "270" "classname" "func_door" } { "classname" "trigger_objective" "spawnflags" "49153" "frags" "18" "targetname" "t177" "origin" "-936 1808 312" "model" "*50" "scale" ".2" } { "origin" "-520 1632 144" "targetname" "t198" "classname" "trigger_objective" "frags" "2" "spawnflags" "49153" } { "origin" "-552 1584 312" "targetname" "t197" "spawnflags" "49153" "frags" "2" "classname" "trigger_objective" } { "origin" "680 216 -136" "targetname" "t196" "frags" "2" "spawnflags" "49154" "classname" "trigger_objective" } { "origin" "648 208 -152" "targetname" "t196" "frags" "12" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "2272 -208 -312" "spawnflags" "32769" "targetname" "t195" "frags" "11" "classname" "trigger_objective" } { "classname" "trigger_objective" "model" "*76" "model" "*68" "targetname" "t178" "spawnflags" "32770" "origin" "-536 2760 488" "frags" "3" "angle" "-1" "classname" "func_door" "spawnflags" "32768" "speed" "50" } { "classname" "trigger_objective" "targetname" "t177" "spawnflags" "49154" "origin" "-968 1808 312" "frags" "4" } { "classname" "trigger_objective" "targetname" "t176" "spawnflags" "32769" "origin" "3104 1880 280" "frags" "3" } { "targetname" "t184" "frags" "12" "spawnflags" "49154" "classname" "trigger_objective" "origin" "-576 1584 312" } { "classname" "trigger_objective" "spawnflags" "49154" "targetname" "t175" "frags" "12" "origin" "-648 1616 152" } "keep3.txt" { "origin" "-1032 -56 120" "targetname" "t379" "frags" "16" "spawnflags" "57346" "classname" "trigger_objective" "model" "*9" "CD" "4" } { "origin" "-1032 -40 120" "targetname" "t379" "frags" "15" "spawnflags" "53250" "classname" "trigger_objective" } { "origin" "-1032 -72 120" "targetname" "t379" "frags" "17" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "-464 112 120" "targetname" "t205" "frags" "17" "spawnflags" "49154" "classname" "trigger_objective" } { "origin" "-440 112 120" "targetname" "t377" "classname" "trigger_objective" "spawnflags" "53249" "frags" "15" } { "origin" "-416 112 120" "targetname" "t377" "frags" "16" "spawnflags" "57345" "classname" "trigger_objective" } { "origin" "1416 1088 16" "targetname" "t208" "spawnflags" "49154" "frags" "14" "classname" "trigger_objective" } { "origin" "1384 1008 16" "targetname" "t375" "spawnflags" "49153" "frags" "13" "classname" "trigger_objective" } { "origin" "-392 112 128" "targetname" "t374" "frags" "14" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "-392 96 128" "targetname" "t374" "frags" "13" "spawnflags" "49154" "classname" "trigger_objective" } { "classname" "trigger_objective" "spawnflags" "49154" "frags" "18" "targetname" "t237" "origin" "-952 136 288" "classname" "func_monsterspawner" } { "origin" "3392 1520 -136" "targetname" "t368" "frags" "5" "spawnflags" "32770" "classname" "trigger_objective" } { "origin" "-48 -1904 104" "frags" "5" "targetname" "t367" "spawnflags" "32769" "classname" "trigger_objective" } { "classname" "trigger_objective" "spawnflags" "49153" "frags" "4" "targetname" "t365" "origin" "-904 120 288" } "keep4.txt" { "frags" "19" "origin" "-1128 408 48" "targetname" "t50" "spawnflags" "49154" "classname" "trigger_objective" "CD" "5" } { "origin" "1336 352 -192" "targetname" "t96" "frags" "19" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "1376 360 -216" "targetname" "t96" "frags" "1" "spawnflags" "49154" "classname" "trigger_objective" } { "spawnflags" "32770" "origin" "-144 -592 -384" "targetname" "t90" "frags" "0" "classname" "trigger_objective" } { "spawnflags" "49153" "classname" "trigger_objective" "frags" "1" "targetname" "t89" "origin" "-1104 408 32" } { "classname" "trigger_objective" "frags" "1" "spawnflags" "49153" "targetname" "t87" "origin" "752 -296 -24" } { "classname" "trigger_objective" "frags" "0" "targetname" "t86" "origin" "224 1584 -88" "spawnflags" "32769" } "keep5.txt" { "origin" "168 -1872 120" "model" "*27" "targetname" "t78" "spawnflags" "32769" "frags" "7" "classname" "trigger_objective" "classname" "func_door_rotating" "origin" "265 -2224 64" "model" "*56" "model" "*52" } { "origin" "544 -576 -200" "targetname" "spock" "frags" "7" "spawnflags" "32770" "classname" "trigger_objective" } { "frags" "6" "origin" "-2208 -232 8" "targetname" "t80" "spawnflags" "49154" "classname" "trigger_objective" } { "origin" "960 -1008 160" "targetname" "t81" "frags" "6" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "976 -1240 8" "targetname" "t75" "frags" "20" "spawnflags" "49154" "classname" "trigger_objective" } { "origin" "-2208 -208 8" "targetname" "t80" "frags" "20" "spawnflags" "49153" "classname" "trigger_objective" } "tibet1.txt" { "classname" "trigger_objective" "spawnflags" "32770" "frags" "59" "targetname" "t81" "origin" "-1008 -56 88" "MIDI" "tulku10" } { "classname" "trigger_objective" "spawnflags" "32769" "frags" "59" "targetname" "t80" "origin" "-88 -368 -8" } { "origin" "1248 -1768 -32" "wait" "-1" "model" "*7" "target" "t75" "targetname" "t74" "spawnflags" "49154" "frags" "22" "classname" "trigger_objective" "speed" "50" "t_length" "120" "t_width" "8" } { "origin" "2432 1760 -248" "targetname" "t73" "frags" "22" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "152 -240 16" "targetname" "t70" "frags" "23" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "104 -240 16" "targetname" "t1" "frags" "24" "spawnflags" "49154" "classname" "trigger_objective" } "tibet10.txt" "tibet2.txt" { "origin" "1072 -2280 840" "model" "*84" "targetname" "t356" "frags" "23" "spawnflags" "49154" "classname" "trigger_objective" "targetname" "t355" "model" "*87" "wait" "-1" "spawnflags" "32" } { "origin" "1072 -2256 840" "targetname" "t356" "frags" "24" "classname" "trigger_objective" "spawnflags" "49153" } { "origin" "1136 -232 776" "frags" "26" "targetname" "t357" "spawnflags" "32769" "classname" "trigger_objective" } { "origin" "1136 -256 776" "targetname" "t357" "frags" "25" "spawnflags" "32770" "classname" "trigger_objective" } { "origin" "-112 -776 24" "targetname" "t7" "frags" "28" "spawnflags" "49154" "classname" "trigger_objective" } { "origin" "-80 -808 8" "targetname" "t358" "frags" "27" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "-2904 -1208 712" "targetname" "t359" "frags" "28" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "-2928 -1208 712" "targetname" "t359" "frags" "27" "spawnflags" "49154" "classname" "trigger_objective" } { "origin" "-64 -2144 856" "targetname" "t367" "spawnflags" "49154" "frags" "30" "classname" "trigger_objective" } { "origin" "112 -2144 856" "targetname" "t361" "classname" "trigger_objective" "frags" "29" "spawnflags" "49153" } { "origin" "1288 -3560 680" "targetname" "t362" "spawnflags" "49154" "frags" "29" "classname" "trigger_objective" } { "origin" "1312 -3560 680" "targetname" "t362" "frags" "30" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "-1632 -2696 792" "targetname" "t364" "frags" "29" "spawnflags" "49153" "classname" "trigger_objective" } { "classname" "trigger_objective" "spawnflags" "32769" "frags" "51" "targetname" "t370" "origin" "-1656 -2464 800" "model" "*105" } { "classname" "trigger_objective" "spawnflags" "32770" "frags" "51" "targetname" "t371" "origin" "-1736 -3288 648" } "tibet3.txt" { "classname" "trigger_objective" "spawnflags" "32770" "frags" "52" "targetname" "t54" "origin" "612 52 128" "delay" "2" } { "classname" "trigger_objective" "spawnflags" "32769" "frags" "52" "targetname" "t53" "origin" "-1560 -1352 56" } { "classname" "trigger_objective" "spawnflags" "49153" "frags" "25" "targetname" "t50" "origin" "-496 88 288" } { "classname" "trigger_objective" "spawnflags" "49154" "frags" "26" "targetname" "openbuddha" "origin" "-496 104 288" } { "targetname" "t55" "origin" "-704 232 488" "frags" "32" "spawnflags" "49154" "classname" "trigger_objective" } { "targetname" "t48" "frags" "31" "origin" "-696 432 496" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "-480 24 152" "frags" "31" "spawnflags" "49154" "targetname" "t47" "classname" "trigger_objective" } { "origin" "-480 8 152" "frags" "32" "spawnflags" "49153" "targetname" "t47" "classname" "trigger_objective" } "tibet4.txt" { "origin" "1616 -1168 -312" "targetname" "t168" "spawnflags" "49154" "frags" "26" "classname" "trigger_objective" "message" "473" } { "origin" "1568 352 8" "targetname" "t165" "frags" "33" "spawnflags" "49153" "classname" "trigger_objective" "health" "999999999999" "model" "*91" "soundtype" "2" "model" "*59" } { "origin" "1888 136 8" "targetname" "t73" "frags" "34" "spawnflags" "49154" "classname" "trigger_objective" } { "origin" "-328 2712 72" "targetname" "t166" "frags" "34" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "-312 2712 72" "targetname" "t166" "frags" "33" "spawnflags" "49154" "classname" "trigger_objective" } { "origin" "1600 -1168 -312" "targetname" "t168" "frags" "53" "spawnflags" "32769" "classname" "trigger_objective" } { "origin" "2592 320 -56" "targetname" "t169" "frags" "53" "spawnflags" "32770" "classname" "trigger_objective" } "tibet5.txt" { "frags" "41" "spawnflags" "49154" "classname" "trigger_objective" "targetname" "t25" "origin" "-64 1064 56" "abslight" ".6" "speed" "50" "origin" "1676 520 -92" "model" "*17" } { "classname" "trigger_objective" "spawnflags" "49153" "frags" "41" "targetname" "t52" "origin" "-64 1048 56" } { "spawnflags" "49153" "classname" "trigger_objective" "frags" "48" "targetname" "t51" "origin" "-256 872 64" } { "classname" "trigger_objective" "spawnflags" "49154" "frags" "48" "targetname" "t26" "origin" "-272 872 64" } { "spawnflags" "49153" "classname" "trigger_objective" "frags" "35" "targetname" "t49" "origin" "-576 1048 56" } { "classname" "trigger_objective" "spawnflags" "49153" "frags" "36" "targetname" "t49" "origin" "-576 1032 56" } { "classname" "trigger_objective" "frags" "36" "spawnflags" "49154" "targetname" "t24" "origin" "-576 1072 56" } { "classname" "trigger_objective" "spawnflags" "49154" "targetname" "t48" "frags" "35" "origin" "1712 568 -80" } { "classname" "trigger_objective" "spawnflags" "49153" "targetname" "t48" "frags" "36" "origin" "1736 568 -80" } { "frags" "54" "classname" "trigger_objective" "spawnflags" "32770" "targetname" "t47" "origin" "-816 984 128" } { "classname" "trigger_objective" "spawnflags" "32769" "frags" "54" "targetname" "t46" "origin" "208 -472 -184" } "tibet6.txt" { "origin" "-1208 -1160 8" "targetname" "t66" "spawnflags" "49154" "frags" "40" "classname" "trigger_objective" "lip" "8" "level" "96" "spawnflags" "196608" "model" "*6" } { "origin" "1008 1128 328" "targetname" "t65" "frags" "40" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "992 1128 328" "targetname" "t65" "frags" "39" "spawnflags" "49154" "classname" "trigger_objective" } { "origin" "-1192 -1144 8" "targetname" "t63" "frags" "39" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "872 320 248" "targetname" "t61" "frags" "37" "spawnflags" "49154" "classname" "trigger_objective" } { "origin" "856 320 248" "targetname" "t61" "frags" "38" "spawnflags" "49153" "classname" "trigger_objective" } { "targetname" "t67" "origin" "-952 -1128 8" "frags" "38" "spawnflags" "49154" "classname" "trigger_objective" } { "origin" "-936 -1144 8" "targetname" "t60" "frags" "37" "spawnflags" "49153" "classname" "trigger_objective" } "tibet7.txt" { "origin" "2072 -2400 32" "targetname" "t43" "frags" "44" "spawnflags" "49153" "classname" "trigger_objective" "classname" "worldspawn" } { "origin" "2320 -2408 32" "targetname" "t5" "frags" "45" "spawnflags" "49154" "classname" "trigger_objective" } { "origin" "392 280 224" "frags" "44" "targetname" "t42" "spawnflags" "49154" "classname" "trigger_objective" } { "origin" "392 296 224" "targetname" "t42" "frags" "45" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "1944 728 240" "targetname" "t7" "frags" "43" "spawnflags" "49154" "classname" "trigger_objective" } { "origin" "1904 712 240" "targetname" "t40" "frags" "42" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "2392 -1352 16" "targetname" "t39" "frags" "43" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "2392 -1336 16" "targetname" "t39" "frags" "42" "spawnflags" "49154" "classname" "trigger_objective" } { "origin" "-2960 -2064 -288" "targetname" "t38" "frags" "46" "spawnflags" "32769" "classname" "trigger_objective" } { "origin" "-2792 3176 96" "targetname" "t27" "frags" "48" "spawnflags" "49153" "classname" "trigger_objective" } { "origin" "-2784 2840 16" "targetname" "t37" "classname" "trigger_objective" "spawnflags" "32770" "frags" "47" } { "origin" "-2808 2840 16" "targetname" "t37" "frags" "46" "spawnflags" "32770" "classname" "trigger_objective" } { "origin" "616 -1888 8" "targetname" "t36" "frags" "47" "spawnflags" "32769" "classname" "trigger_objective" } "tibet8.txt" { "classname" "trigger_objective" "spawnflags" "49154" "frags" "57" "targetname" "t43" "origin" "-1264 -784 56" "spawnflags" "1" } { "spawnflags" "49153" "classname" "trigger_objective" "frags" "56" "targetname" "t45" "origin" "-96 424 24" } { "classname" "trigger_objective" "spawnflags" "49154" "frags" "55" "targetname" "t45" "origin" "-96 408 24" } { "frags" "56" "classname" "trigger_objective" "spawnflags" "49154" "targetname" "t37" "origin" "-312 -1376 8" } { "classname" "trigger_objective" "spawnflags" "49153" "frags" "55" "targetname" "t44" "origin" "-336 -1360 8" } { "classname" "trigger_objective" "spawnflags" "49153" "frags" "49" "targetname" "t43" "origin" "-1248 -784 56" } { "classname" "trigger_objective" "spawnflags" "32769" "frags" "50" "targetname" "t42" "origin" "584 -1048 -184" } "tibet9.txt" { "classname" "trigger_objective" "model" "*79" "model" "*59" "spawnflags" "32770" "frags" "50" "targetname" "t56" "origin" "-80 776 -40" "classname" "func_button" "model" "*80" "spawnflags" "196649" "classname" "func_door" } { "classname" "trigger_objective" "spawnflags" "49153" "frags" "58" "targetname" "t57" "origin" "176 -152 -40" } { "classname" "trigger_objective" "spawnflags" "49154" "frags" "58" "targetname" "t58" "origin" "384 872 -328" } { "classname" "trigger_objective" "spawnflags" "49153" "targetname" "t60" "frags" "57" "origin" "-168 -48 -40" } gamecode/devel/objectives_sorted_by_map.txt000066400000000000000000000317771444734033100215500ustar00rootroot00000000000000 keep1.txt 8 32769 0x8001 [ON] Find the spring that leads to the village well keep1.txt 8 32770 0x8002 [OFF] Find the spring that leads to the village well keep1.txt 9 49153 0xC001 [ON, !HARD] Take the Sphere of Order to the Thaumaturgical Conveyance keep1.txt 9 49154 0xC002 [OFF, !HARD] Take the Sphere of Order to the Thaumaturgical Conveyance keep1.txt 10 49153 0xC001 [ON, !HARD] Get the Sphere of Order from Eidolon's Throne Room keep1.txt 10 49154 0xC002 [OFF, !HARD] Get the Sphere of Order from Eidolon's Throne Room keep1.txt 11 49153 0xC001 [ON, !HARD] Find Razumen's House. He should be there keep2.txt 2 49153 0xC001 [ON, !HARD] Get Mage's Key from the Tavern keep2.txt 2 49153 0xC001 [ON, !HARD] Get Mage's Key from the Tavern keep2.txt 2 49154 0xC002 [OFF, !HARD] Get Mage's Key from the Tavern keep2.txt 3 32769 0x8001 [ON] Go to the Duke's Keep keep2.txt 3 32770 0x8002 [OFF] Go to the Duke's Keep keep2.txt 4 49154 0xC002 [OFF, !HARD] Get Dragon's Tongue from Razumen's Lab in town keep2.txt 11 32769 0x8001 [ON] Find Razumen's House. He should be there keep2.txt 11 32770 0x8002 [OFF] Find Razumen's House. He should be there keep2.txt 11 32770 0x8002 [OFF] Find Razumen's House. He should be there keep2.txt 11 32770 0x8002 [OFF] Find Razumen's House. He should be there keep2.txt 12 49153 0xC001 [ON, !HARD] Take the Mage's Key to Razumen's Lab keep2.txt 12 49154 0xC002 [OFF, !HARD] Take the Mage's Key to Razumen's Lab keep2.txt 12 49154 0xC002 [OFF, !HARD] Take the Mage's Key to Razumen's Lab keep2.txt 18 49153 0xC001 [ON, !HARD] Take Dragon's Tongue to Razumen's Castle Quarters keep3.txt 4 49153 0xC001 [ON, !HARD] Get Dragon's Tongue from Razumen's Lab in town keep3.txt 5 32769 0x8001 [ON] Gain entrance to the Catacombs keep3.txt 5 32770 0x8002 [OFF] Gain entrance to the Catacombs keep3.txt 13 49153 0xC001 [ON, !HARD] Get the Scroll of Enchantment from Razumen's Castle Quarters keep3.txt 13 49154 0xC002 [OFF, !HARD] Get the Scroll of Enchantment from Razumen's Castle Quarters keep3.txt 14 49153 0xC001 [ON, !HARD] Take the Scroll of Enchantment to the Ballista near the magic field keep3.txt 14 49154 0xC002 [OFF, !HARD] Take the Scroll of Enchantment to the Ballista near the magic field keep3.txt 15 53249 0xD001 [ON, !HARD, !EASY] Get the Duke's Seal by completing the potion keep3.txt 15 53250 0xD002 [OFF, !HARD, !EASY] Get the Duke's Seal by completing the potion keep3.txt 16 57345 0xE001 [ON, !HARD, !MEDIUM] Get the Duke's Seal by completing the potion in the next room keep3.txt 16 57346 0xE002 [OFF, !HARD, !MEDIUM] Get the Duke's Seal by completing the potion in the next room keep3.txt 17 49153 0xC001 [ON, !HARD] Take the Duke's Seal to the locked cabinet keep3.txt 17 49154 0xC002 [OFF, !HARD] Take the Duke's Seal to the locked cabinet keep3.txt 18 49154 0xC002 [OFF, !HARD] Take Dragon's Tongue to Razumen's Castle Quarters keep4.txt 0 32769 0x8001 [ON] Find the Hall of the Dead keep4.txt 0 32770 0x8002 [OFF] Find the Hall of the Dead keep4.txt 1 49153 0xC001 [ON, !HARD] Find the Chalice of St. Eric keep4.txt 1 49153 0xC001 [ON, !HARD] Find the Chalice of St. Eric keep4.txt 1 49154 0xC002 [OFF, !HARD] Find the Chalice of St. Eric keep4.txt 19 49153 0xC001 [ON, !HARD] Take the Chalice of St. Eric to the Altar of the Four Kings keep4.txt 19 49154 0xC002 [OFF, !HARD] Take the Chalice of St. Eric to the Altar of the Four Kings keep5.txt 6 49153 0xC001 [ON, !HARD] Find the Cross Key in the Warrior's Tomb keep5.txt 6 49154 0xC002 [OFF, !HARD] Find the Cross Key in the Warrior's Tomb keep5.txt 7 32769 0x8001 [ON] Investigate the King's Tomb keep5.txt 7 32770 0x8002 [OFF] Investigate the King's Tomb keep5.txt 20 49153 0xC001 [ON, !HARD] Take the Cross Key to the Cross Lock keep5.txt 20 49154 0xC002 [OFF, !HARD] Take the Cross Key to the Cross Lock tibet1.txt 22 49153 0xC001 [ON, !HARD] Use the Orb of Dakini at the temple to break the ice on the frozen lake tibet1.txt 22 49154 0xC002 [OFF, !HARD] Use the Orb of Dakini at the temple to break the ice on the frozen lake tibet1.txt 23 49153 0xC001 [ON, !HARD] Get the Ajanti Dagger from the meeting room in the Ice Caverns tibet1.txt 24 49154 0xC002 [OFF, !HARD] Take the Ajanti Dagger to the Gates of Tulku tibet1.txt 59 32769 0x8001 [ON] To start your journey towards Praevus, you must gain entrance to the False Temple tibet1.txt 59 32770 0x8002 [OFF] To start your journey towards Praevus, you must gain entrance to the False Temple tibet2.txt 23 49154 0xC002 [OFF, !HARD] Get the Ajanti Dagger from the meeting room in the Ice Caverns tibet2.txt 24 49153 0xC001 [ON, !HARD] Take the Ajanti Dagger to the Gates of Tulku tibet2.txt 25 32770 0x8002 [OFF] Get the Jade Buddha from the Ice Caverns tibet2.txt 26 32769 0x8001 [ON] Take the Jade Buddha to the offering place in the False Temple tibet2.txt 27 49153 0xC001 [ON, !HARD] You need the Stone of Seldoot that is locked in the Storage Room tibet2.txt 27 49154 0xC002 [OFF, !HARD] You need the Stone of Seldoot that is locked in the Storage Room tibet2.txt 28 49153 0xC001 [ON, !HARD] Take the Stone of Seldoot to the Cauldron room tibet2.txt 28 49154 0xC002 [OFF, !HARD] Take the Stone of Seldoot to the Cauldron room tibet2.txt 29 49153 0xC001 [ON, !HARD] You need to find Tien's key tibet2.txt 29 49153 0xC001 [ON, !HARD] You need to find Tien's key tibet2.txt 29 49154 0xC002 [OFF, !HARD] You need to find Tien's key tibet2.txt 30 49153 0xC001 [ON, !HARD] Tien's key has multiple uses tibet2.txt 30 49154 0xC002 [OFF, !HARD] Tien's key has multiple uses tibet2.txt 51 32769 0x8001 [ON] Find a way to return to the Gates of Tulku tibet2.txt 51 32770 0x8002 [OFF] Find a way to return to the Gates of Tulku tibet3.txt 25 49153 0xC001 [ON, !HARD] Get the Jade Buddha from the Ice Caverns tibet3.txt 26 49154 0xC002 [OFF, !HARD] Take the Jade Buddha to the offering place in the False Temple tibet3.txt 31 49153 0xC001 [ON, !HARD] Get the staff of Emperor Lo Pan from the offering room tibet3.txt 31 49154 0xC002 [OFF, !HARD] Get the staff of Emperor Lo Pan from the offering room tibet3.txt 32 49153 0xC001 [ON, !HARD] Take the staff of Emperor Lo Pan to the Arcane Sacrarium tibet3.txt 32 49154 0xC002 [OFF, !HARD] Take the staff of Emperor Lo Pan to the Arcane Sacrarium tibet3.txt 52 32769 0x8001 [ON] The exit to the False Temple lies behind the Great Buddha tibet3.txt 52 32770 0x8002 [OFF] The exit to the False Temple lies behind the Great Buddha tibet4.txt 26 49154 0xC002 [OFF, !HARD] Take the Jade Buddha to the offering place in the False Temple tibet4.txt 33 49153 0xC001 [ON, !HARD] Get the sixth Prayer Wheel from the Symbolic Room of Kalachakra tibet4.txt 33 49154 0xC002 [OFF, !HARD] Get the sixth Prayer Wheel from the Symbolic Room of Kalachakra tibet4.txt 34 49153 0xC001 [ON, !HARD] Take the sixth Prayer Wheel to the Way of the Wheels tibet4.txt 34 49154 0xC002 [OFF, !HARD] Take the sixth Prayer Wheel to the Way of the Wheels tibet4.txt 53 32769 0x8001 [ON] Avoid unseen pitfalls as you make your way to the Main Temple tibet4.txt 53 32770 0x8002 [OFF] Avoid unseen pitfalls as you make your way to the Main Temple tibet5.txt 35 49153 0xC001 [ON, !HARD] Get the Jewel of Buddha by stopping the Wheel of Time tibet5.txt 35 49154 0xC002 [OFF, !HARD] Get the Jewel of Buddha by stopping the Wheel of Time tibet5.txt 36 49153 0xC001 [ON, !HARD] Offer the Jewel of Buddha to Shiva tibet5.txt 36 49153 0xC001 [ON, !HARD] Offer the Jewel of Buddha to Shiva tibet5.txt 36 49154 0xC002 [OFF, !HARD] Offer the Jewel of Buddha to Shiva tibet5.txt 41 49153 0xC001 [ON, !HARD] Offer the Sangha Gem to Shiva tibet5.txt 41 49154 0xC002 [OFF, !HARD] Offer the Sangha Gem to Shiva tibet5.txt 48 49153 0xC001 [ON, !HARD] Offer the Dharma Jewel to Shiva tibet5.txt 48 49154 0xC002 [OFF, !HARD] Offer the Dharma Jewel to Shiva tibet5.txt 54 32769 0x8001 [ON] Complete the three trials to continue on your journey towards Praevus tibet5.txt 54 32770 0x8002 [OFF] Complete the three trials to continue on your journey towards Praevus tibet6.txt 37 49153 0xC001 [ON, !HARD] Get the Eyes of Buddha by following the unseen path tibet6.txt 37 49154 0xC002 [OFF, !HARD] Get the Eyes of Buddha by following the unseen path tibet6.txt 38 49153 0xC001 [ON, !HARD] Use the Eyes of Buddha to see the guardian of the portal tibet6.txt 38 49154 0xC002 [OFF, !HARD] Use the Eyes of Buddha to see the guardian of the portal tibet6.txt 39 49153 0xC001 [ON, !HARD] Get the Sangha Gem from its resting place tibet6.txt 39 49154 0xC002 [OFF, !HARD] Get the Sangha Gem from its resting place tibet6.txt 40 49153 0xC001 [ON, !HARD] Bring the Sangha Gem to the guardian of the portal tibet6.txt 40 49154 0xC002 [OFF, !HARD] Bring the Sangha Gem to the guardian of the portal tibet7.txt 42 49153 0xC001 [ON, !HARD] Get the Oil of Annointment from the Invocation Chambers tibet7.txt 42 49154 0xC002 [OFF, !HARD] Get the Oil of Annointment from the Invocation Chambers tibet7.txt 43 49153 0xC001 [ON, !HARD] Take the Oil of Annointment to the Pool of Cleansing tibet7.txt 43 49154 0xC002 [OFF, !HARD] Take the Oil of Annointment to the Pool of Cleansing tibet7.txt 44 49153 0xC001 [ON, !HARD] Get the Incense of Enlightenment from Hidden Supplication Chamber tibet7.txt 44 49154 0xC002 [OFF, !HARD] Get the Incense of Enlightenment from Hidden Supplication Chamber tibet7.txt 45 49153 0xC001 [ON, !HARD] Use the Incense of Enlightenment in the Invocation Chambers tibet7.txt 45 49154 0xC002 [OFF, !HARD] Use the Incense of Enlightenment in the Invocation Chambers tibet7.txt 46 32769 0x8001 [ON] Enter the Pillar of Fire tibet7.txt 46 32770 0x8002 [OFF] Enter the Pillar of Fire tibet7.txt 47 32769 0x8001 [ON] Ready your body for the journey through death and rebirth tibet7.txt 47 32770 0x8002 [OFF] Ready your body for the journey through death and rebirth tibet7.txt 48 49153 0xC001 [ON, !HARD] Offer the Dharma Jewel to Shiva tibet8.txt 49 49153 0xC001 [ON, !HARD] To pass the Guardians in the Inner Chambers, you must use the Vajra Scepter and the Bell of Ghanta tibet8.txt 50 32769 0x8001 [ON] Gain entrance to the Inner Sanctum of Praevus tibet8.txt 55 49153 0xC001 [ON, !HARD] To search the rest of the Palace, you must first acquire the Emperor's Key tibet8.txt 55 49154 0xC002 [OFF, !HARD] To search the rest of the Palace, you must first acquire the Emperor's Key tibet8.txt 56 49153 0xC001 [ON, !HARD] The Emperor's Key will let you pass the Portal of Nobility tibet8.txt 56 49154 0xC002 [OFF, !HARD] The Emperor's Key will let you pass the Portal of Nobility tibet8.txt 57 49154 0xC002 [OFF, !HARD] Find the Vajra Scepter in an earlier portion of the Palace tibet9.txt 50 32770 0x8002 [OFF] Gain entrance to the Inner Sanctum of Praevus tibet9.txt 57 49153 0xC001 [ON, !HARD] Find the Vajra Scepter in an earlier portion of the Palace tibet9.txt 58 49153 0xC001 [ON, !HARD] The Bell of Ghanta is nearby... tibet9.txt 58 49154 0xC002 [OFF, !HARD] The Bell of Ghanta is nearby... gamecode/devel/romeric5_tree.txt000066400000000000000000001521411444734033100172350ustar00rootroot00000000000000 _top= "|t10" "|t11" "|t12" "|t13" "|t14" "|t15" "|t17" "|t18" "|t19" "|t2" "|t26" "|t43" "|t45" "|t46" "|t47" "|t51" "|t6" "|t7" "|t8" "|t9" "romeric7|" found 116 entities with "target" a/o "targetname" property ---------------------------- { #6 "classname" "trigger_crosslevel_target" "target" "t10" "spawnflags" "8" "model" "*15" } { #36 "classname" "item_spawner" "targetname" "t10" "cnt_summon" "1" "origin" "1152 -808 -36" } ---------------------------- { #7 "classname" "trigger_crosslevel_target" "target" "t11" "spawnflags" "16" "model" "*11" } { #37 "classname" "item_spawner" "targetname" "t11" "cnt_tome" "1" "origin" "1344 -472 -36" } ---------------------------- { #8 "classname" "trigger_crosslevel_target" "target" "t12" "spawnflags" "32" "model" "*14" } { #38 "classname" "item_spawner" "targetname" "t12" "cnt_polymorph" "1" "origin" "1344 -808 -36" } ---------------------------- { #9 "classname" "trigger_crosslevel_target" "target" "t13" "spawnflags" "64" "model" "*12" } { #39 "classname" "item_spawner" "targetname" "t13" "origin" "1536 -472 -36" "cnt_cubeofforce" "1" } ---------------------------- { #10 "classname" "trigger_crosslevel_target" "target" "t14" "model" "*13" } { #40 "classname" "item_spawner" "targetname" "t14" "cnt_teleport" "1" "origin" "1536 -808 -36" } ---------------------------- { #11 "classname" "trigger_interval" "target" "t15" "wait" ".6" "origin" "320 1024 72" } { #41 "classname" "func_door_rotating" "targetname" "t15" "dmg" "1000" "flags" "60" "spawnflags" "160" "wait" "0" "flags2" "1" "abslight" ".25" "soundtype" "9" "origin" "0 1440 216" "model" "*18" } { #42 "classname" "func_door_rotating" "targetname" "t15" "dmg" "1000" "flags" "60" "spawnflags" "162" "wait" "0" "flags2" "1" "abslight" ".25" "soundtype" "9" "origin" "0 1312 216" "model" "*25" } { #43 "classname" "func_door_rotating" "targetname" "t15" "dmg" "1000" "spawnflags" "160" "flags" "60" "wait" "0" "flags2" "1" "abslight" ".25" "soundtype" "9" "origin" "0 1216 216" "model" "*24" } { #44 "classname" "func_door_rotating" "targetname" "t15" "dmg" "1000" "spawnflags" "162" "flags" "60" "wait" "0" "flags2" "1" "abslight" ".25" "soundtype" "9" "origin" "0 1088 216" "model" "*19" } ---------------------------- { #12 "classname" "func_button" "target" "t17" "abslight" ".1" "wait" "2" "angle" "-2" "model" "*22" } { #46 "classname" "func_door" "targetname" "t17" "angle" "-2" "soundtype" "2" "model" "*21" } ---------------------------- { #13 "classname" "func_button" "target" "t18" "abslight" ".2" "killtarget" "t26" "wait" "-1" "lip" "16" "angle" "0" "model" "*23" } { #47 "classname" "breakable_brush" "targetname" "t18" "abslight" ".1" "thingtype" "3" "spawnflags" "64" "model" "*31" } { #48 "classname" "breakable_brush" "targetname" "t18" "abslight" ".5" "model" "*37" } { #49 "classname" "weather_lightning_start" "target" "t16" "targetname" "t18" "lifespan" "1.5" "wait" "-1" "origin" "-1056 128 232" } | { #45 | "classname" "weather_lightning_end" | "targetname" "t16" | "origin" "-1056 128 -208" | } { #50 "classname" "weather_lightning_start" "target" "t49" "targetname" "t18" "lifespan" "1.5" "wait" "-1" "origin" "-1152 32 -192" } | { #106 | "classname" "weather_lightning_end" | "targetname" "t49" | "origin" "-960 224 -192" | } { #51 "classname" "weather_lightning_start" "target" "t50" "targetname" "t18" "lifespan" "1.5" "wait" "-1" "origin" "-1152 224 -192" } | { #108 | "classname" "weather_lightning_end" | "targetname" "t50" | "origin" "-960 32 -192" | } ---------------------------- { #14 "classname" "func_button" "target" "t19" "abslight" ".25" "angle" "270" "wait" "-1" "lip" "16" "model" "*29" } { #15 "classname" "func_button" "target" "t19" "abslight" ".25" "lip" "16" "wait" "-1" "angle" "270" "model" "*28" } { #52 "classname" "trigger_counter" "target" "t20" "targetname" "t19" "model" "*30" } | { #54 | "classname" "func_door" | "targetname" "t20" | "angle" "180" | "lip" "64" | "speed" "25" | "wait" "-1" | "soundtype" "2" | "model" "*27" | } | { #55 | "classname" "func_door" | "targetname" "t20" | "wait" "-1" | "speed" "25" | "lip" "64" | "angle" "0" | "soundtype" "2" | "model" "*26" | } ---------------------------- { #16 "classname" "trigger_once" "target" "t2" "model" "*4" } { #53 "classname" "trigger_relay" "target" "t3" "targetname" "t2" "origin" "32 -736 168" "delay" ".5" } | { #68 | "classname" "func_door_rotating" | "targetname" "t3" | "soundtype" "1" | "spawnflags" "2" | "wait" "-1" | "flags" "180" | "abslight" ".25" | "origin" "76 -884 112" | "model" "*3" | } | { #69 | "classname" "trigger_relay" | "target" "t4" | "targetname" "t3" | "origin" "0 -736 168" | "delay" ".5" | } | | { #80 | | "classname" "func_door_rotating" | | "targetname" "t4" | | "soundtype" "1" | | "wait" "-1" | | "flags" "180" | | "abslight" ".25" | | "origin" "-76 -884 72" | | "model" "*2" | | } | | { #81 | | "classname" "trigger_relay" | | "target" "t5" | | "targetname" "t4" | | "origin" "-32 -736 168" | | "delay" ".5" | | } | | | { #107 | | | "classname" "func_door_rotating" | | | "targetname" "t5" | | | "soundtype" "1" | | | "wait" "-1" | | | "spawnflags" "2" | | | "flags" "180" | | | "abslight" ".25" | | | "origin" "76 -884 32" | | | "model" "*1" | | | } ---------------------------- { #17 "classname" "trigger_interval" "target" "t26" "wait" "1" "origin" "-1056 -112 -24" } { #64 "classname" "trigger_multiple" "target" "t25" "targetname" "t26" "origin" "-1056 -96 -32" "model" "*32" } | { #60 | "classname" "weather_lightning_start" | "target" "t21" | "targetname" "t25" | "lifespan" ".5" | "wait" "-1" | "origin" "-936 32 -192" | } | | { #56 | | "classname" "weather_lightning_end" | | "targetname" "t21" | | "origin" "-936 224 -192" | | } | { #61 | "classname" "weather_lightning_start" | "target" "t22" | "targetname" "t25" | "lifespan" ".5" | "wait" "-1" | "origin" "-960 248 -192" | } | | { #57 | | "classname" "weather_lightning_end" | | "targetname" "t22" | | "origin" "-1152 248 -192" | | } | { #62 | "classname" "weather_lightning_start" | "target" "t23" | "targetname" "t25" | "lifespan" ".5" | "wait" "-1" | "origin" "-1176 224 -192" | } | | { #58 | | "classname" "weather_lightning_end" | | "targetname" "t23" | | "origin" "-1176 32 -192" | | } | { #63 | "classname" "weather_lightning_start" | "target" "t24" | "targetname" "t25" | "lifespan" ".5" | "wait" "-1" | "origin" "-1152 8 -192" | } | | { #59 | | "classname" "weather_lightning_end" | | "targetname" "t24" | | "origin" "-960 8 -192" | | } ---------------------------- { #18 "classname" "worldspawn" "target" "t43" "CD" "13" "wad" "gfx/roman.wad" "message" "289" "MIDI" "roma2" } { #85 "classname" "light" "targetname" "t43" "style" "32" "spawnflags" "1" "light" "400" "origin" "0 3456 -392" } { #86 "classname" "func_train" "target" "t27" "targetname" "t43" "soundtype" "2" "speed" "25" "model" "*39" } | { #65 | "classname" "path_corner" | "target" "t28" | "targetname" "t27" | "origin" "-64 3360 -376" | } | | { #66 | | "classname" "path_corner" | | "target" "t27" | | "targetname" "t28" | | "wait" "-1" | | "origin" "-96 3296 -376" | | } | | | | loop: target "t27" is parent { #87 "classname" "func_train" "target" "t29" "targetname" "t43" "soundtype" "2" "speed" "25" "model" "*38" } | { #67 | "classname" "path_corner" | "target" "t30" | "targetname" "t29" | "origin" "0 3360 -376" | } | | { #70 | | "classname" "path_corner" | | "target" "t29" | | "targetname" "t30" | | "wait" "-1" | | "origin" "32 3296 -376" | | } | | | | loop: target "t29" is parent { #88 "classname" "func_train" "target" "t31" "targetname" "t43" "soundtype" "2" "speed" "25" "model" "*45" } | { #71 | "classname" "path_corner" | "target" "t32" | "targetname" "t31" | "origin" "0 3392 -376" | } | | { #72 | | "classname" "path_corner" | | "target" "t31" | | "targetname" "t32" | | "wait" "-1" | | "origin" "64 3360 -376" | | } | | | | loop: target "t31" is parent { #89 "classname" "func_train" "target" "t33" "targetname" "t43" "soundtype" "2" "speed" "25" "model" "*44" } | { #73 | "classname" "path_corner" | "target" "t34" | "targetname" "t33" | "origin" "0 3456 -376" | } | | { #74 | | "classname" "path_corner" | | "target" "t33" | | "targetname" "t34" | | "wait" "-1" | | "origin" "64 3488 -376" | | } | | | | loop: target "t33" is parent { #90 "classname" "func_train" "target" "t35" "targetname" "t43" "soundtype" "2" "speed" "25" "model" "*43" } | { #75 | "classname" "path_corner" | "target" "t36" | "targetname" "t35" | "origin" "0 3456 -376" | } | | { #76 | | "classname" "path_corner" | | "target" "t35" | | "targetname" "t36" | | "wait" "-1" | | "origin" "32 3520 -376" | | } | | | | loop: target "t35" is parent { #91 "classname" "func_train" "target" "t37" "targetname" "t43" "soundtype" "2" "speed" "25" "model" "*40" } | { #77 | "classname" "path_corner" | "target" "t38" | "targetname" "t37" | "origin" "-96 3392 -376" | } | | { #78 | | "classname" "path_corner" | | "target" "t37" | | "targetname" "t38" | | "wait" "-1" | | "origin" "-160 3360 -376" | | } | | | | loop: target "t37" is parent { #92 "classname" "func_train" "target" "t39" "targetname" "t43" "soundtype" "2" "speed" "25" "model" "*41" } | { #79 | "classname" "path_corner" | "target" "t40" | "targetname" "t39" | "origin" "-96 3456 -376" | } | | { #82 | | "classname" "path_corner" | | "target" "t39" | | "targetname" "t40" | | "wait" "-1" | | "origin" "-160 3488 -376" | | } | | | | loop: target "t39" is parent { #93 "classname" "func_train" "target" "t41" "targetname" "t43" "soundtype" "2" "speed" "25" "model" "*42" } | { #83 | "classname" "path_corner" | "target" "t42" | "targetname" "t41" | "origin" "-64 3456 -376" | } | | { #84 | | "classname" "path_corner" | | "target" "t41" | | "targetname" "t42" | | "wait" "-1" | | "origin" "-96 3520 -376" | | } | | | | loop: target "t41" is parent ---------------------------- { #19 "classname" "trigger_once" "target" "t45" "no_puzzle_msg" "291" "puzzle_piece_1" "r6" "spawnflags" "16" "model" "*51" } { #95 "classname" "puzzle_static_piece" "targetname" "t45" "angle" "180" "puzzle_id" "r6" "origin" "192 3456 -312" } { #96 "classname" "trigger_relay" "target" "t44" "targetname" "t45" "origin" "368 3456 0" } | { #94 | "classname" "trigger_counter" | "target" "t48" | "targetname" "t44" | "count" "3" | "model" "*46" | } | | { #101 | | "classname" "func_door_rotating" | | "targetname" "t48" | | "flags" "90" | | "wait" "-1" | | "speed" "25" | | "spawnflags" "130" | | "soundtype" "2" | | "abslight" ".2" | | "origin" "-384 3456 -128" | | "model" "*48" | | } | | { #102 | | "classname" "func_door_rotating" | | "targetname" "t48" | | "soundtype" "2" | | "flags" "90" | | "spawnflags" "64" | | "speed" "25" | | "wait" "-1" | | "abslight" ".2" | | "origin" "0 3072 -128" | | "model" "*47" | | } | | { #103 | | "classname" "func_door_rotating" | | "targetname" "t48" | | "soundtype" "2" | | "flags" "90" | | "spawnflags" "66" | | "speed" "25" | | "wait" "-1" | | "abslight" ".2" | | "origin" "0 3840 -128" | | "model" "*49" | | } | | { #104 | | "classname" "func_door_rotating" | | "targetname" "t48" | | "soundtype" "2" | | "flags" "90" | | "wait" "-1" | | "speed" "25" | | "spawnflags" "128" | | "abslight" ".2" | | "origin" "384 3456 -128" | | "model" "*50" | | } | | { #105 | | "classname" "trigger_relay" | | "target" "t43" | | "targetname" "t48" | | "origin" "16 3456 -232" | | } | | | { #85 (2) | | | "classname" "light" | | | "targetname" "t43" | | | "style" "32" | | | "spawnflags" "1" | | | "light" "400" | | | "origin" "0 3456 -392" | | | } | | | { #86 (2) | | | "classname" "func_train" | | | "target" "t27" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*39" | | | } | | | | { #65 (2) | | | | "classname" "path_corner" | | | | "target" "t28" | | | | "targetname" "t27" | | | | "origin" "-64 3360 -376" | | | | } | | | | | { #66 (2) | | | | | "classname" "path_corner" | | | | | "target" "t27" | | | | | "targetname" "t28" | | | | | "wait" "-1" | | | | | "origin" "-96 3296 -376" | | | | | } | | | | | | | | | | loop: target "t27" is parent | | | { #87 (2) | | | "classname" "func_train" | | | "target" "t29" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*38" | | | } | | | | { #67 (2) | | | | "classname" "path_corner" | | | | "target" "t30" | | | | "targetname" "t29" | | | | "origin" "0 3360 -376" | | | | } | | | | | { #70 (2) | | | | | "classname" "path_corner" | | | | | "target" "t29" | | | | | "targetname" "t30" | | | | | "wait" "-1" | | | | | "origin" "32 3296 -376" | | | | | } | | | | | | | | | | loop: target "t29" is parent | | | { #88 (2) | | | "classname" "func_train" | | | "target" "t31" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*45" | | | } | | | | { #71 (2) | | | | "classname" "path_corner" | | | | "target" "t32" | | | | "targetname" "t31" | | | | "origin" "0 3392 -376" | | | | } | | | | | { #72 (2) | | | | | "classname" "path_corner" | | | | | "target" "t31" | | | | | "targetname" "t32" | | | | | "wait" "-1" | | | | | "origin" "64 3360 -376" | | | | | } | | | | | | | | | | loop: target "t31" is parent | | | { #89 (2) | | | "classname" "func_train" | | | "target" "t33" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*44" | | | } | | | | { #73 (2) | | | | "classname" "path_corner" | | | | "target" "t34" | | | | "targetname" "t33" | | | | "origin" "0 3456 -376" | | | | } | | | | | { #74 (2) | | | | | "classname" "path_corner" | | | | | "target" "t33" | | | | | "targetname" "t34" | | | | | "wait" "-1" | | | | | "origin" "64 3488 -376" | | | | | } | | | | | | | | | | loop: target "t33" is parent | | | { #90 (2) | | | "classname" "func_train" | | | "target" "t35" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*43" | | | } | | | | { #75 (2) | | | | "classname" "path_corner" | | | | "target" "t36" | | | | "targetname" "t35" | | | | "origin" "0 3456 -376" | | | | } | | | | | { #76 (2) | | | | | "classname" "path_corner" | | | | | "target" "t35" | | | | | "targetname" "t36" | | | | | "wait" "-1" | | | | | "origin" "32 3520 -376" | | | | | } | | | | | | | | | | loop: target "t35" is parent | | | { #91 (2) | | | "classname" "func_train" | | | "target" "t37" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*40" | | | } | | | | { #77 (2) | | | | "classname" "path_corner" | | | | "target" "t38" | | | | "targetname" "t37" | | | | "origin" "-96 3392 -376" | | | | } | | | | | { #78 (2) | | | | | "classname" "path_corner" | | | | | "target" "t37" | | | | | "targetname" "t38" | | | | | "wait" "-1" | | | | | "origin" "-160 3360 -376" | | | | | } | | | | | | | | | | loop: target "t37" is parent | | | { #92 (2) | | | "classname" "func_train" | | | "target" "t39" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*41" | | | } | | | | { #79 (2) | | | | "classname" "path_corner" | | | | "target" "t40" | | | | "targetname" "t39" | | | | "origin" "-96 3456 -376" | | | | } | | | | | { #82 (2) | | | | | "classname" "path_corner" | | | | | "target" "t39" | | | | | "targetname" "t40" | | | | | "wait" "-1" | | | | | "origin" "-160 3488 -376" | | | | | } | | | | | | | | | | loop: target "t39" is parent | | | { #93 (2) | | | "classname" "func_train" | | | "target" "t41" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*42" | | | } | | | | { #83 (2) | | | | "classname" "path_corner" | | | | "target" "t42" | | | | "targetname" "t41" | | | | "origin" "-64 3456 -376" | | | | } | | | | | { #84 (2) | | | | | "classname" "path_corner" | | | | | "target" "t41" | | | | | "targetname" "t42" | | | | | "wait" "-1" | | | | | "origin" "-96 3520 -376" | | | | | } | | | | | | | | | | loop: target "t41" is parent ---------------------------- { #20 "classname" "trigger_once" "target" "t46" "no_puzzle_msg" "292" "puzzle_piece_1" "r7" "spawnflags" "16" "model" "*52" } { #97 "classname" "puzzle_static_piece" "targetname" "t46" "angle" "270" "puzzle_id" "r7" "origin" "0 3648 -312" } { #98 "classname" "trigger_relay" "target" "t44" "targetname" "t46" "origin" "0 3824 0" } | { #94 (2) | "classname" "trigger_counter" | "target" "t48" | "targetname" "t44" | "count" "3" | "model" "*46" | } | | { #101 (2) | | "classname" "func_door_rotating" | | "targetname" "t48" | | "flags" "90" | | "wait" "-1" | | "speed" "25" | | "spawnflags" "130" | | "soundtype" "2" | | "abslight" ".2" | | "origin" "-384 3456 -128" | | "model" "*48" | | } | | { #102 (2) | | "classname" "func_door_rotating" | | "targetname" "t48" | | "soundtype" "2" | | "flags" "90" | | "spawnflags" "64" | | "speed" "25" | | "wait" "-1" | | "abslight" ".2" | | "origin" "0 3072 -128" | | "model" "*47" | | } | | { #103 (2) | | "classname" "func_door_rotating" | | "targetname" "t48" | | "soundtype" "2" | | "flags" "90" | | "spawnflags" "66" | | "speed" "25" | | "wait" "-1" | | "abslight" ".2" | | "origin" "0 3840 -128" | | "model" "*49" | | } | | { #104 (2) | | "classname" "func_door_rotating" | | "targetname" "t48" | | "soundtype" "2" | | "flags" "90" | | "wait" "-1" | | "speed" "25" | | "spawnflags" "128" | | "abslight" ".2" | | "origin" "384 3456 -128" | | "model" "*50" | | } | | { #105 (2) | | "classname" "trigger_relay" | | "target" "t43" | | "targetname" "t48" | | "origin" "16 3456 -232" | | } | | | { #85 (3) | | | "classname" "light" | | | "targetname" "t43" | | | "style" "32" | | | "spawnflags" "1" | | | "light" "400" | | | "origin" "0 3456 -392" | | | } | | | { #86 (3) | | | "classname" "func_train" | | | "target" "t27" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*39" | | | } | | | | { #65 (3) | | | | "classname" "path_corner" | | | | "target" "t28" | | | | "targetname" "t27" | | | | "origin" "-64 3360 -376" | | | | } | | | | | { #66 (3) | | | | | "classname" "path_corner" | | | | | "target" "t27" | | | | | "targetname" "t28" | | | | | "wait" "-1" | | | | | "origin" "-96 3296 -376" | | | | | } | | | | | | | | | | loop: target "t27" is parent | | | { #87 (3) | | | "classname" "func_train" | | | "target" "t29" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*38" | | | } | | | | { #67 (3) | | | | "classname" "path_corner" | | | | "target" "t30" | | | | "targetname" "t29" | | | | "origin" "0 3360 -376" | | | | } | | | | | { #70 (3) | | | | | "classname" "path_corner" | | | | | "target" "t29" | | | | | "targetname" "t30" | | | | | "wait" "-1" | | | | | "origin" "32 3296 -376" | | | | | } | | | | | | | | | | loop: target "t29" is parent | | | { #88 (3) | | | "classname" "func_train" | | | "target" "t31" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*45" | | | } | | | | { #71 (3) | | | | "classname" "path_corner" | | | | "target" "t32" | | | | "targetname" "t31" | | | | "origin" "0 3392 -376" | | | | } | | | | | { #72 (3) | | | | | "classname" "path_corner" | | | | | "target" "t31" | | | | | "targetname" "t32" | | | | | "wait" "-1" | | | | | "origin" "64 3360 -376" | | | | | } | | | | | | | | | | loop: target "t31" is parent | | | { #89 (3) | | | "classname" "func_train" | | | "target" "t33" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*44" | | | } | | | | { #73 (3) | | | | "classname" "path_corner" | | | | "target" "t34" | | | | "targetname" "t33" | | | | "origin" "0 3456 -376" | | | | } | | | | | { #74 (3) | | | | | "classname" "path_corner" | | | | | "target" "t33" | | | | | "targetname" "t34" | | | | | "wait" "-1" | | | | | "origin" "64 3488 -376" | | | | | } | | | | | | | | | | loop: target "t33" is parent | | | { #90 (3) | | | "classname" "func_train" | | | "target" "t35" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*43" | | | } | | | | { #75 (3) | | | | "classname" "path_corner" | | | | "target" "t36" | | | | "targetname" "t35" | | | | "origin" "0 3456 -376" | | | | } | | | | | { #76 (3) | | | | | "classname" "path_corner" | | | | | "target" "t35" | | | | | "targetname" "t36" | | | | | "wait" "-1" | | | | | "origin" "32 3520 -376" | | | | | } | | | | | | | | | | loop: target "t35" is parent | | | { #91 (3) | | | "classname" "func_train" | | | "target" "t37" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*40" | | | } | | | | { #77 (3) | | | | "classname" "path_corner" | | | | "target" "t38" | | | | "targetname" "t37" | | | | "origin" "-96 3392 -376" | | | | } | | | | | { #78 (3) | | | | | "classname" "path_corner" | | | | | "target" "t37" | | | | | "targetname" "t38" | | | | | "wait" "-1" | | | | | "origin" "-160 3360 -376" | | | | | } | | | | | | | | | | loop: target "t37" is parent | | | { #92 (3) | | | "classname" "func_train" | | | "target" "t39" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*41" | | | } | | | | { #79 (3) | | | | "classname" "path_corner" | | | | "target" "t40" | | | | "targetname" "t39" | | | | "origin" "-96 3456 -376" | | | | } | | | | | { #82 (3) | | | | | "classname" "path_corner" | | | | | "target" "t39" | | | | | "targetname" "t40" | | | | | "wait" "-1" | | | | | "origin" "-160 3488 -376" | | | | | } | | | | | | | | | | loop: target "t39" is parent | | | { #93 (3) | | | "classname" "func_train" | | | "target" "t41" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*42" | | | } | | | | { #83 (3) | | | | "classname" "path_corner" | | | | "target" "t42" | | | | "targetname" "t41" | | | | "origin" "-64 3456 -376" | | | | } | | | | | { #84 (3) | | | | | "classname" "path_corner" | | | | | "target" "t41" | | | | | "targetname" "t42" | | | | | "wait" "-1" | | | | | "origin" "-96 3520 -376" | | | | | } | | | | | | | | | | loop: target "t41" is parent ---------------------------- { #21 "classname" "trigger_once" "target" "t47" "no_puzzle_msg" "293" "puzzle_piece_1" "r8" "spawnflags" "16" "model" "*53" } { #99 "classname" "puzzle_static_piece" "targetname" "t47" "angle" "0" "puzzle_id" "r8" "origin" "-192 3456 -312" } { #100 "classname" "trigger_relay" "target" "t44" "targetname" "t47" "origin" "-368 3456 0" } | { #94 (3) | "classname" "trigger_counter" | "target" "t48" | "targetname" "t44" | "count" "3" | "model" "*46" | } | | { #101 (3) | | "classname" "func_door_rotating" | | "targetname" "t48" | | "flags" "90" | | "wait" "-1" | | "speed" "25" | | "spawnflags" "130" | | "soundtype" "2" | | "abslight" ".2" | | "origin" "-384 3456 -128" | | "model" "*48" | | } | | { #102 (3) | | "classname" "func_door_rotating" | | "targetname" "t48" | | "soundtype" "2" | | "flags" "90" | | "spawnflags" "64" | | "speed" "25" | | "wait" "-1" | | "abslight" ".2" | | "origin" "0 3072 -128" | | "model" "*47" | | } | | { #103 (3) | | "classname" "func_door_rotating" | | "targetname" "t48" | | "soundtype" "2" | | "flags" "90" | | "spawnflags" "66" | | "speed" "25" | | "wait" "-1" | | "abslight" ".2" | | "origin" "0 3840 -128" | | "model" "*49" | | } | | { #104 (3) | | "classname" "func_door_rotating" | | "targetname" "t48" | | "soundtype" "2" | | "flags" "90" | | "wait" "-1" | | "speed" "25" | | "spawnflags" "128" | | "abslight" ".2" | | "origin" "384 3456 -128" | | "model" "*50" | | } | | { #105 (3) | | "classname" "trigger_relay" | | "target" "t43" | | "targetname" "t48" | | "origin" "16 3456 -232" | | } | | | { #85 (4) | | | "classname" "light" | | | "targetname" "t43" | | | "style" "32" | | | "spawnflags" "1" | | | "light" "400" | | | "origin" "0 3456 -392" | | | } | | | { #86 (4) | | | "classname" "func_train" | | | "target" "t27" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*39" | | | } | | | | { #65 (4) | | | | "classname" "path_corner" | | | | "target" "t28" | | | | "targetname" "t27" | | | | "origin" "-64 3360 -376" | | | | } | | | | | { #66 (4) | | | | | "classname" "path_corner" | | | | | "target" "t27" | | | | | "targetname" "t28" | | | | | "wait" "-1" | | | | | "origin" "-96 3296 -376" | | | | | } | | | | | | | | | | loop: target "t27" is parent | | | { #87 (4) | | | "classname" "func_train" | | | "target" "t29" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*38" | | | } | | | | { #67 (4) | | | | "classname" "path_corner" | | | | "target" "t30" | | | | "targetname" "t29" | | | | "origin" "0 3360 -376" | | | | } | | | | | { #70 (4) | | | | | "classname" "path_corner" | | | | | "target" "t29" | | | | | "targetname" "t30" | | | | | "wait" "-1" | | | | | "origin" "32 3296 -376" | | | | | } | | | | | | | | | | loop: target "t29" is parent | | | { #88 (4) | | | "classname" "func_train" | | | "target" "t31" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*45" | | | } | | | | { #71 (4) | | | | "classname" "path_corner" | | | | "target" "t32" | | | | "targetname" "t31" | | | | "origin" "0 3392 -376" | | | | } | | | | | { #72 (4) | | | | | "classname" "path_corner" | | | | | "target" "t31" | | | | | "targetname" "t32" | | | | | "wait" "-1" | | | | | "origin" "64 3360 -376" | | | | | } | | | | | | | | | | loop: target "t31" is parent | | | { #89 (4) | | | "classname" "func_train" | | | "target" "t33" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*44" | | | } | | | | { #73 (4) | | | | "classname" "path_corner" | | | | "target" "t34" | | | | "targetname" "t33" | | | | "origin" "0 3456 -376" | | | | } | | | | | { #74 (4) | | | | | "classname" "path_corner" | | | | | "target" "t33" | | | | | "targetname" "t34" | | | | | "wait" "-1" | | | | | "origin" "64 3488 -376" | | | | | } | | | | | | | | | | loop: target "t33" is parent | | | { #90 (4) | | | "classname" "func_train" | | | "target" "t35" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*43" | | | } | | | | { #75 (4) | | | | "classname" "path_corner" | | | | "target" "t36" | | | | "targetname" "t35" | | | | "origin" "0 3456 -376" | | | | } | | | | | { #76 (4) | | | | | "classname" "path_corner" | | | | | "target" "t35" | | | | | "targetname" "t36" | | | | | "wait" "-1" | | | | | "origin" "32 3520 -376" | | | | | } | | | | | | | | | | loop: target "t35" is parent | | | { #91 (4) | | | "classname" "func_train" | | | "target" "t37" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*40" | | | } | | | | { #77 (4) | | | | "classname" "path_corner" | | | | "target" "t38" | | | | "targetname" "t37" | | | | "origin" "-96 3392 -376" | | | | } | | | | | { #78 (4) | | | | | "classname" "path_corner" | | | | | "target" "t37" | | | | | "targetname" "t38" | | | | | "wait" "-1" | | | | | "origin" "-160 3360 -376" | | | | | } | | | | | | | | | | loop: target "t37" is parent | | | { #92 (4) | | | "classname" "func_train" | | | "target" "t39" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*41" | | | } | | | | { #79 (4) | | | | "classname" "path_corner" | | | | "target" "t40" | | | | "targetname" "t39" | | | | "origin" "-96 3456 -376" | | | | } | | | | | { #82 (4) | | | | | "classname" "path_corner" | | | | | "target" "t39" | | | | | "targetname" "t40" | | | | | "wait" "-1" | | | | | "origin" "-160 3488 -376" | | | | | } | | | | | | | | | | loop: target "t39" is parent | | | { #93 (4) | | | "classname" "func_train" | | | "target" "t41" | | | "targetname" "t43" | | | "soundtype" "2" | | | "speed" "25" | | | "model" "*42" | | | } | | | | { #83 (4) | | | | "classname" "path_corner" | | | | "target" "t42" | | | | "targetname" "t41" | | | | "origin" "-64 3456 -376" | | | | } | | | | | { #84 (4) | | | | | "classname" "path_corner" | | | | | "target" "t41" | | | | | "targetname" "t42" | | | | | "wait" "-1" | | | | | "origin" "-96 3520 -376" | | | | | } | | | | | | | | | | loop: target "t41" is parent ---------------------------- { #22 "classname" "trigger_once" "target" "t51" "spawnflags" "196608" "model" "*67" } { #109 "classname" "trigger_relay" "target" "t19" "targetname" "t51" "origin" "-56 1696 -48" } | { #52 (2) | "classname" "trigger_counter" | "target" "t20" | "targetname" "t19" | "model" "*30" | } | | { #54 (2) | | "classname" "func_door" | | "targetname" "t20" | | "angle" "180" | | "lip" "64" | | "speed" "25" | | "wait" "-1" | | "soundtype" "2" | | "model" "*27" | | } | | { #55 (2) | | "classname" "func_door" | | "targetname" "t20" | | "wait" "-1" | | "speed" "25" | | "lip" "64" | | "angle" "0" | | "soundtype" "2" | | "model" "*26" | | } { #110 "classname" "trigger_relay" "target" "t19" "targetname" "t51" "origin" "56 1696 -48" } | { #52 (3) | "classname" "trigger_counter" | "target" "t20" | "targetname" "t19" | "model" "*30" | } | | { #54 (3) | | "classname" "func_door" | | "targetname" "t20" | | "angle" "180" | | "lip" "64" | | "speed" "25" | | "wait" "-1" | | "soundtype" "2" | | "model" "*27" | | } | | { #55 (3) | | "classname" "func_door" | | "targetname" "t20" | | "wait" "-1" | | "speed" "25" | | "lip" "64" | | "angle" "0" | | "soundtype" "2" | | "model" "*26" | | } ---------------------------- { #23 "classname" "func_button" "target" "t6" "abslight" ".25" "wait" "-1" "lip" "16" "angle" "0" "model" "*6" } { #111 "classname" "func_door" "targetname" "t6" "angle" "-1" "wait" "-1" "soundtype" "2" "model" "*64" } { #112 "classname" "func_door" "targetname" "t6" "wait" "-1" "angle" "-1" "soundtype" "2" "model" "*5" } { #113 "classname" "func_door" "targetname" "t6" "wait" "-1" "angle" "-1" "soundtype" "2" "model" "*63" } ---------------------------- { #24 "classname" "trigger_crosslevel_target" "target" "t7" "spawnflags" "1" "model" "*9" } { #114 "classname" "item_spawner" "targetname" "t7" "cnt_mana_boost" "1" "origin" "960 -472 -36" } ---------------------------- { #25 "classname" "trigger_crosslevel_target" "target" "t8" "spawnflags" "2" "model" "*16" } { #115 "classname" "item_spawner" "targetname" "t8" "cnt_sh_boost" "1" "origin" "960 -808 -36" } ---------------------------- { #26 "classname" "trigger_crosslevel_target" "target" "t9" "spawnflags" "4" "model" "*10" } { #116 "classname" "item_spawner" "targetname" "t9" "cnt_haste" "1" "origin" "1152 -472 -36" } ---------------------------- { #27 "classname" "info_player_coop" "targetname" "romeric7" "angle" "0" "origin" "-400 -736 88" } { #28 "classname" "info_player_coop" "targetname" "romeric7" "angle" "180" "origin" "400 -640 88" } { #29 "classname" "info_player_coop" "targetname" "romeric7" "angle" "180" "origin" "400 -832 88" } { #30 "classname" "info_player_coop" "targetname" "romeric7" "angle" "90" "origin" "-80 -752 24" } { #31 "classname" "info_player_coop" "targetname" "romeric7" "origin" "-400 -640 88" "angle" "0" } { #32 "classname" "info_player_coop" "targetname" "romeric7" "origin" "-400 -832 88" "angle" "0" } { #33 "classname" "info_player_coop" "targetname" "romeric7" "origin" "400 -736 88" "angle" "180" } { #34 "classname" "info_player_coop" "targetname" "romeric7" "origin" "80 -752 24" "angle" "90" } { #35 "classname" "info_player_start" "targetname" "romeric7" "origin" "0 -688 24" "angle" "90" } gamecode/hc/000077500000000000000000000000001444734033100132175ustar00rootroot00000000000000gamecode/hc/h2/000077500000000000000000000000001444734033100135305ustar00rootroot00000000000000gamecode/hc/h2/MG_AI.hc000066400000000000000000000720731444734033100147310ustar00rootroot00000000000000/* ================================================================== MG_AI.HC Michael Gummelt Artificial Intelligence Routines!!! US Patent# 2.56734376314532533 + E17 Hoo-hah! ================================================================== */ /* ============================================== GENERAL ============================================== */ /* ============= get_visibility Checks for drf_translucent and abslight of an object, and uses that and it's world lighting value to set it's visibility value >=1 = Totally visible (default) . . . <=0 = Totally invisible This value should be used in monster aiming as well. NOTE: This only works on players since light_level info is taken from player's weaponmodel lighting (0-255) ============= */ void get_visibility (entity targ , float range_mod) { //NOTE: incorporate distance? float base, divider, attack_mod; //FIXME: .light_level gives a value of 0 if MLS_POWERMODE is on... //Temp fix for now... if(targ.classname!="player"||targ.drawflags&MLS_POWERMODE) { targ.visibility=1; return; } if(targ.effects&EF_NODRAW) { targ.visibility=0; return; } if(targ.drawflags&DRF_TRANSLUCENT) { if(targ.model=="models/assassin.mdl") divider=3+targ.level;//Bonus for hiding in shadows else divider=3; //Makes it 3 times harder to see } else divider=1; if(targ.drawflags&MLS_ABSLIGHT) base=targ.abslight/2.5; else base=targ.light_level/75;//75 is semi-fullbright if(range_mod) range_mod=vlen(targ.origin-self.origin)/333; else range_mod = 1; if(targ.last_attack>time - 3)//Remember where they were when fired attack_mod=time - targ.last_attack; targ.visibility=base/divider/range_mod + attack_mod; } /* ============= float visibility_good (entity targ,float chance_mod) MG Does a random check to see if self can see the target based it's visibility (calls get_visibility for that targ first) The higher the chance_mod, the lower the chance of good visibility. ============= */ float visibility_good (entity targ,float chance_mod) { if(!targ) return FALSE; get_visibility(targ,TRUE); if(random(chance_mod)256) ignore_height=FALSE; //also check to make sure you can't walkmove forward if(self.jump_flag>time) //Don't jump too many times in a row { // dprint("just jumped\n"); return FALSE; } if(pointcontents(self.goalentity.origin)!=CONTENT_EMPTY) { // dprint("goalentity in water or lava\n"); return FALSE; } if(!visible(self.goalentity)) { // dprint("can't see goalentity\n"); return FALSE; } if(!ignore_height&&self.goalentity.absmin_z+36>=self.absmin_z&&self.think!=SpiderJumpBegin&&self.classname!="monster_mezzoman") { // dprint("not above goalentity, and not spider\n"); return FALSE; } if(!self.flags&FL_ONGROUND) { // dprint("not on ground\n"); return FALSE; } if(!self.goalentity.flags&FL_ONGROUND&&self.goalentity.classname!="waypoint") { // dprint("goalentity in air\n"); return FALSE; } if(!infront(self.goalentity)) { // dprint("goalentity not in front\n"); return FALSE; } if(vlen(spot1-spot2)>777&&!ignore_height) { // dprint("too far away\n"); return FALSE; } if(vlen(spot1-spot2)<=100)//&&self.think!=SpiderMeleeBegin) { // dprint("too close & not spider\n"); return FALSE; } if(self.think==SpiderJumpBegin) jump_height=vlen((self.goalentity.absmax+self.goalentity.absmin)*0.5-self.origin)/13; else if(self.classname=="monster_mezzoman") if(self.goalentity.absmin_z>=self.absmin_z+36) { jump_height=vlen((self.goalentity.absmax+self.goalentity.absmin)*0.5-self.origin)/13; jumpup=TRUE; } else if(self.goalentity.absmin_z>self.absmin_z - 36) { if(ignore_height) jump_height=vlen((self.goalentity.absmax+self.goalentity.absmin)*0.5-self.origin)/13; else { // dprint("Mezzo: Goal not above and not below\n"); return FALSE; } } spot1=self.origin; spot1_z=self.absmax_z; spot2=spot1; spot2_z+=36; traceline(spot1, spot2,FALSE,self); if(trace_fraction<1) { // dprint("not enough room above\n"); return FALSE; } if(!jumpup) { float content; // spot1+=normalize(v_forward)*((self.maxs_x+self.maxs_y)*0.5); spot1+=jumpdir*((self.maxs_x+self.maxs_y)*0.5); traceline(self.origin, spot1 + '0 0 36',FALSE,self); if(trace_fraction<1) { // dprint("not enough room in front\n"); return FALSE; } traceline(spot1,spot1+jumpdir*64 - '0 0 500',FALSE,self); content=pointcontents(trace_endpos); if(content==CONTENT_WATER||content==CONTENT_SLIME||content==CONTENT_LAVA) { // dprint("won't jump in water\n"); return FALSE; } } ai_face(); // self.ideal_yaw=jumpdir_y; // ChangeYaw(); if(self.think!=SpiderJumpBegin) { self.jump_flag=time + 7; //Only try to jump once every 7 seconds SightSound(); if(!jumpup) { self.velocity=jumpdir*jump_height*17*self.scale; self.velocity_z = jump_height*12*self.scale; } else { self.velocity=jumpdir*jump_height*10*self.scale; self.velocity_z = jump_height*14*self.scale; } self.flags(-)FL_ONGROUND; if(self.th_jump) self.th_jump(); else thinktime self : 0.3; } else { self.level=jump_height; return TRUE; } } /* ==================================================================== void MonsterCheckContents () MG Monsters check to see if they're in lava or under water and do damage do themselves if appropriate. void do_contents_dam () Just spawns a temporary ent to damage self, using T_Damage on self does weird stuff- won't kill self, just become invincible ==================================================================== */ void do_contents_dam () { T_Damage(self.enemy,world,world,self.dmg); if(self.dmg==5) { self.classname="contents damager"; setorigin(self,self.enemy.origin+self.enemy.view_ofs); DeathBubbles(1); } remove(self); } void MonsterCheckContents () { if(random()>0.3) return; if(pointcontents(self.origin)==CONTENT_LAVA) { if(self.flags&FL_FIREHEAL) { if(self.health0.05&&self.movetype==MOVETYPE_STEP) self.flags(-)FL_ONGROUND; if(trace_fraction==1) return; slope=trace_plane_normal; } new_angles=vectoangles(slope); new_angles_x=(90-new_angles_x)*-1;//Gets actual slope new_angles2='0 0 0'; new_angles2_y=new_angles_y; makevectors(new_angles2); mod=v_forward*old_right; if(mod<0) mod=1; else mod=-1; dot=v_forward*old_forward; self.angles_x=dot*new_angles_x; self.angles_z=(1-fabs(dot))*new_angles_x*mod; } /* ============================================== IMP ============================================== */ /* ================================================================ checkenemy() Checks to see if enemy is of the same monstertype and old enemy is alive and visible. If so, changes back to it's last enemy. ================================================================ */ void checkenemy (void) { entity oldtarget; /* if(self.enemy==world) { if(!LocateTarget()) { if(self.controller.classname=="player") self.enemy=self.controller; else { self.enemy=world; self.think=self.th_stand; } } self.goalentity=self.enemy; return; } */ if(self.enemy.classname=="player"&&self.enemy.flags2&FL_ALIVE&&self.enemy!=self.controller) return; if (!self.enemy.flags2&FL_ALIVE||self.enemy==self.controller) { if(self.controller.classname=="player") { self.enemy = self.controller; self.goalentity=self.enemy; } else self.enemy = world; if (self.oldenemy.flags2&FL_ALIVE) { self.enemy = self.oldenemy; self.goalentity = self.enemy; self.think = self.th_run; } else if(LocateTarget()) { self.goalentity = self.enemy; self.think = self.th_run; } else { if(self.controller.classname=="player") self.goalentity=self.enemy=self.controller; else self.goalentity=self.enemy=world; if (self.pathentity) self.think=self.th_walk; else self.think=self.th_stand; } thinktime self : 0; return; } if(self.classname=="monster_imp_lord") return; if(self.oldenemy.classname=="player"&&(self.oldenemy.flags2&FL_ALIVE)&&visible(self.oldenemy)) { if((self.model=="models/spider.mdl"||self.model=="models/scorpion.mdl")&&self.enemy.model==self.model) self.enemy=self.oldenemy; else { oldtarget=self.enemy; self.enemy=self.oldenemy; self.oldenemy=oldtarget; } self.goalentity=self.enemy; } } /* ================================================================ fov() Field-Of-View Returns TRUE if vector from entity "from" to entity "targ" is within "scope" degrees of entity "from"'s forward angle. ================================================================ */ float fov(entity targ,entity from,float scope) { vector spot1,spot2; float dot; spot1=from.origin+from.proj_ofs; spot2=(targ.absmin+targ.absmax)*0.5; if(from.classname=="player") makevectors(from.v_angle); else makevectors(from.angles); // scope=1 - (scope/180);//converts angles into % dot=normalize(spot2-spot1)*v_forward; dot=180 - (dot*180); // dprintf("FOV value : %s\n",dot); if(dot<=scope) return TRUE; return FALSE; } /* ================================================================ check_pos_enemy() MG Checks to see if enemy is visible, if so, remember the spot for waypoints, else set your waypoint at the last spot you saw him. Also resets search_time timer if you see him. ================================================================ */ void check_pos_enemy () { if(!self.mintel) return; if(!visible(self.enemy)) { self.attack_state = AS_STRAIGHT; SetNextWaypoint(); if(self.model=="models/imp.mdl") //Imps keep looking in general area for a while if(self.search_time=THINGTYPE_WEBS) traceline (trace_endpos, destiny, FALSE, trace_ent); if (trace_ent == targ) return TRUE; if(whole_body) { if(self.attack_state!=AS_FERRY) self.attack_state = AS_SLIDING; return FALSE; } if(trace_ent.health>25||!trace_ent.takedamage||(trace_ent.flags&FL_MONSTER&&trace_ent.classname!="player_sheep")) {//Don't have a clear shot, and don't want to shoot obstruction self.attack_state = AS_SLIDING; return FALSE; } return TRUE; } /* ================================================================ check_view(entity targ,vector org,vector dir,float dist,float interval) MG Will see if it can see the targ entity along the dir given to it- used to determine which direction a monster should move in to get a clear line of sight to the targ. Returns the distance it took to see targ, FALSE if couldn't. ================================================================ */ float check_view(entity targ,vector org,vector dir,float dist,float interval) { float dist_counter; newmis=spawn(); dir=normalize(dir); while(dist_countermaxspeed) go_dist=maxspeed; else if(go_distmaxdist) if(goaldist>0) goaldist=maxdist; else goaldist=maxdist*-1; if(!movestep(0,0,goaldist, FALSE)) return FALSE; } return TRUE; } /* ==================================================================== MEDUSA ==================================================================== */ /* ============================================================ float lineofsight(entity targ, entity from) MG Traces a line along "from"'s view_ofs along v_forward and returns VRAI if it hits targ, FAUX if not, mon ami. ============================================================ */ float lineofsight(entity targ, entity from) { //FIXME: account for monster's lack of pitch if z diff vector org,dir; if(from.classname=="player") makevectors(from.v_angle); /* else if(from.classname=="monster_medusa") makevectors(from.angles+from.angle_ofs); */ else makevectors(from.angles); org=from.origin+from.view_ofs; dir=normalize(v_forward); traceline(org, org+dir*1000,FALSE,from); if(trace_ent!=targ) return FALSE; else { // dprint("Line of sight from "); // dprint(from.classname); // dprint(" to "); // dprint(targ.classname); // dprint(" confirmed\n"); return TRUE; } } /* ==================================================================== EIDOLON ==================================================================== */ /* ===================================================== entity riderpath_findbest(entity subject_path) MG Returns closest rider path corner that "subject_path" leads to. Used for Rider Bosses. ===================================================== */ entity riderpath_findbest(entity subject_path) { entity search,found,best_path; float next,num_points,position,bestdist,lastdist; num_points = 0; if (subject_path.next_path_1) num_points += 1; if (subject_path.next_path_2) num_points += 1; if (subject_path.next_path_3) num_points += 1; if (subject_path.next_path_4) num_points += 1; if (subject_path.next_path_5) num_points += 1; if (subject_path.next_path_6) num_points += 1; if (!num_points) { dprintf("rider path %s has no next points\n",subject_path.path_id); remove(self); return world; } bestdist=vlen(self.goalentity.origin-self.origin); lastdist=bestdist; position=0; best_path=world; while(position0) { traceline(startpos,startpos-'0 0 18',TRUE,self); if(trace_fraction==1) return FALSE; startpos+=dir*5; diff_count-=1; } return TRUE; } /* ====================================================== float check_heading_left_or_right (entity object) MG Will check to see if the given object will be to the left or the right of self once it gets to self. Uses it's current position and extrapolates based on it's heading (velocity). Will return: 1 = left -1 = right 0 = neither. Special case: If called by a monster that's not awake, will return opposite of these assuming that the monster wants to cut off player- only used by the Rolling Ambush Mezzomen. ====================================================== */ float check_heading_left_or_right (entity object) { vector spot1, spot2, vec; float dot, rng, reverse; makevectors (self.angles); spot1 = self.origin + self.view_ofs; spot2 = object.origin; //To get the eventual location of the projectile when it gets to him... rng=vlen(spot1-spot2); spot2+=normalize(object.velocity)*(rng+15);//Add a bit for good measure vec = normalize (spot2 - spot1); //FIXME? What about behind me? if(object.classname=="player"&&!self.monster_awake) { self.monster_awake=TRUE; sound(self,CHAN_VOICE,"mezzo/attack.wav",1,ATTN_NORM); reverse=-1; } else reverse=1; dot = vec * v_right; if ( dot > 0) return -1*reverse; dot = vec * (v_right*-1); if ( dot > 0) return 1*reverse; return 0; } /* ====================================================== float navigate (float walkspeed) MG Checks to see which side of the entity is blocked and will move in the opposite direction using walkmove (for left-right) or movestep (for up-down) if it can. Will move the specified distance. If it can't move that way or it doesn't find a blocked side, it returns false. Meant for use with flying and swimming monsters because movetogoal doesn't make them navigate! ====================================================== */ float navigate (float walkspeed) { vector checkdir,org,new_angle; float vert_size,horz_size; makevectors(self.angles); checkdir=v_right; org=self.origin+checkdir*self.size_x; vert_size=self.size_z/2; horz_size=(self.size_x+self.size_y)/4; traceline(org,org+v_forward*horz_size,FALSE,self); if(trace_fraction==1&&!trace_allsolid) { checkdir=v_right*-1; org=self.origin+checkdir*horz_size; traceline(org,org+v_forward*horz_size,FALSE,self); } if(self.flags&FL_FLY||self.flags&FL_SWIM) { if(trace_fraction==1&&!trace_allsolid) { checkdir=v_up; org=self.origin+checkdir*vert_size; traceline(org,org+v_forward*horz_size,FALSE,self); } if(trace_fraction==1&&!trace_allsolid) { checkdir=v_up*-1; org=self.origin+checkdir*vert_size; traceline(org,org+v_forward*horz_size,FALSE,self); } } if(trace_fraction<1||trace_allsolid) { if(checkdir==v_right||checkdir==v_right*-1) { new_angle=vectoangles(checkdir*-1); if(!walkmove(new_angle_y,walkspeed,FALSE)) { // dprint("Couldn't Side-step\n"); return FALSE; } // dprint("Side-stepped\n"); return TRUE; } if(checkdir==v_up) walkspeed*=-1; if(!movestep(0,0,walkspeed,FALSE)) { // dprint("couldn't move up/down\n"); return FALSE; } // dprint("up-down move\n"); return TRUE; } // dprint("Not blocked\n"); return FALSE;//FOUND NO BLOCKING!!! } /* ============================================================= vector extrapolate_pos_for_speed (vector p1,float pspeed,entity targ,float accept) MG Estimates where the "targ" will be by the time a projectile travelling at "pspeed" leaving "org" arrives at "targ"'s origin. It then calculates a new spot to shoot at so that the projectile will arrive at such spot at the same time as "targ". Will return '0 0 0' (FALSE) if there is not a clear line of fire to the spot or if the new vector is out of the acceptable range (based on dot product of original vec and the new vec). ============================================================= */ vector extrapolate_pos_for_speed (vector p1,float pspeed,entity targ,float accept) { float dist1,dist2,tspeed,dot,eta1,eta2,eta_delta,failed; vector p2,p3,targ_dir,vec1,vec2; // dprint("Extrapolating\n"); p2=targ.origin+targ.view_ofs; //current target viewport vec1=p2 - p1; //vector to p2 dist1=vlen(vec1); //distance to p2 vec1=normalize(vec1); //direction to p2 targ_dir=targ.velocity; //target velocity tspeed=vlen(targ_dir); //target speed targ_dir=normalize(targ_dir); //target direction eta1=dist1/pspeed; //Estimated time of arrival of projectile to p2 p3=p2 + targ_dir * tspeed * eta1; //Extrapolated postion of targ at time + eta1 dist2=vlen(p3-p1); //new distance to p3 eta2=dist2/pspeed; //ETA of projectile to p3 eta_delta=eta2-eta1; //change in ETA's p3+=targ_dir*tspeed*eta_delta*random();//Add any diff in ETA to p3's location,random a little in case they slow down traceline(p1,p3,FALSE,self); if(trace_fraction<1) { if(trace_ent.thingtype>=THINGTYPE_WEBS) traceline (trace_endpos, p3, FALSE, trace_ent); if(trace_fraction<1) if(trace_ent.health>25||!trace_ent.takedamage||(trace_ent.flags&FL_MONSTER&&trace_ent.classname!="player_sheep")) {//Don't have a clear shot, and don't want to shoot obstruction // dprint("No clear shot\n"); self.attack_state = AS_SLIDING; failed=TRUE; } } vec2=normalize(p3-p1); //New vector to p3 dot=vec1*vec2; if(dot0) { vofs=v_up*0.5*random(0-ofs,ofs)+v_right*1.5*random(0-ofs,ofs); return vofs; } return '0 0 0'; } gamecode/hc/h2/ai.hc000066400000000000000000000605011444734033100144370ustar00rootroot00000000000000void(entity etemp, entity stemp, entity stemp, float dmg) T_Damage; /* .enemy Will be world if not currently angry at anyone. .pathcorner The next path spot to walk toward. If .enemy, ignore .pathcorner When an enemy is killed, the monster will try to return to it's path. .hunt_time Set to time + something when the player is in sight, but movement straight for him is blocked. This causes the monster to use wall following code for movement direction instead of sighting on the player. .ideal_yaw A yaw angle of the intended direction, which will be turned towards at up to 45 deg / state. If the enemy is in view and hunt_time is not active, this will be the exact line towards the enemy. .pausetime A monster will leave it's stand state and head towards it's .pathcorner when time > .pausetime. walkmove(angle, speed) primitive is all or nothing */ float ArcherCheckAttack (void); float MedusaCheckAttack (void); void()SetNextWaypoint; void()SpiderMeleeBegin; void()spider_onwall_wait; float(entity targ , entity from)infront_of_ent; void(entity proj)mezzo_choose_roll; void()hive_die; //void()check_climb; // // globals // float current_yaw; // // when a monster becomes angry at a player, that monster will be used // as the sight target the next frame so that monsters near that one // will wake up even if they wouldn't have noticed the player // float sight_entity_time; float(float v) anglemod = { while (v >= 360) v = v - 360; while (v < 0) v = v + 360; return v; }; //============================================================================ /* ============= range returns the range catagorization of an entity reletive to self 0 melee range, will become hostile even if back is turned 1 visibility and infront, or visibility and show hostile 2 infront and show hostile 3 only triggered by damage ============= */ float(entity targ) range = { vector spot1, spot2; float r,melee; if((self.solid==SOLID_BSP||self.solid==SOLID_TRIGGER)&&self.origin=='0 0 0') spot1=(self.absmax+self.absmin)*0.5; else spot1 = self.origin + self.view_ofs; if((targ.solid==SOLID_BSP||targ.solid==SOLID_TRIGGER)&&targ.origin=='0 0 0') spot2=(targ.absmax+targ.absmin)*0.5; else spot2 = targ.origin + targ.view_ofs; r = vlen (spot1 - spot2); if (self.classname=="monster_mummy") melee = 50; else melee = 100; if (r < melee) return RANGE_MELEE; if (r < 500) return RANGE_NEAR; if (r < 1000) return RANGE_MID; return RANGE_FAR; }; /* ============= visible2ent returns 1 if the entity is visible to self, even if not infront () ============= */ float visible2ent (entity targ, entity forent) { vector spot1, spot2; if((forent.solid==SOLID_BSP||forent.solid==SOLID_TRIGGER)&&forent.origin=='0 0 0') spot1=(forent.absmax+forent.absmin)*0.5; else spot1 = forent.origin + forent.view_ofs; if((targ.solid==SOLID_BSP||targ.solid==SOLID_TRIGGER)&&targ.origin=='0 0 0') spot2=(targ.absmax+targ.absmin)*0.5; else spot2 = targ.origin + targ.view_ofs; traceline (spot1, spot2, TRUE, forent); // see through other monsters if(trace_ent.thingtype>=THINGTYPE_WEBS) traceline (trace_endpos, spot2, TRUE, trace_ent); // else if (trace_inopen && trace_inwater)//FIXME? Translucent water? // return FALSE; // sight line crossed contents if (trace_fraction == 1) { if(forent.flags&FL_MONSTER) { if(visibility_good(targ,0.15 - skill/20)) return TRUE; } else return TRUE; } return FALSE; } /* ============= infront_of_ent returns 1 if the targ is in front (in sight) of from ============= */ float infront_of_ent (entity targ , entity from) { vector vec,spot1,spot2; float accept,dot; if(from.classname=="player") makevectors (from.v_angle); /* else if(from.classname=="monster_medusa") makevectors (from.angles+from.angle_ofs); */ else makevectors (from.angles); if((from.solid==SOLID_BSP||from.solid==SOLID_TRIGGER)&&from.origin=='0 0 0') spot1=(from.absmax+from.absmin)*0.5; else spot1 = from.origin + from.view_ofs; spot2=(targ.absmax+targ.absmin)*0.5; vec = normalize (spot2 - spot1); dot = vec * v_forward; accept = 0.3; if ( dot > accept) return TRUE; return FALSE; } /* ============= visible returns 1 if the entity is visible to self, even if not infront () ============= */ float visible (entity targ) { return visible2ent(targ,self); } /* ============= infront returns 1 if the entity is in front (in sight) of self ============= */ float infront (entity targ) { return infront_of_ent(targ,self); } //============================================================================ /* =========== ChangeYaw Turns towards self.ideal_yaw at self.yaw_speed Sets the global variable current_yaw Called every 0.1 sec by monsters ============ */ /* void ChangeYaw () { float ideal, move; //current_yaw = self.ideal_yaw; // mod down the current angle current_yaw = anglemod( self.angles_y ); ideal = self.ideal_yaw; if (current_yaw == ideal) return; move = ideal - current_yaw; if (ideal > current_yaw) { if (move > 180) move = move - 360; } else { if (move < -180) move = move + 360; } if (move > 0) { if (move > self.yaw_speed) move = self.yaw_speed; } else { if (move < 0-self.yaw_speed ) move = 0-self.yaw_speed; } current_yaw = anglemod (current_yaw + move); self.angles_y = current_yaw; } */ //============================================================================ void() HuntTarget = { self.goalentity = self.enemy; if(self.spawnflags&PLAY_DEAD) { // dprint("getting up!!!\n"); self.think=self.th_possum_up; self.spawnflags(-)PLAY_DEAD; } else self.think = self.th_run; // self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin); self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin); thinktime self : 0.1; // SUB_AttackFinished (1); // wait a while before first attack }; void SightSound (void) { if (self.classname == "monster_archer") sound (self, CHAN_VOICE, "archer/sight.wav", 1, ATTN_NORM); else if (self.classname == "monster_archer_lord") sound (self, CHAN_VOICE, "archer/sight2.wav", 1, ATTN_NORM); else if (self.classname == "monster_mummy") sound (self, CHAN_WEAPON, "mummy/sight.wav", 1, ATTN_NORM); else if (self.classname == "monster_mummy_lord") sound (self, CHAN_WEAPON, "mummy/sight2.wav", 1, ATTN_NORM); } void() FoundTarget = { if (self.enemy.classname == "player") { // let other monsters see this monster for a while sight_entity = self; sight_entity_time = time + 1; } self.show_hostile = time + 1; // wake up other monsters SightSound (); HuntTarget (); }; /* =========== FindTarget Self is currently not attacking anything, so try to find a target Returns TRUE if an enemy was sighted When a player fires a missile, the point of impact becomes a fakeplayer so that monsters that see the impact will respond as if they had seen the player. To avoid spending too much time, only a single client (or fakeclient) is checked each frame. This means multi player games will have slightly slower noticing monsters. ============ */ float(float dont_hunt) FindTarget = { entity client; float r; // if the first spawnflag bit is set, the monster will only wake up on // really seeing the player, not another monster getting angry // spawnflags & 3 is a big hack, because zombie crucified used the first // spawn flag prior to the ambush flag, and I forgot about it, so the second // spawn flag works as well if(!deathmatch&&(self.classname=="monster_imp_lord"||self.classname=="cube_of_force")) return FindMonsterTarget(); if (sight_entity_time >= time&&sight_entity!=world) { client = sight_entity; if (client.enemy == self.enemy) return TRUE; } else { client = checkclient (); if (!client) return FALSE; // current check entity isn't in PVS } if (client == self.enemy) return FALSE; if (client.flags & FL_NOTARGET) return FALSE; r = range (client); if (r == RANGE_FAR) return FALSE; if(!visibility_good(client,5)) { // dprint("Monster has low visibility on "); // dprint(client.netname); // dprintf("(%s)\n",client.visibility); return FALSE; } if(self.think!=spider_onwall_wait) if (r == RANGE_NEAR) { if (client.show_hostile < time && !infront (client)) return FALSE; } else if (r == RANGE_MID) { if (!infront (client)) return FALSE; } if (!visible (client)) return FALSE; // // got one // self.enemy = client; if (self.enemy.classname != "player") { self.enemy = self.enemy.enemy; if (self.enemy.classname != "player") { self.enemy = world; return FALSE; } } /* if(self.spawnflags&PLAY_DEAD) { if(r==RANGE_MELEE) { if(!dont_hunt) FoundTarget (); return TRUE; } else if(!infront_of_ent(self,self.enemy)&&random()<0.1&&random()<0.1) { if(!dont_hunt) FoundTarget (); return TRUE; } else { self.enemy=world; return FALSE; } } */ if(!dont_hunt) FoundTarget (); return TRUE; }; void()SpiderJumpBegin; //============================================================================= void(float dist) ai_forward = { walkmove (self.angles_y, dist, FALSE); }; void(float dist) ai_back = { walkmove ( (self.angles_y+180), dist, FALSE); }; /* ============= ai_pain stagger back a bit ============= */ void(float dist) ai_pain = { // ai_back (dist); float away; away = vectoyaw (self.origin - self.enemy.origin)+90*random(0.5,-0.5); walkmove (away, dist,FALSE); }; /* ============= ai_painforward stagger back a bit ============= */ void(float dist) ai_painforward = { walkmove (self.ideal_yaw, dist, FALSE); }; /* ============= ai_walk The monster is walking it's beat ============= */ void(float dist) ai_walk = { MonsterCheckContents(); movedist = dist; // check for noticing a player if (FindTarget (FALSE)) return; movetogoal (dist); }; /* ============= ai_stand The monster is staying in one place for a while, with slight angle turns ============= */ void() ai_stand = { MonsterCheckContents(); if (FindTarget (FALSE)) return; if(self.spawnflags&PLAY_DEAD) return; if (time > self.pausetime) { self.th_walk (); return; } // change angle slightly }; /* ============= ai_turn don't move, but turn towards ideal_yaw ============= */ void() ai_turn = { if (FindTarget (FALSE)) return; ChangeYaw (); }; //============================================================================= /* ============= ChooseTurn ============= */ void(vector dest3) ChooseTurn = { local vector dir, newdir; dir = self.origin - dest3; newdir_x = trace_plane_normal_y; newdir_y = 0 - trace_plane_normal_x; newdir_z = 0; if (dir * newdir > 0) { dir_x = 0 - trace_plane_normal_y; dir_y = trace_plane_normal_x; } else { dir_x = trace_plane_normal_y; dir_y = 0 - trace_plane_normal_x; } dir_z = 0; self.ideal_yaw = vectoyaw(dir); }; /* ============ FacingIdeal Within angle to launch attack? ============ */ float() FacingIdeal = { local float delta; delta = anglemod(self.angles_y - self.ideal_yaw); if (delta > 45 && delta < 315) return FALSE; return TRUE; }; //============================================================================= float() CheckAnyAttack = { if (self.model=="models/medusa.mdl"||self.model=="models/medusa2.mdl") return(MedusaCheckAttack ()); if (!enemy_vis) return FALSE; if (self.model=="models/archer.mdl") return(ArcherCheckAttack ()); if(self.goalentity==self.controller) return FALSE; return CheckAttack (); }; /* ============= ai_attack_face Turn in place until within an angle to launch an attack ============= */ void() ai_attack_face = { self.ideal_yaw = enemy_yaw; ChangeYaw (); if (FacingIdeal()) // Ready to go get em { if (self.attack_state == AS_MISSILE) self.th_missile (); else if (self.attack_state == AS_MELEE) self.th_melee (); self.attack_state = AS_STRAIGHT; } }; /* ============= ai_run_slide Strafe sideways, but stay at aproximately the same range ============= */ void ai_run_slide () { float ofs; self.ideal_yaw = enemy_yaw; ChangeYaw (); if (self.lefty) ofs = 90; else ofs = -90; if (walkmove (self.ideal_yaw + ofs, movedist, FALSE)) return; self.lefty = 1 - self.lefty; walkmove (self.ideal_yaw - ofs, movedist, FALSE); } /* ============= ai_run The monster has an enemy it is trying to kill ============= */ void(float dist) ai_run = { MonsterCheckContents(); movedist = dist; // see if the enemy is dead if (!self.enemy.flags2&FL_ALIVE||(self.enemy.artifact_active&ARTFLAG_STONED&&self.classname!="monster_medusa")) { self.enemy = world; // FIXME: look all around for other targets if (self.oldenemy.health > 0) { self.enemy = self.oldenemy; HuntTarget (); } else if(coop) { if(!FindTarget(TRUE)) //Look for other enemies in the area { if (self.pathentity) self.th_walk (); else self.th_stand (); return; } } else { if (self.pathentity) self.th_walk (); else self.th_stand (); return; } } self.show_hostile = time + 1; // wake up other monsters // check knowledge of enemy enemy_vis = visible(self.enemy); if (enemy_vis) { self.search_time = time + 5; if(self.mintel) { self.goalentity=self.enemy; self.wallspot=(self.enemy.absmin+self.enemy.absmax)*0.5; } } else { if(coop) { if(!FindTarget(TRUE)) if(self.model=="models/spider.mdl") { if(random()<0.5) SetNextWaypoint(); } else SetNextWaypoint(); } if(self.mintel) if(self.model=="models/spider.mdl") { if(random()<0.5) SetNextWaypoint(); } else SetNextWaypoint(); } if(random()<0.5&&(!self.flags&FL_SWIM)&&(!self.flags&FL_FLY)&&(self.spawnflags&JUMP)) CheckJump(); // look for other coop players if (coop && self.search_time < time) { if (FindTarget (FALSE)) return; } enemy_infront = infront(self.enemy); enemy_range = range(self.enemy); enemy_yaw = vectoyaw(self.goalentity.origin - self.origin); if ((self.attack_state == AS_MISSILE) || (self.attack_state == AS_MELEE)) // turning to attack { ai_attack_face (); return; } if (CheckAnyAttack ()) return; // beginning an attack if (self.attack_state == AS_SLIDING) { ai_run_slide (); return; } // head straight in // if(self.netname=="spider") // check_climb(); movetogoal (dist); // done in C code... }; void() monster_imp_fire; void() monster_imp_ice; void() monster_archer; void() monster_skull_wizard; void() monster_scorpion_black; void() monster_scorpion_yellow; void() monster_spider_red_large; void() monster_spider_red_small; void() monster_spider_yellow_large; void() monster_spider_yellow_small; void(vector org, entity death_owner) spawn_tdeath; void spawnspot_activate (void) { self.deadflag=FALSE;//it's that simple! } float monster_spawn_precache (void) { float have_monsters; if (self.spawnflags & IMP) { precache_model ("models/imp.mdl"); precache_model ("models/h_imp.mdl");//empty for now precache_sound ("imp/up.wav"); precache_sound ("imp/die.wav"); precache_sound ("imp/swoop.wav"); precache_sound ("imp/fly.wav"); precache_model ("models/shardice.mdl"); precache_model ("models/fireball.mdl"); precache_sound ("imp/swoophit.wav"); precache_sound ("imp/fireball.wav"); precache_sound ("imp/shard.wav"); precache_sound("hydra/turn-s.wav"); have_monsters=TRUE; } if (self.spawnflags & ARCHER) { precache_archer(); have_monsters=TRUE; } if (self.spawnflags & WIZARD) { precache_model("models/skullwiz.mdl"); precache_model("models/skulbook.mdl"); precache_model("models/skulhead.mdl"); precache_model("models/skulshot.mdl"); precache_model("models/spider.mdl"); precache_sound("skullwiz/death.wav"); precache_sound("skullwiz/blinkspk.wav"); precache_sound("skullwiz/growl.wav"); precache_sound("skullwiz/scream.wav"); precache_sound("skullwiz/pain.wav"); precache_sound("skullwiz/gate.wav"); precache_sound("skullwiz/blinkin.wav"); precache_sound("skullwiz/blinkout.wav"); precache_sound("skullwiz/push.wav"); precache_sound("skullwiz/firemisl.wav"); precache_spider(); have_monsters=TRUE; } if (self.spawnflags & SCORPION) { precache_model2("models/scorpion.mdl"); precache_sound2("scorpion/awaken.wav"); precache_sound2("scorpion/walk.wav"); precache_sound2("scorpion/clawsnap.wav"); precache_sound2("scorpion/tailwhip.wav"); precache_sound2("scorpion/pain.wav"); precache_sound2("scorpion/death.wav"); have_monsters=TRUE; } if(self.spawnflags & SPIDER) { precache_spider(); have_monsters=TRUE; } return have_monsters; } float check_monsterspawn_ok (void) { vector org; if(self.spawnername) { entity findspot; float foundspot,founddead; //FIXME: have it chain them at spawning and do a self.controller=self.controller.chain; findspot=find(self.controller,netname,self.netname); while(!foundspot) { //Warning! If you forget to put spawnspots and you give the spawner //a spawnername string, it will go into an infinite loop and the Universe //will cease to exist! if(findspot.spawnername==self.spawnername) { if(findspot.aflag==self.level+1) if(findspot.deadflag) { founddead=TRUE; self.level=findspot.aflag; } else foundspot=TRUE; // Oops! doesn't automatically loop if(findspot==self.controller) // if(findspot==world) if(self.level==0) if(founddead)//found some, but they're not active yet return FALSE; else { remove(self); return FALSE; } else self.level=0; } if(!foundspot) findspot=find(findspot,netname,self.netname); } self.level=findspot.aflag; self.controller=findspot; org=findspot.origin; } else org=self.origin; if(self.controller.spawnflags&ONDEATH&&(self.controller.goalentity.flags2&FL_ALIVE)) return FALSE; tracearea(org,org,self.mins,self.maxs,FALSE,self); newmis = spawn(); if(trace_fraction<1) if(trace_ent.flags2&FL_ALIVE) { remove(newmis); return FALSE; } else spawn_tdeath(trace_ent.origin,newmis); newmis.angles = self.angles; newmis.flags2(+)FL_SUMMONED; // newmis.spawnflags=NO_DROP; float foundthink,rnd; while(!foundthink) { rnd=rint(random(1,5)); rnd=byte_me(rnd); if(self.controller.spawnflags&rnd) foundthink=TRUE; } if (rnd==IMP) { if(random()<0.5) { newmis.classname = "monster_imp_ice"; newmis.think = monster_imp_ice; } else { newmis.classname = "monster_imp_fire"; newmis.think = monster_imp_fire; } } else if (rnd==ARCHER) { newmis.classname = "monster_archer"; newmis.think = monster_archer; } else if (rnd==WIZARD) { newmis.classname = "monster_skull_wizard"; newmis.think = monster_skull_wizard; } else if (rnd==SCORPION) { if(random()<0.5) { newmis.classname = "monster_scorpion_black"; newmis.think = monster_scorpion_black; } else { newmis.classname = "monster_scorpion_yellow"; newmis.think = monster_scorpion_yellow; } } else//it must be a spider, baby! { rnd=rint(random(1,4)); if(rnd==4) { newmis.classname = "monster_spider_yellow_large"; newmis.think = monster_spider_yellow_large; } else if(rnd==3) { newmis.classname = "monster_spider_yellow_small"; newmis.think = monster_spider_yellow_small; } else if(rnd==2) { newmis.classname = "monster_spider_red_large"; newmis.think = monster_spider_red_large; } else { newmis.classname = "monster_spider_red_small"; newmis.think = monster_spider_red_small; } } self.controller.goalentity=newmis; setorigin(newmis,org); if(!self.controller.spawnflags&QUIET) spawn_tfog(org); newmis.nextthink = time; return TRUE; } void monsterspawn_active (void) { self.think=monsterspawn_active; if(check_monsterspawn_ok()) { self.controller.frags+=1; if(self.controller!=self) self.frags+=1; if(self.controller!=self) { self.controller.deadflag=TRUE; self.controller.think=self.controller.use; self.controller.nextthink=time+self.controller.wait; } if(self.controller.frags>=self.controller.cnt) remove(self.controller); if(self.frags>=self.cnt) remove(self); if(self.spawnflags&TRIGGERONLY) self.nextthink=-1; else self.nextthink=time+self.wait; } else if(self.spawnflags&TRIGGERONLY)//Don't keep trying self.nextthink=-1; else self.nextthink=time+0.1; } /*QUAKED func_monsterspawner (1 .8 0) (-16 -16 0) (16 16 56) IMP ARCHER WIZARD SCORPION SPIDER ONDEATH QUIET TRIGGERONLY If something is blocking the spawnspot, this will telefrag it as long as it's not a living entity (flags2&FL_ALIVE) You can set up as many spots as you want for it to spawn at and it will cycle between these. Make them classname func_monsterspawn_spot and their spawnername has to match this entity's spawnername. You can control the order in which they are used by setting thier aflag (1, 2, 3, etc- there is NO ZERO, that's for you designers!) You should give the spawner an aflag too if you want it's origin included in the cycle of spawning, but if there are no spawn spots (just a spawner), no aflag is needed anywhere. WARNING!!! If you forget to put spawnspots and you give the spawner a spawnername, it will go into an infinite loop and the Universe will cease to exist! If you only want monsters to spawn at the monster spawner origin, then don't worry about aflags or the spawnername, it will know... It's that cool. The Monsters will be spawned at the origin of the spawner (and/or spawnspots), so if you want them not to stick in the ground, put this above the ground some- maybe 24? Make sure there's enough room around it for the monsters. ONDEATH = only spawn the new monster after the last has died, defaults to FALSE (doesn't wait) TRIGGERONLY = Will only spawn a monster when it's been used by a trigger. The default is continous spawning. wait = time to wait after spawning a monster until the next monster is spawned, defaults to 0.5 seconds. If there are multiple spawn spots, this will be the time between cycles (default 0.5) cnt = number of monsters, max to spawn, defaults to 17 (no reason, just like that number!) If there are multiple spots, this should be the total off ALL the spots, including the spawner itself. aflag = order in the spawning cycle spawnername = spawnspots to look for- be sure to make spawnspots! targetname = not needed unless you plan to activate this with a trigger There will be a test on this on Thursday. Interns are NOT exempt. */ void func_monsterspawner (void) { if(!self.cnt) self.cnt=17; if(!self.wait) self.wait=0.5; self.netname="monsterspawn_spot"; if(self.spawnername) self.controller=world;//misleading name, this is the last spawnspot else self.controller=self; self.level=0;//Spawn cycle counter //setmodel(self,self.model); setsize(self,'-16 -16 0','16 16 56'); setorigin(self,self.origin); if(!monster_spawn_precache()&&!self.spawnername) { // dprint("You don't have any monsters assigned to me, and I have no spawnername!\n"); remove(self); } if(self.targetname) self.use=monsterspawn_active; else { self.think=monsterspawn_active; self.nextthink=time+3;//wait while map starts } } /*QUAKED func_monsterspawn_spot (1 .3 0) (-16 -16 0) (16 16 56) IMP ARCHER WIZARD SCORPION SPIDER ONDEATH QUIET All this does is mark where to spawn monsters for a spawn spot. You can set any monster type for each spawnspot. Just make the spawnername of this entity equal to the spawnername of the spawner and the spawner will cycle through all it's spawnspots in the world. If you don't set the aflag, the spawn spot will NOT be used. Note that if you set a trigger to activate this spawnspot, you can have it turn on and be included in the spawner's cycle, but you still always have to set the aflag. aflag = order in the spawning cycle, you MUST set this, or it's useless. cnt = number of monsters this spot will spawn, defaults to 17. spawnername = this HAS to match the spawner's spawnername! wait = how long the minimum interval should be between monster spawns for THIS spot. targetname = used if you want this to wait to be activated by a seperate trigger before being included in the spawning cycle. */ void func_monsterspawn_spot (void) { if(!self.aflag) { // dprint("Ooo! You didn't include me in the spawn cycle! FIXME!\n"); remove(self); } if(!self.cnt) self.cnt=17; self.netname="monsterspawn_spot"; if(!monster_spawn_precache()) { // dprint("You didn't give me any monsters to spawn!!!\n"); remove(self); } //setmodel(self,self.model); setsize(self,'-16 -16 0','16 16 56'); setorigin(self,self.origin); if(self.targetname) self.deadflag=TRUE; else self.use=spawnspot_activate; } void hive_die(){} void spawn_ghost (entity attacker){} gamecode/hc/h2/ai2.hc000066400000000000000000000346301444734033100145250ustar00rootroot00000000000000void(entity etemp, entity stemp, entity stemp, float dmg) T_Damage; /* .enemy Will be world if not currently angry at anyone. .pathcorner The next path spot to walk toward. If .enemy, ignore .pathcorner When an enemy is killed, the monster will try to return to it's path. .hunt_time Set to time + something when the player is in sight, but movement straight for him is blocked. This causes the monster to use wall following code for movement direction instead of sighting on the player. .ideal_yaw A yaw angle of the intended direction, which will be turned towards at up to 45 deg / state. If the enemy is in view and hunt_time is not active, this will be the exact line towards the enemy. .pausetime A monster will leave it's stand state and head towards it's .pathcorner when time > .pausetime. walkmove(angle, speed) primitive is all or nothing */ void()SetNextWaypoint; float(entity targ , entity from)infront_of_ent; float()eidolon_check_attack; void()multiplayer_health; //void()check_climb; // // globals // float current_yaw; // // when a monster becomes angry at a player, that monster will be used // as the sight target the next frame so that monsters near that one // will wake up even if they wouldn't have noticed the player // float sight_entity_time; void()riderpath_init; void(float move_speed)riderpath_move; float(float move_speed)eidolon_riderpath_move; void() eidolon_guarding; void()hive_die; float(float v) anglemod = { while (v >= 360) v = v - 360; while (v < 0) v = v + 360; return v; }; //============================================================================ /* ============= range returns the range catagorization of an entity reletive to self 0 melee range, will become hostile even if back is turned 1 visibility and infront, or visibility and show hostile 2 infront and show hostile 3 only triggered by damage ============= */ float(entity targ) range = { local vector spot1, spot2; local float r,melee; if((self.solid==SOLID_BSP||self.solid==SOLID_TRIGGER)&&self.origin=='0 0 0') spot1=(self.absmax+self.absmin)*0.5; else spot1 = self.origin + self.view_ofs; if((targ.solid==SOLID_BSP||targ.solid==SOLID_TRIGGER)&&targ.origin=='0 0 0') spot2=(targ.absmax+targ.absmin)*0.5; else spot2 = targ.origin + targ.view_ofs; r = vlen (spot1 - spot2); if (self.classname=="monster_mummy") melee = 50; else melee = 100; if (r < melee) return RANGE_MELEE; if (r < 500) return RANGE_NEAR; if (r < 1000) return RANGE_MID; return RANGE_FAR; }; /* ============= visible2ent returns 1 if the entity is visible to self, even if not infront () ============= */ float visible2ent (entity targ, entity forent) { vector spot1, spot2; if((forent.solid==SOLID_BSP||forent.solid==SOLID_TRIGGER)&&forent.origin=='0 0 0') spot1=(forent.absmax+forent.absmin)*0.5; else spot1 = forent.origin + forent.view_ofs; if((targ.solid==SOLID_BSP||targ.solid==SOLID_TRIGGER)&&targ.origin=='0 0 0') spot2=(targ.absmax+targ.absmin)*0.5; else spot2 = targ.origin + targ.view_ofs; traceline (spot1, spot2, TRUE, forent); // see through other monsters if(trace_ent.thingtype>=THINGTYPE_WEBS) traceline (trace_endpos, spot2, TRUE, trace_ent); else if (trace_inopen && trace_inwater) return FALSE; // sight line crossed contents if (trace_fraction == 1) return TRUE; return FALSE; } /* ============= infront_of_ent returns 1 if the targ is in front (in sight) of from ============= */ float infront_of_ent (entity targ , entity from) { vector vec,spot1,spot2; float accept,dot; if(from.classname=="player") makevectors (from.v_angle); /* else if(from.classname=="monster_medusa") makevectors (from.angles+from.angle_ofs); */ else makevectors (from.angles); if((from.solid==SOLID_BSP||from.solid==SOLID_TRIGGER)&&from.origin=='0 0 0') spot1=(from.absmax+from.absmin)*0.5; else spot1 = from.origin + from.view_ofs; spot2=(targ.absmax+targ.absmin)*0.5; vec = normalize (spot2 - spot1); dot = vec * v_forward; accept = 0.3; if ( dot > accept) return TRUE; return FALSE; } /* ============= visible returns 1 if the entity is visible to self, even if not infront () ============= */ float visible (entity targ) { return visible2ent(targ,self); } /* ============= infront returns 1 if the entity is in front (in sight) of self ============= */ float infront (entity targ) { return infront_of_ent(targ,self); } //============================================================================ /* =========== ChangeYaw Turns towards self.ideal_yaw at self.yaw_speed Sets the global variable current_yaw Called every 0.1 sec by monsters ============ */ /* void ChangeYaw () { float ideal, move; //current_yaw = self.ideal_yaw; // mod down the current angle current_yaw = anglemod( self.angles_y ); ideal = self.ideal_yaw; if (current_yaw == ideal) return; move = ideal - current_yaw; if (ideal > current_yaw) { if (move > 180) move = move - 360; } else { if (move < -180) move = move + 360; } if (move > 0) { if (move > self.yaw_speed) move = self.yaw_speed; } else { if (move < 0-self.yaw_speed ) move = 0-self.yaw_speed; } current_yaw = anglemod (current_yaw + move); self.angles_y = current_yaw; } */ //============================================================================ void() HuntTarget = { self.goalentity = self.enemy; if(self.spawnflags&PLAY_DEAD) { // dprint("getting up!!!\n"); self.think=self.th_possum_up; self.spawnflags(-)PLAY_DEAD; } else self.think = self.th_run; // self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin); self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin); thinktime self : 0.1; // SUB_AttackFinished (1); // wait a while before first attack }; void SightSound (void) { if (self.classname == "monster_archer") sound (self, CHAN_VOICE, "archer/sight.wav", 1, ATTN_NORM); else if (self.classname == "monster_archer_lord") sound (self, CHAN_VOICE, "archer/sight2.wav", 1, ATTN_NORM); else if (self.classname == "monster_mummy") sound (self, CHAN_WEAPON, "mummy/sight.wav", 1, ATTN_NORM); else if (self.classname == "monster_mummy_lord") sound (self, CHAN_WEAPON, "mummy/sight2.wav", 1, ATTN_NORM); } void() FoundTarget = { if (self.enemy.classname == "player") { // let other monsters see this monster for a while sight_entity = self; sight_entity_time = time + 1; } self.show_hostile = time + 1; // wake up other monsters SightSound (); HuntTarget (); }; /* =========== FindTarget Self is currently not attacking anything, so try to find a target Returns TRUE if an enemy was sighted When a player fires a missile, the point of impact becomes a fakeplayer so that monsters that see the impact will respond as if they had seen the player. To avoid spending too much time, only a single client (or fakeclient) is checked each frame. This means multi player games will have slightly slower noticing monsters. ============ */ float(float dont_hunt) FindTarget = { entity client; float r; // if the first spawnflag bit is set, the monster will only wake up on // really seeing the player, not another monster getting angry // spawnflags & 3 is a big hack, because zombie crucified used the first // spawn flag prior to the ambush flag, and I forgot about it, so the second // spawn flag works as well if(!deathmatch&&(self.classname=="monster_imp_lord"||self.classname=="cube_of_force")) return FindMonsterTarget(); if (sight_entity_time >= time) { client = sight_entity; if (client.enemy == self.enemy) return TRUE; } else { client = checkclient (); if (!client) return FALSE; // current check entity isn't in PVS } if (client == self.enemy) return FALSE; if (client.flags & FL_NOTARGET) return FALSE; r = range (client); if (r == RANGE_FAR) return FALSE; if (r == RANGE_NEAR) { if (client.show_hostile < time && !infront (client)) return FALSE; } else if (r == RANGE_MID) { if (!infront (client)) return FALSE; } if (!visible (client)) return FALSE; // // got one // self.enemy = client; if (self.enemy.classname != "player") { self.enemy = self.enemy.enemy; if (self.enemy.classname != "player") { self.enemy = world; return FALSE; } } /* if(self.spawnflags&PLAY_DEAD) { if(r==RANGE_MELEE) { if(!dont_hunt) FoundTarget (); return TRUE; } else if(!infront_of_ent(self,self.enemy)&&random()<0.1&&random()<0.1) { if(!dont_hunt) FoundTarget (); return TRUE; } else { self.enemy=world; return FALSE; } } */ if(!dont_hunt) FoundTarget (); return TRUE; }; //============================================================================= void(float dist) ai_forward = { walkmove (self.angles_y, dist, FALSE); }; void(float dist) ai_back = { walkmove ( (self.angles_y+180), dist, FALSE); }; /* ============= ai_pain stagger back a bit ============= */ void(float dist) ai_pain = { // ai_back (dist); float away; away = vectoyaw (self.origin - self.enemy.origin)+90*random(0.5,-0.5); walkmove (away, dist,FALSE); }; /* ============= ai_painforward stagger back a bit ============= */ void(float dist) ai_painforward = { walkmove (self.ideal_yaw, dist, FALSE); }; /* ============= ai_walk The monster is walking it's beat ============= */ void(float dist) ai_walk = { MonsterCheckContents(); movedist = dist; // check for noticing a player if (FindTarget (FALSE)) return; if(self.classname=="monster_eidolon") { if (!self.path_current) riderpath_init(); if(!eidolon_riderpath_move(dist)) { if(self.think==self.th_walk) self.think=eidolon_guarding; } else if(self.think==eidolon_guarding) self.think=self.th_walk; } else movetogoal (dist); }; /* ============= ai_stand The monster is staying in one place for a while, with slight angle turns ============= */ void() ai_stand = { MonsterCheckContents(); if (FindTarget (FALSE)) return; if(self.spawnflags&PLAY_DEAD) return; if (time > self.pausetime) { self.th_walk (); return; } // change angle slightly }; /* ============= ai_turn don't move, but turn towards ideal_yaw ============= */ void() ai_turn = { if (FindTarget (FALSE)) return; ChangeYaw (); }; //============================================================================= /* ============= ChooseTurn ============= */ void(vector dest3) ChooseTurn = { local vector dir, newdir; dir = self.origin - dest3; newdir_x = trace_plane_normal_y; newdir_y = 0 - trace_plane_normal_x; newdir_z = 0; if (dir * newdir > 0) { dir_x = 0 - trace_plane_normal_y; dir_y = trace_plane_normal_x; } else { dir_x = trace_plane_normal_y; dir_y = 0 - trace_plane_normal_x; } dir_z = 0; self.ideal_yaw = vectoyaw(dir); }; /* ============ FacingIdeal Within angle to launch attack? ============ */ float() FacingIdeal = { local float delta; delta = anglemod(self.angles_y - self.ideal_yaw); if (delta > 45 && delta < 315) return FALSE; return TRUE; }; //============================================================================= float() CheckAnyAttack = { if (!enemy_vis) return FALSE; if(self.classname=="monster_eidolon") if(self.goalentity==self.controller) return FALSE; else return eidolon_check_attack(); return CheckAttack (); }; /* ============= ai_attack_face Turn in place until within an angle to launch an attack ============= */ void() ai_attack_face = { self.ideal_yaw = enemy_yaw; ChangeYaw (); if (FacingIdeal()) // Ready to go get em { if (self.attack_state == AS_MISSILE) self.th_missile (); else if (self.attack_state == AS_MELEE) self.th_melee (); self.attack_state = AS_STRAIGHT; } }; /* ============= ai_run_slide Strafe sideways, but stay at aproximately the same range ============= */ void ai_run_slide () { float ofs; self.ideal_yaw = enemy_yaw; ChangeYaw (); if (self.lefty) ofs = 90; else ofs = -90; if (walkmove (self.ideal_yaw + ofs, movedist, FALSE)) return; self.lefty = 1 - self.lefty; walkmove (self.ideal_yaw - ofs, movedist, FALSE); } /* ============= ai_run The monster has an enemy it is trying to kill ============= */ void(float dist) ai_run = { MonsterCheckContents(); movedist = dist; // see if the enemy is dead if (!self.enemy.flags2&FL_ALIVE||(self.enemy.artifact_active&ARTFLAG_STONED&&self.classname!="monster_medusa")) { self.enemy = world; // FIXME: look all around for other targets if (self.oldenemy.health > 0) { self.enemy = self.oldenemy; HuntTarget (); } else if(coop) { if(!FindTarget(TRUE)) //Look for other enemies in the area { if (self.pathentity) self.th_walk (); else self.th_stand (); return; } } else { if (self.pathentity) self.th_walk (); else self.th_stand (); return; } } self.show_hostile = time + 1; // wake up other monsters // check knowledge of enemy enemy_vis = visible(self.enemy); if (enemy_vis) { self.search_time = time + 5; if(self.mintel) { self.goalentity=self.enemy; self.wallspot=(self.enemy.absmin+self.enemy.absmax)*0.5; } } else { if(coop) { if(!FindTarget(TRUE)) if(self.model=="models/spider.mdl") { if(random()<0.5) SetNextWaypoint(); } else SetNextWaypoint(); } if(self.mintel) if(self.model=="models/spider.mdl") { if(random()<0.5) SetNextWaypoint(); } else SetNextWaypoint(); } if(random()<0.5&&(!self.flags&FL_SWIM)&&(!self.flags&FL_FLY)&&(self.spawnflags&JUMP)) CheckJump(); // look for other coop players if (coop && self.search_time < time) { if (FindTarget (FALSE)) return; } enemy_infront = infront(self.enemy); enemy_range = range(self.enemy); if(self.classname!="monster_eidolon") enemy_yaw = vectoyaw(self.goalentity.origin - self.origin); if ((self.attack_state == AS_MISSILE) || (self.attack_state == AS_MELEE)) // turning to attack { if(self.classname!="monster_eidolon") ai_attack_face (); return; } if (CheckAnyAttack ()) return; // beginning an attack if (self.attack_state == AS_SLIDING) { ai_run_slide (); return; } // head straight in // if(self.netname=="spider") // check_climb(); if(self.classname=="monster_eidolon") { if(!self.path_current) riderpath_init(); if(!eidolon_riderpath_move(dist)) { if(self.think==self.th_run) eidolon_guarding(); } else if(self.think==eidolon_guarding) self.th_run(); } else movetogoal (dist); // done in C code... }; //FAKE FUNCTIONS void SpiderJumpBegin() { } float mezzo_choose_roll (entity null) { return FALSE; } gamecode/hc/h2/allplay.hc000066400000000000000000000341631444734033100155110ustar00rootroot00000000000000/*========================================= FUNCTIONS THAT ALL PLAYERS WILL CALL ===========================================*/ void() bubble_bob; void PlayerSpeed_Calc (void) { if (self.playerclass==CLASS_ASSASSIN) self.hasted=1; else if (self.playerclass==CLASS_PALADIN) self.hasted=.96; else if (self.playerclass==CLASS_CRUSADER) self.hasted=.93; else if(self.playerclass==CLASS_NECROMANCER) self.hasted=.9; if (self.artifact_active & ART_HASTE) self.hasted *= 2.9; if (self.hull==HULL_CROUCH) // Player crouched self.hasted *= .6; } vector VelocityForDamage (float dm) { local vector v; v = randomv('-100 -100 200', '100 100 300'); if (dm > -50) v = v * 0.7; else if (dm > -200) v = v * 2; else v = v * 10; return v; } void ReadySolid () { if(!self.headmodel) self.headmodel="models/flesh1.mdl";//Temp until player head models are in MakeSolidCorpse (); } void StandardPain(void) { if(self.playerclass==CLASS_ASSASSIN) { if (random() > 0.5) sound (self, CHAN_VOICE, "player/asspain1.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "player/asspain2.wav", 1, ATTN_NORM); } else if (random() > 0.5) sound (self, CHAN_VOICE, "player/palpain1.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "player/palpain2.wav", 1, ATTN_NORM); } void PainSound (void) { if (self.health <= 0) return; if (self.deathtype == "teledeath"||self.deathtype == "teledeath2"||self.deathtype == "teledeath3"||self.deathtype == "teledeath4") { sound (self, CHAN_VOICE, "player/telefrag.wav", 1, ATTN_NONE); return; } if (self.pain_finished > time) return; self.pain_finished = time + 0.5; // FIXME: Are we doing seperate sounds for these different pains???? if (self.model=="models/sheep.mdl") sheep_sound(1); else if (/*self.watertype == CONTENT_WATER &&*/ self.waterlevel == 3) { if(self.playerclass==CLASS_ASSASSIN) sound (self, CHAN_VOICE, "player/assdrown.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "player/paldrown.wav", 1, ATTN_NORM); } else //if (self.watertype == CONTENT_SLIME) { StandardPain(); } /* else if (self.watertype == CONTENT_LAVA) { if(self.playerclass==CLASS_ASSASSIN) { if (random() > 0.5) sound (self, CHAN_VOICE, "player/asspain1.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "player/asspain2.wav", 1, ATTN_NORM); } else if (random() > 0.5) sound (self, CHAN_VOICE, "player/palpain1.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "player/palpain2.wav", 1, ATTN_NORM); } else { if(self.playerclass==CLASS_ASSASSIN) { if (random() > 0.5) sound (self, CHAN_VOICE, "player/asspain1.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "player/asspain2.wav", 1, ATTN_NORM); } else if (random() > 0.5) sound (self, CHAN_VOICE, "player/palpain1.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "player/palpain2.wav", 1, ATTN_NORM); }*/ } void player_pain (void) { //FIX this = need to check if firing, else make idle frames of all // weapons frame 0? //if (self.weaponframe) // return; if (self.last_attack + 0.5 > time || self.button0) return; PainSound(); // self.weaponframe=0;//Why? if (self.hull==HULL_PLAYER) self.act_state=ACT_PAIN; else self.act_state=ACT_CROUCH_MOVE;//No pain animation for crouch- maybe jump? //Make it make you stand up? } void DeathBubblesSpawn () { entity bubble; vector offset; offset_x = random(18,-18); offset_y = random(18,-18); if (pointcontents(self.owner.origin+self.owner.view_ofs)!=CONTENT_WATER) { remove(self); return; } bubble = spawn_temp(); setmodel (bubble, "models/s_bubble.spr"); setorigin (bubble, self.owner.origin+self.owner.view_ofs+offset); bubble.movetype = MOVETYPE_NOCLIP; bubble.solid = SOLID_NOT; bubble.velocity = '0 0 17'; thinktime bubble : 0.5; bubble.think = bubble_bob; bubble.classname = "bubble"; bubble.frame = 0; bubble.cnt = 0; bubble.abslight=0.5; bubble.drawflags(+)DRF_TRANSLUCENT|MLS_ABSLIGHT; setsize (bubble, '-8 -8 -8', '8 8 8'); thinktime self : 0.1; self.think = DeathBubblesSpawn; self.air_finished = self.air_finished + 1; if (self.air_finished >= self.bubble_count) remove(self); } void DeathBubbles (float num_bubbles) { entity bubble_spawner, bubble_owner; if(self.classname=="contents damager") bubble_owner = self.enemy; else bubble_owner = self; bubble_spawner = spawn(); setorigin (bubble_spawner, bubble_owner.origin+bubble_owner.view_ofs); bubble_spawner.movetype = MOVETYPE_NONE; bubble_spawner.solid = SOLID_NOT; bubble_spawner.owner = bubble_owner; thinktime bubble_spawner : 0.1; bubble_spawner.think = DeathBubblesSpawn; bubble_spawner.air_finished = 0; bubble_spawner.bubble_count = num_bubbles; } void DeathSound () { // water death sounds if (self.waterlevel == 3) { DeathBubbles(20); if(self.playerclass==CLASS_ASSASSIN) sound (self, CHAN_VOICE, "player/assdieh2.wav", 1, ATTN_NONE); else sound (self, CHAN_VOICE, "player/paldieh2.wav", 1, ATTN_NONE); return; } else { if(self.playerclass==CLASS_ASSASSIN) { if (random() > 0.5) sound (self, CHAN_VOICE, "player/assdie1.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "player/assdie2.wav", 1, ATTN_NORM); } else if (random() > 0.5) sound (self, CHAN_VOICE, "player/paldie1.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "player/paldie2.wav", 1, ATTN_NORM); } } void PlayerDead () { self.nextthink = -1; // allow respawn after a certain time self.deadflag = DEAD_DEAD; if(self.model!=self.headmodel) { self.angles_x=self.angles_z=0; pitch_roll_for_slope('0 0 0'); } } void ThrowGib (string gibname, float dm) { entity new; new = spawn_temp(); new.origin = (self.absmin+self.absmax)*0.5; setmodel (new, gibname); setsize (new, '0 0 0', '0 0 0'); new.velocity = VelocityForDamage (dm); new.movetype = MOVETYPE_BOUNCE; new.solid = SOLID_NOT; new.avelocity_x = random(600); new.avelocity_y = random(600); new.avelocity_z = random(600); new.think = SUB_Remove; new.ltime = time; thinktime new : random(20,10); new.scale=random(.5,.9); new.frame = 0; new.flags = 0; } void ThrowHead (string gibname, float dm) { vector org; if(self.decap==2) {//Brains! if(self.movedir=='0 0 0') { self.movedir=normalize(self.origin+self.view_ofs-self.enemy.origin+self.enemy.proj_ofs); self.movedir_z=0; } traceline(self.origin + self.view_ofs, self.origin+self.view_ofs+self.movedir*100, FALSE, self); if (trace_fraction < 1&&!trace_ent.flags2&FL_ALIVE&&trace_ent.solid==SOLID_BSP) { self.wallspot=trace_endpos; ZeBrains(trace_endpos, trace_plane_normal, random(1.3,2), rint(random(1)),random(360)); } else self.wallspot='0 0 0'; } setmodel (self, gibname); self.frame = 0; self.takedamage = DAMAGE_NO; if(self.classname!="player") self.solid = SOLID_BBOX; self.movetype = MOVETYPE_BOUNCE; self.mass = 1; self.view_ofs = '0 0 8'; self.proj_ofs='0 0 2'; self.hull=HULL_POINT; org=self.origin; org_z=self.absmax_z - 4; setsize (self, '-4 -4 -4', '4 4 4'); setorigin(self,org); self.flags(-)FL_ONGROUND; self.avelocity = randomv('0 -600 0', '0 600 0'); if(self.decap==2) self.velocity = VelocityForDamage (dm)+'0 0 50'; else self.velocity = VelocityForDamage (dm)+'0 0 200'; if(self.decap==2||(self.decap==1&&vlen(self.velocity)>300)) { if(self.wallspot=='0 0 0') self.wallspot=org; self.pausetime=time+5;//watch splat or body } self.think=PlayerDead; thinktime self : 1; } void PlayerUnCrouching () { tracearea (self.origin,self.origin+'0 0 28','-16 -16 0','16 16 28',FALSE,self); if (trace_fraction < 1) { centerprint(self,STR_NOROOM); self.crouch_stuck = 1; return; } setsize (self, '-16 -16 0', '16 16 56'); self.hull=HULL_PLAYER; if (self.viewentity.classname=="chasecam") self.view_ofs = '0 0 0'; PlayerSpeed_Calc(); self.crouch_time = time; if (self.velocity_x || self.velocity_y) self.act_state=ACT_RUN; else self.act_state=ACT_STAND; } void PlayerCrouching () { if (self.health <= 0) return; setsize (self,'-16 -16 0','16 16 28'); self.hull=HULL_CROUCH; if (self.viewentity.classname=="chasecam") self.view_ofs = '0 0 0'; self.absorb_time=time + 0.3; PlayerSpeed_Calc(); self.crouch_time = time; self.crouch_stuck = 0; self.act_state=ACT_CROUCH_MOVE; } void PlayerCrouch () { if (self.hull==HULL_PLAYER) PlayerCrouching(); else if (self.hull==HULL_CROUCH) PlayerUnCrouching(); } void GibPlayer () { ThrowHead (self.headmodel, self.health); ThrowGib ("models/flesh1.mdl", self.health); ThrowGib ("models/flesh2.mdl", self.health); ThrowGib ("models/flesh3.mdl", self.health); ThrowGib ("models/flesh1.mdl", self.health); ThrowGib ("models/flesh2.mdl", self.health); ThrowGib ("models/flesh3.mdl", self.health); self.deadflag = DEAD_DEAD; if (self.deathtype == "teledeath"||self.deathtype == "teledeath2"||self.deathtype == "teledeath3"||self.deathtype == "teledeath4") { sound (self, CHAN_VOICE, "player/telefrag.wav", 1, ATTN_NONE); return; } if(self.health<-80) sound (self, CHAN_VOICE, "player/megagib.wav", 1, ATTN_NONE); else if (random() < 0.5) sound (self, CHAN_VOICE, "player/gib1.wav", 1, ATTN_NONE); else sound (self, CHAN_VOICE, "player/gib2.wav", 1, ATTN_NONE); } void DecapPlayer () { entity headless; headless=spawn(); headless.classname="headless"; headless.decap=TRUE; headless.movetype=MOVETYPE_STEP; headless.solid=SOLID_PHASE; headless.frame=50; headless.skin=self.skin; //Took this out so you can't fall "into" it... // headless.owner=self; headless.thingtype=self.thingtype; headless.angles_y=self.angles_y; setmodel(headless,self.model); setsize(headless,'-16 -16 0','16 16 36'); setorigin(headless,self.origin); headless.playerclass=self.playerclass; headless.think=self.th_goredeath; thinktime headless : 0; self.health=self.health*4; if(self.health>-30) self.health=-30; if(self.decap==2) { ThrowHead ("models/flesh1.mdl", self.health); SpawnPuff(self.origin+self.view_ofs,'0 0 0',fabs(self.health),self); } else ThrowHead (self.headmodel, self.health); ThrowGib ("models/flesh1.mdl", self.health); ThrowGib ("models/flesh2.mdl", self.health); ThrowGib ("models/flesh3.mdl", self.health); self.deadflag = DEAD_DEAD; if (random() < 0.5) sound(self,CHAN_VOICE,"player/decap.wav",1,ATTN_NORM); else if (random() < 0.5) sound (self, CHAN_VOICE, "player/gib1.wav", 1, ATTN_NONE); else sound (self, CHAN_VOICE, "player/gib2.wav", 1, ATTN_NONE); } void PlayerDie () { if(self.viewentity!=self) { if(self.viewentity.classname=="chasecam") remove(self.viewentity); self.viewentity=self; CameraViewPort(self,self); CameraViewAngles(self,self); } msg_entity=self; WriteByte(MSG_ONE, SVC_CLEAR_VIEW_FLAGS); WriteByte(MSG_ONE,255); self.artifact_low = self.artifact_active = self.invisible_time = self.effects= self.colormap=0; if (deathmatch || coop) DropBackpack(); if(self.model=="models/sheep.mdl") self.headmodel=""; self.weaponmodel=""; self.deadflag = DEAD_DYING; self.solid = SOLID_NOT; self.flags(-)FL_ONGROUND; self.movetype = MOVETYPE_TOSS; self.attack_finished=self.teleport_time=self.pausetime=time; self.drawflags=self.effects=FALSE; if (self.velocity_z < 10) self.velocity_z += random(300); self.artifact_active = 0; self.rings_active =0; if (self.deathtype == "teledeath"||self.deathtype == "teledeath2"||self.deathtype == "teledeath3"||self.deathtype == "teledeath4") { self.decap=0; self.health=-99; } if(self.deathtype=="ice shatter"||self.deathtype=="stone crumble") { shatter(); ThrowHead(self.headmodel,self.health); if(self.health<-99) self.health=-99; return; } else if(self.decap) { DecapPlayer(); if(self.health<-99) self.health=-99; return; } else if(self.health < -40||self.model=="models/sheep.mdl")//self.modelindex==modelindex_sheep) { GibPlayer (); if(self.health<-99) self.health=-99; return; } DeathSound(); self.angles_x = 0; self.angles_z = 0; if(self.bloodloss==666) DecapPlayer(); else { self.act_state=ACT_DEAD; player_frames(); } if(self.health<-99) self.health=-99; } void set_suicide_frame () { // used by kill command and disconnect command if (self.model != self.init_model) return; // already gibbed //have a self.deathframe value? Or just if-thens // self.frame = $deatha11; self.solid = SOLID_NOT; self.movetype = MOVETYPE_TOSS; self.deadflag = DEAD_DEAD; self.nextthink = -1; } void Head () { ThrowSolidHead(0); } void Corpse () { MakeSolidCorpse(); } void SolidPlayer () { entity corpse; corpse = spawn(); if(self.angles_x>15||self.angles_x<-15) self.angles_x=0; if(self.angles_z>15||self.angles_z<-15) self.angles_z=0; corpse.angles = self.angles; setmodel(corpse,self.model); corpse.frame = self.frame; corpse.colormap = self.colormap; corpse.movetype = self.movetype; corpse.velocity = self.velocity; corpse.flags = 0; corpse.effects = 0; corpse.skin = self.skin; corpse.controller = self; corpse.thingtype=self.thingtype; setorigin (corpse, self.origin); if(self.model==self.headmodel) { self.classname="head";//So they don't get mixed up with players corpse.think=Head; } else { self.classname="corpse";//So they don't get mixed up with players corpse.think=Corpse; } thinktime corpse : 0; } void player_behead () { self.frame=self.level+self.cnt; makevectors(self.angles); if(!self.cnt) MeatChunks (self.origin + '0 0 50',v_up*200, 3,self); else if (self.cnt==1) { SpawnPuff (self.origin+v_forward*8, '0 0 48', 30,self); sound (self, CHAN_AUTO, "misc/decomp.wav", 1, ATTN_NORM); } else if (self.cnt==3) { SpawnPuff (self.origin+v_forward*16, '0 0 36'+v_forward*16, 20,self); sound (self, CHAN_AUTO, "misc/decomp.wav", 1, ATTN_NORM); } else if (self.cnt==5) { SpawnPuff (self.origin+v_forward*28, '0 0 20'+v_forward*32, 15,self); sound (self, CHAN_AUTO, "misc/decomp.wav", 0.8, ATTN_NORM); } else if (self.cnt==8) { SpawnPuff (self.origin+v_forward*40, '0 0 10'+v_forward*40, 10,self); sound (self, CHAN_AUTO, "misc/decomp.wav", 0.6, ATTN_NORM); } if (self.frame==self.dmg) { SpawnPuff (self.origin+v_forward*56, '0 0 -5'+v_forward*40, 5,self); sound (self, CHAN_AUTO, "misc/decomp.wav", 0.4, ATTN_NORM); ReadySolid(); } else { self.think=player_behead; thinktime self : 0.1; } self.cnt+=1; } gamecode/hc/h2/altdeath.hc000066400000000000000000000066671444734033100156510ustar00rootroot00000000000000void ice_melt (void) { self.scale -= 0.05; if (self.scale<=0.05) remove(self); else self.think=ice_melt; thinktime self : 0.05; } void ice_think (void) { if(self.velocity=='0 0 0') { self.touch=SUB_Null; self.think=ice_melt; thinktime self : 1.5; } else { self.think=ice_think; thinktime self : 0.1; } } void ice_hit (void) { if (random()<0.2) { particleexplosion(self.origin,14,20,5); remove(self); } } void todust (void) { particleexplosion(self.origin,self.aflag,20,5); remove(self); } void pebble_hit (void) { self.wait=self.wait + 1; sound(self,CHAN_BODY,"misc/rubble.wav",1,ATTN_NORM); if(self.wait>=3||random()<0.1) todust(); else { self.think=todust; thinktime self : 2; } } /* void ash_hit (void) { sound(self,CHAN_BODY,"misc/rubble.wav",1,ATTN_NORM); self.wait=self.wait + 1; if(self.wait>=3||random()<0.2) todust(); else { self.think=todust; thinktime self : 1; } } */ void throw_shard (vector org,vector dir,vector spin,string type,vector ownersize) { float chunk_size; newmis=spawn_temp(); newmis.movetype=MOVETYPE_BOUNCE; newmis.solid=SOLID_TRIGGER; newmis.velocity=dir; newmis.avelocity=spin; chunk_size=(ownersize_x+ownersize_y+ownersize_z)/3; newmis.scale=random(0.5)*chunk_size/24; if(!newmis.scale) newmis.scale=0.3; newmis.classname="type"; setmodel(newmis,"models/shard.mdl"); if(type=="ice") { newmis.skin=0; newmis.frame=0; newmis.touch=ice_hit; newmis.think=ice_think; thinktime newmis : 1; newmis.drawflags(+)DRF_TRANSLUCENT|MLS_ABSLIGHT; newmis.abslight=0.75; } else if(type=="pebbles") { newmis.skin=1; newmis.frame=rint(random(1,2)); newmis.touch=pebble_hit; newmis.speed=16; newmis.aflag=10; } /* else if(type=="ashes") { newmis.skin=2; newmis.frame=rint(random(1,2)); newmis.touch=ash_hit; newmis.speed=1; newmis.aflag=10; } */ setsize(newmis,'0 0 0','0 0 0'); setorigin(newmis,org); } void shatter () { vector dir,spin,org; float numshards,maxshards,rng; string type; if(self.movechain!=world&&!self.movechain.flags&FL_CLIENT) remove(self.movechain); if(self.scale==0) self.scale=1; if(self.classname=="snowball") maxshards=random(4,2); else maxshards=random(7,10); org=(self.absmin+self.absmax)*0.5; if(self.deathtype=="ice shatter"||self.deathtype=="ice melt") { //origin color radius count particleexplosion(org,14,25,50); // particle2(org,'-50 -50 -50','50 50 50',145,14,50); if(self.deathtype=="ice shatter") rng=600; else rng=self.size_x/2; if(self.classname!="snowball") sound(self,CHAN_BODY,"misc/icestatx.wav",1,ATTN_NORM); type="ice"; } else if(self.deathtype=="stone crumble") { sound(self,CHAN_BODY,"misc/sshatter.wav",1,ATTN_NORM); particleexplosion(org,10,60,50); // particle2(org,'-30 -30 -30','30 30 30',16,10,50); rng=450; type="pebbles"; } /* else if(self.deathtype=="burnt crumble") { sound(self,CHAN_BODY,"misc/bshatter.wav",1,ATTN_NORM); particleexplosion(org,1,60,50); // particle2(org,'-30 -30 -30','30 30 30',1,10,50); rng=200; type="ashes"; } */ while(numshards= THINGTYPE_WEBS) traceline(trace_endpos, spot2, FALSE, trace_ent); if (trace_ent != self.enemy) { if ((trace_ent.thingtype!=THINGTYPE_GLASS) || !trace_ent.takedamage || (trace_ent.flags & FL_MONSTER && trace_ent.classname!="player_sheep")) { return FALSE; } } return TRUE; } void archer_duck () [++ $duck1..$duck14] { ai_face(); if(cycle_wrapped) { setsize(self,'-16 -16 0','16 16 56'); if(infront(self.enemy)) self.think=archermissile; else self.think=archerdrawhold; thinktime self : 0; return; } else if (self.frame==$duck1) if (self.classname == "monster_archer") sound (self, CHAN_VOICE, "archer/growl.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "archer/pain2.wav", 1, ATTN_NORM); else if (self.frame == $duck5) setsize(self,'-16 -16 0','16 16 28'); else if (self.frame==$duck7) { archer_check_defense(); thinktime self :0.2; } } void archer_check_defense() { entity enemy_proj; if(random(2)>skill/10+self.skin/2) return; if (self.enemy.last_attack+0.5= $pain1) && (self.frame <= $pain16)) // Still going through pain anims return; if (self.attack_state == AS_MISSILE) return; // Hurt him, will he flinch or fight back? setsize(self,'-16 -16 0','16 16 56'); if ((self.pain_finished > time) || (random() < .5)) { archerredraw(); } else archer_pain_anim(); } void archer_launcharrow (float arrowtype,vector spot1,vector spot2) { self.last_attack=time; makevectors(self.angles); if (arrowtype==GREEN_ARROW) { sound (self, CHAN_WEAPON, "archer/arrowg.wav", 1, ATTN_NORM); Create_Missile(self,spot1,spot2, "models/akarrow.mdl","green_arrow",0,1000,archer_arrow_touch); } else if (arrowtype==RED_ARROW) { sound (self, CHAN_WEAPON, "archer/arrowr.wav", 1, ATTN_NORM); CreateRedFlash(spot1); Create_Missile(self,spot1,spot2,"models/akarrow.mdl","red_arrow",1,1000,archer_arrow_touch); } else { sound (self, CHAN_WEAPON, "archer/arrowg.wav", 1, ATTN_NORM); CreateRedFlash(spot1); Create_Missile(self,spot1,spot2,"models/akarrow.mdl","gold_arrow",2,1000,archer_arrow_touch); } newmis.drawflags(+)MLS_ABSLIGHT; newmis.abslight=0.5; thinktime newmis : 2.5; } /*----------------------------------------- archerdrawdone - drawing his bow to fire -----------------------------------------*/ void archerdrawdone () [-- $tranA7..$tranA1] { archer_check_defense(); walkmove(self.angles_y,0.5,FALSE); if(visible(self.enemy)) ai_face(); if(cycle_wrapped) { self.pausetime = time + random(.5,2); self.attack_state = AS_WAIT; archer_run(); } } /*----------------------------------------- archermissile - fire his arrow -----------------------------------------*/ void archermissile () [++ $fire1..$fire4] { float enemy_range,chance,ok,tspeed; vector spot1, spot2; self.attack_state = AS_MISSILE; if(visible(self.enemy)) ai_face(); else { self.think=self.th_run; thinktime self : 0; return; } if (self.frame == $fire2) // FIRE!!!! { makevectors(self.angles); spot1 = self.origin + v_forward*4 + v_right * 10 + v_up * 36; if(self.classname=="monster_archer_lord") { tspeed=vlen(self.enemy.velocity); if(tspeed>100) spot2=extrapolate_pos_for_speed(spot1,1000,self.enemy,0.3); } if(spot2=='0 0 0') { spot2 = self.enemy.origin + self.enemy.view_ofs; traceline (spot1, spot2, FALSE, self); if(trace_ent.thingtype>=THINGTYPE_WEBS) traceline (trace_endpos, spot2, FALSE, trace_ent); if (trace_ent == self.enemy) ok=TRUE; else if((trace_ent.health<=25||trace_ent.thingtype>=THINGTYPE_WEBS)&&trace_ent.takedamage&&(!trace_ent.flags&FL_MONSTER||trace_ent.classname=="player_sheep")) ok=TRUE; } else ok=TRUE; if(ok) { enemy_range = range(self.enemy); if (enemy_range < RANGE_MELEE) // Which arrow to use? chance = 0.80; else if (enemy_range < RANGE_NEAR) chance = 0.50; else if (enemy_range < RANGE_MID) chance = 0.30; else if (enemy_range < RANGE_FAR) chance = 0.10; if (self.classname=="monster_archer") { if (random(1) < chance) archer_launcharrow(RED_ARROW,spot1,spot2); else archer_launcharrow(GREEN_ARROW,spot1,spot2); } else // Archer Lord { if (random(1) < chance) archer_launcharrow(GOLD_ARROW,spot1,spot2); else archer_launcharrow(RED_ARROW,spot1,spot2); } self.attack_finished = time + random(.5,1); if(!self.skin) chance-=0.3; chance+=skill/10; // Reattack? if (random () > chance) // Is he done? archerdrawdone(); } else { self.attack_finished = time + random(); sound (self, CHAN_WEAPON, "misc/null.wav", 1, ATTN_NORM); archer_run(); return; } } if(cycle_wrapped) { self.frame = $draw10 ; archerredraw(); } else if(visible(self.enemy)) ai_face(); } /*----------------------------------------- archerredraw - redrawing his bow to fire -----------------------------------------*/ void archerredraw () [++ $redraw1..$redraw12] { self.attack_state = AS_MISSILE; archer_check_defense(); if (self.frame == $redraw8) sound (self, CHAN_WEAPON, "archer/draw.wav", 1, ATTN_NORM); if (cycle_wrapped) archermissile(); else if(visible(self.enemy)) ai_face(); else { self.think=self.th_run; thinktime self : 0; return; } } /*----------------------------------------- archerdrawhold - waiting for right chance to attack -----------------------------------------*/ void archerdrawhold () { float chance,startframe,endframe; if(visible(self.enemy)) ai_face(); else { self.think=self.th_run; thinktime self : 0; return; } archer_check_defense(); startframe=$waitB1; endframe=$waitB12; if (!self.spawnflags & ARCHER_STUCK) { if (vlen(self.enemy.origin - self.origin)<=200) { if(infront(self.enemy)) if(walkmove(self.angles_y+180,3,FALSE)) { startframe=$backup1; endframe=$backup8; } } } AdvanceFrame(startframe,endframe); self.think=archerdrawhold; thinktime self : 0.05; enemy_range = range(self.enemy); if (enemy_range < RANGE_NEAR && random() < 0.4) archermissile(); else if (cycle_wrapped||random()= $tranA1) && (self.frame <= $tranA13)) walkmove(self.angles_y+180,.28,FALSE); if (cycle_wrapped) // Archer has drawn the arrow archerdrawhold(); else if (visible(self.enemy)) ai_face(); else { self.think=self.th_run; thinktime self : 0; return; } if ((random(1)<.10) && (self.frame == $walk1)) { if (self.classname == "monster_archer") sound (self, CHAN_BODY, "archer/growl.wav", 1, ATTN_NORM); else { if (random() < .70) sound (self, CHAN_BODY, "archer/growl2.wav", 1, ATTN_NORM); else sound (self, CHAN_BODY, "archer/growl3.wav", 1, ATTN_NORM); } } } /*----------------------------------------- archer_run - run towards the enemy -----------------------------------------*/ void archer_run(void) { self.think = archer_run; thinktime self : HX_FRAME_TIME; archer_check_defense(); if ((random(1)<.10) && (self.frame == $walk1)) { if (self.classname == "monster_archer") sound (self, CHAN_VOICE, "archer/growl.wav", 1, ATTN_NORM); else { if (random() < .70) sound (self, CHAN_VOICE, "archer/growl2.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "archer/growl3.wav", 1, ATTN_NORM); } } if ((self.spawnflags & ARCHER_STUCK) || (self.attack_state == AS_WAIT)) { AdvanceFrame($waitA1,$waitA18); ai_run(0); } else { AdvanceFrame($walk1,$walk16); ai_run(4); } } /*----------------------------------------- archer_walk - walking his beat -----------------------------------------*/ void archer_walk(void) [++ $patrol1..$patrol22] { thinktime self : HX_FRAME_TIME + .01; // Make him move a little slower so his run will look faster archer_check_defense(); if ((random()<.05) && (self.frame == $patrol1)) { if (self.classname == "monster_archer") sound (self, CHAN_VOICE, "archer/growl.wav", 1, ATTN_NORM); else { if (random() < .70) sound (self, CHAN_VOICE, "archer/growl2.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "archer/growl3.wav", 1, ATTN_NORM); } } if (self.spawnflags & ARCHER_STUCK) // No moving { self.frame = $patrol1; // FIXME: this should be the wait animations ai_walk(0); } else ai_walk(2.3); } /*----------------------------------------- archer_stand - standing and waiting -----------------------------------------*/ void archer_stand(void) [++ $waitA1..$waitA18] { if (random()<0.5) { archer_check_defense(); ai_stand(); } } /*QUAKED monster_archer (1 0.3 0) (-16 -16 0) (16 16 50) AMBUSH STUCK JUMP PLAY_DEAD DORMANT The Archer Knight monster -------------------------FIELDS------------------------- Health : 80 Experience Pts: 25 Favorite TV shows: Friends & Baywatch Favorite Color: Blue Likes: Shooting arrows into people and long walks along the beach Dislikes: Anything having to do with Pauly Shore -------------------------------------------------------- */ void monster_archer () { if (deathmatch) { remove(self); return; } if (!self.flags2 & FL_SUMMONED) { precache_archer(); } CreateEntityNew(self,ENT_ARCHER,"models/archer.mdl",archer_die); self.th_stand = archer_stand; self.th_walk = archer_walk; self.th_run = archer_run; self.th_melee = archerdraw; self.th_missile = archerdraw; self.th_pain = archer_pain; self.decap = 0; self.headmodel = "models/archerhd.mdl"; self.mintel = 7; self.monsterclass = CLASS_GRUNT; self.experience_value = 25; self.health = 80; self.flags (+) FL_MONSTER; self.yaw_speed = 10; self.view_ofs = '0 0 40'; walkmonster_start(); } /*QUAKED monster_archer_lord (1 0.3 0) (-16 -16 0) (16 16 50) AMBUSH STUCK JUMP PLAY_DEAD DORMANT The Archer Lord monster -------------------------FIELDS------------------------- Health : 325 Experience Pts: 200 Favorite Cities: Madrid & Las Vegas Favorite Flower: Orchid What people don't know about me: I cry at sad movies What people say when they see me: Don't shoot!! Don't shoot!! -------------------------------------------------------- */ void monster_archer_lord () { if (deathmatch) { remove(self); return; } if (!self.flags2 & FL_SUMMONED) { precache_model("models/archer.mdl"); precache_model("models/archerhd.mdl"); precache_model("models/gspark.spr"); precache_sound ("archer/arrowg.wav"); precache_sound ("archer/arrowr.wav"); precache_sound ("archer/growl2.wav"); precache_sound ("archer/growl3.wav"); precache_sound ("archer/pain2.wav"); precache_sound ("archer/sight2.wav"); precache_sound ("archer/death2.wav"); precache_sound ("archer/draw.wav"); } CreateEntityNew(self,ENT_ARCHER,"models/archer.mdl",archer_die); self.th_stand = archer_stand; self.th_walk = archer_walk; self.th_run = archer_run; self.th_melee = archerdraw; self.th_missile = archerdraw; self.th_pain = archer_pain; self.decap = 0; self.headmodel = "models/archerhd.mdl"; self.mintel = 7; self.monsterclass = CLASS_HENCHMAN; self.experience_value = 200; self.health = 325; self.skin = 1; self.flags (+) FL_MONSTER; self.yaw_speed = 10; self.view_ofs = '0 0 40'; walkmonster_start(); } /* ================= ArcherCheckAttack ================= */ float ArcherCheckAttack (void) { local vector spot1, spot2; local entity targ; local float chance; // if (enemy_range <= RANGE_MELEE) // Enemy is too close...attack // { // self.attack_state = AS_MISSILE; self.pain_finished = 0; // self.attack_finished = 0; // } if (self.attack_finished > time) return FALSE; if (!enemy_vis) return FALSE; if (enemy_range == RANGE_FAR) { if (self.attack_state != AS_STRAIGHT) { self.attack_state = AS_STRAIGHT; archer_run (); } return FALSE; } targ = self.enemy; makevectors(self.angles); // see if any entities are in the way of the shot spot1 = self.origin + v_right * 10 + v_up * 36; spot2 = targ.origin + targ.view_ofs; traceline (spot1, spot2, FALSE, self); if(trace_ent.thingtype>=THINGTYPE_WEBS) traceline (trace_endpos, spot2, FALSE, trace_ent); if (trace_ent != targ) if((trace_ent.health>25&&trace_ent.thingtype!=THINGTYPE_GLASS)||!trace_ent.takedamage||(trace_ent.flags&FL_MONSTER&&trace_ent.classname!="player_sheep")) { self.attack_state = AS_STRAIGHT; return FALSE; } // Chances of attack // 50% at MID range // 65% at NEAR range // 80% at MELEE range enemy_range = range(self.enemy); if (enemy_range == RANGE_MELEE) chance = 0.40; else if (enemy_range == RANGE_NEAR) chance = 0.3; else if (enemy_range == RANGE_MID) chance = 0.2; else chance = 0; if ((random () < chance) && (self.attack_state != AS_MISSILE)) // Will he attack? { self.attack_state = AS_MISSILE; return TRUE; } if (enemy_range == RANGE_MID) { if (random (1) < .5) // Will he side step? self.attack_state = AS_SLIDING; else self.attack_state = AS_STRAIGHT; } else { if (self.attack_state != AS_SLIDING) self.attack_state = AS_SLIDING; } return FALSE; } gamecode/hc/h2/artifact.hc000066400000000000000000000347041444734033100156510ustar00rootroot00000000000000/* * artifact_touch() -- Called when an artifact is being touched. * Awards players random amounts of whatever they represent. */ void() SUB_regen; void() StartItem; void ring_touch(void); void artifact_touch() { float amount; if(other.classname != "player"||other.model=="models/sheep.mdl") { // Only players can take artifacts return; } if(other.health <= 0) { // Player is dead return; } if (self.owner == other && self.artifact_ignore_owner_time > time) return; if (self.artifact_ignore_time > time) return; // Take appropriate action if(self.netname == STR_TORCH) { if ((other.cnt_torch + 1) > 15) return; other.cnt_torch += 1; } else if(self.netname == STR_HEALTHBOOST) // 25 limit { if ((other.cnt_h_boost + 1) > 30||(other.playerclass!=CLASS_CRUSADER&&other.cnt_h_boost + 1 > 15)) return; other.cnt_h_boost += 1; } else if(self.netname == STR_SUPERHEALTHBOOST) // 5 limit { if (deathmatch&&(other.cnt_sh_boost + 1) > 2) return; if ((other.cnt_sh_boost + 1) > 5) return; other.cnt_sh_boost += 1; } else if(self.netname == STR_MANABOOST) { if ((other.cnt_mana_boost + 1) > 15) return; other.cnt_mana_boost += 1; } else if(self.netname == STR_TELEPORT) { if ((other.cnt_teleport + 1) > 15) return; other.cnt_teleport += 1; } else if(self.netname == STR_TOME) { if ((other.cnt_tome + 1) > 15) return; other.cnt_tome += 1; } else if(self.netname == STR_SUMMON) { if ((other.cnt_summon + 1) > 15) return; other.cnt_summon += 1; } else if(self.netname == STR_INVISIBILITY) { if ((other.cnt_invisibility + 1) > 15) return; other.cnt_invisibility += 1; } else if(self.netname == STR_GLYPH) { if(other.playerclass==CLASS_CRUSADER) { if ((other.cnt_glyph + 5) > 50) return; other.cnt_glyph += 5; } else { if ((other.cnt_glyph + 1) > 15) return; other.cnt_glyph += 1; } } else if(self.netname == STR_HASTE) { if ((other.cnt_haste + 1) > 15) return; other.cnt_haste += 1; } else if(self.netname == STR_BLAST) { if ((other.cnt_blast + 1) > 15) return; other.cnt_blast += 1; } else if(self.netname == STR_POLYMORPH) { if ((other.cnt_polymorph + 1) > 15) return; other.cnt_polymorph += 1; } else if(self.netname == STR_FLIGHT) { if ((other.cnt_flight + 1) > 15) return; other.cnt_flight += 1; } else if(self.netname == STR_CUBEOFFORCE) { if ((other.cnt_cubeofforce + 1) > 15) return; other.cnt_cubeofforce += 1; } else if(self.netname == STR_INVINCIBILITY) { if ((other.cnt_invincibility + 1) > 15) return; other.cnt_invincibility += 1; } /* else if(self.classname == "art_sword_and_crown") { centerprint(other,"You are victorious!\n"); bprint(other.netname); bprint(" has captured the Crown!\n"); } */ amount = random(); if (amount < 0.5) { sprint (other, STR_YOUPOSSESS); sprint (other, self.netname); } else { sprint (other, STR_YOUHAVEACQUIRED); sprint (other, self.netname); } sprint (other,"\n"); if (self.artifact_respawn) { self.mdl = self.model; if(self.netname==STR_INVINCIBILITY) thinktime self : 120; else if(self.netname==STR_INVISIBILITY) thinktime self : 90; else thinktime self : 60; self.think = SUB_regen; } sound(other, CHAN_VOICE, "items/artpkup.wav", 1, ATTN_NORM); stuffcmd(other, "bf\n"); self.solid = SOLID_NOT; // other.items (+) self.items; self.model = string_null; activator = other; SUB_UseTargets(); // Fire all targets / killtargets if(!self.artifact_respawn) { remove(self); } } void Artifact_Cheat(void) { self.cnt_sh_boost = 20; self.cnt_summon = 20; self.cnt_glyph = 20; self.cnt_blast = 20; self.cnt_polymorph = 20; self.cnt_flight = 20; self.cnt_cubeofforce = 20; self.cnt_invincibility = 20; self.cnt_invisibility = 20; self.cnt_haste = 20; self.cnt_mana_boost = 20; self.cnt_sh_boost = 20; self.cnt_h_boost = 20; self.cnt_teleport = 20; self.cnt_tome = 20; self.cnt_torch = 20; } /*----------------------------------------- GenerateArtifactModel - generate the artifact -----------------------------------------*/ void GenerateArtifactModel(string modelname,string art_name,float respawnflag) { if (respawnflag) // Should this thing respawn self.artifact_respawn = deathmatch; setmodel(self, modelname); self.netname = art_name; if (modelname == "models/ringft.mdl") { self.netname = "Ring of Flight"; self.touch = ring_touch; } else if (modelname != "models/a_xray.mdl") self.touch = artifact_touch; setsize (self, '0 0 0', '0 0 0'); StartItem(); } /*----------------------------------------- spawn_artifact - decide which artifact to spawn -----------------------------------------*/ void spawn_artifact (float artifact,float respawnflag) { if (artifact == ARTIFACT_HASTE) GenerateArtifactModel("models/a_haste.mdl",STR_HASTE,respawnflag); else if (artifact == ARTIFACT_POLYMORPH) GenerateArtifactModel("models/a_poly.mdl",STR_POLYMORPH,respawnflag); else if (artifact == ARTIFACT_GLYPH) GenerateArtifactModel("models/a_glyph.mdl",STR_GLYPH,respawnflag); else if (artifact == ARTIFACT_INVISIBILITY) GenerateArtifactModel("models/a_invis.mdl",STR_INVISIBILITY,respawnflag); else if (artifact == ARTIFACT_INVINCIBILITY) GenerateArtifactModel("models/a_invinc.mdl",STR_INVINCIBILITY,respawnflag); else if (artifact == ARTIFACT_CUBEOFFORCE) GenerateArtifactModel("models/a_cube.mdl",STR_CUBEOFFORCE,respawnflag); else if (artifact == ARTIFACT_SUMMON) GenerateArtifactModel("models/a_summon.mdl",STR_SUMMON,respawnflag); else if (artifact == ARTIFACT_TOME) GenerateArtifactModel("models/a_tome.mdl",STR_TOME,respawnflag); else if (artifact == ARTIFACT_TELEPORT) GenerateArtifactModel("models/a_telprt.mdl",STR_TELEPORT,respawnflag); else if (artifact == ARTIFACT_MANA_BOOST) GenerateArtifactModel("models/a_mboost.mdl",STR_MANABOOST,respawnflag); else if (artifact == ARTIFACT_BLAST) GenerateArtifactModel("models/a_blast.mdl",STR_BLAST,respawnflag); else if (artifact == ARTIFACT_TORCH) GenerateArtifactModel("models/a_torch.mdl",STR_TORCH,respawnflag); else if (artifact == ARTIFACT_HP_BOOST) GenerateArtifactModel("models/a_hboost.mdl",STR_HEALTHBOOST,respawnflag); else if (artifact == ARTIFACT_SUPER_HP_BOOST) GenerateArtifactModel("models/a_shbost.mdl",STR_SUPERHEALTHBOOST,respawnflag); else if (artifact == ARTIFACT_FLIGHT) GenerateArtifactModel("models/ringft.mdl",STR_FLIGHT,respawnflag); } /* ==================================================================================================== SUPER HP BOOST ==================================================================================================== */ void DecrementSuperHealth() { float wait_time,over,decr_health; if (self.health > self.max_health) { if (self.health<200) { wait_time = 2; decr_health = 1; } else if (self.health<400) // Vary rate of update time { decr_health = 1; over = 200 - (self.health - 200); wait_time = over/400; if (wait_time < .10) wait_time = .10; } else // Vary the amount of the decrement { wait_time = .10; over = self.health - 400; decr_health = over * .016; decr_health = ceil(decr_health); if (decr_health < 2) decr_health = 2; } self.health = self.health - decr_health; self.healthtime = time + wait_time; } else // All done, get rid of it self.artifact_flags (-) AFL_SUPERHEALTH; } void use_super_healthboost() { self.healthtime = time + .05; if(self.health<-100) self.health=1; else if(self.health<0) self.health+=100; else if (self.health < 899) self.health = self.health + 100; else if (self.health > 999) self.health = 999; self.cnt_sh_boost -= 1; self.artifact_flags(+)AFL_SUPERHEALTH; // Show the health is in use } /*QUAKED art_SuperHBoost (.0 .0 .5) (-8 -8 -44) (8 8 20) FLOATING Artifact for the Super Health Boost -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void art_SuperHBoost() { spawn_artifact(ARTIFACT_SUPER_HP_BOOST,RESPAWN); } /* ==================================================================================================== HP BOOST ==================================================================================================== */ void use_healthboost() { if(self.health >= self.max_health) { // Already at max health return; } self.cnt_h_boost -= 1; self.health += 25; if(self.health > self.max_health) { self.health = self.max_health; } } /*QUAKED art_HealthBoost (.0 .0 .5) (-8 -8 -44) (8 8 20) FLOATING Artifact for the Health Boost -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void art_HealthBoost() { spawn_artifact(ARTIFACT_HP_BOOST,RESPAWN); } /* ==================================================================================================== The TORCH ==================================================================================================== */ /*QUAKED art_torch (.0 .0 .5) (-8 -8 -44) (8 8 20) FLOATING Artifact for the torch -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void art_torch() { spawn_artifact(ARTIFACT_TORCH,RESPAWN); } void KillTorch() { if(!self.artifact_active&ART_INVISIBILITY) self.effects(-)EF_DIMLIGHT; // Turn off lights self.artifact_flags(-)AFL_TORCH; // Turn off torch flag } void DouseTorch()//Never called?! { sound (self, CHAN_BODY, "raven/douse.wav", 1, ATTN_IDLE); self.torchtime = 0; KillTorch(); } void DimTorch() { sound (self, CHAN_BODY, "raven/kiltorch.wav", 1, ATTN_IDLE); self.effects(-)EF_TORCHLIGHT; self.torchtime = time + 7; self.torchthink = KillTorch; } void FullTorch() { sound (self, CHAN_BODY, "raven/fire1.wav", 1, ATTN_NORM); self.effects(+)EF_TORCHLIGHT; self.torchtime = time + 23; self.torchthink = DimTorch; } /* ============ TorchBurn ============ */ void UseTorch() { if((self.effects!=EF_DIMLIGHT) && (self.effects!=EF_TORCHLIGHT)) { sound (self, CHAN_WEAPON, "raven/littorch.wav", 1, ATTN_NORM); self.effects(+)EF_DIMLIGHT; // set player to emit light self.torchtime = time + 1; self.torchthink = FullTorch; self.artifact_flags (+) AFL_TORCH; // Show the torch is in use self.cnt_torch -= 1; } } /*QUAKED art_blastradius (.0 .0 .5) (-8 -8 -44) (8 8 20) FLOATING Artifact for Blast Radius -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void art_blastradius() { spawn_artifact(ARTIFACT_BLAST,RESPAWN); } void UseManaBoost() { self.bluemana = self.max_mana; self.greenmana = self.max_mana; self.cnt_mana_boost -= 1; } /*QUAKED art_manaboost (.0 .0 .5) (-8 -8 -44) (8 8 20) FLOATING Artifact for Mana Boost -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void art_manaboost() { spawn_artifact(ARTIFACT_MANA_BOOST,RESPAWN); } /*QUAKED art_teleport (.0 .0 .5) (-8 -8 -44) (8 8 20) FLOATING Artifact for Teleportation -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void art_teleport() { spawn_artifact(ARTIFACT_TELEPORT,RESPAWN); } /*QUAKED art_tomeofpower (.0 .0 .5) (-8 -8 -44) (8 8 20) FLOATING Artifact for Tome of Power -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void art_tomeofpower() { spawn_artifact(ARTIFACT_TOME,RESPAWN); } /*QUAKED art_summon (.0 .0 .5) (-8 -8 -44) (8 8 20) FLOATING Artifact for Summoning -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void art_summon() { spawn_artifact(ARTIFACT_SUMMON,RESPAWN); } /*QUAKED art_glyph (.0 .0 .5) (-8 -8 -44) (8 8 20) FLOATING Artifact for Glyph of the Ancients -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void art_glyph() { spawn_artifact(ARTIFACT_GLYPH,RESPAWN); } /*QUAKED art_haste (.0 .0 .5) (-8 -8 -44) (8 8 20) FLOATING Artifact for Haste -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void art_haste() { spawn_artifact(ARTIFACT_HASTE,RESPAWN); } /*QUAKED art_polymorph (.0 .0 .5) (-8 -8 -44) (8 8 20) FLOATING Artifact for Polymorph -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void art_polymorph() { spawn_artifact(ARTIFACT_POLYMORPH,RESPAWN); } /*QUAKED art_cubeofforce (.0 .0 .5) (-8 -8 -44) (8 8 20) FLOATING Artifact for Cube Of Force -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void art_cubeofforce() { spawn_artifact(ARTIFACT_CUBEOFFORCE,RESPAWN); } /*QUAKED art_invincibility (.0 .0 .5) (-8 -8 -44) (8 8 20) FLOATING Artifact for Invincibility -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void art_invincibility() { spawn_artifact(ARTIFACT_INVINCIBILITY,RESPAWN); } /*QUAKED art_invisibility (.0 .0 .5) (-8 -8 -44) (8 8 20) FLOATING Artifact for Invisibility -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void art_invisibility() { spawn_artifact(ARTIFACT_INVISIBILITY,RESPAWN); } /* void spawn_art_sword_and_crown(void) { setmodel(self, "models/xcalibur.mdl"); self.netname = "Sword"; self.touch = artifact_touch; setsize (self, '-8 -8 -44', '8 8 20'); StartItem(); } */ /*QUAKED art_sword_and_crown (.0 .0 .5) (-8 -8 -44) (8 8 20) FLOATING Artifact for Sword and Crown -------------------------FIELDS------------------------- None -------------------------------------------------------- */ /* void art_sword_and_crown() { precache_model2("models/xcalibur.mdl"); self.artifact_respawn = deathmatch; spawn_art_sword_and_crown(); } */ void item_spawner_use(void) { DropBackpack(); } /*QUAKED item_spawner (.0 .0 .5) (-8 -8 -44) (8 8 20) Generic item spawner -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void item_spawner() { setmodel(self, self.model); // set size and link into world self.solid = SOLID_NOT; self.movetype = MOVETYPE_NONE; self.modelindex = 0; self.model = ""; self.effects = EF_NODRAW; self.use = item_spawner_use; } gamecode/hc/h2/assgren.hc000066400000000000000000000150431444734033100155110ustar00rootroot00000000000000/* * Grenade Throw, Assassin. */ /* ============================================================================== Q:\art\models\weapons\grenades\final\assgr.hc ============================================================================== */ // For building the model $cd Q:\art\models\weapons\grenades\final $origin 0 0 0 $base BASE skin $skin skin $flags 0 $frame select1 select2 select3 select4 select5 $frame select6 $frame idle $frame throw1 throw2 throw3 throw4 throw5 $frame throw6 throw7 throw8 throw9 throw10 $frame throw11 throw12 void grenade_trail () { if(self.lifetime70) missile.think=SuperGrenadeExplode; else missile.think=DarkExplosion; thinktime missile : random(0.1,0.6); } self.dmg*=2; if(self.classname=="multigrenade") { if(random()<0.3) MonsterQuake(200); MultiExplode(); } else DarkExplosion(); }; void() ThrowMultiGrenade = {//FIXME: too many t_rad's? entity missile; makevectors(self.v_angle); self.greenmana-=12; sound(self,CHAN_WEAPON,"misc/whoosh.wav",1,ATTN_NORM); missile=spawn(); missile.frags=TRUE; missile.owner=self; missile.classname="multigrenade"; missile.movetype=MOVETYPE_BOUNCE; missile.solid=SOLID_BBOX; missile.takedamage=DAMAGE_YES; missile.health=3; missile.th_die=SuperGrenadeExplode; missile.touch=GrenadeTouch2; missile.dmg=250;//simulates max level for now missile.o_angle = self.origin+self.proj_ofs+v_forward*8+v_right*8; missile.speed=500+self.weaponframe_cnt*10; // missile.velocity=(normalize(v_forward)+'0 0 .4')*missile.speed; //UQ method if(self.v_angle_x) missile.velocity = v_forward*missile.speed + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10; else { missile.velocity = aim(self, missile.o_angle,1000); missile.velocity = missile.velocity * missile.speed; missile.velocity_z = 200; } missile.angles = vectoangles(missile.velocity); missile.avelocity=randomv('-300 -300 -300','300 300 300'); setmodel(missile,"models/assgren.mdl"); missile.scale=2; setsize (missile, '0 0 0', '0 0 0'); setorigin(missile,missile.o_angle); missile.lifetime=time+2; missile.think=grenade_trail; thinktime missile : 0; }; void()grenade_select; void()grenade_throw; void grenade_idle(void) { self.th_weapon=grenade_idle; self.weaponframe=$idle; } void grenade_reload (void) { self.th_weapon=grenade_reload; self.wfs = advanceweaponframe($select1,$select6); self.weaponmodel = "models/v_assgr.mdl"; if (self.wfs==WF_CYCLE_WRAPPED) grenade_idle(); } void grenade_throw (void) { self.th_weapon=grenade_throw; self.wfs = advanceweaponframe($throw1,$throw12); if(self.button0&&self.weaponframe==$throw5) { self.weaponframe=$throw4; if(self.weaponframe_cnt<50) self.weaponframe_cnt+=1; } else if(self.weaponframe==$throw10) { if(self.artifact_active&ART_TOMEOFPOWER) { ThrowMultiGrenade(); self.attack_finished=time + 2; } else { ThrowMiniGrenade(); self.attack_finished=time+0.3; } self.weaponframe_cnt=0; } else if (self.wfs==WF_CYCLE_WRAPPED) grenade_reload(); } void grenade_select (void) { //selection sound? self.th_weapon=grenade_select; self.wfs = advanceweaponframe($select1,$select6); self.weaponmodel = "models/v_assgr.mdl"; if (self.wfs==WF_CYCLE_WRAPPED) { self.attack_finished = time - 1; grenade_idle(); } } void grenade_deselect (void) { //selection sound? self.th_weapon=grenade_deselect; self.wfs = advanceweaponframe($select6,$select1); if (self.wfs==WF_CYCLE_WRAPPED) W_SetCurrentAmmo(); } gamecode/hc/h2/axe.hc000066400000000000000000000125431444734033100146260ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\weapons\axe\final\axe.hc ============================================================================== */ // For building the model $cd Q:\art\models\weapons\axe\final $origin 10 -10 10 $base BASE skin $skin skin $flags 0 $frame AxeRoot1 $frame 1stAxe1 1stAxe2 1stAxe3 1stAxe4 1stAxe5 $frame 1stAxe6 1stAxe7 1stAxe8 $frame 1stAxe11 1stAxe12 1stAxe14 $frame 1stAxe15 1stAxe17 1stAxe18 $frame 1stAxe21 1stAxe22 1stAxe23 $frame 1stAxe25 1stAxe27 float AXE_DAMAGE = 24; float AXE_ADD_DAMAGE = 6; void() T_PhaseMissileTouch; void axeblade_gone(void) { sound (self, CHAN_VOICE, "misc/null.wav", 1, ATTN_NORM); sound (self, CHAN_WEAPON, "misc/null.wav", 1, ATTN_NORM); if (self.skin==0) CreateLittleWhiteFlash(self.origin); else CreateLittleBlueFlash(self.origin); remove(self.goalentity); remove(self); } void axeblade_run (void) [ ++ 0 .. 5] { //dvanceFrame(0,5); if (self.lifetime < time) axeblade_gone(); } void axetail_run (void) { if(!self.owner) remove(self); else { self.origin = self.owner.origin; self.velocity = self.owner.velocity; self.owner.angles = vectoangles(self.velocity); self.angles = self.owner.angles; self.origin = self.owner.origin; } } void launch_axtail (entity axeblade) { local entity tail; tail = spawn (); tail.movetype = MOVETYPE_NOCLIP; tail.solid = SOLID_NOT; tail.classname = "ax_tail"; setmodel (tail, "models/axtail.mdl"); setsize (tail, '0 0 0', '0 0 0'); tail.drawflags (+)DRF_TRANSLUCENT; tail.owner = axeblade; tail.origin = tail.owner.origin; tail.velocity = tail.owner.velocity; tail.angles = tail.owner.angles; axeblade.goalentity = tail; } void launch_axe (vector dir_mod,vector angle_mod) { entity missile; self.attack_finished = time + 0.4; missile = spawn (); CreateEntityNew(missile,ENT_AXE_BLADE,"models/axblade.mdl",SUB_Null); missile.owner = self; missile.classname = "ax_blade"; // set missile speed makevectors (self.v_angle + dir_mod); missile.velocity = normalize(v_forward); missile.velocity = missile.velocity * 900; missile.touch = T_PhaseMissileTouch; // Point it in the proper direction missile.angles = vectoangles(missile.velocity); missile.angles += angle_mod; // set missile duration missile.counter = 4; // Can hurt two things before disappearing missile.cnt = 0; // Counts number of times it has hit walls missile.lifetime = time + 2; // Or lives for 2 seconds and then dies when it hits anything setorigin (missile, self.origin + self.proj_ofs + v_forward*10 + v_right * 1); //sound (missile, CHAN_VOICE, "paladin/axblade.wav", 1, ATTN_NORM); if (self.artifact_active & ART_TOMEOFPOWER) { missile.frags=TRUE; missile.classname = "powerupaxeblade"; missile.skin = 1; missile.drawflags = (self.drawflags & MLS_MASKOUT)| MLS_POWERMODE; } else missile.classname = "axeblade"; missile.lifetime = time + 2; thinktime missile : HX_FRAME_TIME; missile.think = axeblade_run; launch_axtail(missile); } /* ================ axeblade_fire ================ */ void() axeblade_fire = { if ((self.artifact_active & ART_TOMEOFPOWER) && (self.greenmana >= 8)) { FireMelee (50,25,64); sound (self, CHAN_WEAPON, "paladin/axgenpr.wav", 1, ATTN_NORM); // makevectors (self.v_angle); // CreateLittleBlueFlash(self.origin + v_forward*40 + v_up * 42); launch_axe('0 0 0','0 0 0'); // Middle launch_axe('0 5 0','0 0 0'); // Side launch_axe('0 -5 0','0 0 0'); // Side // launch_axe('5 0 0','0 0 0'); // Top // launch_axe('-5 0 0','0 0 0'); // Bottom self.greenmana -= 8; } else if (self.greenmana >= 2) { FireMelee (WEAPON1_BASE_DAMAGE,WEAPON1_ADD_DAMAGE,64); if (self.greenmana >= 2) { sound (self, CHAN_WEAPON, "paladin/axgen.wav", 1, ATTN_NORM); // makevectors (self.v_angle); // CreateLittleWhiteFlash(self.origin + self.proj_ofs + v_forward*24 + v_right * 2); launch_axe('0 0 0','0 0 300'); self.greenmana -= 2; } } }; void axe_ready (void) { self.th_weapon=axe_ready; self.weaponframe = $AxeRoot1; } void axe_select (void) { self.wfs = advanceweaponframe($1stAxe18,$1stAxe3); if (self.weaponframe == $1stAxe14) sound (self, CHAN_WEAPON, "weapons/vorpswng.wav", 1, ATTN_NORM); self.weaponmodel = "models/axe.mdl"; self.th_weapon=axe_select; self.last_attack=time; if (self.wfs == WF_LAST_FRAME) { self.attack_finished = time - 1; axe_ready(); } } void axe_deselect (void) { self.wfs = advanceweaponframe($1stAxe18,$1stAxe3); self.th_weapon=axe_deselect; self.oldweapon = IT_WEAPON3; if (self.wfs == WF_LAST_FRAME) W_SetCurrentAmmo(); } void axe_a (void) { self.wfs = advanceweaponframe($1stAxe1,$1stAxe25); self.th_weapon = axe_a; // These frames are used during selection animation if ((self.weaponframe >= $1stAxe2) && (self.weaponframe <= $1stAxe4)) self.weaponframe +=1; else if ((self.weaponframe >= $1stAxe6) && (self.weaponframe <= $1stAxe7)) self.weaponframe +=1; if (self.weaponframe == $1stAxe15) { sound (self, CHAN_WEAPON, "weapons/vorpswng.wav", 1, ATTN_NORM); axeblade_fire(); } if (self.wfs == WF_LAST_FRAME) axe_ready(); } void pal_axe_fire(void) { axe_a (); if (self.artifact_active & ART_TOMEOFPOWER) self.attack_finished = time + .7; else self.attack_finished = time + .35; } gamecode/hc/h2/barrel.hc000066400000000000000000000241271444734033100153210ustar00rootroot00000000000000/* ============================================================================== BARRELS ============================================================================== */ $cd \art\models\objects\barrel\final $base base 128 128 $skin skin $frame resting //void MakeExplosion(string explodemodel); void()barrel_check_float; /*void obj_barrel_gravity() { local vector slope; if(self.ltime <= time) { slope = getslope(self); if(slope != self.dest) { self.speed = trace_plane_normal_z * 20; self.ideal_yaw = self.dest = trace_plane_normal; } self.ltime = time + 0.4; } thinktime self : 0.05; } */ void float(void) { float x_mod, y_mod, z_mod; float content; vector org; org=self.origin; content=pointcontents(org); if(content==CONTENT_WATER||content==CONTENT_SLIME||content==CONTENT_LAVA) { if(self.velocity_x) self.velocity_x/=1.1; if(self.velocity_y) self.velocity_y/=1.1; org_z+= self.maxs_z*0.77;//float only 23% above waterlevel content=pointcontents(org); if(content==CONTENT_WATER||content==CONTENT_SLIME||content==CONTENT_LAVA) { // self.flags(+)FL_SWIM; self.flags(-)FL_ONGROUND; if(self.velocity_z<77) self.velocity_z=80; else self.velocity_z+=random(0,0.01); } else { self.velocity_z-=random(0,0.01); } if(random()<0.3) { y_mod=random(-0.15,0.15); if(random()<0.5) self.angles_y+=y_mod; else self.angles_y+=y_mod; } if(random()<0.3) { x_mod=random(-0.15,0.15); if(fabs(self.angles_x+x_mod)>10) self.angles_x-=x_mod; else self.angles_x+=x_mod; } if(random()<0.3) { z_mod=random(-0.15,0.15); if(fabs(self.angles_z+z_mod)>10) self.angles_z-=z_mod; else self.angles_z+=z_mod; } thinktime self : 0.1; } else { self.angles_z=self.angles_z=0; self.classname="barrel"; self.think=barrel_check_float; thinktime self : 0.5; } } void barrel_check_float (void) { vector org; float content; org=self.origin; content=pointcontents(org); if(content==CONTENT_WATER||content==CONTENT_SLIME||content==CONTENT_LAVA&&(!self.spawnflags&BARREL_SINK)) { self.classname=="barrel_floating"; self.think=float; thinktime self : 0; } else { self.classname="barrel"; self.think=barrel_check_float; thinktime self : 0.5; } } /* * obj_barrel_explode() -- Blows the barrel up. */ void obj_barrel_explode() { entity attacker; self.th_die=SUB_Null; self.takedamage = DAMAGE_NO; if(self.enemy.flags2&FL_ALIVE)//give credit to person who blew it up attacker=self.enemy; else attacker=self; T_RadiusDamage(self, attacker, 100, self); sound(self, CHAN_VOICE, "weapons/explode.wav", 1, ATTN_NORM); particleexplosion(self.origin + '0 0 83',384,60,40); starteffect(CE_LG_EXPLOSION , (self.absmin+self.absmax)*0.5); chunk_death(); } void()monster_rat; void rat_spawn(float offset) { newmis=spawn(); newmis.angles_y=self.angles_y+offset*60; newmis.flags2(+)FL_SUMMONED; if(self.target) newmis.target=self.target; makevectors(newmis.angles); setorigin(newmis,self.origin+v_forward*16); newmis.think=monster_rat; thinktime newmis : 0; } void barrel_die () { self.solid=SOLID_NOT; if(random()<0.3||self.target!=""||self.classname=="monster_ratnest") { float r; r=rint(random(3,6)); while(r>0) { rat_spawn(r); r-=1; } } if(self.classname!="monster_ratnest") chunk_death(); else remove(self); } /* * obj_barrel_slide() -- Slides a barrel in the direction given in self.movedir. */ /* void obj_barrel_slide() { local entity victim; local float direction; //old if function, direction was null // if(walkmove(direction, self.count) == FALSE) if(!walkmove(self.cnt, self.count, FALSE)) { //Is this supposed to push something else it hits? victim = findradius(self.origin, self.count + 38); while(victim) { if(victim.movetype != MOVETYPE_NONE && victim != self) { direction = vectoyaw(victim.origin - self.origin); if(self.cnt <= 60 && direction >= 300) direction -= 360; if(direction > self.cnt - 60 && direction < self.cnt + 60) victim.velocity += (victim.origin - self.origin) * self.count; } victim = victim.chain; } } // if(!self.spawnflags & BARREL_DOWNHILL) // self.count -= 0.7; // if(self.count < 0.5) // self.think = obj_barrel_gravity; //What is this supposed to do? // obj_barrel_gravity(); // reduce self.count to simulate friction, right? self.count = self.count - 1; if(self.count<=0) { self.think=SUB_Null; self.nextthink = -1; } else thinktime self : 0.05; } */ void obj_barrel_roll (void) { self.v_angle_y=self.angles_y; self.v_angle_x=self.v_angle_z=0; makevectors(self.v_angle); self.velocity-=self.movedir*self.speed; self.movedir=normalize(v_forward); self.speed/=1.01; self.anglespeed=self.speed/7.7; if(self.speed<1&&self.speed>-1) { self.velocity = '0 0 0'; self.think = SUB_Null; self.nextthink = -1; } else { vector dircheck; // float hitcheck; //Rolling sound if(self.flags&FL_ONGROUND) { self.flags(-)FL_ONGROUND; self.last_onground=time; self.level=TRUE; } else if(self.level) { self.level=FALSE; self.last_onground = time - 1; self.speed/=2;//slow down if go off cliff, looks weird otherwise } self.velocity+=self.movedir*self.speed; self.angles_x-=self.anglespeed; self.think=obj_barrel_roll; thinktime self : 0.05; dircheck=normalize(self.velocity); makevectors(self.angles); traceline(self.origin,self.origin+dircheck*39,FALSE,self); if(trace_fraction==1) { traceline(self.origin+v_right*16,self.origin+v_right*16+dircheck*39,FALSE,self); if(trace_fraction==1) traceline(self.origin-v_right*16,self.origin-v_right*16+dircheck*39,FALSE,self); } if(trace_fraction<1) { sound(self,CHAN_AUTO,"fx/thngland.wav",1,ATTN_NORM); // landing thud self.speed*=-0.25; self.last_onground=time - 1; obj_fly_hurt(trace_ent); self.velocity=dircheck*self.speed; } } } /* * obj_barrel_shoot() -- Called when a barrel is shot. */ void obj_barrel_shoot() { sound(self,CHAN_AUTO,"fx/thngland.wav",1,ATTN_NORM); // landing thud // if(self.aflag) // return; /* self.movedir = normalize(self.origin - self.enemy.origin); self.velocity=self.velocity + self.movedir*self.count; self.avelocity_y=random(100,300); self.velocity_z=self.velocity_z + 50; if(self.velocity_z>150) self.velocity_z=150; self.flags(-)FL_ONGROUND; */ } /* * obj_barrel_use() -- Called when a barrel is triggered. */ void obj_barrel_use() { // if(other.movetype == MOVETYPE_STEP) // obj_pull(); // else obj_barrel_explode(); } void spawn_barrel(float barrel_type) { precache_model("models/barrel.mdl"); if(barrel_type==BARREL_NORMAL) precache_model("models/rat.mdl"); CreateEntityNew(self,ENT_BARREL,"models/barrel.mdl",chunk_death); if(self.spawnflags&ON_SIDE) { self.aflag=1; self.frame=1; self.v_angle=self.angles; setsize(self, '-18 -13 -13','18 13 13'); self.hull=HULL_CROUCH; } else setsize(self, '-13 -13 0','13 13 36'); if (self.scale) self.mass *=self.scale; self.classname = "barrel"; self.flags(+)FL_PUSH; self.touch = obj_push; self.th_pain = obj_barrel_shoot; if (barrel_type==BARREL_NORMAL) { self.th_die = barrel_die; self.skin=0; } else if (barrel_type==BARREL_INDESTRUCTIBLE) { self.health = 999999; self.th_die = SUB_Null; self.skin=1; } else if (barrel_type==BARREL_EXPLODING) { self.th_die = obj_barrel_explode; self.skin=2; } if(!self.spawnflags&BARREL_SINK) { self.think=barrel_check_float; thinktime self : 0; } if(pointcontents(self.origin)!=CONTENT_EMPTY&&(!self.spawnflags&BARREL_SINK)) { self.classname="barrel_floating"; self.think=float; thinktime self : 0; } else if(!self.flags2&FL_SUMMONED&&!self.spawnflags&BARREL_NO_DROP) droptofloor(); if(self.targetname) self.use=self.th_die; } /*QUAKED obj_barrel (0.3 0.1 0.6) (-13 -13 0) (13 13 36) DOWNHILL NO_DROP ON_SIDE SINK A barrel, just a plain old barrel -------------------------FIELDS------------------------- .health - How hard it is to smash Default: 25 DOWNHILL - This barrel will slide downhill with gravity. NO_DROP - Will not drop to floor before spawning ON_SIDE - Will make the barrel appear to be on it's side, the top will point right (90 degrees) Note- barrels on their side must be placed at least 13 units above the floor. SINK - Floats in water -------------------------------------------------------- */ void obj_barrel(void) { precache_sound("misc/squeak.wav"); spawn_barrel(BARREL_NORMAL); self.mass = 75; } /*QUAKED obj_barrel_indestructible (0.3 0.1 0.6) (-13 -13 0) (13 13 36) DOWNHILL NO_DROP ON_SIDE SINK A barrel you just can't break -------------------------FIELDS------------------------- DOWNHILL - This barrel will slide downhill with gravity. NO_DROP - Will not drop to floor before spawning ON_SIDE - Will make the barrel appear to be on it's side, the top will point right (90 degrees) Note- barrels on their side must be placed at least 13 units above the floor. SINK - Floats in water -------------------------------------------------------- */ void obj_barrel_indestructible(void) { spawn_barrel(BARREL_INDESTRUCTIBLE); self.mass = 95; } /*QUAKED obj_barrel_exploding (0.3 0.1 0.6) (-13 -13 0) (13 13 36) DOWNHILL NO_DROP ON_SIDE SINK An exploding barrel with red XXX on the side WARNING!: Putting too many exploding barrels next to each other will cause a crash, there is no way around this, so if it happens, it's to be considered a Designer error! Putting them in lines and chains seems to be ok, as long as you don't stack them or group them too closely, more than 4 in a tight group is probably pushing it. -------------------------FIELDS------------------------- .health - How hard it is to blow up Default: 25 DOWNHILL - This barrel will slide downhill with gravity. NO_DROP - Will not drop to floor before spawning ON_SIDE - Will make the barrel appear to be on it's side, the top will point right (90 degrees) Note- barrels on their side must be placed at least 13 units above the floor. SINK - Sinks in water -------------------------------------------------------- */ void obj_barrel_exploding(void) { spawn_barrel(BARREL_EXPLODING); self.mass = 85; } gamecode/hc/h2/boner.hc000066400000000000000000000301731444734033100151550ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\weapons\spllbook\spllbook.hc ============================================================================== */ // For building the model $cd Q:\art\models\weapons\spllbook $origin 0 0 0 $base BASE skin $skin skin $flags 0 // $frame fire1 fire2 fire3 fire4 fire5 $frame fire6 fire7 fire8 fire9 fire10 $frame fire11 fire12 // $frame go2mag01 go2mag02 go2mag03 go2mag04 go2mag05 $frame go2mag06 go2mag07 go2mag08 go2mag09 go2mag10 $frame go2mag11 go2mag12 go2mag13 // $frame go2shd01 go2shd02 $frame go2shd03 go2shd04 go2shd05 go2shd06 go2shd07 $frame go2shd08 go2shd09 go2shd10 go2shd11 go2shd12 $frame go2shd13 go2shd14 // $frame idle1 idle2 idle3 idle4 idle5 $frame idle6 idle7 idle8 idle9 idle10 $frame idle11 idle12 idle13 idle14 idle15 $frame idle16 idle17 idle18 idle19 idle20 $frame idle21 idle22 // $frame mfire1 mfire2 mfire3 mfire4 mfire5 $frame mfire6 mfire7 mfire8 // $frame midle01 midle02 midle03 midle04 midle05 $frame midle06 midle07 midle08 midle09 midle10 $frame midle11 midle12 midle13 midle14 midle15 $frame midle16 midle17 midle18 midle19 midle20 $frame midle21 midle22 // $frame mselect01 mselect02 mselect03 mselect04 mselect05 $frame mselect06 mselect07 mselect08 mselect09 mselect10 $frame mselect11 mselect12 mselect13 mselect14 mselect15 $frame mselect16 mselect17 mselect18 mselect19 mselect20 // $frame select1 select2 select3 select4 select5 $frame select6 select7 /* ============================================================================== MULTI-DAMAGE Collects multiple small damages into a single damage ============================================================================== */ void(vector org)smolder; void(vector org, float damage) Ricochet = { //float r; particle4(org,3,random(368,384),PARTICLETYPE_GRAV,damage/2); /* r = random(100); if (r > 95) sound (targ,CHAN_AUTO,"weapons/ric1.wav",1,ATTN_NORM); else if (r > 91) sound (targ,CHAN_AUTO,"weapons/ric2.wav",1,ATTN_NORM); else if (r > 87) sound (targ,CHAN_AUTO,"weapons/ric3.wav",1,ATTN_NORM); */ }; entity multi_ent; float multi_damage; void() ClearMultDamg = { multi_ent = world; multi_damage = 0; }; void() ApplyMultDamg = { float kicker, inertia; if (!multi_ent) return; entity loser,winner; winner=self; loser=multi_ent; kicker = multi_damage * 7 - vlen(winner.origin - loser.origin); if(kicker>0) { if(loser.flags&FL_ONGROUND) { loser.flags(-)FL_ONGROUND; loser.velocity_z = loser.velocity_z + 150; } if (loser.mass<=10) inertia = 1; else inertia = loser.mass/10; if(loser==self) loser.velocity = loser.velocity - (normalize(loser.v_angle) * (kicker / inertia)); else loser.velocity = loser.velocity + (normalize(winner.v_angle) * (kicker / inertia)); T_Damage (loser, winner, winner, multi_damage); } }; void(entity hit, float damage) AddMultDamg = { if (!hit) return; if (hit != multi_ent) { ApplyMultDamg (); multi_damage = damage; multi_ent = hit; } else multi_damage = multi_damage + damage; }; void(float damage, vector dir) TraceHit = { local vector vel, org; vel = (normalize(dir + v_factorrange('-1 -1 0','1 1 0')) + 2 * trace_plane_normal) * 200; org = trace_endpos - dir*4; if (trace_ent.takedamage) { SpawnPuff (org, vel*0.1, damage*0.25,trace_ent); AddMultDamg (trace_ent, damage); } else Ricochet(org,damage); }; void(float shotcount, vector dir, vector spread) InstantDamage = { vector direction; vector src; makevectors(self.v_angle); src = self.origin + self.proj_ofs+'0 0 6'+v_forward*10; ClearMultDamg (); while (shotcount > 0) { direction = dir + random(-1,1)*spread_x*v_right; direction += random(-1,1)*spread_y*v_up; traceline (src, src + direction*2048, FALSE, self); if (trace_fraction != 1.0) TraceHit (4, direction); shotcount = shotcount - 1; } ApplyMultDamg (); }; void bone_shard_touch () { if(other==self.owner) return; string hitsound; if(other.takedamage) { hitsound="necro/bonenhit.wav"; T_Damage(other, self,self.owner,self.dmg); } else { hitsound="necro/bonenwal.wav"; T_RadiusDamage(self,self.owner,self.dmg*2,self.owner); } //FIXME: add sprite, particles, sound starteffect(CE_WHITE_SMOKE, self.origin,'0 0 0', HX_FRAME_TIME); sound(self,CHAN_WEAPON,hitsound,1,ATTN_NORM); particle4(self.origin,3,random(368,384),PARTICLETYPE_GRAV,self.dmg/2); endeffect(MSG_ALL,self.wrq_effect_id); remove(self); } void bone_removeshrapnel (void) { endeffect(MSG_ALL,self.wrq_effect_id); remove(self); } void fire_bone_shrapnel () { vector shard_vel; newmis=spawn(); newmis.owner=self.owner; newmis.movetype=MOVETYPE_BOUNCE; newmis.solid=SOLID_PHASE; newmis.effects (+) EF_NODRAW; newmis.touch=bone_shard_touch; newmis.dmg=15; newmis.think=bone_removeshrapnel; thinktime newmis : 3; newmis.speed=777; trace_fraction=0; trace_ent=world; while(trace_fraction!=1&&!trace_ent.takedamage) { shard_vel=randomv('1 1 1','-1 -1 -1'); traceline(self.origin,self.origin+shard_vel*36,TRUE,self); } newmis.velocity=shard_vel*newmis.speed; newmis.avelocity=randomv('777 777 777','-777 -777 -777'); setmodel(newmis,"models/boneshrd.mdl"); setsize(newmis,'0 0 0','0 0 0'); setorigin(newmis,self.origin+shard_vel*8); newmis.wrq_effect_id = starteffect(CE_BONESHRAPNEL, newmis.origin, newmis.velocity, newmis.angles,newmis.avelocity); } void bone_shatter () { float shard_count; shard_count=20; while(shard_count) { fire_bone_shrapnel(); shard_count-=1; } } void bone_power_touch () { vector randomvec; sound(self,CHAN_WEAPON,"necro/bonephit.wav",1,ATTN_NORM); if(other.takedamage) { // dprint("Doing damage\n"); T_Damage(other, self,self.owner,self.dmg*2); // dprint("Doing effects\n"); randomvec=randomv('-20 -20 -20','20 20 20'); starteffect(CE_GHOST, self.origin-self.movedir*8+randomvec,'0 0 30'+randomvec, 0.1); randomvec=randomv('-20 -20 -20','20 20 20'); starteffect(CE_GHOST, self.origin-self.movedir*8+randomvec,'0 0 30'+randomvec, 0.1); randomvec=randomv('-20 -20 -20','20 20 20'); starteffect(CE_GHOST, self.origin-self.movedir*8+randomvec,'0 0 30'+randomvec, 0.1); randomvec=randomv('-20 -20 -20','20 20 20'); starteffect(CE_GHOST, self.origin-self.movedir*8+randomvec,'0 0 30'+randomvec, 0.1); } self.flags2(+)FL2_ADJUST_MON_DAM; // dprint("Doing radius damage\n"); T_RadiusDamage(self,self.owner,self.dmg,other); self.solid=SOLID_NOT; // dprint("shattering\n"); bone_shatter(); // dprint("Doing final effect\n"); starteffect(CE_BONE_EXPLOSION, self.origin-self.movedir*6,'0 0 0', HX_FRAME_TIME); particle4(self.origin,50,random(368,384),PARTICLETYPE_GRAV,10); // dprint("removing\n"); remove(self); } /* void power_trail() { if(self.owner.classname!="player") dprint("ERROR: Bone powered owner not player!\n"); if(self.touch==SUB_Null) dprint("ERROR: Bone powered touch is null!\n"); particle4(self.origin,10,random(368,384),PARTICLETYPE_SLOWGRAV,3); thinktime self : 0.05; } */ void bone_smoke_fade () { thinktime self : 0.05; self.abslight-=0.05; self.scale+=0.05; if(self.abslight==0.35) self.skin=1; else if(self.abslight==0.2) self.skin=2; else if(self.abslight<=0.1) remove(self); } void MakeBoneSmoke () { entity smoke; smoke=spawn_temp(); smoke.movetype=MOVETYPE_FLYMISSILE; smoke.velocity=randomv('0 0 20')+v_forward*20; smoke.drawflags(+)MLS_ABSLIGHT|DRF_TRANSLUCENT; smoke.abslight=0.5; smoke.angles=vectoangles(v_forward); smoke.avelocity_x=random(-600,600); smoke.scale=0.1; setmodel(smoke,"models/bonefx.mdl"); setsize(smoke,'0 0 0','0 0 0'); setorigin(smoke,self.origin); smoke.think=bone_smoke_fade; thinktime smoke : 0.05; } void bone_smoke () { self.cnt+=1; MakeBoneSmoke(); if(self.cnt>3) self.nextthink=-1; else thinktime self : 0.01; } void bone_fire(float powered_up, vector ofs) { //SOUND vector org; makevectors(self.v_angle); newmis=spawn(); newmis.owner=self; newmis.movetype=MOVETYPE_FLYMISSILE; newmis.solid=SOLID_BBOX; newmis.speed=1000; newmis.velocity=v_forward*newmis.speed; org=self.origin+self.proj_ofs+v_forward*8+v_right*(ofs_y+12)+v_up*ofs_z; setorigin(newmis,org); if(powered_up) { self.punchangle_x=-2; sound(self,CHAN_WEAPON,"necro/bonefpow.wav",1,ATTN_NORM); self.attack_finished=time + 1.3; newmis.dmg=100;//was 200 newmis.frags=TRUE; // newmis.takedamage=DAMAGE_YES; // newmis.health=3; // newmis.th_die=bone_shatter; newmis.touch=bone_power_touch; newmis.avelocity=randomv('777 777 777','-777 -777 -777'); setmodel(newmis,"models/bonelump.mdl"); setsize(newmis,'0 0 0','0 0 0'); //newmis.think=power_trail; //thinktime newmis : 0; self.greenmana-=20; } else { newmis.speed+=random(500); newmis.dmg=7; newmis.touch=bone_shard_touch; newmis.effects (+) EF_NODRAW; setmodel(newmis,"models/boneshot.mdl"); setsize(newmis,'0 0 0','0 0 0'); newmis.velocity+=v_right*ofs_y*10+v_up*ofs_z*10; newmis.angles=vectoangles(newmis.velocity); // newmis.avelocity_z=random(777,-777); newmis.wrq_effect_id = starteffect(CE_BONESHARD, newmis.origin, newmis.velocity, newmis.angles,newmis.avelocity); //newmis.think=bone_smoke; //thinktime newmis : 0.06; } } void bone_normal() { vector dir; //sound sound(self,CHAN_WEAPON,"necro/bonefnrm.wav",1,ATTN_NORM); self.effects(+)EF_MUZZLEFLASH; makevectors(self.v_angle); dir=normalize(v_forward); InstantDamage(4,dir,'0.1 0.1 0.1'); // InstantDamage(12,dir,'0.1 0.1 0.1'); self.greenmana-=1; self.attack_finished=time+0.3; } void bone_fire_once() { vector ofs; ofs_z=random(-5,5); ofs_x=random(-5,5); ofs_y=random(-5,5); bone_fire(FALSE,ofs); } /*====================== ACTION select deselect ready loop relax loop fire once fire loop ready to relax(after short delay) relax to ready(Fire delay? or automatic if see someone?) =======================*/ void()boneshard_ready; void() Nec_Bon_Attack; void boneshard_fire (void) { self.wfs = advanceweaponframe($fire1,$fire12); if(self.button0&&self.weaponframe>$fire3 &&!self.artifact_active&ART_TOMEOFPOWER) self.weaponframe=$fire3; self.th_weapon=boneshard_fire; self.last_attack=time; if(self.wfs==WF_CYCLE_WRAPPED||self.greenmana<1||(self.greenmana<10&&self.artifact_active&ART_TOMEOFPOWER)) boneshard_ready(); else if(self.weaponframe==$fire3) if(self.artifact_active&ART_TOMEOFPOWER) bone_fire(TRUE,'0 0 0'); else bone_normal(); if(random()<0.8&&!self.artifact_active&ART_TOMEOFPOWER&&self.weaponframe<=$fire6) bone_fire_once(); } void() Nec_Bon_Attack = { boneshard_fire(); thinktime self : 0; }; void boneshard_jellyfingers () { self.wfs = advanceweaponframe($idle1,$idle22); self.th_weapon=boneshard_jellyfingers; if(self.wfs==WF_CYCLE_WRAPPED) boneshard_ready(); } void boneshard_ready (void) { self.weaponframe=$idle1; if(random()<0.1&&random()<0.3&&random()<0.5) self.th_weapon=boneshard_jellyfingers; else self.th_weapon=boneshard_ready; } void boneshard_select (void) { self.wfs = advanceweaponframe($select7,$select1); self.weaponmodel = "models/spllbook.mdl"; self.th_weapon=boneshard_select; if(self.wfs==WF_CYCLE_WRAPPED) { self.attack_finished = time - 1; boneshard_ready(); } } void boneshard_deselect (void) { self.wfs = advanceweaponframe($select1,$select7); self.th_weapon=boneshard_deselect; if(self.wfs==WF_CYCLE_WRAPPED) W_SetCurrentAmmo(); } void boneshard_select_from_mmis (void) { self.wfs = advanceweaponframe($go2shd01,$go2shd14); self.weaponmodel = "models/spllbook.mdl"; self.th_weapon=boneshard_select_from_mmis; if(self.wfs==WF_CYCLE_WRAPPED) { self.attack_finished = time - 1; boneshard_ready(); } } gamecode/hc/h2/breakabl.hc000066400000000000000000000170061444734033100156130ustar00rootroot00000000000000/* ============================================================================== BREAKABLE BRUSHES ============================================================================== */ float BREAK_KILLALL = 1; float BREAK_HIERARCH = 2; float BREAK_NOLINK = 4; float BREAK_CHECKNAME = 8; float BREAK_ORDERED = 16; float BREAK_TRANSLUCENT = 32; float BREAK_INVINCIBLE = 64; float BREAK_INVISIBLE = 128; float (entity e1, entity e2) EntitiesTouching; float breakhealth[21] = { 0, 75, // THINGTYPE_GREYSTONE 50, // THINGTYPE_WOOD 100, // THINGTYPE_METAL 30, // THINGTYPE_FLESH 999, // THINGTYPE_FIRE 25, // THINGTYPE_CLAY 35, // THINGTYPE_LEAVES 35, // THINGTYPE_HAY 75, // THINGTYPE_BROWNSTONE 35, // THINGTYPE_CLOTH 35, // THINGTYPE_WOOD_LEAF 75, // THINGTYPE_WOOD_METAL 65, // THINGTYPE_WOOD_STONE 90, // THINGTYPE_METAL_STONE 60, // THINGTYPE_METAL_CLOTH 10, // THINGTYPE_WEB 10, // THINGTYPE_GLASS 50, // THINGTYPE_ICE 10, // THINGTYPE_CLEARCLASS 10 // THINGTYPE_CLEARCLASS }; //============================================================================ void linkBreakables() { local entity t, starte; local vector cmins, cmaxs; if (self.enemy) return; // already linked by another breakable if (self.spawnflags & BREAK_NOLINK) { self.owner = self.enemy = self; return; // don't want to link this door } cmins = self.mins; cmaxs = self.maxs; starte = self; t = self; loop /*do*/ { self.owner = starte; // master breakable if (self.health) starte.health = self.health; if (self.targetname) starte.targetname = self.targetname; t = find (t, classname, self.classname); if (!t) { self.enemy = starte; // make the chain a loop self = self.owner; if (self.health) return; if (self.targetname) return; if (self.items) return; return; } if (EntitiesTouching(self,t) && (((self.spawnflags & BREAK_CHECKNAME) && (self.netname == t.netname)) || (!self.spawnflags & BREAK_CHECKNAME))) { if (t.enemy) { //dprint("cross connected brushes!!\n"); return; } self.enemy = t; self = t; if (t.mins_x < cmins_x) cmins_x = t.mins_x; if (t.mins_y < cmins_y) cmins_y = t.mins_y; if (t.mins_z < cmins_z) cmins_z = t.mins_z; if (t.maxs_x > cmaxs_x) cmaxs_x = t.maxs_x; if (t.maxs_y > cmaxs_y) cmaxs_y = t.maxs_y; if (t.maxs_z > cmaxs_z) cmaxs_z = t.maxs_z; } } /*while (1);*/ } //============================================================================ void brush_use_hierarchy() { local entity starte, oself, other; local float headNum; oself = starte = self; headNum = self.frags; do { if (self.frags >= headNum) self.th_die(); self = self.enemy; } while ( (self != starte) && (self != world) && (self.frags != self.cnt)); } void brush_use_ordered() { local entity starte, oself; starte = oself = self; self.health = self.max_health; if (self.frags == self.cnt) self.th_die(); else do { self = self.enemy; } while ( (self != oself) && (self != world) && (self.frags != self.cnt)); if (self.frags == self.cnt && self != world && self != oself) self.th_die(); do { oself.cnt += 1; oself = oself.enemy; } while ( (oself != starte) && (oself != world) ); } void brush_use() { local entity starte, other; starte = self; activator = other; if (starte.spawnflags & BREAK_KILLALL) { do { other = self.enemy; self.th_die(); self = other; } while ( (self != starte) && (self != world) ); } else { // dprint("Chunkalicious baby!\n"); self.th_die(); } } void brush_no_link_use (void) { //entity found, starte; SUB_UseTargets(); /* if(self.target) { found=find(found,targetname,self.target); if(found!=world) { starte=found; found.think=found.use; thinktime found : 0; found=find(found,targetname,self.target); while(found!=starte&&found!=world) { found.think=found.use; thinktime found : 0; found=find(found,targetname,self.target); } } } */ self.th_die(); } /*QUAKED breakable_brush (0 0 1) ? KILLALL HIERARCH NOLINK CHECKNAME ORDERED TRANSLUCENT INVINCIBLE INVISIBLE Breakable window or wall You can manually control the heirarchy of breaking by targeting all the brushes you want this brush to break. If you target a light with this object and turn on the "breaklight" spawnflag, it will turn off that light when it's broken. The light will default to 300 if no lightvalue1 is given. AUTOMATIC LINKING OPTIONS: killall - when killed, the brush will kill all connected brushes hierarch - link all brushes in a hierarchy. The hierarchy priority is set in the frags field of each brush. Lower numbers will kill higher numbers. If brushes share the same priority, they will die at the same time. nolink - don't automatically link this brush with other brushes (use only manual targeting to link) checkname - link brushes, but also check the name you place in the netname field. Brushes must then not only touch, but also have the same netname to link ordered - like hierarch, except that no matter which brush you kill, the brushes will always break in a certain order. The order is set in the frags field. The brush with a frags set to 1 will break first, brush with frags set to 2 will break second, etc. OTHER FIELDS: translucent - you can see through it invincible - can't be shot and broken, but will break by linking -------------------------FIELDS------------------------- flag - order number thingtype - type of chunks and sprites it will generate 0 - glass (default) 1 - grey stone 2 - wood 3 - metal 4 - flesh 5 - fire 6 - clay 7 - leaves 8 - hay 9 - brown stone 10 - cloth 11 - wood & leaf 12 - wood & metal 13 - wood stone 14 - metal stone 15 - metal cloth 16 - spider web 19 - clear glass 20 - red glass health - amount of damage item can take. Default is based on thingtype glass - 25 grey stone - 75 wood - 50 metal - 100 flesh - 30 fire - 999 clay - 25 leaves - 35 hay - 35 brown stone - 75 cloth - 35 wood&leaf - 35 wood&metal - 75 wood&stone - 65 metal&stone - 90 metal&cloth - 60 others - 25 abslight - to set the absolute light level -------------------------------------------------------- */ void breakable_brush() { self.max_health = self.health; self.solid = SOLID_BSP; self.movetype = MOVETYPE_PUSH; setorigin (self, self.origin); setmodel (self, self.model); if (self.spawnflags & BREAK_ORDERED) { self.th_die = brush_use_ordered; self.cnt = 1; } else if (self.spawnflags & BREAK_HIERARCH) self.th_die = brush_use_hierarchy; else self.th_die = brush_use; if (self.spawnflags & BREAK_TRANSLUCENT) self.drawflags(+)DRF_TRANSLUCENT; if(!self.thingtype) self.thingtype=THINGTYPE_GLASS; if (!self.health) self.health=breakhealth[self.thingtype]; self.max_health = self.health; if (self.flags) self.frags = self.flags; if (self.abslight) self.drawflags(+)MLS_ABSLIGHT; if (self.spawnflags&BREAK_INVINCIBLE) self.takedamage = DAMAGE_NO; else self.takedamage = DAMAGE_YES; // self.takedamage = DAMAGE_NO_GRENADE; if (self.spawnflags&BREAK_INVISIBLE) { self.effects (+) EF_NODRAW; self.th_die = SUB_Remove; } else self.th_die = chunk_death; if(self.spawnflags&BREAK_NOLINK) self.use=self.th_die;//brush_no_link_use; else { self.use = brush_use; self.think=linkBreakables; self.nextthink=self.ltime+0.1; } self.ltime = time; } gamecode/hc/h2/builtin.hc000066400000000000000000000131261444734033100155150ustar00rootroot00000000000000//************************************************************************** //** builtin.hc //************************************************************************** // Sets v_forward, etc. void makevectors(vector ang) : 1; void setorigin(entity e, vector o) : 2; // Set movetype and solid before calling. void setmodel(entity e, string m) : 3; void setsize(entity e, vector min, vector max) : 4; void lightstylestatic(float style, float value) : 5; void debugbreak(void) : 6; // Returns 0 - 1. float random(void) : 7; void sound(entity e, float chan, string samp, float vol, float atten) : 8; vector normalize(vector v) : 9; void error(string e) : 10; void objerror(string e) : 11; float vlen(vector v) : 12; float vectoyaw(vector v) : 13; entity spawn(void) : 14; void remove(entity e) : 15; // Sets trace_* globals. void traceline(vector v1, vector v2, float nomonsters, entity forent) : 16; // Returns a client to look for. entity checkclient(void) : 17; entity find(entity start, .string fld, string match) : 18; // For sounds in demo and retail version. string precache_sound(string s) : 19; // For models in demo and retail version. string precache_model(string s) : 20; void stuffcmd(entity client, string s) : 21; entity findradius(vector org, float rad) : 22; void bprint(string s) : 23; void sprint(entity client, string s) : 24; void dprint(string s) : 25; string ftos(float f) : 26; string vtos(vector v) : 27; // Prints all edicts. void coredump(void) : 28; void traceon(void) : 29; void traceoff(void) : 30; // Prints an entire edict. void eprint(entity e) : 31; // Returns TRUE or FALSE. float walkmove (float yaw, float dist, float set_trace) : 32; float tracearea(vector v1, vector v2, vector mins, vector maxs, float nomonsters, entity forent) : 33; // TRUE if landed on floor. float droptofloor(void) : 34; void lightstyle(float style, string value) : 35; // Round to nearest int. float rint(float v) : 36; // Largest integer <= v. float floor(float v) : 37; // Smallest integer >= v. float ceil(float v) : 38; // Award experience to player. //void AwardExperience(entity ToEnt, entity FromEnt, float Amount) : 39; // TRUE if self is on ground. float checkbottom(entity e) : 40; // Returns a CONTENT_*. float pointcontents(vector v) : 41; // Start a particle effect. void particle2(vector o, vector dmin, vector dmax, float color, float type, float count) : 42; float fabs(float f) : 43; // Returns a shooting vector. vector aim(entity e,vector d,float speed) : 44; // Returns the cvar value. float cvar(string s) : 45; // Put a string into local que. void localcmd(string s) : 46; // For looping through all ents. entity nextent(entity e) : 47; // Start a particle effect. void particle(vector o, vector d, float color, float count) : 48; // Turn towards self.ideal_yaw at self.yaw_speed. float ChangeYaw(void) : 49; // Calculate distance, ignoring z. float vhlen(vector v) : 50; vector vectoangles(vector v) : 51; void WriteByte(float to, float f) : 52; void WriteChar(float to, float f) : 53; void WriteShort(float to, float f) : 54; void WriteLong(float to, float f) : 55; void WriteCoord(float to, float f) : 56; void WriteAngle(float to, float f) : 57; void WriteString(float to, string s) : 58; void WriteEntity(float to, entity s) : 59; void dprintf(string s, float num) : 60; float cos(float angle) : 61; float sin(float angle) : 62; float AdvanceFrame(float start, float end) : 63; void dprintv(string s, vector vec) : 64; float RewindFrame(float start, float end) : 65; void setclass(entity e, float value) : 66; void movetogoal(float step) : 67; // For files in demo and retail version. string precache_file(string s) : 68; void makestatic(entity e) : 69; void changelevel(string level, string spot) : 70; // Returns the current value of a light style. float lightstylevalue(float style) : 71; // Sets a cvar value. void cvar_set(string var, string val) : 72; // Same as sprint(), but centered. void centerprint(entity client, string s) : 73; void ambientsound(vector pos, string samp, float vol, float atten) : 74; // For models only in retail version. string precache_model2(string s) : 75; // For sounds only in retail version. string precache_sound2(string s) : 76; // For files only in retail version. string precache_file2(string s) : 77; // Sets parm1... to the values at level start for coop respawn. void setspawnparms(entity e) : 78; void plaque_draw(float to, float index) : 79; // Create rain. void rain_go(vector v1, vector v2, vector e_size, vector dir, float color, float count) : 80; // Particle explosion. void particleexplosion(vector v,float f,float c,float s) : 81; // Move a step. float movestep(float x, float y, float z, float set_trace) : 82; // Returns TRUE or FALSE (move other). float advanceweaponframe(float startframe, float endframe) : 83; float sqrt(float num1) : 84; void particle3(vector o, vector box, float color, float type, float count) : 85; void particle4(vector o, float radius, float color, float type, float count) : 86; // m will used as: models/puzzle/m.mdl void setpuzzlemodel(entity e, string m) : 87; //float starteffect(float to, float effect) : 88; float starteffect(...) : 88; float endeffect(float to, float effect_id) : 89; string precache_puzzle_model(string s) : 90; vector concatv(vector in, vector limit) : 91; string getstring(float id) : 92; entity spawn_temp(void) : 93; vector v_factor(vector factor) : 94; // returns (v_right * factor_x) + (v_forward * factor_y) + (v_up * factor_z) vector v_factorrange(vector start, vector end) : 95; string precache_sound3(string s) : 96; string precache_model3(string s) : 97; string precache_file3(string s) : 98; gamecode/hc/h2/buttons.hc000066400000000000000000000232401444734033100155430ustar00rootroot00000000000000// button and multiple button float SPAWNFLAG_BUTTON_ACTIVATE = 1; float FIRE_MULTIPLE = 4; void() button_wait; void() button_return; void() pressure_use; void() pressure_touch; void() button_wait = { self.state = STATE_TOP; if(self.wait==-1) if(!self.inactive) self.nextthink=-1; else self.nextthink=self.ltime+0.3; else self.nextthink = self.ltime + self.wait; self.think = button_return; activator = self.enemy; if (!self.inactive) SUB_UseTargets(); self.frame = 1; // use alternate textures }; void() button_done = { self.state = STATE_BOTTOM; }; void() button_return = { self.state = STATE_DOWN; SUB_CalcMove (self.pos1, self.speed, button_done); self.frame = 0; // use normal textures if (self.health) self.takedamage = DAMAGE_NO_GRENADE; // can be shot again }; void() button_blocked = { // do nothing, just don't come all the way back out }; void() button_fire = { string s; if (self.inactive) { if (other.classname == "player" && self.msg2) { s = getstring(self.msg2); centerprint(other, s); } return; } if (self.state == STATE_UP) return; self.check_ok = TRUE; sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); self.state = STATE_UP; SUB_CalcMove (self.pos2, self.speed, button_wait); }; void() button_use = { self.enemy = activator; button_fire (); }; void() button_touch = { if ((!other.flags&FL_PUSH)&&other.classname!="player") return; // if(self.inactive) // return; if (self.state == STATE_TOP) return; self.enemy = other; button_fire (); }; void() button_killed = { self.enemy = damage_attacker; self.health = self.max_health; self.takedamage = DAMAGE_NO; // wil be reset upon return button_fire (); }; /*QUAKED func_button (0 .5 .8) ? deactivated FIREONLY FIRE_MULTIPLE x x x When a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again, unless it's a pressure plate, in which case it will not return to it's position until it's not being touched anymore. FIREONLY - has to be killed, touching won't do it. FIRE_MULTIPLE - can be shot over and over (give it a high health) -----------------------FIELDS------------------------- "angle" determines the opening direction "target" all entities with a matching targetname will be used "speed" override the default 40 speed "wait" override the default 1 second wait (-1 = never return) "lip" override the default 4 pixel lip remaining at end of move "health" if set, the button can be killed and touched "abslight" - to set the absolute light level "soundtype" 0) steam metal 1) wooden clunk 2) metallic click 3) in-out deactivated - button must be activated before it will work -------------------------------------------------------- */ void() func_button = { if (self.soundtype == 0) { precache_sound ("buttons/button1.wav"); self.noise = "buttons/button1.wav"; } if (self.soundtype == 1) { precache_sound ("buttons/button2.wav"); self.noise = "buttons/button2.wav"; } if (self.soundtype == 2) { precache_sound ("buttons/button3.wav"); self.noise = "buttons/button3.wav"; } if (self.soundtype == 3) { precache_sound ("buttons/button4.wav"); self.noise = "buttons/button4.wav"; } SetMovedir (); if (self.abslight) self.drawflags(+)MLS_ABSLIGHT; self.classname="button"; self.movetype = MOVETYPE_PUSH; self.solid = SOLID_BSP; setmodel (self, self.model); self.blocked = button_blocked; self.use = button_use; if (self.health) { self.max_health = self.health; if(self.spawnflags&FIRE_MULTIPLE) self.th_pain = button_use;//for multiple uses self.th_die = button_killed; self.takedamage = DAMAGE_NO_GRENADE; } if (!self.spawnflags & 2) { if (!self.health) self.health = 10; self.touch = button_touch; } if (!self.speed) self.speed = 40; if (!self.wait) self.wait = 1; if (!self.lip) self.lip = 4; //If activatable, set usable flags off if(self.spawnflags&SPAWNFLAG_BUTTON_ACTIVATE) self.inactive=TRUE; else self.inactive=FALSE; self.state = STATE_BOTTOM; self.pos1 = self.origin; self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip); self.ltime = time; }; /* ------------------------------------------------------------------------- */ float pressure_weight_check () { vector org; float len, totalmass; entity head; org = (self.absmax + self.absmin)*0.5; len = vlen(self.absmax - self.absmin)*0.66; head = findradius(org, len); while (head) { if(head!=self) { if(head.flags2&FL_ALIVE) totalmass += head.mass*10; else totalmass += head.mass; /* if(head.netname) dprint(head.netname); else dprint(head.classname); dprint(" weight added\n"); */ } head = head.chain; } /* dprint("Measured mass: "); dprint(ftos(totalmass)); dprint("\n"); dprint("Required mass: "); dprint(ftos(self.mass)); dprint("\n"); */ if (totalmass >= self.mass) return TRUE; else return FALSE; } float pressure_bounds_check () { vector org1,org2,org3,org4, center,found_bottom; float radius; entity found; org1_z=org2_z=org3_z=org4_z=self.absmax_z+3; org1_x = self.absmin_x; org1_y = self.absmin_y; org2_x = self.absmin_x; org2_y = self.absmax_y; org3_x = self.absmax_x; org3_y = self.absmin_y; org4_x = self.absmax_x; org4_y = self.absmax_y; center=(self.absmax+self.absmin)*0.5; center_z=self.absmax_z; radius=fabs(self.absmax_x-center_x); found=findradius(center,radius); while(found) { found_bottom=(found.absmin+found.absmax)*0.5; found_bottom_z=found.absmin_z; if(found!=self) if(found_bottom_x>self.absmin_x&&found_bottom_xself.absmin_y&&found_bottom_y=self.absmax_z - 3&&found_bottom_z<=self.absmax_z+7) return TRUE; else dprint("Not right height\n"); else dprint("Not right y\n"); else dprint("Not right x\n"); found=found.chain; } return FALSE; } void() pressure_wait = { float tripped; tripped=TRUE; if(!pressure_bounds_check()) tripped=FALSE; if (!pressure_weight_check()) tripped=FALSE; if(tripped) { self.check_ok = TRUE; self.nextthink = self.ltime + 0.05; } else { self.check_ok = FALSE; pressure_use(); self.touch = pressure_touch; SUB_CalcMove(self.pos1, self.speed, SUB_Null); } }; void() pressure_fire = { sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); self.state = STATE_UP; self.touch = SUB_Null; SUB_UseTargets(); SUB_CalcMove (self.pos2, self.speed, pressure_wait); }; void() pressure_use = { self.enemy = activator; pressure_fire (); }; void() pressure_touch = { if(other==world) return; entity found; float inbounds,enough_weight; if(pressure_bounds_check()) inbounds=TRUE; if (pressure_weight_check()) enough_weight=TRUE; if(inbounds) { if(enough_weight) { self.check_ok = TRUE; self.touch = SUB_Null; pressure_use(); } else { if(self.pain_finishedself.level) self.cnt-=1; makevectors(self.owner.v_angle); viewdir=normalize(v_forward); spot1=self.owner.origin+self.owner.proj_ofs+'0 0 6'; spot2=spot1-viewdir*self.cnt; traceline(spot1,spot2,TRUE,self.owner); viewdir=normalize(spot1-trace_endpos); setorigin(self,trace_endpos+viewdir*4); self.think=CameraThink; thinktime self : 0; } void MakeCamera () { entity oself,nself; if(!self.flags&FL_CLIENT) { if(coop||deathmatch) return; nself=find(world,classname,"player"); if(!nself.flags&FL_CLIENT) return; oself=self; self=nself; } if(self.viewentity.classname=="chasecam") { //Turn off camera view CameraViewPort(self,self); CameraViewAngles(self,self); remove(self.viewentity); self.viewentity=self; self.view_ofs=self.proj_ofs+'0 0 6'; self.attack_finished=0; self.weaponmodel=self.lastweapon; self.oldweapon=FALSE; W_SetCurrentWeapon(); } else { self.lastweapon=self.weaponmodel; self.oldweapon=0; self.weaponmodel=""; makevectors(self.v_angle); self.viewentity=spawn(); self.viewentity.owner=self; self.viewentity.angles=self.angles; self.viewentity.level=cvar("chase_back"); if(!self.viewentity.level) self.viewentity.level=68; self.viewentity.cnt=4; self.viewentity.classname="chasecam"; self.view_ofs='0 0 0'; setmodel(self.viewentity,"models/null.spr"); setsize(self.viewentity, '0 0 0','0 0 0'); setorigin(self.viewentity,self.origin+self.proj_ofs+'0 0 6'-v_forward*4); CameraViewPort(self,self.viewentity); CameraViewAngles(self,self.viewentity); self.viewentity.think=CameraThink; thinktime self.viewentity : 0; } if(oself) self=oself; } gamecode/hc/h2/cat2.hc000066400000000000000000000145611444734033100147040ustar00rootroot00000000000000/*QUAKED obj_catapult2 (0 .5 .8) (-150 -150 0) (150 150 28) ? "speed" Throw speed (300 default) "wait" wait before resetting (3 default) "health" Just how tough is it (defaults to 1000) "mass" How hard is it to push (defaults to 1000) "thingtype" Defaults to THINGTYPE_WOOD "sounds" 0) no sound 1) stone 2) base 3) stone chain 4) screechy metal */ void catapult_ready (void) { //FIXME: No linking in touch, do it all in here with a tracearea (is that working?) and findradius if(self.flags&FL_ONGROUND) self.movetype = MOVETYPE_NONE; if(self.origin!=self.oldorigin||self.angles!=self.o_angle) { entity found; vector dir,org; // dprint("updating dependancies\n"); makevectors(self.angles); dir=normalize(v_forward); org=self.origin+dir*-4*self.level; org_z=self.absmax_z; found=nextent(world); while(found) { if(vhlen(found.origin-org)<2*self.level&&found.catapulter==self) { // dprint(found.classname); // dprint(" updated\n"); setorigin(found,org+found.pos_ofs); found.angles=self.angles+found.angle_ofs; found.velocity='0 0 0'; found.flags(+)FL_ONGROUND; } found=nextent(found); } } self.o_angle=self.angles; self.oldorigin=self.origin; self.think=catapult_ready; thinktime self : 0; } void() catapult_reset = { if(self.frame==22) { sound(self,CHAN_VOICE,"misc/catdrop.wav",1,ATTN_NORM); self.frame=0; } if(self.frame>=20) { sound(self,CHAN_VOICE,"misc/catreset.wav",1,ATTN_NORM); self.frame=20; self.think=catapult_ready; thinktime self : 0; } else { self.frame+=1; self.think=catapult_reset; thinktime self : 0.05; } }; void catapult_wait (void) { self.think=catapult_reset; thinktime self : self.wait; } void catapult_fire (void) { if(self.frame==20) { sound(self,CHAN_VOICE,"misc/catlnch.wav",1,ATTN_NORM); entity found; vector dir,org,addvel; float distance, force,centrifugal; found=nextent(world); makevectors(self.angles); dir=normalize(v_forward); org=self.origin+dir*-4*self.level; org_z=self.absmax_z; while(found) { distance=vhlen(found.origin-org); // if(found.catapulter==self) if(distance<2*self.level&&found.origin_z>self.origin_z+self.maxs_z*0.75)//&&found.catapulter==self) { // inertia? found.catapult_time=time+3; found.catapulter=world; centrifugal=vhlen(found.origin-self.origin); force=self.speed + random(-100,100) + centrifugal*4;//Ignore mass, Not exact physics, but feels better addvel=dir*force+v_right*random(-50,50);//Give some left-right innacuracy to it force=self.speed + random(-100,100)+ centrifugal*4; addvel_z=force; found.velocity+=addvel; if(!found.touch) found.touch=obj_push; found.flags(-)FL_ONGROUND; if(!found.flags2&FL_ALIVE) { found.avelocity=found.velocity*random(-1,1); found.movetype=MOVETYPE_BOUNCE; } if(found.model=="models/sheep.mdl") { found.avelocity=found.velocity*random(-1,1); found.movetype=MOVETYPE_BOUNCE; sound(found,CHAN_VOICE,"misc/sheepfly.wav",1,ATTN_NORM); found.pain_finished=time+1; } } found=nextent(found); } } if(self.frame>=22) { self.frame=22; self.think=catapult_wait; thinktime self : 0; } else { self.frame+=1; self.think=catapult_fire; thinktime self : 0.05; } } void catapult_pain (void) { if(!self.enemy.flags2&FL_ALIVE) return; if(self.frame==20) catapult_fire(); } void catapult2_touch(void) { if(other.solid==SOLID_BSP||other==world) return; // dprint(ftos(other.absmin_z)); // dprint(" > "); // dprint(ftos(self.absmax_z)); // dprint("?\n"); if(other.origin_z-(other.mins_z*0.75)>=self.origin_z+(self.maxs_z*0.75)) { if(other.solid!=SOLID_TRIGGER&&other.movetype&&other.catapulter!=self&&other.catapult_time100&&other.flags&FL_ONGROUND) { // Push or spin vector dir1, dir2; float magnitude,dot_forward,inertia;//dot_right, self.angles_x=self.angles_z=0; makevectors(self.angles); magnitude=vlen(other.velocity); inertia=1/(self.mass/10); dir1=normalize(self.origin-other.origin); dir2=normalize(v_forward); dir1_z=dir2_z=0; dot_forward= dir1*dir2; if(dot_forward >0.8) { // dprint("Move forward\n"); self.velocity+=dir2*magnitude*inertia; // dprint(vtos(self.velocity)); self.flags(-)FL_ONGROUND; } else if(dot_forward<-0.8) { // dprint("Move backwards\n"); self.velocity+=dir2*magnitude*-1*inertia; self.flags(-)FL_ONGROUND; } else { dir1=normalize(other.velocity); dir2=normalize(v_right); dot_right= dir1*dir2; if(dot_right >0.2) { if(dot_forward >0.2) { self.angles_y-=1*magnitude/100; } else if(dot_forward<-0.2) { self.angles_y+=1*magnitude/100; } } else if(dot_right<-0.2) { if(dot_forward >0.2) { self.angles_y+=1*magnitude/100; } else if(dot_forward<-0.2) { self.angles_y-=1*magnitude/100; } } } } */ } void obj_catapult2 (void) { precache_model("models/cattest.mdl"); precache_sound ("misc/catlnch.wav"); precache_sound ("misc/catreset.wav"); precache_sound ("misc/catdrop.wav"); self.solid = SOLID_BBOX; self.movetype = MOVETYPE_PUSHPULL; self.touch=catapult2_touch; // setmodel (self, "models/catapult.mdl"); setmodel (self, "models/cattest.mdl"); setsize(self,'-145 -145 0','145 145 26'); self.hull=HULL_SCORPION; setorigin (self, self.origin); self.classname="catapult"; self.level=30; self.frame=20; self.th_pain=catapult_pain; self.th_weapon=catapult_fire; // if (!self.speed) self.speed = 300;//Too strong? if (self.wait==0) self.wait = 3; self.th_die = chunk_death; self.takedamage = DAMAGE_YES; self.use=catapult_fire; if(!self.thingtype) self.thingtype = THINGTYPE_WOOD; if(!self.mass) self.mass = 1000; if (!self.health) self.health=1000; self.max_health = self.health; self.think=catapult_ready; thinktime self : 0; } gamecode/hc/h2/chunk.hc000066400000000000000000000376351444734033100151720ustar00rootroot00000000000000void ThrowSolidHead (float dm); void blood_splatter() { SpawnPuff(self.origin,normalize(self.velocity)*-20,10,self); remove(self); } void ThrowBlood (vector org,vector dir) { entity blood; blood=spawn_temp(); blood.solid=SOLID_BBOX; blood.movetype=MOVETYPE_TOSS; blood.touch=blood_splatter; blood.velocity=dir; blood.avelocity=randomv('-700 -700 -700','700 700 700'); blood.thingtype=THINGTYPE_FLESH; setmodel(blood,"models/bldspot4.spr"); // 8 x 8 sprite size setsize(blood,'0 0 0','0 0 0'); setorigin(blood,org); } void ZeBrains (vector spot, vector normal, float scaling, float face, float roll) { newmis=spawn(); newmis.scale=scaling; newmis.angles=vectoangles(normal); if(face) newmis.angles_y+=180; newmis.angles_z=roll; setmodel(newmis,"models/brains.mdl"); setsize(newmis,'0 0 0','0 0 0'); setorigin(newmis,spot+normal*1); newmis.think=corpseblink; thinktime newmis : 30; spot=newmis.origin; makevectors(normal); ThrowBlood(spot,(normal+random(0.75,0.75)*v_up+random(0.75,0.75)*v_right)*random(200,400)); ThrowBlood(spot,(normal+random(0.75,0.75)*v_up+random(0.75,0.75)*v_right)*random(200,400)); ThrowBlood(spot,(normal+random(0.75,0.75)*v_up+random(0.75,0.75)*v_right)*random(200,400)); ThrowBlood(spot,(normal+random(0.75,0.75)*v_up+random(0.75,0.75)*v_right)*random(200,400)); ThrowBlood(spot,(normal+random(0.75,0.75)*v_up+random(0.75,0.75)*v_right)*random(200,400)); } void ChunkRemove (void) { chunk_cnt-=1; SUB_Remove (); } vector ChunkVelocity (void) { local vector v; v_x = 300 * crandom(); v_y = 300 * crandom(); v_z = random(100,400); v = v * 0.7; return v; } void ThrowSingleChunk (string chunkname,vector location,float life_time,float skinnum) { entity chunk; if (chunk_cnt < CHUNK_MAX) { chunk=spawn_temp(); setmodel (chunk, chunkname); chunk.frame = 0; setsize (chunk, '0 0 0', '0 0 0'); chunk.movetype = MOVETYPE_BOUNCE; chunk.solid = SOLID_NOT; chunk.takedamage = DAMAGE_NO; chunk.velocity = ChunkVelocity(); chunk.think = ChunkRemove; chunk.flags(-)FL_ONGROUND; chunk.origin = location; chunk.avelocity_x = random(10); chunk.avelocity_y = random(10); chunk.avelocity_z = random(30); chunk.skin = skinnum; chunk.ltime = time; thinktime chunk : life_time; chunk_cnt+=1; } } void MeatChunks (vector org,vector dir,float chunk_count,entity loser) { float final; entity chunk; while(chunk_count) { chunk=spawn_temp(); chunk_count-=1; final = random(); if(loser.model=="models/spider.mdl") { if (final < 0.33) setmodel (chunk, "models/sflesh1.mdl"); else if (final < 0.66) setmodel (chunk, "models/sflesh2.mdl"); else setmodel (chunk, "models/sflesh3.mdl"); } else if (final < 0.33) setmodel (chunk, "models/flesh1.mdl"); else if (final < 0.66) setmodel (chunk, "models/flesh2.mdl"); else setmodel (chunk, "models/flesh3.mdl"); setsize (chunk, '0 0 0', '0 0 0'); // chunk.skin=1; chunk.movetype = MOVETYPE_BOUNCE; chunk.solid = SOLID_NOT; if(dir=='0 0 0') chunk.velocity = ChunkVelocity(); else chunk.velocity=dir;//+randomv('-200 -200 -200','200 200 200'); chunk.think = ChunkRemove; chunk.avelocity_x = random(1200); chunk.avelocity_y = random(1200); chunk.avelocity_z = random(1200); chunk.scale = .45; chunk.ltime = time; thinktime chunk : random(2); setorigin (chunk, org); } } void CreateModelChunks (vector space,float scalemod) { entity chunk; float final; chunk = spawn_temp(); space_x = space_x * random(); space_y = space_y * random(); space_z = space_z * random(); setorigin (chunk, self.absmin + space); final = random(); if ((self.thingtype==THINGTYPE_GLASS) || (self.thingtype==THINGTYPE_REDGLASS) || (self.thingtype==THINGTYPE_CLEARGLASS) || (self.thingtype==THINGTYPE_WEBS)) { if (final<0.20) setmodel (chunk, "models/shard1.mdl"); else if (final<0.40) setmodel (chunk, "models/shard2.mdl"); else if (final<0.60) setmodel (chunk, "models/shard3.mdl"); else if (final<0.80) setmodel (chunk, "models/shard4.mdl"); else setmodel (chunk, "models/shard5.mdl"); if (self.thingtype==THINGTYPE_CLEARGLASS) { chunk.skin=1; chunk.drawflags (+) DRF_TRANSLUCENT; } else if (self.thingtype==THINGTYPE_REDGLASS) chunk.skin=2; else if (self.thingtype==THINGTYPE_WEBS) { chunk.skin=3; // chunk.drawflags (+) DRF_TRANSLUCENT; } } else if (self.thingtype==THINGTYPE_WOOD) { if (final < 0.25) setmodel (chunk, "models/splnter1.mdl"); else if (final < 0.50) setmodel (chunk, "models/splnter2.mdl"); else if (final < 0.75) setmodel (chunk, "models/splnter3.mdl"); else setmodel (chunk, "models/splnter4.mdl"); } else if (self.thingtype==THINGTYPE_METAL) { if (final < 0.25) setmodel (chunk, "models/metlchk1.mdl"); else if (final < 0.50) setmodel (chunk, "models/metlchk2.mdl"); else if (final < 0.75) setmodel (chunk, "models/metlchk3.mdl"); else setmodel (chunk, "models/metlchk4.mdl"); } else if (self.thingtype==THINGTYPE_FLESH) { if(self.model=="models/spider.mdl") { if (final < 0.33) setmodel (chunk, "models/sflesh1.mdl"); else if (final < 0.66) setmodel (chunk, "models/sflesh2.mdl"); else setmodel (chunk, "models/sflesh3.mdl"); } else if (final < 0.33) setmodel (chunk, "models/flesh1.mdl"); else if (final < 0.66) setmodel (chunk, "models/flesh2.mdl"); else setmodel (chunk, "models/flesh3.mdl"); if(self.classname=="hive") chunk.skin=1; } else if (self.thingtype==THINGTYPE_BROWNSTONE) { if (final < 0.25) setmodel (chunk, "models/schunk1.mdl"); else if (final < 0.50) setmodel (chunk, "models/schunk2.mdl"); else if (final < 0.75) setmodel (chunk, "models/schunk3.mdl"); else setmodel (chunk, "models/schunk4.mdl"); chunk.skin = 1; } else if (self.thingtype==THINGTYPE_CLAY) { if (final < 0.25) setmodel (chunk, "models/clshard1.mdl"); else if (final < 0.50) setmodel (chunk, "models/clshard2.mdl"); else if (final < 0.75) setmodel (chunk, "models/clshard3.mdl"); else setmodel (chunk, "models/clshard4.mdl"); } else if (self.thingtype==THINGTYPE_LEAVES) { if (final < 0.33) setmodel (chunk, "models/leafchk1.mdl"); else if (final < 0.66) setmodel (chunk, "models/leafchk2.mdl"); else setmodel (chunk, "models/leafchk3.mdl"); } else if (self.thingtype==THINGTYPE_HAY) { if (final < 0.33) setmodel (chunk, "models/hay1.mdl"); else if (final < 0.66) setmodel (chunk, "models/hay2.mdl"); else setmodel (chunk, "models/hay3.mdl"); } else if (self.thingtype==THINGTYPE_CLOTH) { if (final < 0.33) setmodel (chunk, "models/clthchk1.mdl"); else if (final < 0.66) setmodel (chunk, "models/clthchk2.mdl"); else setmodel (chunk, "models/clthchk3.mdl"); } else if (self.thingtype==THINGTYPE_WOOD_LEAF) { if (final < 0.14) setmodel (chunk, "models/splnter1.mdl"); else if (final < 0.28) setmodel (chunk, "models/leafchk1.mdl"); else if (final < 0.42) setmodel (chunk, "models/splnter2.mdl"); else if (final < 0.56) setmodel (chunk, "models/leafchk2.mdl"); else if (final < 0.70) setmodel (chunk, "models/splnter3.mdl"); else if (final < 0.84) setmodel (chunk, "models/leafchk3.mdl"); else setmodel (chunk, "models/splnter4.mdl"); } else if (self.thingtype==THINGTYPE_WOOD_METAL) { if (final < 0.125) setmodel (chunk, "models/splnter1.mdl"); else if (final < 0.25) setmodel (chunk, "models/metlchk1.mdl"); else if (final < 0.375) setmodel (chunk, "models/splnter2.mdl"); else if (final < 0.50) setmodel (chunk, "models/metlchk2.mdl"); else if (final < 0.625) setmodel (chunk, "models/splnter3.mdl"); else if (final < 0.75) setmodel (chunk, "models/metlchk3.mdl"); else if (final < 0.875) setmodel (chunk, "models/splnter4.mdl"); else setmodel (chunk, "models/metlchk4.mdl"); } else if (self.thingtype==THINGTYPE_WOOD_STONE) { if (final < 0.125) setmodel (chunk, "models/splnter1.mdl"); else if (final < 0.25) setmodel (chunk, "models/schunk1.mdl"); else if (final < 0.375) setmodel (chunk, "models/splnter2.mdl"); else if (final < 0.50) setmodel (chunk, "models/schunk2.mdl"); else if (final < 0.625) setmodel (chunk, "models/splnter3.mdl"); else if (final < 0.75) setmodel (chunk, "models/schunk3.mdl"); else if (final < 0.875) setmodel (chunk, "models/splnter4.mdl"); else setmodel (chunk, "models/schunk4.mdl"); } else if (self.thingtype==THINGTYPE_METAL_STONE) { if (final < 0.125) setmodel (chunk, "models/metlchk1.mdl"); else if (final < 0.25) setmodel (chunk, "models/schunk1.mdl"); else if (final < 0.375) setmodel (chunk, "models/metlchk2.mdl"); else if (final < 0.50) setmodel (chunk, "models/schunk2.mdl"); else if (final < 0.625) setmodel (chunk, "models/metlchk3.mdl"); else if (final < 0.75) setmodel (chunk, "models/schunk3.mdl"); else if (final < 0.875) setmodel (chunk, "models/metlchk4.mdl"); else setmodel (chunk, "models/schunk4.mdl"); } else if (self.thingtype==THINGTYPE_METAL_CLOTH) { if (final < 0.14) setmodel (chunk, "models/metlchk1.mdl"); else if (final < 0.28) setmodel (chunk, "models/clthchk1.mdl"); else if (final < 0.42) setmodel (chunk, "models/metlchk2.mdl"); else if (final < 0.56) setmodel (chunk, "models/clthchk2.mdl"); else if (final < 0.70) setmodel (chunk, "models/metlchk3.mdl"); else if (final < 0.84) setmodel (chunk, "models/clthchk3.mdl"); else setmodel (chunk, "models/metlchk4.mdl"); } else if (self.thingtype==THINGTYPE_ICE) { setmodel(chunk,"models/shard.mdl"); chunk.skin=0; chunk.frame=random(2); chunk.drawflags(+)DRF_TRANSLUCENT|MLS_ABSLIGHT; chunk.abslight=0.5; } else// if (self.thingtype==THINGTYPE_GREYSTONE) { if (final < 0.25) setmodel (chunk, "models/schunk1.mdl"); else if (final < 0.50) setmodel (chunk, "models/schunk2.mdl"); else if (final < 0.75) setmodel (chunk, "models/schunk3.mdl"); else setmodel (chunk, "models/schunk4.mdl"); chunk.skin = 0; } setsize (chunk, '0 0 0', '0 0 0'); chunk.movetype = MOVETYPE_BOUNCE; chunk.solid = SOLID_NOT; chunk.velocity = ChunkVelocity(); chunk.think = ChunkRemove; chunk.avelocity_x = random(1200); chunk.avelocity_y = random(1200); chunk.avelocity_z = random(1200); if(self.classname=="monster_eidolon") chunk.scale=random(2.1,2.5); else chunk.scale = random(scalemod,scalemod + .1); chunk.ltime = time; thinktime chunk : random(2); } void DropBackpack(void); // in items.hc // Put a little splat down if it will fit void TinySplat (vector location) { vector holdplane; entity splat; traceline (location + v_up*8 + v_right * 8 + v_forward * 8,location - v_up*32 + v_right * 8 + v_forward * 8, TRUE, self); holdplane = trace_plane_normal; if(trace_fraction==1) // Nothing below victim return; traceline (location + v_up*8 - v_right * 8 + v_forward * 8,location - v_up*32 - v_right * 8 + v_forward * 8, TRUE, self); if ((holdplane != trace_plane_normal) || (trace_fraction==1)) return; traceline (location + v_up*8 + v_right * 8 - v_forward * 8,location - v_up*32 + v_right * 8 - v_forward * 8, TRUE, self); if ((holdplane != trace_plane_normal) || (trace_fraction==1)) return; traceline (location + v_up*8 - v_right * 8 - v_forward * 8,location - v_up*32 - v_right * 8 - v_forward * 8, TRUE, self); if ((holdplane != trace_plane_normal) || (trace_fraction==1)) return; traceline (location + v_up*8 ,location - v_up*32 , TRUE, self); splat=spawn(); splat.owner=self; splat.classname="bloodsplat"; splat.movetype=MOVETYPE_NONE; splat.solid=SOLID_NOT; // Flat to the surface trace_plane_normal_x = trace_plane_normal_x * -1; trace_plane_normal_y = trace_plane_normal_y * -1; splat.angles = vectoangles(trace_plane_normal); setmodel(splat,"models/bldspot4.spr"); // 8 x 8 sprite setsize(splat,'0 0 0','0 0 0'); setorigin(splat,trace_endpos + '0 0 2'); } void BloodSplat(void) { entity splat; vector holdangles; if (random() < .5) { holdangles_x = random(-30,-20); holdangles_y = random(30,20); } else { holdangles_x = random(30,20); holdangles_y = random(-30,-20); } holdangles_z = 16; TinySplat (self.origin + holdangles); if (random() < .5) { holdangles_x = random(-30,-10); holdangles_y = random(30,10); } else { holdangles_x = random(30,10); holdangles_y = random(-30,-10); } holdangles_z = 16; TinySplat (self.origin + holdangles); makevectors (self.angles); traceline (self.origin + v_up*8,self.origin - v_up*32, TRUE, self); if(trace_fraction==1) // Nothing below victim { dprint("\n no floor "); return; } splat=spawn(); splat.owner=self; splat.classname="bloodsplat"; splat.movetype=MOVETYPE_NONE; splat.solid=SOLID_NOT; // Flat to the surface trace_plane_normal_x = trace_plane_normal_x * -1; trace_plane_normal_y = trace_plane_normal_y * -1; splat.angles = vectoangles(trace_plane_normal); // setmodel(splat,"models/bldspot1.spr"); // 30 x 30 sprite size setmodel(splat,"models/bldspot2.spr"); // 20 x 20 sprite size // setmodel(splat,"models/bldspot3.spr"); // 18 x 18 sprite size // setmodel(splat,"models/bldspot4.spr"); // 8 x 8 sprite size setsize(splat,'0 0 0','0 0 0'); setorigin(splat,trace_endpos + '0 0 2'); } void chunk_reset () { chunk_cnt=FALSE; remove(self); } void make_chunk_reset () { newmis=spawn(); newmis.think=chunk_reset; thinktime newmis : 1.5; } void chunk_death (void) { vector space; float spacecube,model_cnt,scalemod; string deathsound; DropBackpack(); // BloodSplat(); space = self.absmax - self.absmin; spacecube = space_x * space_y * space_z; model_cnt = spacecube / 8192; // (16 * 16 * 16) if ((self.thingtype==THINGTYPE_GLASS) || (self.thingtype==THINGTYPE_CLEARGLASS) || (self.thingtype==THINGTYPE_REDGLASS)) deathsound="fx/glassbrk.wav"; else if ((self.thingtype==THINGTYPE_WOOD) || (self.thingtype==THINGTYPE_WOOD_METAL)) if(self.classname=="bolt") deathsound="assassin/arrowbrk.wav"; else deathsound="fx/woodbrk.wav"; else if ((self.thingtype==THINGTYPE_GREYSTONE) || (self.thingtype==THINGTYPE_BROWNSTONE) || (self.thingtype==THINGTYPE_WOOD_STONE) || (self.thingtype==THINGTYPE_METAL_STONE)) deathsound="fx/wallbrk.wav"; else if ((self.thingtype==THINGTYPE_METAL) || (self.thingtype==THINGTYPE_METAL_CLOTH)) deathsound="fx/metalbrk.wav"; else if ((self.thingtype==THINGTYPE_CLOTH) || (self.thingtype==THINGTYPE_REDGLASS)) deathsound="fx/clothbrk.wav"; else if (self.thingtype==THINGTYPE_FLESH) { //Made temporary changes to make weapons look and sound //better, more blood and gory sounds. if(self.health<-80) deathsound="player/megagib.wav"; else deathsound="player/gib1.wav"; sound(self,CHAN_AUTO,deathsound,1,ATTN_NORM); self.level=-666; } else if (self.thingtype==THINGTYPE_CLAY) deathsound="fx/claybrk.wav"; else if ((self.thingtype==THINGTYPE_LEAVES) || (self.thingtype==THINGTYPE_WOOD_LEAF)) deathsound="fx/leafbrk.wav"; else if (self.thingtype==THINGTYPE_ICE) deathsound="misc/icestatx.wav"; else deathsound="fx/wallbrk.wav"; if(self.level!=-666) sound (self, CHAN_VOICE, deathsound, 1, ATTN_NORM); // Scale 0 - 50,000 small // 50,000 - 500,000 medium // 500,000 large // 1,000,000 + huge if (spacecube < 5000) { scalemod = .20; model_cnt = model_cnt * 3; // Because so few pieces come out of a small object } else if (spacecube < 50000) { scalemod = .45; model_cnt = model_cnt * 3; // Because so few pieces come out of a small object } else if (spacecube < 500000) { scalemod = .50; } else if (spacecube < 1000000) { scalemod = .75; } else { scalemod = 1; } if(model_cnt>CHUNK_MAX) model_cnt=CHUNK_MAX; while (model_cnt>0) { if (chunk_cnt < CHUNK_MAX*2) { CreateModelChunks(space,scalemod); chunk_cnt+=1; } model_cnt-=1; } make_chunk_reset(); if(self.classname=="monster_eidolon") return; SUB_UseTargets(); if(self.headmodel!=""&&self.classname!="head") ThrowSolidHead (50); else remove(self); } gamecode/hc/h2/client.hc000066400000000000000000001702041444734033100153260ustar00rootroot00000000000000// prototypes void () W_WeaponFrame; void() W_SetCurrentAmmo; void() player_pain; void (vector org, entity death_owner) spawn_tdeath; void() DecrementSuperHealth; void CheckRings (void); void FreezeAllEntities(void) { entity search; search = nextent(world); while(search != world) { if (search.classname != "player") { thinktime search : 99999; } search = nextent(search); } } /* ============================================================================= LEVEL CHANGING / INTERMISSION ============================================================================= */ float intermission_running; float intermission_exittime; /*QUAKED info_intermission (1 0.5 0.5) (-16 -16 -16) (16 16 16) This is the camera point for the intermission. Use mangle instead of angle, so you can set pitch or roll as well as yaw. 'pitch roll yaw' -----------------------FIELDS------------------------- -------------------------------------------------------- */ void info_intermission(void) { } /* ============ FindIntermission Returns the entity to view from ============ */ entity FindIntermission(void) { entity spot; float cyc; // look for info_intermission first spot = find (world, classname, "info_intermission"); if (spot) { // pick a random one cyc = random(4); while (cyc > 1) { spot = find (spot, classname, "info_intermission"); if (!spot) spot = find (spot, classname, "info_intermission"); cyc = cyc - 1; } return spot; } // then look for the start position spot = find (world, classname, "info_player_start"); if (spot) return spot; // testinfo_player_start is only found in regioned levels spot = find (world, classname, "testplayerstart"); if (spot) return spot; objerror ("FindIntermission: no spot"); } string nextmap; string nextstartspot; void GotoNextMap(void) { if (cvar("samelevel")) // if samelevel is set, stay on same level { changelevel (mapname, startspot); } else { changelevel (nextmap, nextstartspot); } } void ExitIntermission(void) { // skip any text in deathmatch if (deathmatch) { intermission_exittime = intermission_running = 0; } other = find (world, classname, "player"); while (other != world) { stuffcmd(other, "-showdm\n"); other.frags=0;//reset frags other.takedamage = DAMAGE_YES; other.solid = SOLID_BBOX; other.movetype = MOVETYPE_WALK; other.flags(-)FL_NOTARGET; other.effects=FALSE; other.weaponmodel=other.lastweapon; other = find (other, classname, "player"); } if (deathmatch) { gameover = FALSE; GotoNextMap (); return; } intermission_exittime = time + 1; intermission_running = intermission_running + 1; // // run some text if at the end of an episode // if (intermission_running == 2) GotoNextMap(); } /* ============ IntermissionThink When the player presses attack or jump, change to the next level ============ */ void IntermissionThink(void) { if (time < intermission_exittime) return; if (!self.button0 && !self.button1 && !self.button2) return; ExitIntermission (); } void() execute_changelevel = { intermission_running = 1; // enforce a wait time before allowing changelevel if (deathmatch) intermission_exittime = time + 5; else intermission_exittime = time + 2; other = find (world, classname, "player"); while (other != world) { // other.sv_flags=serverflags; thinktime other : 0.5; other.takedamage = DAMAGE_NO; other.solid = SOLID_NOT; other.movetype = MOVETYPE_NONE; other.flags(+)FL_NOTARGET; other.effects=EF_NODRAW; other.lastweapon=other.weaponmodel; stuffcmd(other,"+showdm\n"); other = find (other, classname, "player"); } }; void FindDMLevel(void) { serverflags (+) SFL_NEW_UNIT; nextmap = string_null; if (cvar("registered") != 0 || cvar("oem") != 0) { if (mapname == "demo1") nextmap = "demo2"; else if (mapname == "demo2") nextmap = "demo3"; else if (mapname == "demo3") nextmap = "village1"; else if (mapname == "village1") nextmap = "village2"; else if (mapname == "village2") nextmap = "village3"; else if (mapname == "village3") nextmap = "village4"; else if (mapname == "village4") nextmap = "village5"; else if (mapname == "village5") nextmap = "rider1a"; else if (mapname == "rider1a") nextmap = "demo1"; else if (mapname == "meso1") nextmap = "meso2"; else if (mapname == "meso2") nextmap = "meso3"; else if (mapname == "meso3") nextmap = "meso4"; else if (mapname == "meso4") nextmap = "meso5"; else if (mapname == "meso5") nextmap = "meso6"; else if (mapname == "meso6") nextmap = "meso8"; else if (mapname == "meso8") nextmap = "meso9"; else if (mapname == "meso9") nextmap = "meso1"; else if (mapname == "egypt1") nextmap = "egypt2"; else if (mapname == "egypt2") nextmap = "egypt3"; else if (mapname == "egypt3") nextmap = "egypt4"; else if (mapname == "egypt4") nextmap = "egypt5"; else if (mapname == "egypt5") nextmap = "egypt6"; else if (mapname == "egypt6") nextmap = "egypt7"; else if (mapname == "egypt7") nextmap = "rider2c"; else if (mapname == "rider2c") nextmap = "egypt1"; else if (mapname == "romeric1") nextmap = "romeric2"; else if (mapname == "romeric2") nextmap = "romeric3"; else if (mapname == "romeric3") nextmap = "romeric4"; else if (mapname == "romeric4") nextmap = "romeric5"; else if (mapname == "romeric5") nextmap = "romeric6"; else if (mapname == "romeric6") nextmap = "romeric7"; else if (mapname == "romeric7") nextmap = "romeric1"; else if (mapname == "cath") nextmap = "tower"; else if (mapname == "tower") nextmap = "castle4"; else if (mapname == "castle4") nextmap = "castle5"; else if (mapname == "castle5") nextmap = "eidolon"; else if (mapname == "eidolon") nextmap = "cath"; else if (mapname == "ravdm1") nextmap = "ravdm2"; else if (mapname == "ravdm2") nextmap = "ravdm3"; else if (mapname == "ravdm3") nextmap = "ravdm4"; else if (mapname == "ravdm4") nextmap = "ravdm5"; else if (mapname == "ravdm5") nextmap = "ravdm1"; } else { /* O.S - 2006-10-30: version 1.11 of the demo already has the demo3 level. Added it here. */ if (mapname == "demo1") nextmap = "demo2"; else if (mapname == "demo2") nextmap = "demo3"; else if (mapname == "demo3") nextmap = "ravdm1"; else if (mapname == "ravdm1") nextmap = "demo1"; } } void() changelevel_touch = { if (other.classname != "player")//||(!infront_of_ent(self,other))) return; /* noexit is only for dm - from Maddes' QuakeC patches: */ if (deathmatch && ((cvar("noexit") == 1) || (cvar("noexit") == 2))) { T_Damage (other, self, self, 50000); return; } if (self.movedir != '0 0 0') { makevectors (other.angles); if (v_forward * self.movedir < 0) return; // not facing the right way } //FIXME: temp server flags fix // other.sv_flags=serverflags; if (coop || deathmatch) { bprint (other.netname); bprint (" exited the level\n"); } if (deathmatch) FindDMLevel(); else { nextmap = self.map; nextstartspot = self.target; } SUB_UseTargets (); if (cvar("registered") == 0 && cvar("oem") == 0 && nextmap == "village1") { remove(self); intermission_running = 2; intermission_exittime = time + 20; WriteByte (MSG_ALL, SVC_INTERMISSION); WriteByte (MSG_ALL, 5); FreezeAllEntities(); return; } /* if (self.spawnflags & 2) { serverflags (+) SFL_NEW_UNIT; serverflags (-) SFL_CROSS_TRIGGERS; } else serverflags (-) SFL_NEW_UNIT; if (self.spawnflags & 4) { serverflags (+) SFL_NEW_EPISODE; serverflags (-) SFL_CROSS_TRIGGERS; } else serverflags (-) SFL_NEW_EPISODE; */ // rjr spawnflag 1 use to be "no intermission" - removed the option completely // if ( (self.spawnflags & 1) && (deathmatch == 0) ) if ( (deathmatch == 0) ) { // NO_INTERMISSION GotoNextMap(); return; } self.touch = SUB_Null; // we can't move people right now, because touch functions are called // in the middle of C movement code, so set a think time to do it self.think = execute_changelevel; thinktime self : 0.1; }; void() changelevel_use = { local entity saveOther; saveOther = other; other = activator; changelevel_touch (); other = saveOther; }; /*QUAKED trigger_changelevel (0.5 0.5 0.5) ? x END_OF_UNIT END_OF_EPISODE When the player touches this, he gets sent to the map listed in the "map" variable. Unless the NO_INTERMISSION flag is set, the view will go to the info_intermission spot and display stats. */ void() trigger_changelevel = { if (!self.map) objerror ("changelevel trigger doesn't have map"); InitTrigger (); self.touch = changelevel_touch; self.use = changelevel_use; }; /* ============================================================================= PLAYER GAME EDGE FUNCTIONS ============================================================================= */ //void() set_suicide_frame; void()GibPlayer; // called by ClientKill and DeadThink void() respawn = { if (coop) { // make a copy of the dead body for appearances sake SolidPlayer(); // get the spawn parms as they were at level start setspawnparms (self); // respawn PutClientInServer (); } else if (deathmatch) { // make a copy of the dead body for appearances sake SolidPlayer(); PutClientInServer (); } else { // restart the entire server if(parm7) changelevel (mapname, startspot); else localcmd ("restart restore\n"); } }; /* ============ ClientKill Player entered the suicide command ============ */ void() ClientKill = { bprint (self.netname); bprint (" suicides\n"); self.model=self.init_model; GibPlayer(); self.frags -= 2; // extra penalty drop_level(self,2); respawn (); }; float(vector v) CheckSpawnPoint = { return FALSE; }; /* ============ SelectSpawnPoint Returns the entity to spawn at ============ */ //@@ TODO: not fixed order!!! entity() SelectSpawnPoint = {//FIXME: if start on 2nd - 5th hubs, fill in correct startspot string entity spot; entity thing; float pcount; float ok; // testinfo_player_start is only found in regioned levels spot = find (world, classname, "testplayerstart"); if (spot) return spot; // choose a info_player_deathmatch point if(self.newclass) { spot = find(world, classname, "classchangespot"); if(spot) { spot.think=SUB_Remove; thinktime spot : 1; return spot; } } if (coop) { spot = lastspawn; pcount = 1; while (pcount > 0 && pcount < 3) { spot = find(spot, classname, "info_player_coop"); if (spot != world && (spot.targetname == startspot) || (startspot == string_null && spot.spawnflags & 1)) { thing = findradius(spot.origin, 64); ok = TRUE; while (thing) { if (thing.classname == "player") { thing = world; ok = FALSE; } else thing = thing.chain; } if (ok) { lastspawn = spot; return lastspawn; } } if (spot == world) pcount += 1; } // dprint("Resorting to info_player_start\n"); lastspawn = find (lastspawn, classname, "info_player_start"); if (lastspawn != world) return lastspawn; } else if (deathmatch) { spot = lastspawn; loop /*while (1)*/ { spot = find(spot, classname, "info_player_deathmatch"); if (spot != world) { if (spot == lastspawn) return lastspawn; pcount = 0; thing = findradius(spot.origin, 64); while(thing) { if (thing.classname == "player") pcount = pcount + 1; thing = thing.chain; } if (pcount == 0) { lastspawn = spot; return spot; } } } } if (startspot) { spot = world; pcount = 1; while(pcount) { spot = find (spot, classname, "info_player_start"); if (!spot) pcount = 0; else if (spot.targetname == startspot) pcount = 0; } } if (!spot) { spot = find (world, classname, "info_player_start"); if (!spot) error ("PutClientInServer: no info_player_start on level"); } return spot; }; /* =========== PutClientInServer called each time a player is spawned ============ */ void() PlayerDie; void() PutClientInServer = { entity spot; spot = SelectSpawnPoint (); if(deathmatch) { self.items(-)IT_WEAPON4|IT_WEAPON3|IT_WEAPON4_1|IT_WEAPON4_2|IT_WEAPON2; self.skin=0; } // else if(self.sv_flags) // serverflags=self.sv_flags; self.classname = "player"; self.takedamage = DAMAGE_YES; self.solid = SOLID_SLIDEBOX; self.movetype = MOVETYPE_WALK; self.deathtype=""; self.viewentity=self; self.wallspot='0 0 0'; self.scale=1; self.skin=0; self.drawflags=self.abslight=self.effects=0; self.flags(+)FL_CLIENT; self.flags2(+)FL_ALIVE; self.air_finished = time + 12; self.dmg = 2; // initial water damage self.thingtype=THINGTYPE_FLESH; self.adjust_velocity = '-999 -999 -999'; //Reset all time-based fields self.act_state = self.show_hostile = self.onfire= self.invisible_time= self.camptime= self.last_attack= self.torchtime= self.healthtime= self.catapult_time= self.safe_time= self.absorb_time= self.last_impact= self.sheep_sound_time= self.still_time= self.last_onground= self.invisible_finished= self.invincible_time= self.splash_time= self.ring_regen_time= self.rings_low= self.pausetime = self.teleport_time = self.sheep_time = self.attack_finished = self.super_damage_time= self.haste_time = self.tome_time = self.camera_time= self.ring_regen_time= self.ring_flight_time= self.ring_water_time= self.ring_turning_time= self.super_damage= self.super_damage_low= self.hasted= self.decap= self.frozen= self.plaqueflg = 0; self.artifact_active(-)ARTFLAG_FROZEN|ARTFLAG_STONED; if(self.newclass) { bprint(self.netname); bprint(" becomes a "); if(self.newclass==CLASS_PALADIN) bprint("Paladin!\n"); else if(self.newclass==CLASS_CRUSADER) bprint("Crusader!\n"); else if(self.newclass==CLASS_NECROMANCER) bprint("Necromancer!\n"); else bprint("Assassin!\n"); self.playerclass=self.newclass; setclass(self,self.playerclass); stats_NewClass(self); self.newclass=FALSE; } if(deathmatch&&randomclass) self.playerclass=CLASS_NONE; if (self.playerclass == CLASS_NONE) { // Default it to the paladin if not selected if (cvar("registered") != 0 || cvar("oem") != 0) setclass(self,rint(random(1,4))); else { if (random() < 0.5) setclass(self,CLASS_PALADIN); else setclass(self,CLASS_ASSASSIN); } } if(self.max_health<=0) stats_NewPlayer(self); else self.health = self.max_health; if(self.max_health<=0||self.health<=0) { dprint("ERROR: Respawned Dead!\n"); dprintf("Class: %s\n",self.playerclass); dprint("Map: "); dprint(mapname); dprint("\n"); dprintf("Max: %s\n",self.max_health); dprintf("Health: %s\n",self.health); dprint("Autofix: health default to 100\n"); self.health=self.max_health=100; } self.deadflag = DEAD_NO; setorigin(self, spot.origin + '0 0 1'); self.angles = spot.angles; self.fixangle = TRUE; // turn this way immediately if(!self.weapon) { self.items=IT_WEAPON1; self.weapon=IT_WEAPON1; self.oldweapon=IT_WEAPON1; } if(deathmatch) self.weapon=IT_WEAPON1; if(coop) {//Need more mana in coop, especially if you die if(self.bluemana<25) self.bluemana=25; if(self.greenmana<25) self.greenmana=25; } W_SetCurrentAmmo (); SetModelAndThinks(); PlayerSpeed_Calc(); if(deathmatch) { self.effects=0; self.artifact_active=ART_INVINCIBILITY; self.invincible_time = time + 3; self.artifact_low(+)ART_INVINCIBILITY; if(self.playerclass==CLASS_CRUSADER) self.skin = GLOBAL_SKIN_STONE; else if(self.playerclass==CLASS_PALADIN) self.effects(+)EF_BRIGHTLIGHT; else if(self.playerclass==CLASS_ASSASSIN) self.colormap=140; else if(self.playerclass==CLASS_NECROMANCER) self.effects(+)EF_DARKLIGHT; } self.ring_regen_time = 0; self.ring_flight_time=0; self.ring_water_time=0; self.ring_turning_time=0; self.ring_flight=0; // Health of rings 0 - 100 self.ring_water=0; // self.ring_turning=0; // self.ring_regeneration=0; // self.rings = 0; self.view_ofs = '0 0 50'; self.proj_ofs=' 0 0 44'; self.hull=HULL_PLAYER; self.idealpitch = cvar("sv_walkpitch"); self.button0 = self.button1 = self.button2 = 0; self.attack_finished=time+0.5;//so no respawn fire // self.th_stand(); player_frames(); if (deathmatch || coop) { makevectors(self.angles); GenerateTeleportEffect(self.origin,0); } spawn_tdeath (self.origin, self); }; void ClientReEnter(float TimeDiff) { /* Called for living players entering a level (except for first starting a game) or when you die any time other than on the first level you started playing on. */ entity spot; //string tempmodel; if(!self.flags2&FL_ALIVE||self.health<1||(self.newclass&&!deathmatch&&!coop)) {//If dead, put them in the right spot. self.weapon=IT_WEAPON1; PutClientInServer(); return; } // Need to reset these because they could still point to entities in the previous map self.enemy = self.groundentity = self.chain = self.goalentity = self.dmg_inflictor = self.owner = world; //RESET TIMERS: if(deathmatch) { self.items(-)IT_WEAPON4|IT_WEAPON2|IT_WEAPON3|IT_WEAPON4_1|IT_WEAPON4_2; self.skin=0; } // else if(self.sv_flags) // serverflags=self.sv_flags; self.movetype=MOVETYPE_WALK; self.viewentity=self; self.wallspot='0 0 0'; self.deathtype=""; self.act_state = self.onfire= self.healthtime= self.splash_time= self.decap= self.frozen= self.plaqueflg = 0; self.artifact_active(-)ARTFLAG_FROZEN|ARTFLAG_STONED; self.ring_flight_time = 0; self.ring_flight = 0; self.rings (-) RING_FLIGHT; self.rings_active (-) RING_FLIGHT; self.air_finished = time + 12; self.ring_regen_time += TimeDiff; self.ring_water_time += TimeDiff; self.ring_turning_time += TimeDiff; self.super_damage_time += TimeDiff; self.haste_time += TimeDiff; self.tome_time += TimeDiff; self.camera_time += TimeDiff; self.torchtime += TimeDiff; self.pausetime += TimeDiff; self.teleport_time += TimeDiff; self.sheep_time += TimeDiff; self.attack_finished += TimeDiff; self.catapult_time+= TimeDiff; self.safe_time+= TimeDiff; self.absorb_time+= TimeDiff; self.last_impact+= TimeDiff; self.sheep_sound_time+= TimeDiff; self.still_time+= TimeDiff; self.last_onground+= TimeDiff; self.invincible_time+= TimeDiff; self.show_hostile+= TimeDiff; self.invisible_time+= TimeDiff; self.camptime+= TimeDiff; self.last_attack= self.attack_finished=0; self.light_level = 128; // So the assassin doesn't go invisible coming out of the teleporter self.dmg = 2; // initial water damage setsize (self, '-16 -16 0', '16 16 56'); self.hull=HULL_PLAYER; self.view_ofs = '0 0 50'; self.proj_ofs='0 0 44'; spot = SelectSpawnPoint (); setorigin(self, spot.origin + '0 0 1'); self.angles = spot.angles; self.fixangle = TRUE; // turn this way immediately self.velocity = '0 0 0'; self.avelocity = '0 0 0'; self.adjust_velocity = '-999 -999 -999'; if (deathmatch || coop) { makevectors(self.angles); GenerateTeleportEffect(self.origin,0); } spawn_tdeath (self.origin, self); SetModelAndThinks(); PlayerSpeed_Calc(); W_SetCurrentAmmo (); force_retouch = 2; // make sure even still objects get hit self.think=player_frames; thinktime self : 0; } /* ============================================================================= QUAKED FUNCTIONS ============================================================================= */ /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 24) The normal starting point for a level. -----------------------FIELDS------------------------- -------------------------------------------------------- */ void() info_player_start = { }; /*QUAKED info_player_start2 (1 0 0) (-16 -16 -24) (16 16 24) Only used on start map for the return point from an episode. -----------------------FIELDS------------------------- -------------------------------------------------------- */ void() info_player_start2 = { }; /* saved out by quak ed in region mode */ void() testplayerstart = { }; /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 24) potential spawning position for deathmatch games -----------------------FIELDS------------------------- -------------------------------------------------------- */ void() info_player_deathmatch = { if(!deathmatch) remove(self); }; /*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 24) DEFAULT potential spawning position for coop games -----------------------FIELDS------------------------- -------------------------------------------------------- */ void() info_player_coop = { }; /* =============================================================================== RULES =============================================================================== */ /* go to the next level for deathmatch only called if a time or frag limit has expired */ void() NextLevel = { entity o; FindDMLevel(); if (nextmap == "") { // find a trigger changelevel o = find(world, classname, "trigger_changelevel"); // go back to start if no trigger_changelevel if (!o) { o = spawn(); nextmap = "demo1"; o.map = nextmap; } else { nextmap = o.map; } } else { o = spawn(); o.map = nextmap; } gameover = TRUE; if (o.nextthink < time) { o.think = execute_changelevel; thinktime o : 0.1; } }; /* ============ CheckRules Exit deathmatch games upon conditions ============ */ void() CheckRules = { float timelimit; float fraglimit; /* timelimit/fraglimit are only for dm - from Maddes' QuakeC patches: */ if (gameover || !deathmatch) // someone else quit the game already return; timelimit = cvar("timelimit") * 60; fraglimit = cvar("fraglimit"); if (timelimit && time >= timelimit) { NextLevel (); return; } if (fraglimit && self.frags >= fraglimit) { NextLevel (); return; } }; //============================================================================ void() PlayerDeathThink = { float forward; if ((self.flags & FL_ONGROUND)) { forward = vlen (self.velocity); forward = forward - 20; if (forward <= 0) self.velocity = '0 0 0'; else self.velocity = forward * normalize(self.velocity); } // wait for all buttons released if (self.deadflag == DEAD_DEAD) { if (self.button2 || self.button0) return; self.deadflag = DEAD_RESPAWNABLE; return; } // wait for any button down if (!self.button2 && !self.button1 && !self.button0) return; self.button0 = 0; self.button1 = 0; self.button2 = 0; respawn(); }; void CheckWaterJump() { vector start, end; // check for a climb out of water makevectors (self.angles); start = self.origin + self.proj_ofs - '0 0 8'; v_forward_z = 0; normalize(v_forward); end = start + v_forward*24; traceline (start, end, TRUE, self); if (trace_fraction < 1) { // solid at waist if(self.model=="models/sheep.mdl") start_z = self.origin_z + self.proj_ofs_z + 26;//was absmax - 8 else start_z = self.origin_z + self.proj_ofs_z + 6;//was absmax - 8 end = start + v_forward*24; self.movedir = trace_plane_normal * -50; traceline (start, end, TRUE, self); if (trace_fraction == 1) { // open at eye level self.flags(+)FL_WATERJUMP; self.velocity_z = 225; self.flags(-)FL_JUMPRELEASED; self.teleport_time = time + 2; // safety net return; } } } void()catapult_fire; void() PlayerJump = { if(self.flags&FL_ONGROUND) { traceline(self.origin,self.origin-'0 0 3',FALSE,self); if(trace_ent.classname=="catapult"&&trace_ent.frame==20) { trace_ent.think=catapult_fire; thinktime trace_ent : 0; } } if (self.flags & FL_WATERJUMP) return; if (self.movetype==MOVETYPE_FLY) return; if (self.waterlevel >= 2) { if (self.watertype == CONTENT_WATER) self.velocity_z = 100*self.scale; else if (self.watertype == CONTENT_SLIME) self.velocity_z = 80*self.scale; else self.velocity_z = 50*self.scale; // play swiming sound if (self.swim_flag < time) { self.swim_flag = time + 1; if (random() < 0.5) sound (self, CHAN_BODY, "player/swim1.wav", 1, ATTN_NORM); else sound (self, CHAN_BODY, "player/swim2.wav", 1, ATTN_NORM); } return; } if (!(self.flags & FL_ONGROUND)) { return; } if ( !(self.flags & FL_JUMPRELEASED) ) return; // don't pogo stick self.act_state=ACT_JUMP; self.flags(-)FL_JUMPRELEASED; self.flags(-)FL_ONGROUND; // don't stairwalk self.button2 = 0; // player jumping sound if(self.model=="models/sheep.mdl")//self.modelindex==modelindex_sheep) sheep_sound(1); else if(self.playerclass==CLASS_ASSASSIN) sound (self, CHAN_BODY,"player/assjmp.wav", 1, ATTN_NORM); else sound (self, CHAN_BODY,"player/paljmp.wav", 1, ATTN_NORM); // if (self.playerclass == CLASS_PALADIN) // Can't put this in until SV_GetSpace is fixed // sound (self, CHAN_BODY, "player/pa1jmp.wav", 1, ATTN_NORM); // else if (self.playerclass == CLASS_CRUSADER) // sound (self, CHAN_BODY, "player/crujmp.wav", 1, ATTN_NORM); // else if (self.playerclass == CLASS_NECROMANCER) // sound (self, CHAN_BODY, "player/necjmp.wav", 1, ATTN_NORM); // else if (self.playerclass == CLASS_ASSASSIN) // sound (self, CHAN_BODY, "player/assjmp.wav", 1, ATTN_NORM); self.velocity_z = self.velocity_z + 270*self.scale; }; /* =========== WaterMove ============ */ void() WaterMove = { //dprint (ftos(self.waterlevel)); if (self.movetype == MOVETYPE_NOCLIP) return; if (self.health <= 0) return; if ((self.flags & FL_INWATER) && (self.watertype == CONTENT_WATER) && (self.waterlevel == 3) && (!self.lefty)) { DeathBubbles(10); self.lefty = 1; } /* if ((self.flags & FL_INWATER) && (self.splash_time < time)) { if (((self.velocity_x) || (self.velocity_y) || (self.velocity_z)) && (self.watertype == CONTENT_WATER)) { if (self.waterlevel == 1) { CreateWaterSplash(self.origin + '0 0 10'); } else if (self.waterlevel == 2) { CreateWaterSplash(self.origin + '0 0 20'); } } self.splash_time = time + random(HX_FRAME_TIME,HX_FRAME_TIME*2); } */ if (self.waterlevel != 3) // Not up to the eyes { if (self.air_finished < time) { if (self.model=="models/sheep.mdl") sheep_sound(1); else if(self.playerclass==CLASS_ASSASSIN) sound (self, CHAN_VOICE, "player/assgasp1.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "player/palgasp1.wav", 1, ATTN_NORM); } else if (self.air_finished < time + 9) { if (self.model=="models/sheep.mdl") sheep_sound(1); else if(self.playerclass==CLASS_ASSASSIN) sound (self, CHAN_VOICE, "player/assgasp2.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "player/palgasp2.wav", 1, ATTN_NORM); } self.air_finished = time + 12; self.dmg = 2; } // Completely submerged and no air else if ((self.air_finished < time) && (!self.rings & RING_WATER)) { if(self.playerclass==CLASS_PALADIN&&self.flags&FL_SPECIAL_ABILITY1) { self.air_finished = time + 12; self.dmg = 2; } else if (self.pain_finished < time) {// Drown self.dmg = self.dmg + 2; if (self.dmg > 15) self.dmg = 10; T_Damage (self, world, world, self.dmg); self.pain_finished = time + 1; } } if (!self.waterlevel) { // Getting out of the water if (self.flags & FL_INWATER) { // play leave water sound sound (self, CHAN_BODY, "raven/outwater.wav", 1, ATTN_NORM); self.flags(-)FL_INWATER; self.lefty = 0; } return; } if (self.watertype == CONTENT_LAVA) { // do damage if (self.dmgtime < time) { self.dmgtime = time + 0.5; if(other.flags&FL_FIREHEAL) other.health=other.health+5*self.waterlevel; else if(!other.flags&FL_FIRERESIST) T_Damage (self, world, world, 5*self.waterlevel); } } else if (self.watertype == CONTENT_SLIME) { // do damage if (self.dmgtime < time) { self.dmgtime = time + 1; T_Damage (self, world, world, 4*self.waterlevel); } } // Just entering fluid if (!(self.flags & FL_INWATER)) { self.splash_time = time + .05; // player enter water sound if (self.watertype == CONTENT_LAVA) sound (self, CHAN_BODY, "raven/inlava.wav", 1, ATTN_NORM); else if (self.watertype == CONTENT_WATER) { sound (self, CHAN_BODY, "raven/inh2o.wav", 1, ATTN_NORM); } else if (self.watertype == CONTENT_SLIME) sound (self, CHAN_BODY, "player/slimbrn1.wav", 1, ATTN_NORM); self.flags(+)FL_INWATER; self.dmgtime = 0; } if (! (self.flags & FL_WATERJUMP) ) self.velocity = self.velocity - 0.8*self.waterlevel*frametime*self.velocity; }; void CheckCrouch (void) { if ((self.crouch_time) && (self.crouch_time < time)) // Time to crouch or uncrouch a little { if (self.hull==HULL_CROUCH) // Player crouching { self.crouch_stuck = 0; self.view_ofs_z -= 10; self.proj_ofs_z -= 10; if (self.view_ofs_z < 24) { self.view_ofs_z = 24; self.proj_ofs_z = 18; self.crouch_time = 0; } else self.crouch_time = time + HX_FRAME_TIME/4; } else { self.view_ofs_z += 10; self.proj_ofs_z += 10; if (self.view_ofs_z > 50) { self.view_ofs_z = 50; self.proj_ofs_z = 44; self.crouch_time = 0; } else self.crouch_time = time + HX_FRAME_TIME/4; } } if ((self.flags2 & FL2_CROUCHED||self.model=="models/sheep.mdl"||self.flags2&FL2_CROUCH_TOGGLE) && (self.hull!=HULL_CROUCH)) PlayerCrouching (); else if (((!self.flags2 & FL2_CROUCHED&&self.model!="models/sheep.mdl"&&!self.flags2&FL2_CROUCH_TOGGLE) && (self.hull==HULL_CROUCH)) || (self.crouch_stuck)) // If stuck, constantly try to unstick PlayerUnCrouching(); } void CheckIncapacities () { vector dir; if(self.frozen>0) if(self.flags2&FL_ALIVE&&self.health) { if(self.colormap>144) { self.colormap-=0.5; self.abslight-=0.025; } else { self.colormap=0; self.abslight=0.5; self.skin=GLOBAL_SKIN_ICE; } if(self.pausetime<=time) { if(self.skin==GLOBAL_SKIN_ICE) self.skin=self.oldskin; self.colormap=0; self.abslight=0; self.thingtype=THINGTYPE_FLESH; self.drawflags(-)DRF_TRANSLUCENT|MLS_ABSLIGHT; self.frozen=FALSE; self.artifact_active(-)ARTFLAG_FROZEN; } } else self.frozen=self.pausetime=self.teleport_time=0; if(self.pausetime>time&&self.model!=self.headmodel) { if(self.model=="models/flesh1.mdl") { dir=normalize(self.wallspot-self.origin+self.view_ofs); dir=vectoangles(dir); self.o_angle_x=dir_x*-1; self.o_angle_y=dir_y; self.o_angle_z=self.v_angle_z; } else if(!self.flags2&FL_ALIVE&&self.enemy.flags2&FL_ALIVE)//&&visible(self.enemy)) {//face enemy self.o_angle=normalize(self.enemy.origin+self.enemy.proj_ofs-self.origin+self.view_ofs); self.o_angle=vectoangles(self.o_angle); self.o_angle_x*=-1;//have to reverse the pitch if(self.o_angle_y>180) self.o_angle_y-=360; else if(self.o_angle_y<-180) self.o_angle_y+=360; self.o_angle_z=self.v_angle_z; self.o_angle-=self.v_angle; if(self.o_angle_x>7) self.o_angle_x=7; else if(self.o_angle_x<-7) self.o_angle_x=-7; if(self.o_angle_y>10) self.o_angle_y=10; else if(self.o_angle_y<-10) self.o_angle_y=-10; self.o_angle+=self.v_angle; } msg_entity = self; WriteByte (MSG_ONE, 10); WriteAngle (MSG_ONE, self.o_angle_x); WriteAngle (MSG_ONE, self.o_angle_y); WriteAngle (MSG_ONE, self.o_angle_z); if(self.flags&FL_ONGROUND) self.velocity='0 0 0'; self.button0=0; self.button2=0; self.impulse=0; } if(self.flags2&FL_CHAINED) self.button0=self.button1=self.button2=0; } /* ================ PlayerPreThink Called every frame before physics are run ================ */ void() PlayerPreThink = { vector spot1, spot2; if (!self.flags & FL_INWATER) self.aflag = 0; // dprint(teststr[1]); // dprint("\n"); if (intermission_running) { IntermissionThink (); // otherwise a button could be missed between return; // the think tics } if (self.view_ofs == '0 0 0'&& self.viewentity.classname!="chasecam"&& !self.button0&&!self.button2)//Causing them to not be able to respawn? return; // intermission or finale if (self.adjust_velocity_x != -999) { self.velocity_x = self.adjust_velocity_x; } if (self.adjust_velocity_y != -999) { self.velocity_y = self.adjust_velocity_y; } if (self.adjust_velocity_z != -999) { self.velocity_z = self.adjust_velocity_z; } self.adjust_velocity = '-999 -999 -999'; CheckIncapacities(); if(self.viewentity!=self) { CameraViewPort(self,self.viewentity); if(self.viewentity.classname!="chasecam")//&&self.viewentity.classname!="camera_remote") { self.weaponframe=self.viewentity.weaponframe; self.weaponmodel=self.viewentity.weaponmodel; CameraViewAngles(self,self.viewentity); } else self.weaponmodel=""; } makevectors (self.v_angle); // is this still used self.friction=0; // If in entity FRICTION_TOUCH will reset this CheckRules (); CheckRings (); CheckAbilities (); CheckCrouch (); WaterMove (); if (self.waterlevel == 2) CheckWaterJump (); if (self.deadflag >= DEAD_DEAD) { PlayerDeathThink (); return; } // Turn off plaque if it is on if (self.plaqueflg) { // Is moving or looking around so kill plaque if (((self.velocity_x) || (self.velocity_y) || (self.velocity_z)) || (self.plaqueangle != self.v_angle)) { makevectors (self.v_angle); spot1 = self.origin + self.view_ofs; spot2 = spot1 + (v_forward*25); // Look just a little ahead traceline (spot1, spot2 , FALSE, self); if ((trace_fraction == 1.0) || (trace_ent.classname!="plaque")) { traceline (spot1, spot2 - (v_up * 30), FALSE, self); // 30 down if ((trace_fraction == 1.0) || (trace_ent.classname!="plaque")) { traceline (spot1, spot2 + v_up * 30, FALSE, self); // 30 up if ((trace_fraction == 1.0) || (trace_ent.classname!="plaque")) { self.plaqueflg=0; msg_entity = self; plaque_draw(MSG_ONE,0); } } } if (self.plaqueflg) self.plaqueangle = self.v_angle; } } // Twitch every so often if not moving if ((!self.velocity_x) && (!self.velocity_y) && (!self.velocity_z)) { // FIXME: needs to be a random number between 5 - 8 minutes or so if ((self.camptime + 600) < time) { if (self.playerclass==CLASS_PALADIN) { if (self.weapon==IT_WEAPON1) gauntlet_twitch(); else if (self.weapon==IT_WEAPON2) vorpal_twitch(); self.camptime = time + random(840,420); } } } else self.camptime = time + random(420,840); if (self.deadflag == DEAD_DYING) return; // dying, so do nothing if (self.button2) PlayerJump (); else self.flags(+)FL_JUMPRELEASED; // teleporters can force a non-moving pause time if (time < self.pausetime) self.velocity = '0 0 0'; // Change weapon if (time > self.attack_finished && self.weapon != IT_WEAPON1) { if (((self.weapon == IT_WEAPON3) && (self.greenmana<1)) || ((self.weapon == IT_WEAPON4) && (self.bluemana<1) && (self.greenmana<1))) { W_BestWeapon (); W_SetCurrentWeapon (); } } }; void CheckRings (void) { entity victim; vector dir; float chance; if (self.health <= 0) return; if (self.rings & RING_REGENERATION) { if (self.ring_regen_time < time) { if (self.health < self.max_health) { self.ring_regeneration -= 100/RING_REGENERATION_MAX; self.health += 1; self.ring_regen_time = time + 1; } if ((self.ring_regeneration < 10) && (!self.rings_low & RING_REGENERATION)) { self.rings_low (+) RING_REGENERATION; centerprint (self, "Ring of Regeneration is running low"); sound (self, CHAN_BODY, "misc/comm.wav", 1, ATTN_NORM); } if (self.ring_regeneration <=0) { self.ring_regeneration = 0; self.rings (-) RING_REGENERATION; self.rings_active (-) RING_REGENERATION; } } } if (self.rings & RING_FLIGHT) { if (self.ring_flight_time < time) { self.ring_flight -= 100/RING_FLIGHT_MAX; if ((self.ring_flight < 25) && (!self.rings_low & RING_FLIGHT)) { self.rings_low (+) RING_FLIGHT; centerprint (self, "Ring of Flight is running low"); sound (self, CHAN_BODY, "misc/comm.wav", 1, ATTN_NORM); } if (self.ring_flight <=0) { self.ring_flight = 0; self.rings (-) RING_FLIGHT; self.rings_active (-) RING_FLIGHT; player_stopfly(); if (deathmatch) self.cnt_flight -= 1; } self.ring_flight_time = time + 1; } } if ((self.rings & RING_WATER) && (self.waterlevel == 3) && (self.air_finished < time)) { self.rings_active (+) RING_WATER; if (self.ring_water_time < time) { self.ring_water -= 100/RING_WATER_MAX; if ((self.ring_water < 25) && (!self.rings_low & RING_WATER)) { self.rings_low (+) RING_WATER; centerprint (self, "Ring of Water Breathing is running low"); sound (self, CHAN_BODY, "misc/comm.wav", 1, ATTN_NORM); } if (self.ring_water <=0) { self.ring_water = 0; self.rings (-) RING_WATER; self.rings_active (-) RING_WATER; } self.ring_water_time = time + 1; } } else self.rings_active (-) RING_WATER; if (self.rings & RING_TURNING) { victim = findradius( self.origin, 100); while(victim) { if ((victim.movetype == MOVETYPE_FLYMISSILE) && (victim.owner != self)) { victim.owner = self; chance = random(); dir = victim.origin + (v_forward * -1); CreateLittleWhiteFlash(dir); sound (self, CHAN_WEAPON, "weapons/vorpturn.wav", 1, ATTN_NORM); if (chance < 0.9) // Deflect it { victim.v_angle = self.v_angle + randomv('0 0 0', '360 360 360'); makevectors (victim.v_angle); victim.velocity = v_forward * 1000; } else // reflect missile victim.velocity = '0 0 0' - victim.velocity; } victim = victim.chain; } if (self.ring_turning_time < time) { self.ring_turning -= 100/RING_TURNING_MAX; if ((self.ring_turning < 10) && (!self.rings_low & RING_TURNING)) { self.rings_low (+) RING_TURNING; centerprint (self, "Ring of Reflection is running low"); sound (self, CHAN_BODY, "misc/comm.wav", 1, ATTN_NORM); } if (self.ring_turning <=0) { self.ring_turning = 0; self.rings (-) RING_TURNING; self.rings_active (-) RING_TURNING; } self.ring_turning_time = time + 1; } } } void remove_invincibility(entity loser) { loser.artifact_low(-)ART_INVINCIBILITY; loser.artifact_active (-) ART_INVINCIBILITY; loser.invincible_time = 0; loser.air_finished = time + 12; if(loser.playerclass==CLASS_CRUSADER) loser.skin = 0; else if(loser.playerclass==CLASS_PALADIN) loser.effects(-)EF_BRIGHTLIGHT; else if(loser.playerclass==CLASS_ASSASSIN) loser.colormap=0; else if(loser.playerclass==CLASS_NECROMANCER) loser.effects(-)EF_DARKLIGHT; } /* ================ CheckPowerups Check for turning off powerups ================ */ void() CheckPowerups = { if (self.health <= 0) return; if (self.divine_time < time) self.artifact_active (-) ARTFLAG_DIVINE_INTERVENTION; // Crusader's special ability to smite if (self.super_damage) { if (self.super_damage_time < time) { self.super_damage = 0; } else if (((self.super_damage_time - 10) < time) && (!self.super_damage_low)) { self.super_damage_low = 1; sprint (self, "Holy Strength begins to wane\n"); stuffcmd (self, "bf\n"); } } if (self.artifact_active & ART_HASTE) { if (self.haste_time < time) { self.artifact_low (-) ART_HASTE; self.artifact_active (-) ART_HASTE; self.effects(-)EF_DARKFIELD; PlayerSpeed_Calc(); self.haste_time = 0; self.air_finished = time + 12; } else if ((self.haste_time - 10) < time) self.artifact_low (+) ART_HASTE; } if (self.artifact_active & ART_INVINCIBILITY) { if (self.invincible_time < time) remove_invincibility(self); else if ((self.invincible_time - 10) < time) self.artifact_low (+) ART_INVINCIBILITY; } // if (self.artifact_active & ART_TOMEOFPOWER) // { if ((self.drawflags & MLS_MASKIN) != MLS_POWERMODE) self.drawflags = (self.drawflags & MLS_MASKOUT)| MLS_POWERMODE; if (self.tome_time < time) { self.artifact_low (-) ART_TOMEOFPOWER; self.artifact_active (-) ART_TOMEOFPOWER; self.tome_time = 0; self.drawflags = (self.drawflags & MLS_MASKOUT)| 0; } else if ((self.tome_time - 10) < time) self.artifact_low (+) ART_TOMEOFPOWER; // } // invisibility if (self.artifact_active & ART_INVISIBILITY) { if (self.invisible_time < time) { // just stopped self.artifact_low (-) ART_INVISIBILITY; self.artifact_active (-) ART_INVISIBILITY; self.invisible_time = 0; msg_entity=self; WriteByte(MSG_ONE, SVC_CLEAR_VIEW_FLAGS); WriteByte(MSG_ONE,DRF_TRANSLUCENT); self.effects(-)EF_NODRAW|EF_LIGHT; } else if ((self.invisible_time - 10) < time) self.artifact_low (+) ART_INVISIBILITY; } if (self.sheep_timetime||self.button0||self.button2) CameraReturn (); } else if (self.camera_time < time) CameraReturn (); }; /* ================ Player Touch Mainly used to allow player to climb on top of monsters, other players, etc. ================ */ void PlayerTouch (void) { if(other.classname=="monster_eidolon") return; if(other.dmg==666&&(other.velocity!='0 0 0'||other.avelocity!='0 0 0')) { self.decap=TRUE; T_Damage (self, other, other, self.health+300); return; } if(((vlen(self.velocity)*(self.mass/10)>=100&&self.last_onground+0.3=THINGTYPE_WEBS)&&self.last_impact+0.1<=time) obj_fly_hurt(other); if(other==world) return; if(self.flags&FL_ONGROUND) return; if((other.classname=="player"||other.flags&FL_ONGROUND||other.health)&&self.origin_z>=(other.absmin_z+other.absmax_z)*0.5&&self.velocity_z<10) self.flags(+)FL_ONGROUND; } /* ================ PlayerPostThink Called every frame after physics are run ================ */ void() PlayerPostThink = { if (intermission_running) return; if (self.deadflag) return; // do weapon stuff W_WeaponFrame (); if(self.viewentity.classname=="chasecam") self.weaponmodel=""; // check to see if player landed and play landing sound if ((self.jump_flag*(self.mass/10) < -300) && (self.flags & FL_ONGROUND) && (self.health > 0)) { if(self.absorb_time>=time) self.jump_flag/=2; if (self.watertype == CONTENT_WATER) sound (self, CHAN_BODY, "player/h2ojmp.wav", 1, ATTN_NORM); else if (self.jump_flag*(self.mass/10) < -500)//was -650 { // T_Damage (self, world, world, 5); if(self.playerclass==CLASS_ASSASSIN) sound (self, CHAN_VOICE, "player/asslnd.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "player/pallnd.wav", 1, ATTN_NORM); self.deathtype = "falling"; } else sound (self, CHAN_VOICE, "player/land.wav", 1, ATTN_NORM); if(self.scale>1&&self.jump_flag*(self.mass/10) < -500) MonsterQuake((self.mass/10)*self.jump_flag); self.jump_flag = 0; } if (!(self.flags & FL_ONGROUND)) self.jump_flag = self.velocity_z; else self.last_onground=time; CheckPowerups (); if ((self.artifact_flags & AFL_TORCH) && (self.torchtime < time)) self.torchthink (); if ((self.artifact_flags & AFL_SUPERHEALTH) && (self.healthtime < time)) DecrementSuperHealth (); }; /* =========== ClientConnect called when a player connects to a server ============ */ void() ClientConnect = { bprint (self.netname); bprint (STR_JOINEDTHEGAME); // a client connecting during an intermission can cause problems if (intermission_running) ExitIntermission (); }; /* =========== ClientDisconnect called when a player disconnects from a server ============ */ void() ClientDisconnect = { if (gameover) return; // if the level end trigger has been activated, just return // since they aren't *really* leaving // let everyone else know bprint (self.netname); bprint (STR_LEFTTHEGAMEWITH); bprint (ftos(self.frags)); bprint (STR_FRAGS); sound (self, CHAN_BODY, "player/leave.wav", 1, ATTN_NONE); GibPlayer(); set_suicide_frame (); }; /* =========== ClientObituary called when a player dies ============ */ void(entity targ, entity attacker, entity inflictor) ClientObituary = { float rnum,tclass,aclass,reversed,powered_up, exp_mult; string deathstring, deathstring2,iclass; if (targ.classname != "player") return; tclass=targ.playerclass; aclass=attacker.playerclass; iclass=inflictor.classname; powered_up=inflictor.frags; exp_mult=1; rnum = random(); if (targ.deathtype == "teledeath") { bprint (targ.netname); bprint (STR_WASTELEFRAGGEDBY); bprint (attacker.netname); bprint ("\n"); attacker.frags += 1; return; } if (targ.deathtype == "teledeath2") { bprint ("The power of invincibility reflects "); bprint (targ.netname); bprint ("'s telefrag\n"); targ.frags -= 1; return; } if (targ.deathtype == "teledeath3") { bprint (attacker.netname); bprint (" telefragged "); bprint (targ.netname); bprint (", his own teammate!\n"); attacker.frags -= 1; return; } if (targ.deathtype == "teledeath4") { bprint (attacker.netname); bprint ("'s invincibility met "); bprint (targ.netname); bprint ("'s invincibility and mutual annihilation resulted!\n"); targ.frags -= 1; return; } // Was killed by a player if (attacker.classname == "player") { if (targ == attacker) { // killed self attacker.frags -= 1; bprint (targ.netname); if(random()<0.5) bprint (" must be a masochist!\n"); else bprint (" becomes bored with life...\n"); return; } else if ( (teamplay == 2) && (targ.team > 0)&&(targ.team == attacker.team) ) { if (rnum < 0.25) deathstring = " mows down a teammate\n"; else if (rnum < 0.50) deathstring = " checks his glasses\n"; else if (rnum < 0.75) deathstring = " gets a frag for the other team\n"; else deathstring = " loses another friend\n"; bprint (attacker.netname); bprint (deathstring); attacker.frags -= 1; return; } else { attacker.frags += 1; rnum = attacker.weapon; if(attacker.model=="models/sheep.mdl") { deathstring = " was nibbled to death by "; deathstring2 = " the sheep!!\n"; } else if(targ.decap==1) { if(tclass==CLASS_ASSASSIN) deathstring = " lost her head over "; else deathstring = " lost his head over "; deathstring2 = "!\n"; } else if (targ.decap==2) { if (tclass==CLASS_ASSASSIN) deathstring = " got her head blown clean off by "; else deathstring = " got his head blown clean off by "; deathstring2 = "!\n"; } else if (iclass=="cube_of_force") { deathstring = " was ventilated by "; deathstring2 = "'s Force Cube!\n"; } else if(iclass=="tripwire") { deathstring = " tripped on "; deathstring2 = "'s tripwire glyph!\n"; } else if(iclass=="fireballblast") { deathstring = " was blown away by "; deathstring2 = "'s delayed fireball glyph!\n"; } else if(iclass=="proximity") { deathstring = " got too close for comfort to "; deathstring2 = "'s proximity glyph!\n"; } else if(iclass=="timebomb") { deathstring = " was in the wrong place at the wrong time thanks to "; deathstring2 = "'s timebomb glyph!\n"; } else if(iclass=="tornato") { deathstring = " isn't in kansas anymore thanks to "; deathstring2 = "'s tornado!\n"; } else if(iclass=="blizzard") { deathstring = " was snowed in by "; deathstring2 = "'s blizzard!\n"; } else if(targ.deathtype=="hammercrush") { deathstring = " was crushed by the righteous might of "; deathstring2 = "'s Hammer!\n"; } else if (iclass == "monster_imp_lord") { deathstring =" was jacked up by "; deathstring2 ="'s Summoned Imp Lord!\n"; } else if(inflictor.frags==2) { deathstring = " was destroyed by the power of "; deathstring2 = "'s Disc of Repulsion!\n"; } else if (rnum == IT_WEAPON1) { if(attacker.artifact_active&ART_TOMEOFPOWER) exp_mult=1.5; else exp_mult=2; if(aclass==CLASS_ASSASSIN) { deathstring = " got penetrated by "; deathstring2 = "'s Katar\n"; } else if(aclass==CLASS_CRUSADER) { if(exp_mult==1.5) { deathstring = " was fried by the holy lightning of "; deathstring2 = "'s Mjolnir!\n"; } else { deathstring = " was whalloped by "; deathstring2 = "'s hammer!\n"; } } else if(aclass==CLASS_PALADIN) { deathstring = " got KO'd by "; deathstring2 = "'s fists of fury!\n"; } else { deathstring = " was sliced and diced by "; deathstring2 = "'s sickle!\n"; } } else if (rnum == IT_WEAPON2) { if(powered_up) exp_mult=1; else exp_mult=1.2; if(aclass==CLASS_ASSASSIN) { if(powered_up) { deathstring = " was stuck like a pig by "; deathstring2 = "'s arrows!\n"; } else { deathstring = " took one of "; deathstring2 = "'s arrows to the heart!\n"; } } else if(aclass==CLASS_CRUSADER) { if(powered_up) { deathstring = " befell the subzero temperatures of "; deathstring2 = "'s blizzard!\n"; } else { deathstring = " gets the cold shoulder from "; deathstring2 = "!\n"; } } else if(aclass==CLASS_PALADIN) { if(powered_up) { deathstring = " took a shock to the system from "; deathstring2 = "'s Vorpal Shockwave!\n"; } else { deathstring = " was cut to pieces by "; deathstring2 = "'s vorpal sword!\n"; } } else { if(powered_up) { deathstring = " was tracked down by "; deathstring2 = "'s Magic Missiles!\n"; } else { deathstring = " was mowed down by "; deathstring2 = "'s Magic Missiles!\n"; } } } else if (rnum == IT_WEAPON3) { if(powered_up) exp_mult=0.8; else exp_mult=1; if(aclass==CLASS_ASSASSIN) { if(powered_up) { reversed=TRUE; deathstring = " opened up a nice big can o' whoop-ass on "; deathstring2 = "!\n"; } else { deathstring = " sucked down "; deathstring2 = "'s grenade!\n"; } } else if(aclass==CLASS_CRUSADER) { if(powered_up) { deathstring = " was whisked away by "; deathstring2 = "'s tornado!\n"; } else { deathstring = " took a nice hot meteor shower courtesy of "; deathstring2 = "!\n"; } } else if(aclass==CLASS_PALADIN) { if(powered_up) { deathstring = " was cut down by "; deathstring2 = "'s magic axeblades!\n"; } else { deathstring = " got a nasty papercut from "; deathstring2 = "'s axeblade!\n"; } } else { if(powered_up) { deathstring = " was fragged by "; deathstring2 = "'s Frag Bones!\n"; } else { reversed=TRUE; deathstring = " broke "; deathstring2 = "'s bones with the bone shard spell!\n"; } } } else if (rnum == IT_WEAPON4) { if(powered_up) exp_mult=0.5; else exp_mult=0.8; if(aclass==CLASS_ASSASSIN) { if(powered_up) { deathstring = " got into a little S&M with "; deathstring2 = "'s chains!\n"; } else { deathstring = " got cored by "; deathstring2 = "'s Scarab Staff!\n"; } } else if(aclass==CLASS_CRUSADER) { if(attacker.artifact_active&ART_TOMEOFPOWER) { exp_mult=0.5; deathstring = " needs some SPF 5,000,000 to stop "; deathstring2 = "'s Sunstaff!\n"; } else { deathstring = " smells like fried chicken thanks to "; deathstring2 = "'s Sunstaff!\n"; } } else if(aclass==CLASS_PALADIN) { if(powered_up) { deathstring = " was blown into next week by "; deathstring2 = "'s Purifier Seeker!\n"; } else { deathstring = "'s evil ways were purified by "; deathstring2 = "!\n"; } } else { if(powered_up) { deathstring = " succumbed to the black death of "; deathstring2 = "'s Ravens!\n"; } else { deathstring = " befell the black magic of "; deathstring2 = "'s Ravenstaff!\n"; } } } if(reversed) { bprint (attacker.netname); bprint (deathstring); bprint (targ.netname); bprint (deathstring2); } else { bprint (targ.netname); bprint (deathstring); bprint (attacker.netname); bprint (deathstring2); } } return; } // was not killed by a player else { targ.frags -= 1; bprint (targ.netname); if (attacker.flags & FL_MONSTER) { if(attacker.model=="models/sheep.mdl") { if(random()<0.5) bprint (" was savagely mauled by a sheep!\n"); else bprint (" says 'HELLO DOLLY!'\n"); return; } if (attacker.classname == "monster_archer") { bprint (" was skewered by an Archer!\n"); return; } if (attacker.classname == "monster_archer_lord") { bprint (" got Horshacked!\n"); return; } if (attacker.classname == "monster_fallen_angel") { bprint (" was felled by the Fallen Angel\n"); return; } if (attacker.classname == "monster_fallen_angel_lord") { bprint (" was decimated by a Fallen Angel Lord!\n"); return; } if (attacker.classname == "monster_golem_bronze") { if(targ.decap==1) bprint ("'s head was taken as a trophy for the Bronze Golem!\n"); else if(targ.decap==2) bprint (" became a permanent stain on the wall!\n"); else bprint (" was squished like an insect by a Bronze Golem!\n"); return; } if (attacker.classname == "monster_golem_iron") { if (inflictor.classname == "golem_iron_proj") bprint(" felt the sting of the Iron Golem's jewel!\n"); else if(targ.decap==2) bprint ("'s brains make nice wall decorations!\n"); else bprint (" was crushed by the Iron Golem's fist!\n"); return; } if (attacker.classname == "monster_golem_stone") { if(targ.decap==2) bprint (" is feeling a little light-headed!\n"); else bprint (" was pummeled by a Stone Golem!\n"); return; } if (attacker.classname == "monster_golem_crystal") { bprint (" was mangled by the Enchanted Crystal Golem!\n"); return; } if (attacker.classname == "monster_hydra") { bprint (" becomes food for the Hydra!\n"); return; } if (attacker.classname == "monster_imp_fire") { bprint (" was roasted by a Fire Imp!\n"); return; } if (attacker.classname == "monster_imp_ice") { bprint (" chills out with the Ice Imps!\n"); return; } if (attacker.classname == "monster_medusa") { if (attacker.skin==1) bprint (" was stricken by the beauty of the Crimson Medusa!\n"); else bprint (" is helpless in the face of the Medusa's beauty!\n"); return; } if (attacker.classname == "monster_mezzoman") { if (attacker.skin==1) bprint (" is not yet worthy of facing the WerePanther!\n"); else bprint (" is no match for the WereJaguar!\n"); return; } if (attacker.classname == "monster_mummy") { bprint (" got mummified!\n"); return; } if (attacker.classname == "monster_mummy_lord") { bprint (" was escorted to the Underworld by a Mummy Lord!\n"); return; } if (attacker.classname == "monster_scorpion_black") { bprint (" submits to the sting of the Black Scorpion!\n"); return; } if (attacker.classname == "monster_scorpion_yellow") { bprint (" was poisoned by the fatal Golden Scorpion!\n"); return; } if (attacker.classname == "monster_skull_wizard") { bprint (" succumbed to the Skull Wizard's magic!\n"); return; } if (attacker.classname == "monster_skull_wizard_lord") { bprint (" was Skull-duggeried!\n"); return; } if (attacker.classname == "monster_snake") { bprint (" was bitten by the lethal Cobra!\n"); return; } if (attacker.classname == "monster_spider_red_large") { bprint (" was overcome by the Crimson Spiders!\n"); return; } if (attacker.classname == "monster_spider_red_small") { bprint (" was eaten alive by the spiders!\n"); return; } if (attacker.classname == "monster_spider_yellow_large") { bprint (" was overwhelmed by the Golden Spiders!\n"); return; } if (attacker.classname == "monster_spider_yellow_small") { bprint (" is a meal for the spiders!\n"); return; } if (attacker.classname == "rider_famine") { bprint(" was drained of life-force by Famine!\n"); return; } if (attacker.classname == "rider_death") { if(inflictor==attacker) bprint(" was snuffed out of existance by Death!\n"); else if(inflictor.netname=="deathbone") bprint(" had his bones crushed to a fine powder by Death!\n"); else if(iclass=="deathmissile") bprint(" was shot down by Death's crimson bolts!\n"); else bprint(" was smitten by Death's unholy fire\n"); return; } if (attacker.classname == "rider_pestilence") { if(targ.deathtype=="poison") bprint(" was poisoned to death by Pestilence's Crossbow!\n"); else bprint("'s rotted corpse is the possession of Pestilence!\n"); return; } if (attacker.classname == "rider_war") { bprint(" was taught the true meaning of War!\n"); return; } if (attacker.classname == "monster_eidolon") { if(inflictor==attacker) bprint(" was squashed like an insect by Eidolon!\n"); else if(inflictor.classname=="eidolon fireball") bprint(" was obliterated by Eidolon's fireballs!\n"); else if(inflictor.classname=="eidolon spell") bprint(" was introduced to a new level of pain by Eidolon's Magic!\n"); else if(inflictor.classname=="eidolon flames") bprint(" was roasted to a crisp by Eidolon's Hellfire!\n"); } return; } // tricks and traps if(targ.decap==1) { if(targ.playerclass==CLASS_ASSASSIN) bprint(" should have quit while she was a head... oh, she IS a head!\n"); else bprint(" should have quit while he was a head... oh, he IS a head!\n"); return; } if(targ.decap==2) { if(targ.playerclass==CLASS_ASSASSIN) bprint(" got her head blown off!\n"); else bprint(" got his head blown off!\n"); return; } if(attacker.classname=="light_thunderstorm") { if(mapname=="eidolon") bprint(" was smited by Eidolon's unholy lightning!\n"); else bprint(" shouldn't mess with Mother Nature!\n"); return; } if(targ.deathtype=="zap") { bprint(" was electrocuted!\n"); return; } if(targ.deathtype=="chopped") { bprint(" was sliced AND diced!\n"); return; } if (attacker.solid == SOLID_BSP && attacker != world) { bprint (" was squished\n"); return; } if (attacker.classname == "trap_shooter" || attacker.classname == "trap_spikeshooter") { bprint (" was spiked"); if (attacker.enemy.classname == "player" && attacker.enemy != targ) { bprint(" by "); bprint(attacker.enemy.netname); attacker.enemy.frags += 1; } bprint("\n"); return; } if (attacker.classname == "fireball") { bprint (" ate a lavaball\n"); return; } if (attacker.classname == "trigger_changelevel") { bprint (" tried to leave\n"); return; } // in-water deaths rnum = targ.watertype; if (rnum == -3) { if (random() < 0.5) bprint (" takes a nice, deep breath of H2O!\n"); else bprint (" needed gills\n"); return; } else if (rnum == -4) { if (random() < 0.5) bprint (" gulped a load of slime\n"); else bprint (" can't exist on slime alone\n"); return; } else if (rnum == -5) { if (random() < 0.3) bprint (" needs a cold shower\n"); else if (random() < 0.5) bprint (" likes it HOT!\n"); else bprint (" smells like burnt hair\n"); return; } // fell to their death? if (targ.deathtype == "falling") { targ.deathtype = ""; bprint (STR_CHUNKYSALSA); return; } // hell if I know; he's just dead!!! bprint (STR_CEASEDTOFUNCTION); } }; gamecode/hc/h2/combat.hc000066400000000000000000000060041444734033100153110ustar00rootroot00000000000000void(vector org, vector vel, float damage, entity victim) SpawnPuff; float MetalHitSound (float targettype) { if(targettype==THINGTYPE_FLESH) { sound (self, CHAN_WEAPON, "weapons/met2flsh.wav", 1, ATTN_NORM); return TRUE; } else if(targettype==THINGTYPE_WOOD) { sound (self, CHAN_WEAPON, "weapons/met2wd.wav", 1, ATTN_NORM); return TRUE; } else if(targettype==THINGTYPE_METAL) { sound (self, CHAN_WEAPON, "weapons/met2met.wav", 1, ATTN_NORM); return TRUE; } else if(targettype==THINGTYPE_BROWNSTONE||targettype==THINGTYPE_GREYSTONE) { sound (self, CHAN_WEAPON, "weapons/met2stn.wav", 1, ATTN_NORM); return TRUE; } return FALSE; } /* ================ FireMelee ================ */ void FireMelee (float damage_base,float damage_mod,float attack_radius) { vector source; vector org; float damg, backstab; float chance,point_chance; makevectors (self.v_angle); source = self.origin+self.proj_ofs; traceline (source, source + v_forward*64, FALSE, self); if (trace_fraction == 1.0) { traceline (source, source + v_forward*64 - (v_up * 30), FALSE, self); // 30 down if (trace_fraction == 1.0) { traceline (source, source + v_forward*64 + v_up * 30, FALSE, self); // 30 up if (trace_fraction == 1.0) return; } } org = trace_endpos + (v_forward * 4); if (trace_ent.takedamage) { //FIXME:Add multiplier for level and strength if(self.playerclass == CLASS_ASSASSIN && self.weapon == IT_WEAPON1) { if(self.level > 5) /* Pa3PyX: this ability starts at clvl 6 */ { if((trace_ent.flags2 & FL_ALIVE) && !infront_of_ent(self, trace_ent) && random(1, 10) < self.level) { CreateRedFlash(trace_endpos); damage_base*=random(2.5,4); backstab=TRUE; } } } damg = random(damage_mod+damage_base,damage_base); SpawnPuff (org, '0 0 0', damg,trace_ent); T_Damage (trace_ent, self, self, damg); if(!(trace_ent.flags2 & FL_ALIVE) && backstab) { centerprint(self,"Critical Hit Backstab!\n"); AwardExperience(self,trace_ent,10); } if(trace_ent.thingtype==THINGTYPE_FLESH) sound (self, CHAN_WEAPON, "weapons/slash.wav", 1, ATTN_NORM); else sound (self, CHAN_WEAPON, "weapons/hitwall.wav", 1, ATTN_NORM); // Necromancer stands a chance of vampirically stealing health points if (self.playerclass == CLASS_NECROMANCER) { if ((trace_ent.flags & FL_MONSTER) || (trace_ent.flags & FL_CLIENT)) { chance = self.level * .05; if (chance > random()) { point_chance = self.level; point_chance *= random(); if (point_chance < 1) point_chance = 1; sound (self, CHAN_BODY, "weapons/drain.wav", 1, ATTN_NORM); self.health += point_chance; if (self.health>self.max_health) self.health = self.max_health; } } } } else { // hit wall sound (self, CHAN_WEAPON, "weapons/hitwall.wav", 1, ATTN_NORM); WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); WriteByte (MSG_BROADCAST, TE_GUNSHOT); WriteCoord (MSG_BROADCAST, org_x); WriteCoord (MSG_BROADCAST, org_y); WriteCoord (MSG_BROADCAST, org_z); } } gamecode/hc/h2/constant.hc000066400000000000000000000433121444734033100157000ustar00rootroot00000000000000// // constants // float FALSE = 0; float TRUE = 1; float HX_FRAME_TIME = 0.05; float HX_FPS = 20; // edict.flags float FL_FLY = 1; float FL_SWIM = 2; float FL_PUSH = 4; // Object is pushable float FL_CLIENT = 8; // set for all client edicts float FL_INWATER = 16; // for enter / leave water splash float FL_MONSTER = 32; float FL_GODMODE = 64; // player cheat float FL_NOTARGET = 128; // player cheat float FL_ITEM = 256; // extra wide size for bonus items float FL_ONGROUND = 512; // standing on something float FL_PARTIALGROUND = 1024; // not all corners are valid float FL_WATERJUMP = 2048; // player jumping out of water float FL_JUMPRELEASED = 4096; // for jump debouncing float FL_FLASHLIGHT = 8192; // quake 2 thingy float FL_ARTIFACTUSED = 16384; // an artifact was just used float FL_MOVECHAIN_ANGLE = 32768; // when in a move chain, will update the angle float FL_FIRERESIST = 65536; // resistant to fire and heat and lava float FL_FIREHEAL = 131072; // healed by fire, heat, and lava float FL_COLDHEAL = 524288; // healed by freezing float FL_ARCHIVE_OVERRIDE = 1048576; // quake 2 thingy float FL_CLASS_DEPENDENT = 2097152; // model will appear different to each player float FL_SPECIAL_ABILITY1 = 4194304; // has 1st special ability float FL_SPECIAL_ABILITY2 = 8388608; // has 2nd special ability //edict.flags2 //FIXME: Shielded and small may be able to be determined by //other means... float FL2_ADJUST_MON_DAM = 1; //Do more damage to monsters float FL_NODAMAGE = 2; //Special flag put on a missle to make it not do damage- used only by mezzoman float FL_SMALL = 4; //Small enough to be crsuhed underfoot float FL_ALIVE = 8; //Dead or alive. float FL_FAKE_WATER = 16; //Fake water float FL_SUMMONED = 32; //Summoned monster, stops it from precaching float FL_LEDGEHOLD = 64; //Can realistically pull yourself up over ledges, etc. float FL_TORNATO_SAFE = 512; float FL_CHAINED = 2048; //Held by chains float FL2_CROUCHED = 4096; float FL2_CROUCH_TOGGLE = 8192; // edict.drawflags float MLS_MASKIN = 7; // MLS: Model Light Style float MLS_MASKOUT = 248; float MLS_NONE = 0; float MLS_FULLBRIGHT = 1; float MLS_POWERMODE = 2; float MLS_TORCH = 3; float MLS_FIREFLICKER = 4; float MLS_CRYSTALGOLEM = 5; float MLS_ABSLIGHT = 7; float SCALE_TYPE_MASKIN = 24; float SCALE_TYPE_MASKOUT = 231; float SCALE_TYPE_UNIFORM = 0; // Scale X, Y, and Z float SCALE_TYPE_XYONLY = 8; // Scale X and Y float SCALE_TYPE_ZONLY = 16; // Scale Z float SCALE_ORIGIN_MASKIN = 96; float SCALE_ORIGIN_MASKOUT = 159; float SCALE_ORIGIN_CENTER = 0; // Scaling origin at object center float SCALE_ORIGIN_BOTTOM = 32; // Scaling origin at object bottom float SCALE_ORIGIN_TOP = 64; // Scaling origin at object top float DRF_TRANSLUCENT = 128; // Artifact Flags float AFL_CUBE_RIGHT = 1; float AFL_CUBE_LEFT = 2; float AFL_TORCH = 4; float AFL_SUPERHEALTH = 8; // edict.movetype values float MOVETYPE_NONE = 0; // never moves //float MOVETYPE_ANGLENOCLIP = 1; //float MOVETYPE_ANGLECLIP = 2; float MOVETYPE_WALK = 3; // players only float MOVETYPE_STEP = 4; // discrete, not real time unless fall float MOVETYPE_FLY = 5; float MOVETYPE_TOSS = 6; // gravity float MOVETYPE_PUSH = 7; // no clip to world, push and crush float MOVETYPE_NOCLIP = 8; float MOVETYPE_FLYMISSILE = 9; // fly with extra size against monsters float MOVETYPE_BOUNCE = 10; float MOVETYPE_BOUNCEMISSILE = 11; // bounce with extra size and no gravity float MOVETYPE_PUSHPULL = 13; // pushable/pullable object float MOVETYPE_SWIM = 14; // object won't move out of water // particle types float PARTICLETYPE_STATIC = 0; float PARTICLETYPE_GRAV = 1; float PARTICLETYPE_FASTGRAV = 2; float PARTICLETYPE_SLOWGRAV = 3; float PARTICLETYPE_FIRE = 4; float PARTICLETYPE_EXPLODE = 5; float PARTICLETYPE_EXPLODE2 = 6; float PARTICLETYPE_BLOB = 7; float PARTICLETYPE_BLOB2 = 8; float PARTICLETYPE_RAIN = 9; float PARTICLETYPE_C_EXPLODE = 10; float PARTICLETYPE_C_EXPLODE2 = 11; float PARTICLETYPE_SPIT = 12; float PARTICLETYPE_FIREBALL = 13; float PARTICLETYPE_ICE = 14; float PARTICLETYPE_SPELL = 15; // Hexen hull constants float HULL_IMPLICIT = 0; //Choose the hull based on bounding box- like in Quake float HULL_POINT = 1; //0 0 0, 0 0 0 float HULL_PLAYER = 2; //'-16 -16 0', '16 16 56' float HULL_SCORPION = 3; //'-24 -24 -20', '24 24 20' float HULL_CROUCH = 4; //'-16 -16 0', '16 16 28' //Next 2 clip though world? float HULL_HYDRA = 5; //'-28 -28 -24', '28 28 24' float HULL_GOLEM = 6; //???,??? // Keep around old constants until all references are removed float HULL_OLD = 0; float HULL_SMALL = 1; float HULL_NORMAL = 2; float HULL_BIG = 3; // edict.solid values float SOLID_NOT = 0; // no interaction with other objects float SOLID_TRIGGER = 1; // touch on edge, but not blocking float SOLID_BBOX = 2; // touch on edge, block float SOLID_SLIDEBOX = 3; // touch on edge, but not an onground float SOLID_BSP = 4; // bsp clip, touch on edge, block float SOLID_PHASE = 5; // will interact with all objects except entities with FL_MONSTER & FL_CLIENT - those it will pass through // range values float RANGE_MELEE = 0; float RANGE_NEAR = 1; float RANGE_MID = 2; float RANGE_FAR = 3; // deadflag values float DEAD_NO = 0; float DEAD_DYING = 1; float DEAD_DEAD = 2; float DEAD_RESPAWNABLE = 3; // takedamage values float DAMAGE_NO = 0; // Entity cannot be hurt float DAMAGE_YES = 1; // Can be hurt float DAMAGE_NO_GRENADE = 2; // Will not trigger a grenade to explode // use inventory flags to show which item is the current item float INV_NONE = 0; float INV_TORCH = 1; float INV_HP_BOOST = 2; float INV_SUPER_HP_BOOST = 3; float INV_MANA_BOOST = 4; float INV_TELEPORT = 5; float INV_TOME = 6; float INV_SUMMON = 7; float INV_INVISIBILITY = 8; float INV_GLYPH = 9; float INV_HASTE = 10; float INV_BLAST = 11; float INV_POLYMORPH = 12; float INV_FLIGHT = 13; float INV_CUBEOFFORCE = 14; float INV_INVINCIBILITY = 15; float ARTIFACT_TORCH = 1; float ARTIFACT_HP_BOOST = 2; float ARTIFACT_SUPER_HP_BOOST = 3; float ARTIFACT_MANA_BOOST = 4; float ARTIFACT_TELEPORT = 5; float ARTIFACT_TOME = 6; float ARTIFACT_SUMMON = 7; float ARTIFACT_INVISIBILITY = 8; float ARTIFACT_GLYPH = 9; float ARTIFACT_HASTE = 10; float ARTIFACT_BLAST = 11; float ARTIFACT_POLYMORPH = 12; float ARTIFACT_FLIGHT = 13; float ARTIFACT_CUBEOFFORCE = 14; float ARTIFACT_INVINCIBILITY = 15; // Use ring flags to show which rings hero carries float RING_NONE = 0; float RING_FLIGHT = 1; float RING_WATER = 2; float RING_REGENERATION = 4; float RING_TURNING = 8; // Use artifact flags to show which artifacts are in use float ART_NONE = 0; float ART_HASTE = 1; float ART_INVINCIBILITY = 2; float ART_TOMEOFPOWER = 4; float ART_INVISIBILITY = 8; float ARTFLAG_FROZEN = 128; float ARTFLAG_STONED = 256; float ARTFLAG_DIVINE_INTERVENTION = 512; // Gobal skin textures float GLOBAL_SKIN_STONE = 100; float GLOBAL_SKIN_ICE = 101; // Player Classes float CLASS_NONE = 0; float CLASS_PALADIN = 1; float CLASS_CRUSADER = 2; float CLASS_NECROMANCER = 3; float CLASS_ASSASSIN = 4; // Monster Classes float CLASS_GRUNT = 1; float CLASS_HENCHMAN = 2; float CLASS_LEADER = 3; float CLASS_BOSS = 4; float CLASS_FINAL_BOSS = 5; float MAX_HEALTH = 200; // Player Mode float MODE_NORMAL = 0; // normal play mode float MODE_CAMERA = 1; // player is a camera right now float AS_STRAIGHT = 1; float AS_SLIDING = 2; float AS_MELEE = 3; float AS_MISSILE = 4; float AS_WAIT = 5; float AS_FERRY = 6; // Generic Weapon Names float IT_WEAPON1 = 4096; float IT_WEAPON2 = 1; float IT_WEAPON3 = 2; float IT_WEAPON4 = 4; float IT_TESTWEAP = 8; float IT_WEAPON4_1 = 16; // First half of weapon float IT_WEAPON4_2 = 32; // Second half of weapon // paladin weapons //float IT_GAUNTLETS = 4096; // items /* float IT_AXE = 4096; float IT_SHOTGUN = 1; float IT_SUPER_SHOTGUN = 2; float IT_NAILGUN = 4; float IT_SUPER_NAILGUN = 8; float IT_GRENADE_LAUNCHER = 16; float IT_ROCKET_LAUNCHER = 32; float IT_LIGHTNING = 64; float IT_EXTRA_WEAPON = 128; */ //float IT_ARMOR1 = 8192; //float IT_ARMOR2 = 16384; //float IT_ARMOR3 = 32768; float IT_SUPERHEALTH = 65536; float IT_INVISIBILITY = 524288; //float IT_INVULNERABILITY = 1048576; //float IT_SUIT = 2097152; //float IT_QUAD = 4194304; // rings - amount of time they work float FLIGHT_TIME = 30; float WATER_TIME = 30; float ABSORPTION_TIME = 30; float REGEN_TIME = 30; float TURNING_TIME = 30; // artifacts - amount of time they work //float HASTE_TIME = 15; float TOME_TIME = 30; float RESPAWN_TIME = 30; // weapon damage values float WEAPON1_BASE_DAMAGE = 12; float WEAPON1_ADD_DAMAGE = 12; float WEAPON1_PWR_BASE_DAMAGE = 30; float WEAPON1_PWR_ADD_DAMAGE = 20; float WEAPON1_PUSH = 5; // glyph of the ancients float GLYPH_BASE_DAMAGE = 100; float GLYPH_ADD_DAMAGE = 20; // Modifier for HASTE //float HASTE_MOD = 2; float BLAST_RADIUS = 200; float BLASTDAMAGE = 2; // Damage values for attacks from monsters //float DMG_ARCHER_PUNCH = 4; float DMG_MUMMY_PUNCH = 8; //float DMG_MUMMY_BITE = 2; //Thing Types float THINGTYPE_GREYSTONE = 1; float THINGTYPE_WOOD = 2; float THINGTYPE_METAL = 3; float THINGTYPE_FLESH = 4; float THINGTYPE_FIRE = 5; float THINGTYPE_CLAY = 6; float THINGTYPE_LEAVES = 7; float THINGTYPE_HAY = 8; float THINGTYPE_BROWNSTONE = 9; float THINGTYPE_CLOTH = 10; float THINGTYPE_WOOD_LEAF = 11; float THINGTYPE_WOOD_METAL = 12; float THINGTYPE_WOOD_STONE = 13; float THINGTYPE_METAL_STONE = 14; float THINGTYPE_METAL_CLOTH = 15; float THINGTYPE_WEBS = 16; float THINGTYPE_GLASS = 17; float THINGTYPE_ICE = 18; float THINGTYPE_CLEARGLASS = 19; float THINGTYPE_REDGLASS = 20; // point content values float CONTENT_EMPTY = -1; float CONTENT_SOLID = -2; float CONTENT_WATER = -3; float CONTENT_SLIME = -4; float CONTENT_LAVA = -5; float CONTENT_SKY = -6; float STATE_TOP = 0; float STATE_BOTTOM = 1; float STATE_UP = 2; float STATE_DOWN = 3; float STATE_MOVING = 4; vector VEC_ORIGIN = '0 0 0'; vector VEC_HULL_MIN = '-16 -16 -24'; vector VEC_HULL_MAX = '16 16 32'; //Temp- because player models origins are at feet, //Above values raise them 12 above the floor! //But what about monsters using this Hull size?? //vector VEC_HULL_MIN = '-16 -16 0'; //vector VEC_HULL_MAX = '16 16 56'; vector VEC_HULL2_MIN = '-32 -32 -24'; vector VEC_HULL2_MAX = '32 32 64'; // protocol bytes float SVC_TEMPENTITY = 23; float SVC_KILLEDMONSTER = 27; float SVC_FOUNDSECRET = 28; float SVC_INTERMISSION = 30; float SVC_FINALE = 31; float SVC_CDTRACK = 32; float SVC_SELLSCREEN = 33; float SVC_SET_VIEW_FLAGS = 40; float SVC_CLEAR_VIEW_FLAGS = 41; float SVC_SET_VIEW_TINT = 46; // Client Effects float CE_RAIN = 1; float CE_FOUNTAIN = 2; float CE_QUAKE = 3; float CE_WHITE_SMOKE = 4; float CE_BLUESPARK = 5; float CE_YELLOWSPARK = 6; float CE_SM_CIRCLE_EXP = 7; float CE_BG_CIRCLE_EXP = 8; float CE_SM_WHITE_FLASH = 9; float CE_WHITE_FLASH = 10; float CE_YELLOWRED_FLASH = 11; float CE_BLUE_FLASH = 12; float CE_SM_BLUE_FLASH = 13; float CE_RED_FLASH = 14; float CE_SM_EXPLOSION = 15; float CE_LG_EXPLOSION = 16; float CE_FLOOR_EXPLOSION = 17; float CE_RIDER_DEATH = 18; float CE_BLUE_EXPLOSION = 19; float CE_GREEN_SMOKE = 20; float CE_GREY_SMOKE = 21; float CE_RED_SMOKE = 22; float CE_SLOW_WHITE_SMOKE = 23; float CE_REDSPARK = 24; float CE_GREENSPARK = 25; float CE_TELESMK1 = 26; float CE_TELESMK2 = 27; float CE_ICE_HIT = 28;// icehit.spr 0-5 float CE_MEDUSA_HIT = 29;// medhit.spr 0-6 float CE_MEZZO_REFLECT = 30;// mezzoref.spr 0-5 float CE_FLOOR_EXPLOSION2 = 31;// flrexpl2.spr 0-19 float CE_XBOW_EXPLOSION = 32;// xbowexpl.spr 0-16 float CE_NEW_EXPLOSION = 33;// gen_expl.spr 0-13 float CE_MAGIC_MISSILE_EXPLOSION = 34;// mm_expld.spr float CE_GHOST = 35;// ghost.spr- translucent float CE_BONE_EXPLOSION = 36;// bonexpld.spr float CE_REDCLOUD = 37;// rcloud.spr float CE_TELEPORTERPUFFS = 38; float CE_TELEPORTERBODY = 39; float CE_BONESHARD = 40; float CE_BONESHRAPNEL = 41; // Temporary entities float TE_SPIKE = 0; float TE_SUPERSPIKE = 1; float TE_GUNSHOT = 2; float TE_EXPLOSION = 3; float TE_TAREXPLOSION = 4; float TE_LIGHTNING1 = 5; float TE_LIGHTNING2 = 6; float TE_WIZSPIKE = 7; float TE_KNIGHTSPIKE = 8; float TE_LIGHTNING3 = 9; float TE_LAVASPLASH = 10; float TE_TELEPORT = 11; float TE_STREAM_CHAIN = 25; float TE_STREAM_SUNSTAFF1 = 26; float TE_STREAM_SUNSTAFF2 = 27; float TE_STREAM_LIGHTNING = 28; float TE_STREAM_COLORBEAM = 29; float TE_STREAM_ICECHUNKS = 30; float TE_STREAM_GAZE = 31; float TE_STREAM_FAMINE = 32; // Stream flags float STREAM_ATTACHED = 16; float STREAM_TRANSLUCENT = 32; // sound channels // channel 0 never willingly overrides // other channels (1-7) always override a playing sound on that channel float CHAN_AUTO = 0; float CHAN_WEAPON = 1; float CHAN_VOICE = 2; float CHAN_ITEM = 3; float CHAN_BODY = 4; float ATTN_NONE = 0; float ATTN_NORM = 1; float ATTN_IDLE = 2; float ATTN_STATIC = 3; // update types //float UPDATE_GENERAL = 0; //float UPDATE_STATIC = 1; //float UPDATE_BINARY = 2; //float UPDATE_TEMP = 3; // entity effects float EF_BRIGHTFIELD = 1; float EF_MUZZLEFLASH = 2; float EF_BRIGHTLIGHT = 4; float EF_TORCHLIGHT = 6; float EF_DIMLIGHT = 8; float EF_DARKLIGHT = 16; float EF_DARKFIELD = 32; float EF_LIGHT = 64; float EF_NODRAW = 128; // messages float MSG_BROADCAST = 0; // unreliable to all float MSG_ONE = 1; // reliable to one (msg_entity) float MSG_ALL = 2; // reliable to all float MSG_INIT = 3; // write to the init string //float STEP_HEIGHT = 18; // Max step height // monster AI states float AI_DECIDE = 0; // An action was just finished - time to decide what to do float AI_STAND = 1; // Standing guard float AI_WALK = 2; // Walking float AI_CHARGE = 4; // Charging enemy float AI_WANDER = 8; // Wandering around mindlessly float AI_MELEE_ATTACK = 16; // float AI_MISSILE_ATTACK = 32; // float AI_MISSILE_REATTACK = 64; // Attacking again from attack stance (archer) float AI_PAIN = 128; // Monster has only 1 type of pain float AI_PAIN_CLOSE = 256; // Pain when close to enemy float AI_PAIN_FAR = 512; // Pain when far from enemy float AI_DEAD = 1024; // float AI_TURNLOOK = 2048; // Turning to look for enemy float AI_DEAD_GIB = 4096; // Can be gibbed when killed float AI_DEAD_TWITCH = 8192; // Twitches while dead // Return values for AdvanceFrame() float AF_NORMAL = 0; float AF_BEGINNING = 1; float AF_END = 2; float CHUNK_MAX = 30; // Max number of chunks (models) that can be alive at one time float MAX_LEVELS = 10; // server flags //float SFL_EPISODE_1 = 1; //float SFL_EPISODE_2 = 2; //float SFL_EPISODE_3 = 4; //float SFL_EPISODE_4 = 8; float SFL_NEW_UNIT = 16; float SFL_NEW_EPISODE = 32; // = 64; // = 128; float SFL_CROSS_TRIGGER_1 = 256; float SFL_CROSS_TRIGGER_2 = 512; float SFL_CROSS_TRIGGER_3 = 1024; float SFL_CROSS_TRIGGER_4 = 2048; float SFL_CROSS_TRIGGER_5 = 4096; float SFL_CROSS_TRIGGER_6 = 8192; float SFL_CROSS_TRIGGER_7 = 16384; float SFL_CROSS_TRIGGER_8 = 32768; float SFL_CROSS_TRIGGERS = 65280; //float attck_cnt; float WF_NORMAL_ADVANCE = 0; // States when using advanceweaponframe float WF_CYCLE_STARTED = 1; float WF_CYCLE_WRAPPED = 2; float WF_LAST_FRAME = 3; float WORLDTYPE_CASTLE = 0; float WORLDTYPE_EGYPT = 1; float WORLDTYPE_MESO = 2; float WORLDTYPE_ROMAN = 3; //Spawnflags for monster spawners float IMP = 1; float ARCHER = 2; float WIZARD = 4; float SCORPION = 8; float SPIDER = 16; float ONDEATH = 32; float QUIET = 64; float TRIGGERONLY = 128; //spawnflag for all monsters float JUMP = 4; //Gives monster the ability to jump float PLAY_DEAD = 8; //Makes a monster play dead at start float NO_DROP = 32; //Keeps them from dropping to the ground at spawntime //spawnflag for items, weapons, artifacts float FLOATING = 1; //Keeps them from dropping to the ground at spawntime //Spawnflags for barrels float BARREL_DOWNHILL = 1; float BARREL_NO_DROP = 2; float ON_SIDE = 4; float BARREL_SINK = 8; float BARREL_UNBREAKABLE = 16; float BARREL_NORMAL = 32; float BARREL_EXPLODING = 64; float BARREL_INDESTRUCTIBLE = 128; //For func_rotate float GRADUAL = 32; float TOGGLE_REVERSE = 64; float KEEP_START = 128; float NO_RESPAWN = 0; // For the spawning of artifacts float RESPAWN = 1; float RING_REGENERATION_MAX = 150; // Number of health points ring gives you back float RING_FLIGHT_MAX = 60; // Number of seconds you can fly float RING_WATER_MAX = 60; // Number of seconds you can stay under water float RING_TURNING_MAX = 60; // Number of seconds you can turn missiles float SVC_SETVIEWPORT = 5; // Net.Protocol 0x05- for camera float SVC_SETVIEWANGLES = 10; // Net.Protocol 0x0A- for camera //act_states - for player anim float ACT_STAND = 0; float ACT_RUN = 1; float ACT_SWIM_FLY = 2; float ACT_ATTACK = 3; float ACT_PAIN = 4; float ACT_JUMP = 5; float ACT_CROUCH_STAND = 6; float ACT_CROUCH_MOVE = 7; float ACT_DEAD = 8; float ACT_DECAP = 9; gamecode/hc/h2/corpse.hc000066400000000000000000000045431444734033100153450ustar00rootroot00000000000000void corpseblink (void) { self.think = corpseblink; thinktime self : 0.1; self.scale -= 0.10; if (self.scale < 0.10) remove(self); } void init_corpseblink (void) { CreateYRFlash(self.origin); self.drawflags (+) DRF_TRANSLUCENT | SCALE_TYPE_ZONLY | SCALE_ORIGIN_BOTTOM; corpseblink(); } void() Spurt = { float bloodleak; makevectors(self.angles); bloodleak=rint(random(3,8)); SpawnPuff (self.origin+v_forward*24+'0 0 -22', '0 0 -5'+ v_forward*random(20,40), bloodleak,self); sound (self, CHAN_AUTO, "misc/decomp.wav", 0.3, ATTN_NORM); if (self.lifetime < time||self.watertype==CONTENT_LAVA) T_Damage(self,world,world,self.health); else { self.think=Spurt; thinktime self : random(0.5,6.5); } }; void () CorpseThink = { self.think = CorpseThink; thinktime self : 3; if (self.watertype==CONTENT_LAVA) // Corpse fell in lava T_Damage(self,self,self,self.health); else if (self.lifetime < time) // Time is up, begone with you init_corpseblink(); }; /* * This uses entity.netname to hold the head file (for CorpseDie()) * hack so that we don't have to set anything outside this function. */ void()MakeSolidCorpse = { vector newmaxs; // Make a gibbable corpse, change the size so we can jump on it //Won't be necc to pass headmdl once everything has it's .headmodel //value set in spawn self.th_die = chunk_death; self.touch = obj_push; self.health = random(10,25); self.takedamage = DAMAGE_YES; self.solid = SOLID_PHASE; self.experience_value = 0; if(self.classname!="monster_hydra") self.movetype = MOVETYPE_STEP;//Don't get in the way if(!self.mass) self.mass=1; //To fix "player stuck" probem newmaxs=self.maxs; if(newmaxs_z>5) newmaxs_z=5; setsize (self, self.mins,newmaxs); if(self.flags&FL_ONGROUND) self.velocity='0 0 0'; self.flags(-)FL_MONSTER; self.controller = self; self.onfire = FALSE; pitch_roll_for_slope('0 0 0'); if ((self.decap) && (self.classname == "player")) { if (deathmatch||teamplay) self.lifetime = time + random(20,40); // decompose after 40 seconds else self.lifetime = time + random(10,20); // decompose after 20 seconds self.owner=self; self.think=Spurt; thinktime self : random(1,4); } else { self.lifetime = time + random(10,20); // disappear after 20 seconds self.think=CorpseThink; thinktime self : 0; } }; gamecode/hc/h2/crossbow.hc000066400000000000000000000251101444734033100157040ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\weapons\crossbow\final\crossbow.hc ============================================================================== */ // For building the model $cd Q:\art\models\weapons\crossbow\final $origin 0 0 0 $base base skin $skin skin $flags 0 // $frame select1 select2 select3 select4 select5 $frame select6 select7 select8 select9 select10 $frame select11 select12 select13 select14 select15 // $frame shoot1 shoot2 shoot3 shoot4 shoot5 $frame shoot6 shoot7 shoot8 shoot9 shoot10 $frame shoot11 shoot12 shoot13 shoot14 shoot15 $frame shoot16 shoot17 shoot18 shoot19 void flashspin () { if(self.lifetime9) self.frame=0; } self.angles=vectoangles(self.velocity); self.think=ArrowFlyThink; thinktime self : 0.05; } void ArrowSound (void) { //attn_static instead? // sound(self,CHAN_BODY,"assassin/arrowfly.wav",1,ATTN_NORM); self.think=ArrowFlyThink; thinktime self : 0; } void FlamingArrowThink (void) { float waterornot; waterornot=pointcontents(self.origin); if(waterornot==CONTENT_WATER||waterornot==CONTENT_SLIME) { sound (self, CHAN_WEAPON, "misc/fout.wav", 1, ATTN_NORM); DeathBubbles(1); setmodel(self,"models/NFarrow.mdl"); } ArrowSound(); } void ArrowThink (void) { vector dir; dir=normalize(self.velocity); traceline(self.origin,self.origin+dir*1000,FALSE,self); if(!trace_ent.takedamage) HomeThink(); self.angles=vectoangles(self.velocity); if(self.classname=="bolt") self.think=ArrowSound; else self.think=FlamingArrowThink; thinktime self : 0; } void(float offset, float powered_up) FireCB_Bolt = { local entity missile; makevectors(self.v_angle); missile=spawn(); missile.owner=self; missile.solid=SOLID_BBOX; missile.hull=HULL_POINT; missile.health=3; if(deathmatch) offset*=.333; if(powered_up) { missile.frags=TRUE; missile.thingtype=THINGTYPE_METAL; missile.movetype=MOVETYPE_FLYMISSILE; missile.classname="flaming arrow"; setmodel(missile,"models/flaming.mdl"); missile.dmg=40; missile.drawflags(+)MLS_FIREFLICKER; missile.th_die=MultiExplode; } else { missile.thingtype=THINGTYPE_WOOD; missile.movetype=MOVETYPE_FLYMISSILE; missile.classname="bolt"; setmodel(missile,"models/arrow.mdl"); missile.th_die=chunk_death; } missile.touch=CB_BoltHit; missile.speed=random(700,1200); missile.o_angle=missile.velocity=normalize(v_forward)*missile.speed+v_right*offset; missile.angles=vectoangles(missile.velocity); missile.ideal_yaw=TRUE; missile.turn_time = 0; missile.veer=0; missile.think= ArrowThink; thinktime missile : 0; missile.lifetime=time+0.2; setsize(missile,'0 0 0','0 0 0'); setorigin(missile,self.origin+self.proj_ofs+v_forward*8); }; void()crossbow_fire; void crossbow_idle(void) { self.th_weapon=crossbow_idle; self.weaponframe=$shoot19; } void crossbow_fire (void) { // Pa3PyX: rewrote the code for framerate independence local float advance_frames; local float cnt_frame; local float attackframe1_passed; local float attackframe2_passed; local float attackframe3_passed; local float arate_factor; // Did the delay from previous attack expire yet? if ((time >= self.attack_finished) || (self.ltime > 0)) { if (self.ltime <= 0) self.ltime = time; // Tomed xbow: 3 shots per second; untomed: 2 shots per second // (xbow has 20 frames, so unscaled animation is 1 second long) if (self.artifact_active & ART_TOMEOFPOWER) arate_factor = 3.0; else arate_factor = 2.0; // Animation loop factor advance_frames = rint(arate_factor * (time - self.ltime) / HX_FRAME_TIME); if (advance_frames >= 1) { cnt_frame = 0; attackframe1_passed = attackframe2_passed = attackframe3_passed = FALSE; // Advance frames while ((cnt_frame < advance_frames) && (self.wfs != WF_LAST_FRAME)) { self.wfs = advanceweaponframe($shoot1,$shoot18); self.weaponframe_cnt += 1; // Did we go over any attack frames? if (self.weaponframe_cnt == 2) attackframe1_passed = TRUE; if (self.weaponframe_cnt == 3) attackframe2_passed = TRUE; if (self.weaponframe_cnt == 4) attackframe3_passed = TRUE; cnt_frame += 1; } if (self.wfs == WF_LAST_FRAME) { // End of animation, clean up and exit self.wfs = WF_NORMAL_ADVANCE; self.weaponframe_cnt = 0; self.ltime = -1; self.attack_finished = time; self.th_weapon = crossbow_idle; } else { self.ltime = time; self.th_weapon=crossbow_fire; } // Attack frames were encountered in frame advance -- // perform attack actions if (self.artifact_active & ART_TOMEOFPOWER) { if (attackframe1_passed) { sound(self,CHAN_WEAPON,"assassin/firefblt.wav",1,ATTN_NORM); self.bluemana-=10; FireCB_Bolt(0,TRUE); } if (attackframe2_passed) { FireCB_Bolt(-100,TRUE); FireCB_Bolt(100,TRUE); } if (attackframe3_passed) { FireCB_Bolt(-200,TRUE); FireCB_Bolt(200,TRUE); } } else { if (attackframe1_passed) { sound(self,CHAN_WEAPON,"assassin/firebolt.wav",1,ATTN_NORM); self.bluemana-=3; FireCB_Bolt(0,FALSE); } if (attackframe2_passed) FireCB_Bolt(-100,FALSE); if (attackframe3_passed) FireCB_Bolt(100,FALSE); } } else self.th_weapon = crossbow_fire; } else self.th_weapon = crossbow_fire; thinktime self: 0; } void crossbow_select (void) { //selection sound? self.wfs = advanceweaponframe($select15,$select1); self.weaponmodel = "models/crossbow.mdl"; self.th_weapon=crossbow_select; if (self.weaponframe==$select1) { self.attack_finished = time - 1; // Pa3PyX self.ltime = -1; self.weaponframe_cnt = 0; self.wfs = WF_NORMAL_ADVANCE; crossbow_idle(); } } void crossbow_deselect (void) { self.wfs = advanceweaponframe($select1,$select15); self.th_weapon=crossbow_deselect; if (self.wfs==WF_CYCLE_WRAPPED) W_SetCurrentAmmo(); } gamecode/hc/h2/cube.hc000066400000000000000000000117731444734033100147730ustar00rootroot00000000000000float cube_distance = 500; void CubeDie(void) { self.owner.artifact_flags(-)self.artifact_flags; remove(self); } float cube_find_target(void) { entity item; item = findradius(self.origin, cube_distance); while (item) { if (((item.flags & FL_MONSTER && item.controller!=self.controller) || (item.classname == "player" && deathmatch == 1)) && item.health > 0) { tracearea (self.origin,item.origin,self.mins,self.maxs,FALSE,self); if (trace_ent == item) { if (!item.effects & EF_NODRAW) { self.enemy = item; return TRUE; } } } item = item.chain; } return FALSE; } void do_fireball(vector offset); vector CubeDirection[6] = { '90 0 0', '-90 0 0', '0 90 0', '0 -90 0', '0 0 90', '0 0 -90' }; void cube_fire(void) { // float RanVal; float Distance; entity temp; if (time > self.monster_duration || self.owner.health <= 0 || self.shot_cnt >= 10) { CubeDie(); return; } if (!self.enemy) { self.cnt += 1; if (self.cnt > 5) { cube_find_target(); self.cnt = 0; } } if (self.enemy) { if (self.enemy.health <= 0) { self.enemy = world; //self.drawflags (+) DRF_TRANSLUCENT; } } if (self.enemy) { if (random() < .7) { Distance = vlen(self.origin - self.enemy.origin); if (Distance > cube_distance*2) { self.enemy = world; //self.drawflags (+) DRF_TRANSLUCENT; } else { // Got to do this otherwise tracearea sees right through you temp = self.owner; self.owner = self; tracearea (self.origin,self.enemy.origin,self.mins,self.maxs,FALSE,self); if (trace_ent == self.enemy) { self.adjust_velocity = CubeDirection[random(0,5)]; self.abslight = 1; self.shot_cnt += 1; do_fireball('0 0 0'); } else { self.enemy = world; //self.drawflags (+) DRF_TRANSLUCENT; } self.owner = temp; } } } } void cube_rotate(void) { vector NewOffset; NewOffset = concatv(self.adjust_velocity,'5 5 5'); self.adjust_velocity -= NewOffset; self.v_angle += NewOffset; } vector CubeFollowRate = '14 14 14'; vector CubeAttackRate = '3 3 3'; void CubeThinkerB(void) { vector NewSpot; float Distance; thinktime self : 0.05; if (!self.owner.flags2 & FL_ALIVE) { CubeDie(); return; } if (self.adjust_velocity == '0 0 0') { cube_fire(); if (self.adjust_velocity == '0 0 0') { if (random() < 0.02) { self.adjust_velocity = CubeDirection[random(0,5)]; } } } cube_rotate(); if (self.abslight > .1) self.abslight -= 0.1; self.angles = self.owner.angles + self.v_angle; self.count += random(4,6); if (self.count > 360) { self.count -= 360; } Distance = vlen(self.origin - self.owner.origin); if (Distance > cube_distance) { self.enemy = world; //self.drawflags (+) DRF_TRANSLUCENT; } if (self.enemy != world) { NewSpot = self.enemy.origin + self.enemy.view_ofs; if (self.artifact_flags & AFL_CUBE_LEFT) { NewSpot += (cos(self.count) * 40 * '1 0 0') + (sin(self.count) * 40 * '0 1 0'); } else { NewSpot += (sin(self.count) * 40 * '1 0 0') + (cos(self.count) * 40 * '0 1 0'); } self.movedir_z += random(10,12); if (self.movedir_z > 360) { self.movedir_z -= 360; } NewSpot_z += sin(self.movedir_z) * 10; NewSpot = self.origin + concatv(NewSpot - self.origin, CubeAttackRate); } else { makevectors(self.owner.v_angle); if (self.artifact_flags & AFL_CUBE_LEFT) { NewSpot = self.owner.origin + self.owner.view_ofs + '0 0 10' + v_factor('40 60 0'); } else { NewSpot = self.owner.origin + self.owner.view_ofs + '0 0 10' + v_factor('-40 60 0'); } self.movedir_z += random(10,12); if (self.movedir_z > 360) { self.movedir_z -= 360; } NewSpot += (v_right * cos(self.count) * 15) + (v_up * sin(self.count) * 15) + (v_forward * sin(self.movedir_z) * 15); NewSpot = self.origin + concatv(NewSpot - self.origin, CubeFollowRate); } setorigin(self,NewSpot); } void UseCubeOfForce(void) { entity cube; if ((self.artifact_flags & AFL_CUBE_LEFT) && (self.artifact_flags & AFL_CUBE_RIGHT)) { // Already got two running return; } cube = spawn(); cube.owner = self; cube.controller = self; cube.solid = SOLID_SLIDEBOX; cube.movetype = MOVETYPE_NOCLIP;//MOVETYPE_FLY; cube.flags (+) FL_FLY | FL_NOTARGET; setorigin (cube, cube.owner.origin); setmodel (cube, "models/cube.mdl"); setsize (cube, '-5 -5 -5', '5 5 5'); cube.classname = "cube_of_force"; cube.health = 10; cube.dmg = -1; if (self.artifact_flags & AFL_CUBE_LEFT) { self.artifact_flags (+) AFL_CUBE_RIGHT; cube.artifact_flags (+) AFL_CUBE_RIGHT; } else { self.artifact_flags (+) AFL_CUBE_LEFT; cube.artifact_flags (+) AFL_CUBE_LEFT; } cube.think = CubeThinkerB; cube.th_die = CubeDie; thinktime cube : 0.01; cube.monster_duration = time + 45; cube.shot_cnt = 0; cube.movedir = '100 100 0'; cube.count = random(360); self.movedir_z = random(360); // cube.drawflags (+) DRF_TRANSLUCENT; cube.drawflags (+) MLS_ABSLIGHT; cube.abslight = .1; self.cnt_cubeofforce -= 1; } gamecode/hc/h2/damage.hc000066400000000000000000000551631444734033100152740ustar00rootroot00000000000000void() T_MissileTouch; void() info_player_start; void necromancer_sphere(entity ent); void crusader_sphere(entity ent); void() monster_death_use; void()player_pain; void()PlayerDie; void MonsterDropStuff(void); void Use_TeleportCoin(void); void UseInvincibility(void); void Use_TomeofPower(void); void use_super_healthboost(); float ClassArmorProtection[16] = { // Paladin Armor Protection .05, // AMULET .10, // BRACERS .25, // BREASTPLATE .15, // HELMET // Crusader Armor Protection .15, // AMULET .05, // BRACER .10, // BREASTPLATE .25, // HELMET // Necromancer Armor Protection .25, // AMULET .15, // BRACER .05, // BREASTPLATE .10, // HELMET // Assassin Armor Protection .10, // AMULET .15, // BRACER .25, // BREASTPLATE .05 // HELMET }; //============================================================================ /* ============ CanDamage Returns true if the inflictor can directly damage the target. Used for explosions and melee attacks. ============ */ float(entity targ, entity inflictor) CanDamage = { // bmodels need special checking because their origin is 0,0,0 vector inflictor_org,targ_org,ofs; float targ_rad,loop_cnt; if(inflictor.flags2&FL_ALIVE) inflictor_org = inflictor.origin+inflictor.proj_ofs; else inflictor_org = (inflictor.absmin+inflictor.absmax)*0.5; targ_org=(targ.absmin+targ.absmax)*0.5; // targ_rad=targ.maxs_x; targ_rad=15; if (targ.movetype == MOVETYPE_PUSH) { traceline(inflictor_org, targ_org, TRUE, self); if (trace_fraction == 1) return TRUE; if (trace_ent == targ) return TRUE; return FALSE; } ofs='0 0 0'; loop_cnt=5; while(loop_cnt) { if(loop_cnt!=5) { if(loop_cnt<3) ofs_x=targ_rad*-1; else ofs_x=targ_rad; if(loop_cnt==3||loop_cnt==2) ofs_y=targ_rad*-1; else ofs_y=targ_rad; } traceline(inflictor_org, targ_org + ofs, TRUE, self); if (trace_fraction == 1) return TRUE; loop_cnt-=1; } // dprintv("Can't damage from %s",inflictor_org); // dprintv(" to %s\n",targ_org); return FALSE; }; entity FindExpLeader() { entity lastent, leader; float top_exp; lastent=nextent(world); num_players=0; while(lastent) { if(lastent.classname=="player") { num_players+=1; if(lastent.experience>top_exp) { leader=lastent; top_exp=leader.experience; } } lastent=find(lastent,classname,"player"); } return leader; } float Pal_DivineIntervention(void) { float chance; if (self.level < 6) return(FALSE); chance = self.level * .02; if (chance > .20) chance = .20; if (chance < random()) return(FALSE); centerprint (self,"Your God has saved your mortal body!"); self.health = self.max_health; self.cnt_teleport += 1; Use_TeleportCoin(); self.cnt_invincibility += 1; UseInvincibility (); self.invincible_time = time + 5; self.cnt_tome += 1; Use_TomeofPower (); self.artifact_active(+)ARTFLAG_DIVINE_INTERVENTION; self.divine_time = time + HX_FRAME_TIME; sound (self, CHAN_BODY, "paladin/devine.wav", 1, ATTN_NORM); return(TRUE); } /* ============ Killed ============ */ void(entity targ, entity attacker, entity inflictor) Killed = { entity oself; float exp_bonus; oself = self; self = targ; if(!self.flags2&FL_ALIVE) if (self.movetype == MOVETYPE_PUSH || self.movetype == MOVETYPE_NONE) { // doors, triggers, etc if(self.th_die) self.th_die(); self=oself; return; } self.flags2(-)FL_ALIVE; self.touch = self.th_pain = SUB_Null; if (attacker.classname == "player") { if ((attacker.playerclass==CLASS_NECROMANCER) && (attacker.level >= 3)) { if ((targ.flags & FL_MONSTER) || (targ.flags & FL_CLIENT)) necromancer_sphere (attacker); } // else if ((attacker.playerclass==CLASS_CRUSADER) && (attacker.level >= 3)) // Pa3PyX: this is supposed to be a level 6 ability, // according to the manual, not level 3. else if ((attacker.playerclass == CLASS_CRUSADER) && (attacker.level >= 6)) { if ((targ.flags & FL_MONSTER) || (targ.flags & FL_CLIENT)) crusader_sphere (attacker); } } //Check for decapitation death self.movedir='0 0 0'; if(self.model!="models/sheep.mdl"&&self.deathtype!="teledeath"&&self.deathtype!="teledeath2"&&self.deathtype!="teledeath3"&&self.deathtype != "teledeath4") if (inflictor.classname=="ax_blade"|| (inflictor.classname=="player"&& ( (attacker.playerclass==CLASS_ASSASSIN&&attacker.weapon==IT_WEAPON1)|| (attacker.playerclass==CLASS_PALADIN&&attacker.weapon!=IT_WEAPON4)|| (attacker.playerclass==CLASS_NECROMANCER&&attacker.weapon==IT_WEAPON1) ) ) ) if(random()<0.3||self.classname=="monster_medusa") self.decap=2; else self.decap=TRUE; else if(inflictor.classname!="player"&&vlen(inflictor.origin-self.origin+self.view_ofs)<=17&&self.health>=-40&&self.health<-10) if(random()<0.4) { self.movedir=normalize(self.origin+self.view_ofs-inflictor.origin); self.decap=2; } if(self.skin==GLOBAL_SKIN_STONE||self.frozen>0) { //Frozen or stoned if(self.classname!="player") self.th_die=shatter; thinktime self : 0; self.attack_finished=time; self.pausetime=time; self.teleport_time=time; if(self.frozen>0) self.deathtype="ice shatter"; else if(self.skin==GLOBAL_SKIN_STONE) self.deathtype="stone crumble"; } if (self.classname == "player") ClientObituary(self, attacker, inflictor); if(attacker.deadflag2)//Only give bonus if more than 2 players targ.experience_value+=500*num_players - 2; //Give an extra 500* num players,you beat others to the kill } if((self.classname=="player"&&attacker.classname=="player"&&teamplay&&attacker.team==self.team)||attacker==targ) drop_level(attacker,1);//Killed someone on your team, or killed self, lose a level, get no exp else { if(attacker.level (targ.armor_amulet + targ.armor_bracer + targ.armor_breastplate + targ.armor_helmet)) { targ.armor_amulet = 0; targ.armor_bracer = 0; targ.armor_breastplate = 0; targ.armor_helmet = 0; } else // Damage the armor { curr_damage = armor_damage; // FIXME: Commented out the loop for E3 because of a runaway loop message // while (curr_damage>0) // { armor_cnt = armor_inv(targ); perpiece = curr_damage / armor_cnt; if ((targ.armor_amulet) && (curr_damage)) { targ.armor_amulet -= perpiece; curr_damage -= perpiece; if (targ.armor_amulet < 0) { curr_damage -= targ.armor_amulet; targ.armor_amulet = 0; } if (targ.armor_amulet < 1) targ.armor_amulet = 0; } if ((targ.armor_bracer) && (curr_damage)) { targ.armor_bracer -= perpiece; curr_damage -= perpiece; if (targ.armor_bracer < 0) { curr_damage -= targ.armor_bracer; targ.armor_bracer = 0; } if (targ.armor_bracer < 1) targ.armor_bracer = 0; } if ((targ.armor_breastplate) && (curr_damage)) { targ.armor_breastplate -= perpiece; curr_damage -= perpiece; if (targ.armor_breastplate < 0) { curr_damage -= targ.armor_breastplate; targ.armor_breastplate = 0; } if (targ.armor_breastplate < 1) targ.armor_breastplate = 0; } if ((targ.armor_helmet) && (curr_damage)) { targ.armor_helmet -= perpiece; curr_damage -= perpiece; if (targ.armor_helmet < 0) { curr_damage -= targ.armor_helmet; targ.armor_helmet = 0; } if (targ.armor_helmet < 1) targ.armor_helmet = 0; } // } } } else armor_damage =0; return(armor_damage); } /* ============ T_Damage The damage is coming from inflictor, but get mad at attacker This should be the only function that ever reduces health. ============ */ void(entity targ, entity inflictor, entity attacker, float damage) T_Damage= { vector dir; entity oldself; float save; float total_damage,do_mod; float armor_damage; entity holdent; if (!targ.takedamage) return; if(targ.invincible_time>time) { sound(targ,CHAN_ITEM,"misc/pulse.wav",1,ATTN_NORM); return; } if(targ!=attacker) if (targ.deathtype != "teledeath"&&targ.deathtype != "teledeath2"&&targ.deathtype != "teledeath3"&&targ.deathtype != "teledeath4") { if(coop&&teamplay&&attacker.classname=="player"&&targ.classname=="player") return; if(teamplay) if(attacker.classname=="player"&&targ.classname=="player") if(attacker.team==targ.team) return; } if (targ.flags & FL_GODMODE) return; if(targ.classname=="monster_mezzoman") { if(inflictor.flags2&FL_NODAMAGE) { inflictor.flags2(-)FL_NODAMAGE; if(random()<0.3) CreateSpark (inflictor.origin); return; } if(targ.movechain.classname=="mezzo reflect") if(infront_of_ent(inflictor,targ)) { sound(targ,CHAN_AUTO,"mezzo/slam.wav",1,ATTN_NORM); makevectors(targ.angles); if(random()<0.1) CreateSpark(targ.origin+targ.view_ofs+v_forward*12); else if(random()<0.7) particle4(targ.origin+targ.view_ofs+v_forward*12,random(5,15),256 + (8 * 15),PARTICLETYPE_FASTGRAV,2 * damage); return; } } // Nothing but melee weapons hurt the snake // if ((targ.classname == "monster_snake") && // ((!inflictor.classname == "player") || (!attacker.classname == "player"))) // return; if(targ.health<=0) { targ.health=targ.health-damage;//Keep taking damage while dying, if enough, may gib in mid-death return; } //Damage modifiers // used by buttons and triggers to set activator for target firing damage_attacker = attacker; if(attacker.flags&FL_CLIENT&&attacker==inflictor) {//Damage mod for strength using melee weaps if(attacker.weapon==IT_WEAPON1) { if(attacker.playerclass==CLASS_CRUSADER) { if(!attacker.artifact_active&ART_TOMEOFPOWER) do_mod=TRUE; } else do_mod=TRUE; } else if(attacker.playerclass==CLASS_PALADIN) if(attacker.weapon==IT_WEAPON2&&!attacker.artifact_active&ART_TOMEOFPOWER) do_mod=TRUE; if(do_mod) { do_mod = attacker.strength - 11; damage+=damage*do_mod/30;//from .84 - 1.23 } } if(targ.flags&FL_MONSTER&&inflictor.flags2&FL2_ADJUST_MON_DAM) damage*=2;//Special- more damage against monsters if (attacker.super_damage) damage += attacker.super_damage * damage; // Calculating Damage to a player if (targ.classname == "player") { // How much armor does he have armor_damage = armor_calc(targ,damage); // What hits player total_damage = damage - armor_damage; } else total_damage = damage; // add to the damage total for clients, which will be sent as a single // message at the end of the frame // FIXME: remove after combining shotgun blasts? if (targ.flags & FL_CLIENT) { targ.dmg_take = targ.dmg_take + total_damage; targ.dmg_save = targ.dmg_save + save; targ.dmg_inflictor = inflictor; } // figure momentum add if ( (inflictor != world) && (targ.movetype == MOVETYPE_WALK) ) { dir = targ.origin - (inflictor.absmin + inflictor.absmax) * 0.5; dir = normalize(dir); targ.velocity = targ.velocity + dir*damage*8; } // check for godmode or invincibility // do the damage targ.health = targ.health - total_damage; if(targ.health>=0&&targ.health<1.0000)//No more Zombie mode!!! (Sorry!) targ.health=-0.1; if (targ.health <=0 && targ.classname == "player" && targ.cnt_sh_boost) { if (deathmatch || skill == 0) // Only in deatmatch or easy mode { holdent = self; self = targ; use_super_healthboost(); centerprint(self,"Saved by the Mystic Urn!\n"); stuffcmd(self,"bf\n"); sound (self, CHAN_AUTO, "misc/comm.wav", 1, ATTN_NORM); self.deathtype=""; self = holdent; return; } } // Check to see if divine intervention took place if ((targ.health <= 0) && (targ.classname == "player") && (targ.playerclass == CLASS_PALADIN)) { holdent = self; self = targ; if (Pal_DivineIntervention()) { self.deathtype=""; self = holdent; return; } self = holdent; } if (targ.health <= 0) { if(attacker.controller.classname=="player") {//Proper frag credit to controller of summoned stuff inflictor=attacker; attacker=attacker.controller; } targ.th_pain=SUB_Null; //Should prevents interruption of death sequence Killed (targ, attacker,inflictor); return; } // react to the damage oldself = self; self = targ; // barrels need sliding information if (self.classname == "barrel") { self.enemy = inflictor; self.count = damage; } else if (self.classname == "catapult") self.enemy = inflictor; else if(self.classname=="player") self.enemy = attacker; if ( (self.flags & FL_MONSTER) && attacker != world && !(attacker.flags & FL_NOTARGET)&&attacker!=self.controller&&(attacker.controller!=self.controller||attacker.controller==world)) { // Monster's shouldn't attack each other (kin don't shoot kin) if (self != attacker && attacker != self.enemy&&(self.enemy.classname!="player"||attacker.classname=="player"||attacker.controller.classname=="player"))// && attacker.flags & FL_CLIENT) { if (self.classname != attacker.classname||random(100)<=5) //5% chance they'll turn on selves { if((self.model=="models/spider.mdl"||self.model=="models/scorpion.mdl")&&attacker.model==self.model) { if(random()<0.3) monster_pissed(attacker); } else monster_pissed(attacker); } } } if (self.th_pain) { if(self.classname=="player"&&self.model!="models/sheep.mdl") player_pain(); else self.th_pain (attacker, total_damage); // nightmare mode monsters don't go into pain frames often if (skill == 3) self.pain_finished = time + 5; } self = oldself; }; /* ============ T_RadiusDamage ============ */ //void(entity loser)SpawnFlameOn; void(entity inflictor, entity attacker, float damage, entity ignore) T_RadiusDamage = { float points,inertia,radius; entity head; vector inflictor_org, org; //FIXME: If too many radius damage effects go off at the same time, it crashes in a loop // This usually occurs when object whose death is radius damage destoy // other objects with a radius damage death (namely: exploding barrels) inflictor_org = (inflictor.absmin+inflictor.absmax)*0.5; if(inflictor.classname=="circfire") radius=150; else radius=damage+40; head = findradius(inflictor_org, radius); if(inflictor.classname=="fireballblast") damage+=attacker.level*33; while (head) { if (head != ignore&&head!=inflictor)// && head!=inflictor.owner) { if (head.takedamage) { org = (head.absmax + head.absmin)*0.5; points = 0.5*vlen (inflictor_org - org); if (points < 0) points = 0; points = damage - points; if (head == attacker) if(attacker.classname=="monster_eidolon"||attacker.playerclass==CLASS_NECROMANCER)//Necromancer takes no radius damage from his own magic points = 0; else if(inflictor.model=="models/assgren.mdl")//Some more resistance to the Assassin's own Big One points*=0.25; else points*=0.5; //following stops multiple grenades from blowing each other up if(head.owner==inflictor.owner&& head.classname==inflictor.classname&& (head.classname=="stickmine"||head.classname=="tripwire")) points=0; if((inflictor.classname=="snowball"||inflictor.classname=="blizzard")&&head.frozen>0) points=0; if (points > 0) { if (CanDamage (head, inflictor)) { if(other.movetype!=MOVETYPE_PUSH) { if (head.mass<=10) inertia = 1; else if(head.mass<=100) inertia = head.mass/10; else inertia = head.mass; head.velocity=head.velocity+normalize(org-inflictor_org)*(points*10/inertia); head.flags(-)FL_ONGROUND; } if(inflictor.classname=="fireballblast") { if(points>10||points<5) points=random(5,10); if(head.flags&FL_FIREHEAL) { if(head.health+points<=head.max_health) head.health=head.health+points; else head.health=head.max_health; } else if(!head.flags&FL_FIRERESIST) { if(head.health<=points) points=1000; T_Damage (head, inflictor, attacker, points); } } else T_Damage (head, inflictor, attacker, points); } } } } head = head.chain; } }; /* ============ T_RadiusDamageWater ============ */ void(entity inflictor, entity attacker, float dam, entity ignore) T_RadiusDamageWater = { local float points; local entity head; local vector org; head = findradius(inflictor.origin, dam); while (head) { if (head != ignore) { if (head.takedamage) { points=pointcontents(head.origin); if (points == CONTENT_WATER || points == CONTENT_SLIME) // visible(inflictor)? { if (head.classname == "player" && head != attacker) head.enemy = attacker; org = head.origin + (head.mins + head.maxs)*0.5; points = 0.25 * vlen (inflictor.origin - org); if (points <= 64) points = 1; points = dam/points; if (points < 1||(self.classname=="mjolnir"&&head==self.controller)||head.classname=="monster_hydra") points = 0; if (points > 0) { head.deathtype="zap"; spawnshockball((head.absmax+head.absmin)*0.5); T_Damage (head, inflictor, attacker, points); //Bubbles if dead? } } } } head = head.chain; } }; /* ============ T_BeamDamage ============ */ /* void(entity attacker, float damage) T_BeamDamage = { local float points; local entity head; head = findradius(attacker.origin, damage+40); while (head) { if (head.takedamage) { points = 0.5*vlen (attacker.origin - head.origin); if (points < 0) points = 0; points = damage - points; if (head == attacker) points = points * 0.5; if (points > 0) { if (CanDamage (head, attacker)) T_Damage (head, attacker, attacker, points); } } head = head.chain; } }; */ /* ============ T_RadiusManaDamage ============ */ /* void(entity inflictor, entity attacker, float manadamage, entity ignore) T_RadiusManaDamage = { local float points; local entity head; local vector org; head = findradius(inflictor.origin, manadamage); while (head) { if (head != ignore) { if ((head.takedamage) && (head.classname=="player")) { org = head.origin + (head.mins + head.maxs)*0.5; points = 0.5 * vlen (inflictor.origin - org); if (points < 0) points = 0; points = manadamage - points; if (head == attacker) points = points * 0.5; if (points > 0) { if (CanDamage (head, inflictor)) { head.bluemana = head.bluemana - points; if (head.bluemana<0) head.bluemana=0; head.greenmana = head.greenmana - points; if (head.greenmana<0) head.greenmana=0; } } } } head = head.chain; } }; */ gamecode/hc/h2/doors.hc000066400000000000000000001075411444734033100152020ustar00rootroot00000000000000float DOOR_START_OPEN = 1; float REVERSE = 2; float DOOR_DONT_LINK = 4; float DOOR_TOGGLE = 8; float DOOR_SLIDE = 16; float DOOR_NORMAL = 32; float DOOR_REMOVE_PP = 64; float DOOR_NO_PP = 128; /* Doors are similar to buttons, but can spawn a fat trigger field around them to open without a touch, and they link together to form simultanious double/quad doors. Door.owner is the master door. If there is only one door, it points to itself. If multiple doors, all will point to a single one. Door.enemy chains from the master door through all doors linked in the chain. */ void door_hit_bottom(); void door_hit_top(); /* =========================================================================== door_slide =========================================================================== */ void door_slide_next() { local vector vdestdelta, odelta; local float len, tspeed;//, len2; tspeed = self.speed; if(!tspeed) objerror("No speed is defined!"); // Make sure we're not already at the destination if(self.finaldest == self.origin) { self.velocity = '0 0 0'; if(self.state == STATE_DOWN) self.think = door_hit_bottom; else if(self.state == STATE_UP) self.think = door_hit_top; self.nextthink = self.ltime + 0.1; return; } vdestdelta = self.finaldest - self.origin; // Set destdelta to the vector needed to move odelta = self.origin - self.pos1; len = vlen(odelta); // Get the length of the vector // If the length is this small, just stop if(vlen(vdestdelta) < 0.1) { if(self.state == STATE_DOWN) door_hit_bottom(); else if(self.state == STATE_UP) door_hit_top(); else dprint("Bad door state!\n"); return; } self.nextthink = self.ltime + 0.1; self.think = door_slide_next; tspeed = ((self.speed - (len / 10)) / 20); if(tspeed < 2) { if(self.state == STATE_DOWN) SUB_CalcMove(self.finaldest, self.speed, door_hit_bottom); else if(self.state == STATE_UP) SUB_CalcMove(self.finaldest, self.speed, door_hit_top); else dprint("Bad door state!\n"); return; } self.velocity = vdestdelta * tspeed; } void door_slide(vector tdest) { self.finaldest = tdest; door_slide_next(); } /* =========================================================================== door_crash =========================================================================== */ void door_crash_next() { local vector vdestdelta, nextvect, testvect; local vector tdest; local float len, nextlen, testlen; local float tspeed; tdest = self.finaldest; tspeed = self.speed; if(!tspeed) objerror("No speed is defined!"); if(tdest == self.origin) { self.velocity = '0 0 0'; if(self.state == STATE_DOWN) self.think = door_hit_bottom; else if(self.state == STATE_UP) self.think = door_hit_top; else dprint("Bad door state!\n"); self.nextthink = self.ltime + 0.1; return; } // set destdelta to the vector needed to move vdestdelta = self.finaldest - self.origin; nextvect = self.pos2 - self.origin; testvect = self.pos2 - self.finaldest; // calculate length of vector len = vlen (vdestdelta); nextlen = vlen(nextvect) + 1; testlen = vlen(testvect); if(len < 0.1 || nextlen > testlen) { door_hit_bottom; return; } else { self.velocity = vdestdelta * (nextlen / len) * 4; nextvect = self.origin + self.velocity; nextlen = vlen(nextvect); if(nextlen >= testlen * 2) { SUB_CalcMove(self.finaldest, self.speed * (len / (len / 3)), door_hit_bottom); return; } } self.nextthink = self.ltime + 0.1; self.think = door_crash_next; } void door_crash(vector tdest) { self.finaldest = tdest; door_crash_next(); } /* ============================================================================= THINK FUNCTIONS ============================================================================= */ void door_go_down(); void door_go_up(); void door_damage() { T_Damage (other, self, self, self.dmg); } void door_blocked() { //FIXME: Rotating doors sem to think they're being blocked // even if they're rotating down and the object is above them if(self.wait>-2&&self.strength<=0) if(self.dmg==666) { if(other.classname=="player"&&other.flags2&FL_ALIVE) { other.decap=TRUE; T_Damage (other, self, self, other.health+300); } else T_Damage (other, self, self, other.health+50); } else T_Damage (other, self, self, self.dmg); //Rotating doors rotating around a x or z axis push you up and in the direction they're turning /* if(self.strength==2) { other.flags(-)FL_ONGROUND; other.velocity=normalize(self.origin-(other.absmin+other.absmax)*0.5)*100; } else*/ if(other.flags&FL_ONGROUND&&(self.movedir_x||self.movedir_z)&&self.strength==1)//&&other.origin_z>self.origin_z { other.flags(-)FL_ONGROUND; other.velocity_z+=self.speed*2; other.velocity_x-=self.speed*self.movedir_x; other.velocity_y-=self.speed*self.movedir_z; } else { other.flags(-)FL_ONGROUND; other.velocity_z+=10; } // if a door has a negative wait, it would never come back if blocked, // so let it just squash the object to death real fast if (self.wait >= 0) { if (self.state == STATE_DOWN) door_go_up (); else door_go_down (); } } void door_hit_top() { self.velocity = '0 0 0'; sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); self.state = STATE_TOP; if (self.spawnflags & DOOR_TOGGLE) return; // don't come down automatically if(self.wait== -2) self.th_die(); else if(self.wait== -1) self.nextthink = -1; else { self.think = door_go_down; self.nextthink = self.ltime + self.wait; } } void door_hit_bottom() { self.velocity = '0 0 0'; sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); self.state = STATE_BOTTOM; } void door_go_down() { sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); if(!self.thingtype && self.max_health) { self.takedamage = DAMAGE_YES; self.health = self.max_health; } self.state = STATE_DOWN; if(self.classname == "door") { if(self.spawnflags & DOOR_SLIDE) door_slide(self.pos1); else if(self.spawnflags & DOOR_NORMAL) if(self.v_angle!='0 0 0') if(self.speed) if(self.anglespeed) SUB_CalcMoveAndAngleInit (self.pos1, self.speed, self.o_angle, self.anglespeed, door_hit_bottom,FALSE); else SUB_CalcMoveAndAngleInit (self.pos1, self.speed, self.o_angle, self.anglespeed, door_hit_bottom,TRUE); else SUB_CalcAngleMove(self.o_angle, self.anglespeed, door_hit_bottom); else SUB_CalcMove(self.pos1, self.speed, door_hit_bottom); else door_crash(self.pos1); } else if (self.classname == "door_rotating") SUB_CalcAngleMove(self.pos1, self.speed, door_hit_bottom); } void new_movedir (vector movin,float dir) { self.movedir = movin; // check for clockwise rotation if (dir<0) self.movedir = self.movedir * -1; self.pos1 = self.angles; self.pos2 = self.angles + self.movedir * self.dflags; } void door_go_up() { if(self.state == STATE_UP) /* Already going up */ return; if(self.state == STATE_TOP) /* Reset top wait time */ { self.nextthink = self.ltime + self.wait; return; } sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); self.state = STATE_UP; if(self.classname == "door") { if(self.spawnflags & DOOR_NORMAL) { if(self.v_angle!='0 0 0') { if(self.speed) { if(self.anglespeed) SUB_CalcMoveAndAngleInit (self.pos2, self.speed, self.v_angle, self.anglespeed, door_hit_top,FALSE); else SUB_CalcMoveAndAngleInit (self.pos2, self.speed, self.v_angle, self.anglespeed, door_hit_top,TRUE); } else SUB_CalcAngleMove(self.v_angle, self.anglespeed, door_hit_top); } else SUB_CalcMove(self.pos2, self.speed, door_hit_top); } else door_slide(self.pos2); } else if(self.classname == "door_rotating") SUB_CalcAngleMove(self.pos2, self.speed, door_hit_top); SUB_UseTargets(); } /* ============================================================================= ACTIVATION FUNCTIONS ============================================================================= */ void door_fire() { local entity oself; local entity starte; if (self.owner != self) objerror ("door_fire: self.owner != self"); self.no_puzzle_msg = 0; // play use key sound self.message = 0; // no more message oself = self; if (self.spawnflags & DOOR_TOGGLE) { if (self.state == STATE_UP || self.state == STATE_TOP) { starte = self; do { door_go_down (); self = self.enemy; } while ( (self != starte) && (self != world) ); self = oself; return; } } // trigger all paired doors starte = self; do { door_go_up (); self = self.enemy; } while ( (self != starte) && (self != world) ); self = oself; } /* * door_use() -- Called whenever a door is opened. */ void door_use() { local entity oself; self.message = 0; // door messages are for touch only self.owner.message = 0; self.enemy.message = 0; oself = self; self = self.owner; door_fire (); self = oself; } // defined in triggers.hc float check_puzzle_pieces(entity client, float remove_pieces, float inverse); /* * door_trigger_touch() -- Called when someone touches a door. */ void door_trigger_touch() { entity door; string temp; float removepp, inversepp; // if(!other.flags2&FL_ALIVE) // return; if(!other.flags&FL_CLIENT&&!other.flags&FL_MONSTER) return; if(time < self.attack_finished) return; door = self; self = self.owner; if(!deathmatch) { removepp = (self.spawnflags & DOOR_REMOVE_PP); inversepp = (self.spawnflags & DOOR_NO_PP); if (!check_puzzle_pieces(other,removepp,inversepp)) { if (self.no_puzzle_msg && !deathmatch) { temp = getstring(self.no_puzzle_msg); centerprint (other, temp); door.attack_finished = time + 2; } return; } } self.attack_finished = time + 1; activator = other; door_use(); } /* * door_killed() -- Used to open doors that open when shot. */ void door_killed() { local entity oself; oself = self; self = self.owner; self.health = self.max_health; self.takedamage = DAMAGE_NO; // wil be reset upon return door_use(); self = oself; } /* * door_touch() -- Prints messages and opens key doors. */ void door_touch() { string temp; float removepp, inversepp; // if(!other.flags2&FL_ALIVE) // return; if(!other.flags&FL_CLIENT&&!other.flags&FL_MONSTER) return; if(self.dmg==666&&(self.velocity!='0 0 0'||self.avelocity!='0 0 0')) { if(other.classname=="player"&&other.flags2&FL_ALIVE) { other.decap=TRUE; T_Damage (other, self, self, other.health+300); } else T_Damage (other, self, self, other.health+50); } if(self.owner.attack_finished > time) return; self.owner.attack_finished = time + 2; if(self.owner.message != 0 && !deathmatch) { temp = getstring(self.owner.message); centerprint (other, temp); sound (other, CHAN_VOICE, "misc/comm.wav", 1, ATTN_NORM); } // key door stuff if (!self.puzzle_piece_1 && !self.puzzle_piece_2 && !self.puzzle_piece_3 && !self.puzzle_piece_4) return; // FIXME: blink key on player's status bar removepp = (self.spawnflags & DOOR_REMOVE_PP); inversepp = (self.spawnflags & DOOR_NO_PP); if (!check_puzzle_pieces(other,removepp,inversepp)) { if (self.no_puzzle_msg && !deathmatch) { temp = getstring(self.no_puzzle_msg); centerprint (other, temp); } return; } self.touch = SUB_Null; if (self.enemy) self.enemy.touch = SUB_Null; // get paired door door_use (); } /* ============================================================================= SPAWNING FUNCTIONS ============================================================================= */ entity spawn_field(vector fmins, vector fmaxs, entity door) { entity trigger; vector t1, t2; trigger = spawn(); trigger.movetype = MOVETYPE_NONE; trigger.solid = SOLID_TRIGGER; trigger.owner = door; trigger.touch = door_trigger_touch; t1 = fmins; t2 = fmaxs; // if (door.classname == "door") // { // if(self.v_angle!='0 0 0') // { t1 += door.origin; t2 += door.origin; setsize (trigger, t1 - '60 60 8', t2 + '60 60 8'); /* } else setsize (trigger, t1 - '60 60 8', t2 + '60 60 8'); } else if (door.classname == "door_rotating") { t1 += door.origin; t2 += door.origin; setsize (trigger, t1 - '60 60 8', t2 + '60 60 8'); } */ return (trigger); } float EntitiesTouching(entity e1, entity e2) { local vector e1max, e1min, e2max, e2min; //Rotating door's mins and maxs aren't based on their world positions, //so the origin needs to be applied to make sure they are checking their //real positions if (e1.classname == "door_rotating"||(e1.classname=="door"&&e1.v_angle!='0 0 0')) { e1max = e1.maxs + e1.origin; e1min = e1.mins + e1.origin; e2max = e2.maxs + e2.origin; e2min = e2.mins + e2.origin; } else { e1max = e1.maxs; e1min = e1.mins; e2max = e2.maxs; e2min = e2.mins; } if (e1min_x > e2max_x) return FALSE; if (e1min_y > e2max_y) return FALSE; if (e1min_z > e2max_z) return FALSE; if (e1max_x < e2min_x) return FALSE; if (e1max_y < e2min_y) return FALSE; if (e1max_z < e2min_z) return FALSE; return TRUE; } /* * LinkDoors() */ void LinkDoors() { local entity t, starte; local vector cmins, cmaxs; if (self.enemy) return; // already linked by another door if (self.spawnflags & 4) { self.owner = self.enemy = self; return; // don't want to link this door } cmins = self.mins; cmaxs = self.maxs; starte = self; t = self; loop /*do*/ { self.owner = starte; // master door if (!self.thingtype && self.health) starte.health = self.health; if (self.targetname) starte.targetname = self.targetname; if (self.message != 0) starte.message = self.message; t = find (t, classname, self.classname); if (!t) { self.enemy = starte; // make the chain a loop // shootable, fired, or key doors just needed the owner/enemy links, // they don't spawn a field self = self.owner; if (!self.thingtype && self.health) return; if (self.targetname) return; if (self.puzzle_piece_1 != string_null || self.puzzle_piece_2 != string_null || self.puzzle_piece_3 != string_null || self.puzzle_piece_4 != string_null) return; self.owner.trigger_field = spawn_field(cmins, cmaxs, self.owner); return; } if (EntitiesTouching(self,t)) { if (t.enemy) objerror ("cross connected doors"); self.enemy = t; self = t; if (t.mins_x < cmins_x) cmins_x = t.mins_x; if (t.mins_y < cmins_y) cmins_y = t.mins_y; if (t.mins_z < cmins_z) cmins_z = t.mins_z; if (t.maxs_x > cmaxs_x) cmaxs_x = t.maxs_x; if (t.maxs_y > cmaxs_y) cmaxs_y = t.maxs_y; if (t.maxs_z > cmaxs_z) cmaxs_z = t.maxs_z; } } /*while (1);*/ } void door_sounds(void) { self.noise3 = "doors/baddoor.wav"; if (self.soundtype == 0) // No sound { precache_sound ("misc/null.wav"); self.noise1 = "misc/null.wav"; self.noise2 = "misc/null.wav"; self.noise4 = "misc/null.wav"; } else if (self.soundtype == 1) // Big Metal door, swinging { precache_sound ("doors/gatestop.wav"); precache_sound ("doors/gateswng.wav"); precache_sound ("doors/gatestrt.wav"); self.noise1 = "doors/gatestop.wav"; self.noise2 = "doors/gateswng.wav"; self.noise4 = "doors/gatestrt.wav"; } else if (self.soundtype == 2) // Big Stone Door, sliding { precache_sound ("doors/doorstop.wav"); precache_sound ("doors/stonslid.wav"); precache_sound ("doors/dorstart.wav"); self.noise1 = "doors/doorstop.wav"; self.noise2 = "doors/stonslid.wav"; self.noise4 = "doors/dorstart.wav"; } else if (self.soundtype == 3) // Big Wood Door, Swinging { precache_sound ("doors/swngstop.wav"); precache_sound ("doors/wdswngbg.wav"); precache_sound ("doors/dorstart.wav"); self.noise1 = "doors/swngstop.wav"; self.noise2 = "doors/wdswngbg.wav"; self.noise4 = "doors/dorstart.wav"; } else if (self.soundtype == 4) // Normal Wood Door, Swinging { precache_sound ("doors/swngstop.wav"); precache_sound ("doors/wdswngsm.wav"); precache_sound ("doors/dorstart.wav"); self.noise1 = "doors/swngstop.wav"; self.noise2 = "doors/wdswngsm.wav"; self.noise4 = "doors/dorstart.wav"; } else if (self.soundtype == 5) // Big Wood Door, Sliding { precache_sound ("doors/swngstop.wav"); precache_sound ("doors/woodslid.wav"); precache_sound ("doors/dorstart.wav"); self.noise1 = "doors/swngstop.wav"; self.noise2 = "doors/woodslid.wav"; self.noise4 = "doors/dorstart.wav"; } else if (self.soundtype == 6) // Drawbridge, Falling and crushing innocent peasants who are // basically slave labor, who toil away their lives so that the rich upperclass // can keep busy inbreeding with each other. Damn the aristocracy! Freedom to // the common man! Burn the castle! Death to the tyrants! { precache_sound ("doors/doorstop.wav"); precache_sound ("doors/drawmove.wav"); precache_sound ("doors/drawstrt.wav"); self.noise1 = "doors/doorstop.wav"; self.noise2 = "doors/drawmove.wav"; self.noise4 = "doors/dorstart.wav"; } else if (self.soundtype == 7) // Rotating Walkway { precache_sound ("doors/doorstop.wav"); precache_sound ("doors/stonslid.wav"); precache_sound ("doors/dorstart.wav"); self.noise1 = "doors/doorstop.wav"; self.noise2 = "doors/stonslid.wav"; self.noise4 = "doors/dorstart.wav"; } else if (self.soundtype == 8) // Big Metal door, sliding { precache_sound ("doors/mtlstop.wav"); precache_sound ("doors/mtlslide.wav"); precache_sound ("doors/mtlstart.wav"); self.noise1 = "doors/mtlstop.wav"; self.noise2 = "doors/mtlslide.wav"; self.noise4 = "doors/mtlstart.wav"; } else if (self.soundtype == 9) // Pendulum { precache_sound2 ("doors/penstop.wav"); precache_sound2 ("doors/penswing.wav"); precache_sound2 ("doors/penstart.wav"); self.noise1 = "doors/penstop.wav"; self.noise2 = "doors/penswing.wav"; self.noise4 = "doors/penstart.wav"; } } void door_rotate_incr_done() { // if(self.strength==2) // cvar_set ("sv_gravity", "800"); } void() door_rotate_incr = { vector newvect; if(self.strength==2) cvar_set ("sv_gravity", "100"); self.dflags=self.flags; self.flags=0; if(self.v_angle!='0 0 0') { if(random()>0.5&&self.v_angle_x!=0) { self.cnt=self.dflags=self.v_angle_x; new_movedir('1 0 0',self.cnt); } else if(random()>0.5&&self.v_angle_y!=0) { self.cnt=self.dflags=self.v_angle_y; new_movedir('0 1 0',self.cnt); } else if(self.v_angle_z!=0) { self.cnt=self.dflags=self.v_angle_z; new_movedir('0 0 1',self.cnt); } } newvect = self.movedir * self.cnt; SUB_CalcAngleMove(self.angles + newvect, self.speed, door_rotate_incr_done); }; /*QUAKED func_door (0 .5 .8) ? START_OPEN REVERSE DOOR_DONT_LINK TOGGLE SLIDE NORMAL_MOVE remove_pp no_pp if two doors touch, they are assumed to be connected and operate as a unit. -----------------------FIELDS------------------------- TOGGLE causes the door to wait in both the start and end states for a trigger event. START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors). Key doors are always wait -1. "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet "angle" determines the opening direction "level" how far (in map units) to move in the specified angle- overrides default movement that is size of door "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. "health" if set, door must be shot open "speed" movement speed (100 default), -1 will not move, just rotate "wait" wait before returning (3 default, -1 = never return) "lip" lip remaining at end of move (8 default) "dmg" damage to inflict when blocked (2 default) If you make it 666, it will gib anything it touches, and behead players. ROTATING DOORS: MUST HAVE AN ORIGIN BRUSH "v_angle" Angle to turn, in: pitch yaw roll, '0 0 0' will not rotate, just move (default = '0 0 0') "anglespeed" how quickly to turn in that direction. no anglespeed will force it to choose one that will synch the completion of the rotation iwth the completion of the move. (default = 0) "strength" When set to 1, it will throw something if it gets in the way "soundtype" 0) no sound 1) Big metal door, swinging 2) Big stone door, sliding 3) Big wood door, swinging 4) Normal wood door, swinging 5) Big wood door, sliding 6) Drawbridge 7) Rotating walkway 8) Big metal door, sliding 9) Pendulum swinging Puzzle Pieces (use the puzzle_id value from the pieces) puzzle_piece_1 puzzle_piece_2 puzzle_piece_3 puzzle_piece_4 no_puzzle_msg: message when player doesn't have the right pieces -------------------------------------------------------- */ void func_door() { door_sounds(); SetMovedir (); // check for clockwise rotation if (self.spawnflags & REVERSE) { self.movedir = self.movedir * -1; self.v_angle = self.v_angle * -1; } self.max_health = self.health; self.solid = SOLID_BSP; self.movetype = MOVETYPE_PUSH; setorigin (self, self.origin); setmodel (self, self.model); self.classname = "door"; self.blocked = door_blocked; self.use = door_use; if (self.abslight) self.drawflags (+) MLS_ABSLIGHT; if (self.speed==-1) self.speed=0; else if(!self.speed) self.speed = 100; if (!self.wait) self.wait = 3; if (!self.lip) self.lip = 8; if (!self.dmg) self.dmg = 2; self.pos1 = self.origin; if(self.level) self.pos2 = self.pos1 + self.movedir*(self.level - self.lip); else self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip); if(self.v_angle!='0 0 0') { self.o_angle=self.angles; self.v_angle+=self.angles; } if(self.wait== -2) { self.th_die = chunk_death; // self.takedamage = DAMAGE_YES; if (!self.health) { if ((self.thingtype == THINGTYPE_GLASS) || (self.thingtype == THINGTYPE_CLEARGLASS)) self.max_health = self.health = 25; else if ((self.thingtype == THINGTYPE_GREYSTONE) || (self.thingtype == THINGTYPE_BROWNSTONE)) self.max_health = self.health = 75; else if (self.thingtype == THINGTYPE_WOOD) self.max_health = self.health = 50; else if (self.thingtype == THINGTYPE_METAL) self.max_health = self.health = 100; else if (self.thingtype == THINGTYPE_FLESH) self.max_health = self.health = 30; else self.max_health = self.health = 25; } } // DOOR_START_OPEN is to allow an entity to be lighted in the closed position // but spawn in the open position if (self.spawnflags & DOOR_START_OPEN) { if(self.v_angle!='0 0 0') { self.angles = self.v_angle; self.v_angle = self.o_angle; self.o_angle = self.angles; } setorigin (self, self.pos2); self.pos2 = self.pos1; self.pos1 = self.origin; } self.state = STATE_BOTTOM; if (self.health) { self.takedamage = DAMAGE_YES; self.th_die =self.th_pain= door_killed; } if (self.puzzle_piece_1 != string_null || self.puzzle_piece_2 != string_null || self.puzzle_piece_3 != string_null || self.puzzle_piece_4 != string_null) self.wait = -1; self.touch = door_touch; // LinkDoors can't be done until all of the doors have been spawned, so // the sizes can be detected properly. self.think = LinkDoors; self.nextthink = self.ltime + 0.1; if (self.cnt) { self.touch = SUB_Null; self.use = door_rotate_incr; } } /*QUAKED func_door_smashing (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK TOGGLE SLIDE NORMAL_MOVE remove_pp no_pp if two doors touch, they are assumed to be connected and operate as a unit. -----------------------FIELDS------------------------- TOGGLE causes the door to wait in both the start and end states for a trigger event. START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors). Key doors are always wait -1. "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet "angle" determines the opening direction "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. "speed" movement speed (100 default) "wait" wait before returning (3 default, -1 = never return) "lip" lip remaining at end of move (8 default) "dmg" damage to inflict when blocked (2 default) "soundtype" 0) no sound 1) Big metal door, swinging 2) Big stone door, sliding 3) Big wood door, swinging 4) Normal wood door, swinging 5) Big wood door, sliding 6) Drawbridge 7) Rotating walkway 8) Big metal door, sliding 9) Pendulum swinging Puzzle Pieces (use the puzzle_id value from the pieces) puzzle_piece_1 puzzle_piece_2 puzzle_piece_3 puzzle_piece_4 no_puzzle_msg: message when player doesn't have the right pieces -------------------------------------------------------- void func_door_smashing() { door_sounds(); SetMovedir (); self.solid = SOLID_BSP; self.movetype = MOVETYPE_PUSH; setorigin (self, self.origin); setmodel (self, self.model); self.classname = "door"; self.blocked = door_blocked; self.use = door_use; if (!self.speed) self.speed = 100; if (!self.wait) self.wait = 3; if (!self.lip) self.lip = 8; if (!self.dmg) self.dmg = 2; self.pos1 = self.origin; self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip); // DOOR_START_OPEN is to allow an entity to be lighted in the closed position // but spawn in the open position if (self.spawnflags & DOOR_START_OPEN) { setorigin (self, self.pos2); self.pos2 = self.pos1; self.pos1 = self.origin; } self.state = STATE_BOTTOM; self.takedamage = DAMAGE_YES; self.th_die = chunk_death; if(self.soundtype == 1) { self.thingtype = THINGTYPE_GREYSTONE; self.max_health = self.health = 75; } else if(self.soundtype == 4) { self.thingtype = THINGTYPE_METAL; self.max_health = self.health = 100; } else { self.thingtype = THINGTYPE_WOOD; self.max_health = self.health = 50; } self.touch = door_touch; // LinkDoors can't be done until all of the doors have been spawned, so // the sizes can be detected properly. self.think = LinkDoors; self.nextthink = self.ltime + 0.1; }*/ /* ============================================================================= SECRET DOORS ============================================================================= */ void fd_secret_move1(); void fd_secret_move2(); void fd_secret_move3(); void fd_secret_move4(); void fd_secret_move5(); void fd_secret_move6(); void fd_secret_done(); float SECRET_OPEN_ONCE = 1; // stays open float SECRET_1ST_LEFT = 2; // 1st move is left of arrow float SECRET_1ST_DOWN = 4; // 1st move is down from arrow float SECRET_NO_SHOOT = 8; // only opened by trigger float SECRET_YES_SHOOT = 16; // shootable even if targeted void fd_secret_use() { local float temp; self.health = 10000; // exit if still moving around... if(self.origin != self.oldorigin) return; self.message = 0; // no more message SUB_UseTargets(); // fire all targets / killtargets if(!self.spawnflags & SECRET_NO_SHOOT) { self.th_pain = SUB_Null; self.takedamage = DAMAGE_NO; } self.velocity = '0 0 0'; // Make a sound, wait a little... sound(self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); self.nextthink = self.ltime + 0.1; temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1 makevectors(self.mangle); if(!self.t_width) { if (self.spawnflags & SECRET_1ST_DOWN) self. t_width = fabs(v_up * self.size); else self. t_width = fabs(v_right * self.size); } if (!self.t_length) self. t_length = fabs(v_forward * self.size); if (self.spawnflags & SECRET_1ST_DOWN) self.dest1 = self.origin - v_up * self.t_width; else self.dest1 = self.origin + v_right * (self.t_width * temp); self.dest2 = self.dest1 + v_forward * self.t_length; SUB_CalcMove(self.dest1, self.speed, fd_secret_move1); sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); } /* Wait after the first movement... */ void fd_secret_move1() { self.nextthink = self.ltime + 1; self.think = fd_secret_move2; sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); } /* Start moving sideways with sound... */ void fd_secret_move2() { sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); SUB_CalcMove(self.dest2, self.speed, fd_secret_move3); } /* Wait here until it's time to go back... */ void fd_secret_move3() { sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); if(!self.spawnflags & SECRET_OPEN_ONCE) { self.nextthink = self.ltime + self.wait; self.think = fd_secret_move4; } } /* Move backward... */ void fd_secret_move4() { sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); SUB_CalcMove(self.dest1, self.speed, fd_secret_move5); } /* Wait for one second... */ void fd_secret_move5() { self.nextthink = self.ltime + 1; self.think = fd_secret_move6; sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); } void fd_secret_move6() { sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done); } void fd_secret_done() { if (!self.targetname || self.spawnflags&SECRET_YES_SHOOT) { self.health = 10000; self.takedamage = DAMAGE_YES; self.th_pain = fd_secret_use; } sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); } void secret_blocked() { if (time < self.attack_finished) return; self.attack_finished = time + 0.5; T_Damage (other, self, self, self.dmg); } /* * secret_touch() -- Prints messages. */ void secret_touch() { string s; if (other.classname != "player") return; if (self.attack_finished > time) return; self.attack_finished = time + 2; if (self.message) { s = getstring(self.message); centerprint (other, s); sound (other, CHAN_BODY, "misc/comm.wav", 1, ATTN_NORM); } } /*QUAKED func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot x remove_pp no_pp Basic secret door. Slides back, then to the side. Angle determines direction. -----------------------FIELDS------------------------- wait = # of seconds before coming back 1st_left = 1st move is left of arrow 1st_down = 1st move is down from arrow always_shoot = even if targeted, keep shootable t_width = override WIDTH to move back (or height if going down) t_length = override LENGTH to move sideways "dmg" damage to inflict when blocked (2 default) If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage. "soundtype" 0) no sound 1) Big metal door, swinging 2) Big stone door, sliding 3) Big wood door, swinging 4) Normal wood door, swinging 5) Big wood door, sliding 6) Drawbridge 7) Rotating walkway 8) Big metal door, sliding 9) Pendulum swinging Puzzle Pieces (use the puzzle_id value from the pieces) puzzle_piece_1 puzzle_piece_2 puzzle_piece_3 puzzle_piece_4 no_puzzle_msg: message when player doesn't have the right pieces -------------------------------------------------------- */ void func_door_secret() { door_sounds(); if (!self.dmg) self.dmg = 2; // Magic formula... self.mangle = self.angles; self.angles = '0 0 0'; self.solid = SOLID_BSP; self.movetype = MOVETYPE_PUSH; self.classname = "door"; setmodel (self, self.model); setorigin (self, self.origin); self.touch = secret_touch; self.blocked = secret_blocked; self.speed = 50; self.use = fd_secret_use; if ( !self.targetname || self.spawnflags&SECRET_YES_SHOOT) { self.health = 10000; self.takedamage = DAMAGE_YES; self.th_pain = fd_secret_use; self.th_die = fd_secret_use; } self.oldorigin = self.origin; if (!self.wait) self.wait = 5; // 5 seconds before closing } /*QUAKED func_door_rotating (0 .5 .8) ? START_OPEN REVERSE DOOR_DONT_LINK remove_pp no_pp TOGGLE X_AXIS Y_AXIS if two doors touch, they are assumed to be connected and operate as a unit. TOGGLE causes the door to wait in both the start and end states for a trigger event. START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors). Key doors are always wait -1. You need to have an origin brush as part of this entity. The center of that brush will be the point around which it is rotated. It will rotate around the Z axis by default. You can check either the X_AXIS or Y_AXIS box to change that. REVERSE will cause the door to rotate in the opposite direction. "flags" is how many degrees the door will be rotated. "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. "health" if set, door must be shot open "speed" movement speed (100 default) "wait" wait before returning (3 default, -1 = never return) "dmg" damage to inflict when blocked (2 default) "flags2" will damage the object that touches it "strength" When set to 1, it will throw something if it gets in the way "soundtype" 0) no sound 1) Big metal door, swinging 2) Big stone door, sliding 3) Big wood door, swinging 4) Normal wood door, swinging 5) Big wood door, sliding 6) Drawbridge 7) Rotating walkway 8) Big metal door, sliding 9) Pendulum swinging "abslight" - to set the absolute light level Puzzle Pieces (use the puzzle_id value from the pieces) puzzle_piece_1 puzzle_piece_2 puzzle_piece_3 puzzle_piece_4 no_puzzle_msg: message when player doesn't have the right pieces */ void func_door_rotating() { vector vec; self.dflags=self.flags;//don't ask self.flags=0; // set the axis of rotation if (self.spawnflags & 64) self.movedir = '0 0 1'; else if (self.spawnflags & 128) self.movedir = '1 0 0'; else self.movedir = '0 1 0'; // check for clockwise rotation if (self.spawnflags & 2) self.movedir = self.movedir * -1; // CHEAT hack to get the puzzle piece flags stored in the // same area, without re-arranging the fields so that the // designers don't complain self.spawnflags (-) 192; if (self.spawnflags & 8) self.spawnflags (+) DOOR_REMOVE_PP; if (self.spawnflags & 16) self.spawnflags (+) DOOR_NO_PP; if (self.spawnflags & 32) self.spawnflags (+) DOOR_TOGGLE; self.pos1 = self.angles; self.pos2 = self.angles + self.movedir * self.dflags; self.max_health = self.health; self.solid = SOLID_BSP; self.movetype = MOVETYPE_PUSH; setorigin (self, self.origin); setmodel (self, self.model); self.classname = "door_rotating"; if (self.abslight) self.drawflags (+) MLS_ABSLIGHT; if (!self.speed) self.speed = 100; if (self.wait==0) self.wait = 3; if (!self.dmg) self.dmg = 2; if(self.wait== -2) { self.th_die = chunk_death; // self.takedamage = DAMAGE_YES; if (!self.health) { if ((self.thingtype == THINGTYPE_GLASS) || (self.thingtype == THINGTYPE_CLEARGLASS)) self.max_health = self.health = 25; else if ((self.thingtype == THINGTYPE_GREYSTONE) || (self.thingtype == THINGTYPE_BROWNSTONE)) self.max_health = self.health = 75; else if (self.thingtype == THINGTYPE_WOOD) self.max_health = self.health = 50; else if (self.thingtype == THINGTYPE_METAL) self.max_health = self.health = 100; else if (self.thingtype == THINGTYPE_FLESH) self.max_health = self.health = 30; else self.max_health = self.health = 25; } } door_sounds(); // DOOR_START_OPEN is to allow an entity to be lighted in the closed position // but spawn in the open position if (self.spawnflags & DOOR_START_OPEN) { self.angles = self.pos2; vec = self.pos2; self.pos2 = self.pos1; self.pos1 = vec; self.movedir = self.movedir * -1; } self.state = STATE_BOTTOM; self.touch = door_touch; self.blocked = door_blocked; self.use = door_use; if (self.puzzle_piece_1 != string_null || self.puzzle_piece_2 != string_null || self.puzzle_piece_3 != string_null || self.puzzle_piece_4 != string_null) self.wait = -1; // LinkDoors can't be done until all of the doors have been spawned, so // the sizes can be detected properly. self.think = LinkDoors; self.nextthink = self.ltime + 0.1; if (self.cnt) { self.touch = SUB_Null; self.use = door_rotate_incr; } if (self.flags2) { self.touch = door_damage; self.flags2=FALSE; } } gamecode/hc/h2/dthhorse.hc000066400000000000000000000503031444734033100156650ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\monsters\RdrDeath\Horse\ ============================================================================== */ // For building the model $cd Q:\art\models\monsters\RdrDeath\Horse\Final $origin 0 0 0 $base base skin $skin skin $flags 0 // Horse frames $frame Hgall1 Hgall2 Hgall3 Hgall4 Hgall5 $frame Hgall6 Hgall7 Hgall8 Hgall9 Hgall10 $frame Hgall11 Hgall12 $framevalue 0 // Rider Frames $frame Rgall1 Rgall2 Rgall3 Rgall4 Rgall5 $frame Rgall6 Rgall7 Rgall8 Rgall9 Rgall10 $frame Rgall11 Rgall12 // $frame Rsickl1 Rsickl2 Rsickl3 Rsickl4 Rsickl5 $frame Rsickl6 Rsickl7 Rsickl8 Rsickl9 Rsickl10 $frame Rsickl11 Rsickl12 float death_color[12] = { 15, 14, 13, 12, 10, 8, 6, 5, 7, 10, 13, 0 }; /* 15, 13, 11, 9, 7, 5, 3, 1, 5, 10, 12, 0 */ float death_start[1] = { $Hgall1 }; float death_end[1] = { $Hgall12 }; float death_speed[1] = { 12 }; // Array to align frames float DeathRiderFrames[4] = { $Rgall1, // Animation for gallop $Rsickl1, // Animation for attack #1 $Rsickl8, // Attack Frame 1 $Rsickl9 // Attack Frame 2 }; float DH_STAGE_NORMAL = 0; float DH_STAGE_ATTACK1 = 4; float DH_STAGE_ATTACK2 = 8; float DH_STAGE_ATTACK3 = 16; void die_out () { if(self.owner.cnt>0) self.owner.cnt -= 1; sound(self,CHAN_BODY,"death/fout.wav",1,ATTN_NORM); remove(self); } void circle_of_fire () { self.angles_y+=15; if(self.angles_y>360) self.angles_y-=360; if (self.dmg >= 80) { self.think=die_out; thinktime self : 4.2; } else { setorigin(self,self.o_angle-'0 0 76'); self.dmg+=3.33; T_RadiusDamage(self,self.owner,self.dmg/3,self.owner); makevectors(self.angles); setorigin(self,self.o_angle+v_forward*100); SpawnMummyFlame(); self.think=circle_of_fire; thinktime self : 0.05; } } void go_boom() { sound (newmis, CHAN_AUTO, "weapons/expsmall.wav", 1, ATTN_NORM); BecomeExplosion(CE_FLOOR_EXPLOSION); } void fire_circ_hit () { self.touch=SUB_Null; self.solid=SOLID_NOT; self.movetype=MOVETYPE_NONE; self.effects=EF_NODRAW; self.angles='0 0 0'; sound(self,CHAN_VOICE,"eidolon/flamend.wav",1,ATTN_NORM); sound(self,CHAN_BODY,"misc/fburn_bg.wav",1,ATTN_NORM); if(other.takedamage) { setorigin(self,other.origin+'0 0 100'); self.angles_y=other.angles_y; } self.o_angle=self.origin; self.ideal_yaw=self.angles_y; newmis=spawn(); newmis.owner = self.owner; traceline(self.origin,self.origin-'0 0 600',TRUE,self); setorigin(newmis,trace_endpos); newmis.think=go_boom; thinktime newmis : 0; self.dmg=0; self.think=circle_of_fire; thinktime self : 0; } void firecirc_fall_think() { if (self.lifetime < time || vlen(self.enemy.origin - self.origin) < 40) { other=self.enemy; self.think=self.touch; thinktime self : 0; return; } HomeThink(); self.angles=vectoangles(self.velocity); AdvanceFrame(0,9); self.think = firecirc_fall_think; thinktime self : 0.1; } void drop_fire_circ () { self.cnt += 1; self.attack_finished = time + 10; if(!self.enemy.flags2&FL_ALIVE) self.enemy=find(world,classname,"player"); if(!self.enemy) return; sound (self, CHAN_WEAPON, "death/dthfire.wav", 1, ATTN_NONE); Create_Missile(self,self.origin + v_forward*4 + v_right * 10 + v_up * 36, self.movechain.enemy.origin+self.movechain.enemy.view_ofs + self.movechain.size+'0 0 30',"models/fireball.mdl","circfire",0,700,fire_circ_hit); newmis.enemy=newmis.lockentity=self.enemy; newmis.movetype=MOVETYPE_FLYMISSILE; newmis.speed=700; //Speed newmis.turn_time=0.5; //Lower the number, tighter the turn newmis.lifetime = time + 3; newmis.think=firecirc_fall_think; newmis.owner = self; thinktime newmis : 0; } void bone_think(void) { remove(self); } void bone_touch(void) { self.flags (-) FL_ONGROUND; if (self.classname == "b4" || self.classname == "b5") remove(self); if (self.count) remove(self); self.avelocity_x /= 2; self.avelocity_y /= 2; self.avelocity_z /= 2; if (self.classname == "b6") self.velocity_z /= 2; else self.velocity_z /= 4; particle4(self.origin + '0 0 10', random(20), 256+random(80, 95), PARTICLETYPE_GRAV, random(10)); self.think = chunk_death; thinktime self : 0.1; T_Damage(other, self, self.owner, random(1,3)); } void generate_bone(void) { entity bone; float chance,c2; vector place; bone = spawn(); bone.owner = self.owner; bone.solid = SOLID_BBOX; bone.movetype = MOVETYPE_BOUNCE; chance = self.enemy.angles_y + random(-50,50); c2 = random(170,250); place_x = c2 * cos(chance); place_y = c2 * sin(chance); place_z = -10; place += self.maxs; bone.avelocity = randomv('-400 -400 -400', '400 400 400'); chance = random(); if (chance < 0.1) // rib { setmodel(bone,"models/boss/bone1.mdl"); bone.classname = "b1"; } else if (chance < 0.2) // femer? { setmodel(bone,"models/boss/bone2.mdl"); bone.classname = "b2"; } else if (chance < 0.3) // skull { setmodel(bone,"models/boss/bone6.mdl"); bone.classname = "b6"; } else if (chance < 0.6) // Small piece { setmodel(bone,"models/boss/bone4.mdl"); bone.classname = "b4"; } else // Sharp peice { setmodel(bone,"models/boss/bone5.mdl"); bone.classname = "b5"; bone.avelocity = '0 0 0'; } setsize(bone, '0 0 0', '0 0 0'); setorigin(bone, place); place = self.enemy.origin - bone.origin; place_z = 0; place = normalize(place); place *= random(200,280); place_z = random(-400, -600); bone.velocity = place; if (random() < 0.8) self.count = 1; bone.netname="deathbone"; bone.touch = bone_touch; bone.think = bone_think; thinktime bone : 5; } void bones_think(void) { if (time > self.monster_duration) { if(self.owner.cnt>0) self.owner.cnt -= 1; remove(self); return; } thinktime self : 0.1; traceline(self.enemy.origin,self.enemy.origin + '0 0 999', 1, self.enemy); self.maxs = trace_endpos; traceline(self.enemy.origin,self.enemy.origin - '0 0 999', 1, self.enemy); self.mins = trace_endpos; generate_bone(); if (random() < 0.8) generate_bone(); if (random() < 0.5) generate_bone(); } void death_bones(void) { entity dr; sound (self, CHAN_BODY, "death/dthlaugh.wav", 1, ATTN_NONE); dr = spawn(); dr.owner = self.owner; dr.solid = SOLID_NOT; dr.movetype = MOVETYPE_NONE; dr.effects = EF_NODRAW; dr.monster_duration = time + random(4,6); dr.enemy = self.enemy; dr.think = bones_think; thinktime dr : HX_FRAME_TIME; } void death_missile_die(void) { if(self.owner.cnt>0) self.owner.cnt -= 1; CreateRedCloud (self.origin,'0 0 0',HX_FRAME_TIME); remove(self.trigger_field.trigger_field); remove(self.trigger_field); remove(self); } void death_missile_touch(void) { remove(self.trigger_field.trigger_field); remove(self.trigger_field); if (other.classname != "player") {//FIXME: temp effect if(self.owner.cnt>0) self.owner.cnt -= 1; MultiExplode(); return; } death_bones(); remove(self); } void death_missile_think(void) { //float diff,adjust; if(self.lifetime<=time) { remove(self.trigger_field.trigger_field); remove(self.trigger_field); remove(self); } else { thinktime self : 0.1; self.trigger_field.trigger_field.origin = self.trigger_field.origin; self.trigger_field.trigger_field.angles = self.trigger_field.angles; self.trigger_field.origin = self.wallspot; self.trigger_field.angles = self.o_angle; self.o_angle=self.angles; self.wallspot=self.origin; HomeThink(); self.angles=vectoangles(self.velocity); } /* if (self.lifetime < time) { self.th_die(); return; } self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin); self.count += 9; if (self.count > 360) self.count = -360; adjust = sin(self.count) * 20; self.trigger_field.trigger_field.origin = self.trigger_field.origin; self.trigger_field.trigger_field.angles = self.trigger_field.angles; self.trigger_field.origin = self.origin; self.trigger_field.angles = self.angles; ChangeYaw(); walkmove(self.angles_y + adjust, 25, TRUE); if (trace_ent) { other = trace_ent; death_missile_touch(); } diff = self.enemy.origin_z - self.origin_z + self.enemy.view_ofs_z; if (diff > 10) diff = 10; else if (diff < -10) diff = -10; diff += adjust / 4; movestep(0,0,diff,FALSE); */ } void death_missile_2_touch(void) { float damg; if(self.owner.cnt>0) self.owner.cnt -= 1; if (pointcontents(self.origin) == CONTENT_SKY) { remove(self); return; } damg = random(4,8); T_Damage (other, self, self.owner, damg ); self.origin = self.origin - 8 * normalize(self.velocity) - '0 0 40'; sound (self, CHAN_WEAPON, "weapons/explode.wav", 1, ATTN_NORM); CreateRedSpark (self.origin); remove(self); } void death_missile_2_think (void) { if (self.lifetime < time || !self.enemy.flags2 & FL_ALIVE) { if(self.owner.cnt>0) self.owner.cnt -= 1; sound (self, CHAN_WEAPON, "weapons/explode.wav", 1, ATTN_NORM); CreateRedSpark (self.origin); remove(self); } else { HomeThink(); self.angles = vectoangles(self.velocity); self.think=famine_missile_think; thinktime self : 0.1; } } void death_missile_2(float dir) { entity newmis; vector diff; if(!self.enemy.flags2&FL_ALIVE) self.enemy=find(world,classname,"player"); if(!self.enemy) return; self.cnt += 1; self.attack_finished = time + 7; newmis = spawn (); newmis.enemy=newmis.lockentity=self.enemy; newmis.owner = self; newmis.movetype = MOVETYPE_FLYMISSILE; newmis.solid = SOLID_BBOX; setmodel (newmis, "models/famshot.mdl"); setsize (newmis, '0 0 0', '0 0 0'); setorigin (newmis, self.origin + '0 0 120'); newmis.angles = self.angles; makevectors(self.angles); // if (infront(self.enemy)) diff = v_forward * 16; // else // diff = v_forward * -16; newmis.velocity = normalize(diff); newmis.angles = vectoangles(newmis.velocity); if (dir == 0) newmis.angles_y -= 25; else if (dir == 2) newmis.angles_y += 25; makevectors (newmis.angles); newmis.velocity = normalize(v_forward); if (dir ==1) newmis.speed=800; //Speed else newmis.speed=700; //Speed newmis.classname = "deathmissile"; newmis.angles = vectoangles(newmis.velocity); newmis.veer=FALSE; //No random wandering newmis.turn_time=2.25; //Lower the number, tighter the turn newmis.lifetime = time + 3; newmis.think=famine_missile_think; thinktime newmis : 0.1; newmis.touch = famine_missile_touch; } void death_missile(void) { entity dm; if(!self.enemy.flags2&FL_ALIVE) self.enemy=find(world,classname,"player"); if(!self.enemy) return; self.cnt += 1; self.attack_finished = time + 10; dm = spawn(); dm.enemy=dm.lockentity=self.enemy; dm.solid = SOLID_BBOX; dm.movetype = MOVETYPE_FLYMISSILE; dm.classname = "death_missile"; // dm.flags (+) FL_FLY; dm.drawflags (+) DRF_TRANSLUCENT | MLS_POWERMODE; dm.enemy = self.enemy; // dm.yaw_speed = 8; dm.takedamage = DAMAGE_YES; dm.health = 10; dm.owner = self; dm.scale = 1.5; setmodel(dm,"models/boss/bone3.mdl"); setorigin(dm, self.movechain.origin+self.movechain.size); dm.think = death_missile_think; dm.touch = death_missile_touch; dm.th_die = death_missile_die; dm.lifetime = time + 5; dm.speed=500; dm.veer=50; //some random wandering dm.turn_time=2; //Lower the number, tighter the turn setsize(dm,'0 0 0', '0 0 0'); thinktime dm : HX_FRAME_TIME; dm.trigger_field = spawn(); dm = dm.trigger_field; dm.classname = "death_missile"; dm.solid = SOLID_NOT; dm.movetype = MOVETYPE_FLYMISSILE; dm.flags (+) FL_FLY; dm.drawflags (+) DRF_TRANSLUCENT | MLS_POWERMODE | SCALE_TYPE_UNIFORM; dm.enemy = self.enemy; dm.owner = dm.trigger_field; dm.lifetime = time + 5; setmodel(dm,"models/boss/bone3.mdl"); setsize(dm,'0 0 0', '0 0 0'); setorigin(dm, self.origin); dm.trigger_field = spawn(); dm = dm.trigger_field; dm.classname = "death_missile"; dm.solid = SOLID_NOT; dm.movetype = MOVETYPE_FLYMISSILE; dm.flags (+) FL_FLY; dm.drawflags (+) DRF_TRANSLUCENT | MLS_POWERMODE | SCALE_TYPE_UNIFORM; dm.enemy = self.enemy; dm.owner = dm.trigger_field; dm.scale = .5; dm.lifetime = time + 5; setmodel(dm,"models/boss/bone3.mdl"); setsize(dm,'0 0 0', '0 0 0'); setorigin(dm, self.origin); sound (dm, CHAN_BODY, "ambience/moan1.wav", 1, ATTN_NORM); } void create_deathrider(entity horse) { entity rider; rider = spawn(); rider.monsterclass = CLASS_BOSS; rider.solid = SOLID_NOT; rider.movetype = MOVETYPE_NONE; rider.origin = horse.origin; rider.angles = self.angles; setmodel (rider, "models/boss/dthrider.mdl"); rider.skin = 0; horse.movechain = rider; self.cnt = 0; rider.flags (+) FL_MOVECHAIN_ANGLE; } void ghost_tint () { if(self.cnt<12) { if(!self.cnt) { self.touch=SUB_Null; self.effects=EF_NODRAW; } self.enemy.colormap = self.enemy.movechain.colormap = 2*16+ death_color[self.cnt]; self.cnt+=1; thinktime self : 0.05; } else { self.enemy.colormap = self.enemy.movechain.colormap = 0; remove(self); } } void ghost_touch () { if(other.classname!="rider_death"||self.lifespan>time) return; else { if(!self.owner.flags2&FL_ALIVE) self.owner.enemy=other; sound(self.enemy,CHAN_VOICE,"death/victory.wav",1,ATTN_NONE); self.think=ghost_tint; thinktime self : 0; } } void ghost_think () { if(self.scale<=2.4) self.scale+=0.1; if(self.speed<333) self.speed+=7; HomeThink(); self.flags(-)FL_ONGROUND; self.angles=vectoangles(self.velocity); thinktime self : 0.1; } void spawn_ghost (entity attacker) { float r; newmis=spawn(); newmis.movetype=MOVETYPE_NOCLIP; newmis.solid=SOLID_TRIGGER; newmis.classname=newmis.netname="Booberry"; newmis.owner=self; newmis.drawflags(+)MLS_POWERMODE|DRF_TRANSLUCENT; newmis.scale=0.5; newmis.enemy=newmis.lockentity=attacker; self.enemy=newmis; newmis.flags2(+)FL_ALIVE; newmis.veer=100; newmis.speed=100; newmis.lifespan=time+7; newmis.turn_time=2; newmis.hoverz=TRUE; newmis.touch=ghost_touch; newmis.think=ghost_think; makevectors(self.angles); newmis.velocity=v_forward*150+v_up*30; newmis.angles=vectoangles(newmis.velocity); newmis.think=ghost_think; thinktime newmis : 1; setmodel(newmis,"models/booberry.mdl"); setsize(newmis,'-8 -8 -8','8 8 8'); newmis.hull=HULL_POINT; setorigin(newmis,(self.absmin+self.absmax)*0.5); r=random(); if(r<0.33) newmis.noise="ambience/moan1.wav"; else if(r<0.66) newmis.noise="ambience/moan2.wav"; else newmis.noise="ambience/moan3.wav"; sound(newmis,CHAN_AUTO,newmis.noise,1,ATTN_NONE); } void deathhorse_move(void) { float retval, r; if (self.velocity) self.velocity = self.velocity * 0.9; if (coop) checkenemy(); if(!self.enemy.flags2&FL_ALIVE&&self.enemy!=world) self.enemy=world; self.think = deathhorse_move; thinktime self : HX_FRAME_TIME; retval = AdvanceFrame(death_start[self.rider_gallop_mode],death_end[self.rider_gallop_mode]); self.movechain.angles = self.angles; self.movechain.origin = self.origin; if (!self.path_current) { riderpath_init(); } if (self.frame == 4 || self.frame == 6 || self.frame == 8 || self.frame == 10) { r = random(); if (r < 0.2) sound (self, CHAN_BODY, "death/clop.wav", 0.3, ATTN_NORM); else if (r < 0.4) sound (self, CHAN_BODY, "death/clop1.wav", 0.3, ATTN_NORM); else if (r < 0.6) sound (self, CHAN_BODY, "death/clop2.wav", 0.3, ATTN_NORM); else sound (self, CHAN_BODY, "death/clop3.wav", 0.3, ATTN_NORM); } riderpath_move(self.speed); if (retval == AF_BEGINNING) { retval = fabs(self.rider_y_change); // Force a new gallop frame in self.frame = death_start[self.rider_gallop_mode]; self.monster_stage = DH_STAGE_NORMAL; if (self.rider_gallop_mode == 0) { if (!self.enemy) { self.enemy = find(world, classname, "player"); while(!self.enemy.flags2&FL_ALIVE&&self.enemy!=world) self.enemy = find(self.enemy, classname, "player"); } if (self.enemy != world && random() < 0.7+skill/10&&!self.cnt) { r = random(); if (r < 0.3) self.monster_stage = DH_STAGE_ATTACK1; else if (r < 0.6) self.monster_stage = DH_STAGE_ATTACK3; else self.monster_stage = DH_STAGE_ATTACK2; } } } if (self.cnt && self.attack_finished < time) self.cnt = 0; if (self.monster_stage && self.enemy.flags2 & FL_ALIVE) { if (self.rider_gallop_mode == 0) { if (self.monster_stage == DH_STAGE_ATTACK1) { self.movechain.frame = DeathRiderFrames[1] + (self.frame - death_start[self.rider_gallop_mode]); if (self.movechain.frame == DeathRiderFrames[2]) { sound (self, CHAN_WEAPON, "death/dthfire.wav", 1, ATTN_NONE); death_missile(); } } else if (self.monster_stage == DH_STAGE_ATTACK2) { self.movechain.frame = DeathRiderFrames[1] + (self.frame - death_start[self.rider_gallop_mode]); if (self.movechain.frame == 18) sound (self, CHAN_WEAPON, "death/shot.wav", 1, ATTN_NORM); if (self.movechain.frame == 18 || self.movechain.frame == 20 || self.movechain.frame == 22) death_missile_2(FALSE); } else if (self.monster_stage == DH_STAGE_ATTACK3) { self.movechain.frame = DeathRiderFrames[1] + (self.frame - death_start[self.rider_gallop_mode]); if (self.movechain.frame == DeathRiderFrames[2]) drop_fire_circ(); } self.colormap = self.movechain.colormap = death_color[self.frame - death_start[self.rider_gallop_mode]]; if(self.colormap) { self.colormap+=8*16; self.movechain.colormap+=8*16; } } } else { self.movechain.frame = DeathRiderFrames[self.rider_gallop_mode] + (self.frame - death_start[self.rider_gallop_mode]); } // make sure we use the last attack frame before we go out of the mode if (retval == AF_END) { self.monster_stage = DH_STAGE_NORMAL; } if (fabs(death_speed[self.rider_gallop_mode] - self.speed) < 0.2) self.speed = death_speed[self.rider_gallop_mode]; else if (death_speed[self.rider_gallop_mode] > self.speed) self.speed += 0.2; else self.speed -= 0.2; } /*QUAKED rider_death (1 0 0) (-55 -55 -24) (55 55 100) TRIGGER_WAIT Death rider monster. You must place rider_path entites on the map. The rider will first proceed to the rider_path point with a path_id of 1. -------------------------FIELDS------------------------- map: next map to go to when you kill the rider target: start spot on the next map -------------------------------------------------------- */ void rider_death(void) { if (deathmatch) { remove(self); return; } precache_model2 ("models/boss/dthhorse.mdl"); precache_model2 ("models/boss/dthrider.mdl"); precache_model2 ("models/famshot.mdl"); precache_model2 ("models/boss/bone1.mdl"); precache_model2 ("models/boss/bone2.mdl"); precache_model2 ("models/boss/bone3.mdl"); precache_model2 ("models/boss/bone4.mdl"); precache_model2 ("models/boss/bone5.mdl"); precache_model2 ("models/boss/bone6.mdl"); precache_model2 ("models/mumshot.mdl"); precache_model2 ("models/booberry.mdl"); precache_sound2 ("mummy/mislfire.wav"); precache_sound2 ("eidolon/flamend.wav"); precache_sound2 ("misc/fburn_bg.wav"); precache_sound2 ("death/fout.wav"); precache_sound2 ("death/dthdie.wav"); precache_sound2 ("death/dthfire.wav"); precache_sound2 ("death/victory.wav"); precache_sound2 ("death/dthlaugh.wav"); precache_sound2 ("death/clop.wav"); precache_sound2 ("death/clop1.wav"); precache_sound2 ("death/clop2.wav"); precache_sound2 ("death/clop3.wav"); precache_sound2 ("death/shot.wav"); precache_sound2 ("ambience/moan1.wav"); precache_sound2 ("ambience/moan2.wav"); precache_sound2 ("ambience/moan3.wav"); rider_init(); self.solid = SOLID_SLIDEBOX; self.movetype = MOVETYPE_FLY; self.thingtype = THINGTYPE_FLESH; self.yaw_speed = 4; setmodel (self, "models/boss/dthhorse.mdl"); self.hull = HULL_POINT; self.skin = 0; self.flags (+) FL_FLY|FL_MONSTER; self.flags2(+) FL_ALIVE; self.monsterclass = CLASS_BOSS; setsize (self, '-55 -55 -24', '55 55 100'); self.health = self.max_health = 3500; self.experience_value = 1000; self.dflags = 0; self.rider_gallop_mode = 0; self.speed = death_speed[self.rider_gallop_mode]; self.rider_path_distance = 100; self.monster_stage = DH_STAGE_NORMAL; self.mass = 30000; create_deathrider(self); self.noise = "death/dthdie.wav"; self.delay = 3; self.th_save = deathhorse_move; self.think = multiplayer_health; thinktime self : 1; } gamecode/hc/h2/eidolon.hc000066400000000000000000001044321444734033100155010ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\monsters\SerpentR\final\big\bigeido.hc MG ============================================================================== */ /* // For building the model $cd Q:\art\models\monsters\SerpentR\final\big $origin 0 0 0 $base BASE SKIN $skin SKIN $skin SKIN2 $flags 0 $scale 2.5 */ //Shared $frame painA1 painA2 painA3 painA4 painA5 $frame painA6 painA7 painA8 painA9 // $frame spell1 spell2 spell3 spell4 spell5 $frame spell6 spell7 spell8 spell9 spell10 $frame spell11 spell12 spell13 spell14 spell15 $frame spell16 spell17 spell18 spell19 spell20 // $frame tranA1 tranA2 tranA3 tranA4 tranA5 $frame tranA6 tranA7 tranA8 // $frame tranB1 tranB2 tranB3 tranB4 tranB5 $frame tranB6 tranB7 tranB8 // $frame wait1 wait2 wait3 wait4 wait5 $frame wait6 wait7 wait8 wait9 wait10 $frame wait11 wait12 wait13 wait14 wait15 $frame wait16 // $frame walk1 walk2 walk3 walk4 walk5 $frame walk6 walk7 walk8 walk9 walk10 $frame walk11 walk12 walk13 walk14 walk15 $frame walk16 walk17 walk18 walk19 walk20 $frame walk21 walk22 walk23 walk24 // $frame howl1 howl2 howl3 howl4 howl5 $frame howl6 howl7 howl8 howl9 howl10 $frame howl11 howl12 howl13 howl14 howl15 $frame howl16 howl17 howl18 howl19 howl20 $frame howl21 howl22 howl23 howl24 howl25 $frame howl26 howl27 howl28 howl29 howl30 $frame howl31 howl32 howl33 howl34 howl35 $frame howl36 howl37 howl38 howl39 howl40 $frame howl41 howl42 howl43 howl44 howl45 $frame howl46 howl47 howl48 howl49 howl50 $frame howl51 howl52 howl53 howl54 howl55 $frame howl56 howl57 howl58 howl59 howl60 $framesave x //SMALL // // $frame death1 death2 death3 death4 death5 $frame death6 death7 death8 death9 death10 $frame death11 death12 death13 death14 death15 $frame death16 death17 death18 death19 death20 $frame death21 death22 death23 death24 death25 $frame death26 death27 death28 death29 death30 // $frame dwait1 dwait3 dwait5 $frame dwait7 dwait9 $frame dwait11 dwait13 dwait15 $frame dwait17 dwait19 $frame dwait21 dwait23 dwait25 $frame dwait27 dwait29 // $frame grow1 grow3 grow5 $frame grow7 grow9 $frame grow11 grow13 grow15 $frame grow17 grow19 $frame grow21 grow23 grow25 $frame grow27 grow29 $frame grow31 grow33 grow35 $frame grow37 grow39 $frame grow41 grow43 grow45 $frame grow47 grow49 $frame grow51 grow53 grow55 $frame grow57 grow59 $frame grow61 grow63 grow65 $frame grow67 grow69 $frame grow71 grow72 grow73 grow74 grow75 $frame grow76 grow77 grow78 grow79 grow80 $frame grow81 grow82 grow83 grow84 grow85 $frame grow86 grow87 grow88 grow89 grow90 $frame grow91 grow92 grow93 grow94 grow95 $frame grow96 grow97 grow98 grow99 grow100 $framerestore x //BIG // $frame breath1 breath2 breath3 breath4 breath5 $frame breath6 breath7 breath8 breath9 breath10 $frame breath11 breath12 breath13 breath14 breath15 $frame breath16 breath17 breath18 breath19 breath20 $frame breath21 breath22 breath23 breath24 breath25 $frame breath26 breath27 breath28 breath29 breath30 $frame breath31 breath32 breath33 breath34 breath35 $frame breath36 breath37 breath38 breath39 breath40 $frame breath41 breath42 breath43 breath44 breath45 $frame breath46 breath47 breath48 breath49 breath50 // $frame painB1 painB2 painB3 painB4 painB5 $frame painB6 painB7 painB8 painB9 painB10 $frame painB11 painB12 painB13 painB14 painB15 $frame painB16 painB17 painB18 painB19 painB20 // $frame power1 power2 power3 power4 power5 $frame power6 power7 power8 power9 power10 $frame power11 power12 power13 power14 power15 $frame power16 power17 power18 power19 power20 /*========================================================*/ void()orb_wait; void()eidolon_orb_pain; void()eidolon_run; void()eidolon_roar; void()eidolon_ready_roar; void()eidolon_grow; void(entity attacker,float total_damage)eidolon_check_fake; void()eidolon_power; void()eidolon_face_orb; void orb_die() { self.owner.health=4000+skill*2000; self.owner.th_save=eidolon_ready_roar; self.owner.controller=world; self.owner.goalentity=self.owner.enemy; self.owner.think=multiplayer_health; thinktime self.owner : 0; sound(self,CHAN_AUTO,"eidolon/orbxpld.wav",1,ATTN_NONE); MonsterQuake(500); MultiExplode(); } void orb_tint_flash_from_red() [++ 0 .. 35] { self.colormap+=1; if(self.colormap==143) self.think=orb_wait; } void orb_tint_flash_to_red() [++ 0 .. 35] { self.colormap-=1; if(self.colormap==128) self.think=orb_tint_flash_from_red; } void orb_pain (entity attacker,float damage) { if(attacker==self.owner||self.owner.think==eidolon_grow) { self.health+=damage; return; } if(self.pain_finished155) self.colormap-=1; self.v_angle=randomv('0 0 0','360 360 360'); makevectors(self.v_angle); self.view_ofs=self.origin+'0 0 1'*self.absmax_z*0.6+v_forward*44; if(self.owner.think==eidolon_grow) { self.proj_ofs=(self.owner.absmin+self.owner.absmax)*0.5-'0 0 64'; self.proj_ofs_x+=random(0-self.owner.size_x,self.owner.size_x); self.proj_ofs_y+=random(0-self.owner.size_x,self.owner.size_x); self.proj_ofs_z+=random(0-self.owner.size_x,self.owner.size_x); } else { makevectors(self.owner.angles); self.proj_ofs=self.owner.origin+self.owner.proj_ofs+v_forward*200+v_right*36+'0 0 100'; } do_lightning (self.owner,self.weaponframe_cnt,0, 2, self.view_ofs, self.proj_ofs,0); } void orb_lightning_pattern () [++ 0 .. 35] { if(self.frame==0) sound(self,CHAN_BODY,"eidolon/orbpulse.wav",1,ATTN_NONE); if(self.cnt) { if(self.weaponframe_cnt<16) self.weaponframe_cnt+=1; else self.weaponframe_cnt=0; self.cnt-=1; self.view_ofs=self.proj_ofs; // if(random()<0.5) self.v_angle+=randomv('15 15 15','45 45 45'); // else // self.v_angle-=randomv('15 15 15','45 45 45'); makevectors(self.v_angle); self.proj_ofs=self.origin+'0 0 1'*self.absmax_z*0.6+v_forward*54; do_lightning (self.owner,self.weaponframe_cnt,0, random(4), self.view_ofs, self.proj_ofs,0); } else { self.think=orb_wait; thinktime self : 0; } } void orb_lightning_pattern_init () { // dprint("Orb starting lightning\n"); self.cnt=random(40,60); self.weaponframe_cnt=0; self.v_angle=randomv('0 0 0','360 360 360'); makevectors(self.v_angle); self.view_ofs=self.origin+'0 0 1'*self.absmax_z*0.6+v_forward*54; self.v_angle+=randomv('-45 -45 -45','45 45 45'); makevectors(self.v_angle); self.proj_ofs=self.origin+'0 0 1'*self.absmax_z*0.6+v_forward*54; do_lightning (self.owner,self.weaponframe_cnt,0, random(3), self.view_ofs, self.proj_ofs,0); self.think=orb_lightning_pattern; thinktime self :0.05; } void orb_wait () [++ 0 .. 35] { self.aflag=FALSE; if(self.frame==0) sound(self,CHAN_BODY,"eidolon/orbpulse.wav",1,ATTN_NONE); self.velocity='0 0 0'; self.colormap=0; if(random()<0.1) { self.think=orb_lightning_pattern_init; thinktime self :0; } } void obj_chaos_orb_find_movechain () { //So chaos orb moves with platform but doesn't block geometry entity found; found=find(world,netname,self.netname); if(found) { found.movechain=self; self.flags(+)FL_MOVECHAIN_ANGLE; } self.think=orb_wait; thinktime self : 0; } /*QUAKED obj_chaos_orb (1 0 0) (-100 -100 0) (100 100 200) JEAN LUC PICARD Big round smooth thingie. -------------------------FIELDS------------------------- -------------------------------------------------------- */ void obj_chaos_orb () { if(deathmatch) { remove(self); return; } precache_model2 ("models/boss/chaosorb.mdl"); self.solid = SOLID_BBOX; self.movetype = MOVETYPE_NOCLIP; self.takedamage=DAMAGE_YES; self.thingtype=THINGTYPE_GLASS; setmodel (self, "models/boss/chaosorb.mdl"); setsize (self, '-48 -48 0', '48 48 123'); self.hull=HULL_POINT; self.monsterclass=CLASS_BOSS; self.health = self.max_health = 2000; self.mass = 5000; self.cnt=50; self.flags2(+)FL_ALIVE; self.dmg=200; self.th_die=orb_die; self.th_pain=orb_pain; self.drawflags(+)SCALE_ORIGIN_BOTTOM|MLS_POWERMODE; self.think=obj_chaos_orb_find_movechain; thinktime self : 0.1; } //==================================================================== void() eidolon_walk; void()eidolon_wait; float eidolon_check_attack() { vector spot1, spot2; entity targ; if(self.movetype) return FALSE; if(self.goalentity==self.controller) return FALSE; targ = self.enemy; // see if any entities are in the way of the shot spot1 = self.origin + self.proj_ofs; spot2 = (targ.absmin+targ.absmax)*0.5; traceline (spot1, spot2, FALSE, self); if(trace_ent.thingtype>=THINGTYPE_WEBS) traceline (trace_endpos, spot2, FALSE, trace_ent); if (trace_ent != targ) if(trace_ent.health>200||!trace_ent.takedamage||(trace_ent.flags&FL_MONSTER&&trace_ent.classname!="player_sheep")) return FALSE;//Don't have a clear shot, and don't want to shoot obstruction enemy_range=vlen(self.enemy.origin-self.origin); if (enemy_range < 200&&self.scale>1) { self.th_melee(); return TRUE; } // missile attack if (time < self.attack_finished) return FALSE; if(random()<0.3 - skill/10&&self.controller.flags2&FL_ALIVE) return FALSE; self.th_missile (); SUB_AttackFinished (random(0,2)); return TRUE; } void check_use_model (string whichmodel) { if(self.model!=whichmodel) setmodel(self,whichmodel); if(!self.flags2&FL_SMALL) setsize(self,'-54 -54 0', '54 54 666'); else setsize(self,'-16 -16 0', '16 16 200'); self.hull=HULL_POINT; } float eidolon_riderpath_move(float move_speed) { entity next_path; float distance;//, altitude, temp; vector displace; if(self.movetype==MOVETYPE_NOCLIP) self.velocity='0 0 0'; next_path=riderpath_findbest(self.path_last); distance = vhlen(self.origin - self.path_current.origin); if (distance < self.rider_path_distance) { if(next_path==world) //Already on closest path return FALSE; else if(self.turn_time$howl5 && self.frame<$howl55 &&random()<0.3) self.frame+=random(-4,4); self.angles_y+=random(-11,9); if(self.scale>2.45) self.scale=2.3; else if(self.scale>1&&self.scale<=2.45) self.scale+=random(-0.1,0.05); if(self.abslight>2) self.abslight=1.7; else if(self.abslight>0.3&&self.abslight<=2) self.abslight+=random(-0.05,0.05); if(self.lifetime$howl13) eidolon_spawn_lightning(); if(self.frame==$howl60) { self.movetype = MOVETYPE_NONE; self.flags(-)FL_GODMODE; self.th_pain = eidolon_check_fake; self.attack_finished=time+3; self.think=eidolon_guarding; } } void eidolon_ready_roar() { // check_use_model("models/boss/bigeido.mdl"); sound(self,CHAN_VOICE,"eidolon/roar.wav",1,ATTN_NONE); self.attack_finished=time+1.5; self.frame=$howl1; self.think=eidolon_roar; thinktime self : 0; } void eidolon_grow () [++ $grow1 .. $grow100] { //FIXME: If player too close, push away entity found; if(self.frame<$grow71) thinktime self : 0.1; // check_use_model("models/boss/bigeido.mdl"); if(self.scale<2.52) self.scale+=0.03; setsize (self, '-16 -16 0'*1.3333333*self.scale, '16 16 200'*1.3333333*self.scale); self.mass=2000*1.34*self.scale; self.hull=HULL_POINT; self.health=self.max_health; found=findradius(self.origin,self.size_x+25); while(found) { if(found!=self&&found.solid&&found.movetype&&found.health&&found.flags2&FL_ALIVE) { found.velocity=normalize(found.origin-self.origin)*100; found.velocity_z+=100; found.flags(-)FL_ONGROUND; T_Damage(found,self,self,3); } found=found.chain; } if(cycle_wrapped) { self.scale=2.55; self.rider_path_distance=64; self.weapon=0; self.health=10000; self.experience_value=100000; self.drawflags(-)MLS_POWERMODE; self.flags2(-)FL_SMALL; self.controller.think=orb_wait; thinktime self.controller : 0; self.proj_ofs=self.view_ofs='0 0 200'; check_use_model("models/boss/bigeido.mdl"); self.frame=$howl1; self.think=eidolon_ready_roar; thinktime self : 1; } } void eidolon_ready_grow () [++ $dwait1 .. $dwait29] { thinktime self : 0.1; if(self.frame==$dwait29 &&self.lifetime2) { lightval-=1; lightstylestatic(self.lockentity.style,lightval); } else if(self.frame==$dwait29) /* if(infront_of_ent(self,self.enemy)) if(infront_of_ent(self.controller,self.enemy)) if(vlen(self.enemy.origin-self.origin)<1500) { self.velocity='0 0 0'; self.movetype=MOVETYPE_NOCLIP; self.flags(+)FL_FLY; MonsterQuake(500); SUB_UseTargets(); self.target=""; self.lifetime=time+2; self.think=eidolon_ready_grow; } self.health=self.max_health; self.lockentity.lightvalue1=lightval; */ { watcher=self.enemy; if(self.enemy.classname=="monster_imp_lord")//if enemy an imp, look for it's owner watcher=self.enemy.controller; if(!watcher.flags2&FL_ALIVE) {//If enemy not alive, look for other players watcher=find(world,classname,"player"); while(watcher!=world&&!watcher.flags2&FL_ALIVE) watcher=find(watcher,classname,"player"); } if(infront_of_ent(self,watcher)) if(infront_of_ent(self.controller,watcher)) if(vlen(self.enemy.origin-self.origin)<1500) { self.velocity='0 0 0'; self.movetype=MOVETYPE_NOCLIP; self.flags(+)FL_FLY; MonsterQuake(500); SUB_UseTargets(); self.target=""; self.lifetime=time+2; self.think=eidolon_ready_grow; } } self.health=self.max_health; self.lockentity.lightvalue1=lightval; } void eidolon_fake_die () [++ $death1 .. $death30] { // check_use_model("models/boss/smaleido.mdl"); self.health=self.max_health; if(self.frame==$death30) if(self.lockentity!=world) { self.lockentity.wait=100; self.lockentity.dmg=0; self.think=eidolon_darken_sky; } else { self.flags(-)FL_GODMODE; self.th_pain=eidolon_check_fake; self.think=eidolon_run; } } void eidolon_orb_pain () [++ $painB1 .. $painB20] { if(self.frame==$painB1) { // check_use_model("models/boss/bigeido.mdl"); if(self.controller.think==orb_lightning_recharge) { self.controller.think=orb_wait; thinktime self.controller : 0; } } if(self.frame==$painB20) { self.weapon=0; self.goalentity=self.enemy; self.colormap=0; self.level=FALSE; self.think=eidolon_run; } } void()eidolon_face_orb; void eidolon_pain () [++ $painA1 .. $painA9] { // if(self.frame==$painA1) // check_use_model("models/boss/smaleido.mdl"); if(self.frame==$painA9) { if(self.weapon>=1000&&self.controller.flags2&FL_ALIVE) { self.weapon=0; self.think=eidolon_face_orb; } else self.think=eidolon_run; } } void eidolon_check_fake (entity attacker,float total_damage) { float pain_chance; if(self.controller.flags2&FL_ALIVE||self.scale<1) {//orb alive or still small self.dmg+=self.max_health-self.health; self.health=self.max_health; if(self.scale>1) { pain_chance=0.1; self.weapon+=total_damage; } else { pain_chance=0.6; self.weapon=0; } } else {//Big and orb dead pain_chance=0.2; self.dmg=0; } pain_chance-=self.torncount*0.02; if(self.movetype!=MOVETYPE_NONE) return; if(self.pain_finished>time) return; self.pain_finished=time+3+skill; if(self.dmg>=2000&&self.scale<1) { /* see devel/eidolon.txt for why god mode is needed. */ self.flags(+)FL_GODMODE; self.th_pain=SUB_Null; if(attacker.classname=="player") AwardExperience(attacker,self,self.experience_value); sound(self,CHAN_VOICE,"eidolon/fakedie.wav",1,ATTN_NONE); self.goalentity=self.enemy; self.think=eidolon_fake_die; } else if(random()=1000) { self.goalentity=self.enemy; sound(self,CHAN_VOICE,"eidolon/pain.wav",1,ATTN_NONE); if(self.level) self.think=eidolon_orb_pain; else self.think=eidolon_pain; } else return; thinktime self : 0; } void eidolon_fireball (void) { entity missile; self.last_attack=time; self.cnt+=1; if(self.cnt==2) { self.attack_finished=time+7; self.cnt=0; } missile = spawn (); missile.owner = self; missile.movetype = MOVETYPE_FLYMISSILE; missile.solid = SOLID_BBOX; missile.classname = "eidolon fireball"; // set missile speed makevectors (self.angles); missile.enemy=self.enemy; setmodel (missile, "models/eidoball.mdl"); setsize (missile, '0 0 0', '0 0 0'); setorigin (missile, self.origin + v_forward*128 + self.proj_ofs+v_right*24-v_up*12); missile.wallspot=normalize((self.enemy.absmin+self.enemy.absmax)*0.5-missile.origin); missile.movedir=v_forward; missile.movedir_z=missile.wallspot_z; missile.speed=1000; missile.velocity = missile.movedir*missile.speed; missile.touch = pmissile2_touch; missile.angles = vectoangles(missile.velocity); sound(self,CHAN_AUTO,"eidolon/fireball.wav",1,ATTN_NONE); thinktime missile : 0.15; missile.think = pmissile2_puff; missile.lifetime = time + 2; missile.drawflags(+)MLS_ABSLIGHT; missile.abslight=0.5; // missile.effects=EF_BRIGHTLIGHT; missile.scale=2; //Homing stuff------------------- missile.veer=FALSE; //No random wandering missile.turn_time=2;//Lower the number, tighter the turn missile.ideal_yaw=TRUE;//Only track things in front //End homing stuff------------------- } void flame_stream_touch () { if(other.classname=="flamestream") return; self.effects(+)EF_MUZZLEFLASH; if(other.takedamage) T_Damage(other,self,self.owner,self.dmg); else T_RadiusDamage(self,self.owner,self.dmg*2,self.owner); if(self.frame<24) self.frame=24; } void eidolon_power () [++ $power1 .. $power20] { // check_use_model("models/boss/bigeido.mdl"); if(self.frame==$power20) { self.colormap=0; self.level=FALSE; self.controller.think=orb_wait; self.goalentity=self.enemy; self.think=eidolon_run; thinktime self : 0; } if(self.frame==$power8) { self.controller.think=orb_lightning_recharge; thinktime self.controller : 0; } if(self.frame>=$power5 && self.frame<=$power15) { if(self.frame==$power10) thinktime self : 2; if(self.frame<$power10 ) self.colormap=159 - (self.frame-$power5); else self.colormap=159 - ($power15 - self.frame); } } void eidolon_face_orb () [++ $walk1 .. $walk16] { // check_use_model("models/boss/smaleido.mdl"); self.ideal_yaw = vectoyaw(self.controller.origin - self.origin); ChangeYaw(); if(self.angles_y>self.ideal_yaw - 10&&self.angles_y=$breath25 &&self.frame<=$breath41) { makevectors(self.angles); if(!self.aflag) { // sound(self,CHAN_BODY,"misc/combust.wav",1,ATTN_NONE); // bprint("Flame start\n"); sound(self,CHAN_BODY,"eidolon/flamstrt.wav",1,ATTN_NONE); self.aflag=TRUE; } if(self.t_width0.85) { newmis.movedir_y=newmis.wallspot_y; newmis.movedir_x=newmis.wallspot_x; } newmis.speed=300+random(50); newmis.velocity=newmis.movedir*newmis.speed; setmodel(newmis,"models/eidoflam.spr"); newmis.think=fire_anim; thinktime newmis : 0; setsize(newmis,'0 0 0','0 0 0'); setorigin(newmis,newmis.o_angle); self.attack_finished+=1; } if(self.frame==$breath50) { sound(self,CHAN_VOICE,"eidolon/flamend.wav",1,ATTN_NONE); self.aflag=FALSE; self.attack_finished=time+10; self.think=eidolon_guarding; } if(self.frame>$breath24 && self.frame<$breath41) { if(!self.frags) { self.frame-=1; self.frags=TRUE; } else self.frags=FALSE; thinktime self : 0.025; } } void eidolon_fireballs () [++ $breath1 .. $breath50] { // check_use_model("models/boss/bigeido.mdl"); ai_face(); if(self.frame==$breath36) eidolon_fireball(); if(random()<(0.1+skill/10)&&self.frame>$breath24 && self.frame<$breath41) eidolon_fireball(); if(self.frame==$breath50) { self.attack_finished=time+3; self.think=eidolon_guarding; } } void EidoPoly () { vector forward_dir; float dot; makevectors(self.angles); newmis=spawn(); newmis.movetype=MOVETYPE_FLYMISSILE; newmis.solid=SOLID_BBOX; newmis.owner=self; newmis.touch=poly_touch; newmis.speed=700; forward_dir=v_forward; newmis.o_angle=self.origin+self.proj_ofs+forward_dir*220+v_right*36; newmis.wallspot=normalize(self.enemy.origin-newmis.o_angle); newmis.movedir=forward_dir; newmis.movedir_z=newmis.wallspot_z*1.7; newmis.wallspot_z=0; makevectors(newmis.wallspot); dot=forward_dir*v_forward; if(dot>0.85) { newmis.movedir_y=newmis.wallspot_y; newmis.movedir_x=newmis.wallspot_x; } newmis.velocity=newmis.movedir*newmis.speed+v_right*random(-100,100); newmis.drawflags=MLS_POWERMODE; sound(newmis,CHAN_BODY,"necro/mmfire.wav",1,ATTN_NORM); newmis.think=polymorph_anim; thinktime newmis : 0; newmis.scale=2; setmodel(newmis,"models/polymrph.spr"); setsize(newmis,'0 0 0','0 0 0'); setorigin(newmis,newmis.o_angle); } void eidolon_spell () [++ $spell1 .. $spell20] { // check_use_model("models/boss/smaleido.mdl"); ai_face(); if((random()<(0.2+skill/10)&&self.frame>=$spell8 &&self.frame<=$spell16)||self.frame==$spell10) { makevectors(self.angles); self.movedir=normalize((self.enemy.absmax+self.enemy.absmin)*0.5-(self.origin+self.proj_ofs+v_forward*64)); self.v_angle=v_forward; self.v_angle_z=self.movedir_z; if(self.veer) EidoPoly(); else FireMagicMissile(0); } if(self.frame==$spell20) { self.veer=FALSE; self.attack_finished=time+2; self.think=eidolon_guarding; } } void eidolon_fire () { float enemy_dist; //NOTE: Use special pain- if hit him in the mouth as he's // about to use fireball or flames- hurts bad if(self.scale>1) if(random()<0.1) if(random()<0.1) { self.veer=TRUE; eidolon_spell(); return; } enemy_dist=vlen(self.enemy.origin-self.origin); if(self.scale==0.75) { if(range(self.enemy)<=RANGE_MELEE) UseBlast(); self.artifact_active(+)ART_TOMEOFPOWER; eidolon_spell(); } else if(enemy_dist>128&&enemy_dist<424) self.think=eidolon_flames; else if(random()<0.2) self.think=eidolon_ready_roar; else { enemy_vis=visible(self.enemy); enemy_infront=infront(self.enemy); if(enemy_vis&&enemy_infront) self.think=eidolon_fireballs; else self.cnt=FALSE; } } void eidolon_find_lightning () { entity found; found=find(world,classname,"light_thunderstorm"); if(found) self.lockentity=found; } void eidolon_find_orb () { entity found; found=find(world,classname,"obj_chaos_orb"); if(found) { found.owner=self; self.controller=found; } } void eidolon_walk () [++ $walk1 .. $walk24] { // check_use_model("models/boss/smaleido.mdl"); if(self.scale>1&&(self.frame==$walk2 ||self.frame==$walk14)) sound(self,CHAN_BODY,"eidolon/stomp.wav",1,ATTN_NONE); if(!self.lockentity) eidolon_find_lightning(); if(!self.controller) eidolon_find_orb(); ai_walk(self.speed*self.scale); } void eidolon_enemy () { if (self.enemy == world) { if (self.oldenemy != world && self.oldenemy.flags2 & FL_ALIVE) { self.enemy = self.oldenemy; self.goalentity = self.enemy; } else self.think = eidolon_wait; } else if (!self.enemy.flags2 & FL_ALIVE) { self.think = eidolon_ready_roar; self.enemy = world; } else ai_run(self.speed * self.scale); } void eidolon_run () [++ $walk1 .. $walk24] { // dprint("Chasing\n"); // check_use_model("models/boss/smaleido.mdl"); if(self.scale>1&&(self.frame==$walk2 ||self.frame==$walk14)) sound(self,CHAN_BODY,"eidolon/stomp.wav",1,ATTN_NONE); eidolon_enemy(); } void eidolon_guarding () [++ $wait1 .. $wait16] { // check_use_model("models/boss/smaleido.mdl"); // dprint("Guarding\n"); ai_face(); eidolon_enemy(); } void eidolon_wait () [++ $wait1 .. $wait16] { // dprint("Waiting\n"); // check_use_model("models/boss/smaleido.mdl"); if(!self.lockentity) eidolon_find_lightning(); if(!self.controller) eidolon_find_orb(); ai_stand(); } void multiplayer_health () { entity lastent; float num_players; if(coop) { lastent=nextent(world); num_players=0; while(lastent) { if(lastent.flags&FL_CLIENT) num_players+=1; lastent=find(lastent,classname,"player"); } if(num_players>4) num_players=4; if(num_players>0) { self.max_health+=1000*num_players; self.torncount=num_players - 1; } self.health=self.max_health; } if(self.th_save!=SUB_Null) self.th_save(); } /*QUAKED monster_eidolon (1 0 0) (-100 -100 0) (100 100 666) CUTE CUDDLY The big bad ugly boss guy -------------------------FIELDS------------------------- -------------------------------------------------------- */ void monster_eidolon(void) { if (deathmatch) { remove(self); return; } precache_model2 ("models/boss/smaleido.mdl"); precache_model2 ("models/boss/bigeido.mdl"); precache_model2 ("models/eidoball.mdl"); precache_model2 ("models/eidoflam.spr"); precache_model2 ("models/glowball.mdl"); precache_model2 ("models/boss/shaft.mdl"); precache_model2 ("models/boss/circle.mdl"); precache_model2 ("models/boss/star.mdl"); precache_sound2 ("eidolon/roar.wav"); precache_sound2 ("eidolon/pain.wav"); //Hurt precache_sound2 ("eidolon/death.wav"); //Dies- long and agonizing precache_sound2 ("eidolon/fakedie.wav");//1st death- fake precache_sound2 ("eidolon/spell.wav"); //Spell attack (tracking globes) precache_sound2 ("eidolon/stomp.wav"); //Hot-steppin' precache_sound2 ("eidolon/fireball.wav"); //Launching Nasty fireballs precache_sound2 ("eidolon/flamstrt.wav"); // precache_sound2 ("eidolon/flambrth.wav"); // precache_sound2 ("eidolon/flamend.wav"); // precache_sound2 ("eidolon/growl.wav"); // precache_sound2 ("eidolon/chrgstrt.wav"); //Orb starts recharging Eido precache_sound2 ("eidolon/orbhurt.wav"); //Orb gets hit precache_sound2 ("eidolon/orbxpld.wav"); //Orb gets destroyed precache_sound2 ("eidolon/orbpulse.wav"); //Orb pulsating precache_sound2 ("famine/flashdie.wav"); total_monsters += 1; self.speed=10.5; self.solid = SOLID_SLIDEBOX; self.movetype = MOVETYPE_STEP; self.takedamage=DAMAGE_YES; self.monsterclass=CLASS_FINAL_BOSS; /* see devel/eidolon.txt for why god mode is needed. */ self.flags(+)FL_MONSTER|FL_GODMODE; self.flags2(+)FL_ALIVE|FL_SMALL; self.thingtype=THINGTYPE_FLESH; setmodel (self, "models/boss/smaleido.mdl"); self.skin = 0; setsize (self, '-40 -40 0', '40 40 150'); // setsize (self, '-32 -32 0', '32 32 150'); self.hull=HULL_GOLEM; self.health = self.max_health=3000+skill*1000; self.yaw_speed = 10; self.mass = 2000; self.rider_path_distance=30; self.proj_ofs=self.view_ofs='0 0 100'; self.experience_value = 10000; self.th_stand = eidolon_wait; self.th_jump = eidolon_ready_roar; self.th_walk = eidolon_walk; self.th_run = eidolon_run; self.th_die = eidolon_die; self.th_melee = eidolon_flames; self.th_pain = eidolon_check_fake; self.th_missile = eidolon_fire; self.scale = 0.75; self.drawflags (+) SCALE_ORIGIN_BOTTOM; self.touch=SUB_Null; self.th_save=walkmonster_start; self.think=multiplayer_health; thinktime self : 2; } gamecode/hc/h2/entity.hc000066400000000000000000000433051444734033100153650ustar00rootroot00000000000000//************************************************************************** //** entity.hc //************************************************************************** // SYSTEM FIELDS ----------------------------------------------------------- // (entvars_t C structure, *** = don't modify in HexC) --------------------- // *** Model index in the precached list. .float modelindex; // *** Origin + mins / maxs .vector absmin, absmax; // Local time for entity. .float ltime; .float movetype; .float solid; // *** .vector origin; // *** .vector oldorigin; .vector velocity; .vector angles; .vector avelocity; // Temp angle adjust from damage or recoil. .vector punchangle; // Spawn function. .string classname; .string model; .float frame; .float skin; .float effects; .float scale; .float drawflags; .float abslight; // Bounding box extents relative to origin. .vector mins, maxs; // maxs - mins .vector size; // Which clipping hull to use. .float hull; .void() touch; .void() use; .void() think; // For doors or plats, called when can't push other. .void() blocked; .float nextthink; .entity groundentity; // Stats .float stats_restored; .float frags; .float weapon; .string weaponmodel; .float weaponframe; .float health; // HP .float max_health; // Max HP .float playerclass; // 0 (none), 1-4 .float bluemana; // Blue mana .float greenmana; // Green mana .float max_mana; // Maximum amount of mana for current class / level .float armor_amulet; // Health of amulet armor .float armor_bracer; // Health of bracer armor .float armor_breastplate; // Health of breastplate armor .float armor_helmet; // Health of helmet armor .float level; // Player level .float intelligence; // Player INT .float wisdom; // Player WIS .float dexterity; // Player DEX .float strength; // Player STR .float experience; // Accumulated experience points .float ring_flight; // Health of rings 0 - 100 .float ring_water; // .float ring_turning; // .float ring_regeneration; // .float haste_time; // When hast is depleted .float tome_time; // When tome of power is depleted .string puzzle_inv1; // Puzzle piece inventory... .string puzzle_inv2; .string puzzle_inv3; .string puzzle_inv4; .string puzzle_inv5; .string puzzle_inv6; .string puzzle_inv7; .string puzzle_inv8; // Experience this entity is worth when killed or used. .float experience_value; // Bit flags. .float items; .float takedamage; .entity chain; .float deadflag; // Add to origin to get eye point. .vector view_ofs; // Fire. .float button0; // Use. .float button1; // Jump. .float button2; // Weapon changes, misc. .float impulse; .float fixangle; // View / targeting angle for players. .vector v_angle; // Calculated pitch angle for slopes. .float idealpitch; .float idealroll; .float hoverz; .string netname; .entity enemy; .float flags; .float flags2; .float artifact_flags; .float colormap; .float team; .float light_level; // Don't back up. .float teleport_time; // Save this fraction of incoming damage. .float armortype; .float armorvalue; // 0 = not in, 1 = feet, 2 = waist, 3 = eyes. .float waterlevel; // A contents value. .float watertype; // 0 = not in a friction entity, else the friction of the entity. .float friction; .float ideal_yaw; .float yaw_speed; //rj.entity aiment; // A pathentity or an enemy. also used by axeblade for it's tail .entity goalentity; .float spawnflags; // The target of this entity. .string target; .string targetname; // Damage is accumulated through a frame and sent as one single // message, so the super shotgun doesn't generate huge messages. .float dmg_take; .float dmg_save; .entity dmg_inflictor; // Who launched a missile. .entity owner; // Mostly or doors, but also used for waterjump. .vector movedir; // Trigger messages. .float message; // Either a CD track number or a sound number. .float soundtype; // Contains names of .WAVs to play. .string noise, noise1, noise2, noise3; .float rings; // Which rings hero has .float rings_active; // Shows which rings have been activated .float rings_low; // Shows which rings are low on power .float artifacts; // Which artifact hero has .float artifact_active; // Shows which artifact have been activated .float artifact_low; // Shows which artifact is running out .float hasted; // % of normal speed player has been hasted .float inventory; // Which item is currently chosen? //rj.float ordr_cnt; // Number of items in order // make sure you change references to: // max_ammo2() DropBackpack() BackpackTouch() // when adding or changing inventory fields .float cnt_torch; // Count of inventory item - Torch .float cnt_h_boost; // Count of inventory item - Health Boost .float cnt_sh_boost; // Count of inventory item - Super Health Boost .float cnt_mana_boost; // Count of inventory item - Mana Boost .float cnt_teleport; // Count of inventory item - Teleport .float cnt_tome; // Count of inventory item - Tome of Power .float cnt_summon; // Count of inventory item - Summon .float cnt_invisibility; // Count of inventory item - Invisibility .float cnt_glyph; // Count of inventory item - Glyph of the Ancients .float cnt_haste; // Count of inventory item - Haste .float cnt_blast; // Count of inventory item - Blast Radius .float cnt_polymorph; // Count of inventory item - Polymorph .float cnt_flight; // Count of inventory item - Flight .float cnt_cubeofforce; // Count of inventory item - Cube of Force .float cnt_invincibility; // Count of inventory item - Invincibility .entity cameramode; .entity movechain; .void() chainmoved; .float string_index; // Index used for global string table // END SYSTEM FIELDS ------------------------------------------------------- // Flag the compiler. void end_sys_fields; // World fields .string wad; .string map; .float worldtype; // 0=medieval 1=metal 2=base .string killtarget; // QuakeEd fields .float light_lev; // Not used by game, but parsed by light util .float style; // Monster AI, doubled over for player .void() th_stand; .void() th_walk; //player_crouch_move .void() th_run; .void() th_missile; //player_attack .void() th_melee; .void(entity attacker, float damage) th_pain; .void() th_die; .void() th_save; // In case you need to save/restore a thinking state // Mad at this player before taking damage. .entity oldenemy; .float speed; .float lefty; .float search_time; .float attack_state; // Monster AI stuff .float monster_stage; .float monster_duration; // Stage duration .float monster_awake; .float monster_check; .vector monster_last_seen; // because of how physics works, certain calls to the touch // function of other entities involving the player do not // allow you to adjust the velocity, so you have to do it // outside of the inner physics stuff .vector adjust_velocity; .union { // Entity type specific stuff struct // player stuff { float splash_time; // When to generate the next splash float camera_time; // float weaponframe_cnt; // float attack_cnt; // Shows which attack animation can be used float ring_regen_time; // When to add the next point of health float ring_flight_time; // When to update ring of flight health float ring_water_time; // When to update ring of waterbreathing health float ring_turning_time;// When to update ring of turning health float super_damage; // Player does this much more damage (Like Crusader with Special Ability #2) float super_damage_low; // Flag the super damage is low float puzzles_cheat; // Allows player past puzzle triggers float camptime; // Amount of time player has been motionless float crouch_time; // Next time player should run crouch subroutine float crouch_stuck; // If set this means the player has released the crouch key in an area too small to uncrouch in float divine_time; // Amount of time flash happens in divine intervention float act_state; // Anim info float raven_cnt; // Number of raven's this guys has in the world float newclass; // If doing a quick class change }; struct { // Fallen Angel float fangel_SaveFrame; float fangel_Count; float shoot_cnt; float shoot_time; // Time of last shot float z_movement; float z_duration; float drop_time; }; struct { // Fallen Angel's Spell float spell_angle; }; struct { // Hydra float hydra_FloatTo; float hydra_chargeTime; }; struct { // Spider float spiderType; // SPIDER_? types float spiderActiveCount; // Tallies "activity" float spiderGoPause; // Active/pause threshold float spiderPauseLength; // Pause duration in frames float spiderPauseCount; // Tallies paused frames }; struct { // Scorpion float scorpionType; // SCORPION_? types float scorpionRest; // Resting state counter float scorpionWalkCount; // Counts walking frames }; struct { // Golem float golemSlideCounter; float golemBeamDelay; float golemBeamOff1; float golemBeamOff2; }; struct { // Imp float impType; // IMP_? types }; struct { // Mummy float parts_gone; float mummy_state; float mummy_state_time; }; struct { // Artifacts float artifact_respawn; // Should respawn? float artifact_ignore_owner_time; float artifact_ignore_time; }; struct { // Rider path float next_path_1; float next_path_2; float next_path_3; float next_path_4; float path_id; float next_path_5; float next_path_6; }; struct { // Rider triggers float rt_chance; }; struct { // Rider data float rider_gallop_mode; float rider_last_y_change; float rider_y_change; float rider_death_speed; float rider_path_distance; float rider_move_adjustment; }; struct { // War rider axe float waraxe_offset; float waraxe_horizontal; float waraxe_track_inc; float waraxe_track_limit; float waraxe_max_speed; float waraxe_max_height; }; struct { // War rider's quake float wrq_effect_id; float wrq_radius; float wrq_count; }; struct { // Rider's beam float beam_angle_a; float beam_angle_b; float beam_max_scale; float beam_direction; float beam_speed; }; struct { // Used by smoke generator float z_modifier; }; struct { float last_health; // Used by bell entity }; struct // For raven staff Ravens { float idealpitch; float pitchdowntime; float searchtime; // Amount of time bird has been searching float next_action; // Next time to take action float searchtime; // When search was first started float damage_max; // Amount of damage each raven can do before it has to leave }; struct { // fish float fish_speed; float fish_leader_count; }; struct { // Used by particle explosion entity. float exploderadius; }; struct { // Skull missiles from skullwizard float scream_time; }; struct { float attack_cnt; }; struct { // Pestalance's Hive float beginframe; }; struct { // Soul spheres float sound_time; }; struct { // Cube of force float shot_cnt; // Number of shots the force cube has shot }; }; // Once we can do unions above end_sys, have this with the field 'playerclass' .float monsterclass; // FIXME: Remove the ring of spell turning and all references to this .float turn_time; // Triggers / doors .string puzzle_piece_1; .string puzzle_piece_2; .string puzzle_piece_3; .string puzzle_piece_4; .float no_puzzle_msg; // Puzzle Item .string puzzle_id; // More rider stuff that can't be in the union .entity path_current; .vector oldangles; .string lastweapon; // Weapon model player had before changing to camera mode .float lifetime; .float lifespan; .float walkframe; .float wfs; // Weapon frame state .float attack_finished; .float pain_finished; .float invisible_finished; .float invincible_time, invincible_sound; .float invisible_time; .float super_damage_time; // Set to time+0.2 whenever a client fires a weapon or takes damage. // Used to alert monsters that otherwise would let the player go. .float show_hostile; // Player jump flag. .float jump_flag; // Player swimming sound flag. .float swim_flag; // When time > air_finished, start drowning. .float air_finished; // Keeps track of the number of bubbles. .float bubble_count; // Keeps track of how the player died. .string deathtype; // Object stuff. .string mdl; .vector mangle; // Angle at start // Only used by secret door. .vector oldorigin; .float t_length, t_width; // Things color. .float color; // Count of things (used by rain entity) .float counter; // Can these be made part of a union?? .float plaqueflg; // 0 if not using a plaque, 1 if using a plaque .vector plaqueangle; // Angle player was facing when the plaque was touched // Doors, etc. .vector dest, dest1, dest2; .float wait; // Time from firing to restarting .float delay; // Time from activation to firing .entity trigger_field; // Door's trigger entity .string noise4; // Monsters. .float pausetime; .entity pathentity; // Doors. .float aflag; .float dmg; // Damage done by door when hit // Misc flag. .float cnt; // What type of thing is this? .float thingtype; // Amount of time left on torch. .float torchtime; // Next torch think. .void() torchthink; // Amount of time left on the super health. .float healthtime; // Subs .void() think1; .vector finaldest, finalangle; // For counting triggers .float count; .float spawn_health; // Set to >0 to spawn instant health // Plats/doors/buttons .float lip; .float state; .vector pos1, pos2; // Top and bottom positions .float height; // Sounds //.float waitmin, waitmax; //.float distance; //.float volume; .vector orgnl_mins, orgnl_maxs; // original bounding box .float veer; //Amount of veer when Veer function called (included in HomeThink Function) //The higher the number, the more drastic the wander is. .float homerate;//Turning rate on homing missiles, is used as the nextthink time //so the lower the number, the tighter thr turn radius. //From the SpiralThink function, a value of FALSE will //stop it from randomly homing while spiraling, //a value of TRUE will allow it to randomly Home, but //does not effect rate of homing since it only calls //it randomly. .float mass; //NOTE: 1 = @18.5 pounds. //How much they weigh- should be used in all velocity mods //(pushing, impact, throwing). Used as a divider, so //the higher the number, the higher the mass, the less //distance it will go. Make sure it's >0 //Buttons and pressure plates can use this too so that //if a light object is placed on it, it won't activate //(but it should be cumulative so that you can stack several //light objects to increase mass and activate it) //Also, a light player (featherfall?) could get around //certain traps? .float onfire; //A value that, when FALSE means the object is not on //fire. A greater than zero value indicates how fast //the thing is burning. The higher the number, the higher //the damage and the more flames. .vector o_angle;//Just to remember an old angle or vector //Player .float bloodloss;//For the Bleed() function which will remove health //and add graphic. Set to 666 for beheading death. .float oldweapon;//For remembering your last weapon, has many uses //Monsters (and some projectiles) .entity controller; //What is the owner of this thing, this allows //it to touch it's owner if you set the owner //to self. .float init_modelindex;//initial model index, so you can switch away and back .string init_model; //Player Only th_*** .void() th_swim; .void() th_jump; .void() th_fly; .void() th_die1; .void() th_die2; .void() th_goredeath; .void() th_possum; //Monster playing dead .void() th_possum_up; //Monster getting up from playing dead .float last_attack; //Used for weapons that go into rest mode after //a while .entity shield; .float frozen; //Can't be a flag, is a counter .float oldskin; .void() oldthink; .void() th_weapon; .float decap; //To know if was beheaded, not a flag, set to 2 if //head should explode .string headmodel; .void() oldtouch; //These two are for when you're frozen and thaw out .float oldmovetype; .float target_scale; .float scalerate; .float blizzcount; .float tripwire_cnt; .float imp_count; .vector proj_ofs; //Projectile offset, different from view_ofs. .string spawnername; //for monster spawner .entity catapulter; .float catapult_time; .float last_onground; //Timer- helps keep track of how long something has been in the air. .vector pos_ofs; //Position ofset .vector angle_ofs; //Angle offset .float safe_time; //How long after a tornado throws you that it cant pick you up again .float absorb_time; //for 0.3 seconds after crouching, you will absorb 1/2 of your falling damage upon impact .float mintel; //Monster intelligence- temp since entity.hc was checked out .vector wallspot; //Last place enemy was seen- for waypoint ai .vector lastwaypointspot;//explains itself .entity lockentity; //for waypoint system .float last_impact; //Last time touch function was called .float inactive; .float msg2; .string msg3; .string nexttarget; //For target transferral .float gravity; //Gravity, duh .float upside_down; .float lightvalue1; .float lightvalue2; .float fadespeed; .float point_seq; //Waypoint sequence number .float sheep_time; //How long you will be a sheep for .float sheep_sound_time; .float still_time; //How long she's been standing still .float visibility_offset; //How hard it is to see and aim at entity, from 0 to 1 //0 is totally visible, 1 is invisible .float check_ok; //For trigger check, instead of re-using aflag .entity check_chain; //for trigger_check, keeps track of it's targetted entities .void() th_spawn; //Monster function you spawned with .float freeze_time; .float level_frags; .float visibility; entity sight_entity; //So monsters wake up other monsters .entity viewentity; .float sv_flags; //temp serverflags fix .float dmgtime; .float healamount, healtype; .float anglespeed; .float angletime; .float movetime; .float hit_z; .float torncount; .entity path_last; .float dflags; gamecode/hc/h2/eric.hc000066400000000000000000000047541444734033100150000ustar00rootroot00000000000000void()trap_fireball_use; void trap_fireball_wait () { self.use = trap_fireball_use; self.think=SUB_Null; self.nextthink=-1; } void fireball_think () { particle4(self.origin,3,random(160,176),PARTICLETYPE_FIREBALL,random(10,20)); self.think=fireball_think; thinktime self : 0.1; } void trap_fireball_use () { entity fireball; vector vec; float dot; if(self.spawnflags&1) { self.think=trap_fireball_wait; thinktime self : 0; } else { self.use=trap_fireball_wait; self.think = trap_fireball_use; thinktime self : self.wait; } if(self.goalentity) self.enemy=self.goalentity; else self.enemy = find (world, classname, "player"); if((visible(self.enemy)&&self.enemy!=world)||self.goalentity==self.enemy) { vec=normalize((self.enemy.absmin+self.enemy.absmax)*0.5-self.origin); makevectors(self.angles); dot = v_forward*vec; if(dot>0.6||self.goalentity==self.enemy) { sound (self, CHAN_WEAPON, "imp/fireball.wav", 1, ATTN_NORM); fireball = spawn (); fireball.movetype = MOVETYPE_FLYMISSILE; fireball.solid = SOLID_BBOX; fireball.speed=1000; fireball.velocity=vec*fireball.speed; fireball.touch = fireballTouch; fireball.dmg=self.dmg; fireball.owner = self; fireball.angles = vectoangles (fireball.velocity); fireball.think = fireball_think; thinktime fireball : 0.05; self.last_attack=time; setmodel (fireball, "models/drgnball.mdl"); setsize (fireball, '0 0 0', '0 0 0'); setorigin (fireball,self.origin); } } } void locate_first_target () { self.goalentity=find(world,targetname,self.target); if(!self.goalentity) dprint("ERROR: Targeted Fireball can't find target\n"); } /*QUAKED trap_fireball (1 0.3 0) (0 0 0) (16 16 16) TRIGGER_ONLY New item for QuakeEd It works! It really works! If TRIGGER_ONLY is turned on, it will fire once each time it's triggered (used) otherwise, each time it's used, it's turned on or off. If it's targetted to something, it will fire at that rather than tracking the player. -------------------------FIELDS------------------------- .wait = How long to wait between firings (default 0.5) .dmg = How much damage to do with each shot (default 10) -------------------------------------------------------- */ void () trap_fireball = { precache_sound2("imp/fireball.wav"); precache_model2("models/drgnball.mdl"); if(!self.wait) self.wait=0.5; if(!self.dmg) self.dmg=10; if(self.target) { self.think=locate_first_target; thinktime self : 0.5; } self.use = trap_fireball_use; }; gamecode/hc/h2/explode.hc000066400000000000000000000142051444734033100155060ustar00rootroot00000000000000void() CB_BoltStick; void FireMeteor (string type); void()BlowUp= { if(self.dmg<2.5&&self.scale>=0.1) { self.v_angle=RandomVector('180 180 180'); self.scale=self.dmg; T_RadiusDamage (self, self.owner, self.dmg*100, world); self.dmg += 0.1; if(self.enemy) //Stay with enemy; setorigin(self,self.enemy.origin+self.view_ofs); self.think=BlowUp; thinktime self : 0.025; } else { self.think=SUB_Remove; thinktime self : 0; } }; void() SprayFire= { local entity fireballblast; sound(self,CHAN_AUTO,"weapons/fbfire.wav",1,ATTN_NORM); fireballblast=spawn(); fireballblast.enemy=self.enemy; fireballblast.movetype=MOVETYPE_NOCLIP; fireballblast.owner=self.owner; fireballblast.classname="fireballblast"; fireballblast.solid=SOLID_NOT; fireballblast.drawflags(+)MLS_ABSLIGHT|SCALE_TYPE_UNIFORM|SCALE_ORIGIN_CENTER; fireballblast.abslight= 0.5; fireballblast.scale=0.1; setmodel(fireballblast,"models/blast.mdl"); setsize(fireballblast,'0 0 0','0 0 0'); setorigin(fireballblast,self.origin); // fireballblast.effects=EF_BRIGHTLIGHT; fireballblast.dmg=0.1; fireballblast.avelocity='50 50 50'; fireballblast.think=BlowUp; thinktime fireballblast : 0; remove(self); }; void SmallExplosion (void) { sound(self,CHAN_AUTO,"weapons/explode.wav",0.5,ATTN_NORM); BecomeExplosion(CE_SM_EXPLOSION); } void DarkExplosion () { entity ignore; if(self.classname=="timebomb") { sound(self,CHAN_AUTO,"weapons/explode.wav",1,ATTN_NORM); ignore=self.enemy; } else if(self.classname=="pincer") { sound(self,CHAN_BODY,"weapons/explode.wav",1,ATTN_NORM); ignore=self.owner; } else { if(self.controller.classname=="multigrenade") sound(self.controller,CHAN_BODY,"weapons/explode.wav",1,ATTN_NORM); else sound(self,CHAN_AUTO,"weapons/explode.wav",1,ATTN_NORM); ignore=world; } T_RadiusDamage (self, self.owner, self.dmg, ignore); if(self.classname=="minigrenade"&&random()<0.5) BecomeExplosion(FALSE); else if(self.classname=="flaming arrow") { starteffect(CE_XBOW_EXPLOSION,self.origin); remove(self); } else { starteffect(CE_NEW_EXPLOSION,self.origin); remove(self); } } void() MultiExplode = { //FIXME: For some reason, the light casting effects in Hex2 //are a lot more costly than they were in Quake... if(self.classname=="stickmine") { SprayFire(); return; } T_RadiusDamage (self, self.owner, self.dmg, world); if(self.classname=="meteor") { local float nummeteorites; nummeteorites=random(3,10); while(nummeteorites>0) { FireMeteor("minimeteor"); nummeteorites =nummeteorites - 1; } } if(self.flags2&FL_SMALL) SmallExplosion(); else { //FIXME: This causes a crash if removed, and sometimes the explosions are spawned at the world origin! // if(self.classname!="minigrenade"||(self.classname=="flaming arrow"&&random()<0.5)) // { WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); WriteByte (MSG_BROADCAST, TE_EXPLOSION); WriteCoord (MSG_BROADCAST, self.origin_x); WriteCoord (MSG_BROADCAST, self.origin_y); WriteCoord (MSG_BROADCAST, self.origin_z); // } BecomeExplosion(FALSE); } }; void() SuperGrenadeExplode; void() GrenadeTouch2 = { if (other == self.owner) return; // don't explode on owner if(other.owner==self.owner&&other.classname==self.classname&&self.classname=="minigrenade") return; if (other.takedamage==DAMAGE_YES)//let them damage b_models on impact? { T_Damage(other,self,self.owner,self.dmg); self.dmg/=2; if(self.classname=="multigrenade") self.think=SuperGrenadeExplode; else if(self.classname=="minigrenade"||self.classname=="flaming arrow") self.think=DarkExplosion; else self.think=MultiExplode; thinktime self : 0; } else { sound (self, CHAN_WEAPON, "assassin/gbounce.wav", 1, ATTN_NORM); // bounce sound if (self.velocity == '0 0 0') self.avelocity = '0 0 0'; } }; void() StickMineTouch = { if(other==self.owner) return; self.skin=1; vector stickdir; self.touch=SUB_Null; if(other.takedamage) { sound(self,CHAN_WEAPON,"weapons/met2flsh.wav",1,ATTN_NORM); T_Damage(other,self,self.owner,3); if(other.solid!=SOLID_BSP) { // makevectors(self.angles); stickdir=other.origin+normalize(self.origin-other.origin)*12; //Modify height for shorter or taller models like imp, golem, spider, etc. if(other.classname=="player") //Put it right below view of player stickdir_z=other.origin_z+other.proj_ofs_z + 1; else if(other.classname=="monster_spider") stickdir_z=(self.origin_z+(other.origin_z+other.size_z*0.2)*3)*0.25; else stickdir_z=(self.origin_z+(other.origin_z+other.size_z*0.6)*3)*0.25; setorigin(self,stickdir); SpawnPuff(self.origin+v_forward*8,'0 0 0'-v_forward*24,10,other); } } else { setorigin(self,self.origin+normalize(self.velocity)*-3); sound(self,CHAN_WEAPON,"weapons/met2stn.wav",1,ATTN_NORM); SpawnPuff(self.origin+v_forward*8,'0 0 0'-v_forward*24,10,world); } self.velocity='0 0 0'; self.movetype=MOVETYPE_NOCLIP; self.solid=SOLID_NOT; self.touch=SUB_Null; self.wait=time + 1; self.health=other.health; if(other.takedamage) { self.enemy=other; self.view_ofs=(self.origin-other.origin); self.o_angle=(self.angles-self.enemy.angles); self.think=CB_BoltStick; thinktime self : 0; } else { self.enemy=world; self.movetype=MOVETYPE_NONE; self.think=MultiExplode; thinktime self : 0.5; } }; void() Use_Fireball = { self.attack_finished=time + 1;//So you can't have a ton of them makevectors(self.v_angle); //sound entity missile; missile=spawn(); missile.owner=self; missile.classname="stickmine"; missile.movetype=MOVETYPE_BOUNCE; missile.solid=SOLID_BBOX; missile.touch=StickMineTouch; missile.dmg=50; missile.velocity=normalize(v_forward)*700 +v_up*200; missile.avelocity=RandomVector('300 300 300'); missile.lifetime=time+60; setmodel (missile, "models/glyphwir.mdl"); setsize(missile,'0 0 0','0 0 0'); setorigin(missile,self.origin+self.proj_ofs+v_forward*16); missile.think=MultiExplode; thinktime missile : 10; }; gamecode/hc/h2/fablade.hc000066400000000000000000000032121444734033100154200ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\monsters\Fangel\wingblad\FAblade.hc ============================================================================== */ // For building the model $cd Q:\art\models\monsters\Fangel\wingblad $origin 0 0 0 $base BASE skin1 $skin skin1 $flags 0 // $frame BLADE void() faBladeTouch = { float damg; if (other == self.owner) return; // don't explode on owner if (pointcontents(self.origin) == CONTENT_SKY) { remove(self); return; } damg = random(8,16); if (other.health) T_Damage (other, self, self.owner, damg); sound (self, CHAN_WEAPON, "weapons/expsmall.wav", 1, ATTN_NORM); self.origin = self.origin - 8*normalize(self.velocity); CreateGreenSmoke(self.origin, '0 0 8', HX_FRAME_TIME * 4); remove(self); }; // Frame Code //void() frame_BLADE = [ $BLADE , frame_BLADE ] { }; void(vector offset, float set_speed, vector dest_offset) do_faBlade = { entity missile; vector vec; missile = spawn (); missile.owner = self; missile.movetype = MOVETYPE_FLYMISSILE; missile.solid = SOLID_BBOX; missile.flags = FL_FLY; missile.health = 10; missile.drawflags=MLS_ABSLIGHT; missile.abslight=0.5; setmodel (missile, "models/fablade.mdl"); setsize (missile, '0 0 0', '0 0 0'); // set missile speed makevectors (self.angles); setorigin (missile, self.origin + v_factor(offset)); vec = self.enemy.origin - missile.origin + self.enemy.proj_ofs + dest_offset; vec = normalize(vec); missile.velocity = (vec+aim_adjust(self.enemy))*set_speed; missile.angles = vectoangles(missile.velocity); missile.touch = faBladeTouch; }; gamecode/hc/h2/famhorse.hc000066400000000000000000000512331444734033100156540ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\monsters\RdrFam\Horse\Final\famhorse.hc ============================================================================== */ // For building the model $cd Q:\art\models\monsters\RdrFam\Horse\Final $origin 0 0 0 $base base skin $skin skin $flags 0 // Horse frames $frame Hrear1 Hrear2 Hrear3 Hrear4 Hrear5 $frame Hrear6 Hrear7 Hrear8 Hrear9 Hrear10 $frame Hrear11 Hrear12 Hrear13 Hrear14 Hrear15 $frame Hrear16 Hrear17 Hrear18 Hrear19 Hrear20 $frame Hrear21 Hrear22 Hrear23 Hrear24 Hrear25 $frame Hrear26 Hrear27 Hrear28 Hrear29 Hrear30 $frame Hrear31 Hrear32 Hrear33 Hrear34 Hrear35 $frame Hrear36 Hrear37 Hrear38 Hrear39 Hrear40 $frame Hrear41 Hrear42 Hrear43 Hrear44 Hrear45 $frame Hrear46 Hrear47 Hrear48 Hrear49 Hrear50 // $frame HtranA1 HtranA2 HtranA3 HtranA4 HtranA5 $frame HtranA6 HtranA7 HtranA8 HtranA9 HtranA10 $frame HtranA11 HtranA12 HtranA13 HtranA14 HtranA15 $frame HtranA16 // $frame HtranB1 HtranB2 HtranB3 HtranB4 HtranB5 $frame HtranB6 HtranB7 HtranB8 HtranB9 HtranB10 $frame HtranB11 HtranB12 HtranB13 HtranB14 HtranB15 $frame HtranB16 // $frame Htrot1 Htrot2 Htrot3 Htrot4 Htrot5 $frame Htrot6 Htrot7 Htrot8 Htrot9 Htrot10 $frame Htrot11 Htrot12 $framevalue 0 //---------------------------------------------------------------- // Rider Frames //---------------------------------------------------------------- $frame Frear1 Frear2 Frear3 Frear4 Frear5 $frame Frear6 Frear7 Frear8 Frear9 Frear10 $frame Frear11 Frear12 Frear13 Frear14 Frear15 $frame Frear16 Frear17 Frear18 Frear19 Frear20 $frame Frear21 Frear22 Frear23 Frear24 Frear25 $frame Frear26 Frear27 Frear28 Frear29 Frear30 $frame Frear31 Frear32 Frear33 Frear34 Frear35 $frame Frear36 Frear37 Frear38 Frear39 Frear40 $frame Frear41 Frear42 Frear43 Frear44 Frear45 $frame Frear46 Frear47 Frear48 Frear49 Frear50 // $frame Fscale1 Fscale2 Fscale3 Fscale4 Fscale5 $frame Fscale6 Fscale7 Fscale8 Fscale9 Fscale10 $frame Fscale11 Fscale12 Fscale13 Fscale14 Fscale15 $frame Fscale16 Fscale17 Fscale18 Fscale19 Fscale20 $frame Fscale21 Fscale22 Fscale23 Fscale24 Fscale25 $frame Fscale26 Fscale27 Fscale28 Fscale29 Fscale30 $frame Fscale31 Fscale32 Fscale33 Fscale34 Fscale35 $frame Fscale36 Fscale37 Fscale38 Fscale39 Fscale40 $frame Fscale41 Fscale42 Fscale43 Fscale44 Fscale45 $frame Fscale46 Fscale47 Fscale48 Fscale49 Fscale50 $frame Fscale51 Fscale52 Fscale53 Fscale54 Fscale55 $frame Fscale56 Fscale57 Fscale58 Fscale59 Fscale60 $frame Fscale61 Fscale62 Fscale63 Fscale64 Fscale65 $frame Fscale66 Fscale67 Fscale68 Fscale69 Fscale70 // $frame FtranA1 FtranA2 FtranA3 FtranA4 FtranA5 $frame FtranA6 FtranA7 FtranA8 FtranA9 FtranA10 $frame FtranA11 FtranA12 FtranA13 FtranA14 FtranA15 $frame FtranA16 // $frame FtranB1 FtranB2 FtranB3 FtranB4 FtranB5 $frame FtranB6 FtranB7 FtranB8 FtranB9 FtranB10 $frame FtranB11 FtranB12 FtranB13 FtranB14 FtranB15 $frame FtranB16 // $frame Ftrot1 Ftrot2 Ftrot3 Ftrot4 Ftrot5 $frame Ftrot6 Ftrot7 Ftrot8 Ftrot9 Ftrot10 $frame Ftrot11 Ftrot12 Ftrot13 Ftrot14 Ftrot15 $frame Ftrot16 float fam_start[1] = { $Htrot1 }; float fam_end[1] = { $Htrot12 }; float fam_speed[1] = { 5.5 // Normal speed }; // Array to align frames float FamRiderFrames[5] = { $Ftrot1, // Animation for fast gallop $FtranA1, // Animation for start of rear $Frear1, // Animation for rear $FtranB1, // Animation for end of rear $Fscale1 // Attack Sequence #1 }; float FH_STAGE_NORMAL = 0; float FH_STAGE_BEGIN_REAR = 1; float FH_STAGE_MIDDLE_REAR = 2; float FH_STAGE_END_REAR = 3; float FH_STAGE_ATTACK = 4; float FH_STAGE_STANDING = 5; float FH_STAGE_LAUGHING = 6; float FH_STAGE_ATTACK2 = 7; float pulltime; float looktime; float hurttime; void famhorse_move(void); void famhorse_rear(void); void do_fambeam (entity lowner,float tag, float lflags, float duration, vector spot1, vector spot2) { WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); WriteByte (MSG_BROADCAST, TE_STREAM_FAMINE); WriteEntity (MSG_BROADCAST, lowner); WriteByte (MSG_BROADCAST, tag+lflags); WriteByte (MSG_BROADCAST, duration); WriteCoord (MSG_BROADCAST, spot1_x); WriteCoord (MSG_BROADCAST, spot1_y); WriteCoord (MSG_BROADCAST, spot1_z); WriteCoord (MSG_BROADCAST, spot2_x); WriteCoord (MSG_BROADCAST, spot2_y); WriteCoord (MSG_BROADCAST, spot2_z); } void famine_pain(void) { float chance,rear_chance; rear_chance = self.health / self.max_health; if (rear_chance < .50) rear_chance = .5; if (self.monster_stage == FH_STAGE_NORMAL) { // Force a new gallop frame in self.frame = fam_start[self.rider_gallop_mode]; chance = random(); if (chance > rear_chance) // Like the Lone Ranger but he disappears { self.think = famhorse_rear; self.monster_stage = FH_STAGE_BEGIN_REAR; } else if (chance < 0.20) // Told himself a joke { self.monster_stage = FH_STAGE_LAUGHING; self.movechain.frame = $FtranB1; } else if (chance < 0.60) // Missile attack { self.monster_stage = FH_STAGE_ATTACK2; self.movechain.frame = $Fscale1; } } } void famine_blinkin(void) { thinktime self : HX_FRAME_TIME; self.think = famine_blinkin; self.scale += 0.10; self.movechain.scale += 0.10; if (self.scale >= 1) { self.scale=1; self.movechain.scale=1; thinktime self : HX_FRAME_TIME; self.think = famhorse_rear; } } // Find a point to start at close to the player vector famine_pointcheck(void) { entity search; float diff, holddiff; vector newspot,holdpos; search = find(world, classname, "rider_path"); diff = 99999; while(search != world) { holddiff = vlen(search.origin - self.enemy.origin); if (holddiff < diff) { traceline (search.origin, search.origin - '0 0 600' , FALSE, self); holdpos=trace_endpos; tracearea (holdpos,holdpos + '0 0 40','-55 -55 -24', '55 55 150',FALSE,self); if (trace_fraction == 1.0 && trace_ent == world) // Check no one is standing where monster wants to be { diff = holddiff; newspot= holdpos; self.path_current = search; } } search = find(search, classname, "rider_path"); } return (newspot); } /*----------------------------------------- famine_init - looking for a spot to come back in -----------------------------------------*/ void famine_blinkin_init (void) { vector holdspot; holdspot = famine_pointcheck(); setorigin(self,holdspot); setmodel (self, "models/boss/famhorse.mdl"); setsize (self, '-40 -40 0', '40 40 100'); self.hull = HULL_POINT; self.solid = SOLID_SLIDEBOX; self.takedamage = DAMAGE_YES; setmodel (self.movechain, "models/boss/famrider.mdl"); self.movechain.flags (+) FL_MOVECHAIN_ANGLE; walkmove(self.angles_y, .01, TRUE); // You have to move it a little bit to make it solid riderpath_findnext(); // Find the next path point to turn the horse in that direction self.ideal_yaw = vectoyaw(self.path_current.origin - self.origin); self.angles_y = self.ideal_yaw; sound (self, CHAN_BODY, "skullwiz/blinkin.wav", 1, ATTN_NORM); CreateRedCloud (self.origin + '0 0 40','0 0 0',HX_FRAME_TIME); thinktime self : HX_FRAME_TIME; self.think = famine_blinkin; } /*----------------------------------------- famine_blinkout - blink out -----------------------------------------*/ void famine_blinkout(void) { thinktime self : HX_FRAME_TIME / 2; self.think = famine_blinkout; self.scale -= 0.10; self.movechain.scale -= 0.10; if ((self.scale > 0.79) && (self.scale < 0.89)) { sound (self, CHAN_BODY, "skullwiz/blinkout.wav", 1, ATTN_NORM); CreateRedCloud (self.origin + '0 0 40','0 0 0',HX_FRAME_TIME); } if (self.scale < 0.10) { setmodel(self,string_null); setmodel(self.movechain,string_null); thinktime self : random(0.5,3); // Reappear when self.think = famine_blinkin_init; } } void famine_blinkout_init(void) { self.takedamage = DAMAGE_NO; // So t_damage won't force him into another state self.solid = SOLID_NOT; self.scale = 1; self.drawflags (+) SCALE_TYPE_XYONLY; self.movechain.takedamage = DAMAGE_NO; // So t_damage won't force him into another state self.movechain.scale = 1; self.movechain.drawflags (+) SCALE_TYPE_XYONLY; famine_blinkout(); } void famine_missile_touch(void) { float damg; if (pointcontents(self.origin) == CONTENT_SKY) { remove(self); return; } damg = random(8,16); T_Damage (other, self, self.owner, damg ); self.origin = self.origin - 8 * normalize(self.velocity) - '0 0 40'; sound (self, CHAN_WEAPON, "weapons/explode.wav", 1, ATTN_NORM); CreateRedSpark (self.origin); remove(self); } void famine_missile_think (void) { if (self.lifetime < time) { sound (self, CHAN_WEAPON, "weapons/explode.wav", 1, ATTN_NORM); CreateRedSpark (self.origin); remove(self); } else { HomeThink(); self.angles = vectoangles(self.velocity); self.think=famine_missile_think; thinktime self : HX_FRAME_TIME; } } void famine_missile(float dir) { entity newmis; vector diff; newmis = spawn (); newmis.owner = self; newmis.movetype = MOVETYPE_FLYMISSILE; newmis.solid = SOLID_BBOX; setmodel (newmis, "models/famshot.mdl"); setsize (newmis, '0 0 0', '0 0 0'); setorigin (newmis, self.origin + '0 0 120'); newmis.angles = self.angles; diff = (self.enemy.origin + self.enemy.view_ofs) - self.origin + '0 0 120' ; newmis.velocity = normalize(diff); newmis.angles = vectoangles(newmis.velocity); if (dir == 0) newmis.angles_y -= 25; else if (dir == 2) newmis.angles_y += 25; makevectors (newmis.angles); newmis.velocity = normalize(v_forward); if (dir ==1) newmis.speed=800; //Speed else newmis.speed=700; //Speed newmis.classname = "faminemissile"; newmis.angles = vectoangles(newmis.velocity); newmis.veer=FALSE; //No random wandering newmis.turn_time=5; //Lower the number, tighter the turn newmis.ideal_yaw=FALSE;//Only track things in front newmis.lifetime = time + 2; newmis.think=famine_missile_think; thinktime newmis : 0; newmis.touch = famine_missile_touch; } void famine_missilespawner(void) { CreateRedFlash(self.origin + '0 0 120'); sound (self, CHAN_WEAPON, "famine/shot.wav", 1, ATTN_NORM); famine_missile(0); famine_missile(1); famine_missile(2); } /* --------------------------- Create the rider ---------------------------*/ void create_famrider(entity horse) { entity rider; rider = spawn(); rider.solid = SOLID_NOT; rider.movetype = MOVETYPE_NONE; rider.origin = horse.origin; rider.angles = self.angles; setmodel (rider, "models/boss/famrider.mdl"); rider.skin = 0; horse.movechain = rider; rider.flags (+) FL_MOVECHAIN_ANGLE; } /* --------------------------- The horse is rearing ---------------------------*/ void famhorse_rear(void) { float retval; thinktime self : HX_FRAME_TIME; if (self.monster_stage == FH_STAGE_BEGIN_REAR) { retval = AdvanceFrame($HtranA1,$HtranA16); if (self.frame > $HtranA10) self.speed = 1; riderpath_move(self.speed); if (self.frame == $HtranA8) sound (self, CHAN_VOICE, "famine/whinny.wav", 1, ATTN_NORM); if (retval == AF_END) self.monster_stage = FH_STAGE_MIDDLE_REAR; self.movechain.frame = FamRiderFrames[1] + (self.frame - $HtranA1); } else if (self.monster_stage == FH_STAGE_MIDDLE_REAR) { retval = AdvanceFrame($Hrear1,$Hrear50); if (self.frame == $Hrear10) { famine_blinkout_init(); return; } if (self.enemy) { if (self.enemy.health > 0) { if (self.frame == $Hrear30) famine_missilespawner(); } } if (retval == AF_END) { if (self.enemy != world && random() < 0.7) self.monster_stage = FH_STAGE_STANDING; else self.monster_stage = FH_STAGE_END_REAR; } self.movechain.frame = FamRiderFrames[2] + (self.frame - $Hrear1); } else if (self.monster_stage == FH_STAGE_STANDING) { if (random() < 0.5) self.monster_stage = FH_STAGE_END_REAR; } else if (self.monster_stage == FH_STAGE_END_REAR) { retval = AdvanceFrame($HtranB1,$HtranB16); if (self.frame < $HtranB11) self.speed =0; else if (self.frame < $HtranB13) self.speed = fam_speed[self.rider_gallop_mode] * .5; else self.speed = fam_speed[self.rider_gallop_mode]; if (retval == AF_END) { self.think = famhorse_move; self.monster_stage = FH_STAGE_NORMAL; } riderpath_move(self.speed); self.movechain.frame = FamRiderFrames[3] + (self.frame - $HtranB1); } } void check_remove () { thinktime self : 1; if(pulltime rear_chance) // Like the Lone Ranger but he disappears { self.think = famhorse_rear; self.monster_stage = FH_STAGE_BEGIN_REAR; } else if (chance < 0.05) // Told himself a joke { self.monster_stage = FH_STAGE_LAUGHING; self.movechain.frame = $FtranB1; } // Find an enemy?? else if ((self.rider_gallop_mode == 0) && (looktime < time)) { traceline (self.origin + '0 0 120',self.enemy.origin , FALSE, self); if (self.enemy) { if (self.enemy == trace_ent) { chance = random(); if (chance < .30) // The famous pull attack { pulltime = time + random(2,5); self.monster_stage = FH_STAGE_ATTACK; self.movechain.frame = $Fscale1; sound(self,CHAN_WEAPON,"famine/pull.wav",1,ATTN_NORM); } else if (chance < .70) // Missile attack { self.monster_stage = FH_STAGE_ATTACK2; self.movechain.frame = $Fscale1; } } } } } // Is rider attacking player if (self.monster_stage == FH_STAGE_ATTACK) { if ((self.movechain.frame == $Fscale9) && (self.enemy.health > 0)) { newent = spawn(); setorigin (newent, self.origin + '0 0 120'); setmodel (newent, "models/soulball.mdl"); self.controller = newent; self.controller.think=check_remove; thinktime self.controller : 0; } if ((self.movechain.frame == $Fscale10) && (self.enemy.health > 0)) { diff = self.enemy.origin - self.origin + '0 0 120'; holdvel = normalize(diff); holdangles = vectoangles(holdvel); // traceline in front and behind because the beam is wide makevectors (holdangles); spot1 = self.origin + '0 0 120' + v_right * 25; traceline (spot1,self.enemy.origin , FALSE, self); if (trace_ent != self.enemy) { pulltime = 0; } else { spot1 = self.origin + '0 0 120' - v_right * 25; traceline (spot1,self.enemy.origin , FALSE, self); if (trace_ent != self.enemy) pulltime = 0; } if (pulltime) { self.movechain.drawflags(+)MLS_ABSLIGHT; self.movechain.abslight = .5; do_fambeam (self,1,STREAM_ATTACHED, 1, self.origin + '0 0 120', self.enemy.origin); self.enemy.velocity = '0 0 0'; setorigin (self.controller, self.origin + '0 0 120'); diff = self.enemy.origin - self.origin; hold_velocity = normalize(diff); hold_velocity = hold_velocity * -250; self.enemy.velocity = self.enemy.velocity + hold_velocity; if(vlen(self.enemy.velocity)>500) self.enemy.velocity=normalize(self.enemy.velocity)*500; self.enemy.flags (-) FL_ONGROUND; } if (hurttime < time) { diff2 = vlen(self.enemy.origin - self.origin); damage = 300/diff2; if (damage < 2) damage=2; else if (damage >8) damage = 8; T_Damage (self.enemy, self, self, damage); if (self.enemy.health <= 0) { if(self.controller) remove(self.controller); sound(self,CHAN_WEAPON,"misc/null.wav",1,ATTN_NORM); } hurttime = time + 1; } if (pulltime < time) { if(self.controller) remove(self.controller); sound(self,CHAN_WEAPON,"misc/null.wav",1,ATTN_NORM); self.movechain.drawflags(-)MLS_ABSLIGHT; self.movechain.frame += 50; looktime = time + random(5,10); } } else { self.movechain.frame += 1; } if (self.movechain.frame >= $Fscale70) self.monster_stage = FH_STAGE_NORMAL; } else if (self.monster_stage == FH_STAGE_ATTACK2) { if (self.movechain.frame == $Fscale10) { famine_missilespawner(); self.movechain.frame += 50; } self.movechain.frame += 1; if (self.movechain.frame >= $Fscale70) self.monster_stage = FH_STAGE_NORMAL; } else if (self.monster_stage == FH_STAGE_LAUGHING) { self.movechain.frame += 1; if (self.movechain.frame == $FtranB6) sound (self.movechain, CHAN_VOICE, "famine/laugh.wav", 1, ATTN_NORM); else if (self.movechain.frame >= $FtranB16) self.monster_stage = FH_STAGE_NORMAL; } } /*QUAKED rider_famine (1 0 0) (-55 -55 -24) (55 55 150) TRIGGER_WAIT Famine rider monster. You must place rider_path entites on the map. The rider will first proceed to the rider_path point with a path_id of 1. -------------------------FIELDS------------------------- map: next map to go to when you kill the rider target: start spot on the next map -------------------------------------------------------- */ void rider_famine(void) { if (deathmatch) { remove(self); return; } precache_model3 ("models/boss/famhorse.mdl"); precache_model3 ("models/boss/famrider.mdl"); precache_model3 ("models/famshot.mdl"); precache_sound3 ("famine/die.wav"); precache_sound3 ("famine/laugh.wav"); precache_sound3 ("famine/whinny.wav"); precache_sound3 ("famine/pull.wav"); precache_sound3 ("famine/shot.wav"); precache_sound3 ("famine/snort.wav"); precache_sound3 ("famine/clop1.wav"); precache_sound3 ("famine/clop2.wav"); precache_sound3 ("famine/clop3.wav"); precache_sound3 ("misc/null.wav"); precache_sound3 ("raven/blast.wav"); precache_sound3 ("skullwiz/blinkout.wav"); precache_sound3 ("skullwiz/blinkin.wav"); rider_init(); self.solid = SOLID_SLIDEBOX; self.movetype = MOVETYPE_STEP; self.thingtype = THINGTYPE_FLESH; self.yaw_speed = 2; setmodel (self, "models/boss/famhorse.mdl"); self.skin = 0; setsize (self, '-40 -40 0', '40 40 100'); self.health = self.max_health = 2200; self.th_pain = famine_pain; self.rider_gallop_mode = 0; self.speed = fam_speed[self.rider_gallop_mode]; self.rider_path_distance = 75; self.monster_stage = FH_STAGE_NORMAL; self.mass = 30000; self.flags (+) FL_MONSTER; self.flags2 (+) FL_ALIVE; self.monsterclass = CLASS_BOSS; self.yaw_speed = 10; self.experience_value = 500; create_famrider(self); self.attack_finished = 0; self.hull = HULL_POINT; looktime =0; hurttime = 0; self.noise="famine/die.wav"; self.th_save = famhorse_move; self.think = multiplayer_health; thinktime self : 1; } gamecode/hc/h2/fangel.hc000066400000000000000000000474151444734033100153130ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\monsters\Fangel\final\fangel.hc ============================================================================== */ // For building the model $cd Q:\art\models\monsters\Fangel\final $origin 0 0 0 $base BASE skin $skin skin $flags 0 // $frame fblock1 fblock2 fblock3 fblock4 fblock5 $frame fblock6 fblock7 fblock8 fblock9 fblock10 $frame fblock11 fblock12 fblock13 fblock14 fblock15 $frame fblock16 fblock17 fblock18 fblock19 fblock20 $frame fblock21 // $frame fdeth1 fdeth2 fdeth3 fdeth4 fdeth5 $frame fdeth6 fdeth7 fdeth8 fdeth9 fdeth10 $frame fdeth11 fdeth12 fdeth13 fdeth14 fdeth15 $frame fdeth16 fdeth17 fdeth18 fdeth19 fdeth20 $frame fdeth21 fdeth22 fdeth23 fdeth24 fdeth25 $frame fdeth26 fdeth27 fdeth28 fdeth29 fdeth30 $frame fdeth31 fdeth32 fdeth33 fdeth34 fdeth35 $frame fdeth36 fdeth37 fdeth38 fdeth39 fdeth40 // $frame ffly1 ffly2 ffly3 ffly4 ffly5 $frame ffly6 ffly7 ffly8 ffly9 ffly10 $frame ffly11 ffly12 ffly13 ffly14 ffly15 $frame ffly16 ffly17 ffly18 ffly19 ffly20 $frame ffly21 ffly22 ffly23 ffly24 ffly25 $frame ffly26 ffly27 ffly28 ffly29 ffly30 // $frame fhand1 fhand2 fhand3 fhand4 fhand5 $frame fhand6 fhand7 fhand8 fhand9 fhand10 $frame fhand11 fhand12 fhand13 fhand14 fhand15 $frame fhand16 fhand17 fhand18 fhand19 fhand20 $frame fhand21 fhand22 // $frame fmove1 fmove2 fmove3 fmove4 fmove5 $frame fmove6 fmove7 fmove8 fmove9 fmove10 $frame fmove11 fmove12 fmove13 fmove14 fmove15 $frame fmove16 fmove17 fmove18 fmove19 fmove20 // $frame fpain1 fpain2 fpain3 fpain4 fpain5 $frame fpain6 fpain7 fpain8 fpain9 fpain10 $frame fpain11 fpain12 // $frame ftranA1 ftranA2 ftranA3 ftranA4 ftranA5 $frame ftranA6 ftranA7 ftranA8 ftranA9 ftranA10 $frame ftranA11 ftranA12 ftranA13 ftranA14 ftranA15 $frame ftranA16 ftranA17 ftranA18 ftranA19 ftranA20 // $frame ftranB1 ftranB2 ftranB3 ftranB4 ftranB5 $frame ftranB6 ftranB7 ftranB8 ftranB9 ftranB10 $frame ftranB11 ftranB12 ftranB13 ftranB14 ftranB15 $frame ftranB16 ftranB17 ftranB18 ftranB19 ftranB20 // $frame fwing1 fwing2 fwing3 fwing4 fwing5 $frame fwing6 fwing7 fwing8 fwing9 fwing10 $frame fwing11 fwing12 fwing13 fwing14 fwing15 $frame fwing16 fwing17 fwing18 fwing19 fwing20 $frame fwing21 fwing22 fwing23 fwing24 fwing25 $frame fwing26 fwing27 fwing28 fwing29 fwing30 // Function protos void() fangel_blockframes; void fangel_handframes (void); void fangel_wingframes (void); void fangel_flyframes (void); // Constants float fangel_attack_speed = 11; float fangel_move_speed = 6; float () fangel_check_incoming = { entity item; vector vec, realVec; float dot; if(range(self.enemy)<=RANGE_MELEE) return FALSE; if(fov(self,self.enemy,30)&&self.enemy.last_attack+0.75>time) { self.th_save = self.think; self.fangel_Count = 0; fangel_blockframes(); return TRUE; } if(random()>0.4 + skill/10 + self.skin/10) return FALSE; item = findradius(self.origin, 256); while (item) { if (item.owner.classname == "player" && (item.movetype == MOVETYPE_FLYMISSILE || item.movetype == MOVETYPE_BOUNCEMISSILE || item.movetype==MOVETYPE_BOUNCE)) { vec = normalize(self.origin - item.origin + self.view_ofs); realVec = normalize(item.velocity); dot= vec*realVec; if (dot >= 0.4) { self.th_save = self.think; self.fangel_Count = 0; fangel_blockframes(); return TRUE; } } item = item.chain; } return FALSE; }; // Monster Stages float FANGEL_STAGE_WAIT = 0; float FANGEL_STAGE_FLY = 1; float FANGEL_STAGE_FLOAT = 2; float FANGEL_STAGE_SIDESTEP = 3; //float FANGEL_STAGE_STRAIGHT = 3; //float FANGEL_STAGE_REVERSE = 4; //float FANGEL_STAGE_ATTACK = 10; void() fangel_check_wing = { float retval; if(!self.enemy) return; enemy_infront = visible(self.enemy); // If we are flying, don't attack as often if (self.monster_stage == FANGEL_STAGE_FLY && random() < 0.5) enemy_infront = 0; if (enemy_infront) { self.th_save = self.think; enemy_range = range (self.enemy); if (random() < 0.5) retval = CheckMonsterAttack(MA_FAR_MELEE,1); else retval = CheckMonsterAttack(MA_MISSILE,1); } else retval = MA_NOATTACK; if (retval != MA_SUCCESSFUL) fangel_check_incoming(); else self.yaw_speed = fangel_attack_speed; }; void fangel_find_spot (void) { float crj, radius,dist; vector vec; crj=0; while(crj < 50) { radius = random(100,200); vec = randomv('0 0 0', '360 360 0'); vec = normalize(vec); vec = self.enemy.origin + vec*radius; vec_z = vec_z + random(20, 40) + self.enemy.view_ofs_z; tracearea (self.origin,vec,self.mins,self.maxs,FALSE,self); if (trace_fraction == 1) { self.monster_last_seen = vec; dist = self.origin_z - self.monster_last_seen_z; if (dist < -20) self.z_movement = random(0,2); else if (dist > 20 ) self.z_movement = random(-2,0); else self.z_movement = random(-2,2); // Give her a little up and down movement self.z_duration = time + HX_FRAME_TIME * random(15,25); return; } crj += 1; } self.monster_stage = FANGEL_STAGE_FLOAT; self.monster_duration = random(20,30); } void() fangel_init = { // Set the fallen angel ready // dprint(self.enemy.classname); // dprint("- Found enemy\n"); self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin); self.think=self.th_stand; thinktime self : random(.1,.6); self.count = 0; self.monster_stage = FANGEL_STAGE_FLY; self.monster_duration = 0; self.goalentity = self.enemy; self.monster_awake = TRUE; if (self.skin) self.drawflags (+) MLS_POWERMODE; fangel_find_spot(); }; void fangel_wait (void) { thinktime self : 0.15; LocateTarget(); if(self.enemy) // We found a target fangel_init(); else if(random(100)<5&&self.t_width 20) { self.z_movement = random(-2,0); self.z_duration = time + HX_FRAME_TIME * random(15,25); } if (self.monster_duration <= 0) { chance = random(); if (chance < .33) { self.monster_stage = FANGEL_STAGE_FLOAT; self.monster_duration = random(10,30); } else if (chance < .66) self.think = fangel_handframes; // Come back fighting else self.think = fangel_wingframes; // Come back fighting } } void fangel_fly (float thrust) { float retval; float dist; float Length; Length = vhlen(self.origin - self.monster_last_seen); self.ideal_yaw = vectoyaw(self.monster_last_seen - self.origin); ChangeYaw(); dist = (2.0 + thrust * 4); // Movement distance this turn if (Length < 20) { if (random() < 0.1) fangel_find_spot(); else { self.monster_stage = FANGEL_STAGE_FLOAT; self.monster_duration = random(10,30); } return; } retval = walkmove(self.angles_y, dist, FALSE); if (!retval) { fangel_find_spot(); return; } dist = self.origin_z - self.monster_last_seen_z; if (dist < -20) { self.z_movement = random(2,4); self.z_duration = time + HX_FRAME_TIME * random(15,25); } else if (dist > 20) { self.z_movement = random(-2,-4); self.z_duration = time + HX_FRAME_TIME * random(15,25); } if (self.z_duration > time) movestep(0,0,self.z_movement, FALSE); } void () fangel_float = { self.monster_duration = self.monster_duration - 1; ai_face(); enemy_range = range (self.enemy); // Attack if they are too near if ((enemy_range <= RANGE_NEAR) && (random() < .25)) self.monster_duration = 0; else if ((enemy_range <= RANGE_MELEE) && (random() < .90)) self.monster_duration = 0; if (self.monster_duration <= 0) { self.monster_stage = FANGEL_STAGE_FLY; fangel_find_spot(); } }; void fangel_move (float thrust) { check_z_move(thrust/2); // movestep(0,0,thrust/2, FALSE); if (self.monster_stage == FANGEL_STAGE_WAIT) { // dprint("Waiting\n"); fangel_wait(); return; } else if (self.monster_stage == FANGEL_STAGE_FLY) { fangel_fly(thrust); return; } else if (self.monster_stage == FANGEL_STAGE_SIDESTEP) { fangel_sidestep(); return; } else if (self.monster_stage == FANGEL_STAGE_FLOAT) { fangel_float(); return; } } void () fangel_prepare_attack = { ai_face(); }; void () fangel_hand_fire = { fangel_prepare_attack(); sound (self, CHAN_WEAPON, "fangel/hand.wav", 1, ATTN_NORM); do_faSpell('10 -4 8',400); do_faSpell('10 -4 8',300); }; void () fangel_wing_fire = { fangel_prepare_attack(); sound (self, CHAN_WEAPON, "fangel/wing.wav", 1, ATTN_NORM); makevectors (self.angles); do_faBlade('8 6 4',360, v_right*2); do_faBlade('-8 6 4',360, v_right*(-2)); }; float fangel_fly_offsets[20] = { 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1 }; // Frame Code void() fangel_blockframes = { float RetVal; float chance; self.think = fangel_blockframes; thinktime self : HX_FRAME_TIME; if(self.velocity!='0 0 0') self.velocity=self.velocity*0.7; // Turn to face the attacker ai_face(); check_z_move(3); // movestep(0,0,-1, FALSE); // Float down while deflecting shots if (self.fangel_Count) { self.fangel_Count -= 1; RetVal = 3; } else if(fov(self,self.enemy,30)&&self.enemy.last_attack+0.75>time&&self.frame == $fblock13) self.fangel_Count+=1; else RetVal = AdvanceFrame($fblock1,$fblock21); if (self.frame==$fblock21) // Only deflect after this frame { if(self.movechain) remove(self.movechain); self.movechain=world; } if (RetVal == AF_END) { self.takedamage = DAMAGE_YES; chance = random(); if (chance < .1) self.think = self.th_save; else if (chance < .60) self.think = fangel_handframes; // Come back fighting else self.think = fangel_wingframes; // Come back fighting } else if (RetVal == AF_NORMAL) { if (self.frame == $fblock13) self.fangel_Count = 40; } if (self.frame == $fblock9) { self.takedamage = DAMAGE_NO; spawn_reflect(); } }; void() fangel_deathframes = { //entity skull; if(self.health<=-40) { chunk_death(); return; } self.think = fangel_deathframes; thinktime self : HX_FRAME_TIME; traceline (self.origin, self.origin - '0 0 250',FALSE,self); particle2(trace_endpos,'-30 -30 50','30 30 100',384,PARTICLETYPE_SPELL,80); if (self.frame == 26) self.drop_time = time + .25; if ((self.frame == 27) && (!self.flags & FL_ONGROUND)) { self.frame = 26; self.velocity_z = -20; if (self.drop_time < time) // Dropping too long so let her die. self.frame = 27; } /*Should she spew a head if not chunked? if (self.frame == $fdeth40) { skull= spawn(); CreateEntityNew(skull,ENT_FANGEL_HEAD,"models/h_fangel.mdl",chunk_death); skull.flags(-)FL_ONGROUND; skull.avelocity_x = random(30); skull.avelocity_y = random(30); skull.avelocity_z = random(80); thinktime skull : random(4,10); skull.think = MakeSolidCorpse; setorigin(skull,self.origin); } */ if (AdvanceFrame($fdeth1,$fdeth40) == AF_BEGINNING) remove(self); if (self.frame == $fdeth1) self.movetype = MOVETYPE_STEP; if (self.frame == $fdeth10) { if (self.classname == "monster_fallen_angel") sound (self, CHAN_WEAPON, "fangel/death.wav", 1, ATTN_NORM); else sound (self, CHAN_WEAPON, "fangel/death2.wav", 1, ATTN_NORM); } }; void fangel_flyforward (void) { AdvanceFrame($fmove1,$fmove20); self.frame += 1; fangel_move(3*fangel_fly_offsets[self.frame - $fmove1]); if (self.frame == $fmove2) sound (self, CHAN_WEAPON, "fangel/fly.wav", 1, ATTN_NORM); if ((self.frame >= $fmove6 && self.frame <= $fmove8) || (self.frame >= $fmove16 && self.frame <= $fmove18)) { self.fangel_SaveFrame = self.frame; fangel_check_wing(); } } void fangel_flyother (void) { AdvanceFrame($ffly1,$ffly30); fangel_move(3*fangel_fly_offsets[rint((self.frame - $ffly1)*.65)]); if (self.frame == $ffly1) sound (self, CHAN_WEAPON, "fangel/fly.wav", 1, ATTN_NORM); if ((self.frame >= $ffly11 && self.frame <= $ffly13) || (self.frame >= $ffly26 && self.frame <= $ffly28)) { self.fangel_SaveFrame = self.frame; fangel_check_wing(); } } void() fangel_flyframes = { self.think = fangel_flyframes; thinktime self : HX_FRAME_TIME; fangel_check_incoming(); if(self.velocity!='0 0 0') self.velocity=self.velocity*0.7; if (self.monster_stage == FANGEL_STAGE_FLY) fangel_flyforward(); else fangel_flyother(); }; void fangel_handframes (void) { self.think = fangel_handframes; thinktime self : HX_FRAME_TIME; fangel_check_incoming(); if (AdvanceFrame($fhand1,$fhand22) == AF_END) { fangel_prepare_attack(); if (random() < .25) { self.monster_stage = FANGEL_STAGE_SIDESTEP; self.monster_duration = random(20,40); fangel_find_spot(); self.think = fangel_flyframes; } else { self.think = self.th_save; } self.frame = self.fangel_SaveFrame; self.yaw_speed = fangel_move_speed; } else { if (self.frame == $fhand12) fangel_hand_fire(); else fangel_prepare_attack(); } } void() fangel_painframes = { float chance; self.think = fangel_painframes; thinktime self : HX_FRAME_TIME; if (AdvanceFrame($fpain1,$fpain12) == AF_END) { fangel_check_incoming(); chance = random(); if (chance < .33) self.think = self.th_save; else if (chance < .66) self.think = fangel_handframes; // Come back fighting else self.think = fangel_wingframes; // Come back fighting self.frame = self.fangel_SaveFrame; } }; void() fangel_wingframes = { self.think = fangel_wingframes; thinktime self : HX_FRAME_TIME; if (AdvanceFrame($fwing1,$fwing30) == AF_END) { fangel_prepare_attack(); if (random() < .25) { self.monster_stage = FANGEL_STAGE_SIDESTEP; self.monster_duration = random(20,40); fangel_find_spot(); self.think = fangel_flyframes; } else self.think = self.th_save; self.frame = self.fangel_SaveFrame; self.yaw_speed = fangel_move_speed; } else { if (self.classname == "monster_fallen_angel") { if (self.frame == $fwing21) { if (self.shoot_cnt < 3) { if (self.shoot_time < time) { fangel_wing_fire(); self.shoot_cnt += 1; self.shoot_time = time + HX_FRAME_TIME * 3; } self.frame = $fwing20; } else self.shoot_cnt =0; } else fangel_prepare_attack(); } else { if (self.frame == $fwing21) { if (self.shoot_cnt < 10) { vector org; org=self.origin-'0 0 5'; traceline(org,(self.enemy.absmin+self.enemy.absmax)*0.5,TRUE,self); if (trace_fraction==1) { self.effects(+)EF_MUZZLEFLASH; WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); WriteByte (MSG_BROADCAST, TE_STREAM_COLORBEAM); //beam type WriteEntity (MSG_BROADCAST, self); //owner WriteByte (MSG_BROADCAST, 0); //tag + flags WriteByte (MSG_BROADCAST, 1); //time WriteByte (MSG_BROADCAST, rint(random(0,4))); //color WriteCoord (MSG_BROADCAST, org_x); WriteCoord (MSG_BROADCAST, org_y); WriteCoord (MSG_BROADCAST, org_z); WriteCoord (MSG_BROADCAST, trace_endpos_x); WriteCoord (MSG_BROADCAST, trace_endpos_y); WriteCoord (MSG_BROADCAST, trace_endpos_z); LightningDamage (self.origin, trace_endpos, self, 3,"sunbeam"); self.frame -= 1; self.shoot_cnt += 1; } self.frame = $fwing20; } else self.shoot_cnt =0; } else fangel_prepare_attack(); } } }; void(entity attacker, float damage) fangel_pain = { if (random(self.health) > damage||self.pain_finished>time) return; // didn't flinch self.pain_finished=time + 1 + self.skin; if (self.health < 50) self.drawflags (-) DRF_TRANSLUCENT|MLS_POWERMODE; if ((self.frame >= $ffly11 && self.frame <= $ffly13) || (self.frame >= $ffly26 && self.frame <= $ffly28)) { if (self.classname == "monster_fallen_angel") sound (self, CHAN_WEAPON, "fangel/pain.wav", 1, ATTN_NORM); else sound (self, CHAN_WEAPON, "fangel/pain2.wav", 1, ATTN_NORM); self.fangel_SaveFrame = self.frame; // didn't want to use self.think just in case we just began to attack self.th_save = fangel_flyframes; fangel_painframes(); } }; void() init_fangel = { if (deathmatch) { remove(self); return; } self.monster_stage = FANGEL_STAGE_WAIT; precache_model2 ("models/fangel.mdl"); precache_model2 ("models/faspell.mdl"); precache_model2 ("models/fablade.mdl"); precache_model2 ("models/h_fangel.mdl"); precache_sound2("fangel/fly.wav"); precache_sound2("fangel/deflect.wav"); precache_sound2("fangel/hand.wav"); precache_sound2("fangel/wing.wav"); CreateEntityNew(self,ENT_FANGEL,"models/fangel.mdl",fangel_deathframes); self.skin = 0; self.hull = HULL_BIG; if (self.classname == "monster_fallen_angel") { precache_sound2("fangel/ambi1.wav"); self.skin = 0; self.health = 250; self.experience_value = 150; } else { precache_sound2("fangel/ambi2.wav"); self.skin = 1; self.health = 500; self.experience_value = 400; } self.th_stand = fangel_flyframes; self.th_walk = fangel_flyframes; self.th_run = fangel_flyframes; self.th_pain = fangel_pain; self.th_die = fangel_deathframes; self.th_missile = fangel_handframes; self.th_melee = fangel_wingframes; self.headmodel="models/h_fangel.mdl"; total_monsters += 1; self.ideal_yaw = self.angles * '0 1 0'; self.yaw_speed = fangel_move_speed; self.view_ofs = '0 0 -5'; self.use = monster_use; self.mintel = 3; self.flags (+) FL_FLY | FL_MONSTER; self.flags2 (+) FL_ALIVE; if (self.classname == "monster_fallen_angel_lord") self.drawflags (+) DRF_TRANSLUCENT; self.pausetime = 99999999; self.frame=$fhand1; self.think=fangel_wait; thinktime self : 0; // self.th_stand (); }; /*QUAKED monster_fallen_angel (1 0.3 0) (-14 -14 -41) (14 14 23) AMBUSH STUCK JUMP PLAY_DEAD DORMANT New item for QuakeEd -------------------------FIELDS------------------------- -------------------------------------------------------- */ void() monster_fallen_angel = { precache_sound2("fangel/death.wav"); precache_sound2("fangel/pain.wav"); init_fangel(); }; /*QUAKED monster_fallen_angel_lord (1 0.3 0) (-14 -14 -41) (14 14 23) AMBUSH STUCK JUMP PLAY_DEAD DORMANT New item for QuakeEd -------------------------FIELDS------------------------- -------------------------------------------------------- */ void() monster_fallen_angel_lord = { precache_sound2("fangel/death2.wav"); precache_sound2("fangel/pain2.wav"); init_fangel(); }; gamecode/hc/h2/faspell.hc000066400000000000000000000063241444734033100154770ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\monsters\Fangel\spell\FAspell.hc ============================================================================== */ // For building the model $cd Q:\art\models\monsters\Fangel\spell $origin 0 0 0 $base BASE skin1 $skin skin1 $flags 0 // $frame SPELL01 SPELL02 SPELL03 SPELL04 SPELL05 $frame SPELL06 SPELL07 SPELL08 SPELL09 void() faSpellTouch = { float damg; if (other == self.owner) return; // don't explode on owner if (pointcontents(self.origin) == CONTENT_SKY) { remove(self); return; } damg = random(12,22); if (other.health) T_Damage (other, self, self.owner, damg ); T_RadiusDamage (self, self.owner, damg, other); sound (self, CHAN_WEAPON, "weapons/explode.wav", 1, ATTN_NORM); self.origin = self.origin - 8*normalize(self.velocity); CreateRedSpark(self.origin); remove(self); }; // Frame Code /* void() frame_SPELL01 = [ $SPELL01 , frame_SPELL02 ] { }; void() frame_SPELL02 = [ $SPELL02 , frame_SPELL03 ] { }; void() frame_SPELL03 = [ $SPELL03 , frame_SPELL04 ] { }; void() frame_SPELL04 = [ $SPELL04 , frame_SPELL05 ] { }; void() frame_SPELL05 = [ $SPELL05 , frame_SPELL06 ] { }; void() frame_SPELL06 = [ $SPELL06 , frame_SPELL07 ] { }; void() frame_SPELL07 = [ $SPELL07 , frame_SPELL08 ] { }; void() frame_SPELL08 = [ $SPELL08 , frame_SPELL09 ] { }; void() frame_SPELL09 = [ $SPELL09 , frame_SPELL01 ] { }; */ void() faspell_frames = { float old_angle, old_count; vector new_posA, new_posB, posA, posB; self.think = faspell_frames; thinktime self : HX_FRAME_TIME; AdvanceFrame($SPELL01,$SPELL09); AdvanceFrame($SPELL01,$SPELL09); old_angle = self.spell_angle; old_count = self.count; self.spell_angle += random(32,42); if (self.spell_angle >= 360) self.spell_angle -= 360; if (self.count < 6) self.count += 0.6; makevectors (self.angles); posA = v_right * sin(self.spell_angle) * self.count; posB = v_right * sin(old_angle) * old_count; new_posA = (posA-posB); posA = v_up * cos(self.spell_angle) * self.count; posB = v_up * cos(old_angle) * old_count; new_posB = (posA-posB); new_posA += new_posB; movestep(new_posA_x,new_posA_y,new_posA_z, FALSE); if(self.lifetime=THINGTYPE_WEBS) traceline (trace_endpos, spot2, FALSE, trace_ent); if (trace_ent != targ) if(trace_ent.health>25||!trace_ent.takedamage||(trace_ent.flags&FL_MONSTER&&trace_ent.classname!="player_sheep")) return FALSE;//Don't have a clear shot, and don't want to shoot obstruction //FIXME: check for translucent water? if (trace_inopen && trace_inwater) return FALSE; // sight line crossed contents if (enemy_range == RANGE_MELEE) { // melee attack if (self.th_melee) { self.th_melee (); return TRUE; } } //FIXME: check for darkness, maybe won't fire, maybe aim will be off // missile attack if (!self.th_missile) return FALSE; if (time < self.attack_finished) return FALSE; if (enemy_range == RANGE_FAR) return FALSE; if (enemy_range == RANGE_MELEE) { chance = 0.9; self.attack_finished = 0; } else if (enemy_range == RANGE_NEAR) { if (self.th_melee) chance = 0.2; else chance = 0.4; } else if (enemy_range == RANGE_MID) { if (self.th_melee) chance = 0.05; else chance = 0.1; } else chance = 0; if (random () < chance) { self.th_missile (); SUB_AttackFinished (random(0,2)); return TRUE; } return FALSE; } /* * ai_face() -- Keep facing the enemy. */ void ai_face() { self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin); // self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin); ChangeYaw(); } /* * ai_charge() -- The monster is in a melee attack, so get as close as * possible to .enemy. */ float visible(entity targ); float infront(entity targ); float range (entity targ); void ai_charge(float d) { ai_face(); movetogoal(d); // done in C code... } void ai_charge_side() { local vector dtemp; local float heading; // aim to the left of the enemy for a flyby // self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin); self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin); ChangeYaw(); makevectors (self.angles); dtemp = self.enemy.origin - 30*v_right; heading = vectoyaw(dtemp - self.origin); walkmove(heading, 20, FALSE); } /* * ai_melee() */ void ai_melee() {//Bad idea- doesn't care where around self player is! vector org1,org2; float ldmg; if (!self.enemy) return; // removed before stroke org1=self.origin+self.proj_ofs; org2=self.enemy.origin; if(vlen(org2-org1)>60) return; traceline(org1,org2,FALSE,self); if(trace_ent!=self.enemy) { org2=(self.enemy.absmin+self.enemy.absmax)*0.5; traceline(org1,org2,FALSE,self); } if(!trace_ent.takedamage) return; if(self.model=="models/spider.mdl") ldmg=random(self.scale*3); else ldmg = random(9); T_Damage (trace_ent, self, self, ldmg); } /* * ai_melee_side() */ void ai_melee_side() { local vector delta; local float ldmg; if (!self.enemy) return; // removed before stroke ai_charge_side(); delta = self.enemy.origin - self.origin; if (vlen(delta) > 60) return; if (!CanDamage (self.enemy, self)) return; ldmg = random(9); T_Damage (self.enemy, self, self, ldmg); } gamecode/hc/h2/fireball.hc000066400000000000000000000057021444734033100156300ustar00rootroot00000000000000 /* ============================================================================== FIREBALL ============================================================================== */ // For building the model $cd c:\model\fireball // Directory to find model in $origin 0 0 0 // baseframe is in iceimp.3ds $base fireball // skin is in iceimp.lbm $skin fireball // Imp throwing $frame firbal1 firbal2 firbal3 firbal4 firbal5 $frame firbal6 firbal7 firbal8 firbal9 firbal10 //============================================================================ void FireFizzle (void) { sound (self, CHAN_WEAPON, "misc/fout.wav", 1, ATTN_NORM); DeathBubbles(1); remove(self); } void() fireballTouch = { local float damg; if (other == self.owner) return; // don't explode on owner if (pointcontents(self.origin) == CONTENT_SKY) { remove(self); return; } if (self.dmg == -1) damg = random(5,10); else if (self.dmg) damg = self.dmg; else damg = random(12,22); if (other.health) { if (self.owner.classname == "cube_of_force") T_Damage (other, self.owner, self.owner.owner, damg ); else T_Damage (other, self, self.owner, damg ); } // don't do radius damage to the other, because all the damage // was done in the impact T_RadiusDamage (self, self.owner, damg, other); // sound (self, CHAN_WEAPON, "weapons/explode.wav", 1, ATTN_NORM); self.origin = self.origin - 8*normalize(self.velocity); WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); WriteByte (MSG_BROADCAST, TE_EXPLOSION); WriteCoord (MSG_BROADCAST, self.origin_x); WriteCoord (MSG_BROADCAST, self.origin_y); WriteCoord (MSG_BROADCAST, self.origin_z); remove(self); // BecomeExplosion (); }; //============================================================================ void fireball_1(void) { float retval; self.nextthink = time + HX_FRAME_TIME; retval = AdvanceFrame($firbal1,$firbal10); if (retval == AF_BEGINNING) { if (pointcontents(self.origin) != CONTENT_EMPTY) FireFizzle(); } } //============================================================================ void(vector offset) do_fireball = { entity missile; vector vec; missile = spawn (); missile.owner = self; missile.speed=500; if(self.classname=="monster_imp_lord") { missile.dmg=random(80,120); missile.speed+=500; missile.scale=2; } else missile.dmg = self.dmg; missile.movetype = MOVETYPE_FLYMISSILE; missile.solid = SOLID_BBOX; missile.health = 10; setmodel (missile, "models/fireball.mdl"); setsize (missile, '0 0 0', '0 0 0'); // set missile speed makevectors (self.angles); vec = self.origin + self.view_ofs + v_factor(offset); setorigin (missile, vec); vec = self.enemy.origin - missile.origin + self.enemy.view_ofs; vec = normalize(vec); missile.velocity = (vec+aim_adjust(self.enemy))*missile.speed; missile.angles = vectoangles('0 0 0'-missile.velocity); missile.touch = fireballTouch; missile.think = fireball_1; missile.nextthink = time + HX_FRAME_TIME; }; gamecode/hc/h2/fish.hc000066400000000000000000000123411444734033100147760ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\monsters\FISH\FISH1.hc ============================================================================== */ // For building the model $cd Q:\art\models\monsters\FISH $origin 0 0 0 $base BASE SKIN $skin SKIN $skin SKIN2 $flags 0 // $frame SWIM01 SWIM02 SWIM03 SWIM04 SWIM05 $frame SWIM06 SWIM07 SWIM08 SWIM09 SWIM10 $frame SWIM11 SWIM12 SWIM13 SWIM14 SWIM15 $frame SWIM16 SWIM17 SWIM18 SWIM19 SWIM20 $frame SWIM21 SWIM22 SWIM23 SWIM24 SWIM25 $frame SWIM26 SWIM27 SWIM28 SWIM29 SWIM30 $frame SWIM31 SWIM32 SWIM33 SWIM34 SWIM35 $frame SWIM36 SWIM37 SWIM38 SWIM39 SWIM40 // Frame Code float FISH_STAGE_MOVE = 1; float FISH_STAGE_FOLLOW = 2; float FISH_STAGE_BORED = 3; void fish_hover(void); void fish_move(void); float fish_friends(void) { entity item,test; float bad; item = findradius(self.origin, 100); while (item) { if (item.classname == "monster_fish" && item != self) { test = item.goalentity; bad = FALSE; while(test != world && bad != TRUE) { if (test == self) bad = TRUE; test = test.goalentity; } if (!bad) { self.goalentity = item; self.goalentity.fish_leader_count += 1; return TRUE; } } item = item.chain; } return FALSE; } void fish_follow(void) { thinktime self : HX_FRAME_TIME; AdvanceFrame($SWIM01,$SWIM40); if (self.goalentity == world) /* experienced by Rugxulo */ { self.think = fish_hover; self.monster_stage = FISH_STAGE_MOVE; return; } if (random() > 0.1) return; if (random() < .05) { self.monster_duration = random(250,450); self.monster_stage = FISH_STAGE_BORED; self.think = fish_hover; self.goalentity.fish_leader_count -= 1; self.goalentity = world; //dprint("Fish got bored\n"); //self.drawflags (-) MLS_ABSLIGHT; return; /* a return was missing here -- O.S. */ } self.movedir = self.monster_last_seen - self.origin + randomv('-20 -20 -25', '20 20 25'); if (self.goalentity.goalentity) { self.goalentity.fish_leader_count -= 1; self.goalentity = self.goalentity.goalentity; self.goalentity.fish_leader_count += 1; } self.monster_last_seen = self.goalentity.origin; self.count = 80 + random(20); self.movedir_x /= self.count; self.movedir_y /= self.count; self.movedir_z /= self.count; self.fish_speed = vhlen(self.movedir); self.think = fish_move; } void fish_move(void) { float retval; thinktime self : HX_FRAME_TIME; AdvanceFrame($SWIM01,$SWIM40); self.ideal_yaw = vectoyaw(self.movedir); ChangeYaw(); retval = walkmove(self.angles_y, self.fish_speed, FALSE); retval = movestep(0, 0, self.movedir_z, FALSE); /* if (retval != 2) { self.goalentity = world; self.monster_stage = FISH_STAGE_MOVE; } */ if (self.count >= 170) self.fish_speed *= 1.05; else if (self.count < 30) self.fish_speed *= .9; self.count -= 1; if (self.count < 1) { self.count = 0; if (self.monster_stage == FISH_STAGE_MOVE) { if (fish_friends()) { self.monster_stage = FISH_STAGE_FOLLOW; self.think = fish_follow; self.monster_last_seen = self.goalentity.origin; //self.drawflags (+) MLS_ABSLIGHT; //self.abslight = 0; //self.goalentity.drawflags (+) MLS_ABSLIGHT; //self.goalentity.abslight = 2.5; //dprint("Following!\n"); } else self.think = fish_hover; } else if (self.monster_stage == FISH_STAGE_FOLLOW) self.think = fish_follow; else if (self.monster_stage == FISH_STAGE_BORED) self.think = fish_hover; } } void fish_hover(void) { float try; thinktime self : HX_FRAME_TIME; AdvanceFrame($SWIM01,$SWIM40); if (self.monster_stage == FISH_STAGE_BORED) { self.monster_duration -= 1; if (self.monster_duration <= 0) self.monster_stage = FISH_STAGE_MOVE; } if (random() < 0.02) { try = 0; while (try < 10) { self.movedir = randomv('-100 -100 -30', '100 100 30'); tracearea(self.origin, self.origin + self.movedir, self.mins, self.maxs, FALSE, self); if (trace_fraction == 1) { self.think = fish_move; self.count = 170 + random(30); self.movedir_x /= 400; self.movedir_y /= 400; self.movedir_z /= 400; self.fish_speed = vhlen(self.movedir); try = 999; } try += 1; } } } void fish_die(void) { remove(self); } /*QUAKED monster_fish (1 0 0) (-16 -16 -8) (16 16 8) Ambient Fish -------------------------FIELDS------------------------- skin: 0 = bright colored, 1 = darker colored -------------------------------------------------------- */ void monster_fish(void) { precache_model2 ("models/fish.mdl"); self.takedamage = DAMAGE_YES; self.solid = SOLID_SLIDEBOX; self.movetype = MOVETYPE_FLY; self.flags (+) FL_SWIM | FL_MONSTER; self.yaw_speed = 2; self.hull = HULL_PLAYER; self.monster_stage = FISH_STAGE_MOVE; self.mass = 99999; // Big fishies! // self.drawflags (+) MLS_POWERMODE; setmodel (self, "models/fish.mdl"); self.skin = 0; setsize (self, '-10 -10 -8', '10 10 8'); self.health = 1; self.th_die = fish_die; self.think = fish_hover; thinktime self : HX_FRAME_TIME; } gamecode/hc/h2/fx.hc000066400000000000000000000242521444734033100144660ustar00rootroot00000000000000 float WHITE_PUFF = 0; float RED_PUFF = 1; float GREEN_PUFF = 2; float GREY_PUFF = 3; void CreateTeleporterBodyEffect (vector org,vector vel,float framelength) { starteffect(CE_TELEPORTERBODY, org,vel,framelength); } void CreateTeleporterSmokeEffect (vector org,vector vel,float framelength) { starteffect(CE_TELEPORTERPUFFS, org,vel,framelength); } // ============= SMOKE ================================ void CreateWhiteSmoke (vector org,vector vel,float framelength) { starteffect(CE_WHITE_SMOKE, org,vel,framelength); } void CreateRedSmoke (vector org,vector vel,float framelength) { starteffect(CE_RED_SMOKE, org,vel, framelength); } void CreateGreySmoke (vector org,vector vel,float framelength) { starteffect(CE_GREY_SMOKE, org,vel, framelength); } void CreateGreenSmoke (vector org,vector vel,float framelength) { starteffect(CE_GREEN_SMOKE, org,vel, framelength); } void CreateRedCloud (vector org,vector vel,float framelength) { starteffect(CE_REDCLOUD, org,vel, framelength); } // ============= FLASHES ================================ void CreateLittleWhiteFlash (vector spot) { starteffect(CE_SM_WHITE_FLASH,spot); } void CreateLittleBlueFlash (vector spot) { starteffect(CE_SM_BLUE_FLASH,spot); } void CreateBlueFlash (vector spot) { starteffect(CE_BLUE_FLASH,spot); } void CreateWhiteFlash (vector spot) { starteffect(CE_WHITE_FLASH, spot); } void CreateYRFlash (vector spot) { starteffect(CE_YELLOWRED_FLASH,spot); } // ============= EXPLOSIONS ============================= void CreateBlueExplosion (vector spot) { starteffect(CE_BLUE_EXPLOSION,spot); } void CreateExplosion29 (vector spot) { starteffect(CE_BG_CIRCLE_EXP,spot); } void CreateFireCircle (vector spot) { starteffect(CE_SM_CIRCLE_EXP,spot); } // ============= SPARKS ============================= void CreateRedSpark (vector spot) { starteffect(CE_REDSPARK,spot); } void CreateGreenSpark (vector spot) { starteffect(CE_GREENSPARK,spot); } void CreateBSpark (vector spot) { starteffect(CE_BLUESPARK,spot); } void CreateSpark (vector spot) { starteffect(CE_YELLOWSPARK,spot); } //FIXME:This should be a temp entity void splash_run (void) { float result; result = AdvanceFrame(0,5); self.nextthink = time + HX_FRAME_TIME; self.think = splash_run; if (result == AF_END) { self.nextthink = time + HX_FRAME_TIME; self.think = SUB_Remove; } } void CreateWaterSplash (vector spot) { entity newent; newent = spawn(); setmodel (newent, "models/wsplash.spr"); setorigin (newent, spot); newent.movetype = MOVETYPE_NOCLIP; newent.solid = SOLID_NOT; newent.velocity = '0 0 0'; newent.nextthink = time + 0.05; newent.think = splash_run; } /* ================ SpawnPuff ================ */ void SpawnPuff (vector org, vector vel, float damage,entity victim) { float part_color; float rad; if (victim.thingtype==THINGTYPE_FLESH && victim.classname!="mummy" && victim.netname != "spider") part_color = 256 + 8 * 16 + random(9); //Blood red else if ((victim.thingtype==THINGTYPE_GREYSTONE) || (victim.thingtype==THINGTYPE_BROWNSTONE)) part_color = 256 + 20 + random(8); // Gray else if (victim.thingtype==THINGTYPE_METAL) part_color = 256 + (8 * 15); // Sparks else if (victim.thingtype==THINGTYPE_WOOD) part_color = 256 + (5 * 16) + random(8); // Wood chunks else if (victim.thingtype==THINGTYPE_ICE) part_color = 406+random(8); // Ice particles else if (victim.netname == "spider") part_color = 256 + 183 + random(8); // Spider's have green blood else part_color = 256 + (3 * 16) + 4; // Dust Brown rad=vlen(vel); if(!rad) rad=random(10,20); particle4(org,rad,part_color,PARTICLETYPE_FASTGRAV,2 * damage); } /*----------------------------------------- redblast - the red flash sprite -----------------------------------------*/ void(vector spot) CreateRedFlash = { starteffect(CE_RED_FLASH,spot); }; void() DeathBubblesSpawn; void () flash_remove = { remove(self); }; void GenerateTeleportSound (entity center) { string telesnd; float r; r=rint(random(4))+1; if(r==1) telesnd="misc/teleprt1.wav"; else if(r==2) telesnd="misc/teleprt2.wav"; else if(r==3) telesnd="misc/teleprt3.wav"; else if(r==4) telesnd="misc/teleprt4.wav"; else telesnd="misc/teleprt5.wav"; sound(center,CHAN_AUTO,telesnd,1,ATTN_NORM); } void GenerateTeleportEffect(vector spot1,float teleskin) { entity sound_ent; if (self.attack_finished > time) return; sound_ent = spawn(); setorigin(sound_ent,spot1); GenerateTeleportSound(sound_ent); sound_ent.think = SUB_Remove; thinktime sound_ent : 2; CreateTeleporterBodyEffect (spot1,'0 0 0',teleskin); // 3rd parameter is the skin CreateTeleporterSmokeEffect (spot1,'0 0 0',HX_FRAME_TIME); CreateTeleporterSmokeEffect (spot1 + '0 0 64','0 0 0',HX_FRAME_TIME); // GenerateTeleportSound(newent); // if (self.scale < 0.11) // { // particle4(self.origin + '0 0 40',random(5,10),20,PARTICLETYPE_FASTGRAV,random(20,30)); // particle4(self.origin + '0 0 40',random(5,10),250,PARTICLETYPE_FASTGRAV,random(20,30)); // remove(self); // } } void smoke_generator_use(void) { self.use = smoke_generator_use; self.nextthink = time + HX_FRAME_TIME; if (!self.wait) self.wait = 2; self.owner = other; if (self.lifespan) self.lifetime = time + self.lifespan; } void smoke_generator_run(void) { if (self.thingtype == WHITE_PUFF) CreateWhiteSmoke(self.origin, '0 0 8', HX_FRAME_TIME *3); else if (self.thingtype == RED_PUFF) CreateRedSmoke(self.origin, '0 0 8', HX_FRAME_TIME *3); else if (self.thingtype == GREEN_PUFF) CreateRedSmoke(self.origin, '0 0 8', HX_FRAME_TIME *3); else if (self.thingtype == GREY_PUFF) CreateGreySmoke(self.origin, '0 0 8', HX_FRAME_TIME *3); self.nextthink = time + random(self.wait); self.think = smoke_generator_run; if ((self.lifespan) && (self.lifetime < time)) remove(self); } /*QUAKED fx_smoke_generator (0 1 1) (-8 -8 -8) (8 8 8) Generates smoke puffs -------------------------FIELDS------------------------- wait - how often it should generate smoke (default 2) thingtype - type of smoke to generate 0 - white puff (fire place) 1 - red (lava) 2 - green (slime) 3 - grey (oil) lifespan - fill this in and it will only puff for this long -------------------------------------------------------- */ void() fx_smoke_generator = { setmodel(self, "models/null.spr"); self.solid = SOLID_NOT; self.movetype = MOVETYPE_NONE; setsize (self,'0 0 0' , '0 0 0'); self.th_die = SUB_Remove; if (!self.targetname) // Not targeted by anything so puff away self.nextthink = time + HX_FRAME_TIME; self.use = smoke_generator_use; if (!self.wait) self.wait = 2; self.think = smoke_generator_run; }; void (vector org) fx_flash = { local entity newent; newent = spawn(); setmodel (newent, "models/s_bubble.spr"); // setmodel (newent, newent.model); // newent.modelindex = 0; // newent.model = ""; setorigin (newent, org + '0 0 24'); newent.movetype = MOVETYPE_NOCLIP; newent.solid = SOLID_NOT; newent.velocity = '0 0 0'; newent.nextthink = time + 0.5; newent.think = flash_remove; newent.classname = "bubble"; newent.effects = EF_BRIGHTLIGHT; setsize (newent, '-8 -8 -8', '8 8 8'); }; /* void () friction_change_touch = { if (other == self.owner) return; if (other.classname == "player") other.friction=self.friction; }; */ /*QUAK-ED fx_friction_change (0 1 1) ? Set the friction within this area. -------------------------FIELDS------------------------- 'friction' : this is how quickly the player will slow down after he ceases indicating movement (lets go of arrow keys). 1 : normal friction >0 & <1 : slippery >1 : high friction -------------------------------------------------------- */ /* void() fx_friction_change = { self.movetype = MOVETYPE_NONE; self.owner = self; self.solid = SOLID_TRIGGER; setorigin (self, self.origin); setmodel (self, self.model); self.modelindex = 0; self.model = ""; setsize (self, self.mins , self.maxs); self.touch = friction_change_touch; }; */ void() explosion_done = { self.effects=EF_DIMLIGHT; }; void() explosion_use = { /* if (self.spawnflags & FLASH) { self.effects=EF_BRIGHTLIGHT; self.think=p_explosion_done; self.nextthink= time + 1; } */ sound (self, CHAN_BODY, self.noise1, 1, ATTN_NORM); particleexplosion(self.origin,self.color,self.exploderadius,self.counter); }; /*QUAK-ED fx_particle_explosion (0 1 1) ( -5 -5 -5) (5 5 5) FLASH Gives off a spray of particles like an explosion. -------------------------FIELDS------------------------- FLASH will cause a brief flash of light. "color" is the color of the explosion. Particle colors dim as they move away from the center point. color values : 31 - white 47 - light blue 63 - purple 79 - light green 85 - light brown 101 - red (default) 117 - light blue 133 - yellow 149 - green 238 - red to orange 242 - purple to red 246 - green to purple 250 - blue - green 254 - yellow to blue "exploderadius" is the distance the particles travel before disappearing. 1 - 10 (default 5) "soundtype" the type of sound made during explosion 0 - no sound 1 - rocket explosion (default) 2 - grenade shoot "counter" the number of particles to create 1 - 1024 512 (default) -------------------------------------------------------- */ /* void() fx_particle_explosion = { self.effects=0; self.use=explosion_use; self.movetype = MOVETYPE_NOCLIP; self.owner = self; self.solid = SOLID_NOT; setorigin (self, self.origin); setmodel (self, self.model); setsize (self, self.mins , self.maxs); // Explosion color if ((!self.color) || (self.color>254)) self.color=101; // Explosion sound is what type???? if (self.soundtype>2) self.soundtype=0; else if (!self.soundtype) self.soundtype=1; if (self.soundtype==0) self.noise1 = ("misc/null.wav"); else if (self.soundtype==1) self.noise1 = ("weapons/explode.wav"); else if (self.soundtype==2) self.noise1 = ("weapons/grenade.wav"); self.exploderadius = 10 - self.exploderadius; // This is backwards in builtin function // Explosion radius if ((self.exploderadius<1) || (self.exploderadius>10)) self.exploderadius=5; // Particle count if ((self.counter<1) || (self.counter>1024)) self.counter=512; }; */ gamecode/hc/h2/gauntlet.hc000066400000000000000000000132351444734033100156730ustar00rootroot00000000000000/* ============================================================================== GAUNTLET ============================================================================== */ // For building the model $cd q:/art/models/weapons/gauntlet/final $origin 0 5 10 $base base4 512 473 $skin skin3 // FRAME: 1 $frame GntRoot1 // FRAMES: 2 - 14 $frame 1stGnt1 1stGnt2 1stGnt3 1stGnt4 1stGnt5 $frame 1stGnt6 1stGnt7 1stGnt8 1stGnt9 1stGnt10 $frame 1stGnt11 1stGnt12 1stGnt14 // FRAMES 15 - 28 $frame 2ndGnt1 2ndGnt2 2ndGnt3 2ndGnt4 2ndGnt5 $frame 2ndGnt6 2ndGnt7 2ndGnt8 2ndGnt9 2ndGnt10 $frame 2ndGnt11 2ndGnt12 2ndGnt13 2ndGnt15 $frame 2ndGnt16 2ndGnt19 // FRAMES 29 - 40 $frame 3rdGnt1 3rdGnt3 3rdGnt5 $frame 3rdGnt10 $frame 3rdGnt11 3rdGnt12 3rdGnt13 3rdGnt14 3rdGnt15 $frame 3rdGnt17 $frame 3rdGnt21 3rdGnt22 // Frames 41 - 54 $frame GntTap1 GntTap2 GntTap3 GntTap4 GntTap5 $frame GntTap11 GntTap12 GntTap13 GntTap14 GntTap15 $frame GntTap16 GntTap17 GntTap18 GntTap19 // FRAMES: 55 - 67 $frame 7thGnt1 7thGnt2 7thGnt3 $frame 7thGnt6 7thGnt7 7thGnt8 7thGnt9 7thGnt10 $frame 7thGnt11 7thGnt12 7thGnt13 7thGnt14 $frame 7thGnt19 float GAUNT_BASE_DAMAGE = 16; float GAUNT_ADD_DAMAGE = 12; float GAUNT_PWR_BASE_DAMAGE = 30; float GAUNT_PWR_ADD_DAMAGE = 20; float GAUNT_PUSH = 4; void W_SetCurrentWeapon(void); void gauntlet_fire (float anim) { vector source; vector org; float damg; makevectors (self.v_angle); source = self.origin + self.proj_ofs; traceline (source, source + v_forward*64, FALSE, self); // Straight in front if (trace_fraction == 1.0) { traceline (source, source + v_forward*64 - (v_up * 30), FALSE, self); // 30 down if (trace_fraction == 1.0) { traceline (source, source + v_forward*64 + v_up * 30, FALSE, self); // 30 up if (trace_fraction == 1.0) return; } } org = trace_endpos + (v_forward * 4); if (trace_ent.takedamage) { SpawnPuff (org, '0 0 0', 20,trace_ent); if (self.artifact_active & ART_TOMEOFPOWER) { damg = GAUNT_PWR_BASE_DAMAGE + random(GAUNT_PWR_ADD_DAMAGE); org = trace_endpos + (v_forward * -1); CreateWhiteFlash(org); sound (self, CHAN_WEAPON, "weapons/gauntht1.wav", 1, ATTN_NORM); } else { damg = GAUNT_BASE_DAMAGE + random(GAUNT_ADD_DAMAGE); sound (self, CHAN_WEAPON, "weapons/gauntht1.wav", 1, ATTN_NORM); } T_Damage (trace_ent, self, self, damg); } else { // hit wall sound (self, CHAN_WEAPON, "weapons/gauntht2.wav", 1, ATTN_NORM); WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); WriteByte (MSG_BROADCAST, TE_GUNSHOT); WriteCoord (MSG_BROADCAST, org_x); WriteCoord (MSG_BROADCAST, org_y); WriteCoord (MSG_BROADCAST, org_z); org = trace_endpos + (v_forward * -1); org += '0 0 10'; CreateWhiteSmoke(org,'0 0 2',HX_FRAME_TIME); } } void gauntlet_ready (void) { self.th_weapon=gauntlet_ready; self.weaponframe = $GntRoot1; } void gauntlet_twitch (void) { self.wfs = advanceweaponframe($GntTap1,$GntTap19); self.th_weapon = gauntlet_twitch; if (self.weaponframe == $GntTap3) sound (self, CHAN_VOICE, "fx/wallbrk.wav", 1, ATTN_NORM); if (self.wfs == WF_LAST_FRAME) gauntlet_ready(); } void gauntlet_select (void) { self.wfs = advanceweaponframe($2ndGnt6,$2ndGnt1); self.weaponmodel = "models/gauntlet.mdl"; self.th_weapon=gauntlet_select; self.last_attack=time; self.attack_cnt = 0; if (self.wfs == WF_LAST_FRAME) { self.attack_finished = time - 1; gauntlet_twitch(); } } void gauntlet_deselect (void) { self.wfs = advanceweaponframe($2ndGnt1,$2ndGnt6); self.th_weapon=gauntlet_deselect; self.oldweapon = IT_WEAPON1; if (self.wfs == WF_LAST_FRAME) { W_SetCurrentAmmo(); } } void gauntlet_d (void) { self.wfs = advanceweaponframe($7thGnt3,$7thGnt19); self.th_weapon = gauntlet_d; if (self.weaponframe == $7thGnt6) // Frame 57 sound (self, CHAN_WEAPON, "weapons/gaunt1.wav", 1, ATTN_NORM); else if (self.weaponframe == $7thGnt9) // Frame 63 gauntlet_fire(4); if (self.wfs == WF_LAST_FRAME) gauntlet_ready(); } void gauntlet_c (void) { self.wfs = advanceweaponframe($3rdGnt1,$3rdGnt22); self.th_weapon = gauntlet_c; if (self.weaponframe == $3rdGnt5) sound (self, CHAN_WEAPON, "weapons/gaunt1.wav", 1, ATTN_NORM); else if (self.weaponframe == $3rdGnt12) gauntlet_fire(3); if (self.wfs == WF_LAST_FRAME) gauntlet_ready(); } void gauntlet_b (void) { self.wfs = advanceweaponframe($2ndGnt1,$2ndGnt19); self.th_weapon = gauntlet_b; if ((self.weaponframe == $2ndGnt4) || (self.weaponframe == $2ndGnt5)) self.weaponframe == $2ndGnt6; if (self.weaponframe == $2ndGnt6) sound (self, CHAN_WEAPON, "weapons/gaunt1.wav", 1, ATTN_NORM); else if (self.weaponframe == $2ndGnt9) gauntlet_fire(2); if (self.wfs == WF_LAST_FRAME) gauntlet_ready(); } void gauntlet_a (void) { self.wfs = advanceweaponframe($1stGnt1,$1stGnt14); self.th_weapon = gauntlet_a; if (self.weaponframe == $1stGnt2) sound (self, CHAN_WEAPON, "weapons/gaunt1.wav", 1, ATTN_NORM); else if (self.weaponframe == $1stGnt4) gauntlet_fire(1); if (self.wfs == WF_LAST_FRAME) gauntlet_ready(); } void pal_gauntlet_fire(void) { // r = random(); // Eventually attacks will be random in order if (self.attack_cnt < 1) gauntlet_a (); else if (self.attack_cnt < 2) gauntlet_b (); else if (self.attack_cnt < 3) gauntlet_c (); else if (self.attack_cnt < 4) { gauntlet_d (); self.attack_cnt=-1; } self.attack_cnt += 1; self.attack_finished = time + 0.5; } gamecode/hc/h2/global.hc000066400000000000000000000056131444734033100153110ustar00rootroot00000000000000 //************************************************************************** //** global.hc //************************************************************************** // SYSTEM GLOBALS (globalvars_t C structure) ------------------------------- entity self; entity other; entity world; float time; float frametime; // Force all entities to touch triggers next frame. This is needed // because non-moving things don't normally scan for triggers, and // when a trigger is created (like a teleport trigger), it needs to // catch everything. Decremented each frame, so set to 2 to guarantee // everything is touched. float force_retouch; string mapname; string startspot; float deathmatch; float randomclass; float coop; float teamplay; // Propagated from level to level, used to keep track of completed // episodes. float serverflags; float total_secrets; float total_monsters; // Number of secrets found. float found_secrets; // Number of monsters killed. float killed_monsters; float chunk_cnt; // # of chunks currently existing (don't want to exceed max) // Set by monster spawner to make sure monster init functions don't // precache models after level is started. float done_precache; // Spawnparms are used to encode information about clients across server // level changes. float parm1, parm2, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16; string parm3; // Set by makevectors() vector v_forward, v_up, v_right; // Set by traceline(). float trace_allsolid; float trace_startsolid; float trace_fraction; vector trace_endpos; vector trace_plane_normal; float trace_plane_dist; entity trace_ent; float trace_inopen; float trace_inwater; // Destination of single entity writes. entity msg_entity; // Set by OP_CSTATE ([++ $s..$e], [-- $s..$e]). float cycle_wrapped; float crouch_cnt; float modelindex_assassin; float modelindex_crusader; float modelindex_paladin; float modelindex_necromancer; float modelindex_sheep; float num_players; float exp_mult; // REQUIRED FUNCTIONS ------------------------------------------------------ // Only for testing void main(void); void StartFrame(void); void PlayerPreThink(void); void PlayerPostThink(void); void ClientKill(void); void ClientConnect(void); void PutClientInServer(void); void ClientReEnter(float TimeDiff); void ClientDisconnect(void); void ClassChangeWeapon(void); // END SYSTEM GLOBALS ------------------------------------------------------ // Flag the compiler. void end_sys_globals; float movedist; // Set when a rule exits. float gameover; // NULL string, nothing should be held here. string string_null; // Function launch_spike() sets this after spawning it. entity newmis; // The entity that activated a trigger or brush. entity activator; // Set by T_Damage(). entity damage_attacker; float framecount; float skill; float wp_deselect; // A flag showing a weapon is being deselected ignore impulse 10 gamecode/hc/h2/golem.hc000066400000000000000000000772041444734033100151610ustar00rootroot00000000000000//************************************************************************** //** golem.hc //************************************************************************** // FRAMES ------------------------------------------------------------------ // Common: Rest $frame rest1 rest2 rest3 rest4 rest5 rest6 rest7 rest8 rest9 rest10 $frame rest11 rest12 rest13 rest14 rest15 rest16 rest17 rest18 rest19 $frame rest20 rest21 rest22 // Common: Transition from run to rest $frame transa1 transa2 transa3 transa4 transa5 transa6 transa7 transa8 $frame transa9 transa10 transa11 transa12 transa13 // Common: Transition from rest to run $frame transb1 transb2 transb3 transb4 transb5 transb6 transb7 transb8 $frame transb9 transb10 transb11 transb12 transb13 // Common: Transition from still to run $frame wake1 wake2 wake3 wake4 wake5 wake6 wake7 wake8 wake9 wake10 $frame wake11 wake12 wake13 wake14 wake15 wake16 // Common: Walk $frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 walk10 $frame walk11 walk12 walk13 walk14 walk15 walk16 walk17 walk18 walk19 $frame walk20 walk21 walk22 walk23 walk24 walk25 walk26 walk27 walk28 $frame walk29 walk30 walk31 walk32 walk33 walk34 // Common: Run $frame run1 run2 run3 run4 run5 run6 run7 run8 run9 run10 run11 run12 $frame run13 run14 run15 run16 run17 run18 run19 run20 run21 run22 $frame run23 run24 // Common: Right hand punch attack $frame rpunch1 rpunch2 rpunch3 rpunch4 rpunch5 rpunch6 rpunch7 rpunch8 $frame rpunch9 rpunch10 rpunch11 rpunch12 rpunch13 rpunch14 rpunch15 $frame rpunch16 rpunch17 rpunch18 rpunch19 rpunch20 rpunch21 $frame rpunch22 rpunch23 rpunch24 // Common: Right hand pound attack $frame rpound1 rpound2 rpound3 rpound4 rpound5 rpound6 rpound7 rpound8 $frame rpound9 rpound10 rpound11 rpound12 rpound13 rpound14 rpound15 $frame rpound16 rpound17 rpound18 rpound19 rpound20 rpound21 $frame rpound22 rpound23 rpound24 // Common: Death $frame death1 death2 death3 death4 death5 death6 death7 death8 death9 $frame death10 death11 death12 death13 death14 death15 death16 death17 $frame death18 death19 death20 $frame death21 death22 $framesave x // Stone: Charge at the player $frame rush1 rush2 rush3 rush4 rush5 rush6 rush7 rush8 rush9 rush10 $frame rush11 rush12 rush13 rush14 rush15 rush16 rush17 rush18 rush19 $frame rush20 rush21 rush22 rush23 rush24 $framerestore x // Iron: Gem attack $frame igem1 igem2 igem3 igem4 igem5 igem6 igem7 igem8 igem9 igem10 $frame igem11 igem12 igem13 igem14 igem15 igem16 igem17 igem18 igem19 $frame igem20 igem21 igem22 igem23 igem24 $framerestore x // Bronze: Gem attack $frame bgem1 bgem2 bgem3 bgem4 bgem5 bgem6 bgem7 bgem8 bgem9 bgem10 $frame bgem11 bgem12 bgem13 bgem14 bgem15 bgem16 bgem17 bgem18 bgem19 $frame bgem20 bgem21 bgem22 bgem23 bgem24 // Bronze: Stomp attack $frame stomp1 stomp2 stomp3 stomp4 stomp5 stomp6 stomp7 stomp8 stomp9 $frame stomp10 stomp11 stomp12 stomp13 stomp14 stomp15 stomp16 stomp17 $frame stomp18 stomp19 stomp20 stomp21 stomp22 stomp23 stomp24 // CONSTANTS --------------------------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- void GolemInit(void); void GolemCUse(void); void GolemStand(void); void GolemWalk(void); void GolemRun(void); void GolemSMeleeDecide(void); void GolemIMeleeDecide(void); void GolemBMeleeDecide(void); void GolemPunchRight(void); void GolemPoundRight(void); void GolemSRushBegin(void); void GolemSRushSlide(void); void GolemSRushEnd(void); float GolemFlinch(float firstFrame, float lastFrame); void GolemSPain(void); void GolemIPain(void); void GolemBPain(void); void GolemDie(void); void GolemBBeamBegin(void); void GolemBStomp(void); void GolemIMissile(void); float GolemBCheckBeamAttack(); float GolemICheckMissileAttack(); // PRIVATE DATA DEFINITIONS ------------------------------------------------ // CODE -------------------------------------------------------------------- //========================================================================== // // monster_golem_stone // //========================================================================== /*QUAKED monster_golem_stone (1 0.3 0) (-32 -32 0) (32 32 88) AMBUSH Stone Golem. ------- key / value ---------------------------- health = 200 experience_value = 125 ------- spawnflags ----------------------------- AMBUSH */ void monster_golem_stone(void) { if(deathmatch) { remove(self); return; } precache_model3("models/golem_s.mdl"); precache_model3("models/goarm.mdl"); precache_model3("models/golegs.mdl"); precache_model3("models/g-head.mdl"); precache_sound3("golem/stnfall.wav"); precache_sound3("golem/stnpain.wav"); precache_sound3("golem/slide.wav"); precache_sound3("imp/swoophit.wav"); precache_sound3("golem/dthgroan.wav"); self.thingtype = THINGTYPE_GREYSTONE; setmodel(self, "models/golem_s.mdl"); setsize(self, '-24 -24 0', '24 24 80'); GolemInit(); self.hull = HULL_PLAYER; self.health = 200; self.experience_value = 125; self.mintel = 4; self.th_melee = GolemSMeleeDecide; self.th_pain = GolemSPain; self.view_ofs = self.proj_ofs='0 0 64'; walkmonster_start(); } //========================================================================== // // monster_golem_iron // //========================================================================== /*QUAKED monster_golem_iron (1 0.3 0) (-55 -55 0) (55 55 120) AMBUSH Iron Golem. ------- key / value ---------------------------- health = 400 experience_value = 200 ------- spawnflags ----------------------------- AMBUSH */ void monster_golem_iron(void) { if(deathmatch) { remove(self); return; } precache_model2("models/golem_i.mdl"); precache_model2("models/goarm.mdl"); precache_model2("models/golegs.mdl"); precache_model2("models/g-head.mdl"); precache_model2("models/golemmis.mdl"); precache_sound2("golem/mtlfall.wav"); precache_sound2("golem/mtlpain.wav"); precache_sound2("golem/gbfire.wav"); precache_sound2("golem/dthgroan.wav"); self.thingtype = THINGTYPE_METAL; setmodel(self, "models/golem_i.mdl"); setsize(self, '-32 -32 0', '32 32 80'); GolemInit(); self.health = 450; self.mintel = 6; self.experience_value = 200; self.th_melee = GolemIMeleeDecide; self.th_pain = GolemIPain; self.view_ofs = self.proj_ofs='0 0 64'; walkmonster_start(); } //========================================================================== // // monster_golem_bronze // //========================================================================== /*QUAKED monster_golem_bronze (1 0.3 0) (-64 -64 0) (64 64 194) AMBUSH Bronze Golem. ------- key / value ---------------------------- health = 500 experience_value = 275 ------- spawnflags ----------------------------- AMBUSH */ void monster_golem_bronze(void) { if(deathmatch) { remove(self); return; } self.cnt = 0; precache_model2("models/golem_b.mdl"); precache_model2("models/goarm.mdl"); precache_model2("models/golegs.mdl"); precache_model2("models/g-head.mdl"); precache_sound2("golem/mtlfall.wav"); precache_sound2("golem/mtlpain.wav"); precache_sound2("golem/stomp.wav"); precache_sound2("golem/gbcharge.wav"); precache_sound2("golem/gbfire.wav"); precache_sound2("golem/dthgroan.wav"); self.thingtype = THINGTYPE_METAL; setmodel(self, "models/golem_b.mdl"); setsize(self, '-60 -60 0', '60 60 190'); GolemInit(); self.health = 650; self.mintel = 8; self.experience_value = 275; self.th_melee = GolemBMeleeDecide; self.th_pain = GolemBPain; self.view_ofs = self.proj_ofs='0 0 115'; walkmonster_start(); } //========================================================================== // // monster_golem_crystal // //========================================================================== /*QUAKED monster_golem_crystal (1 0.3 0) (-32 -32 -24) (32 32 64) AMBUSH Crystal Golem. ------- key / value ---------------------------- health = 400 experience_value = 650 ------- spawnflags ----------------------------- AMBUSH */ void monster_golem_crystal(void) { if(deathmatch) { remove(self); return; } precache_model3("models/golem_s.mdl"); precache_sound3("golem/stnpain.wav"); precache_sound3("golem/slide.wav"); precache_sound3("golem/dthgroan.wav"); self.thingtype = THINGTYPE_ICE; setmodel(self, "models/golem_s.mdl"); setsize(self, '-24 -24 0', '24 24 80'); GolemInit(); self.hull = HULL_PLAYER; self.drawflags = DRF_TRANSLUCENT|MLS_ABSLIGHT; self.abslight = 1.4; self.skin = GLOBAL_SKIN_ICE; self.health = 400; self.experience_value = 650; self.th_melee = GolemSMeleeDecide; self.th_pain = GolemSPain; self.use = GolemCUse; self.view_ofs = self.proj_ofs='0 0 64'; walkmonster_start(); self.takedamage = DAMAGE_NO; } //========================================================================== // // GolemInit // //========================================================================== void GolemInit(void) { self.netname="golem"; self.flags (+) FL_MONSTER; self.flags2 (+) FL_ALIVE; self.solid = SOLID_SLIDEBOX; self.movetype = MOVETYPE_STEP; self.yaw_speed = 14; self.mass = 75; self.mintel = 2; self.hull = HULL_GOLEM; self.th_stand = GolemStand; self.th_walk = GolemWalk; self.th_run = GolemRun; self.th_die = GolemDie; precache_sound3("golem/awaken.wav"); precache_sound3("golem/step.wav"); precache_sound3("golem/swing.wav"); } //========================================================================== // // GolemCUse // //========================================================================== void GolemCUse(void) { self.takedamage = DAMAGE_YES; self.drawflags = DRF_TRANSLUCENT|MLS_CRYSTALGOLEM; } //========================================================================== // // GolemStand // //========================================================================== void GolemStand(void) [++ $rest1..$rest22] { ai_stand(); thinktime self : 0.2; } //========================================================================== // // GolemWalk // //========================================================================== void GolemWalk(void) [++ $walk1..$walk34] { if(self.frame == $walk16 || self.frame == $walk33) { sound(self, CHAN_BODY, "golem/step.wav", 1, ATTN_NORM); } ai_walk(2); } //========================================================================== // // GolemRun // //========================================================================== void GolemRun(void) [++ $run1..$run24] { float len; float hdiff; float dist, r; check_pos_enemy(); checkenemy(); if (coop && !visible(self.enemy)) LocateTarget(); if (self.classname == "monster_golem_stone" || self.classname == "monster_golem_crystal") { len = vlen(self.origin - self.enemy.origin); hdiff = fabs(self.origin_z - self.enemy.origin_z); if(len > 50 && len < 300 && hdiff < 80) { if(random() < 0.05) { GolemSRushBegin(); return; } } ai_run(8); } if (self.classname == "monster_golem_bronze") { dist = vlen(self.enemy.origin - self.origin); r = random(0, 10); if (dist < 100) GolemBMeleeDecide(); else if (dist > 256 && visible(self.enemy) && r < 0.6) { if (GolemBCheckBeamAttack() == 1) GolemBBeamBegin(); } else if (dist > 100 && dist < 256 && r < 0.3) GolemBStomp(); ai_run(8); } if (self.classname == "monster_golem_iron") { dist = vlen(self.enemy.origin - self.origin); r = random(0, 10); if (dist < 100) GolemBMeleeDecide(); else if (dist > 100 && visible(self.enemy) && r < 0.4) if (GolemICheckMissileAttack()) GolemIMissile(); ai_run(5); } if(self.frame == $run12 || self.frame == $run24) { sound(self, CHAN_BODY, "golem/step.wav", 1, ATTN_NORM); } } void GolemDoMelee(float critical) { vector delta; float ldmg; if (!self.enemy) return; // removed before stroke delta = self.enemy.origin - self.origin; if (self.classname == "monster_golem_bronze") { if (vlen(delta) > 128) return; ldmg = random(15); } else if (self.classname == "monster_golem_iron") { if (vlen(delta) > 128) return; ldmg = random(12); } else { ldmg = random(8); if (vlen(delta) > 128) return; } if (critical) ldmg = ldmg * 1.5; if (self.attack_finished < time) { sound(self, CHAN_BODY, "imp/swoophit.wav", 1, ATTN_NORM); self.attack_finished = time + 1; } if (self.enemy.health - ldmg <= 0 && critical&&self.enemy.flags2&FL_ALIVE) self.enemy.decap = 2; T_Damage (self.enemy, self, self, ldmg); } //========================================================================== // // GolemSMeleeDecide // //========================================================================== void GolemSMeleeDecide(void) { if(random() < 0.5) { GolemPunchRight(); } else { GolemPoundRight(); } } void GolemIMissileTouch(void) { if (other.health > 0 && other.flags & FL_ALIVE) T_Damage(other, self, self.owner, random(13,17)); remove(self); } void GolemIMissileThink(void) { if (self.count > time) HomeThink(); particle4(self.origin,20,random(128,143),PARTICLETYPE_GRAV,4); self.angles = vectoangles(self.velocity); self.think = GolemIMissileThink; thinktime self : 0.1; } void GolemISpawnMissile(vector vect, vector offset, float vel) { local entity missile; local vector vec; self.last_attack=time; missile = spawn (); missile.classname = "golem_iron_proj"; missile.owner = self; missile.enemy = missile.goalentity = self.enemy; missile.movetype = MOVETYPE_FLYMISSILE; missile.solid = SOLID_BBOX; missile.yaw_speed = 3; missile.drawflags (+) MLS_POWERMODE | SCALE_TYPE_UNIFORM; missile.scale = 2.5; setmodel (missile, "models/golemmis.mdl"); setsize (missile, '0 0 0', '0 0 0'); // set missile speed makevectors (self.angles); setorigin (missile, self.origin + offset); vec = self.enemy.origin - missile.origin + self.enemy.view_ofs; vec = normalize(vec); vec += vect; missile.speed = 300; missile.velocity = vec * (300 - random(vel)); missile.angles = vectoangles(missile.velocity); missile.touch = GolemIMissileTouch; missile.think = GolemIMissileThink; missile.veer=0; //slight veering, random course modifications missile.turn_time = 0.5; missile.hoverz=TRUE; //slow down on turns missile.ideal_yaw=TRUE; missile.count = time + 2; thinktime missile : 0.2; } void GolemIMissile(void) [++ $igem1..$igem24] { vector vect; ai_face(); makevectors(self.angles); if (self.frame == $igem1) self.colormap = 128 + 16; if (self.frame == $igem1) { vect = self.origin + (v_forward * 16); particle4(vect + '0 0 90',15,256+random(128,143),PARTICLETYPE_GRAV,10); } self.colormap -= 0.5; if (self.frame == $igem17 && FacingIdeal()) { sound(self, CHAN_WEAPON, "golem/gbfire.wav", 1, ATTN_NORM); //GolemISpawnMissile(v_right * -1, '0 0 75', 20); GolemISpawnMissile('0 0 0', '0 0 75', 100); /*GolemISpawnMissile(v_right, '0 0 75', 20); GolemISpawnMissile(v_up * -1, '0 0 75', 20); GolemISpawnMissile(v_up, '0 0 75', 20);*/ self.think = self.th_run; self.colormap = 0; thinktime self : 0.1; } } //========================================================================== // // GolemIMeleeDecide // //========================================================================== void GolemIMeleeDecide(void) { if(random() < 0.5) { GolemPunchRight(); } else { GolemPoundRight(); } } //========================================================================== // // GolemBMeleeDecide // //========================================================================== void GolemBMeleeDecide(void) { if(random() < 0.5) { GolemPunchRight(); } else { GolemPoundRight(); } } //========================================================================== // // GolemPunchRight // //========================================================================== void GolemPunchRight(void) [++ $rpunch1..$rpunch24] { vector checkPos; if(cycle_wrapped) { GolemRun(); return; } if(self.frame == $rpunch10) { sound(self, CHAN_BODY, "golem/step.wav", 1, ATTN_NORM); sound(self, CHAN_WEAPON, "golem/swing.wav", 1, ATTN_NORM); } else if(self.frame == $rpunch24) { sound(self, CHAN_BODY, "golem/step.wav", 1, ATTN_NORM); } //ai_charge(8); ai_face(); if (!walkmove(self.angles_y, 8, TRUE)) { if (trace_ent.health <= 0) return; } if(self.frame > $rpunch10 && self.frame < $rpunch16) { makevectors(self.enemy.angles); checkPos = self.enemy.origin + (v_forward * -24); checkPos_z = self.enemy.origin_z + self.enemy.view_ofs_z; traceline(self.enemy.origin, checkPos, FALSE, self.enemy); if (trace_fraction < 1&&!trace_ent.flags2&FL_ALIVE) GolemDoMelee(1); else GolemDoMelee(0); } } //========================================================================== // // GolemPoundRight // //========================================================================== void GolemPoundRight(void) [++ $rpound1..$rpound24] { vector checkPos; if(cycle_wrapped) { GolemRun(); return; } if(self.frame == $rpound10) { sound(self, CHAN_BODY, "golem/step.wav", 1, ATTN_NORM); sound(self, CHAN_WEAPON, "golem/swing.wav", 1, ATTN_NORM); } else if(self.frame == $rpound24) { sound(self, CHAN_BODY, "golem/step.wav", 1, ATTN_NORM); } ai_face(); if (!walkmove(self.angles_y, 10, TRUE)) { if (trace_ent.health <= 0) return; } if(self.frame > $rpound10 && self.frame < $rpound16) { makevectors(self.enemy.angles); checkPos = self.enemy.origin + (v_forward * -24); checkPos_z = self.enemy.origin_z + self.enemy.view_ofs_z; traceline(self.enemy.origin, checkPos, FALSE, self.enemy); if (trace_fraction < 1) GolemDoMelee(1); else GolemDoMelee(0); } } //========================================================================== // // GolemSRushBegin // //========================================================================== void GolemSRushBegin(void) [++ $rush1..$rush12] { if(self.frame == $rush12) { self.golemSlideCounter = 8; self.think = GolemSRushSlide; sound(self, CHAN_WEAPON, "golem/slide.wav", 1, ATTN_NORM); } else if(self.frame == $rush10) { sound(self, CHAN_BODY, "golem/step.wav", 1, ATTN_NORM); } //ai_charge(5); ai_face(); if (!walkmove(self.angles_y, 5, TRUE)) { if (trace_ent.health <= 0) return; } } //========================================================================== // // GolemSRushSlide // //========================================================================== void GolemSRushSlide(void) [$rush12 GolemSRushSlide] { if(walkmove(self.angles_y, 20, FALSE)) { if(random() < 0.2) { CreateWhiteSmoke(self.origin,'0 0 8',HX_FRAME_TIME * 2); } } else { self.think = GolemSRushEnd; return; } self.golemSlideCounter -= 1; if(self.golemSlideCounter < 0) { self.think = GolemSRushEnd; } } float GolemBCheckBeamAttack(void) { vector p1, p2, off; float dist; makevectors(self.angles); dist = vlen(self.enemy.origin - self.origin); off = v_forward * 15 + v_right * 1; p1 = self.origin + '0 0 92' + off; p2 = p1 + (v_forward * dist); p2_z = self.enemy.origin_z + self.enemy.proj_ofs_z; traceline(p1, p2, FALSE, self); if (trace_ent == self.enemy) return 1; return 0; } float GolemICheckMissileAttack(void) { vector p1, p2, off; float dist; makevectors(self.angles); dist = vlen(self.enemy.origin - self.origin); off = v_forward * 15 + v_right * 1; p1 = self.origin + '0 0 92' + off; p2 = p1 + (v_forward * dist); p2_z = self.enemy.origin_z + self.enemy.proj_ofs_z; traceline(p1, p2, FALSE, self); if (trace_ent == self.enemy) return 1; return 0; } void GolemBBeamFinish(void) [++ $bgem18..$bgem24] { if (self.frame == $bgem24) { self.colormap = 0; self.think = self.th_run; thinktime self : 0.1; return; } } void GolemDoBeam(float offset, float damage) { vector p1, p2, off, dir; float dist; makevectors(self.angles); dist = vlen(self.enemy.origin - self.origin); off = v_forward * 15 + v_right * 1; p1 = self.origin + '0 0 92' + off; dist = vlen(p1 - self.enemy.origin + self.enemy.proj_ofs - '0 0 6'); /*p2 = p1 + (v_forward * dist + v_right * offset); p2_z = self.enemy.origin_z + self.enemy.proj_ofs_z;*/ dir = normalize(v_forward*100 + v_right * offset); p2 = p1 + dir*dist; p2_z = self.enemy.origin_z + self.enemy.proj_ofs_z - 6; dir = normalize(p2-p1); traceline(p1, p2+dir*(dist/2), FALSE, self); //traceline(p1, p2, FALSE, self); if (trace_ent != world && trace_ent.health > 0) { sound(trace_ent,CHAN_AUTO,"crusader/sunhit.wav",1,ATTN_NORM); if (trace_ent.health - damage <= 0) trace_ent.decap = TRUE; if (trace_ent.flags & FL_MONSTER) T_Damage(trace_ent,self,self,damage/2); else T_Damage(trace_ent,self,self,damage); } SpawnPuff(trace_endpos, '0 0 10', 1, trace_ent); WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); WriteByte (MSG_BROADCAST, TE_STREAM_COLORBEAM); //beam type WriteEntity (MSG_BROADCAST, self); //owner WriteByte (MSG_BROADCAST, 0); //tag + flags WriteByte (MSG_BROADCAST, 1); //time WriteByte (MSG_BROADCAST, 2); //color WriteCoord (MSG_BROADCAST, p1_x); WriteCoord (MSG_BROADCAST, p1_y); WriteCoord (MSG_BROADCAST, p1_z); WriteCoord (MSG_BROADCAST, trace_endpos_x); WriteCoord (MSG_BROADCAST, trace_endpos_y); WriteCoord (MSG_BROADCAST, trace_endpos_z); } void GolemBBeamFireArch1(void) [$bgem18 GolemBBeamFireArch1] { if (self.golemBeamDelay < time) { self.think = GolemBBeamFinish; thinktime self : 0.1; return; } self.golemBeamOff1 += 5; GolemDoBeam(self.golemBeamOff1, random(7, 12)); } void GolemBBeamFireArch2(void) [$bgem18 GolemBBeamFireArch2] { if (self.golemBeamDelay < time) { self.think = GolemBBeamFinish; thinktime self : 0.1; return; } if (!self.golemBeamOff2) self.golemBeamOff1 += 10; else self.golemBeamOff1 -= 10; if (self.golemBeamOff1 == 100) self.golemBeamOff2 = 1; GolemDoBeam(self.golemBeamOff1, random(7, 12)); } void GolemBBeamFire(void) [++ $bgem11..$bgem18] { float r; ai_face(); if (self.frame == $bgem18) { r = random(); if (visible(self.enemy) && FacingIdeal()) { sound(self, CHAN_WEAPON, "golem/gbfire.wav", 1, ATTN_NORM); if (r < 0.5) { self.golemBeamDelay = time + 2; self.golemBeamOff1 = -100; self.think = GolemBBeamFireArch1; thinktime self : 0.1; } else { self.golemBeamDelay = time + 2; self.golemBeamOff1 = -100; self.golemBeamOff2 = 0; self.think = GolemBBeamFireArch2; thinktime self : 0.1; } } else { self.think = self.th_run; thinktime self : 0.1; } } } void GolemBBeamPause(void) [$bgem11 GolemBBeamPause] { if (self.golemBeamDelay < time && !self.cnt) { self.golemBeamDelay = time + 0.5; self.cnt = 1; self.colormap = 176 + 16; } self.colormap -= 1; if (self.golemBeamDelay > time) { particle4(self.origin + self.view_ofs,5,185 + random(6),PARTICLETYPE_SLOWGRAV,1); ai_face(); return; } self.cnt = 0; GolemBBeamFire(); } //========================================================================== // // GolemBBeam // //========================================================================== void GolemBBeamBegin(void) [++ $bgem1..$bgem11] { sound(self, CHAN_WEAPON, "golem/gbcharge.wav", 1, ATTN_NORM); if (self.frame == $bgem11) { thinktime self : 0.1; self.think = GolemBBeamPause; } } void GolemBStompEffect(void) { float dist; dist = vlen(self.enemy.origin - self.origin); MonsterQuake(350); if (dist < 350) T_Damage(self.enemy, self, self, random(50/dist)); } //========================================================================== // // GolemBStomp // //========================================================================== void GolemBStomp(void) [++ $stomp1..$stomp24] { float numPuffs; vector vect; if (self.frame == $stomp13) { sound(self, CHAN_BODY, "golem/stomp.wav", 1, ATTN_NORM); numPuffs = random(4,10); makevectors(self.angles); while(numPuffs > 0) { vect_x = self.origin_x; vect_y = self.origin_y; vect_z = self.absmin_z; vect_z += v_up_z * 3; vect_x += v_forward_x * random(0, self.size_x); vect_y += v_forward_y * random(0, self.size_y); particle4(vect,20+random(1,10),256+random(90, 95),PARTICLETYPE_GRAV,30); numPuffs -= 1; } GolemBStompEffect(); } if (self.frame == $stomp24) { self.think = self.th_run; thinktime self : 0.1; } } void GolemICheckRushDamage( void ) { float r; float damage; if(!self.enemy) return; r = vlen(self.enemy.origin - self.origin); damage = random(20,30); if (infront(self.enemy) && r < 60) { sound(self, CHAN_BODY, "imp/swoophit.wav", 1, ATTN_NORM); makevectors(self.angles); self.enemy.flags (-) FL_ONGROUND; self.enemy.velocity = (v_forward * (damage * 20)); self.enemy.velocity_z = random(300, 350); T_Damage(self.enemy, self, self, damage); } } //========================================================================== // // GolemSRushEnd // //========================================================================== void GolemSRushEnd(void) [++ $rush13..$rush24] { if(cycle_wrapped) { GolemRun(); return; } if (self.frame == $rush15) { GolemICheckRushDamage(); } if(self.frame == $rush24) { sound(self, CHAN_BODY, "golem/step.wav", 1, ATTN_NORM); } //ai_charge(4); ai_face(); if (!walkmove(self.angles_y, 4, TRUE)) { if (trace_ent.health <= 0) return; } } //========================================================================== // // GolemFlinch // //========================================================================== float GolemFlinch(float firstFrame, float lastFrame) { if(self.frame < firstFrame || self.frame > lastFrame) { return 0; } self.nextthink += 0.1+random()*0.2; self.frame = self.frame - 8 - rint(random() * 12); self.pain_finished = time + 1; if(self.frame < firstFrame) { // Wrap self.frame = lastFrame + 1 - (firstFrame - self.frame); } return 1; } //========================================================================== // // GolemSPain // //========================================================================== void GolemSPain(void) { if(self.pain_finished > time) { return; } //if(GolemFlinch($gwalk1, $gwalk60)) return; //if(GolemFlinch($gLpnch1, $gLpnch22)) return; //if(GolemFlinch($gRpnch1, $gRpnch22)) return; //GolemFlinch($run1, $run24); } //========================================================================== // // GolemIPain // //========================================================================== void GolemIPain(void) { if(self.pain_finished > time) { return; } //if(GolemFlinch($gwalk1, $gwalk60)) return; //if(GolemFlinch($ggem1, $ggem25)) return; //if(GolemFlinch($gRpnch1, $gRpnch22)) return; //GolemFlinch($run1, $run24); } //========================================================================== // // GolemBPain // //========================================================================== void GolemBPain(void) { if(self.pain_finished > time) { return; } //if(GolemFlinch($gwalk1, $gwalk60)) return; //if(GolemFlinch($ggem1, $ggem25)) return; //if(GolemFlinch($gLpnch1, $gLpnch22)) return; //if(GolemFlinch($gRpnd1, $gRpnd20)) return; //GolemFlinch($run1, $run24); } float GolemCheckSolidGround( void ) { vector p1, p2, p3, p4, destin; float numSolid = 4; makevectors(self.angles); p1 = self.origin + (v_forward * (self.size_y * 0.8)); p2 = self.origin - (v_forward * (self.size_y * 0.8)); p3 = self.origin + (v_right * (self.size_x * 0.5)); p4 = self.origin - (v_right * (self.size_x * 0.5)); destin = p1 - (v_up * self.size_y); traceline(p1, destin, FALSE, self); if (trace_fraction == 1) numSolid -= 1; destin = p2 - (v_up * self.size_y); traceline(p2, destin, FALSE, self); if (trace_fraction == 1) numSolid -= 1; destin = p3 - (v_up * self.size_y); traceline(p3, destin, FALSE, self); if (trace_fraction == 1) numSolid -= 1; destin = p4 - (v_up * 2); traceline(p4, destin, FALSE, self); if (trace_fraction == 1) numSolid -= 1; if (numSolid < 3) return 0; return 1; } void GolemChunkPlace(string gibname, vector pos, vector vel) { local entity new; makevectors(self.angles); new = spawn(); new.origin = pos; setmodel (new, gibname); setsize (new, '0 0 0', '0 0 0'); new.velocity = vel; new.movetype = MOVETYPE_BOUNCE; new.solid = SOLID_NOT; new.angles = self.angles; if (gibname != "models/golegs.mdl") { new.avelocity_y = random(100,500); new.velocity_x += random(v_forward_x * 100,v_forward_x * 300); new.velocity_y += random(v_forward_y * 100,v_forward_y * 300); new.velocity_z += random(v_forward_z * 350,v_forward_z * 600); new.flags (-) FL_ONGROUND; } if (self.classname == "monster_golem_crystal") { new.drawflags = DRF_TRANSLUCENT|MLS_ABSLIGHT; new.abslight = 1.4; new.skin = GLOBAL_SKIN_ICE; } if (self.classname == "monster_golem_stone") new.skin = 0; if (self.classname == "monster_golem_iron") { new.scale = 1.5; new.skin = 1; } if (self.classname == "monster_golem_bronze") { new.scale = 2.0; new.skin = 2; } new.think = SUB_Remove; new.ltime = time; new.nextthink = time + 10 + random()*10; new.frame = 0; new.flags = 0; } void GolemChunkDeath(void) { float numPuffs; vector vect, dir; makevectors(self.angles); dir = v_right*random(150, 200); dir_z = random(100, 300); GolemChunkPlace("models/goarm.mdl", self.origin + (v_right * 16), dir); dir = v_right*random(150, 200)*-1; dir_z = random(100, 300); GolemChunkPlace("models/goarm.mdl", self.origin + (v_right * -16), dir); GolemChunkPlace("models/golegs.mdl", self.origin + (v_forward * -12), '0 0 0'); GolemChunkPlace("models/g-head.mdl", self.origin + (v_forward * random(10, 20)), '0 0 250'); numPuffs = random(4,10); while(numPuffs > 0) { vect = self.origin; vect_z += v_up_z * 4; vect_x += v_forward_x * random(0, self.size_x); vect_y += v_forward_y * random(0, self.size_y); particle4(vect,20+random(1,10),256+random(90, 95),PARTICLETYPE_GRAV,10); numPuffs -= 1; } if (self.classname == "monster_golem_crystal") self.thingtype = THINGTYPE_ICE; self.think = chunk_death; thinktime self: 0.1; } void GolemDeathFinish(void) [++ $death12..$death22] { if(self.frame == $death22) { self.nextthink = -1; if(self.classname == "monster_golem_stone" || self.classname == "monster_golem_crystal") { sound(self, CHAN_BODY, "golem/stnfall.wav", 1, ATTN_NORM); } else if(self.classname == "monster_golem_iron") { sound(self, CHAN_BODY, "golem/mtlfall.wav", 1, ATTN_NORM); } else { // Assumed bronze sound(self, CHAN_BODY, "golem/mtlfall.wav", 1, ATTN_NORM); } if (GolemCheckSolidGround()) { GolemChunkDeath(); //MakeSolidCorpse(); } else { self.think = chunk_death; thinktime self : 0.1; } return; } if(self.frame == $death16) { self.solid = SOLID_NOT; } if (self.frame > $death16) { makevectors(self.angles); self.origin += v_forward * 4; } if(self.health < -50) { self.think = chunk_death; } thinktime self : 0.07; } void GolemDeathPause(void) [$death11 GolemDeathPause] { vector vect; if ((self.cnt - time) == 1) { sound(self, CHAN_BODY, "golem/dthgroan.wav", 1, ATTN_NORM); if (self.classname == "monster_golem_bronze") { makevectors(self.angles); vect = self.origin + (v_forward * (self.size_x / 2)); particle4(vect + '0 0 80',15,256+random(184, 191),PARTICLETYPE_GRAV,10); } if (self.classname == "monster_golem_iron") { makevectors(self.angles); vect = self.origin + (v_forward * (self.size_x / 2)); particle4(vect + '0 0 70',15,256+random(128,143),PARTICLETYPE_GRAV,10); } } if (self.cnt < time) GolemDeathFinish(); } //========================================================================== // // GolemDie // //========================================================================== void GolemDie(void) [++ $death1..$death11] { self.colormap = 0; if (self.frame == $death11) { if (self.classname == "monster_golem_bronze" || self.classname == "monster_golem_iron") sound(self, CHAN_BODY, "golem/mtlpain.wav", 1, ATTN_NORM); else sound(self, CHAN_BODY, "golem/stnpain.wav", 1, ATTN_NORM); self.cnt = time + 1; GolemDeathPause(); } } gamecode/hc/h2/head.hc000066400000000000000000000030671444734033100147530ustar00rootroot00000000000000void(string gibname, float dm) ThrowGib; vector(float dm) VelocityForDamage; void CorpseThink (void); void () HeadThink = { if ((self.lifetime time) return 0; dist = vhlen(self.enemy.origin - self.origin); traceline(self.origin, self.enemy.origin, FALSE, self); c1 = fov(self, self.enemy, 80); if ((dist < 256) && c1 && trace_ent == self.enemy) { if (random() < 0.2) return 1; } return 0; } void hydra_blind(void) { stuffcmd (self.enemy, "df\n"); self.hydra_chargeTime = time + 1; self.think = hydra_charge; thinktime self : 0.05; } void hydra_checkForBlind(void) { float r; r = pointcontents(self.enemy.origin); if (r != CONTENT_WATER) { self.think = self.th_run; thinktime self : 0.1; return; } r = vhlen(self.enemy.origin - self.origin); if ((r < 256) && (fov(self, self.enemy, 80)) && (fov(self.enemy, self, 80))) { thinktime self : 0.1; self.think = hydra_blind; } else { self.think = self.th_run; thinktime self : 0.1; } } void hydra_swim(float thrust) { float dist; float temp; if (self.velocity) self.velocity = self.velocity * 0.7; if (!self.enemy.flags2 & FL_ALIVE) { self.goalentity = self.enemy = world; self.monster_stage = HYDRA_STAGE_WAIT; return; } dist = (4.0 + thrust*6); // Movement distance this turn movetogoal(dist); if (self.hydra_FloatTo == 0) { dist = self.enemy.origin_z - self.origin_z; if (dist < -50) self.hydra_FloatTo = dist - random(60); else if (dist > 50) self.hydra_FloatTo = dist + random(60); } if (self.hydra_FloatTo < -10) { temp = random(-3.5,-2.5); // movestep(0,0,temp, FALSE); self.hydra_FloatTo -= temp; } else if (self.hydra_FloatTo > 10) { temp = random(2.5,3.5); // movestep(0,0,temp, FALSE); self.hydra_FloatTo -= temp; } else self.hydra_FloatTo = 0; self.th_save = self.think; //checkenemy(); enemy_range = range (self.enemy); if (hydra_check_blind_melee()) { self.monster_check = 2; hydra_attack(); } dist = vlen(self.enemy.origin - self.origin); if (dist > 350) { if (CheckMonsterAttack(MA_MISSILE,8.0)) return; } else CheckMonsterAttack(MA_MELEE,3.0); } void hydra_float(void) { float Length; if (self.velocity) self.velocity = self.velocity * 0.7; Length = vhlen(self.origin - self.enemy.origin); if (Length < 300.0) { self.monster_stage = HYDRA_STAGE_SWIM; self.hydra_FloatTo = 0; return; } /*self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin); ChangeYaw();*/ ai_face(); self.th_save = self.think; enemy_range = range (self.enemy); if (hydra_check_blind_melee()) { self.monster_check = 2; hydra_attack(); } CheckMonsterAttack(MA_MISSILE,8.0); } void hydra_reverse(void) { float retval; float dist; self.monster_duration -= 1; dist = 4.0; // Movement distance this turn retval = walkmove(self.angles_y + 180, dist, FALSE); /*if (!retval) { self.ideal_yaw = FindDir(); self.monster_duration = 0;//random(40,70); self.monster_stage = HYDRA_STAGE_STRAIGHT; ChangeYaw();//hydra_turn(200); return; }*/ //self.monster_stage = HYDRA_STAGE_FLOAT; } void hydra_move(float thrust) { check_pos_enemy(); if (self.monster_stage == HYDRA_STAGE_SWIM) { hydra_swim(thrust); return; } else if (self.monster_stage == HYDRA_STAGE_FLOAT) { hydra_float(); return; } else if (self.monster_stage == HYDRA_STAGE_WAIT) { hydra_wait(); return; } else if (self.monster_stage == HYDRA_STAGE_REVERSE) { // hydra_reverse(); return; } } void hydra_attack(void) { if (self.monster_check > 0) { hydra_SpitFrames(); } else { hydra_TentFrames(); } } void hydra_fire(void) { sound (self, CHAN_WEAPON, "hydra/spit.wav", 1, ATTN_NORM); do_spit('0 0 0'); do_spit('0 0 0'); do_spit('0 0 0'); } void hydra_tent(float TryHit) { ai_face(); check_pos_enemy(); if (TryHit) { makevectors(self.angles); traceline(self.origin,self.origin+v_forward*128,FALSE,self); if (trace_ent.takedamage) { sound (self, CHAN_WEAPON, "hydra/tent.wav", 1, ATTN_NORM); T_Damage (trace_ent, self, self, random(1,2)); } else movetogoal(15); } } void hydra_open(void) { ai_face(); } void hydra_bob(void) { local float rnd1, rnd2, rnd3; // local vector vtmp1, modi; rnd1 = self.velocity_x + random(-10,10); rnd2 = self.velocity_y + random(-10,10); rnd3 = self.velocity_z + random(10); if (rnd1 > 10) rnd1 = 10; if (rnd1 < -10) rnd1 = -10; if (rnd2 > 10) rnd2 = 10; if (rnd2 < -10) rnd2 = -10; /* if (rnd3 < 10) rnd3 = 15;*/ if (rnd3 > 55) rnd3 = 50; self.velocity_x = rnd1; self.velocity_y = rnd2; self.velocity_z = rnd3; } // Swimming float hydra_Swim[20] = { -0.2, -0.1, 0.0, 0.1, // Top going down 0.2, 0.3, 0.4, 0.5, // middle 0.4, 0.3, 0.2, 0.1, // bottom 0.0, -0.1, -0.2, -0.3, -0.4, -0.5, // middle -0.4, -0.3 }; // Top = 1, tr = 2, br = 3, tl = 4, bl = 5 float hydra_TentAttacks[24] = { 0, 0, 0, 0, 1, 0, 3, 0, 4, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 4, 3, 1, 0, 0 }; // Attacking others void hydra_AttackDieFrames(void) { self.think = hydra_AttackDieFrames; thinktime self : HX_FRAME_TIME; if (self.frame != $hadie36) AdvanceFrame($hadie1,$hadie36); if (self.frame == $hadie35) { self.solid = SOLID_NOT; MakeSolidCorpse(); } if (self.frame >= $hadie13) hydra_bob(); if(self.health<-30) chunk_death(); } void hydra_AttackPainFrames(void) { self.think = hydra_AttackPainFrames; thinktime self : HX_FRAME_TIME; AdvanceFrame($hapan1,$hapan10); } // Swimming others void hydra_SwimDieFrames(void) { self.think = hydra_SwimDieFrames; thinktime self : HX_FRAME_TIME; if (self.frame != $hsdie36) AdvanceFrame($hsdie1,$hsdie36); if (self.frame == $hsdie35) { self.solid = SOLID_NOT; MakeSolidCorpse(); } if (self.frame >= $hsdie8) hydra_bob(); if(self.health<-30) chunk_death(); } void hydra_SwimPainFrames(void) { self.think = hydra_SwimPainFrames; thinktime self : HX_FRAME_TIME; AdvanceFrame($hspan1,$hspan10); } // Attacking void hydra_OpenFrames(void) { self.think = hydra_OpenFrames; thinktime self : HX_FRAME_TIME; if (self.enemy.watertype != CONTENT_WATER) { self.monster_stage = HYDRA_STAGE_FLOAT; self.think = self.th_run; thinktime self : 0.1; return; } hydra_open(); if (AdvanceFrame($hopen1,$hopen8) == AF_END) { hydra_attack(); } } void hydra_CloseFrames(void) { self.think = hydra_CloseFrames; thinktime self : HX_FRAME_TIME; if (RewindFrame($hopen8,$hopen1) == AF_END) { self.monster_stage -= HYDRA_STAGE_ATTACK; self.think = self.th_save; } } void hydra_SpitFrames(void) { vector vecA,vecB; self.think = hydra_SpitFrames; thinktime self : HX_FRAME_TIME; self.angles_y = vectoyaw(self.enemy.origin - self.origin); if (AdvanceFrame($hspit1,$hspit12) == AF_END) { self.think = hydra_CloseFrames; self.monster_check = -1; } else if (self.frame == $hspit7 && self.monster_check == 2) { sound (self, CHAN_WEAPON, "hydra/spit.wav", 1, ATTN_NORM); vecA = self.enemy.origin - self.origin + self.enemy.proj_ofs; vecA = vectoangles(vecA); makevectors(vecA); v_forward_z = 0 - v_forward_z; vecA = v_factor('-40 400 -40'); vecB = v_factor('40 500 40'); particle2(self.origin+v_forward*40,vecA,vecB,256,12,400); self.think = hydra_checkForBlind; thinktime self : 0.1; } else if (self.frame == $hspit8 && self.monster_check == 1) { hydra_fire(); } } void hydra_TentFrames(void) { float r; if (!self.enemy.flags2 & FL_ALIVE) { self.goalentity = self.enemy = world; self.monster_stage = HYDRA_STAGE_WAIT; self.think = self.th_stand; thinktime self : 0.1; return; } self.think = hydra_TentFrames; thinktime self : 0.025; if (AdvanceFrame($htent1,$htent24) == AF_END) { r = range(self.enemy); if ((r == RANGE_MELEE) && (random(0, 1) < 0.3)) { self.frame = $htent1; hydra_tent(hydra_TentAttacks[self.frame - $htent1]); } else self.think = hydra_CloseFrames; } else { hydra_tent(hydra_TentAttacks[self.frame - $htent1]); } } // Regular swimming / movement void hydra_SwimFrames(void) { self.think = hydra_SwimFrames; thinktime self : HX_FRAME_TIME; AdvanceFrame($hswim1,$hswim20); if (self.frame == $hswim1) sound (self, CHAN_WEAPON, "hydra/swim.wav", 1, ATTN_NORM); hydra_move((hydra_Swim[self.frame - $hswim1]) + 0.3); } /*QUAKED misc_fountain (0 1 0.8) (0 0 0) (32 32 32) New item for QuakeEd -------------------------FIELDS------------------------- angles 0 0 0 the direction it should move the particles movedir 1 1 1 the force it should move them color 256 the color cnt 2 the number of particles each time -------------------------------------------------------- */ void misc_fountain(void) { starteffect(CE_FOUNTAIN, self.origin, self.angles,self.movedir,self.color,self.cnt); } void do_hydra_spit(void) { self.monster_check = 1; self.monster_stage += HYDRA_STAGE_ATTACK; sound (self, CHAN_WEAPON, "hydra/open.wav", 1, ATTN_NORM); hydra_OpenFrames(); } void do_hydra_tent(void) { self.monster_check = 0; self.monster_stage += HYDRA_STAGE_ATTACK; sound (self, CHAN_WEAPON, "hydra/open.wav", 1, ATTN_NORM); hydra_OpenFrames(); } void do_hydra_die(void) { self.flags (+) FL_SWIM; sound (self, CHAN_WEAPON, "hydra/die.wav", 1, ATTN_NORM); if (self.monster_stage >= HYDRA_STAGE_ATTACK) hydra_AttackDieFrames(); else hydra_SwimDieFrames(); } void hydra_retreat() { self.monster_stage = HYDRA_STAGE_REVERSE; self.think = self.th_run; thinktime self : 0.1; } void hydra_pain(entity attacker, float damage) { sound (self, CHAN_WEAPON, "hydra/pain.wav", 1, ATTN_NORM); } void init_hydra(void) { if (deathmatch) { remove(self); return; } self.monster_stage = HYDRA_STAGE_WAIT; precache_model ("models/hydra.mdl"); precache_model ("models/spit.mdl"); self.solid = SOLID_SLIDEBOX; self.movetype = MOVETYPE_SWIM; self.thingtype=THINGTYPE_FLESH; self.classname="monster_hydra"; self.mass = 7; setmodel (self, "models/hydra.mdl"); self.skin = 0; setsize (self, '-30 -30 -24', '30 30 24'); self.hull = HULL_SCORPION; //self.hull = HULL_HYDRA; self.health = 125; self.experience_value = 50; self.mintel = 4; self.th_stand = hydra_SwimFrames; self.th_walk = hydra_SwimFrames; self.th_run = hydra_SwimFrames; self.th_pain = hydra_pain; self.th_die = do_hydra_die; self.th_missile = do_hydra_spit; self.th_melee = do_hydra_tent; self.takedamage = DAMAGE_YES; self.flags2 (+) FL_ALIVE; self.ideal_yaw = self.angles * '0 1 0'; if (!self.yaw_speed) self.yaw_speed = 5; self.view_ofs = '0 0 0'; self.use = monster_use; self.flags (+) FL_SWIM | FL_MONSTER; self.pausetime = 99999999; total_monsters += 1; thinktime self : random(0.5); self.think = self.th_stand; } /*QUAKED monster_hydra (1 0.3 0) (-40 -40 -42) (40 40 42) STAND HOVER JUMP PLAY_DEAD DORMANT New item for QuakeEd -------------------------FIELDS------------------------- NOTE: Normal QuakEd monster spawnflags don't apply here (no_jump, play_dead, no_drop) -------------------------------------------------------- */ void monster_hydra(void) { init_hydra(); precache_sound("hydra/pain.wav"); precache_sound("hydra/die.wav"); precache_sound("hydra/open.wav"); precache_sound("hydra/turn-s.wav"); precache_sound("hydra/turn-b.wav"); precache_sound("hydra/swim.wav"); precache_sound("hydra/tent.wav"); precache_sound("hydra/spit.wav"); } gamecode/hc/h2/icemace.hc000066400000000000000000000377551444734033100154530ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\weapons\icestaff\final\icestaff.hc ============================================================================== */ // For building the model $cd Q:\art\models\weapons\icestaff\final $origin 10 -10 10 $base BASE skin $skin skin $skin skin2 $flags 0 // $frame fire1a fire1b fire1c $frame fire2a fire2b fire2c $frame fire3a fire3b fire3c $frame fire4a fire4b fire4c $frame fire5a fire5b fire5c // $frame idle1 idle2 idle3 idle4 idle5 $frame idle6 idle7 idle8 idle9 idle10 $frame idle11 idle12 idle13 idle14 idle15 $frame idle16 // $frame power1 power2 power3 power4 power5 $frame power6 power7 power8 power9 // $frame select1 select2 select3 select4 select5 $frame select6 select7 select8 select9 select10 void() IceCubeThink = { if(self.freeze_time144) { self.colormap-=1; self.abslight-=0.05; } else { self.colormap=0; self.abslight=0.5; self.skin=GLOBAL_SKIN_ICE; } if(random()<0.2&&random()<0.2) sound(self,CHAN_AUTO,"misc/drip.wav",1,ATTN_NORM); self.think=IceCubeThink; thinktime self : 0.1; }; void (entity loser,entity forwhom) SnowJob= { //FIXME: Make gradual- person slows down then stops entity oself; if(loser.solid==SOLID_BSP) return; if(coop) if(loser.classname=="player"&&forwhom.classname=="player") return; if(teamplay) if(loser.team==forwhom.team) return; if(loser.flags&FL_MONSTER&&loser.monsterclass>=CLASS_BOSS) { T_Damage(loser,self,forwhom,10); return; } sound(loser,CHAN_BODY,"crusader/frozen.wav",1,ATTN_NORM); loser.frozen=50; loser.oldskin=loser.skin; if(loser.classname!="player") { loser.colormap=159; loser.thingtype=THINGTYPE_ICE; loser.freeze_time=time+5; if(loser.scale==0) loser.scale = 1; loser.lifetime=loser.scale; loser.o_angle=loser.mins; loser.v_angle=loser.maxs; loser.enemy=forwhom; loser.oldthink=loser.think; loser.think=IceCubeThink; thinktime loser : 0; loser.touch=SUB_Null; loser.th_pain=SUB_Null; loser.wait = time + 3; if(loser.angles_x==0&&loser.angles_z==0) loser.drawflags(+)SCALE_ORIGIN_BOTTOM; loser.oldmovetype=loser.movetype; loser.movetype=MOVETYPE_TOSS; loser.health=1; loser.deathtype="ice melt"; loser.th_die=shatter; AwardExperience(forwhom,loser,loser.experience_value); loser.experience_value=0; oself=self; self=loser; SUB_UseTargets(); self=oself; } else { loser.artifact_active(+)ARTFLAG_FROZEN; loser.colormap=159; loser.thingtype=THINGTYPE_ICE; loser.o_angle=loser.v_angle; loser.pausetime = time + 10; loser.attack_finished = time + 10; //Temp -turns screen blue loser.health=1; thinktime loser : 10; //Prevent interruption? loser.th_pain=SUB_Null; } loser.flags(-)FL_FLY; loser.flags(-)FL_SWIM; if(loser.flags&FL_ONGROUND) loser.last_onground=time; loser.flags(-)FL_ONGROUND; //need to be able to reverse this... loser.oldtouch=loser.touch; loser.touch=obj_push; loser.drawflags(+)DRF_TRANSLUCENT|MLS_ABSLIGHT; loser.abslight=1; }; void FreezeDie() { particleexplosion(self.origin,14,10,10); particle2(self.origin,'-10 -10 -10','10 10 10',145,14,5); if(self.movechain!=world) remove(self.movechain); remove(self); } void remove_artflag () { if(self.enemy.frozen<=0) self.enemy.artifact_active(-)self.artifact_active; remove(self); } void() FreezeTouch= { if(other==self.owner) return; starteffect(CE_ICE_HIT,self.origin-self.movedir*8); if(other.takedamage&&other.health&&other.frozen<=0&&other.thingtype==THINGTYPE_FLESH)//FIXME: Thingtype_flesh only { sound (self, CHAN_BODY, "crusader/icehit.wav", 1, ATTN_NORM); if(other.frozen<=0) { if(other.freeze_time<=time) other.frozen=0; other.freeze_time=time+2.5; other.frozen-=1; if(other.classname=="player") { other.artifact_active(+)ARTFLAG_FROZEN; newmis=spawn(); newmis.enemy=other; newmis.artifact_active=ARTFLAG_FROZEN; newmis.think=remove_artflag; thinktime newmis : 0.1; } } if(other.flags&FL_COLDHEAL)//Had to take out cold heal, so cold resist T_Damage(other,self,self.owner,3); else if((other.health<=10||(other.classname=="player"&&other.frozen<=-5&&other.health<200))&&other.solid!=SOLID_BSP&&!other.artifact_active&ART_INVINCIBILITY&&other.thingtype==THINGTYPE_FLESH&&other.health<100) SnowJob(other,self.owner); else T_Damage(other,self,self.owner,10); self.think=FreezeDie; thinktime self : 0; } else if(other.frozen<=0) { sound (self, CHAN_BODY, "crusader/icewall.wav", 1, ATTN_NORM); T_RadiusDamage(self,self.owner,30,self.owner); self.touch=SUB_Null; shatter(); } else sound(self,CHAN_BODY,"misc/tink.wav",1,ATTN_NORM); }; void()FireFreeze= { //vector dir; self.bluemana-=1; self.attack_finished=time + 0.05; self.punchangle_x= -1; makevectors (self.v_angle); sound (self, CHAN_WEAPON, "crusader/icefire.wav", 1, ATTN_NORM); newmis = spawn (); newmis.owner = self; newmis.movetype = MOVETYPE_BOUNCEMISSILE; newmis.solid = SOLID_BBOX; newmis.th_die=shatter; newmis.deathtype="ice shatter"; newmis.touch = FreezeTouch; newmis.classname = "snowball"; newmis.speed = 1200; newmis.movedir=normalize(v_forward); newmis.velocity = newmis.movedir * newmis.speed; newmis.angles = vectoangles(newmis.velocity); newmis.avelocity=randomv('-600 -600 -600','600 600 600'); setmodel (newmis, "models/iceshot1.mdl"); newmis.drawflags=MLS_ABSLIGHT; newmis.abslight=0.5; setsize (newmis, '0 0 0', '0 0 0'); setorigin (newmis, self.origin+self.proj_ofs + v_forward*8); entity corona; corona=spawn(); newmis.movechain=corona; corona.movetype=MOVETYPE_NOCLIP; corona.avelocity=randomv('-600 -600 -600','600 600 600'); corona.drawflags=DRF_TRANSLUCENT|MLS_ABSLIGHT; corona.abslight=0.5; corona.scale=2; corona.think=SUB_Remove; thinktime corona : 2; setmodel(corona,"models/iceshot2.mdl"); setsize(corona,'0 0 0','0 0 0'); setorigin(corona,newmis.origin); }; void shard_hit (void) { if(other.classname=="blizzard shard") return; if(other.takedamage&&other.health&&other!=self.owner) T_Damage(other,self,self.owner,50*self.scale); sound(self,CHAN_AUTO,"crusader/icewall.wav",0.1,ATTN_NORM); particleexplosion(self.origin,14,20,5); // particle2(self.origin,'-10 -10 -10','10 10 10',145,14,5); remove(self); } void FireShard (void) { local vector org,dir; newmis=spawn(); newmis.movetype=MOVETYPE_BOUNCE; newmis.solid=SOLID_TRIGGER; newmis.owner=self.owner; dir_x=random(50,100); dir_y=random(50,100); dir_z=random(-250,-180); org_x= random(-84,-8); org_y= random(-84,-8); if(org_x<64) org_z=64+org_x; else if(org_y<64) org_z=64+org_y; org_x+=self.origin_x; org_y+=self.origin_y; org_z+=self.origin_z+64; traceline(self.origin,org,TRUE,self); org=trace_endpos; newmis.velocity=dir; newmis.angles=vectoangles(newmis.velocity)+'90 0 0'; newmis.scale=random(0.05,0.55); newmis.skin=0; newmis.frame=0; newmis.touch=shard_hit; setmodel(newmis,"models/shard.mdl"); setsize(newmis,'0 0 0','0 0 0'); setorigin(newmis,org); } void() blizzard_think= { entity loser; vector dir, top, bottom, beam_angle; float beam_count; if(self.lifetime=10) icestaff_blizzard(); else if(!self.artifact_active&ART_TOMEOFPOWER&&self.bluemana>=1) FireFreeze(); else icestaff_idle(); } void icestaff_f2 (void) { self.th_weapon=icestaff_f2; self.wfs = advanceweaponframe($fire2a,$fire2c); if (self.wfs==WF_CYCLE_WRAPPED) if(!self.button0) icestaff_idle(); else icestaff_shard(); else if (self.attack_finished<=time&&self.weaponframe==$fire2a) if(self.artifact_active&ART_TOMEOFPOWER&&self.bluemana>=10) icestaff_blizzard(); else if(!self.artifact_active&ART_TOMEOFPOWER&&self.bluemana>=1) FireFreeze(); else icestaff_idle(); } void icestaff_f3 (void) { self.th_weapon=icestaff_f3; self.wfs = advanceweaponframe($fire3a,$fire3c); if (self.wfs==WF_CYCLE_WRAPPED) if(!self.button0) icestaff_idle(); else icestaff_shard(); else if (self.attack_finished<=time&&self.weaponframe==$fire3a) if(self.artifact_active&ART_TOMEOFPOWER&&self.bluemana>=10) icestaff_blizzard(); else if(!self.artifact_active&ART_TOMEOFPOWER&&self.bluemana>=1) FireFreeze(); else icestaff_idle(); } void icestaff_f4 (void) { self.th_weapon=icestaff_f4; self.wfs = advanceweaponframe($fire4a,$fire4c); if (self.wfs==WF_CYCLE_WRAPPED) if(!self.button0) icestaff_idle(); else icestaff_shard(); else if (self.attack_finished<=time&&self.weaponframe==$fire4a) if(self.artifact_active&ART_TOMEOFPOWER&&self.bluemana>=10) icestaff_blizzard(); else if(!self.artifact_active&ART_TOMEOFPOWER&&self.bluemana>=1) FireFreeze(); else icestaff_idle(); } void icestaff_f5 (void) { self.th_weapon=icestaff_f5; self.wfs = advanceweaponframe($fire5a,$fire5c); if (self.wfs==WF_CYCLE_WRAPPED) if(!self.button0) icestaff_idle(); else icestaff_shard(); else if (self.attack_finished<=time&&self.weaponframe==$fire5a) if(self.artifact_active&ART_TOMEOFPOWER&&self.bluemana>=10) icestaff_blizzard(); else if(!self.artifact_active&ART_TOMEOFPOWER&&self.bluemana>=1) FireFreeze(); else icestaff_idle(); } void icestaff_shard (void) { float r; r=rint(random(4)) + 1; if(r==1) icestaff_f1(); else if(r==2) icestaff_f2(); else if(r==3) icestaff_f3(); else if(r==4) icestaff_f4(); else icestaff_f5(); } void Cru_Ice_Fire (void) { if(self.artifact_active&ART_TOMEOFPOWER) icestaff_blizzard(); else icestaff_shard(); } gamecode/hc/h2/imp.hc000066400000000000000000001076771444734033100146530ustar00rootroot00000000000000/* ============================================================================== IMP MG&RJ ============================================================================== */ // For building the model $cd c:\model\imp // Directory to find model in $origin 0 0 0 // baseframe is in iceimp.3ds $base iceimp $flags 0 // skin is in iceimp.lbm $skin skin $skin skinice $frame death1 death2 death3 death4 death5 $frame death6 death7 death8 death9 death10 $frame death11 death12 death13 death14 // $frame impfir1 impfir2 impfir3 impfir4 impfir5 $frame impfir6 impfir7 impfir8 impfir9 impfir10 $frame impfir11 impfir12 impfir13 impfir14 impfir15 $frame impfir16 impfir17 impfir18 impfir19 impfir20 $frame impfir21 // $frame impfly1 impfly2 impfly3 impfly4 impfly5 $frame impfly6 impfly7 impfly8 impfly9 impfly10 $frame impfly11 impfly12 impfly13 impfly14 impfly15 $frame impfly16 impfly17 impfly18 impfly19 impfly20 // $frame impup1 impup2 impup3 impup4 impup5 $frame impup6 impup7 impup8 impup9 impup10 $frame impup11 impup12 impup13 impup14 impup15 $frame impup16 impup17 impup18 impup19 impup20 $frame impup21 impup22 impup23 // $frame impwat1 impwat2 impwat3 impwat4 impwat5 $frame impwat6 impwat7 impwat8 impwat9 impwat10 $frame impwat11 impwat12 impwat13 impwat14 impwat15 $frame impwat16 impwat17 impwat18 impwat19 impwat20 $frame impwat21 impwat22 impwat23 impwat24 // $frame swoop1 swoop2 swoop3 swoop4 swoop5 $frame swoop6 swoop7 swoop8 swoop9 swoop10 $frame swoop11 swoop12 swoop13 swoop14 swoop15 $frame swoop16 swoop17 swoop18 swoop19 swoop20 // $frame swpcyc1 swpcyc2 swpcyc3 swpcyc4 // $frame swpend1 swpend2 swpend3 swpend4 swpend5 $frame swpend6 swpend7 swpend8 swpend9 swpend10 $frame swpend11 swpend12 swpend13 swpend14 swpend15 // $frame swpout1 swpout2 swpout3 swpout4 swpout5 $frame swpout6 swpout7 swpout8 swpout9 swpout10 $frame swpout11 swpout12 swpout13 swpout14 swpout15 //============================================================================ //============================================================ float MONSTER_STAND_GROUND = 1; float MONSTER_HOVER = 2; float SF_IMP_DORMANT = 16; float PICKUP = 32; void()imp_fly; void()imp_hover; void()imp_abort_swoop; void()imp_touch; float()imp_find_target; float imp_up_amounts[23] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 6, 7, 5, 2 }; float imp_fly_amounts[20] = { -0.4, -0.2, 0, 0, 0.2, 0.4, 0.4, 0.6, 0.6, 0.8, 0.6, 0.4, 0, 0, -0.2, -0.4, -0.8, -0.8, -0.8, -0.4 }; void imp_drop (void) { self.attack_state=AS_STRAIGHT; self.enemy.flags(-)FL_FLY; if(self.classname=="monster_imp_lord") { if(self.enemy.classname!="player") self.enemy.health=10; self.enemy.movetype=MOVETYPE_BOUNCE; self.enemy.mass=10000; self.enemy.velocity_z=-300; } } void summoned_imp_die () [-- $impup23 .. $impup1] { if(self.health<-40) { sound (self, CHAN_BODY, "misc/null.wav", 1, ATTN_NONE); self.flags2(-)FL_ALIVE; /* THOMAS: otherwise Eidolon gets confused. */ chunk_death(); return; } if(self.frame==$impup23) { self.velocity='0 0 0'; self.effects=EF_DIMLIGHT; self.movetype=MOVETYPE_NOCLIP; self.drawflags=SCALE_ORIGIN_CENTER; self.flags=0; self.solid=SOLID_NOT; self.avelocity_y=-200; self.count=0; sound (self, CHAN_VOICE, "imp/diebig.wav", 1, ATTN_NORM); } self.velocity='0 0 0'; self.scale-=0.025; self.avelocity_y-=10; self.count+=1; if(self.count==4) self.count=0; else if(self.frame<$impup23) self.frame+=1; if(self.frame==$impup1) { sound (self, CHAN_BODY, "items/itmspawn.wav", 1, ATTN_NORM); CreateRedFlash(self.origin); self.flags2(-)FL_ALIVE; /* THOMAS: otherwise Eidolon gets confused. */ remove(self); } } void imp_die () { self.touch=SUB_Null; if(self.health<-30) { sound (self, CHAN_BODY, "misc/null.wav", 1, ATTN_NONE); chunk_death(); return; } if(self.frame!=$death14) AdvanceFrame($death1,$death14); if(self.frame==$death1) { self.flags(-)FL_ONGROUND; if(self.skin==3) sound (self, CHAN_BODY, "imp/diebig.wav", 1, ATTN_NORM); else sound (self, CHAN_BODY, "imp/die.wav", 1, ATTN_NORM); makevectors (self.angles); self.movetype = MOVETYPE_BOUNCE; self.velocity_z = 0; self.velocity += v_forward*80; if(self.attack_state==AS_FERRY) imp_drop(); } if(self.frame==$death8) if (self.flags & FL_ONGROUND) { // when he has finally rested on the ground, finish the death sound(self,CHAN_BODY,"player/land.wav",1,ATTN_NORM); self.velocity_x = self.velocity_y = 0.0; } else self.frame==$death7; MonsterCheckContents(); if(self.frame==$death14 &&self.flags&FL_ONGROUND) MakeSolidCorpse(); else if(self.health<-30) chunk_death(); //check to see if health > 0. if so, come back alive //This could happen by magic or by fire for fire imp or cold for ice imp else if(self.health>0) { self.takedamage = DAMAGE_YES; self.flags (+) FL_FLY; self.flags2 (+) FL_ALIVE; self.solid = SOLID_SLIDEBOX; self.movetype = MOVETYPE_FLY; if(self.classname=="monster_imp_lord") { setsize (self, '-32 -32 0', '32 32 56'); self.hull=HULL_SCORPION; } else { setsize (self, '-16 -16 0', '16 16 36'); self.hull=HULL_CROUCH; } self.th_stand (); return; } else { self.think=imp_die; if (self.frame == $death7 || self.frame == $death8 ) // when cycling between these 2 frames during falling, make him flutter more randomly thinktime self : random(0.05,0.217); else thinktime self : HX_FRAME_TIME; } } void imp_die_init () { setsize (self, '-16 -16 0', '16 16 24'); self.hull=HULL_CROUCH; imp_die(); } void imp_up_down() { // Function to make the imp randomly change height vector end_vec,vec1; // dprint("CHECKING UP/DOWN\n"); if (self.velocity_z > -10.0 && self.velocity_z < 10.0) { // If we aren't moving up/down that much, maybe we want to change altitudes self.velocity_z = 0.0; if (random() < 0.3||self.attack_state==AS_SLIDING) { // Should we change altitudes? makevectors (self.angles); if (random() < 0.5) { // Go Down vec1 = self.origin + v_up*-48; traceline (self.origin,vec1,FALSE,self); if(trace_fraction==1) { end_vec=vec1+v_forward*32; traceline (vec1,end_vec,FALSE,self); if (trace_fraction == 1||random()<0.5) { //dprint("Goin down\n"); self.velocity_z = self.speed*-7; } } } else { // Go Up vec1 = self.origin + v_up*88; traceline (self.origin,vec1,FALSE,self); if(trace_fraction==1) { end_vec=vec1+v_forward*32; traceline (vec1,end_vec,FALSE,self); if (trace_fraction == 1||random()<0.5) { //dprint("Goin up\n"); self.velocity_z = self.speed*7; } } } self.flags(-)FL_ONGROUND; } } } void imp_ferry () { float dist; vector org; movetogoal(self.speed); /* if(!check_z_move(self.level)) imp_up_down(); */ self.velocity_z=self.goalentity.origin_z-self.origin_z; if(fabs(self.velocity_z)>self.speed*30) if(self.velocity_z<0) self.velocity_z=self.speed*-30; else self.velocity_z=self.speed*30; self.flags(-)FL_ONGROUND; // dprintf("Boost : %s\n",self.velocity_z); org=self.goalentity.origin; dist=vlen(self.origin-org); if(dist<=self.size_x) { imp_drop(); return; } org=self.enemy.origin; org_z=self.enemy.absmax_z; dist=vlen(self.origin-org); if(dist>200) { imp_drop(); return; } else self.enemy.velocity=normalize(self.origin-org)*dist*5; self.enemy.angles=self.angles; MonsterCheckContents(); } void imp_pick_up (void) { self.attack_state=AS_FERRY; self.goalentity=find(world,targetname,self.target); self.enemy.flags(+)FL_FLY; } void imp_set_speeds () { float anglediff,dist; if(pointcontents(self.origin+self.view_ofs)==CONTENT_WATER) dist=4; else dist=8; //Movement distance this turn anglediff= fabs(self.angles_y - self.ideal_yaw); //How far we're trying to turn if (anglediff > 20) // If it is a big distance to turn, then don't move as far forward dist = dist / 1.5; self.level=imp_fly_amounts[self.frame - $impfly1]; self.speed = dist + self.level*4*self.scale; //tweaks to speed based on anim frame if(!visible(self.enemy)) { if(self.mintel) SetNextWaypoint(); if(self.search_time=77&&enemy_hdist<=77) { // dprint("too close!\n"); return TRUE; } return FALSE; } void imp_move () { float too_close; if((self.skin==3&&self.enemy==self.controller&&vlen(self.enemy.origin-self.origin)<128)||(self.goalentity==world||self.enemy==world)) { self.think=self.th_stand; return; } if(self.attack_state!=AS_FERRY) checkenemy(); self.velocity=self.velocity*(1/1.05); if(imp_check_too_close()) { self.ideal_yaw=self.angles_y; too_close=TRUE; } else ai_face(); imp_set_speeds(); if(self.attack_state==AS_FERRY) { imp_ferry(); return; } /* if(self.goalentity.classname=="waypoint") { dprint("Following waypoints"); if(self.search_time=time) { enemy_vis=visible(self.enemy); if(!enemy_vis||!clear_path(self.enemy,FALSE)) { vector go_dir; float r,minspeed,maxspeed; makevectors(self.angles); minspeed=100*self.scale; maxspeed=300*self.scale; r=random(); if(r<0.25) go_dir=check_axis_move(v_right,minspeed,maxspeed); else if(r<=0.5) go_dir=check_axis_move(v_up,minspeed,maxspeed); else if(r<=0.75) go_dir=check_axis_move((v_right+v_up)*0.5,minspeed,maxspeed); else go_dir=check_axis_move((v_right*-1+v_up)*0.5,minspeed,maxspeed); if(go_dir!='0 0 0') { if(r<0.25) { self.velocity_x=go_dir_x; self.velocity_y=go_dir_y; } else if(r<=0.5) self.velocity_z=go_dir_z; else self.velocity=go_dir; self.flags(-)FL_ONGROUND; } } } if(self.spawnflags&PICKUP) if(random()<0.2) if(self.target!="") if(self.origin_z>self.enemy.absmax_z - 8) if(vlen(self.enemy.origin+self.enemy.proj_ofs-self.origin)<=self.size_x*1.5) imp_pick_up(); MonsterCheckContents(); } float imp_new_action () { float too_close; enemy_vis=visible(self.enemy); too_close=imp_check_too_close(); if((random()<0.7&&self.enemy.flags2&FL_ALIVE)||!enemy_vis||too_close||self.attack_state==AS_FERRY) { if(self.think!=imp_fly&&self.enemy!=world) { self.think=imp_fly; thinktime self : 0; return TRUE; } return FALSE; } else { if(self.think!=imp_hover) { self.think=imp_hover; thinktime self : 0; return TRUE; } return FALSE; } return FALSE; } void imp_strafe_left () [++ $impfly1 .. $impfly20] { vector dir; ai_face(); if(cycle_wrapped) { self.think=imp_fly; thinktime self : 0; return; } // else if(self.frame==$impfly1) // dprint("strafing left\n"); makevectors(self.angles); dir='0 0 0' - (v_right*(self.frame - $impfly1)*30); self.velocity_x=dir_x; self.velocity_y=dir_y; check_pos_enemy(); //FIXME: check for attack? } void imp_strafe_right () [++ $impfly1 .. $impfly20] { vector dir; ai_face(); if(cycle_wrapped) { self.think=imp_fly; thinktime self : 0; return; } // else if(self.frame==$impfly1) // dprint("strafing right\n"); makevectors(self.angles); dir=v_right*(self.frame - $impfly1)*30; self.velocity_x=dir_x; self.velocity_y=dir_y; check_pos_enemy(); //FIXME: check for attack? } void imp_rise () [++ $impfly1 .. $impfly20] { check_pos_enemy(); if(self.frame==$impfly1) { // dprint("shooting up\n"); self.attack_finished=time+1.7; self.velocity_z=600; } else if(self.frame>$impfly14 &&self.attack_finished 0.1) { imp_strafe_left();//Left because you want to go away from it return TRUE; } else if( dot < -0.1) { imp_strafe_right(); return TRUE; } dot = proj_spot_dir * v_up; if ( dot > 0.2) { imp_dive(); return TRUE; } else if( dot < 0) { imp_rise(); return TRUE; } // dprint("no defense dir found\n"); return FALSE; } return FALSE; } void imp_missile () { if(self.classname == "monster_imp_ice"||(self.classname == "monster_imp_lord"&&random()<0.5)) { sound (self, CHAN_WEAPON, "imp/shard.wav", 1, ATTN_NORM); makevectors (self.angles); do_shard('14 8 0'*self.scale,360 + random()*150, '0 0 0'); do_shard('14 8 0'*self.scale,360 + random()*150, (v_forward * ((random() * 40) - 20)) + (v_right * ((random() * 40) - 20)) + (v_up * ((random() * 20) - 10))); do_shard('14 8 0'*self.scale,360 + random()*150, (v_forward * ((random() * 40) - 20)) + (v_right * ((random() * 40) - 20)) + (v_up * ((random() * 20) - 10))); if (random() < 0.5) do_shard('14 8 0'*self.scale,360 + random()*150, (v_forward * ((random() * 40) - 20)) + (v_right * ((random() * 40) - 20)) + (v_up * ((random() * 20) - 10))); if (random() < 0.5) do_shard('14 8 0'*self.scale,360 + random()*150, (v_forward * ((random() * 40) - 20)) + (v_right * ((random() * 40) - 20)) + (v_up * ((random() * 20) - 10))); if (random() < 0.5) do_shard('14 8 0'*self.scale,360 + random()*150, (v_forward * ((random() * 40) - 20)) + (v_right * ((random() * 40) - 20)) + (v_up * ((random() * 20) - 10))); } else { sound (self, CHAN_WEAPON, "imp/fireball.wav", 1, ATTN_NORM); do_fireball('14 8 0'*self.scale); } } void imp_melee () { vector org,destiny; float dist,damg; makevectors(self.angles); org=self.origin+self.proj_ofs+v_forward*16*self.scale; destiny=(self.enemy.absmin+self.enemy.absmax)*0.5; dist=vlen(destiny-org); traceline(org,destiny,FALSE,self); if(dist>48*self.scale||trace_fraction==1) { imp_missile(); return; } if(trace_ent.takedamage) { string hitsound; if(self.skin==3) { if(trace_ent.thingtype==THINGTYPE_FLESH) MeatChunks (trace_endpos,v_right*random(-100,-300)+'0 0 200', 3,trace_ent); hitsound="weapons/slash.wav"; } else hitsound="assassin/chntear.wav"; sound(trace_ent,CHAN_AUTO,hitsound,1,ATTN_NORM); if(self.skin==3) damg=40; else damg=10*self.scale; T_Damage(trace_ent,self,self,damg); } else hitsound="weapons/gauntht2.wav"; SpawnPuff(trace_endpos,v_right*-100,10*self.scale,trace_ent); sound(self,CHAN_AUTO,hitsound,1,ATTN_NORM); } void imp_attack(void) { self.last_attack=time; if (vlen(self.origin-self.enemy.origin) <= 64*self.scale) { self.v_angle=self.angles; imp_melee (); } else imp_missile(); } void imp_attack_anim() [++ $impfir1 .. $impfir21] { check_pos_enemy(); ai_face(); if(imp_check_defense()) return; if (self.frame == $impfir17) imp_attack(); else if (cycle_wrapped) { self.think=imp_fly; if(visible(self.enemy)&&self.enemy.flags2&FL_ALIVE) if(random()<0.2+skill/10) self.think=imp_attack_anim; self.attack_finished=time + 1; thinktime self : 0; } } void imp_abort_swoop () [++ $swpout1 .. $swpout15] { check_pos_enemy(); if(self.velocity_x>10) self.velocity_x/=2; else self.velocity_x=0; if(self.velocity_x>10) self.velocity_y/=2; else self.velocity_y=0; if(self.frame==$swpout15) { self.attack_finished=time + 1; self.think=imp_fly; thinktime self : 0; } } void imp_swoop_end () [++ $swpend1 .. $swpend15] { check_pos_enemy(); self.flags (-) FL_ONGROUND; // swoop him back up and slow down his forward velocity (xy) self.velocity_z += 15; self.velocity_x /= 1.2; self.velocity_y /= 1.2; if (self.frame == $swpend15) { // Finished swooping self.attack_finished=time + 1; self.velocity = '0 0 0'; self.yaw_speed = 8; self.think=imp_hover; thinktime self : 0; } } void imp_swoop_charge () [++ $swpcyc1 .. $swpcyc4] { vector dir,destiny,org; self.last_attack=time; check_pos_enemy(); destiny=self.enemy.origin+self.enemy.proj_ofs; org=(self.absmin+self.absmax)*0.5; enemy_vis=visible(self.enemy); enemy_infront=infront(self.enemy); enemy_range=vlen(destiny - org); if(self.enemy.last_attack>time - 1 &&fov(self,self.enemy,45)) { self.velocity_z+=150; imp_abort_swoop(); } else if(enemy_vis&&enemy_infront&&enemy_range<2000) { dir=normalize(destiny-org); self.velocity=dir*(377+self.count*7); ai_face(); self.count += 1; if (self.flags & FL_ONGROUND || self.count > 30) { // Didn't hit our target, so go back up self.flags (-) FL_ONGROUND; imp_abort_swoop(); } } else imp_abort_swoop(); } void imp_enter_swoop () [++ $swoop1 .. $swoop20] { vector vec,org; check_pos_enemy(); if(self.frame==$swoop1) { self.yaw_speed=15; self.count=140; self.touch = imp_touch; self.velocity = '0 0 0'; if(self.skin==3) sound (self, CHAN_VOICE, "imp/swoopbig.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "imp/swoop.wav", 1, ATTN_NORM); } ai_face(); self.count *= 1.15; if (self.frame >= $swoop12) { // Start to swoop down org=self.origin; org_z=self.absmin_z; vec = normalize(self.enemy.origin - org + self.enemy.proj_ofs); self.velocity = vec*self.count; if (self.frame <= $swoop13 ) { // If we haven't pulled out yet, keep going straight down self.velocity_x = self.velocity_y = 0; } if(self.absmin_z - self.enemy.absmax_z>50&&self.frame==$swoop13) self.frame=$swoop12; } if (self.frame == $swoop12 || self.frame == $swoop13 ) { // Swoop down cycling frames if (self.flags & FL_ONGROUND) { // Ran into something on the way down self.flags (-) FL_ONGROUND; self.frame = $swoop14; self.count = 280; } if (self.origin_z - self.enemy.origin_z < 60) { // Have to flatten out soon self.frame = $swoop14; self.count = 280; } } if(self.frame==$swoop20) { self.count=0; self.think=imp_swoop_charge; thinktime self : 0.05; } } void imp_straight_swoop () [-- $swpout15 .. $swpout1] { check_pos_enemy(); ai_face(); if(imp_check_defense()) return; if(self.frame==$swpout15) { self.yaw_speed=15; self.count=140; self.velocity = '0 0 0'; self.touch = imp_touch; if(self.skin==3) sound (self, CHAN_VOICE, "imp/swoopbig.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "imp/swoop.wav", 1, ATTN_NORM); } if(self.frame==$swpout1) { self.count=0; self.think=imp_swoop_charge; thinktime self : 0.05; } } void() imp_touch = { float damg,damg_plus; vector punch,dir; self.touch=SUB_Null; if((self.frame >= $swpcyc1 && self.frame <= $swpcyc4 )||(self.frame>=$swoop16 &&self.frame<=$swoop20)) { // If we are in swoop attack frames self.flags (-) FL_ONGROUND; sound (self, CHAN_WEAPON, "imp/swoophit.wav", 1, ATTN_NORM); self.think=imp_swoop_end; if (other.takedamage) { // We sucessfully hit something if(self.frame >= $swpcyc1 && self.frame <= $swpcyc4) damg_plus=self.count; else damg_plus=(self.frame-$swoop16)*5; if(self.classname == "monster_imp_lord") { damg = (33 + (damg_plus *5)); T_Damage (other, self, self.controller, damg); if(other.monsterclass400) { makevectors(self.angles); SpawnPuff(self.origin+self.proj_ofs+v_forward*self.absmax_x,'0 0 0',10,self); self.pain_finished=-666; self.think=self.th_pain; } self.velocity=other.velocity;//?? } else if(vlen(self.velocity)>300) sound (self, CHAN_WEAPON, "imp/swoophit.wav", 1, ATTN_NORM); }; float imp_check_attack () { float enemy_hdist,enemy_zdiff,swoop_no_drop; vector destiny,org; if(self.enemy==world) { self.think=self.th_stand; return FALSE; } if(self.enemy==self.controller||self.enemy==self) if(!LocateTarget()) return FALSE; if(self.attack_state==AS_FERRY) return FALSE; enemy_vis=visible(self.enemy); if (!enemy_vis) { if(self.mintel) SetNextWaypoint(); if(self.search_time70+30*self.scale&&(enemy_zdiff>36||swoop_no_drop)) { tracearea(org,org-'0 0 1'*enemy_zdiff,min,max,FALSE,self); if(trace_fraction==1||swoop_no_drop) { if(swoop_no_drop) tracearea(org,destiny,min,max,FALSE,self); else tracearea(org-'0 0 1'*enemy_zdiff,destiny,min,max,FALSE,self); if(trace_ent==self.enemy) { if(swoop_no_drop) self.think=imp_straight_swoop; else self.think=imp_enter_swoop; thinktime self : 0; return TRUE; } } } } if(self.skin==0)//fire imps attack less- try to get close if(random()<0.5) return FALSE; self.think=imp_attack_anim; thinktime self : 0; return TRUE; } void imp_hover () [++ $impfly1 .. $impfly20] { float too_close; if (self.frame == $impfly1) { if(pointcontents(self.origin+self.view_ofs)==CONTENT_WATER) self.noise="hydra/turn-s.wav"; else if(self.skin==3) self.noise="imp/flybig.wav"; else self.noise="imp/fly.wav"; sound (self, CHAN_BODY, self.noise, 1, ATTN_NORM); } if(self.skin==3) if(self.lifetimetime) return; if(self.targetname!=""&&self.skin==2) { self.think=SUB_Null; self.nextthink=-1; return; } if(random()<0.5&&self.pain_finished!=-666&&attacker!=world&&self.touch!=SUB_Null&&self.monster_awake)//FIXME: make more logical return; self.monster_awake=TRUE; self.pain_finished=time+3;//only react to pain once every 3 seconds max if(self.attack_state==AS_FERRY&&random()<0.2) imp_drop(); //FIXME: pain sound if (self.spawnflags & SF_IMP_DORMANT) { self.frame=$impup1; self.think=stone_imp_awaken; } else { if(self.skin==3) sound(self,CHAN_VOICE,"imp/upbig.wav",1,ATTN_NORM); else sound(self,CHAN_VOICE,"imp/up.wav",1,ATTN_NORM); self.think=imp_pain_anim1; } thinktime self : 0; }; void imp_use (void) { self.use=SUB_Null; self.targetname=""; if(activator.classname=="player") self.enemy=self.goalentity=activator; else dprint("ERROR: monster not activated by player!\n"); self.frame=$impup1; self.think=stone_imp_awaken; thinktime self : random(); } float imp_find_target(void) { // Imp in waiting state if (LocateTarget()) { // We found a target if (self.skin==2) { float self_infront, self_vis, enemy_dist, r1, r2; self_infront=infront_of_ent(self,self.enemy); self_vis=visible2ent(self,self.enemy); enemy_dist=vlen(self.origin-self.enemy.origin); r1=random(); r2=random(); if((self_infront&&self_vis&&r1<0.1&&r2<0.5&&enemy_dist<1000)||enemy_dist<=RANGE_MELEE) { self.goalentity = self.enemy; self.think=stone_imp_awaken; thinktime self : 0; return TRUE; } else { self.goalentity=self.enemy=world; return FALSE; } } else { self.goalentity = self.enemy; self.think = self.th_run; thinktime self : 0; return TRUE; } } else if(self.classname=="monster_imp_lord") self.enemy=self.goalentity=self.controller; return FALSE; } void imp_wait() [++ $impwat1 .. $impwat24] { if(self.skin==2) self.frame=$impwat1; else if(!self.flags&FL_ONGROUND) { self.think=imp_hover; thinktime self : 0; } if(random()<0.5) if(imp_find_target()) return; } void imp_fly () [++ $impfly1 .. $impfly20] { if (self.frame == $impfly1) { if(pointcontents(self.origin+self.view_ofs)==CONTENT_WATER) self.noise="hydra/turn-s.wav"; else if(self.skin==3) self.noise="imp/flybig.wav"; else self.noise="imp/fly.wav"; sound (self, CHAN_BODY, self.noise, 1, ATTN_NORM); } if(self.skin==3) if(self.lifetime300) lift=300; trace_ent.velocity_z+=lift; trace_ent.flags(-)FL_ONGROUND; if(self.playerclass==CLASS_ASSASSIN) sound (self, CHAN_BODY,"player/assjmp.wav", 1, ATTN_NORM); else sound (self, CHAN_BODY,"player/paljmp.wav", 1, ATTN_NORM); self.attack_finished=time+1; } } void AddServerFlag(float addflag) { addflag=byte_me(addflag+8); dprintf("Serverflags were: %s\n",serverflags); dprintf("Added flag %s\n",addflag); serverflags(+)addflag; dprintf("Serverflags are now: %s\n",serverflags); } /* ============ ImpulseCommands ============ */ void() ImpulseCommands = { entity search; float total; // string s2; if(!self.impulse) return; if(self.flags2&FL_CHAINED&&self.impulse!=23) return; if (self.impulse == 9&&skill<3) CheatCommand (); else if (self.impulse == 14) Polymorph(self); else if (self.impulse == 99) ClientKill(); else if (self.impulse ==149) dprintf("Serverflags are now: %s\n",serverflags); // else if (self.impulse >149 && self.impulse <157) // AddServerFlag(self.impulse - 149); // else if (self.impulse == 21 ) // To activate torch // UseTorch (); else if (self.impulse == 23 ) // To use inventory item UseInventoryItem (); else if(self.impulse==33) see_coop_view(); else if(self.impulse==32) PanicButton(); /* else if (self.impulse == 27)//Uncomment this for a good time! MakeCamera();*/ else if (self.impulse == 34) { sprint(self,"Puzzle Inventory: "); if (self.puzzle_inv1) { sprint(self,self.puzzle_inv1); sprint(self," "); } if (self.puzzle_inv2) { sprint(self,self.puzzle_inv2); sprint(self," "); } if (self.puzzle_inv3) { sprint(self,self.puzzle_inv3); sprint(self," "); } if (self.puzzle_inv4) { sprint(self,self.puzzle_inv4); sprint(self," "); } if (self.puzzle_inv5) { sprint(self,self.puzzle_inv5); sprint(self," "); } if (self.puzzle_inv6) { sprint(self,self.puzzle_inv6); sprint(self," "); } if (self.puzzle_inv7) { sprint(self,self.puzzle_inv7); sprint(self," "); } if (self.puzzle_inv8) { sprint(self,self.puzzle_inv8); sprint(self," "); } sprint(self,"\n"); } else if (self.impulse==35&&skill<3) { search = nextent(world); total = 0; while(search != world) { if (search.flags & FL_MONSTER) { total += 1; remove(search); } search = nextent(search); } dprintf("Removed %s monsters\n",total); } else if (self.impulse==36&&skill<3) { search = nextent(world); total = 0; while(search != world) { if (search.flags & FL_MONSTER) { total += 1; thinktime search : 99999; } search = nextent(search); } dprintf("Froze %s monsters\n",total); } else if (self.impulse==37&&skill<3) { search = nextent(world); total = 0; while(search != world) { if (search.flags & FL_MONSTER) { total += 1; thinktime search : HX_FRAME_TIME; } search = nextent(search); } dprintf("UnFroze %s monsters\n",total); } /* else if (self.impulse==38) { sprint(self,"Class: "); s2 = ftos(self.playerclass); sprint(self,s2); sprint(self,"\n"); sprint(self," Hit Points: "); s2 = ftos(self.health); sprint(self,s2); s2 = ftos(self.max_health); sprint(self,"/"); sprint(self,s2); sprint(self,"\n"); sprint(self," Strength: "); s2 = ftos(self.strength); sprint(self,s2); sprint(self,"\n"); sprint(self," Intelligence: "); s2 = ftos(self.intelligence); sprint(self,s2); sprint(self,"\n"); sprint(self," Wisdom: "); s2 = ftos(self.wisdom); sprint(self,s2); sprint(self,"\n"); sprint(self," Dexterity: "); s2 = ftos(self.dexterity); sprint(self,s2); sprint(self,"\n"); }*/ else if(self.impulse==25) { if(deathmatch||coop) { self.impulse=0; return; } self.cnt_tome += 1; Use_TomeofPower(); } else if(self.impulse==39&&skill<3) { if(deathmatch||coop) { self.impulse=0; return; } // Toggle flight if (self.movetype != MOVETYPE_FLY) player_fly(); else player_stopfly(); } else if(self.impulse==40&&skill<3) { if(deathmatch||coop) { self.impulse=0; return; } player_level_cheat(); } else if(self.impulse==41&&skill<3) { if(deathmatch||coop) { self.impulse=0; return; } player_experience_cheat(); } else if (self.impulse == 42) { dprintv("Coordinates: %s\n", self.origin); dprintv("Angles: %s\n",self.angles); dprint("Map is "); dprint(mapname); dprint("\n"); } else if(self.impulse==43&&skill<3) player_everything_cheat(); else if(self.impulse==44) DropInventoryItem(); /* else if (self.impulse == 99) { // RJ's test impulse search = nextent(world); total = 0; while(search != world) { if (search.classname == "monster_fish") { total += 1; dprintf("%s. ",total); dprintv("%s\n",search.origin); } search = nextent(search); } }*/ else if (self.impulse >= 100 && self.impulse <= 115) { Inventory_Quick(self.impulse - 99); } else if (self.impulse == 254) { sprint(self,"King of the Hill is "); search=FindExpLeader(); sprint(self,search.netname); sprint(self," (EXP = "); sprint(self,ftos(search.experience)); sprint(self,") \n"); } else if (self.impulse == 255) PrintFrags(); else if (self.impulse>170&&self.impulse<175&&cvar("registered")) { if(self.level<3) { sprint(self,"You must have achieved level 3 or higher to change class!\n"); self.impulse=0; return; } if(self.impulse==171)//Quick Class-change hot-keys { if(self.playerclass==CLASS_PALADIN) { self.impulse=0; return; } self.newclass=CLASS_PALADIN; } else if(self.impulse==172) { if(self.playerclass==CLASS_CRUSADER) { self.impulse=0; return; } self.newclass=CLASS_CRUSADER; } else if(self.impulse==173) { if(self.playerclass==CLASS_NECROMANCER) { self.impulse=0; return; } self.newclass=CLASS_NECROMANCER; } else if(self.impulse==174) { if(self.playerclass==CLASS_ASSASSIN) { self.impulse=0; return; } self.newclass=CLASS_ASSASSIN; } self.effects=self.drawflags=FALSE; self.playerclass=self.newclass;//So it drops exp the right amount drop_level(self,2); newmis=spawn(); newmis.classname="classchangespot"; newmis.angles=self.angles; setorigin(newmis,self.origin); if(!deathmatch&&!coop) parm7=self.newclass;//Just to tell respawn() not to use restart else { self.model=self.init_model; GibPlayer(); self.frags -= 2; // extra penalty } respawn (); } if(self.model=="models/sheep.mdl") { self.impulse=0; return; } if (self.impulse >= 1 && self.impulse <= 4) W_ChangeWeapon (); else if ((self.impulse == 10) && (wp_deselect == 0)) CycleWeaponCommand (); // else if (self.impulse == 11) // ServerflagsCommand (); else if (self.impulse == 12) CycleWeaponReverseCommand (); else if(self.impulse == 13) HeaveHo(); else if (self.impulse == 22 &&!self.flags2 & FL2_CROUCHED) // To crouch { if(self.flags2 & FL2_CROUCH_TOGGLE) self.flags2(-)FL2_CROUCH_TOGGLE; else self.flags2(+)FL2_CROUCH_TOGGLE; // PlayerCrouch(); } self.impulse = 0; }; gamecode/hc/h2/invntory.hc000066400000000000000000000502141444734033100157360ustar00rootroot00000000000000 entity SelectSpawnPoint(void); void teleport_touch (void); void PlayerSpeed_Calc (void); void player_fly(void); void player_stopfly(void); void Use_RingFlight() { if(self.rings&RING_FLIGHT) {//Toggle off, but don't get leftovers! self.ring_flight=self.ring_flight_time=0; self.rings(-)RING_FLIGHT; self.rings_low (-) RING_FLIGHT; player_stopfly(); self.cnt_flight -= 1; } else { self.rings(+)RING_FLIGHT; self.ring_flight = 100; self.ring_flight_time = time + 1; player_fly(); self.rings_low (-) RING_FLIGHT; } } void()monster_imp_lord; void BecomeImp () { float move_cnt; if(other.solid!=SOLID_BSP) return; self.solid=SOLID_NOT; setorigin(self,self.origin+'0 0 42'); setsize(self,'-40 -40 -42','40 40 42'); self.hull=HULL_HYDRA; newmis=spawn(); setorigin(newmis,self.origin); tracearea(self.origin,self.origin+'0 0 1',self.mins,self.maxs,FALSE,newmis); while((trace_fraction<1||trace_allsolid)&&move_cnt<36) { setorigin(newmis,newmis.origin+'0 0 1'); tracearea(newmis.origin,newmis.origin+'0 0 1',self.mins,self.maxs,FALSE,newmis); move_cnt+=1; } if(trace_fraction==1&&!trace_allsolid) { self.touch=SUB_Null; newmis.flags2(+)FL_SUMMONED; newmis.controller=self.owner; newmis.team=self.owner.team; newmis.classname="monster_imp_lord"; newmis.lifetime=time+30; if(self.owner.enemy!=world&&self.owner.enemy.flags2&FL_ALIVE&&visible2ent(self.owner.enemy,self)) { newmis.enemy=newmis.goalentity=self.owner.enemy; newmis.monster_awake=TRUE; } else { newmis.enemy=newmis.goalentity=self.owner; newmis.monster_awake=TRUE; } self.owner.imp_count+=1; newmis.imp_count=self.owner.imp_count; newmis.think=monster_imp_lord; thinktime newmis : 0; sound (newmis, CHAN_AUTO, "weapons/expsmall.wav", 1, ATTN_NORM); sound (newmis, CHAN_VOICE, "imp/upbig.wav", 1, ATTN_NORM); setorigin(self,self.origin-'0 0 42'); BecomeExplosion(CE_FLOOR_EXPLOSION); } else { entity oself; setorigin(self,self.origin-'0 0 42'-self.movedir*8); traceline(self.origin+'0 0 10',self.origin-'0 0 100',TRUE,self); sound (newmis, CHAN_BODY, "items/itmspawn.wav", 1, ATTN_NORM); // play respawn sound newmis.classname="art_summon"; setorigin(newmis,trace_endpos+'0 0 40'); CreateWhiteFlash(newmis.origin); oself=self; oself.think=SUB_Remove; thinktime oself : 0; self=newmis; spawn_artifact(ARTIFACT_SUMMON,FALSE); } } void Use_Summoner () { self.attack_finished=time + 0.1;//So you can't have a ton of them makevectors(self.v_angle); //sound entity missile; missile=spawn(); missile.owner=self; missile.classname="summon"; missile.movetype=MOVETYPE_BOUNCE; missile.solid=SOLID_BBOX; missile.touch=BecomeImp; missile.effects=EF_DIMLIGHT; missile.drawflags=MLS_POWERMODE; missile.movedir=normalize(v_forward); missile.velocity=normalize(v_forward)*300 +v_up*100; missile.avelocity=RandomVector('300 300 300'); setmodel (missile, "models/a_summon.mdl"); setsize(missile,'0 0 0','0 0 0'); setorigin(missile,self.origin+self.proj_ofs+v_forward*16); missile.think=BecomeImp; thinktime missile : 1; self.cnt_summon-=1; } /* teleport_coin_run - The entity "teleportcoin" is created when teleport artifact is used */ void teleport_coin_run (void) { other = self.enemy; teleport_touch(); } /* Use_teleportCoin - generates an entity that acts like a teleporter trigger which places the player in his startspot or in deathmatch at a random start spot */ void () Use_TeleportCoin = { entity teleport_ent; self.flags2(+)FL_TORNATO_SAFE; teleport_ent = spawn(); teleport_ent.goalentity = SelectSpawnPoint (); teleport_ent.classname = "teleportcoin"; teleport_ent.inactive = FALSE; teleport_ent.think = teleport_coin_run; teleport_ent.nextthink = time + .01; teleport_ent.spawnflags =0; self.cnt_teleport -= 1; teleport_ent.enemy = self; }; void wedge_run(void) { if ((self.owner.velocity_x == 0) && (self.owner.velocity_y == 0) && (self.owner.velocity_z == 0)) self.effects(+)EF_NODRAW; // All stop else if (self.effects & EF_NODRAW) self.effects(-)EF_NODRAW; self.angles = vectoangles(self.owner.velocity); self.origin = self.owner.origin; self.think = wedge_run; //self.nextthink = time + HX_FRAME_TIME; self.nextthink = time + .04; // This faster time is because it would lag behind every once in a while if ((self.owner.health<=0) || !(self.owner.artifact_active & ART_HASTE)) remove(self); } /* void launch_hastewedge (void) { local entity tail; tail = spawn (); tail.movetype = MOVETYPE_NOCLIP; tail.solid = SOLID_NOT; tail.classname = "haste_wedge"; setmodel (tail, "models/wedge.mdl"); setsize (tail, '0 0 0', '0 0 0'); tail.drawflags(+)DRF_TRANSLUCENT; tail.owner = self; tail.origin = tail.owner.origin; tail.velocity = tail.owner.velocity; tail.angles = tail.owner.angles; tail.think = wedge_run; tail.nextthink = time + HX_FRAME_TIME; } */ void Use_TomeofPower (void) { if(self.model=="models/sheep.mdl") self.sheep_time=0; else { self.artifact_active (+) ART_TOMEOFPOWER; self.tome_time = time + TOME_TIME; } self.cnt_tome -= 1; } void () Use_Haste = { self.artifact_active (+) ART_HASTE; self.haste_time = time + TOME_TIME; // launch_hastewedge (); self.effects(+)EF_DARKFIELD; PlayerSpeed_Calc(); self.cnt_haste -= 1; }; /* ============ Use_ProximityMine ============ */ void proximity_think () { float okay; thinktime self : 0.1; if(self.lifetime120) newmis.dmg=120; newmis.health=10; newmis.takedamage=DAMAGE_YES; newmis.touch=newmis.th_die=MultiExplode; newmis.angles_x=90; newmis.avelocity_y=100; newmis.skin=1; newmis.drawflags(+)MLS_POWERMODE; setmodel (newmis, "models/glyphwir.mdl"); setsize(newmis,'-3 -3 -3','3 3 3'); newmis.hull=HULL_POINT; setorigin(newmis,self.origin+self.proj_ofs); newmis.lifetime=time+30; newmis.think=proximity_think; thinktime newmis : 0; } /* ============ UseTimebomb ============ */ void TimeBombBoom() { sound(self,CHAN_AUTO,"misc/warning.wav",1,ATTN_NORM); DarkExplosion(); } void TimeBombTouch() { if(!other.takedamage) return; other=self.enemy; T_Damage(other,self,self.owner,50); TimeBombBoom(); } void Use_TimeBomb() { newmis=spawn(); newmis.owner=self; newmis.enemy=world; newmis.classname="timebomb"; newmis.solid=SOLID_BBOX; newmis.dmg=50; newmis.touch=TimeBombTouch; newmis.angles_x=90; newmis.avelocity_y=100; newmis.skin=1; newmis.drawflags(+)DRF_TRANSLUCENT|MLS_ABSLIGHT; newmis.abslight=0.5; setmodel (newmis, "models/glyphwir.mdl"); setsize(newmis,'0 0 0','0 0 0'); setorigin(newmis,self.origin+self.proj_ofs); newmis.think=TimeBombBoom; thinktime newmis : 0.75; } /* ============ UseBlast ============ */ void UseBlast (void) { vector dir,holdpos; entity victim; float v_length,push,percent,points,inertia; victim = findradius( self.origin, BLAST_RADIUS*2); self.safe_time=time+7; while(victim) { if (victim.classname!="hook"&&victim.owner.classname != "circfire" && victim.classname != "cube_of_force"&&victim.monsterclass 3) percent = 3; if (victim.mass>20) inertia = victim.mass/20; else inertia = 1; push = (percent + 1)/inertia; victim.velocity = dir * push; victim.flags(-)FL_ONGROUND; push = ((percent * 100) + 100)/inertia; victim.velocity_z = push; } } else { victim.frags=2; victim.enemy=victim.owner; victim.owner = self; if (victim.classname!="tornato") { victim.velocity = victim.velocity * -1; victim.angles = vectoangles(victim.velocity); } } holdpos = victim.origin; holdpos_z += (victim.maxs_z - victim.mins_z)/2; traceline(self.origin,holdpos,FALSE,self); CreateBlueFlash(trace_endpos); points = percent * BLASTDAMAGE; // Minimum blast damage if (points > 10) points = 10; T_Damage (victim, self, self, points); } } } if (victim.classname=="tornato" && victim.enemy.flags2&FL_ALIVE) victim.enemy.flags2(+)FL_TORNATO_SAFE; if(victim.classname=="swarm") { victim.think=hive_die; thinktime victim : 0; } victim = victim.chain; } self.cnt_blast -= 1; } void UseInvincibility (void) { self.artifact_active (+) ART_INVINCIBILITY; if(deathmatch) self.invincible_time = time + TOME_TIME; else self.invincible_time = time + 10; self.artifact_low (-) ART_INVINCIBILITY; //Temp invincibility effects if(self.playerclass==CLASS_CRUSADER) self.skin = GLOBAL_SKIN_STONE; else if(self.playerclass==CLASS_PALADIN) self.effects(+)EF_BRIGHTLIGHT; else if(self.playerclass==CLASS_ASSASSIN) self.colormap=140; else if(self.playerclass==CLASS_NECROMANCER) self.effects(+)EF_DARKLIGHT; self.cnt_invincibility -= 1; } void UseInvisibility (void) { centerprint(self,"You are Invisible!\n"); self.artifact_active (+) ART_INVISIBILITY; self.invisible_time = time + TOME_TIME; self.artifact_low (-) ART_INVISIBILITY; msg_entity=self; WriteByte(MSG_ONE, SVC_SET_VIEW_FLAGS); WriteByte(MSG_ONE,DRF_TRANSLUCENT); self.effects(+)EF_NODRAW|EF_LIGHT; self.cnt_invisibility -= 1; } void()Use_Polymorph; void()Use_Tripwire; void()Use_Fireball; void BreakChains() { if(!self.flags2&FL_CHAINED) return; self.flags2(-)FL_CHAINED; if(!self.rings&RING_FLIGHT) self.movetype=MOVETYPE_WALK; } void UseInventoryItem (void) { if (self.health <= 0) return; if(self.flags2&FL_CHAINED&&self.inventory!=INV_TELEPORT&&self.inventory!=INV_BLAST) return; // Is it in the inventory if ((self.inventory == INV_TORCH) && (self.cnt_torch)) { if (self.torchtime < (time + 5)) { UseTorch (); self.flags (+) FL_ARTIFACTUSED; } } else if ((self.inventory == INV_TELEPORT) && (self.cnt_teleport)) { Use_TeleportCoin (); BreakChains(); self.flags (+) FL_ARTIFACTUSED; } else if ((self.inventory == INV_HP_BOOST) && (self.cnt_h_boost)) { use_healthboost (); self.flags (+) FL_ARTIFACTUSED; } else if ((self.inventory == INV_SUPER_HP_BOOST) && (self.cnt_sh_boost)) { use_super_healthboost (); self.flags (+) FL_ARTIFACTUSED; } else if ((self.inventory == INV_MANA_BOOST) && (self.cnt_mana_boost)) { UseManaBoost (); self.flags (+) FL_ARTIFACTUSED; } else if ((self.inventory == INV_GLYPH) && (self.cnt_glyph)) { self.cnt_glyph-=1; if(self.playerclass==CLASS_ASSASSIN) Use_Tripwire(); else if(self.playerclass==CLASS_CRUSADER) Use_TimeBomb (); else if(self.playerclass==CLASS_PALADIN) Use_Fireball(); else if(self.playerclass==CLASS_NECROMANCER) Use_Proximity_Mine(); self.flags (+) FL_ARTIFACTUSED; } else if ((self.inventory == INV_HASTE) && (self.cnt_haste)) { if (self.haste_time < (time + 5)) { Use_Haste(); self.flags (+) FL_ARTIFACTUSED; } } else if ((self.inventory == INV_BLAST) && (self.cnt_blast)) { UseBlast(); BreakChains(); self.flags (+) FL_ARTIFACTUSED; } else if ((self.inventory == INV_CUBEOFFORCE) && (self.cnt_cubeofforce)) { if ((!self.artifact_flags & AFL_CUBE_LEFT) || (!self.artifact_flags & AFL_CUBE_RIGHT)) { UseCubeOfForce(); self.flags (+) FL_ARTIFACTUSED; } } else if ((self.inventory == INV_INVINCIBILITY) && (self.cnt_invincibility)) { if (self.invincible_time < (time + 5)) { UseInvincibility(); self.flags (+) FL_ARTIFACTUSED; } } else if ((self.inventory == INV_INVISIBILITY) && (self.cnt_invisibility)) { if (self.invisible_time < (time + 5)) { UseInvisibility(); self.flags (+) FL_ARTIFACTUSED; } } else if ((self.inventory == INV_TOME) && (self.cnt_tome)) { if (self.tome_time < (time + 5)) { Use_TomeofPower(); self.flags (+) FL_ARTIFACTUSED; } } else if ((self.inventory == INV_POLYMORPH) && (self.cnt_polymorph)) { Use_Polymorph(); self.flags (+) FL_ARTIFACTUSED; } else if ((self.inventory == INV_SUMMON) && (self.cnt_summon)) { Use_Summoner(); self.flags (+) FL_ARTIFACTUSED; } else if ((self.inventory == INV_FLIGHT) && (self.cnt_flight)) { Use_RingFlight(); self.flags (+) FL_ARTIFACTUSED; } else { // Can't even try to use flight in non-deathmatch if ((!deathmatch) && (self.inventory == INV_FLIGHT)) time = time; else { sprint(self, STR_DONOTPOSSESS); sprint(self, "\n"); } } } void PanicButton () { if (self.health <= 0) return; if(self.flags2&FL_CHAINED) return; if (self.cnt_torch) { if (self.torchtime < (time + 5)) { UseTorch (); self.flags (+) FL_ARTIFACTUSED; } } if (self.cnt_teleport) { Use_TeleportCoin (); BreakChains(); self.flags (+) FL_ARTIFACTUSED; } if (self.cnt_h_boost) { use_healthboost (); self.flags (+) FL_ARTIFACTUSED; } if (self.cnt_sh_boost) { use_super_healthboost (); self.flags (+) FL_ARTIFACTUSED; } if (self.cnt_mana_boost) { UseManaBoost (); self.flags (+) FL_ARTIFACTUSED; } if (self.cnt_glyph) { self.cnt_glyph=self.cnt_glyph - 1; if(self.playerclass==CLASS_ASSASSIN) Use_Tripwire(); else if(self.playerclass==CLASS_CRUSADER) Use_TimeBomb (); else if(self.playerclass==CLASS_PALADIN) Use_Fireball(); else if(self.playerclass==CLASS_NECROMANCER) Use_Proximity_Mine(); self.flags (+) FL_ARTIFACTUSED; } if (self.cnt_haste) { if (self.haste_time < (time + 5)) { Use_Haste(); self.flags (+) FL_ARTIFACTUSED; } } if (self.cnt_blast) { UseBlast(); BreakChains(); self.flags (+) FL_ARTIFACTUSED; } if (self.cnt_cubeofforce) { if ((!self.artifact_flags & AFL_CUBE_LEFT) || (!self.artifact_flags & AFL_CUBE_RIGHT)) { UseCubeOfForce(); self.flags (+) FL_ARTIFACTUSED; } } if (self.cnt_invincibility) { if (self.invincible_time < (time + 5)) { UseInvincibility(); self.flags (+) FL_ARTIFACTUSED; } } if (self.cnt_invisibility) { if (self.invisible_time < (time + 5)) { UseInvisibility(); self.flags (+) FL_ARTIFACTUSED; } } if (self.cnt_tome) { if (self.tome_time < (time + 5)) { Use_TomeofPower(); self.flags (+) FL_ARTIFACTUSED; } } if (self.cnt_polymorph) { Use_Polymorph(); self.flags (+) FL_ARTIFACTUSED; } if (self.cnt_summon) { Use_Summoner(); self.flags (+) FL_ARTIFACTUSED; } if (self.cnt_flight) { Use_RingFlight(); self.flags (+) FL_ARTIFACTUSED; } } void DropInventoryItem (void) { entity item,holdent; float throwflag; makevectors(self.v_angle); traceline(self.origin + self.proj_ofs,self.origin + self.proj_ofs + v_forward * 60,FALSE,self); if (trace_fraction < 1) { remove(item); centerprint(self,"Not enough room to throw"); return; } item = spawn(); item.flags(+)FL_ITEM; item.solid = SOLID_TRIGGER; item.movetype = MOVETYPE_TOSS; item.owner = self; item.artifact_ignore_owner_time = time + 2; item.artifact_ignore_time = time + 0.1; setsize (item, '-8 -8 -38', '8 8 24'); holdent=self; self = item; throwflag = 0; // Is it in the inventory if ((holdent.inventory == INV_TORCH) && (holdent.cnt_torch)) { spawn_artifact(ARTIFACT_TORCH,NO_RESPAWN); holdent.cnt_torch -=1; throwflag = 1; } else if ((holdent.inventory == INV_HP_BOOST) && (holdent.cnt_h_boost)) { spawn_artifact(ARTIFACT_HP_BOOST,NO_RESPAWN); holdent.cnt_h_boost -=1; throwflag = 1; } else if ((holdent.inventory == INV_SUPER_HP_BOOST) && (holdent.cnt_sh_boost)) { spawn_artifact(ARTIFACT_SUPER_HP_BOOST,NO_RESPAWN); holdent.cnt_sh_boost -=1; throwflag = 1; } else if ((holdent.inventory == INV_MANA_BOOST) && (holdent.cnt_mana_boost)) { spawn_artifact(ARTIFACT_MANA_BOOST,NO_RESPAWN); holdent.cnt_mana_boost -=1; throwflag = 1; } else if ((holdent.inventory == INV_TELEPORT) && (holdent.cnt_teleport)) { spawn_artifact(ARTIFACT_TELEPORT,NO_RESPAWN); holdent.cnt_teleport -=1; throwflag = 1; } else if ((holdent.inventory == INV_TOME) && (holdent.cnt_tome)) { spawn_artifact(ARTIFACT_TOME,NO_RESPAWN); holdent.cnt_tome -=1; throwflag = 1; } else if ((holdent.inventory == INV_SUMMON) && (holdent.cnt_summon)) { spawn_artifact(ARTIFACT_SUMMON,NO_RESPAWN); holdent.cnt_summon -=1; throwflag = 1; } else if ((holdent.inventory == INV_INVISIBILITY) && (holdent.cnt_invisibility)) { spawn_artifact(ARTIFACT_INVISIBILITY,NO_RESPAWN); holdent.cnt_invisibility -=1; throwflag = 1; } else if ((holdent.inventory == INV_GLYPH) && ((holdent.cnt_glyph&&holdent.playerclass!=CLASS_CRUSADER)||holdent.cnt_glyph>=5)) { spawn_artifact(ARTIFACT_GLYPH,NO_RESPAWN); if(holdent.playerclass==CLASS_CRUSADER) holdent.cnt_glyph -=5; else holdent.cnt_glyph -=1; throwflag = 1; } else if ((holdent.inventory == INV_HASTE) && (holdent.cnt_haste)) { spawn_artifact(ARTIFACT_HASTE,NO_RESPAWN); holdent.cnt_haste -=1; throwflag = 1; } else if ((holdent.inventory == INV_BLAST) && (holdent.cnt_blast)) { spawn_artifact(ARTIFACT_BLAST,NO_RESPAWN); holdent.cnt_blast -=1; throwflag = 1; } else if ((holdent.inventory == INV_POLYMORPH) && (holdent.cnt_polymorph)) { spawn_artifact(ARTIFACT_POLYMORPH,NO_RESPAWN); holdent.cnt_polymorph -=1; throwflag = 1; } else if ((holdent.inventory == INV_FLIGHT) && (holdent.cnt_flight)) { spawn_artifact(ARTIFACT_FLIGHT,NO_RESPAWN); holdent.cnt_flight -=1; throwflag = 1; } else if ((holdent.inventory == INV_CUBEOFFORCE) && (holdent.cnt_cubeofforce)) { spawn_artifact(ARTIFACT_CUBEOFFORCE,NO_RESPAWN); holdent.cnt_cubeofforce -=1; throwflag = 1; } else if ((holdent.inventory == INV_INVINCIBILITY) && (holdent.cnt_invincibility)) { spawn_artifact(ARTIFACT_INVINCIBILITY,NO_RESPAWN); holdent.cnt_invincibility -=1; throwflag = 1; } self = holdent; if (throwflag) // Something could be thrown { // Throw it item.velocity = normalize (v_forward); item.velocity = item.velocity * 200; item.velocity_x += random(-20,20); // So they don't land on top if each other if player is item.velocity_y += random(-20,20); // standing in one place and throwing multiple items item.velocity_z = 200; makevectors(self.v_angle); setorigin(item,self.origin + self.proj_ofs + v_up * 10 + v_forward * 40 + v_right * 8); sound(self,CHAN_BODY,"misc/whoosh.wav",1,ATTN_NORM); } else remove(item); } void Inventory_Quick(float which) { float old_inv; old_inv = self.inventory; self.inventory = which; UseInventoryItem(); self.inventory = old_inv; } gamecode/hc/h2/items.hc000066400000000000000000001112641444734033100151720ustar00rootroot00000000000000void() W_SetCurrentAmmo; void() W_SetCurrentWeapon; void() ring_touch; void()puzzle_touch; /* ALL LIGHTS SHOULD BE 0 1 0 IN COLOR ALL OTHER ITEMS SHOULD BE .8 .3 .4 IN COLOR */ void() SUB_regen = { self.model = self.mdl; // restore original model self.solid = SOLID_TRIGGER; // allow it to be touched again sound (self, CHAN_VOICE, "items/itmspawn.wav", 1, ATTN_NORM); // play respawn sound setorigin (self, self.origin); }; void ItemHitFloorWait () { // dprint("Waiting to hit\n"); if(self.flags&FL_ONGROUND||(pointcontents(self.origin-'0 0 38')==CONTENT_SOLID&&self.velocity_z<=0)) { traceline(self.origin,self.origin-'0 0 38',TRUE,self); self.flags(+)FL_ITEM; // make extra wide self.velocity='0 0 0'; self.solid=SOLID_TRIGGER; if(self.touch==puzzle_touch) { setorigin(self,trace_endpos+'0 0 28'); setsize (self, '-8 -8 -28', '8 8 8'); } else { setorigin(self,trace_endpos+'0 0 38'); setsize (self, '-8 -8 -38', '8 8 24'); } self.nextthink=-1; return; } thinktime self : 0.05; } /* ============ PlaceItem plants the object on the floor ============ */ void() PlaceItem = { float oldz; float oldHull; self.mdl = self.model; // so it can be restored on respawn self.flags(+)FL_ITEM; // make extra wide self.solid = SOLID_TRIGGER; self.movetype = MOVETYPE_TOSS; setsize (self, self.mins,self.maxs); self.velocity = '0 0 0'; self.origin_z = self.origin_z + 6; oldz = self.origin_z; if(!self.spawnflags&FLOATING) { oldHull=self.hull; self.hull = HULL_POINT; if(!droptofloor()) { dprint ("Item :"); dprint (self.classname); dprint (" fell out of level at "); dprint (vtos(self.origin)); dprint ("\n"); remove(self); return; } self.hull=oldHull; if(self.touch==puzzle_touch) { setorigin(self,self.origin+'0 0 28'); setsize (self, '-8 -8 -28', '8 8 8'); } else { setorigin(self,self.origin+'0 0 38'); setsize (self, '-8 -8 -38', '8 8 24'); } } else self.movetype = MOVETYPE_NONE; }; /* ============ StartItem Sets the clipping size and plants the object on the floor ============ */ void() StartItem = { if (self.owner) // Spawned by the backpack function { self.movetype = MOVETYPE_PUSHPULL; if(self.touch==puzzle_touch) setsize (self, '-8 -8 -28', '8 8 8'); else setsize (self, '-16 -16 -38', '16 16 24'); if(self.think!=SUB_Remove&&self.owner.classname=="player"&&self.model!="models/bag.mdl") { self.think=SUB_Remove; thinktime self : 30;//Go away after 30 sec if thrown by player & not a backpack } } else { self.nextthink = time + 0.2; // items start after other solids self.think = PlaceItem; } }; /* //========================================================================= //HEALTH BOX //========================================================================= // // T_Heal: add health to an entity, limiting health to max_health // "ignore" will ignore max_health limit // float (entity e, float healamount, float ignore) T_Heal = { if (e.health <= 0) return 0; if ((!ignore) && (e.health >= other.max_health)) return 0; healamount = ceil(healamount); e.health = e.health + healamount; if ((!ignore) && (e.health >= other.max_health)) e.health = other.max_health; if (e.health > 250) e.health = 250; return 1; }; //QUAK-ED item_health (.3 .3 1) (0 0 0) (32 32 32) rotten megahealth //Health box. Normally gives 25 points. Rotten box heals 5-10 points, //megahealth will add 100 health, then rot you down to your maximum health limit, one point per second. //-------------------------FIELDS------------------------- //-------------------------------------------------------- float H_ROTTEN = 1; float H_MEGA = 2; void() health_touch; void() item_megahealth_rot; //item_megahealth - Added by aleggett for use by the item spawner. void item_megahealth() { self.touch = health_touch; //rj setmodel(self, "maps/b_bh100.bsp"); self.noise = "items/r_item2.wav"; self.healamount = 100; self.healtype = 2; setsize (self, '0 0 0', '0 0 0'); self.hull=HULL_POINT; StartItem (); } void() health_touch = { local float amount; local string s; if (other.classname != "player"||other.model=="models/sheep.mdl") return; if (self.healtype == 2) // Megahealth? Ignore max_health... { if (other.health >= 250) return; if (!T_Heal(other, self.healamount, 1)) return; } else { if (!T_Heal(other, self.healamount, 0)) return; } sprint(other, "You receive "); s = ftos(self.healamount); sprint(other, s); sprint(other, " health\n"); // health touch sound sound(other, CHAN_ITEM, self.noise, 1, ATTN_NORM); stuffcmd (other, "bf\n"); self.model = string_null; self.solid = SOLID_NOT; // Megahealth = rot down the player's super health if (self.healtype == 2) { other.items (+) IT_SUPERHEALTH; self.nextthink = time + 5; self.think = item_megahealth_rot; self.owner = other; } else { if (deathmatch != 2) // deathmatch 2 is the silly old rules { if (deathmatch) self.nextthink = time + 20; self.think = SUB_regen; } } activator = other; SUB_UseTargets(); // fire all targets / killtargets }; void() item_megahealth_rot = { other = self.owner; if (other.health > other.max_health) { other.health = other.health - 1; self.nextthink = time + 1; return; } // it is possible for a player to die and respawn between rots, so don't // just blindly subtract the flag off other.items (-) IT_SUPERHEALTH; if (deathmatch == 1) // deathmatch 2 is silly old rules { self.nextthink = time + 20; self.think = SUB_regen; } }; */ /* =============================================================================== WEAPONS =============================================================================== */ float MAX_INV = 25; void max_ammo2 (entity AddTo, entity AddFrom) { // FIXME: I assume the max will be different between classes and levels if (AddTo.cnt_torch + AddFrom.cnt_torch > MAX_INV) AddFrom.cnt_torch = MAX_INV - AddTo.cnt_torch; if (AddTo.cnt_h_boost + AddFrom.cnt_h_boost > MAX_INV) AddFrom.cnt_h_boost = MAX_INV - AddTo.cnt_h_boost; if (AddTo.cnt_sh_boost + AddFrom.cnt_sh_boost > MAX_INV) AddFrom.cnt_sh_boost = MAX_INV - AddTo.cnt_sh_boost; if (AddTo.cnt_mana_boost + AddFrom.cnt_mana_boost > MAX_INV) AddFrom.cnt_mana_boost = MAX_INV - AddTo.cnt_mana_boost; if (AddTo.cnt_teleport + AddFrom.cnt_teleport > MAX_INV) AddFrom.cnt_teleport = MAX_INV - AddTo.cnt_teleport; if (AddTo.cnt_tome + AddFrom.cnt_tome > MAX_INV) AddFrom.cnt_tome = MAX_INV - AddTo.cnt_tome; if (AddTo.cnt_summon + AddFrom.cnt_summon > MAX_INV) AddFrom.cnt_summon = MAX_INV - AddTo.cnt_summon; if (AddTo.cnt_invisibility + AddFrom.cnt_invisibility > MAX_INV) AddFrom.cnt_invisibility = MAX_INV - AddTo.cnt_invisibility; if (AddTo.cnt_glyph + AddFrom.cnt_glyph > MAX_INV) AddFrom.cnt_glyph = MAX_INV - AddTo.cnt_glyph; if (AddTo.cnt_haste + AddFrom.cnt_haste > MAX_INV) AddFrom.cnt_haste = MAX_INV - AddTo.cnt_haste; if (AddTo.cnt_blast + AddFrom.cnt_blast > MAX_INV) AddFrom.cnt_blast = MAX_INV - AddTo.cnt_blast; if (AddTo.cnt_polymorph + AddFrom.cnt_polymorph > MAX_INV) AddFrom.cnt_polymorph = MAX_INV - AddTo.cnt_polymorph; if (AddTo.cnt_flight + AddFrom.cnt_flight > MAX_INV) AddFrom.cnt_flight = MAX_INV - AddTo.cnt_flight; if (AddTo.cnt_cubeofforce + AddFrom.cnt_cubeofforce > MAX_INV) AddFrom.cnt_cubeofforce = MAX_INV - AddTo.cnt_cubeofforce; if (AddTo.cnt_invincibility + AddFrom.cnt_invincibility > MAX_INV) AddFrom.cnt_invincibility = MAX_INV - AddTo.cnt_invincibility; if (AddTo.bluemana + AddFrom.bluemana > AddTo.max_mana) AddFrom.bluemana = AddTo.max_mana - AddTo.bluemana; if (AddTo.greenmana + AddFrom.greenmana > AddTo.max_mana) AddFrom.greenmana = AddTo.max_mana - AddTo.greenmana; } void max_playermana (void) { if (other.bluemana > other.max_mana) other.bluemana = other.max_mana; if (other.greenmana > other.max_mana) other.greenmana = other.max_mana; } float(float w) RankForWeapon = { if (w&IT_WEAPON4) return 1; if (w == IT_WEAPON3) return 2; if (w == IT_WEAPON2) return 3; if (w == IT_WEAPON1) return 4; return 4; }; /* ============= Deathmatch_Weapon Deathmatch weapon change rules for picking up a weapon ============= */ void(float old, float new) NewBestWeapon = { float or, nr; // change self.weapon if desired or = RankForWeapon (self.weapon); nr = RankForWeapon (new); if ( nr < or ) if(new&IT_WEAPON4) self.weapon=IT_WEAPON4; else self.weapon = new; }; void() W_BestWeapon; /* ============= weapon_touch ============= */ void weapon_touch (void) { float new, old; entity stemp; float leave,hadweap; if (!other.flags & FL_CLIENT||other.model=="models/sheep.mdl") return; if (deathmatch == 2 || coop) { if(other.items&self.items) return; leave = 1; } else leave = 0; // this was causing weapon switching to get stuck if several weapons were // picked up too fast: // http://sourceforge.net/projects/uhexen2/forums/forum/425206/topic/5635367 // other.oldweapon = other.weapon; new = self.items; // Give player weapon and mana if (self.classname=="wp_weapon2") { if (other.playerclass == CLASS_PALADIN) self.netname = STR_VORPAL; else if (other.playerclass == CLASS_CRUSADER) self.netname = STR_ICESTAFF; else if (other.playerclass == CLASS_NECROMANCER) self.netname = STR_MAGICMISSILE; else if (other.playerclass == CLASS_ASSASSIN) self.netname = STR_CROSSBOW; other.bluemana += 25; } else if (self.classname=="wp_weapon3") { if (other.playerclass == CLASS_PALADIN) self.netname = STR_AXE; else if (other.playerclass == CLASS_CRUSADER) self.netname = STR_METEORSTAFF; else if (other.playerclass == CLASS_NECROMANCER) self.netname = STR_BONESHARD; else if (other.playerclass == CLASS_ASSASSIN) self.netname = STR_GRENADES; other.greenmana += 25; } else if (self.classname=="wp_weapon4_head") { if (other.playerclass == CLASS_PALADIN) self.netname = STR_PURIFIER1; else if (other.playerclass == CLASS_CRUSADER) self.netname = STR_SUN1; else if (other.playerclass == CLASS_NECROMANCER) self.netname = STR_RAVENSTAFF1; else if (other.playerclass == CLASS_ASSASSIN) self.netname = STR_SET1; other.bluemana += 25; other.greenmana += 25; if (other.items & IT_WEAPON4_2) new (+) IT_WEAPON4; } else if (self.classname=="wp_weapon4_staff") { if (other.playerclass == CLASS_PALADIN) self.netname = STR_PURIFIER2; else if (other.playerclass == CLASS_CRUSADER) self.netname = STR_SUN2; else if (other.playerclass == CLASS_NECROMANCER) self.netname = STR_RAVENSTAFF2; else if (other.playerclass == CLASS_ASSASSIN) self.netname = STR_SET2; other.bluemana += 25; other.greenmana += 25; if (other.items & IT_WEAPON4_1) new (+) IT_WEAPON4; } else objerror ("weapon_touch: unknown classname"); sprint (other, STR_YOUGOTTHE); sprint (other, self.netname); sprint (other, "\n"); sound (other, CHAN_ITEM, "weapons/weappkup.wav", 1, ATTN_NORM); // touch sound stuffcmd (other, "bf\n"); max_playermana (); // Check mana limits // change to the weapon if(other.items&new) hadweap=TRUE; old = other.items; other.items (+) new; stemp = self; self = other; max_playermana(); if(self.attack_finished other.max_health) other.health = other.max_health; self.model = string_null; self.solid = SOLID_NOT; if (deathmatch == 1) self.nextthink = time + RESPAWN_TIME; self.think = SUB_regen; sprint(other, STR_YOUHAVETHE); sprint(other,self.netname); sprint(other,"\n"); activator = other; SUB_UseTargets(); // fire all targets / killtargets } } void spawn_instant_health(void) { self.touch = ihealth_touch; setmodel (self, "models/i_hboost.mdl"); setsize (self, '0 0 0', '0 0 0'); self.hull=HULL_POINT; self.classname = "item_health"; self.netname = STR_INSTANTHEALTH; StartItem (); } /*QUAKED item_health (0 .5 .8) (-8 -8 -45) (8 8 20) FLOATING Player is given 10 health instantly -------------------------FIELDS------------------------- -------------------------------------------------------- */ void item_health (void) { spawn_instant_health(); } void mana_touch(void) { if ((other.classname!="player") || (other.health < 1)||other.model=="models/sheep.mdl") return; if (self.owner == other && self.artifact_ignore_owner_time > time) return; if (self.artifact_ignore_time > time) return; if ((self.classname == "item_mana_green") && (other.greenmana >= other.max_mana)) return; if ((self.classname == "item_mana_blue") && (other.bluemana >= other.max_mana)) return; if ((self.classname == "item_mana_both") && (other.bluemana >= other.max_mana) && (other.greenmana >= other.max_mana)) return; sprint(other, STR_YOUHAVETHE); sprint(other,self.netname); sprint(other,"\n"); sound (other, CHAN_VOICE, "items/itempkup.wav", 1, ATTN_NORM); stuffcmd (other, "bf\n"); if (self.classname == "item_mana_green") other.greenmana += self.count; else if (self.classname == "item_mana_blue") other.bluemana += self.count; else { other.greenmana += self.count; other.bluemana += self.count; } max_playermana(); self.model = string_null; self.solid = SOLID_NOT; if (deathmatch == 1) self.nextthink = time + RESPAWN_TIME; self.think = SUB_regen; activator = other; SUB_UseTargets(); // fire all targets / killtargets } void spawn_item_mana_green(float amount) { setmodel (self, "models/i_gmana.mdl"); self.touch = mana_touch; setsize (self, '0 0 0', '0 0 0'); self.hull=HULL_POINT; self.classname = "item_mana_green"; self.netname = STR_GREENMANA; self.count=amount; StartItem (); } /*QUAKED item_mana_green (0 .5 .8) (-8 -8 -45) (8 8 20) FLOATING BIG Player is given 15 green mana instantly BIG = 30 mana. -------------------------FIELDS------------------------- none -------------------------------------------------------- */ void item_mana_green (void) { if(self.spawnflags&2) { self.drawflags(+)SCALE_ORIGIN_CENTER|MLS_POWERMODE; self.scale=2; spawn_item_mana_green(30); } else spawn_item_mana_green(15); } void spawn_item_mana_blue(float amount) { self.touch = mana_touch; setmodel (self, "models/i_bmana.mdl"); setsize (self, '0 0 0', '0 0 0'); self.hull=HULL_POINT; self.classname = "item_mana_blue"; self.count=amount; self.netname = STR_BLUEMANA; StartItem (); } /*QUAKED item_mana_blue (0 .5 .8) (-8 -8 -45) (8 8 20) FLOATING BIG Player is given 15 blue mana instantly BIG = 30 -------------------------FIELDS------------------------- -------------------------------------------------------- */ void item_mana_blue (void) { if(self.spawnflags&2) { self.drawflags(+)SCALE_ORIGIN_CENTER|MLS_POWERMODE; self.scale=2; spawn_item_mana_blue(30); } else spawn_item_mana_blue(15); } void spawn_item_mana_both(float amount) { self.touch = mana_touch; setmodel (self, "models/i_btmana.mdl"); setsize (self, '0 0 0', '0 0 0'); self.hull=HULL_POINT; self.classname = "item_mana_both"; self.count=amount; self.netname = STR_COMBINEDMANA; StartItem (); } /*QUAKED item_mana_both (0 .5 .8) (-8 -8 -45) (8 8 20) FLOATING BIG Player is given 15 green and 10 blue mana instantly BIG = 30 each -------------------------FIELDS------------------------- -------------------------------------------------------- */ void item_mana_both (void) { if(self.spawnflags&2) { self.drawflags(+)SCALE_ORIGIN_CENTER|MLS_POWERMODE; self.scale=2; spawn_item_mana_both(30); } else spawn_item_mana_both(15); } /* =============================================================================== ARMOR =============================================================================== */ void armor_touch(void) { if((other.classname != "player") || (other.health <= 0)||other.model=="models/sheep.mdl") { return; } if(self.classname == "item_armor_amulet") { other.armor_amulet = 20; } else if(self.classname == "item_armor_bracer") { other.armor_bracer = 20; } else if(self.classname == "item_armor_breastplate") { other.armor_breastplate = 20; } else if(self.classname == "item_armor_helmet") { other.armor_helmet = 20; } self.solid = SOLID_NOT; self.model = string_null; if(deathmatch == 1) { self.nextthink = time + RESPAWN_TIME; } self.think = SUB_regen; sprint(other, STR_YOUHAVETHE); sprint(other, self.netname); sprint(other, "\n"); sound(other, CHAN_ITEM, "items/armrpkup.wav", 1, ATTN_NORM); stuffcmd(other, "bf\n"); activator = other; SUB_UseTargets(); } void spawn_item_armor_helmet(void) { setmodel (self, "models/i_helmet.mdl"); setsize (self, '0 0 0', '0 0 0'); self.hull=HULL_POINT; self.touch = armor_touch; self.netname = STR_ARMORHELMET; StartItem (); } /*QUAKED item_armor_helmet (0 .5 .8) (-8 -8 -45) (8 8 20) FLOATING -------------------------FIELDS------------------------- -------------------------------------------------------- */ void item_armor_helmet (void) { spawn_item_armor_helmet(); } void spawn_item_armor_breastplate (void) { setmodel (self, "models/i_bplate.mdl"); setsize (self, '0 0 0', '0 0 0'); self.hull=HULL_POINT; self.touch = armor_touch; self.netname = STR_ARMORBREASTPLATE; StartItem (); } /*QUAKED item_armor_breastplate (0 .5 .8) (-8 -8 -45) (8 8 20) FLOATING -------------------------FIELDS------------------------- -------------------------------------------------------- */ void item_armor_breastplate (void) { spawn_item_armor_breastplate(); } void spawn_item_armor_bracer(void) { setmodel (self, "models/i_bracer.mdl"); setsize (self, '0 0 0', '0 0 0'); self.hull=HULL_POINT; self.touch = armor_touch; self.netname = STR_ARMORBRACER; StartItem (); } /*QUAKED item_armor_bracer (0 .5 .8) (-8 -8 -45) (8 8 20) FLOATING -------------------------FIELDS------------------------- -------------------------------------------------------- */ void item_armor_bracer (void) { spawn_item_armor_bracer(); } void spawn_item_armor_amulet(void) { setmodel (self, "models/i_amulet.mdl"); setsize (self, '0 0 0', '0 0 0'); self.hull=HULL_POINT; self.touch = armor_touch; self.netname = STR_ARMORAMULET; StartItem (); } /*QUAKED item_armor_amulet (0 .5 .8) (-8 -8 -45) (8 8 20) FLOATING -------------------------FIELDS------------------------- -------------------------------------------------------- */ void item_armor_amulet (void) { spawn_item_armor_amulet(); } /* =============================================================================== PLAYER BACKPACKS =============================================================================== */ void GetPuzzle2(entity item, entity person, string which); void BackpackTouch(void) { string s; float old, new; float ItemCount; if (other.classname != "player"||other.model=="models/sheep.mdl") return; if (other.health <= 0) return; if (self.owner == other && self.artifact_ignore_owner_time > time) return; if (self.artifact_ignore_time > time) return; ItemCount = 0; sprint (other, "You get "); max_ammo2 (other,self); if (self.cnt_torch > 0) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.cnt_torch += self.cnt_torch; s = ftos(self.cnt_torch); sprint(other,s); sprint(other," "); sprint(other,STR_TORCH); if (self.cnt_torch > 1) // Plural sprint(other,"es"); } if (self.cnt_h_boost > 0) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.cnt_h_boost += self.cnt_h_boost; s = ftos(self.cnt_h_boost); sprint(other,s); sprint(other," "); sprint(other,STR_HEALTHBOOST); if (self.cnt_h_boost > 1) // Plural sprint(other,"s"); } if (self.cnt_sh_boost > 0) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.cnt_sh_boost += self.cnt_sh_boost; s = ftos(self.cnt_sh_boost); sprint(other,s); sprint(other," "); sprint(other,STR_SUPERHEALTHBOOST); if (self.cnt_sh_boost > 1) // Plural sprint(other,"s"); } if (self.cnt_mana_boost > 0) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.cnt_mana_boost += self.cnt_mana_boost; s = ftos(self.cnt_mana_boost); sprint(other,s); sprint(other," "); if (self.cnt_mana_boost == 1) sprint(other,STR_MANABOOST); else sprint(other,"Kraters of Might"); } if (self.cnt_teleport > 0) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.cnt_teleport += self.cnt_teleport; s = ftos(self.cnt_teleport); sprint(other,s); sprint(other," "); sprint(other,STR_TELEPORT); if (self.cnt_teleport > 1) // Plural sprint(other,"s"); } if (self.cnt_tome > 0) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.cnt_tome += self.cnt_tome; s = ftos(self.cnt_tome); sprint(other,s); sprint(other," "); if (self.cnt_tome == 1) sprint(other,STR_TOME); else sprint(other,"Tomes of Power"); } if (self.cnt_summon > 0) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.cnt_summon += self.cnt_summon; s = ftos(self.cnt_summon); sprint(other,s); sprint(other," "); if (self.cnt_summon == 1) sprint(other,STR_SUMMON); else sprint(other,"Stones of Summoning"); } if (self.cnt_flight > 0) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.cnt_flight += self.cnt_flight; s = ftos(self.cnt_flight); sprint(other,s); sprint(other," "); if (self.cnt_flight == 1) sprint(other,STR_RINGFLIGHT); else sprint(other,"Rings of Flight"); } if (self.cnt_glyph > 0) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.cnt_glyph += self.cnt_glyph; s = ftos(self.cnt_glyph); sprint(other,s); sprint(other," "); if (self.cnt_glyph == 1) sprint(other,STR_GLYPH); else sprint(other,"Glyphs Of The Ancients"); } if (self.cnt_haste > 0) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.cnt_haste += self.cnt_haste; s = ftos(self.cnt_haste); sprint(other,s); sprint(other," "); sprint(other,STR_HASTE); } if (self.cnt_blast > 0) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.cnt_blast += self.cnt_blast; s = ftos(self.cnt_blast); sprint(other,s); sprint(other," "); if (self.cnt_blast == 1) sprint(other,STR_BLAST); else sprint(other,"Discs of Repulsion"); } if (self.cnt_polymorph > 0) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.cnt_polymorph += self.cnt_polymorph; s = ftos(self.cnt_polymorph); sprint(other,s); sprint(other," "); if (self.cnt_polymorph == 1) sprint(other,STR_POLYMORPH); else sprint(other,"Seals of the Ovinomancer"); } if (self.cnt_invisibility > 0) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.cnt_invisibility += self.cnt_invisibility; s = ftos(self.cnt_invisibility); sprint(other,s); sprint(other," "); sprint(other,STR_INVISIBILITY); if (self.cnt_polymorph > 1) sprint(other,"s"); } if (self.cnt_cubeofforce > 0) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.cnt_cubeofforce += self.cnt_cubeofforce; s = ftos(self.cnt_cubeofforce); sprint(other,s); sprint(other," "); sprint(other,STR_CUBEOFFORCE); if (self.cnt_cubeofforce > 1) sprint(other,"s"); } if (self.cnt_invincibility > 0) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.cnt_invincibility += self.cnt_invincibility; s = ftos(self.cnt_invincibility); sprint(other,s); sprint(other," "); if (self.cnt_invincibility == 1) sprint(other,STR_INVINCIBILITY); else sprint(other,"Icons of the Defender"); } if (self.bluemana > 0) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.bluemana += self.bluemana; s = ftos(self.bluemana); sprint(other,s); sprint(other," "); sprint(other,STR_BLUEMANA); } if (self.greenmana > 0) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.greenmana += self.greenmana; s = ftos(self.greenmana); sprint(other,s); sprint(other," "); sprint(other,STR_GREENMANA); } if (self.armor_amulet) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.armor_amulet = self.armor_amulet; sprint(other,s); sprint(other," "); sprint(other,STR_ARMORAMULET); } if (self.armor_bracer) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.armor_bracer = self.armor_bracer; sprint(other,s); sprint(other," "); sprint(other,STR_ARMORBRACER); } if (self.armor_breastplate) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.armor_breastplate = self.armor_breastplate; sprint(other,s); sprint(other," "); sprint(other,STR_ARMORBREASTPLATE); } if (self.armor_helmet) { if (ItemCount) sprint(other,", "); ItemCount += 1; other.armor_helmet = self.armor_helmet; sprint(other,s); sprint(other," "); sprint(other,STR_ARMORHELMET); } if (!ItemCount) sprint(other,"...Nothing!"); /* if (self.puzzle_inv1) GetPuzzle2(self, other, self.puzzle_inv1); if (self.puzzle_inv2) GetPuzzle2(self, other, self.puzzle_inv2); if (self.puzzle_inv3) GetPuzzle2(self, other, self.puzzle_inv3); if (self.puzzle_inv4) GetPuzzle2(self, other, self.puzzle_inv4); if (self.puzzle_inv5) GetPuzzle2(self, other, self.puzzle_inv5); if (self.puzzle_inv6) GetPuzzle2(self, other, self.puzzle_inv6); if (self.puzzle_inv7) GetPuzzle2(self, other, self.puzzle_inv7); if (self.puzzle_inv8) GetPuzzle2(self, other, self.puzzle_inv8); */ // if the player was using his best weapon, change up to the new one if better new = self.items; if (!new) new = other.weapon; old = other.items; other.items (+) new; // change weapons sprint (other, "\n"); // backpack touch sound sound (other, CHAN_ITEM, "weapons/ammopkup.wav", 1, ATTN_NORM); stuffcmd (other, "bf\n"); // remove the backpack, change self to the player remove(self); self = other; // change to the weapon if (!deathmatch) self.weapon = new; else NewBestWeapon (old, new); W_SetCurrentWeapon (); } void MonsterDropStuff(void) { float chance; if(!self.flags&FL_MONSTER) return; if (self.monsterclass < CLASS_GRUNT) return; // Grunts drop only instant items if (self.monsterclass == CLASS_GRUNT) { if (random() < .15) // %15 chance he'll drop something { chance = random(); if (chance < .25) self.greenmana = 10; else if (chance < .50) self.bluemana = 10; else if (chance < .75) { self.greenmana = 10; self.bluemana = 10; } else { self.spawn_health = 1; } } } // Henchmen drop instant items or lesser artifacts else if (self.monsterclass == CLASS_HENCHMAN) { if (random() < .15) // %15 chance he'll drop something { chance = random(); if (chance < .08) self.greenmana = 10; else if (chance < .16) self.bluemana = 10; else if (chance < .24) { self.greenmana = 10; self.bluemana = 10; } else if (chance < .32) { self.spawn_health = 1; } else if (chance < .40) self.cnt_torch = 1; else if (chance < .48) self.cnt_h_boost = 1; else if (chance < .56) self.cnt_mana_boost = 1; else if (chance < .64) self.cnt_teleport = 1; else if (chance < .72) self.cnt_tome = 1; else if (chance < .80) self.cnt_haste = 1; else if (chance < .90) self.cnt_blast = 1; } } // Leaders drop armor or artifacts else if (self.monsterclass == CLASS_LEADER) { if (random() < .15) // %15 chance he'll drop something { chance = random(); if (chance < .05) self.cnt_torch = 1; else if (chance < .10) self.cnt_h_boost = 1; else if (chance < .15) self.cnt_sh_boost = 1; else if (chance < .20) self.cnt_mana_boost = 1; else if (chance < .25) self.cnt_teleport = 1; else if (chance < .30) self.cnt_tome = 1; else if (chance < .35) self.cnt_summon = 1; else if (chance < .40) self.cnt_invisibility = 1; else if (chance < .45) self.cnt_glyph = 1; else if (chance < .50) self.cnt_haste = 1; else if (chance < .55) self.cnt_blast = 1; else if (chance < .60) self.cnt_polymorph = 1; else if (chance < .65) self.cnt_cubeofforce = 1; else if (chance < .70) self.cnt_invincibility = 1; else if (chance < .75) self.armor_amulet = 20; else if (chance < .80) self.armor_bracer = 20; else if (chance < .85) self.armor_breastplate = 20; else self.armor_helmet = 20; } } DropBackpack(); } /* =============== DropBackpack =============== */ void DropBackpack(void) { entity item,old_self; float total; item = spawn(); if(self.playerclass==CLASS_NECROMANCER) self.cnt_glyph=rint(self.cnt_glyph/5); total = 0; if (self.cnt_torch > 3) total += item.cnt_torch = 3; else total += item.cnt_torch = self.cnt_torch; if (self.cnt_h_boost > 3) total += item.cnt_h_boost = 3; else total += item.cnt_h_boost = self.cnt_h_boost; if (self.cnt_sh_boost > 3) total += item.cnt_sh_boost = 3; else total += item.cnt_sh_boost = self.cnt_sh_boost; if (self.cnt_mana_boost > 3) total += item.cnt_mana_boost = 3; else total += item.cnt_mana_boost = self.cnt_mana_boost; if (self.cnt_teleport > 3) total += item.cnt_teleport = 3; else total += item.cnt_teleport = self.cnt_teleport; if (self.cnt_tome > 3) total += item.cnt_tome = 3; else total += item.cnt_tome = self.cnt_tome; if (self.cnt_summon > 3) total += item.cnt_summon = 3; else total += item.cnt_summon = self.cnt_summon; if (self.cnt_invisibility > 3) total += item.cnt_invisibility = 3; else total += item.cnt_invisibility = self.cnt_invisibility; if (self.cnt_glyph > 3) total += item.cnt_glyph = 3; else total += item.cnt_glyph = self.cnt_glyph; if (self.cnt_haste > 3) total += item.cnt_haste = 3; else total += item.cnt_haste = self.cnt_haste; if (self.cnt_blast > 3) total += item.cnt_blast = 3; else total += item.cnt_blast = self.cnt_blast; if (self.cnt_polymorph > 3) total += item.cnt_polymorph = 3; else total += item.cnt_polymorph = self.cnt_polymorph; if (self.cnt_flight > 3) total += item.cnt_flight = 3; else total += item.cnt_flight = self.cnt_flight; if (self.cnt_cubeofforce > 3) total += item.cnt_cubeofforce = 3; else total += item.cnt_cubeofforce = self.cnt_cubeofforce; if (self.cnt_invincibility > 3) total += item.cnt_invincibility = 3; else total += item.cnt_invincibility = self.cnt_invincibility; // Full armor on this body? if (self.armor_amulet==20) { total += 1; item.armor_amulet = self.armor_amulet; } if (self.armor_bracer==20) { total += 1; item.armor_bracer = self.armor_bracer; } if (self.armor_breastplate==20) { total += 1; item.armor_breastplate = self.armor_breastplate; } if (self.armor_helmet==20) { total += 1; item.armor_helmet = self.armor_helmet; } /* if (self.puzzle_inv1) { item.puzzle_inv1 = self.puzzle_inv1; total = 999; } if (self.puzzle_inv2) { item.puzzle_inv2 = self.puzzle_inv2; total = 999; } if (self.puzzle_inv3) { item.puzzle_inv3 = self.puzzle_inv3; total = 999; } if (self.puzzle_inv4) { item.puzzle_inv4 = self.puzzle_inv4; total = 999; } if (self.puzzle_inv5) { item.puzzle_inv5 = self.puzzle_inv5; total = 999; } if (self.puzzle_inv6) { item.puzzle_inv6 = self.puzzle_inv6; total = 999; } if (self.puzzle_inv7) { item.puzzle_inv7 = self.puzzle_inv7; total = 999; } if (self.puzzle_inv8) { item.puzzle_inv8 = self.puzzle_inv8; total = 999; } */ // Any mana or instant health item.bluemana = self.bluemana; item.greenmana = self.greenmana; item.spawn_health = self.spawn_health; // total = 1; // item.cnt_tome = 1; if (!total && !item.bluemana && !item.greenmana && !item.spawn_health) { // Nothing to put in the backpack remove(item); return; } setorigin(item,self.origin); item.origin = self.origin + '0 0 40'; item.flags(+)FL_ITEM; item.solid = SOLID_TRIGGER; item.movetype = MOVETYPE_TOSS; item.owner = self; item.artifact_ignore_owner_time = time + 2; item.artifact_ignore_time = time + 0.1; if ((total == 1 && !item.bluemana && !item.greenmana && !item.spawn_health) || (total == 0 && item.bluemana && !item.greenmana && !item.spawn_health) || (total == 0 && !item.bluemana && item.greenmana && !item.spawn_health) || (total == 0 && !item.bluemana && !item.greenmana && item.spawn_health)) { // throw out the individual item item.velocity_z = 200; // item.velocity_x = random(-20,20); // item.velocity_y = random(-20,20); old_self = self; self = item; if (item.cnt_torch) { spawn_artifact(ARTIFACT_TORCH,NO_RESPAWN); } else if (item.cnt_h_boost) { spawn_artifact(ARTIFACT_HP_BOOST,NO_RESPAWN); } else if (item.cnt_sh_boost) { spawn_artifact(ARTIFACT_SUPER_HP_BOOST,NO_RESPAWN); } else if (item.cnt_mana_boost) { spawn_artifact(ARTIFACT_MANA_BOOST,NO_RESPAWN); } else if (item.cnt_teleport) { spawn_artifact(ARTIFACT_TELEPORT,NO_RESPAWN); } else if (item.cnt_tome) { spawn_artifact(ARTIFACT_TOME,NO_RESPAWN); } else if (item.cnt_summon) { spawn_artifact (ARTIFACT_SUMMON,NO_RESPAWN); } else if (item.cnt_invisibility) { spawn_artifact (ARTIFACT_INVISIBILITY,NO_RESPAWN); } else if (item.cnt_glyph) { spawn_artifact (ARTIFACT_GLYPH,NO_RESPAWN); } else if (item.cnt_haste) { spawn_artifact (ARTIFACT_HASTE,NO_RESPAWN); } else if (item.cnt_blast) { spawn_artifact(ARTIFACT_BLAST,NO_RESPAWN); } else if (item.cnt_polymorph) { spawn_artifact (ARTIFACT_POLYMORPH,NO_RESPAWN); } else if (item.cnt_flight) { spawn_artifact (ARTIFACT_FLIGHT,NO_RESPAWN); } else if (item.cnt_cubeofforce) { spawn_artifact (ARTIFACT_CUBEOFFORCE,NO_RESPAWN); } else if (item.cnt_invincibility) { spawn_artifact (ARTIFACT_INVINCIBILITY,NO_RESPAWN); } else if ((item.bluemana) && (item.greenmana)) { spawn_item_mana_both(self.bluemana); } else if (item.bluemana) { spawn_item_mana_blue(self.bluemana); } else if (item.greenmana) { spawn_item_mana_green(self.greenmana); } else if (item.spawn_health) { spawn_instant_health(); } else if (item.armor_amulet) { spawn_item_armor_amulet(); } else if (item.armor_bracer) { spawn_item_armor_bracer(); } else if (item.armor_breastplate) { spawn_item_armor_breastplate(); } else if (item.armor_helmet) { spawn_item_armor_helmet(); } else { dprint("Bad backpack!"); remove(item); self = old_self; return; } self = old_self; } else { item.velocity_z = 300; // item.velocity_x = random(-20,20); // item.velocity_y = random(-20,20); setmodel (item, "models/bag.mdl"); setsize (item, '-16 -16 -45', '16 16 10'); item.hull=HULL_POINT; item.touch = BackpackTouch; item.nextthink = time + 120; // remove after 2 minutes item.think = SUB_Remove; if (!total) { remove(item); return; } } self.cnt_torch=0; self.cnt_h_boost=0; self.cnt_sh_boost=0; self.cnt_mana_boost=0; self.cnt_teleport=0; self.cnt_tome=0; self.cnt_summon=0; self.cnt_invisibility=0; self.cnt_glyph=0; self.cnt_haste=0; self.cnt_blast=0; self.cnt_polymorph=0; self.cnt_flight=0; self.cnt_cubeofforce=0; self.cnt_invincibility=0; self.armor_amulet=0; self.armor_bracer=0; self.armor_breastplate = 0; self.armor_helmet = 0; self.bluemana=0; self.greenmana=0; self.spawn_health=0; } gamecode/hc/h2/light.hc000066400000000000000000000304131444734033100151540ustar00rootroot00000000000000/* ========================================================== LIGHT.HC MG Lights can be toggled/faded, shot out, etc. ========================================================== */ float START_LOW = 1; void initialize_lightstyle (void) { if(self.spawnflags&START_LOW) if(self.lightvalue1=self.fadespeed) { //dprint("light timed out\n"); remove(self); } else if((self.cnt<0&&self.light_lev<=self.level)||(self.cnt>0&&self.light_lev>=self.level)) { //dprint("light fade done\n"); lightstylestatic(self.style, self.level); remove(self); } else { self.nextthink=time+0.05; self.think=fadelight; } } void lightstyle_change_think() { //dprint("initializing light change\n"); self.speed=self.lightvalue2 - self.lightvalue1; self.light_lev=lightstylevalue(self.style); if(self.light_lev==self.lightvalue1) self.level = self.lightvalue2; else if(self.light_lev==self.lightvalue2) self.level = self.lightvalue1; else if(self.speed>0) if(self.light_levself.fadespeed) self.nextthink=-1; else self.nextthink=time+0.05; self.think=torch_think; } void torch_use (void) { self.fadespeed=time+other.fadespeed+1; torch_think(); } /*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_LOW Non-displayed fading light. Default light value is 300 Default style is 0 ---------------------------------- If triggered, will toggle between lightvalue1 and lightvalue2 .lightvalue1 (default 0) .lightvalue2 (default 11, equivalent to 300 brightness) Two values the light will fade-toggle between, 0 is black, 25 is brightest, 11 is equivalent to a value of 300. .fadespeed (default 1) = How many seconds it will take to complete the desired lighting change The light will start on at a default of the higher light value unless you turn on the startlow flag. START_LOW = will make the light start at the lower of the lightvalues you specify (default uses brighter) NOTE: IF YOU DON'T PLAN ON USING THE DEFAULTS, ALL LIGHTS IN THE BANK OF LIGHTS NEED THIS INFO */ void light() { if (self.targetname == "") { remove(self); } else { if(!self.lightvalue2) self.lightvalue2=11; if(!self.fadespeed) self.fadespeed = 1; initialize_lightstyle(); } } /*QUAK-ED light_globe (0 1 0) (-8 -8 -8) (8 8 8) START_LOW Sphere globe light. Default light value is 300 Default style is 0 ---------------------------------- If triggered, will toggle between lightvalue1 and lightvalue2 .lightvalue1 (default 0) .lightvalue2 (default 11, equivalent to 300 brightness) Two values the light will fade-toggle between, 0 is black, 25 is brightest, 11 is equivalent to a value of 300. .fadespeed (default 1) = How many seconds it will take to complete the desired lighting change The light will start on at a default of the higher light value unless you turn on the startlow flag. START_LOW = will make the light start at the lower of the lightvalues you specify (default uses brighter) NOTE: IF YOU DON'T PLAN ON USING THE DEFAULTS, ALL LIGHTS IN THE BANK OF LIGHTS NEED THIS INFO */ /* void() light_globe = { precache_model ("models/s_light.spr"); setmodel (self, "models/s_light.spr"); if(self.targetname) self.use=torch_use; self.mdl = "models/null.spr"; self.weaponmodel = "models/s_light.spr"; if(self.style>=32) { if(!self.lightvalue2) self.lightvalue2=11; if(!self.fadespeed) self.fadespeed = 1; initialize_lightstyle(); self.think = torch_think; self.nextthink = time+1; } else { setmodel(self,self.weaponmodel); makestatic (self); } }; */ void() FireAmbient = { //FIXME: remove ambient sound if light is off, start it again if turned back on precache_sound ("raven/flame1.wav"); // attenuate fast ambientsound (self.origin, "raven/flame1.wav", 0.5, ATTN_STATIC); }; /*QUAK-ED light_torch_small_walltorch (0 .5 0) (-10 -10 -20) (10 10 20) START_LOW Short wall torch Default light value is 200 Default style is 0 ---------------------------------- If triggered, will toggle between lightvalue1 and lightvalue2 .lightvalue1 (default 0) .lightvalue2 (default 11, equivalent to 300 brightness) Two values the light will fade-toggle between, 0 is black, 25 is brightest, 11 is equivalent to a value of 300. .fadespeed (default 1) = How many seconds it will take to complete the desired lighting change The light will start on at a default of the higher light value unless you turn on the startlow flag. START_LOW = will make the light start at the lower of the lightvalues you specify (default uses brighter) NOTE: IF YOU DON'T PLAN ON USING THE DEFAULTS, ALL LIGHTS IN THE BANK OF LIGHTS NEED THIS INFO */ void() light_torch_small_walltorch = { precache_model ("models/flame.mdl"); FireAmbient (); if(self.targetname) self.use=torch_use; self.mdl = "models/null.spr"; self.weaponmodel = "models/flame.mdl"; self.abslight = .75; if(self.style>=32) { if(!self.lightvalue2) self.lightvalue2=11; if(!self.fadespeed) self.fadespeed = 1; initialize_lightstyle(); self.think = torch_think; self.nextthink = time+1; } else { self.drawflags(+)MLS_ABSLIGHT; setmodel(self,self.weaponmodel); makestatic (self); } }; /*QUAKED light_flame_large_yellow (0 1 0) (-10 -10 -12) (12 12 18) START_LOW Large yellow flame ---------------------------------- If triggered, will toggle between lightvalue1 and lightvalue2 .lightvalue1 (default 0) .lightvalue2 (default 11, equivalent to 300 brightness) Two values the light will fade-toggle between, 0 is black, 25 is brightest, 11 is equivalent to a value of 300. .fadespeed (default 1) = How many seconds it will take to complete the desired lighting change The light will start on at a default of the higher light value unless you turn on the startlow flag. START_LOW = will make the light start at the lower of the lightvalues you specify (default uses brighter) NOTE: IF YOU DON'T PLAN ON USING THE DEFAULTS, ALL LIGHTS IN THE BANK OF LIGHTS NEED THIS INFO */ void() light_flame_large_yellow = { precache_model ("models/flame1.mdl"); FireAmbient (); if(self.targetname) self.use=torch_use; self.abslight = .75; self.mdl = "models/null.spr"; self.weaponmodel = "models/flame1.mdl"; if(self.style>=32) { if(!self.lightvalue2) self.lightvalue2=11; if(!self.fadespeed) self.fadespeed = 1; initialize_lightstyle(); self.think = torch_think; self.nextthink = time+1; } else { self.drawflags(+)MLS_ABSLIGHT; setmodel(self,self.weaponmodel); makestatic (self); } }; /*QUAKED light_flame_small_yellow (0 1 0) (-8 -8 -8) (8 8 8) START_LOW Small yellow flame ball ---------------------------------- If triggered, will toggle between lightvalue1 and lightvalue2 .lightvalue1 (default 0) .lightvalue2 (default 11, equivalent to 300 brightness) Two values the light will fade-toggle between, 0 is black, 25 is brightest, 11 is equivalent to a value of 300. .fadespeed (default 1) = How many seconds it will take to complete the desired lighting change The light will start on at a default of the higher light value unless you turn on the startlow flag. START_LOW = will make the light start at the lower of the lightvalues you specify (default uses brighter) NOTE: IF YOU DON'T PLAN ON USING THE DEFAULTS, ALL LIGHTS IN THE BANK OF LIGHTS NEED THIS INFO */ void() light_flame_small_yellow = { precache_model ("models/flame2.mdl"); FireAmbient (); if(self.targetname) self.use=torch_use; self.abslight = .75; self.mdl = "models/null.spr"; self.weaponmodel = "models/flame2.mdl"; if(self.style>=32) { if(!self.lightvalue2) self.lightvalue2=11; if(!self.fadespeed) self.fadespeed = 1; initialize_lightstyle(); self.think = torch_think; self.nextthink = time+1; } else { self.drawflags(+)MLS_ABSLIGHT; setmodel(self,self.weaponmodel); makestatic (self); } }; /*QUAK-ED light_flame_small_white (0 1 0) (-10 -10 -40) (10 10 40) START_LOW Small white flame ball ---------------------------------- If triggered, will toggle between lightvalue1 and lightvalue2 .lightvalue1 (default 0) .lightvalue2 (default 11, equivalent to 300 brightness) Two values the light will fade-toggle between, 0 is black, 25 is brightest, 11 is equivalent to a value of 300. .fadespeed (default 1) = How many seconds it will take to complete the desired lighting change The light will start on at a default of the higher light value unless you turn on the startlow flag. START_LOW = will make the light start at the lower of the lightvalues you specify (default uses brighter) NOTE: IF YOU DON'T PLAN ON USING THE DEFAULTS, ALL LIGHTS IN THE BANK OF LIGHTS NEED THIS INFO */ /* void() light_flame_small_white = { precache_model ("models/flame2.mdl"); FireAmbient (); if(self.targetname) self.use=torch_use; self.abslight = .75; self.mdl = "models/null.spr"; self.weaponmodel = "models/flame2.mdl"; if(self.style>=32) { if(!self.lightvalue2) self.lightvalue2=11; if(!self.fadespeed) self.fadespeed = 1; initialize_lightstyle(); self.think = torch_think; self.nextthink = time+1; } else { self.drawflags(+)MLS_ABSLIGHT; setmodel(self,self.weaponmodel); makestatic (self); } }; */ /*QUAKED light_gem (0 1 0) (-8 -8 -8) (8 8 8) START_LOW A gem that displays light. Default light value is 300 Default style is 0 ---------------------------------- If triggered, will toggle between lightvalue1 and lightvalue2 .lightvalue1 (default 0) .lightvalue2 (default 11, equivalent to 300 brightness) Two values the light will fade-toggle between, 0 is black, 25 is brightest, 11 is equivalent to a value of 300. .fadespeed (default 1) = How many seconds it will take to complete the desired lighting change The light will start on at a default of the higher light value unless you turn on the startlow flag. START_LOW = will make the light start at the lower of the lightvalues you specify (default uses brighter) NOTE: IF YOU DON'T PLAN ON USING THE DEFAULTS, ALL LIGHTS IN THE BANK OF LIGHTS NEED THIS INFO */ void() light_gem = { precache_model ("models/gemlight.mdl"); if(self.targetname) self.use=torch_use; self.mdl = "models/null.spr"; self.weaponmodel = "models/gemlight.mdl"; self.abslight = .75; if(self.style>=32) { if(!self.lightvalue2) self.lightvalue2=11; if(!self.fadespeed) self.fadespeed = 1; initialize_lightstyle(); self.think = torch_think; self.nextthink = time+1; } else { self.drawflags(+)MLS_ABSLIGHT; setmodel(self,self.weaponmodel); makestatic (self); } }; gamecode/hc/h2/lightning.hc000066400000000000000000000245631444734033100160410ustar00rootroot00000000000000/* =============================================================================== LIGHTNING.HC MG Lightning and Sunbeam effects, and thunderstorm =============================================================================== */ void smolder_think () { vector ofs; ofs_x=random(-10,10); ofs_y=random(-10,10); particle(self.origin+ofs, '0 0 100', random(272,288), random(1,10)); if(random()<0.1&&random()<0.5) CreateWhiteSmoke(self.origin,'0 0 8',HX_FRAME_TIME * 2); thinktime self : 0.1; if(time>self.lifetime) remove(self); } void smolder (vector org) { newmis=spawn(); setorigin(newmis,org); newmis.effects=EF_NODRAW; newmis.lifetime=time+7; newmis.think=smolder_think; thinktime newmis : 0; } void shock_think() { if (self.skin ==0) self.skin = 1; else self.skin = 0; self.scale-=0.1; thinktime self : 0.05; if(time>self.lifetime||self.scale<=0.1) remove(self); } void spawnshockball (vector org) { newmis=spawn(); newmis.drawflags(+)MLS_TORCH; setmodel (newmis, "models/vorpshok.mdl"); setorigin(newmis, org); newmis.lifetime=time+1; newmis.angles_z=90; newmis.think=shock_think; thinktime newmis : 0; newmis.scale=2.5; } /* ================= LightningDamage ================= */ void (vector endpos) ThroughWaterZap = { entity waterloser, attacker; float damg; waterloser = spawn(); setorigin (waterloser, endpos); if(self.classname=="mjolnir") damg=128; else damg=666*2; attacker=self; if(self.classname!="player") { if(self.owner.classname=="player") attacker=self.owner; else if(self.controller.classname=="player") attacker=self.controller; } T_RadiusDamageWater (waterloser, self, damg,self); remove (waterloser); }; void (vector startpos) ThroughWater = { vector endpos; float mover,content; mover = 600; while (mover) { mover = mover - 10; endpos = startpos + v_forward * mover; content = pointcontents(endpos); if (content == CONTENT_WATER || content == CONTENT_SLIME) ThroughWaterZap(endpos); else if (content == CONTENT_SOLID) return; } }; void do_lightning_dam (entity from, float damage, string type) { vector loser_org; if((trace_ent.classname=="monster_eidolon"||trace_ent.classname=="obj_chaos_orb")&&type=="lightning") return; particle (trace_endpos, '0 0 100', 225, damage*4); if(type=="lightning") spawnshockball((trace_ent.absmax+trace_ent.absmin)*0.5); loser_org=trace_ent.origin; T_Damage (trace_ent, from, from, damage); if(trace_ent.health<=0) smolder(loser_org); if(type=="lightning") sound(trace_ent,CHAN_AUTO,"misc/lighthit.wav",1,ATTN_NORM); else sound(trace_ent,CHAN_AUTO,"crusader/sunhit.wav",1,ATTN_NORM); } void(vector p1, vector p2, entity from, float damage,string type) LightningDamage = { entity e1, e2; vector f; float inertia; float content; f = p2 - p1; normalize (f); f_x = 0 - f_y; f_y = f_x; f_z = 0; f = f*16; e1 = e2 = world; traceline (p1, p2, FALSE, self); content = pointcontents(trace_endpos); if(type=="lightning"&&(content == CONTENT_WATER || content == CONTENT_SLIME)) ThroughWaterZap(trace_endpos); else if(type=="lightning"&&(trace_ent.watertype == CONTENT_WATER || trace_ent.watertype == CONTENT_SLIME)) T_RadiusDamageWater (self, self, 666*2,self); else if(self.classname=="mjolnir"&&trace_ent==self.controller) bprint(""); else if (trace_ent.takedamage) { if (trace_ent.mass<=10) inertia=1; else inertia = trace_ent.mass/10; do_lightning_dam(from,damage,type); if (self.classname=="mjolnir"&&(trace_ent.flags&FL_ONGROUND)&&type=="lightning") { trace_ent.velocity_z = trace_ent.velocity_z + 400/inertia; trace_ent.flags(-)FL_ONGROUND; } } else if(type=="lightning") ThroughWater(p1); e1 = trace_ent; traceline (p1 + f, p2 + f, FALSE, self); if(self.classname=="mjolnir"&&trace_ent==self.controller) bprint(""); else if(trace_ent != e1 && trace_ent.takedamage) do_lightning_dam(from,damage,type); e2 = trace_ent; traceline (p1 - f, p2 - f, FALSE, self); if(self.classname=="mjolnir"&&trace_ent==self.controller) bprint(""); else if (trace_ent != e1 && trace_ent != e2 && trace_ent.takedamage) do_lightning_dam(from,damage,type); }; void do_lightning (entity lowner,float tag, float lflags, float duration, vector spot1, vector spot2, float ldamg) { vector damage_dir; WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); WriteByte (MSG_BROADCAST, TE_STREAM_LIGHTNING); WriteEntity (MSG_BROADCAST, lowner); WriteByte (MSG_BROADCAST, tag+lflags); WriteByte (MSG_BROADCAST, duration); WriteCoord (MSG_BROADCAST, spot1_x); WriteCoord (MSG_BROADCAST, spot1_y); WriteCoord (MSG_BROADCAST, spot1_z); WriteCoord (MSG_BROADCAST, spot2_x); WriteCoord (MSG_BROADCAST, spot2_y); WriteCoord (MSG_BROADCAST, spot2_z); if(ldamg) { if(self.owner.classname=="player") lowner=self.owner; else if(self.controller.classname=="player") lowner=self.controller; damage_dir=normalize(spot2-spot1); LightningDamage (spot1-damage_dir*15, spot2+damage_dir*15, lowner, ldamg,"lightning"); } } void(float max_strikes, float damg) CastLightning = { //Not working, I want 3 seperate beams, when get that, drop damage to 10 vector org, dir,tospot; float number_strikes; self.effects(+)EF_MUZZLEFLASH; if(max_strikes==0) max_strikes=1; while(max_strikes>number_strikes) { if(random()<0.7) sound(self.enemy,CHAN_AUTO,"crusader/lghtn1.wav",1,ATTN_NORM); else sound(self.enemy,CHAN_AUTO,"crusader/lghtn2.wav",1,ATTN_NORM); if(self.enemy.solid==SOLID_BSP&&self.enemy.origin=='0 0 0') org=(self.enemy.absmin+self.enemy.absmax)*0.5; else { org=self.enemy.origin; org_z += 0.5*self.enemy.maxs_z; } dir=org; dir_x+= random(-300,300); dir_y+= random(-300,300); dir_z+= 500; traceline(org,dir,TRUE,self); tospot=org; org=trace_endpos; do_lightning (self,number_strikes,0,4,org,tospot,damg); number_strikes+=1; } }; void()rolling_thunder; void thunder_clear () { self.owner.aflag-=1; remove(self); } void thunder_sound () { float sound_vol; sound_vol=self.lightvalue2/25; if(sound_vol>1) sound_vol=1; else if(sound_vol<0) sound_vol=0.1; sound (self, CHAN_VOICE, "ambience/thunder1.wav", sound_vol, ATTN_NORM); thinktime self : 5; self.think=thunder_clear; } void spawn_thunder () { self.aflag+=1; newmis=spawn(); self.angles_y=random(360); makevectors(self.angles); setorigin(newmis,self.origin+v_forward*self.lightvalue2*10); newmis.owner=self; newmis.lightvalue2=self.lightvalue2; newmis.think=thunder_sound; thinktime newmis : 2.5 - self.lightvalue2/10; } void flash_wait () { lightstylestatic(self.style,self.lightvalue1); self.think=rolling_thunder; thinktime self : 0; } void lightning_strike (void) { vector org,tospot, lightn_dir; float dist, num_branches; dist=random(self.frags); self.angles_y=random(360); makevectors(self.angles); traceline(self.origin,self.origin+v_forward*dist,TRUE,self); org=trace_endpos; tospot=org-'0 0 1000'; traceline(org,tospot,TRUE,self); tospot=trace_endpos; tospot_x+=random(-100,100); tospot_y+=random(-100,100); dist=vlen(tospot-org); newmis=spawn(); setorigin(newmis,org); if(random()<0.5) sound(newmis,CHAN_AUTO,"crusader/lghtn1.wav",1,ATTN_NORM); else sound(newmis,CHAN_AUTO,"crusader/lghtn2.wav",1,ATTN_NORM); newmis.think=SUB_Remove; thinktime newmis : 3; num_branches = rint(random(3,7)); while(num_branches) { self.level+=1; if(self.level>=8) self.level=0; do_lightning (self,self.level,STREAM_ATTACHED,4,org,tospot,10000); lightn_dir=normalize(tospot-org); org=org + lightn_dir*random(num_branches+dist/10,num_branches+dist/5);//Include trace_fraction? tospot=org-'0 0 1000'; traceline(trace_endpos,tospot,TRUE,self); tospot=trace_endpos; if(random()<0.5) tospot_x+=random(125,375); else tospot_x-=random(125,375); if(random()<0.5) tospot_y+=random(125,375); else tospot_y-=random(125,375); /*if(trace_fraction<0.01) { dprint("not into ground\n"); num_branches=0; } else*/ num_branches-=1; } } void rolling_thunder (void) { if(random(100)>=self.wait) { if(self.spawnflags&1&&random(100)=1) self.aflag=TRUE; } else { self.scale-=0.05; if(self.scale<=0.01) self.aflag=FALSE; } // if(random()<0.3) // TorpedoTrail(); self.think=StarTwinkle; thinktime self : 0.05; } void FireMagicMissile (float offset) { entity star1,star2; vector spread; if(self.classname=="monster_eidolon") v_forward=self.v_angle; else makevectors(self.v_angle); self.effects(+)EF_MUZZLEFLASH; newmis=spawn(); newmis.classname="magic missile"; newmis.owner=self; newmis.drawflags(+)SCALE_ORIGIN_CENTER;//|DRF_TRANSLUCENT; newmis.movetype=MOVETYPE_FLYMISSILE; newmis.solid=SOLID_BBOX; newmis.touch=MagicMissileTouch; newmis.dmg=random(20,25); newmis.speed=1000; spread=normalize(v_right)*(offset*25); newmis.velocity=normalize(v_forward)*newmis.speed + spread; newmis.movedir=normalize(newmis.velocity); newmis.avelocity_z=random(300,600); newmis.level=TRUE; setmodel(newmis,"models/ball.mdl"); setsize(newmis,'0 0 0','0 0 0'); if(self.classname=="monster_eidolon") { newmis.scale=0.75; setorigin(newmis,self.origin+self.proj_ofs+v_forward*48+v_right*20); sound(self,CHAN_AUTO,"eidolon/spell.wav",1,ATTN_NORM); } else { newmis.scale=0.5; setorigin(newmis,self.origin+self.proj_ofs+v_forward*8+v_right*7+'0 0 5'); sound(newmis,CHAN_AUTO,"necro/mmfire.wav",1,ATTN_NORM); } if(self.artifact_active&ART_TOMEOFPOWER) { if(self.classname=="monster_eidolon") { newmis.enemy=self.enemy; newmis.classname = "eidolon spell"; newmis.turn_time=3; newmis.dmg=random(30,40); } else { newmis.turn_time=2; newmis.dmg=random(45,55); } newmis.effects=EF_DIMLIGHT; newmis.frags=TRUE; // newmis.dmg=random(30,40); newmis.veer=100; newmis.homerate=0.1; // newmis.turn_time=3; newmis.lifetime=time+5; newmis.th_die=chain_remove; newmis.think=HomeThink; newmis.hoverz=TRUE; thinktime newmis : 0.2; } else { newmis.think=chain_remove; thinktime newmis : 3; } star1=spawn(); newmis.movechain = star1; star1.drawflags(+)MLS_ABSLIGHT; star1.abslight=0.5; star1.avelocity_z=400; star1.avelocity_y=300; star1.angles_y=90; if(self.classname=="monster_eidolon") setmodel(star1,"models/glowball.mdl"); else { setmodel(star1,"models/star.mdl"); star1.scale=0.3; } setorigin(star1,newmis.origin); star2=spawn(); if(self.classname!="monster_eidolon") { star1.movechain = star2; star2.drawflags(+)MLS_ABSLIGHT; star2.abslight=0.5; star2.avelocity_z=-400; star2.avelocity_y=-300; star2.scale=0.3; setmodel(star2,"models/star.mdl"); setorigin(star2,newmis.origin); } star1.movetype=star2.movetype=MOVETYPE_NOCLIP; star1.owner=star2.owner=newmis; star1.think=star2.think=StarTwinkle; thinktime star1 : 0; thinktime star2 : 0; } void flash_think () { makevectors(self.owner.v_angle); self.angles_x=self.owner.v_angle_x*-1; self.angles_y=self.owner.v_angle_y; setorigin(self,self.owner.origin+self.owner.proj_ofs+'0 0 5'+v_right*2+v_forward*6); thinktime self : 0.01; self.abslight-=0.05; self.scale+=0.05; if(self.lifetimetime) return; FireFlash(); FireMagicMissile(-3); FireMagicMissile(0); FireMagicMissile(3); self.bluemana-=10; self.attack_finished=time+0.7; } void mmis_normal() { if(self.attack_finished>time) return; FireFlash(); FireMagicMissile(0); self.bluemana-=2; self.attack_finished=time+0.2; } /*====================== ACTION select deselect ready loop relax loop fire once fire loop ready to relax(after short delay) relax to ready(Fire delay? or automatic if see someone?) =======================*/ void()magicmis_ready; void() Nec_Mis_Attack; void magicmis_fire (void) { if(self.button0&&self.weaponframe==$mfire5 &&!self.artifact_active&ART_TOMEOFPOWER) self.weaponframe=$mfire5; else self.wfs = advanceweaponframe($mfire1,$mfire8); self.th_weapon=magicmis_fire; self.last_attack=time; if(self.wfs==WF_CYCLE_WRAPPED||self.bluemana<2||(self.artifact_active&ART_TOMEOFPOWER&&self.bluemana<10)) magicmis_ready(); else if(self.weaponframe==$mfire5)// &&self.attack_finished<=time) if(self.artifact_active&ART_TOMEOFPOWER) mmis_power(); else mmis_normal(); } void() Nec_Mis_Attack = { magicmis_fire(); thinktime self : 0; }; void magicmis_jellyfingers () { self.wfs = advanceweaponframe($midle01,$midle22); self.th_weapon=magicmis_jellyfingers; if(self.wfs==WF_CYCLE_WRAPPED) magicmis_ready(); } void magicmis_ready (void) { self.weaponframe=$midle01; if(random()<0.1&&random()<0.3&&random()<0.5) self.th_weapon=magicmis_jellyfingers; else self.th_weapon=magicmis_ready; } void magicmis_select (void) { self.wfs = advanceweaponframe($mselect01,$mselect20); self.weaponmodel = "models/spllbook.mdl"; self.th_weapon=magicmis_select; if(self.wfs==WF_CYCLE_WRAPPED) { self.attack_finished = time - 1; magicmis_ready(); } } void magicmis_deselect (void) { self.wfs = advanceweaponframe($mselect20,$mselect01); self.th_weapon=magicmis_deselect; if(self.wfs==WF_CYCLE_WRAPPED) W_SetCurrentAmmo(); } void magicmis_select_from_bone (void) { self.wfs = advanceweaponframe($go2mag01,$go2mag13); self.weaponmodel = "models/spllbook.mdl"; self.th_weapon=magicmis_select_from_bone; if(self.wfs==WF_CYCLE_WRAPPED) { self.attack_finished = time - 1; magicmis_ready(); } } gamecode/hc/h2/math.hc000066400000000000000000000016311444734033100147760ustar00rootroot00000000000000/* * crandom() -- Returns a random number between -1 and 1. */ float crandom() { return random(-1,1); } float fexp(float base,float exponent) {//MG float exp_count; exponent=rint(exponent); if(exponent==0) return 1; if(exponent<0) { base=1/base; exponent=fabs(exponent); } if(exponent==1) return base; exponent-=1; while(exp_count=$swipe6 &&self.frame<=$swipe10) { makevectors(self.angles); traceline(self.origin+'0 0 23',self.origin+'0 0 23'-v_forward*72+v_right*($swipe7 - self.frame)*10,FALSE,self); if(trace_ent.takedamage) { T_Damage(trace_ent,self,self,7); sound(trace_ent,CHAN_BODY,"weapons/met2flsh.wav",1,ATTN_NORM); SpawnPuff(trace_endpos,'0 0 0',7,trace_ent); trace_ent.velocity+=v_right*-200; trace_ent.velocity_z+=100; trace_ent.flags(-)FL_ONGROUND; } } else if(self.frame>$swipe10) ai_face(); } float MedusaCheckAttack (void) { vector org,dir,destiny; float r, loscheck1,loscheck2; if(random()<0.5 - skill/10 - self.skin/5||self.enemy==world) return FALSE; org=self.origin+self.view_ofs; if (time < self.attack_finished) return FALSE; if (!enemy_vis) { if(self.goalentity.classname=="waypoint") { if(visible2ent(self.enemy,self.goalentity)) { MedusaSelectDir(MEDUSA_SNAKES); return TRUE; } } return FALSE; } if(!enemy_infront) { if(enemy_range==RANGE_MELEE) { MedusaSwipe(); return TRUE; } } if (enemy_range == RANGE_FAR) { if (self.attack_state != AS_STRAIGHT) self.attack_state = AS_STRAIGHT; return FALSE; } // see if any entities are in the way of the shot dir = self.enemy.origin + self.enemy.view_ofs; traceline (org, dir, FALSE, self); if (trace_ent != self.enemy) { // don't have a clear shot, so move to a side if (self.attack_state != AS_SLIDING) self.attack_state == AS_SLIDING; return FALSE; } else self.attack_state == AS_STRAIGHT; destiny = self.enemy.origin+self.enemy.view_ofs; //FIXME: account for z difference loscheck1=lineofsight(self.enemy,self); loscheck2=lineofsight(self,self.enemy); r=random(); if(!self.enemy.artifact_active&ARTFLAG_STONED&&loscheck1&& (loscheck2|| (r<0.1&&infront_of_ent(self,self.enemy)) ) ) { MedusaGaze(org,destiny,self.enemy); return TRUE; } if (enemy_range == RANGE_MELEE) { MedusaSelectDir(MEDUSA_HEADBUTT); return TRUE; } else if (enemy_range == RANGE_NEAR) r = 0.2; else if (enemy_range == RANGE_MID) r = 0.3; if (random () < r) { MedusaSelectDir(MEDUSA_SNAKES); return TRUE; } return FALSE; } //==================================================================== void()medusa_look_right; void MedusaHeadTouch () { if(self.velocity!='0 0 0'&&!other.flags2&FL_ALIVE) sound(self,CHAN_AUTO,"weapons/hithurt2.wav",1,ATTN_NORM); } void()MedusaHeadDying; void MedusaHeadDead () [++ 0 .. 45] { thinktime self : 0.1; ai_face(); if(self.frame==20) sound(self,CHAN_VOICE,"medusa/sight.wav",0.5,ATTN_NORM); else if(self.frame==45) { self.aflag=TRUE; self.think=MedusaHeadDying; } } void MedusaHeadDying () [++ 46 .. 105] { if(pointcontents(self.origin)==CONTENT_SOLID) { chunk_death(); return; } if(self.velocity=='0 0 0') if(!self.aflag) { if(self.angles_x<-10||self.angles_x>10) self.angles_x=0; if(self.angles_z<-10||self.angles_z>10) self.angles_z=0; self.solid = SOLID_BBOX; self.think=MedusaHeadDead; thinktime self : 0; } else if(self.frame==105) { self.skin=1; self.think=init_corpseblink; thinktime self : 5; } } void MedusaThrowHead () { newmis = spawn(); newmis.owner=self; newmis.enemy=newmis.goalentity=self.enemy; newmis.yaw_speed=3; setmodel (newmis, self.headmodel); self.headmodel=""; setsize (newmis, '-3 -3 -3', '3 3 3'); setorigin(newmis,self.absmax - '0 0 15'); newmis.velocity = randomv('-200 -200 200','200 200 600'); newmis.movetype = MOVETYPE_BOUNCE; if(pointcontents(newmis.origin)==CONTENT_SOLID) newmis.solid = SOLID_NOT; else newmis.solid = SOLID_BBOX; newmis.takedamage=DAMAGE_YES; newmis.thingtype=self.thingtype; newmis.th_die=chunk_death; newmis.touch=MedusaHeadTouch; newmis.health=25; newmis.scale=2; newmis.avelocity_x = random(600); newmis.avelocity_y = random(600); newmis.avelocity_z = random(600); newmis.think=MedusaHeadDying; thinktime newmis : 0; } void medusa_decap_drop ()[++ $bdecap1 .. $bdecap25] { if(self.frame==$bdecap9) sound(self,CHAN_BODY,"player/land.wav",1,ATTN_NORM); else if(self.frame==$bdecap25) MakeSolidCorpse(); } void medusa_decap_loop ()[++ $adecap1..$adecap88] { if(random()<0.5) self.angles_y+=random(-3,3); walkmove(self.angles_y,self.speed*random(),FALSE); if(random()<0.2) { sound (self, CHAN_VOICE, "misc/decomp.wav", 0.3, ATTN_NORM); SpawnPuff (self.origin+'0 0 56', '0 0 35',5,self); } if(self.frame==$adecap48) sound(self,CHAN_BODY,"medusa/rattle.wav",1,ATTN_NORM); if(cycle_wrapped) { self.think=medusa_decap_drop; thinktime self : 0; } } void medusa_decap_init () { float throwdist; sound(self,CHAN_WEAPON,"misc/null.wav",1,ATTN_NORM); throwdist=self.health; ThrowGib("models/medsnake.mdl",throwdist); ThrowGib("models/medsnake.mdl",throwdist); ThrowGib("models/medsnake.mdl",throwdist); sound(self,CHAN_VOICE,"player/gib2.wav",1,ATTN_NORM); MedusaThrowHead(); SpawnPuff (self.origin+'0 0 56', '0 0 35',5,self); medusa_decap_loop(); } void medusa_die (void) [++ $death01..$death20] { medusa_check_use_model("models/medusa2.mdl"); if(self.decap) medusa_decap_init(); else if(self.health<=-80) { sound(self,CHAN_WEAPON,"misc/null.wav",1,ATTN_NORM); MedusaThrowHead(); chunk_death(); } else { if(self.frame==$death20) MakeSolidCorpse(); else if(self.frame==$death01) { sound(self,CHAN_WEAPON,"misc/null.wav",1,ATTN_NORM); sound(self,CHAN_VOICE,"medusa/death.wav",1,ATTN_NORM); } } } void medusa_pain_anim () [++ $pain1 .. $pain11] { //sound medusa_check_use_model("models/medusa2.mdl"); if (cycle_wrapped) { thinktime self : 0; self.think=self.th_run; } else if(self.frame==$pain1) sound(self,CHAN_VOICE,"medusa/pain.wav",1,ATTN_NORM); } void medusa_pain (entity attacker,float total_damage) { if(random()<0.6&&total_damage<50&&attacker!=self) return; medusa_pain_anim(); } void()medusa_look_left = [++ $looklf1 .. $looklf29] { medusa_check_use_model("models/medusa.mdl"); if(self.oldthink==self.th_run) ai_run(self.speed); else { if(self.oldthink==self.th_stand) ai_stand(); else if(self.oldthink==self.th_walk) ai_walk(5); } if(cycle_wrapped) { self.think=self.oldthink; if(self.think!=self.th_run) if(random()<0.2) self.think=medusa_look_right; thinktime self : 0; } }; void()medusa_look_right = [++ $lookrt1 .. $lookrt29] { medusa_check_use_model("models/medusa.mdl"); if(self.oldthink==self.th_run) ai_run(self.speed); else { if(self.oldthink==self.th_stand) ai_stand(); else if(self.oldthink==self.th_walk) ai_walk(5); } if(cycle_wrapped) { self.think=self.oldthink; if(self.think!=self.th_run) if(random()<0.2) self.think=medusa_look_left; thinktime self : 0; } }; void()medusa_rattle_left = [++ $ratlft1 .. $ratlft29] { //sound medusa_check_use_model("models/medusa.mdl"); if(cycle_wrapped) { thinktime self : 0; self.think=self.th_run; } }; void()medusa_rattle_right = [++ $ratrit1 .. $ratrit29] { //sound medusa_check_use_model("models/medusa.mdl"); if(cycle_wrapped) { thinktime self : 0; self.think=self.th_run; } }; void()medusa_rattle = [++ $ratatt1 .. $ratatt29] { //sound medusa_check_use_model("models/medusa.mdl"); if(cycle_wrapped) { thinktime self : 0; self.think=self.th_run; } }; void MedusaSelectDir (float action) { vector enemy_dir; float dot; medusa_check_use_model("models/medusa.mdl"); self.monster_stage=action; if(action>=MEDUSA_HEADBUTT) { self.last_attack=time; sound (self,CHAN_VOICE,"medusa/hiss.wav",1,ATTN_NORM); medusa_check_use_model("models/medusa2.mdl"); } else if(action==MEDUSA_RATTLE) sound(self,CHAN_BODY,"medusa/rattle.wav",1,ATTN_NORM); makevectors(self.angles); enemy_dir=normalize(self.enemy.origin-self.origin); dot=v_right*enemy_dir; if(dot>0.3) { self.angle_ofs_y=-90; if(action>=MEDUSA_HEADBUTT) self.think=medusa_attack_right; else if(action==MEDUSA_RATTLE) self.think=medusa_rattle_right; else self.think=medusa_look_right; } else if(dot<-0.3) { self.angle_ofs_y=-90; if(action>=MEDUSA_HEADBUTT) self.think=medusa_attack_left; else if(action==MEDUSA_RATTLE) self.think=medusa_rattle_left; else self.think=medusa_look_left; } else { self.angle_ofs_y=0; if(action>=MEDUSA_HEADBUTT) self.think=medusa_attack; else if(action==MEDUSA_RATTLE) self.think=medusa_rattle; else return; } thinktime self : 0; } void medusa_hunt () [++ $medusa1 .. $medusa29] { medusa_check_use_model("models/medusa.mdl"); if(random()<0.1) ai_run(0); else ai_run(self.speed); if(!enemy_vis) if(random()<0.1&&random()<0.5) if(random()<0.5) MedusaSelectDir(MEDUSA_RATTLE); else { self.oldthink=self.th_run; MedusaSelectDir(MEDUSA_LOOK); } } void medusa_walk () [++ $medusa1 .. $medusa29] { medusa_check_use_model("models/medusa.mdl"); self.monster_awake=FALSE; if(random()<0.1) ai_walk(0); else ai_walk(5); if(cycle_wrapped) { if(random()<0.3) { self.oldthink=self.th_walk; if(random()<0.5) { thinktime self : 0; self.think=medusa_look_left; } else { thinktime self : 0; self.think=medusa_look_right; } } } // MedusaCheckAttack(); if(random()<0.1&&random()<0.5) sound(self,CHAN_VOICE,"medusa/hiss.wav",1,ATTN_NORM); } void medusa_stand () [++$stand1..$stand29] { medusa_check_use_model("models/medusa.mdl"); self.monster_awake=FALSE; ai_stand(); if(random()<0.1) { self.oldthink=self.th_stand; if(random()<0.5) { thinktime self : 0; self.think=medusa_look_left; } else { thinktime self : 0; self.think=medusa_look_right; } } if(random()<0.1&&random()<0.3) sound(self,CHAN_VOICE,"medusa/hiss.wav",1,ATTN_NORM); } /*QUAKED monster_medusa_green (1 0.3 0) (-16 -16 0) (16 16 56) AMBUSH STUCK JUMP PLAY_DEAD DORMANT The medusa monster with its nasty sharp pointy teeth -------------------------FIELDS------------------------- -------------------------------------------------------- */ void monster_medusa_green (void) { if (deathmatch) { remove(self); return; } precache_model2("models/medusa.mdl"); precache_model2("models/medusa2.mdl"); precache_model2("models/snakearr.mdl"); precache_model2("models/medhit.spr"); precache_model2("models/medhead.mdl"); precache_model2("models/medsnake.mdl"); precache_sound2("medusa/rattle.wav"); precache_sound2("medusa/hiss.wav"); precache_sound2("medusa/sight.wav"); precache_sound2("medusa/attack1.wav"); precache_sound2("medusa/attack2.wav"); precache_sound2("medusa/pain.wav"); precache_sound2("medusa/death.wav"); precache_sound2("medusa/stoned.wav"); precache_sound2("medusa/hitplayr.wav"); // if(random()<0.5) // self.skin=1; self.headmodel="models/medhead.mdl"; self.solid = SOLID_SLIDEBOX; self.movetype = MOVETYPE_STEP; self.takedamage=DAMAGE_YES; self.thingtype=THINGTYPE_FLESH; self.monsterclass=CLASS_LEADER; self.mintel = 20;//Very smart- excellent tracker self.mass = 15; self.view_ofs = '0 0 53'; self.speed=5; self.yaw_speed = 5; self.classname="monster_medusa"; self.health = 700; self.experience_value = 500; self.th_stand=medusa_stand; self.th_run=medusa_hunt; self.th_walk=medusa_walk; self.th_die=medusa_die; self.th_pain=medusa_pain; self.th_missile=medusa_rattle; self.th_melee=medusa_attack; setmodel (self, "models/medusa.mdl"); setsize(self, '-28 -28 0', '28 28 56'); self.hull=HULL_PLAYER; walkmonster_start(); } void monster_medusa (void) { monster_medusa_green(); } /*QUAKED monster_medusa_red (1 0.3 0) (-16 -16 0) (16 16 56) AMBUSH STUCK JUMP PLAY_DEAD DORMANT The medusa monster with its nasty sharp pointy teeth -------------------------FIELDS------------------------- -------------------------------------------------------- */ void monster_medusa_red (void) { // self.skin=1; monster_medusa_green(); self.health = 250; self.experience_value = 125; } gamecode/hc/h2/meteor.hc000066400000000000000000000446211444734033100153460ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\weapons\meteor\final\meteor.hc ============================================================================== */ // For building the model $cd Q:\art\models\weapons\meteor\final $origin 0 0 0 $base BASE skin $skin skin $flags 0 // $frame idle // $frame Select1 Select2 Select3 Select4 Select5 $frame Select6 Select7 Select8 Select9 Select10 $frame Select11 Select12 Select13 Select14 Select15 $frame Select16 Select17 Select18 // $frame fire1 fire2 fire3 fire4 fire5 $frame fire6 fire7 fire8 fire9 void MeteoriteFizzle (void) { CreateWhiteSmoke(self.origin,'0 0 8',HX_FRAME_TIME * 2); remove(self); } void MeteorThink (void) { if(self.lifetime3) CreateWhiteSmoke(self.origin,'0 0 8',HX_FRAME_TIME * 2); self.think=MeteorThink; thinktime self : 0.3; } void MeteorTouch (void) { if(other.controller==self.owner) return; if(self.dmg==3) { if(other==world) { if(!self.pain_finished&&random()<0.3) { sound(self.controller,CHAN_BODY,"misc/rubble.wav",1,ATTN_NORM); self.pain_finished=TRUE; } return; } else if(other.classname=="meteor") return; } if(other.takedamage&&other.health) { T_Damage(other,self,self.owner,self.dmg); if(self.dmg>3) { if((other.flags&FL_CLIENT||other.flags&FL_MONSTER)&&other.mass<200) { vector hitdir; hitdir=self.o_angle*300; hitdir_z+=150; if(hitdir_z<0) hitdir_z=0; other.velocity=hitdir; other.flags(-)FL_ONGROUND; } self.dmg/=2; } } else if(self.dmg>3) self.dmg=100; if(self.dmg>3) MultiExplode(); else MeteoriteFizzle(); } void FireMeteor (string type) { vector org; entity meteor; meteor=spawn(); setmodel(meteor,"models/tempmetr.mdl"); if(type=="minimeteor") { meteor.classname="minimeteor"; meteor.velocity=RandomVector('200 200 0'); meteor.velocity_z=random(200,400); meteor.lifetime=time + 1.5; meteor.dmg=3; meteor.scale=random(0.15,0.45); meteor.movetype=MOVETYPE_BOUNCE; org=self.origin; setsize(meteor,'0 0 0', '0 0 0'); } else { meteor.th_die=MultiExplode; if(self.classname=="player") { self.greenmana-=8; self.velocity+=normalize(v_forward)*-300;//include mass self.flags(-)FL_ONGROUND; } meteor.classname="meteor"; self.punchangle_x = -6; sound(self,CHAN_AUTO,"crusader/metfire.wav",1,ATTN_NORM); self.attack_finished=time + 0.7; self.effects(+)EF_MUZZLEFLASH; makevectors(self.v_angle); meteor.speed=1000; meteor.o_angle=normalize(v_forward); meteor.velocity=meteor.o_angle*meteor.speed; meteor.veer=30; meteor.lifetime=time + 5; meteor.dmg=75; meteor.movetype=MOVETYPE_FLYMISSILE; org=self.origin+self.proj_ofs+v_forward*12; setsize(meteor,'0 0 0', '0 0 0'); } // meteor.abslight = 0.5; // Pa3PyX // meteor.drawflags(+)MLS_FIREFLICKER;//|MLS_ABSLIGHT; meteor.abslight = 1.0; meteor.drawflags (+) (MLS_FIREFLICKER | MLS_ABSLIGHT); meteor.avelocity=RandomVector('360 360 360'); if(self.classname=="tornato") meteor.owner=self.controller; else if(self.classname=="meteor") meteor.owner=self.owner; else meteor.owner=self; meteor.controller=self; meteor.solid=SOLID_BBOX; meteor.touch=MeteorTouch; meteor.think=MeteorThink; thinktime meteor : 0.1; setorigin(meteor,org); } void() tornato_die = [++24 .. 47] { if(cycle_wrapped) { if(self.enemy) { self.enemy.avelocity='0 500 0'; if(self.enemy.flags2&FL_ALIVE) self.enemy.movetype=self.enemy.oldmovetype; } if(self.movechain!=world) remove(self.movechain); remove(self); } self.movechain.frame+=1; if(self.movechain.frame>24) self.movechain.frame=0; if(self.movechain.scale>0.04) self.movechain.scale-=0.04; if(self.movechain.avelocity_y>0) self.movechain.avelocity_y-=20; }; void() tornato_spin = [++0 .. 23] { float distance,content; if(time>self.lifetime||self.torncount24) self.movechain.frame=0; //FIXME: add tracking to movement and firing. if(random()<0.2) { self.velocity_x+=random(-100*self.scale,100*self.scale); if(fabs(self.velocity_x)>1000) self.velocity_x/=2; } if(random()<0.2) { self.velocity_y+=random(-100*self.scale,100*self.scale); if(fabs(self.velocity_y)>1000) self.velocity_y/=2; } content=pointcontents(self.origin); if(content==CONTENT_WATER||content==CONTENT_LAVA) { self.velocity_z+=random(33,200); particle4(self.origin,random(20),264*15,PARTICLETYPE_GRAV,random()*10); particle4(self.origin,random(20),random(406,414),PARTICLETYPE_GRAV,random(10)); } else if(random()<0.2) { distance=random(-30,15);//tries to stay on ground if(self.goalentity!=world&&self.enemy!=self.goalentity) if(self.goalentity.origin_z>self.origin_z)//unless goal is above it distance=random(-30,30); self.velocity_z+=distance; if(fabs(self.velocity_z)>333) self.velocity_z/=3; } if(self.enemy!=world) { vector org, dir; float let_go; self.velocity=self.velocity*0.5; org=self.origin; if(self.enemy.size_z>=self.size_z) org=self.origin; else org_z+=random(10)*self.scale+4*self.scale; if(!self.enemy.flags2&FL_TORNATO_SAFE) { self.enemy.velocity='0 0 0'; setorigin(self.enemy,org); } else { self.enemy.flags2(-)FL_TORNATO_SAFE; let_go=TRUE; } //FIXME: throw the Sheep if(!let_go&&self.enemy!=world&&!self.enemy.flags2&FL_ALIVE)//Don't let go of it if it's not a creature if(random()>=0.4||self.goalentity==world||(!visible(self.goalentity))||self.goalentity.health<=0) self.pain_finished=time+1; else { self.pain_finished=-1; if(self.goalentity.solid==SOLID_BSP&&self.goalentity.origin=='0 0 0') dir=normalize((self.goalentity.absmax+self.goalentity.absmin)*0.5-self.enemy.origin); else dir=normalize(self.goalentity.origin-self.enemy.origin); } if(!let_go&&self.enemy.takedamage&&self.enemy.health>0&&self.pain_finished>time) { if(random()<0.3) T_Damage(self.enemy,self,self.owner,self.scale);//was 3*is this needed with meteors flying out? } else { if(!let_go) if(self.pain_finished==-1) //Throw it at my goal! self.enemy.velocity=dir*350*self.scale; else { self.enemy.velocity_z=random(200*self.scale); self.enemy.velocity_x=random(200*self.scale,-200*self.scale); self.enemy.velocity_y=random(200*self.scale,-200*self.scale); } self.pain_finished=time; self.enemy.safe_time=time+3+let_go*7;//let them get thrown away from the tornado for a full 3 seconds if(self.enemy.flags2&FL_ALIVE) { self.enemy.movetype=self.enemy.oldmovetype; if(self.enemy.classname=="player_sheep") { sound(self.enemy,CHAN_VOICE,"misc/sheepfly.wav",1,ATTN_NORM); self.enemy.pain_finished=time+1; } } if(!let_go) self.enemy.avelocity_y=random(200*self.scale); self.enemy=self.movechain.movechain=world; } if(self.enemy.classname=="player") { self.enemy.punchangle_y=random(3,12);//FIXME: Do WRITEBYTE on angles? self.enemy.punchangle_x=random(-3,3);//FIXME: Do WRITEBYTE on angles? self.enemy.punchangle_z=random(-3,3);//FIXME: Do WRITEBYTE on angles? } if(self.enemy!=world&&self.goalentity==self.enemy) self.goalentity=world;//Hunt a new target, if it can } if(random()<0.3) { entity sucker; float seekspeed; sucker=findradius(self.origin,500); while(sucker) { if(sucker.takedamage&&sucker.health&&sucker!=self.enemy&&sucker.mass<500*self.scale&&visible(sucker)&&sucker!=self.owner) if(sucker.movetype&&sucker.movetype!=MOVETYPE_PUSH) { seekspeed=(500 - vlen(sucker.origin-self.origin)); sucker.velocity=normalize(self.origin-sucker.origin)*seekspeed; if(sucker.velocity_z<30) sucker.velocity_z=30; sucker.flags(-)FL_ONGROUND; if(sucker.classname=="player") sucker.adjust_velocity=sucker.velocity; } sucker=sucker.chain; } if(self.goalentity!=world&&visible(self.goalentity)&&self.goalentity.health>0) { seekspeed = random(150,333); if(self.goalentity.solid==SOLID_BSP&&self.goalentity.origin=='0 0 0') distance=vlen((self.goalentity.absmax+self.goalentity.absmin)*0.5-self.origin); else distance=vlen(self.goalentity.origin-self.origin);//Swoop in when close! if(distance<256) seekspeed+=(256-distance); if(self.goalentity.velocity) seekspeed+=vlen(self.goalentity.velocity); self.velocity=(self.velocity*3+normalize(self.goalentity.origin-self.origin)*seekspeed*self.scale)*0.25;//too fast? } else { float bestdist; self.goalentity=world;//out of sight, out of mind bestdist=1001; sucker=findradius(self.origin,1000); while(sucker) { if(sucker.takedamage&&sucker.health&&sucker!=self.enemy&&sucker.mass<500*self.scale&&visible(sucker)&&sucker!=self.owner&&!sucker.effects&EF_NODRAW) { if(sucker.solid==SOLID_BSP&&sucker.origin=='0 0 0') distance=vlen((sucker.absmax+sucker.absmin)*0.5-self.origin); else distance=vlen(sucker.origin-self.origin); if(self.goalentity.velocity=='0 0 0') { if(sucker.velocity!='0 0 0'&&(sucker.flags2&FL_ALIVE)) { bestdist=distance; self.goalentity=sucker; } else if(!self.goalentity.flags2&FL_ALIVE) { if(sucker.flags2&FL_ALIVE) { bestdist=distance; self.goalentity=sucker; } else if(distance=self.target_scale) { self.touch=funnal_touch; self.scale=self.owner.scale=self.target_scale; self.think=SUB_Null; self.nextthink=-1; remove(self.goalentity.owner); remove(self.goalentity); } else { self.think=tornato_merge; thinktime self : 0.01; } } void funnal_touch (void) { //FIXME: Ignore the controlling player's projectiles, leaving it in to test if(other.flags&FL_MONSTER&&other.monsterclass>=CLASS_BOSS) { T_Damage(other,self,self.owner,7); traceline((self.absmin+self.absmax)*0.5,(other.absmin+other.absmax)*0.5,FALSE,self); SpawnPuff(trace_endpos,randomv('-1 -1 -1','1 1 1'),5,other); return; } if(other==self.controller||other.controller==self.owner||other==world||other==self.owner||other==self.owner||other.classname=="tornato"||(other.classname=="funnal"&&other.aflag)||other.movetype==MOVETYPE_PUSH) return; if(self.aflag) { self.owner.think=SUB_Remove; self.think=SUB_Remove; return; } if(other.classname=="funnal"&&other.scale>=1&&self.scale>=1&&other.scale+self.scale<2.5) { //Add random to stall the merging tracearea(self.origin,self.origin,self.mins+other.mins,self.maxs+other.maxs,TRUE,self); if(trace_fraction<1) return; self.goalentity=other; self.touch=other.touch=SUB_Null; if(other.controller!=self.controller) self.owner.owner=self.owner.controller=self.controller=self.owner; //make scaling gradual self.drawflags=MLS_ABSLIGHT|SCALE_ORIGIN_BOTTOM; self.owner.drawflags=SCALE_ORIGIN_BOTTOM; other.drawflags=MLS_ABSLIGHT+SCALE_ORIGIN_BOTTOM+SCALE_TYPE_XYONLY; self.target_scale=self.scale+other.scale; if(self.target_scale>2.5) self.target_scale=2.5; setsize(self,self.mins+other.mins,self.maxs+other.maxs); setsize(self.owner,self.owner.mins+other.owner.mins,self.owner.maxs+other.owner.maxs); tornato_merge(); } else if(other!=self.movechain&&other.movetype&&other.mass<500*self.scale&&other.classname!="funnal")//Can't pick up or move extremely heavy objects, bounce off them? { if(other.health&&other.takedamage&&other.solid!=SOLID_BSP)//Ignore health>1000? { if(!other.touch) other.touch=obj_push;//Experimental if(self.movechain==world&&other.safe_time=1) { self.movechain=other; other.flags(+)FL_MOVECHAIN_ANGLE; setorigin(other,self.origin+'0 0 4');//maybe need to take on bounding box of captured enemy too? other.velocity='0 0 0'; if(other.flags2&FL_ALIVE) other.avelocity='0 0 0'; else { other.avelocity_x=random(360); other.avelocity_z=random(360); } other.oldmovetype=other.movetype; other.movetype=MOVETYPE_NONE; self.owner.enemy=other; self.owner.pain_finished=time+random(3,10);//How long to hold them before throwing them away if(other.classname=="player_sheep"&&other.flags2&FL_ALIVE) { sound(other,CHAN_VOICE,"misc/sheepfly.wav",1,ATTN_NORM); other.pain_finished=time+1; } return; } } vector dir; dir=normalize(self.angles); dir*=random(200,700)*self.scale; other.velocity+=dir; other.velocity_z=random(100,250)*self.scale; other.flags(-)FL_ONGROUND; if(other.takedamage) T_Damage(other,self.owner,self.owner.controller,5*self.scale); if(other.classname=="player_sheep"&&other.flags2&FL_ALIVE) { sound(other,CHAN_VOICE,"misc/sheepfly.wav",1,ATTN_NORM); other.pain_finished=time+1; } } } void() tornato_grow = [++48 .. 72] { if(cycle_wrapped) { self.movechain.scale=1; self.think=tornato_spin; thinktime self : 0; } self.movechain.frame+=1; if(self.movechain.frame>24) self.movechain.frame=0; self.movechain.scale+=0.04; }; void FireMeteorTornado (void) { /* FIX: 1: BUG:If pull someone out of water & they die, stay in swim mode.(Fly mode does this with water too) 2: More particles & splash sound when hit water 3: Deflect projectiles 4: Limit 2, if 3rd made, erase 1st 5: Shorten life? 6: Can't hurt owner 9: Player's view should actually be changed with WRITEBYTE's? 10: Screw up aim of people inside tornado 11: gradual suck in, then stick to center? 12: incorporate mass? At least check to see if it can be picked up, maybe give a little resistance 13: Meteors are going through walls 14: Change bounding box to match what it picked up? 15: Scale up to match something big it picked up? 16: Check it there's room in front to make it, and at what height, use v_forward if possible. If not enough room:? 17: Auntie Em, Auntie Em! 18: Don't even consider movetype_push's? 19: Scale randomly? 20: Origins MUST be at bottom 21: Particle and Puff Sprites at origin when onground 22: If pick up something not alive, throw it at goalentity? Random chance? 23: Bounding box should be a little bigger 24: If it hits water while holding a player, it should go down and drown them. */ entity tornato,funnal; vector org; self.greenmana-=20; sound(self,CHAN_WEAPON,"crusader/torngo.wav",1,ATTN_NORM); makevectors(self.v_angle); org=self.origin+normalize(v_forward)*16; org_z=self.origin_z+1; tornato=spawn(); self.torncount+=1; tornato.torncount=self.torncount; tornato.solid=SOLID_NOT; tornato.movetype=MOVETYPE_FLY; tornato.owner=tornato.controller=self; tornato.classname="tornato"; tornato.enemy=world; setmodel(tornato,"models/tornato.mdl"); setsize(tornato,'-18 -18 -3','18 18 64'); tornato.hull=HULL_PLAYER; setorigin(tornato,org); tornato.velocity=normalize(v_forward)*250+'0 0 20'; tornato.velocity_z=0; tornato.scale=1; if(visible(self.enemy)&&self.enemy.flags2&FL_ALIVE)//Infront too? tornato.goalentity=self.enemy; tornato.lifetime=time + 20; tornato.think=tornato_grow; thinktime tornato : 0; funnal=spawn(); funnal.owner=tornato; funnal.solid=SOLID_TRIGGER; funnal.classname="funnal"; funnal.movetype=MOVETYPE_FLYMISSILE; funnal.drawflags(+)MLS_ABSLIGHT|SCALE_ORIGIN_BOTTOM|SCALE_TYPE_ZONLY; funnal.abslight=0.2; funnal.scale=0.01; tornato.movechain=funnal; funnal.avelocity='0 100 0'; funnal.controller=self; funnal.touch=funnal_touch; funnal.lifetime=time+1.7; setmodel(funnal,"models/funnal.mdl"); setsize(funnal,'-18 -18 -3','18 18 64'); funnal.hull=HULL_PLAYER; setorigin(funnal,org); } void()meteor_ready_loop; void() Cru_Met_Attack; void meteor_power_fire (void) { self.wfs = advanceweaponframe($fire1,$fire9); self.th_weapon=meteor_power_fire; // Pa3PyX // if(self.weaponframe==$fire2 && self.attack_finished<=time) if(self.weaponframe==$fire1 && self.attack_finished<=time) { self.attack_finished = time + 0.5;// Pa3PyX FireMeteorTornado(); } if(self.wfs==WF_CYCLE_WRAPPED) { self.last_attack=time; meteor_ready_loop(); } } void meteor_fire (void) { self.wfs = advanceweaponframe($fire1,$fire9); self.th_weapon=meteor_fire; if((!self.button0||self.attack_finished>time)&&self.wfs==WF_CYCLE_WRAPPED) { self.last_attack=time; meteor_ready_loop(); } else if(self.weaponframe==$fire1 &&self.attack_finished<=time) FireMeteor("meteor"); } void() Cru_Met_Attack = { if(self.artifact_active&ART_TOMEOFPOWER) self.th_weapon=meteor_power_fire; else self.th_weapon=meteor_fire; thinktime self : 0; }; void meteor_ready_loop (void) { self.weaponframe = $idle; self.th_weapon=meteor_ready_loop; } void meteor_select (void) { //go to ready loop, not relaxed? self.wfs = advanceweaponframe($Select1,$Select16); self.weaponmodel = "models/meteor.mdl"; self.th_weapon=meteor_select; self.last_attack=time; // Pa3PyX // if(self.wfs==WF_CYCLE_WRAPPED) if(self.weaponframe==$Select16) { self.attack_finished = time - 1; meteor_ready_loop(); } } void meteor_deselect (void) { self.wfs = advanceweaponframe($Select16,$Select1); self.th_weapon=meteor_deselect; if(self.wfs==WF_CYCLE_WRAPPED) W_SetCurrentAmmo(); } gamecode/hc/h2/mezzoman.hc000066400000000000000000001054541444734033100157150ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\monsters\mezzoman\FINAL\mezzoman.hc MG!!! ============================================================================== */ // For building the model $cd Q:\art\models\monsters\mezzoman\FINAL $origin 0 0 -2 $base BASE skin1 $skin skin1 $skin Skin2 $flags 16384 // $frame block1 block2 block3 block4 block5 $frame block6 // $frame charge1 charge2 charge3 charge4 charge5 $frame charge6 charge7 charge8 charge9 charge10 $frame charge11 charge12 charge13 charge14 charge15 $frame charge16 charge17 charge18 charge19 charge20 $frame charge21 charge22 charge23 charge24 charge25 // $frame clober1 clober2 clober3 clober4 clober5 $frame clober6 clober7 clober8 clober9 clober10 $frame clober11 clober12 clober13 clober14 clober15 $frame clober16 // $frame death1 death2 death3 death4 death5 $frame death6 death7 death8 death9 death10 $frame death11 death12 death13 death14 death15 $frame death16 // $frame dive1 dive2 dive3 dive4 dive5 $frame dive6 dive7 dive8 dive9 dive10 $frame dive11 dive12 dive13 dive14 dive15 $frame dive16 dive17 dive18 // $frame jump1 jump2 jump3 jump4 jump5 $frame jump6 jump7 jump8 jump9 jump10 $frame jump11 jump12 jump13 jump14 jump15 $frame jump16 jump17 jump18 jump19 jump20 $frame jump21 jump22 // $frame pain1 pain2 pain3 pain4 pain5 $frame pain6 pain7 // $frame roar1 roar2 roar3 roar4 roar5 $frame roar6 roar7 roar8 roar9 roar10 $frame roar11 roar12 roar13 roar14 roar15 $frame roar16 roar17 roar18 roar19 roar20 $frame roar21 roar22 roar23 roar24 roar25 $frame roar26 roar27 roar28 roar29 roar30 // $frame Roll1 Roll2 Roll3 Roll4 Roll5 $frame Roll6 Roll7 Roll8 Roll9 Roll10 $frame Roll11 Roll12 Roll13 Roll14 Roll15 $frame Roll16 Roll17 Roll18 // $frame run1 run2 run3 run4 run5 $frame run6 run7 run8 run9 run10 $frame run11 run12 run13 run14 run15 $frame run16 run17 run18 run19 run20 $frame run21 run22 // $frame stand1 stand2 stand3 stand4 stand5 $frame stand6 stand7 stand8 stand9 stand10 // $frame sword1 sword2 sword3 sword4 sword5 $frame sword6 sword7 sword8 sword9 sword10 $frame sword11 sword12 sword13 // $frame twirl1 twirl2 twirl3 twirl4 twirl5 $frame twirl6 twirl7 twirl8 twirl9 twirl10 // $frame walk1 walk2 walk3 walk4 walk5 $frame walk6 walk7 walk8 walk9 walk10 $frame walk11 walk12 walk13 walk14 walk15 $frame walk16 walk17 walk18 walk19 walk20 $frame walk21 walk22 walk23 walk24 walk25 $frame walk26 walk27 walk28 walk29 walk30 void() mezzo_skid; void() mezzo_block; void() mezzo_block_wait; void() mezzo_jump; void() mezzo_roar; void() mezzo_in_air; void() mezzo_run_loop; void()mezzo_charge; void mezzo_idle_sound () { string soundstr; if(random()<0.5) soundstr="mezzo/snort.wav"; else soundstr="mezzo/growl.wav"; sound(self,CHAN_VOICE,soundstr,1,ATTN_NORM); } /* void mezzo_possum_up (void)// [-- $death14..$death1] { if (cycle_wrapped) self.think=self.th_run; } void mezzo_playdead (void) { // self.frame=$death14; self.think=mezzo_playdead; thinktime self : 0.1; ai_stand(); } */ void mezzo_roll_right () [-- $Roll18 .. $Roll1] { // if(self.shield.velocity!='0 0 0'&&self.shield.model!="models/null.spr") // dprint("what the?\n"); //SOUND? vector rollangle; makevectors(self.angles); rollangle=vectoangles(v_right); // if(!walkmove(rollangle_y,7,FALSE)&&self.frame>$Roll5 &&self.flags&FL_ONGROUND) // self.frame=$Roll5; walkmove(rollangle_y,7,FALSE); if(cycle_wrapped) { thinktime self : 0; if(!self.flags&FL_ONGROUND) self.think=mezzo_in_air; else self.think=self.th_run; } } void mezzo_roll_left () [++ $Roll1 .. $Roll18] { // if(self.shield.velocity!='0 0 0'&&self.shield.model!="models/null.spr") // dprint("what the?\n"); //SOUND? vector rollangle; makevectors(self.angles); rollangle=vectoangles(v_right); // if(!walkmove(rollangle_y,-7,FALSE)&&self.frame<$Roll14 &&self.flags&FL_ONGROUND) // self.frame=$Roll14; walkmove(rollangle_y,-7,FALSE); if(cycle_wrapped) { thinktime self : 0; if(!self.flags&FL_ONGROUND) self.think=mezzo_in_air; else self.think=self.th_run; } } void mezzo_roll_forward () [++ $dive1 .. $dive18] { //SOUND? //vector rollangle; // if(!walkmove(self.angles_y,7,FALSE)&&self.frame<$dive12 &&self.flags&FL_ONGROUND) // self.frame=$dive12; if(!self.flags&FL_ONGROUND) { if(!infront(self.enemy))//stay facing enemy so if land behind him, will be facing him ai_face(); } else { if(self.dflags) { sound(self,CHAN_BODY,"player/land.wav",1,ATTN_NORM); self.dflags=FALSE; } walkmove(self.angles_y,7,FALSE); } if(cycle_wrapped) { thinktime self : 0; if(!self.flags&FL_ONGROUND) self.think=mezzo_in_air; else self.think=self.th_run; } } void mezzo_duck () [++ $jump13 .. $jump22] { //FIXME: Have him keep checking for befense when staying down for .5 sec vector newmaxs; if(self.frame==$jump14) { newmaxs=self.maxs; newmaxs_z=self.maxs_z*0.5; setsize(self,self.mins,newmaxs); } else if(self.frame==$jump18) { newmaxs=self.maxs; newmaxs_z=self.maxs_z*2; setsize(self,self.mins,newmaxs); } else if(self.frame==$jump16) thinktime self : 0.5; else if(cycle_wrapped) { thinktime self : 0; self.think=self.th_run; } } float mezzo_check_duck (entity proj) { vector proj_mins,duck_hite,proj_dir; proj_mins=proj.origin; proj_mins_z=proj.origin_z - proj.mins_z; duck_hite=self.origin; duck_hite_z=self.origin_z + self.maxs_z/2; proj_dir=normalize(duck_hite-proj_mins); traceline(proj_mins,duck_hite+proj_dir*8,FALSE,self); if(trace_ent!=self||trace_endpos_z>duck_hite_z) return TRUE; else return FALSE; } float mezzo_check_jump (entity proj) { float impact_hite, jump_hite; vector proj_dir, proj_top; if(!self.flags&FL_ONGROUND) return FALSE; proj_dir=normalize(proj.velocity); proj_top=proj.origin; proj_top_z=proj.absmax_z; traceline(proj_top,proj_top+proj_dir*1000,FALSE,proj); if(trace_ent!=self) return FALSE; impact_hite=trace_endpos_z; tracearea(self.origin,self.origin+'0 0 256',self.mins,self.maxs,FALSE,self); jump_hite=trace_fraction*256; if(jump_hite<24) return FALSE; else if(jump_hite>133) jump_hite=133; else if(jump_hite<77) jump_hite=77; if(self.origin_z+jump_hite/2>impact_hite+proj.maxs_z&&random()<0.7) { self.velocity_z=jump_hite*3; self.flags(-)FL_ONGROUND; return TRUE; } return FALSE; } void mezzo_choose_roll (entity proj) { float proj_dir; proj_dir=check_heading_left_or_right(proj); if(proj_dir==0) { if(mezzo_check_duck(proj)) { thinktime self : 0; self.think=mezzo_duck; } else { thinktime self : 0; self.think=mezzo_block; } return; } else { //FIXME: Probably shouldn't try to roll in the other direction makevectors(self.angles); if(solid_under(self.origin, self.origin+v_right*105*proj_dir)) tracearea(self.origin, self.origin+v_right*105*proj_dir,self.mins,self.maxs,FALSE,self); else trace_fraction=FALSE; if(trace_fraction==1) { traceline(trace_endpos, trace_endpos-'0 0 300',TRUE,self); if(pointcontents(trace_endpos)!=CONTENT_EMPTY) trace_fraction=FALSE; else trace_fraction=TRUE; } if(trace_fraction==1) { if(proj_dir>0) { thinktime self : 0; self.think=mezzo_roll_right; } else { thinktime self : 0; self.think=mezzo_roll_left; } return; } else if(mezzo_check_duck(proj)) { thinktime self : 0; self.think=mezzo_duck; } else { thinktime self : 0; self.think=mezzo_block; } /* { tracearea(self.origin, self.origin+v_right*36*proj_dir*-1,self.mins,self.maxs,FALSE,self); if(trace_fraction==1) { if(proj_dir*-1>0) { thinktime self : 0; self.think=mezzo_roll_right; } else { thinktime self : 0; self.think=mezzo_roll_left; } return; } } */ } } void mezzo_check_defense () { // if(self.shield.velocity!='0 0 0'&&self.shield.model!="models/null.spr") // dprint("what the?\n"); //NOTE: Add random chance of failure based on difficulty level - highest diff means no chance of failure here if(skill+self.skin/5=0) self.think=mezzo_duck; else if(self.think==mezzo_block_wait) { self.t_width=time+1; return; } else if(self.think==mezzo_run_loop||random()<0.3) self.think=mezzo_charge; else self.think=mezzo_block; return; } else if(random()<0.5) mezzo_choose_roll(enemy_proj); else if(random()<0.7||self.enemy.v_angle_x>=0) self.think=mezzo_duck; else if(self.think==mezzo_block_wait) { self.t_width=time+1; return; } else if(self.think==mezzo_block_wait) { self.t_width=time+1; return; } else if(self.think==mezzo_run_loop||random()<0.3) self.think=mezzo_charge; else self.think=mezzo_block; return; } if(self.level>0.3)//I've got 0.3 seconds before impact { mezzo_choose_roll(enemy_proj); return; } else if(mezzo_check_duck(enemy_proj)) { thinktime self : 0; tracearea(self.origin,self.origin+v_forward*64,self.mins,'16 16 20',FALSE,self); if(trace_fraction<1||random()<0.2||!infront(enemy_proj)) self.think=mezzo_duck; else self.think=mezzo_roll_forward; return; } else if(mezzo_check_jump(enemy_proj)) { self.think=mezzo_jump; enemy_infront=infront(self.enemy); enemy_vis=visible(self.enemy); trace_fraction=0; if((random()<0.3||self.think==mezzo_run_loop)&&enemy_infront&&enemy_vis&&self.enemy!=world)//Jump towards enemy { vector enemy_dir; enemy_dir=normalize(self.enemy.origin-self.origin); traceline(self.origin,self.origin+enemy_dir*64+'0 0 56',FALSE,self); if(trace_fraction==1) { traceline(trace_endpos,trace_endpos-'0 0 300',TRUE,self); if(pointcontents(trace_endpos)==CONTENT_EMPTY) { self.velocity_x=self.velocity_y=0; self.velocity+=enemy_dir*vlen(self.enemy.origin-self.origin); trace_fraction=1; if(random()<0.7) self.think=mezzo_roll_forward; else self.think=self.th_jump; } else { // dprint("might land in water or lava\n"); trace_fraction=0; } } else { // dprint("not enough room in front \n"); trace_fraction=0; } } // dprint(ftos(trace_fraction)); // dprint(" is the trace_fraction\n"); if(random()<0.5&&trace_fraction<1)//Jump to side { // dprint("checking to sides\n"); if(random()<0.5) r=1; else r=-1; makevectors(self.angles); traceline(self.origin,self.origin+v_right*36*r+'0 0 56',FALSE,self); if(trace_fraction<1) { traceline(self.origin,self.origin-v_right*36*r+'0 0 56',FALSE,self); if(trace_fraction<1) { // dprint("not enough room to jump on that side\n"); self.think=mezzo_jump; } else { traceline(trace_endpos,trace_endpos-'0 0 300',TRUE,self); if(pointcontents(trace_endpos)!=CONTENT_EMPTY) { // dprint("might jump into water or lava\n"); self.think=mezzo_jump; } else { self.velocity-=v_right*r*200; if(r*-1>0) self.think=mezzo_roll_right; else self.think=mezzo_roll_left; } } } else { traceline(trace_endpos,trace_endpos-'0 0 300',TRUE,self); if(pointcontents(trace_endpos)!=CONTENT_EMPTY) { // dprint("might jump into water or lava\n"); self.think=mezzo_jump; } else { self.velocity+=v_right*r*200; if(r>0) self.think=mezzo_roll_right; else self.think=mezzo_roll_left; } } } thinktime self : 0; if(self.think!=mezzo_jump&&self.think!=mezzo_roll_right&&self.think!=mezzo_roll_left&&self.think!=mezzo_roll_forward) { // dprint("What the FUCK!!!\n"); self.think=mezzo_jump; } // return; } else if(infront(enemy_proj)&&random()<0.5) { thinktime self : 0; if(self.think==mezzo_block_wait) { self.t_width=time+1; return; } else if(self.think==mezzo_run_loop||random()<0.3) self.think=mezzo_charge; else self.think=mezzo_block; return; } } void() mezzo_charge_stop; void mezzo_slam () { if(!other.movetype||other.mass>100||other.solid==SOLID_BSP) return; if(!infront(other)||other.safe_time>time) return; if(other.origin_z>self.absmax_z - 6||other.absmax_ztime) return; if(!self.owner) // fix the "assignment to world entity" bug { remove(self); return; } if(!self.owner.flags2&FL_ALIVE||self.owner.frozen>0) { if(self.owner.movechain==self) self.owner.movechain=world; remove(self); // return; // fix the "assignment to world entity" bug } if(other.classname=="funnal"||other.classname=="tornato") return; dir = normalize(other.velocity); magnitude=vlen(other.velocity); org = other.origin; vec = org + dir*100; traceline (org, vec, FALSE, other); if(trace_ent!=self.owner) return; if(self.owner.classname=="monster_mezzoman") sound(self,CHAN_AUTO,"mezzo/slam.wav",1,ATTN_NORM); if(!self.owner.skin&&self.owner.classname=="monster_mezzoman") {//Just block it if(!other.flags2&FL_ALIVE) other.flags2(+)FL_NODAMAGE; } else {//reflect! if(self.owner.classname!="monster_mezzoman") { sound (self, CHAN_WEAPON, "fangel/deflect.wav", 1, ATTN_NORM); CreateWhiteFlash(trace_endpos); if(self.owner.classname=="monster_fallen_angel") { dir=dir*-1; makevectors(dir); dir=v_forward + v_up*random(-0.75,.75) + v_right*random(-0.75,.75); dir=normalize(dir); } else// if(visible(other.owner)) { v_forward=normalize(other.owner.origin+other.owner.view_ofs-other.origin); dir+= 2*v_forward; dir=normalize(dir); } // else // dir=dir*-1; } else { sound(self,CHAN_AUTO,"mezzo/reflect.wav",1,ATTN_NORM); starteffect(CE_MEZZO_REFLECT,self.origin); makevectors(trace_ent.angles); dir+= 2*v_forward; dir=normalize(dir); } if(other.movedir) other.movedir=dir; if(other.o_angle) other.o_angle=dir; if(magnitude20) zofs=20; else if(zofs<-20) zofs=-20; traceline(self.origin+'0 0 30',self.origin+'0 0 30'+v_forward*36+v_up*zofs,FALSE,self); if(trace_fraction==1) return; sound(self,CHAN_VOICE,"mezzo/slam.wav",1,ATTN_NORM); if(trace_ent.movetype&&trace_ent.movetype!=MOVETYPE_PUSH) trace_ent.velocity+=v_forward*200-v_right*100+'0 0 100'; if(trace_ent.takedamage) T_Damage(trace_ent,self,self,5*(self.skin+1)*(self.aflag+1)*(coop + 1)); if(trace_ent.classname=="player") if(infront_of_ent(self,trace_ent)) trace_ent.punchangle_y=4; } else if(cycle_wrapped) { self.attack_finished=time+0.5; thinktime self : 0; self.think=self.th_run; } else if(self.frame==$clober1) { self.last_attack=time; if(random()<0.5) sound(self,CHAN_VOICE,"mezzo/attack.wav",1,ATTN_NORM); } } void mezzo_sword() [++ $sword1 .. $sword13] { ai_face(); ai_charge(3); if(cycle_wrapped) { self.attack_finished=time+0.3; thinktime self : 0; self.think=self.th_run; } else if(self.frame==$sword1) { self.last_attack=time; sound(self,CHAN_WEAPON,"weapons/vorpswng.wav",1,ATTN_NORM); if(random()<0.5) sound(self,CHAN_VOICE,"mezzo/attack.wav",1,ATTN_NORM); } else if(self.frame>=$sword6 && self.frame<=$sword10) { float ofs,zofs; vector dir; makevectors(self.angles); ofs=($sword10 - self.frame)*4; dir_z=ofs - 8; dir+=v_right*(ofs - 8)+v_forward*(48 - fabs(16 - ofs)); dir=normalize(dir); zofs = self.enemy.origin_z - self.origin_z; if(zofs>20) zofs=20; else if(zofs<-20) zofs=-20; traceline(self.origin+'0 0 37',self.origin+'0 0 37'+dir*48+v_up*zofs,FALSE,self); if(trace_fraction==1) return; if(self.t_widthdamage*3||self.pain_finished>time)//only react to 33 percent of current health damage return; self.monster_awake=TRUE; if(self.shield) if(self.shield.classname=="mezzo_reflect" && self.shield.owner==self) remove(self.shield); if(self.health<=100) { self.th_pain=SUB_Null; if(self.health<=100) { if(random()<0.5) { self.oldthink=self.th_run; self.think=mezzo_roar; self.speed=15; self.yaw_speed=20; self.aflag=TRUE;//Berzerk! } else if(!self.flags&FL_ONGROUND) self.think=mezzo_in_air; else self.think=self.th_run; } } else { sound(self,CHAN_VOICE,"mezzo/pain.wav",1,ATTN_NORM); if(!self.enemy||!visible(self.enemy)) { if(self.enemy!=world&&self.enemy!=attacker) self.oldenemy=self.enemy; self.enemy=attacker; } self.pain_finished=time+1+self.skin; self.think=mezzo_pain_seq; } thinktime self : 0; } void mezzo_land () [++ $jump13 .. $jump22] { //SOUND? // dprint("landing\n"); if(cycle_wrapped) { thinktime self : 0; self.think=self.th_run; } else if(self.frame==$jump13) sound(self,CHAN_BODY,"player/land.wav",1,ATTN_NORM); } void mezzo_in_air () { // dprint("in air\n"); self.frame=$jump12; if(self.flags&FL_ONGROUND) { thinktime self : 0; self.think=mezzo_land; } else { if(self.velocity=='0 0 0') self.velocity='0 0 -60'; self.think=mezzo_in_air; if(vlen(self.velocity)>300) { if(random()<0.5) { self.dflags=TRUE;//in air self.think=mezzo_roll_forward; } } thinktime self : 0.05; } } void mezzo_jump () [++ $jump1 .. $jump11] { //SOUND? // dprint("jumping\n"); ai_face(); if(self.flags&FL_ONGROUND) { thinktime self : 0; self.think=mezzo_land; } else if(self.frame==$jump11) { thinktime self : 0.05; self.think=mezzo_in_air; } } void mezzo_charge_stop () [++ $charge15 .. $charge25] { if(cycle_wrapped) { self.touch=obj_push; thinktime self : 0; self.think=self.th_run; } if(!walkmove(self.angles_y,$charge25 - self.frame,FALSE)) { if(!self.ltime) self.think=mezzo_pain_seq; else self.think=self.th_run; thinktime self : 0; } } void mezzo_charge_leap () [++ $charge9 .. $charge14] { //SOUND? if(cycle_wrapped) { thinktime self : 0; self.think=mezzo_charge_stop; } else if(self.frame==$charge9) { makevectors(self.angles); traceline(self.origin+'0 0 25',self.origin+'0 0 25'+v_forward*256,FALSE,self); //Used to make him not charge if you stepped aside, now //only checks if will fall if(!trace_ent.takedamage) { self.think=mezzo_skid; thinktime self : 0; } else { traceline(trace_endpos,trace_endpos-'0 0 300',TRUE,self); if(pointcontents(trace_endpos)!=CONTENT_EMPTY||trace_fraction==1) { self.think=mezzo_skid; thinktime self : 0; } else if(self.flags&FL_ONGROUND) { if(random()<0.5) sound(self,CHAN_VOICE,"mezzo/attack.wav",1,ATTN_NORM); self.velocity=v_forward*700+'0 0 133'; self.flags(-)FL_ONGROUND; } else { self.think=mezzo_in_air; thinktime self : 0; } } } } void mezzo_charge () [++ $charge1 .. $charge8] { if(cycle_wrapped) { thinktime self : 0; self.think=mezzo_charge_leap; } else if(self.frame==$charge1) { self.last_attack=time; self.ltime=0; self.touch=mezzo_slam; self.attack_finished=time+1.25; } walkmove(self.angles_y,15,FALSE); } void mezzo_block_return () [-- $block6 .. $block1] { // if(self.shield.velocity!='0 0 0'&&self.shield.model!="models/null.spr") // dprint("what the?\n"); if(cycle_wrapped) { float r; if(self.shield) if(self.shield.classname=="mezzo_reflect" && self.shield.owner==self) remove(self.shield); r=vlen(self.enemy.origin-self.origin); if(infront(self.enemy)&&r<100) { thinktime self : 0; self.think=self.th_melee; } // else if(random()<0.2&&r<177) // { // thinktime self : 0; // self.think=self.th_stand; // } else { thinktime self : 0; self.think=self.th_run; } } } void mezzo_block_wait () { // if(self.shield.velocity!='0 0 0'&&self.shield.model!="models/null.spr") // dprint("what the?\n"); self.think=mezzo_block_wait; if(self.t_width77) self.th_pain=mezzo_pain; self.t_width=time+1; thinktime self : 0; self.think=mezzo_block_wait; } else if(self.frame==$block1) spawn_reflect(); } void mezzo_skid () [++ $block1 .. $block6] { float skidspeed, anim_stretch; anim_stretch = 3; skidspeed=$block6 - self.frame + anim_stretch - self.level; if(walkmove(self.angles_y,skidspeed*2,FALSE)) { particle(self.origin, '0 0 20'*skidspeed, 344, skidspeed); if(random()<0.2) CreateWhiteSmoke(self.origin,'0 0 8',HX_FRAME_TIME * 2); } else { thinktime self : 0; self.think=mezzo_block_return; return; } if(cycle_wrapped) { self.attack_finished=time+3; thinktime self : 0; self.think=mezzo_block_return; } else if(self.frame==$block1) { spawn_reflect(); sound(self,CHAN_AUTO,"mezzo/skid.wav",1,ATTN_NORM); } else if(self.level33) { if(random()<0.5&&lineofsight(self.enemy,self)) { thinktime self : 0; self.think=mezzo_charge; } else if(dist>84&&self.last_attackRANGE_NEAR&&random()>0.3) { self.oldthink=self.th_run; self.think=mezzo_roar; thinktime self : 0; return; } else { sound(self,CHAN_VOICE,"mezzo/attack.wav",1,ATTN_NORM); self.monster_awake=TRUE; } if(cycle_wrapped) { self.think=mezzo_run_loop; thinktime self : 0; } else mezzo_run_think(); } void mezzo_walk () [++ $walk1 .. $walk30] { if(self.frame==$stand3 &&random()<0.1) mezzo_idle_sound(); mezzo_check_defense(); ai_walk(3); if(self.enemy) if(CheckAnyAttack()) return; } void()mezzo_stand; void mezzo_twirl() [++ $twirl1 .. $twirl10] { mezzo_check_defense(); if(cycle_wrapped) { self.think=mezzo_stand; thinktime self : 0; } else if(self.frame==$twirl1) { sound(self,CHAN_WEAPON,"weapons/vorpswng.wav",0.7,ATTN_NORM); if(random()<0.5) mezzo_idle_sound(); } ai_stand(); } void mezzo_stand2 () [-- $stand10 .. $stand1] { mezzo_check_defense(); if(self.level<3&&self.frame<$stand10) { self.level+=1; self.frame+=1; } else self.level=0; if(self.frame==$stand1) { if(random()<0.1||(self.monster_awake&&random()<0.5)) self.think=mezzo_twirl; else self.think=mezzo_stand; thinktime self : 0; return; } else if(self.frame==$stand10 &&random()<0.1) mezzo_idle_sound(); /* if(self.monster_awake) { float r; r=vlen(self.enemy.origin-self.origin); if(random()<0.1||r>177) { if(r>177) self.think=self.th_run; else if(enemy_infront&&enemy_vis&&r<133) self.think=mezzo_charge; else if(random()<0.5) self.think=self.th_run; else self.think=self.th_walk; thinktime self : 0; } else { if(random()<0.8&&r>100) self.attack_finished=time+0.1; ai_run(0); } } else */ ai_stand(); } void mezzo_stand () [++ $stand1 .. $stand10] { if(random()<0.5) { mezzo_check_defense(); if((!self.enemy.flags2&FL_ALIVE&&self.enemy!=world)||self.enemy==world) { self.monster_awake=FALSE; if(self.oldenemy.flags2&FL_ALIVE) { self.enemy=self.oldenemy; self.oldenemy=world; } else self.enemy=world; } } if(self.level<3&&self.frame>$stand1) { self.level+=1; self.frame-=1; } else self.level=0; if(self.frame==$stand10) { self.think=mezzo_stand2; thinktime self : 0; return; } else if(self.frame==$stand1 &&random()<0.1) mezzo_idle_sound(); if(random()<0.5) ai_stand(); } /*QUAKED monster_werejaguar (1 0.3 0) (-16 -16 0) (16 16 56) AMBUSH STUCK JUMP PLAY_DEAD DORMANT WereCat with jaguar skin If they're targetted by a trigger and used, they'll atack the activator of the target If they can't see the activator, they'll roll left or right (in the direction of the activator) Their roll is 128 units long, so place the mezzoman 128 units away from where you want the roll to stop My babies!!! - MG */ //IDEA: Have mezzoman do ai_face while in air or rolling so always faces you when lands/gets up? void() monster_werejaguar = { if (deathmatch) { remove(self); return; } if (!self.flags2&FL_SUMMONED) { precache_model2 ("models/mezzoman.mdl"); precache_model2 ("models/mezzoref.spr"); precache_model2 ("models/h_mez.mdl"); precache_sound2 ("mezzo/skid.wav"); precache_sound2 ("mezzo/roar.wav"); precache_sound2 ("mezzo/reflect.wav"); precache_sound2 ("mezzo/slam.wav"); precache_sound2 ("mezzo/pain.wav"); precache_sound2 ("mezzo/die.wav"); precache_sound2 ("mezzo/growl.wav"); precache_sound2 ("mezzo/snort.wav"); precache_sound2 ("mezzo/attack.wav"); } self.solid = SOLID_SLIDEBOX; self.takedamage=DAMAGE_YES; self.thingtype=THINGTYPE_FLESH; self.movetype = MOVETYPE_STEP; self.view_ofs = '0 0 53'; self.speed=10; self.yaw_speed = 10; self.health = 250; self.experience_value = 150; self.monsterclass = CLASS_HENCHMAN; self.mass = 10; self.mintel = 15;//Animal sense of smell makes him a good tracker if(self.classname=="monster_werepanther") { self.monsterclass = CLASS_LEADER; self.experience_value = 300; self.health=400; self.skin=1; } self.classname="monster_mezzoman"; self.th_stand=mezzo_stand; self.th_walk=mezzo_walk; self.th_run=mezzo_run; self.th_pain=mezzo_pain; self.th_melee=mezzo_melee; self.th_missile=mezzo_missile; self.th_jump=mezzo_jump; self.th_die=mezzo_die; // self.th_possum = mezzo_playdead; // self.th_possum_up = mezzo_possum_up; self.spawnflags (+) JUMP; setmodel (self, "models/mezzoman.mdl"); self.headmodel="models/h_mez.mdl"; setsize (self, '-16 -16 0', '16 16 56'); self.frame=$stand1; walkmonster_start(); }; void monster_mezzoman (void) { monster_werejaguar(); } /*QUAKED monster_werepanther (1 0.3 0) (-16 -16 0) (16 16 56) AMBUSH STUCK JUMP PLAY_DEAD DORMANT WereCat with panther skin If they're targetted by a trigger and used, they'll atack the activator of the target If they can't see the activator, they'll roll left or right (in the direction of the activator) Their roll is 128 units long, so place the mezzoman 128 units away from where you want the roll to stop My babies!!! - MG */ void monster_werepanther (void) { monster_werejaguar(); } gamecode/hc/h2/misc.hc000066400000000000000000000656131444734033100150120ustar00rootroot00000000000000/*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4) Used as a positional target for spotlights, etc. */ void info_null() { remove(self); } float ROTATE_BREAK = 16; /* //============================================================================ float POWERPOOL_HEALTH = 1; float POWERPOOL_EXPERIENCE = 2; float POWERPOOL_GREENMANA = 4; float POWERPOOL_BLUEMANA = 8; void power_pool_touch() { if(time < self.ltime) return; self.cnt = self.cnt + 1; if(self.cnt > self.count) { self.touch = SUB_Null; return; } if(self.spawnflags & POWERPOOL_HEALTH) other.health = other.health + 1; if(self.spawnflags & POWERPOOL_EXPERIENCE) other.experience = other.experience + 1; if(self.spawnflags & POWERPOOL_GREENMANA) other.greenmana = other.greenmana + 1; if(self.spawnflags & POWERPOOL_BLUEMANA) other.bluemana = other.bluemana + 1; self.ltime = time + 0.15; } //QUAKED power_pool (0 1 0) ? HEALTH EXPERIENCE GREENMANA BLUEMANA //Power pool. You can pick whatever combination of benefits you would like. void power_pool() { if(!self.spawnflags) { remove(self); return; } self.touch = power_pool_touch; self.solid = SOLID_TRIGGER; if(!self.count) self.count = 5; } */ //============================================================================ //Launches a nail in a offset spread (jweier) void launch_spread(float offset) { local vector offang; local vector org, vec; local entity mis; org = self.origin; offang = vectoangles (self.movedir - org); offang_y = offang_y + offset * 6; makevectors (offang); vec = normalize (v_forward); vec_z = 0; mis = spawn (); mis.owner = self; mis.movetype = MOVETYPE_FLYMISSILE; mis.solid = SOLID_BBOX; mis.angles = vectoangles(vec); mis.touch = spike_touch; mis.classname = "spike"; mis.think = SUB_Remove; thinktime mis : 6; setmodel (mis, "models/spike.mdl"); setsize (mis, VEC_ORIGIN, VEC_ORIGIN); setorigin (mis, org); mis.velocity = vec * 1000; } //============================================================================ //============================================================================ /*QUAKED misc_fireball (0 .5 .8) (-8 -8 -8) (8 8 8) Lava Balls */ void fire_fly(); void fire_touch(); void misc_fireball() { precache_model ("models/lavaball.mdl"); self.classname = "fireball"; thinktime self : random(5); self.think = fire_fly; if (!self.speed) self.speed = 1000; } void fire_fly() { local entity fireball; fireball = spawn(); fireball.solid = SOLID_TRIGGER; fireball.movetype = MOVETYPE_TOSS; fireball.velocity = '0 0 1000'; fireball.velocity=RandomVector('50 50 0'); fireball.velocity_z = self.speed + random(200); fireball.classname = "fireball"; setmodel (fireball, "models/lavaball.mdl"); setsize (fireball, '0 0 0', '0 0 0'); setorigin (fireball, self.origin); thinktime fireball : 5; fireball.think = SUB_Remove; fireball.touch = fire_touch; thinktime self : random(3,8); self.think = fire_fly; } void fire_touch() { T_Damage (other, self, self, 20); remove(self); } //============================================================================ /* void() barrel_explode = { self.takedamage = DAMAGE_NO; self.classname = "explo_box"; // did say self.owner T_RadiusDamage (self, self, 160, world); sound (self, CHAN_VOICE, "weapons/explode.wav", 1, ATTN_NORM); particle (self.origin, '0 0 0', 75, 255); self.origin_z = self.origin_z + 32; BecomeExplosion (FALSE); }; /*QUAK-ED misc_explobox (0 .5 .8) (0 0 0) (32 32 64) TESTING THING */ /* void() misc_explobox = { local float oldz; self.solid = SOLID_BBOX; self.movetype = MOVETYPE_NONE; //rj precache_model ("maps/b_explob.bsp"); setmodel (self, "maps/b_explob.bsp"); precache_sound ("weapons/explode.wav"); self.health = 20; self.th_die = barrel_explode; self.takedamage = DAMAGE_YES; self.origin_z = self.origin_z + 2; oldz = self.origin_z; droptofloor(); if (oldz - self.origin_z > 250) { dprint ("item fell out of level at "); dprint (vtos(self.origin)); dprint ("\n"); remove(self); } }; */ /*QUAK-ED misc_explobox2 (0 .5 .8) (0 0 0) (32 32 64) Smaller exploding box, REGISTERED ONLY * /* void() misc_explobox2 = { local float oldz; self.solid = SOLID_BBOX; self.movetype = MOVETYPE_NONE; //rj precache_model2 ("maps/b_exbox2.bsp"); setmodel (self, "maps/b_exbox2.bsp"); precache_sound ("weapons/explode.wav"); self.health = 20; self.th_die = barrel_explode; self.takedamage = DAMAGE_YES; self.origin_z = self.origin_z + 2; oldz = self.origin_z; droptofloor(); if (oldz - self.origin_z > 250) { dprint ("item fell out of level at "); dprint (vtos(self.origin)); dprint ("\n"); remove(self); } }; */ //============================================================================ float SPAWNFLAG_SUPERSPIKE = 1; float SPAWNFLAG_LASER = 2; /* void Laser_Touch() { local vector org; if (other == self.owner) return; // don't explode on owner if (pointcontents(self.origin) == CONTENT_SKY) { remove(self); return; } //sound (self, CHAN_WEAPON, "enforcer/enfstop.wav", 1, ATTN_STATIC); org = self.origin - 8*normalize(self.velocity); if (other.health) { SpawnPuff (org, self.velocity*0.2, 15,other); T_Damage (other, self, self.owner, 15); } else { WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); WriteByte (MSG_BROADCAST, TE_GUNSHOT); WriteCoord (MSG_BROADCAST, org_x); WriteCoord (MSG_BROADCAST, org_y); WriteCoord (MSG_BROADCAST, org_z); } remove(self); } void LaunchLaser(vector org, vector vec) { local vector vec; vec = normalize(vec); newmis = spawn(); newmis.owner = self; newmis.movetype = MOVETYPE_FLY; newmis.solid = SOLID_BBOX; newmis.effects = EF_DIMLIGHT; setmodel (newmis, "models/javproj.mdl"); setsize (newmis, '0 0 0', '0 0 0'); setorigin (newmis, org); newmis.velocity = vec * 600; newmis.angles = vectoangles(newmis.velocity); newmis.angles_y = newmis.angles_y + 30; thinktime newmis : 5; newmis.think = SUB_Remove; newmis.touch = Laser_Touch; } */ void spikeshooter_use() { self.enemy = other.enemy; /* if (self.spawnflags & SPAWNFLAG_LASER) { sound (self, CHAN_VOICE, "enforcer/enfire.wav", 1, ATTN_NORM); LaunchLaser (self.origin, self.movedir); } else {*/ sound (self, CHAN_VOICE, "weapons/spike2.wav", 1, ATTN_NORM); launch_spike (self.origin, self.movedir); newmis.velocity = self.movedir * 500; if (self.spawnflags & SPAWNFLAG_SUPERSPIKE) // newmis.touch = superspike_touch; newmis.touch = spike_touch; // } } void shooter_think() { spikeshooter_use (); thinktime self : self.wait; //newmis.velocity = self.velocity * 500; } void sprayshooter_use() { sound (self, CHAN_VOICE, "weapons/spike2.wav", 1, ATTN_NORM); launch_spread(random(10)); } void sprayshooter_think() { sprayshooter_use (); thinktime self : self.wait; } /*QUAKED trap_spikeshooter_spray (0 .5 .8) (-8 -8 -8) (8 8 8) When triggered, fires a spike in the direction set in QuakeEd. */ void trap_spikeshooter_spray() { SetMovedir (); self.use = sprayshooter_use; precache_sound ("weapons/spike2.wav"); if (self.wait == 0) self.wait = 1; self.nextthink = self.nextthink + self.wait + self.ltime; self.think = sprayshooter_think; } /*QUAKED trap_spikeshooter (0 .5 .8) (-8 -8 -8) (8 8 8) superspike laser When triggered, fires a spike in the direction set in QuakeEd. Laser is only for REGISTERED. */ void trap_spikeshooter() { SetMovedir (); self.use = spikeshooter_use; /* if (self.spawnflags & SPAWNFLAG_LASER) { // precache_model2 ("models/laser.mdl"); //precache_sound2 ("enforcer/enfire.wav"); //precache_sound2 ("enforcer/enfstop.wav"); } else*/ precache_sound ("weapons/spike2.wav"); } /*QUAKED trap_shooter (0 .5 .8) (-8 -8 -8) (8 8 8) superspike laser Continuously fires spikes. "wait" time between spike (1.0 default) "nextthink" delay before firing first spike, so multiple shooters can be stagered. */ void trap_shooter() { trap_spikeshooter (); if (self.wait == 0) self.wait = 1; self.nextthink = self.nextthink + self.wait + self.ltime; self.think = shooter_think; } void () trap_lightning_track = { local vector p1,p2; local entity targ; local float len; targ = find (world, classname, "player"); // Get ending point if (!targ) { dprint("No target for lightning"); return; } if (targ.health <= 0) { self.nextthink = -1; return; } sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); p1 = self.origin; p2 = targ.origin; len = vlen(p2 - p1); traceline(p1, p2, TRUE, self); if (len >= self.aflag || trace_fraction < 1) { if (self.wait == -1 || self.spawnflags & 2) self.nextthink = -1; else if (self.wait == 1) thinktime self : random(self.wait,self.wait+2); else thinktime self : self.wait; return; } do_lightning (self,1,0,4, p1, p2, self.dmg); fx_flash (p2); // Flash of light self.think = trap_lightning_track; if (self.wait == -1 || self.spawnflags & 2) self.nextthink = -1; else if (self.wait == 1) thinktime self : random(self.wait,self.wait+2); else thinktime self : self.wait; }; void () trap_lightning_use = { local vector p1,p2; local entity targ; if (!self.target) { dprint("No target for lightning"); return; } targ = find (world, targetname, self.target); // Get ending point if (!targ) { dprint("No target for lightning"); return; } sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); p1 = self.origin; p2 = targ.origin; WriteByte (MSG_ALL, SVC_TEMPENTITY); WriteByte (MSG_ALL, TE_LIGHTNING1); WriteEntity (MSG_ALL, self); WriteCoord (MSG_ALL, p1_x); WriteCoord (MSG_ALL, p1_y); WriteCoord (MSG_ALL, p1_z); WriteCoord (MSG_ALL, p2_x); WriteCoord (MSG_ALL, p2_y); WriteCoord (MSG_ALL, p2_z); LightningDamage (p1, p2, self, self.dmg,"lightning"); fx_flash (p2); // Flash of light }; /*QUAKED trap_lightning (0 1 1) (-8 -8 -8) (8 8 8) TRACK ONCE Generates a bolt of lightning which ends at the weather_lightning_end that is the target -------------------------FIELDS------------------------- noise - sound generated when lightning appears 1 - no sound 2 - lightning (default) wait - time between shots aflag - radius limiter target - be sure to give this a target fx_lightning_end to hit dmg - damage this bolt does -------------------------------------------------------- */ void () trap_lightning = { self.movetype = MOVETYPE_NOCLIP; self.owner = self; self.solid = SOLID_NOT; setorigin (self,self.origin); setmodel (self,self.model); setsize (self,self.mins, self.maxs); if (!self.noise) self.noise = "raven/lightng1.wav"; if (!self.dmg) self.dmg = 10; if (!self.wait) self.wait = 1; if (!self.aflag) self.aflag = 500; self.ltime = time; self.noise = "raven/lightng1.wav"; precache_sound ("raven/lightng1.wav"); if (self.spawnflags & 1) self.use = trap_lightning_track; else self.use = trap_lightning_use; // For triggered lightning }; /*=============================================================================== =============================================================================== */ //void make_bubbles(); void bubble_remove(); void bubble_bob(); /*QUAK-ED air_bubbles (0 .5 .8) (-8 -8 -8) (8 8 8) testing air bubbles */ /* void air_bubbles() { if (deathmatch) { remove (self); return; } precache_model ("models/s_bubble.spr"); thinktime self : 1; self.think = make_bubbles; } void make_bubbles() { local entity bubble; bubble = spawn_temp(); setmodel (bubble, "models/s_bubble.spr"); setorigin (bubble, self.origin); bubble.movetype = MOVETYPE_NOCLIP; bubble.solid = SOLID_NOT; bubble.velocity = '0 0 15'; thinktime bubble : 0.5; bubble.think = bubble_bob; bubble.touch = bubble_remove; bubble.classname = "bubble"; bubble.frame = 0; bubble.cnt = 0; setsize (bubble, '-8 -8 -8', '8 8 8'); thinktime self : random(0.5,1.5); self.think = make_bubbles; } */ void() bubble_split = { local entity bubble; bubble = spawn_temp(); setmodel (bubble, "models/s_bubble.spr"); setorigin (bubble, self.origin); bubble.movetype = MOVETYPE_NOCLIP; bubble.solid = SOLID_NOT; bubble.velocity = self.velocity; thinktime bubble : 0.5; bubble.think = bubble_bob; bubble.touch = bubble_remove; bubble.classname = "bubble"; bubble.frame = 1; bubble.cnt = 10; setsize (bubble, '-8 -8 -8', '8 8 8'); self.frame = 1; self.cnt = 10; if (self.waterlevel != 3) remove (self); }; void() bubble_remove = { if (other.classname == self.classname) { // dprint ("bump"); return; } remove(self); }; void() bubble_bob = { local float rnd1, rnd2, rnd3; local float waterornot; waterornot=pointcontents(self.origin); if (waterornot!=CONTENT_WATER&&waterornot!=CONTENT_SLIME) remove(self); self.cnt = self.cnt + 1; if (self.cnt == 4) bubble_split(); if (self.cnt == 20) remove(self); rnd1 = self.velocity_x + random(-10,10); rnd2 = self.velocity_y + random(-10,10); rnd3 = self.velocity_z + random(10,20); if (rnd1 > 10) rnd1 = 5; if (rnd1 < -10) rnd1 = -5; if (rnd2 > 10) rnd2 = 5; if (rnd2 < -10) rnd2 = -5; if (rnd3 < 10) rnd3 = 15; if (rnd3 > 30) rnd3 = 25; self.velocity_x = rnd1; self.velocity_y = rnd2; self.velocity_z = rnd3; thinktime self : 0.5; self.think = bubble_bob; }; /*~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~> ~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~*/ /*QUAK-ED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8) Just for the debugging level. Don't use */ /* void viewthing() { self.movetype = MOVETYPE_NONE; self.solid = SOLID_NOT; // precache_model ("models/player.mdl"); // setmodel (self, "models/player.mdl"); } */ /* ============================================================================== SIMPLE BMODELS ============================================================================== */ void() func_wall_use = { // change to alternate textures self.frame = 1 - self.frame; }; /*QUAKED func_wall (0 .5 .8) ? TRANSLUCENT This is just a solid wall if not inhibitted TRANSLUCENT - makes it see-through abslight = how bright to make it */ void func_wall() { self.angles = '0 0 0'; self.movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything self.solid = SOLID_BSP; self.classname="solid wall"; self.use = func_wall_use; setmodel (self, self.model); if(self.spawnflags&1) self.drawflags=DRF_TRANSLUCENT; if(self.abslight) self.drawflags(+)MLS_ABSLIGHT; } /*QUAKED func_illusionary (0 .5 .8) ? TRANSLUCENT LIGHT A simple entity that looks solid but lets you walk through it. */ void func_illusionary() { if (self.spawnflags & 1) self.drawflags (+) DRF_TRANSLUCENT; if (self.abslight) self.drawflags (+) MLS_ABSLIGHT; if (self.spawnflags & 2) self.drawflags (+) MLS_ABSLIGHT; self.classname="illusionary wall"; self.angles = '0 0 0'; self.movetype = MOVETYPE_NONE; self.solid = SOLID_NOT; setmodel (self, self.model); makestatic (self); } //============================================================================ /* void noise_think() { thinktime self : 0.5; //sound (self, 1, "enforcer/enfire.wav", 1, ATTN_NORM); //sound (self, 2, "enforcer/enfstop.wav", 1, ATTN_NORM); // sound (self, 3, "enforcer/sight1.wav", 1, ATTN_NORM); // sound (self, 4, "enforcer/sight2.wav", 1, ATTN_NORM); // sound (self, 5, "enforcer/sight3.wav", 1, ATTN_NORM); // sound (self, 6, "enforcer/sight4.wav", 1, ATTN_NORM); // sound (self, 7, "enforcer/pain1.wav", 1, ATTN_NORM); } */ /*QUAKED misc_noisemaker (1 0.5 0) (-10 -10 -10) (10 10 10) For optimzation testing, starts a lot of sounds. */ /*void misc_noisemaker() { //precache_sound2 ("enforcer/enfire.wav"); //precache_sound2 ("enforcer/enfstop.wav"); // precache_sound2 ("enforcer/sight1.wav"); // precache_sound2 ("enforcer/sight2.wav"); // precache_sound2 ("enforcer/sight3.wav"); // precache_sound2 ("enforcer/sight4.wav"); // precache_sound2 ("enforcer/pain1.wav"); // precache_sound2 ("enforcer/pain2.wav"); // precache_sound2 ("enforcer/death1.wav"); // precache_sound2 ("enforcer/idle1.wav"); thinktime self : random(0.1,1.1); self.think = noise_think; } */ /*QUAKED func_rotating (0 .5 .8) ? START_ON REVERSE X_AXIS Y_AXIS BREAK GRADUAL TOGGLE_REVERSE KEEP_START You need to have an origin brush as part of this entity. The center of that brush will be the point around which it is rotated. It will rotate around the Z axis by default. You can check either the X_AXIS or Y_AXIS box to change that. BREAK makes the brush breakable REVERSE will cause the it to rotate in the opposite direction. GRADUAL will make it slowly speed up and slow down. TOGGLE_REVERSE will make it go in the other direction next time it's triggered KEEP_START means it will return to it's starting position when turned off "speed" determines how fast it moves; default value is 100. "dmg" damage to inflict when blocked (2 default) "lifetime" this will make it stop after a while, then start up again after "wait". Default is staying on. "wait" if it has a lifetime, this is how long it will wait to start up again. default is 3 seconds. "thingtype" type of brush (if breakable - default is wood) "health" (if breakable - default is 25) "abslight" - to set the absolute light level "anglespeed" - If using GRADUAL, this will determine how fast the speed up and down will occur. 1 will be very slow, 100 will be instant. (Default is 10) thingtype - type of chunks and sprites it will generate 0 - glass (default) 1 - stone 2 - wood 3 - metal 4 - flesh health - amount of damage item can take. Default is based on thingtype glass - 25 stone - 75 wood - 50 metal - 100 flesh - 30 */ void() rotating_use; void() rotating_touch; void rotate_wait (void) { thinktime self : 10000000000; } void rotate_reset (void) { if(self.wait) { self.think=rotating_use; thinktime self : self.wait; } else { self.think=SUB_Null; self.nextthink=-1; } } void rotate_wait_startpos (void) { if(self.angles==self.o_angle) { self.avelocity='0 0 0'; rotate_reset(); } else thinktime self : 0.05; } void rotate_slowdown (void) { self.level-=(self.speed/self.anglespeed); if((self.dmg==-1||self.dmg==666)&&self.level<100) self.touch=SUB_Null; if(self.level<1||(self.level<=self.speed/self.anglespeed&&self.spawnflags&KEEP_START)) { if(self.spawnflags&KEEP_START) { self.think=rotate_wait_startpos; thinktime self : 0; } else { self.avelocity='0 0 0'; rotate_reset(); } } else { self.avelocity=self.movedir*self.level; self.think=rotate_slowdown; thinktime self : 0.01; } } void rotate_startup (void) { self.level+=(self.speed/self.anglespeed); if((self.dmg==-1||self.dmg==666)&&self.level>=100&&self.touch==SUB_Null) self.touch=rotating_touch; if(self.pain_finished<=time&&self.lifetime) { self.think=rotating_use; thinktime self : 0; return; } if(self.leveltime&&self.lifetime) { self.think=rotating_use; thinktime self : self.pain_finished; return; } else { self.think=rotate_wait; thinktime self : 10000000000; } } } void rotating_use() { if (self.avelocity != '0 0 0') { if(!self.spawnflags&GRADUAL) { self.avelocity='0 0 0'; rotate_reset(); } else if(self.think==rotate_slowdown) return; else { sound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); self.think=rotate_slowdown; thinktime self : 0; } } else { if(self.lifetime) self.pain_finished=time+self.lifetime; if(self.spawnflags&TOGGLE_REVERSE) self.movedir= self.movedir*-1; if(!self.spawnflags&GRADUAL) { self.avelocity = self.movedir * self.speed; self.think=rotating_use; thinktime self : 10000000000; } else { sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); self.think=rotate_startup; thinktime self : 0; } } } void rotating_damage (entity chopped_liver) { if(self.dmg==666) { if(chopped_liver.classname=="player"&&chopped_liver.flags2&FL_ALIVE) { chopped_liver.decap=TRUE; T_Damage (chopped_liver, self, self, chopped_liver.health+300); } else T_Damage (chopped_liver, self, self, chopped_liver.health+50); } else if(self.dmg==-1&&chopped_liver.health) { float damg; chopped_liver.deathtype="chopped"; damg=vlen(self.avelocity); T_Damage (chopped_liver, self, self, damg); } } void rotating_touch() { if(!other.takedamage) return; rotating_damage(other); } void rotating_blocked (void) { if(!other.takedamage) return; rotating_damage(other); if(other.health>100&&!other.flags2&FL_ALIVE)//allow for blockage { self.avelocity='0 0 0'; self.level=0; self.touch=SUB_Null; self.think=rotating_use; thinktime self : self.wait; } } void func_rotating() { // set the axis of rotation if (self.spawnflags & 4) self.movedir = '0 0 1'; else if (self.spawnflags & 8) self.movedir = '1 0 0'; else self.movedir = '0 1 0'; // check for reverse rotation if (self.spawnflags & 2) self.movedir = self.movedir * -1; if(self.spawnflags&TOGGLE_REVERSE) self.movedir = self.movedir * -1; self.solid = SOLID_BSP; self.movetype = MOVETYPE_PUSH; self.classname="rotating non-door"; setorigin (self, self.origin); setmodel (self, self.model); self.use = rotating_use; self.blocked = rotating_blocked; self.touch=SUB_Null; if (!self.speed) self.speed = 100; if(!self.anglespeed) self.anglespeed = 10; if (self.dmg==0) self.dmg = 2; if(self.lifetime) if(!self.wait) self.wait=3; // self.noise1 = "doors/hydro1.wav"; // self.noise2 = "doors/hydro2.wav"; // precache_sound ("doors/hydro1.wav"); // precache_sound ("doors/hydro2.wav"); if (self.abslight) self.drawflags(+)MLS_ABSLIGHT; if (self.spawnflags & ROTATE_BREAK) { if (!self.thingtype) self.thingtype = THINGTYPE_WOOD; if (!self.health) { if ((self.thingtype == THINGTYPE_GLASS) || (self.thingtype == THINGTYPE_CLEARGLASS)) self.health = 25; else if ((self.thingtype == THINGTYPE_GREYSTONE) || (self.thingtype == THINGTYPE_BROWNSTONE)) self.health = 75; else if (self.thingtype == THINGTYPE_WOOD) self.health = 50; else if (self.thingtype == THINGTYPE_METAL) self.health = 100; else if (self.thingtype == THINGTYPE_FLESH) self.health = 30; else self.health = 25; } self.takedamage = DAMAGE_YES; self.th_die = chunk_death; } if(self.spawnflags&KEEP_START) self.o_angle=self.angles; if (self.spawnflags & 1) self.use(); if (self.flags2) { self.touch = rotating_touch; self.flags2=FALSE; } } /*QUAK-ED trigger_fan_blow (0 .5 .8) ? Will blow anything in the direction of the func_rotating it's targeted by. Note that clockwise rotation pulls you towards it, counterclockwise pushes you away- func_rotating design should match this. To use, target this trigger with with the func_rotating (do NOT target the func_rotating with the trigger!!!). Then place this trigger so that it covers both front and back of the "fan" and extend it as far as you want it to have influence. */ void trigger_find_owner (void) { entity found; found=find(world,target,self.targetname); if(found==world) remove(self); else self.owner=found; } /* void trigger_fan_blow_touch (void) { vector blowdir, org; float blowhard, blowdist; if(other==self.owner) return; if(self.owner.origin=='0 0 0') org=(self.owner.absmin+self.owner.absmax)*0.5; else org=self.owner.origin; if(self.owner.avelocity_x!=0) { //FIXME: Need to cheat here? dilute avelocity? blowhard=self.owner.avelocity_x/3; blowdir = '0 1 0'; blowdist = fabs(org_y - other.origin_y); } else if(self.owner.avelocity_y!=0) { blowhard = self.owner.avelocity_y; blowdir = '0 0 1'; blowdist = fabs(org_z - other.origin_z); } else if(self.owner.avelocity_z!=0) { //FIXME: Need to cheat here? dilute avelocity? blowhard=self.owner.avelocity_z/3; blowdir = '1 0 0'; blowdist = fabs(org_x - other.origin_x); } else return; if(blowdist<100) blowdist=0; if(blowhard>0) { blowhard-=blowdist; if(blowhard<=0) return; } else { blowhard+=blowdist; if(blowhard>=0) return; } blowhard/=10; //FIXME: Factor in mass? blowdir*=blowhard; //FIXME: Need to cheat here? Pull down much faster? if(blowdir_z<0) blowdir*=10; //FIXME: Will actually slow someone down if their already moving in this direction! if(other.velocity!=blowdir) other.velocity+=blowdir; // if(!other.flags&FL_ONGROUND) // { // if(pointcontents(other.origin)==CONTENT_EMPTY&&other.movetype!=MOVETYPE_FLY&&other.movetype!=MOVETYPE_FLYMISSILE&&other.movetype!=MOVETYPE_BOUNCEMISSILE) // other.velocity_z-=60;//FIXME: calculate in gravity too // } // else if(other.flags&FL_ONGROUND) { other.flags(-)FL_ONGROUND; if(other.velocity_z==0) other.velocity_z+=7; } } void trigger_fan_blow (void) { InitTrigger(); self.touch=trigger_fan_blow_touch; self.think=trigger_find_owner; thinktime self : 0.1; } */ void() angletrigger_done = { self.level = FALSE; }; void() angletrigger_blocked = { T_Damage (other, self, self, self.dmg); }; void() angletrigger_use = { vector newvect; if (self.level) return; else self.level = TRUE; if(self.angles_x>=360) self.angles_x-=360; else if(self.angles_x<=-360) self.angles_x+=360; if(self.angles_y>=360) self.angles_y-=360; else if(self.angles_y<=-360) self.angles_y+=360; if(self.angles_z>=360) self.angles_z-=360; else if(self.angles_z<=-360) self.angles_z+=360; newvect = self.movedir * self.cnt; if (self.angles + newvect == self.mangle) { self.check_ok = TRUE; SUB_UseTargets(); } else if (self.check_ok) { self.check_ok = FALSE; SUB_UseTargets(); } SUB_CalcAngleMove(self.angles + newvect, self.speed, angletrigger_done); }; /*QUAKED func_angletrigger (0 .5 .8) ? REVERSE X_AXIS Y_AXIS Rotates at certain intervals, and fires off when a set angle is met mangle = desired angle to trigger at (relative to the world!) cnt = degrees to turn each move dmg = damage if blocked */ void() func_angletrigger = { // set the axis of rotation if (self.spawnflags & 2) self.movedir = '0 0 1'; else if (self.spawnflags & 4) self.movedir = '1 0 0'; else self.movedir = '0 1 0'; // check for clockwise rotation if (self.spawnflags & 1) self.movedir = self.movedir*-1; self.pos1 = self.angles; self.pos2 = self.angles + self.movedir * self.cnt; self.max_health = self.health; self.solid = SOLID_BSP; self.movetype = MOVETYPE_PUSH; setorigin (self, self.origin); setmodel (self, self.model); self.classname = "angletrigger"; if (self.abslight) self.drawflags(+)MLS_ABSLIGHT; if (!self.speed) self.speed = 100; if (self.wait==0) self.wait = 1; if (!self.dmg) self.dmg = 2; /* precache_sound ("doors/hydro1.wav"); precache_sound ("doors/hydro2.wav"); precache_sound ("doors/basetry.wav"); precache_sound ("doors/baseuse.wav"); self.noise2 = "doors/hydro1.wav"; self.noise1 = "doors/hydro2.wav"; self.noise3 = "doors/basetry.wav"; self.noise4 = "doors/baseuse.wav"; */ self.blocked = angletrigger_blocked; self.use = angletrigger_use; if (!self.targetname) self.touch = angletrigger_use; self.inactive = FALSE; }; gamecode/hc/h2/monsters.hc000066400000000000000000000142031444734033100157160ustar00rootroot00000000000000/* ALL MONSTERS SHOULD BE 1 0 0 IN COLOR */ // name =[framenum, nexttime, nextthink] {code} // expands to: // name () // { // self.frame=framenum; // self.nextthink = time + nexttime; // self.think = nextthink // // }; /* ================ monster_use Using a monster makes it angry at the current activator ================ */ void() monster_use = { if (self.enemy) return; if (self.health <= 0) return; if (activator.items & IT_INVISIBILITY) return; if (activator.flags & FL_NOTARGET) return; if (activator.classname != "player") return; if(self.classname=="monster_mezzoman"&&!visible(activator)&&!self.monster_awake) { self.enemy=activator; mezzo_choose_roll(activator); return; } // delay reaction so if the monster is teleported, its sound is still // heard else { self.enemy = activator; thinktime self : 0.1; self.think = FoundTarget; } }; /* ================ monster_death_use When a mosnter dies, it fires all of its targets with the current enemy as activator. ================ */ void() monster_death_use = { // fall to ground self.flags(-)FL_FLY; self.flags(-)FL_SWIM; if (!self.target) return; activator = self.enemy; SUB_UseTargets (); }; //============================================================================ void() walkmonster_start_go = { if(!self.touch) self.touch=obj_push; if(!self.spawnflags&NO_DROP) { self.origin_z = self.origin_z + 1; // raise off floor a bit droptofloor(); if (!walkmove(0,0, FALSE)) { if(self.flags2&FL_SUMMONED) { remove(self); return; /* THOMAS: return was missing here */ } else { dprint ("walkmonster in wall at: "); dprint (vtos(self.origin)); dprint ("\n"); } } if(self.model=="model/spider.mdl"||self.model=="model/scorpion.mdl") pitch_roll_for_slope('0 0 0'); } if(!self.ideal_yaw) { // dprint("no preset ideal yaw\n"); self.ideal_yaw = self.angles * '0 1 0'; } if (!self.yaw_speed) self.yaw_speed = 20; if(self.view_ofs=='0 0 0') self.view_ofs = '0 0 25'; if(self.proj_ofs=='0 0 0') self.proj_ofs = '0 0 25'; if(!self.use) self.use = monster_use; if(!self.flags&FL_MONSTER) self.flags(+)FL_MONSTER; if(self.flags&FL_MONSTER&&self.classname=="player_sheep") self.flags(-)FL_MONSTER; if (self.target) { self.goalentity = self.pathentity = find(world, targetname, self.target); self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin); if (!self.pathentity) { dprint ("Monster can't find target at "); dprint (vtos(self.origin)); dprint ("\n"); } // this used to be an objerror /* if(self.spawnflags&PLAY_DEAD&&self.th_possum!=SUB_Null) { self.think=self.th_possum; thinktime self : 0; } else */ if (self.pathentity.classname == "path_corner") self.th_walk (); else { self.pausetime = 99999999; self.th_stand (); } } else { /* if(self.spawnflags&PLAY_DEAD&&self.th_possum!=SUB_Null) { self.think=self.th_possum; thinktime self : 0; } else { */ self.pausetime = 99999999; self.th_stand (); // } } // spread think times so they don't all happen at same time self.nextthink+=random(0.5); }; void() walkmonster_start = { // delay drop to floor to make sure all doors have been spawned // spread think times so they don't all happen at same time self.takedamage=DAMAGE_YES; self.flags2(+)FL_ALIVE; if(self.scale<=0) self.scale=1; self.nextthink+=random(0.5); self.think = walkmonster_start_go; total_monsters = total_monsters + 1; }; /* void() flymonster_start_go = { self.takedamage = DAMAGE_YES; self.ideal_yaw = self.angles * '0 1 0'; if (!self.yaw_speed) self.yaw_speed = 10; if(self.view_ofs=='0 0 0'); self.view_ofs = '0 0 24'; if(self.proj_ofs=='0 0 0'); self.proj_ofs = '0 0 24'; self.use = monster_use; self.flags(+)FL_FLY; self.flags(+)FL_MONSTER; if(!self.touch) self.touch=obj_push; if (!walkmove(0,0, FALSE)) { dprint ("flymonster in wall at: "); dprint (vtos(self.origin)); dprint ("\n"); } if (self.target) { self.goalentity = self.pathentity = find(world, targetname, self.target); if (!self.pathentity) { dprint ("Monster can't find target at "); dprint (vtos(self.origin)); dprint ("\n"); } // this used to be an objerror // if(self.spawnflags&PLAY_DEAD&&self.th_possum!=SUB_Null) // { // self.think=self.th_possum; // thinktime self : 0; // } // else if (self.pathentity.classname == "path_corner") self.th_walk (); else { self.pausetime = 99999999; self.th_stand (); } } else { // if(self.spawnflags&PLAY_DEAD&&self.th_possum!=SUB_Null) // { // self.think=self.th_possum; // thinktime self : 0; // } // else // { self.pausetime = 99999999; self.th_stand (); // } } }; void() flymonster_start = { // spread think times so they don't all happen at same time self.takedamage=DAMAGE_YES; self.flags2(+)FL_ALIVE; self.nextthink+=random(0.5); self.think = flymonster_start_go; total_monsters = total_monsters + 1; }; void() swimmonster_start_go = { if (deathmatch) { remove(self); return; } if(!self.touch) self.touch=obj_push; self.takedamage = DAMAGE_YES; total_monsters = total_monsters + 1; self.ideal_yaw = self.angles * '0 1 0'; if (!self.yaw_speed) self.yaw_speed = 10; if(self.view_ofs=='0 0 0'); self.view_ofs = '0 0 10'; if(self.proj_ofs=='0 0 0'); self.proj_ofs = '0 0 10'; self.use = monster_use; self.flags(+)FL_SWIM; self.flags(+)FL_MONSTER; if (self.target) { self.goalentity = self.pathentity = find(world, targetname, self.target); if (!self.pathentity) { dprint ("Monster can't find target at "); dprint (vtos(self.origin)); dprint ("\n"); } // this used to be an objerror self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin); self.th_walk (); } else { self.pausetime = 99999999; self.th_stand (); } // spread think times so they don't all happen at same time self.nextthink+=random(0.5); }; void() swimmonster_start = { // spread think times so they don't all happen at same time self.takedamage=DAMAGE_YES; self.flags2(+)FL_ALIVE; self.nextthink+=random(0.5); self.think = swimmonster_start_go; total_monsters = total_monsters + 1; }; */ gamecode/hc/h2/mummy.hc000066400000000000000000000601641444734033100152170ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\monsters\mummy\mummy.hc ============================================================================== */ // For building the model $cd Q:\art\models\monsters\mummy $origin 0 0 0 $base BASE-F skin $skin skin $flags 0 // DEATH : FRAMES 1 - 13, no arms, one leg $frame mdeath1 mdeath2 mdeath3 mdeath4 mdeath5 $frame mdeath6 mdeath7 mdeath8 mdeath9 mdeath10 $frame mdeath11 mdeath12 mdeath13 // STAFF ATTACK : FRAMES 14 - 37, both arms, both legs $frame mstafA1 mstafA2 mstafA3 mstafA4 mstafA5 $frame mstafA6 mstafA7 mstafA8 mstafA9 mstafA10 $frame mstafA11 mstafA12 mstafA13 mstafA14 mstafA15 $frame mstafA16 mstafA17 mstafA18 mstafA19 mstafA20 $frame mstafA21 mstafA22 mstafA23 mstafA24 // MELEE SWING : FRAMES 38 - 47, no arms, both legs, swings his stump at enemy $frame mswingC1 mswingC2 mswingC3 mswingC4 mswingC5 $frame mswingC6 mswingC7 mswingC8 mswingC9 mswingC10 // MELEE SWING : FRAMES 48 - 65, both arms and legs, swings his staff at enemy $frame mswngA1 mswngA2 mswngA3 mswngA4 mswngA5 $frame mswngA6 mswngA7 mswngA8 mswngA9 mswngA10 $frame mswngA11 mswngA12 mswngA13 mswngA14 mswngA15 $frame mswngA16 mswngA17 mswngA18 // MELEE SWING : FRAMES 66 - 83, one arm, both legs, swings his staff at enemy $frame mswngB1 mswngB2 mswngB3 mswngB4 mswngB5 $frame mswngB6 mswngB7 mswngB8 mswngB9 mswngB10 $frame mswngB11 mswngB12 mswngB13 mswngB14 mswngB15 $frame mswngB16 mswngB17 mswngB18 // WALKING: frames 84 - 97, both arms and both legs $frame mwalkA1 mwalkA2 mwalkA3 mwalkA4 mwalkA5 $frame mwalkA6 mwalkA7 mwalkA8 mwalkA9 mwalkA10 $frame mwalkA11 mwalkA12 mwalkA13 mwalkA14 // WALKING: frames 98 - 113, one arm and both legs $frame mwalkB1 mwalkB2 mwalkB3 mwalkB4 mwalkB5 $frame mwalkB6 mwalkB7 mwalkB8 mwalkB9 mwalkB10 $frame mwalkB11 mwalkB12 mwalkB13 mwalkB14 mwalkB15 $frame mwalkB16 // WALKING: frames 114 - 131, no arms, both legs $frame mwalkC1 mwalkC2 mwalkC3 mwalkC4 mwalkC5 $frame mwalkC6 mwalkC7 mwalkC8 mwalkC9 mwalkC10 $frame mwalkC11 mwalkC12 mwalkC13 mwalkC14 mwalkC15 $frame mwalkC16 mwalkC17 mwalkC18 // 132 - 147 $frame shoota1 shoota2 shoota3 shoota4 shoota5 $frame shoota6 shoota7 shoota8 shoota9 shoota10 $frame shoota11 shoota12 shoota13 shoota14 shoota15 $frame shoota16 // $frame shootb1 shootb2 shootb3 shootb4 shootb5 $frame shootb6 shootb7 shootb8 shootb9 shootb10 $frame shootb11 shootb12 shootb13 shootb14 shootb15 $frame shootb16 // $frame staffb1 staffb2 staffb3 staffb4 staffb5 $frame staffb6 staffb7 staffb8 staffb9 staffb10 $frame staffb11 staffb12 staffb13 staffb14 staffb15 $frame staffb16 staffb17 staffb18 staffb19 staffb20 $frame staffb21 staffb22 staffb23 staffb24 staffb25 $frame staffb26 staffb27 // $frame crawl1 crawl2 crawl3 crawl4 crawl5 $frame crawl6 crawl7 crawl8 crawl9 crawl10 $frame crawl11 crawl12 crawl13 crawl14 // What parts are gone from the mummy float MUMMY_NONE = 0; // None float MUMMY_LARM = 1; // Left arm is gone float MUMMY_RARM = 2; // Left and Right arm are gone float MUMMY_LEG = 3; // Left and Right arm are gone,Left leg is gone float MUMMY_WAVER = 1; float MUMMY_DOWN = 2; void mummyrun(void); void mummywalk(void); void mummymelee(void); /* ============ pmissile_touch - missile1 hit something. Hurt it ============ */ void mummissile_touch (void) { float damg; if (other == self.owner) return; // don't explode on owner if (pointcontents(self.origin) == CONTENT_SKY) { pmissile_gone(); return; } damg = random(5,10); if (other.health) T_Damage (other, self, self.owner, damg ); sound (self, CHAN_BODY, "weapons/expsmall.wav", 1, ATTN_NORM); self.origin = self.origin - 8*normalize(self.velocity); CreateFireCircle(self.origin - (v_forward * 8)); self.effects = EF_NODRAW; self.solid = SOLID_NOT; // self.nextthink = time + .5; // So explosion sound can finish out thinktime self : .5; self.think = pmissile_gone; } void mflame1_runup (void) [++ 0 .. 16 ] { if (cycle_wrapped) if(self.cnt) { self.cnt-=1; particle2(self.origin+'0 0 17','0 0 25','0 0 25',168,7,5); } else remove(self); } void mflame2_runup (void) [++ 17 .. 33 ] { if (cycle_wrapped) if(self.cnt) { self.cnt-=1; particle2(self.origin+'0 0 17','0 0 25','0 0 25',168,7,5); } else remove(self); } void mflame3_runup (void) [++ 34 .. 50 ] { if (cycle_wrapped) if(self.cnt) { self.cnt-=1; particle2(self.origin+'0 0 17','0 0 25','0 0 25',168,7,5); } else remove(self); } void mflame_burn(void) { float damg; if ((other.health) && (other != self.owner) && (self.pain_finished time) return; if (self.classname == "monster_mummy_lord") sound (self, CHAN_VOICE, "mummy/pain2.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "mummy/pain.wav", 1, ATTN_NORM); hold_parts = self.parts_gone; if (self.health < 30) { if (self.parts_gone <= MUMMY_NONE) mummy_throw_leftarm(); if (self.parts_gone <= MUMMY_LARM) mummy_throw_rightarm(); if (self.parts_gone <= MUMMY_RARM) mummy_throw_rightleg(); } else if (self.health < 60) { if (self.parts_gone <= MUMMY_NONE) mummy_throw_leftarm(); if (self.parts_gone <= MUMMY_LARM) mummy_throw_rightarm(); } else if (self.health < 100) { if (self.parts_gone == MUMMY_NONE) mummy_throw_leftarm(); } if (hold_parts != self.parts_gone) sound (self, CHAN_BODY, "mummy/limbloss.wav", 1, ATTN_NORM); } void lordmummymissile (void) { float result; vector delta; thinktime self : HX_FRAME_TIME; self.think = lordmummymissile; delta = self.enemy.origin - self.origin; if (vlen(delta) < 70) // Too close to shoot with a missile mummymelee(); if (self.parts_gone == MUMMY_NONE) { result = AdvanceFrame($mstafA1,$mstafA24); if (self.frame == $mstafA10) sound (self, CHAN_WEAPON, "mummy/tap.wav", 1, ATTN_NORM); if (self.frame == $mstafA17) launch_mumshot(); if (result == AF_END) mummyrun(); else ai_face(); } else if (self.parts_gone == MUMMY_LARM) { result=AdvanceFrame($mwalkB1,$mwalkB16); // Because there is no one if (self.frame == $mwalkB16) launch_mumshot(); if (result == AF_END) mummyrun(); else ai_face(); } else mummyrun(); } void mummymissile (void) { float result,chance; vector delta; thinktime self : HX_FRAME_TIME; self.think = mummymissile; delta = self.enemy.origin - self.origin; if (vlen(delta) < 70) // Too close to shoot with a missile mummymelee(); if (self.parts_gone == MUMMY_NONE) { result = AdvanceFrame($shoota1,$shoota16); if (self.frame == $shoota6) { makevectors(self.angles); Create_Missile(self,self.origin + v_forward*14 - v_right * 9 + v_up * 25, self.enemy.origin+self.enemy.view_ofs,"models/akarrow.mdl","green_arrow",0,1000,mummissile_touch); } if (self.frame == $shoota12) { if (enemy_range < RANGE_NEAR) chance = 0.80; else if (enemy_range < RANGE_MID) chance = 0.70; else if (enemy_range < RANGE_FAR) chance = 0.40; if (random() < chance) // Repeat as necessary self.frame = $shoota5; } if (result == AF_END) { mummyrun(); } else ai_face(); } else if (self.parts_gone == MUMMY_LARM) { result=AdvanceFrame($shootb1,$shootb16); // Because there is no one if (self.frame == $shootb6) { makevectors(self.angles); Create_Missile(self,self.origin + v_forward*14 + v_right * 11 + v_up * 40, self.enemy.origin+self.enemy.view_ofs,"models/akarrow.mdl","green_arrow",0,1000,mummissile_touch); } if (self.frame == $shootb12) { if (enemy_range < RANGE_NEAR) chance = 0.80; else if (enemy_range < RANGE_MID) chance = 0.70; else if (enemy_range < RANGE_FAR) chance = 0.40; if (random() < chance) // Repeat as necessary self.frame = $shootb5; } if (result == AF_END) mummyrun(); else ai_face(); } else mummyrun(); } void mummylordchoice (void) { float chance; // He's more likely to use his flame attack when enemy is // farther away if (enemy_range < RANGE_NEAR) chance = 0.60; else if (enemy_range < RANGE_MID) chance = 0.80; else if (enemy_range < RANGE_FAR) chance = 0.90; if (random() < chance) lordmummymissile(); else mummymissile(); } void mummypunch () { local vector delta; local float ldmg; delta = self.enemy.origin - self.origin; if (vlen(delta) > 50) return; self.last_attack=time; if (self.classname == "monster_mummy") ldmg = DMG_MUMMY_PUNCH; else ldmg = DMG_MUMMY_PUNCH * 2; T_Damage (self.enemy, self, self, ldmg); sound (self, CHAN_WEAPON, "weapons/gauntht1.wav", 1, ATTN_NORM); } void mummybite(void) { local vector delta; local float ldmg; delta = self.enemy.origin - self.origin; if (vlen(delta) > 50) return; self.last_attack=time; ldmg = random() * DMG_MUMMY_PUNCH; T_Damage (self.enemy, self, self, ldmg); sound (self, CHAN_WEAPON, "mummy/bite.wav", 1, ATTN_NORM); } void mummymelee(void) { float result; vector delta; self.nextthink = time + HX_FRAME_TIME; self.think = mummymelee; if (self.parts_gone == MUMMY_NONE) { result=AdvanceFrame($mswngA1,$mswngA18); if (self.frame == $mswngA8) sound (self, CHAN_WEAPON, "weapons/vorpswng.wav", 1, ATTN_NORM); if (self.frame == $mswngA11) mummypunch(); } else if (self.parts_gone == MUMMY_LARM) { result=AdvanceFrame($mswngB1,$mswngB18); if (self.frame == $mswngB8) sound (self, CHAN_WEAPON, "weapons/vorpswng.wav", 1, ATTN_NORM); if (self.frame == $mswngB11) mummypunch(); } else if (self.parts_gone == MUMMY_RARM) { result=AdvanceFrame($mswingC1,$mswingC10); if (self.frame == $mswingC5) sound (self, CHAN_WEAPON, "weapons/gaunt1.wav", 1, ATTN_NORM); if (self.frame == $mswingC8) mummypunch(); } else // He's on the floor so he'll bite your legs off { // self.nextthink = time + HX_FRAME_TIME * 2; thinktime self : HX_FRAME_TIME *2; self.mummy_state=MUMMY_DOWN; result=AdvanceFrame($crawl1,$crawl14); if (self.frame == $crawl14) sound (self, CHAN_BODY, "mummy/crawl.wav", 1, ATTN_NORM); if (self.frame == $crawl7) mummybite(); } if (result == AF_END) { delta = self.enemy.origin - self.origin; if (vlen(delta) > 80) mummyrun(); } else ai_charge(1); } void mummyrun(void) { float distance; // self.nextthink = time + HX_FRAME_TIME; thinktime self : HX_FRAME_TIME; self.think = mummyrun; if (self.parts_gone==MUMMY_NONE) { if ((random() < .10) && (self.frame == $mwalkA1)) { if (self.classname == "monster_mummy_lord") sound (self, CHAN_VOICE, "mummy/moan2.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "mummy/moan.wav", 1, ATTN_NORM); } AdvanceFrame($mwalkA1,$mwalkA14); if (self.frame==$mwalkA6) sound (self, CHAN_BODY, "mummy/step.wav", 1, ATTN_NORM); else if (self.frame==$mwalkA7) sound (self, CHAN_BODY, "mummy/slide.wav", 1, ATTN_NORM); if ((self.frame >= $mwalkA1) && (self.frame <= $mwalkA4)) distance = 3.25; else distance = 2.25; } else if (self.parts_gone==MUMMY_LARM) { if ((random() < .10) && (self.frame == $mwalkB1)) { if (self.classname == "monster_mummy_lord") sound (self, CHAN_VOICE, "mummy/moan2.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "mummy/moan.wav", 1, ATTN_NORM); } AdvanceFrame($mwalkB1,$mwalkB16); if (self.frame==$mwalkB6) sound (self, CHAN_BODY, "mummy/step.wav", 1, ATTN_NORM); else if (self.frame==$mwalkB8) sound (self, CHAN_BODY, "mummy/slide.wav", 1, ATTN_NORM); distance = 3; } else if (self.parts_gone==MUMMY_RARM) { if ((random() < .10) && (self.frame == $mwalkC1)) { if (self.classname == "monster_mummy_lord") sound (self, CHAN_VOICE, "mummy/moan2.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "mummy/moan.wav", 1, ATTN_NORM); } AdvanceFrame($mwalkC1,$mwalkC18); if (self.frame==$mwalkC8) sound (self, CHAN_BODY, "mummy/step.wav", 1, ATTN_NORM); else if (self.frame==$mwalkC10) sound (self, CHAN_BODY, "mummy/slide.wav", 1, ATTN_NORM); distance = 3; } else if (self.parts_gone <= MUMMY_LEG) { if (self.mummy_state==MUMMY_WAVER) { AdvanceFrame($mdeath1,$mdeath13); if (self.frame==$mdeath13) self.mummy_state=MUMMY_DOWN; distance = 0; } else { // self.nextthink = time + HX_FRAME_TIME * 2; thinktime self : HX_FRAME_TIME *2; AdvanceFrame($crawl1,$crawl14); if (self.frame == $crawl2) sound (self, CHAN_BODY, "mummy/slide.wav", 1, ATTN_NORM); if ((self.frame >= $crawl1) && (self.frame <= $crawl5)) distance = 3; else distance = 0; } } ai_run(distance); } void mummywalk(void) { float distance; // self.nextthink = time + HX_FRAME_TIME + .02; thinktime self : HX_FRAME_TIME + .02; self.think = mummywalk; if (self.parts_gone==MUMMY_NONE) { if ((random() < .10) && (self.frame == $mwalkA1)) { if (self.classname == "monster_mummy_lord") sound (self, CHAN_VOICE, "mummy/moan2.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "mummy/moan.wav", 1, ATTN_NORM); } AdvanceFrame($mwalkA1,$mwalkA14); if (self.frame==$mwalkA6) sound (self, CHAN_BODY, "mummy/step.wav", 1, ATTN_NORM); else if (self.frame==$mwalkA7) sound (self, CHAN_BODY, "mummy/slide.wav", 1, ATTN_NORM); if ((self.frame >= $mwalkA1) && (self.frame <= $mwalkA4)) distance = 3.25; else distance = 2.25; } else if (self.parts_gone==MUMMY_LARM) { if ((random() < .10) && (self.frame == $mwalkB1)) { if (self.classname == "monster_mummy_lord") sound (self, CHAN_VOICE, "mummy/moan2.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "mummy/moan.wav", 1, ATTN_NORM); } AdvanceFrame($mwalkB1,$mwalkB16); distance = 2.25; if (self.frame==$mwalkB6) sound (self, CHAN_BODY, "mummy/step.wav", 1, ATTN_NORM); else if (self.frame==$mwalkB8) sound (self, CHAN_BODY, "mummy/slide.wav", 1, ATTN_NORM); } else if (self.parts_gone==MUMMY_RARM) { if ((random() < .10) && (self.frame == $mwalkC1)) { if (self.classname == "monster_mummy_lord") sound (self, CHAN_VOICE, "mummy/moan2.wav", 1, ATTN_NORM); else sound (self, CHAN_VOICE, "mummy/moan.wav", 1, ATTN_NORM); } AdvanceFrame($mwalkC1,$mwalkC18); distance = 2.25; if (self.frame==$mwalkC8) sound (self, CHAN_BODY, "mummy/step.wav", 1, ATTN_NORM); else if (self.frame==$mwalkC10) sound (self, CHAN_BODY, "mummy/slide.wav", 1, ATTN_NORM); } else if (self.parts_gone <= MUMMY_LEG) { if (self.mummy_state==MUMMY_WAVER) { AdvanceFrame($mdeath1,$mdeath13); if (self.frame==$mdeath13) self.mummy_state=MUMMY_DOWN; distance = 0; } else { // self.nextthink = time + HX_FRAME_TIME * 2; thinktime self : HX_FRAME_TIME *2; if (self.frame == $crawl2) sound (self, CHAN_BODY, "mummy/slide.wav", 1, ATTN_NORM); AdvanceFrame($crawl1,$crawl14); if (self.frame == $crawl14) sound (self, CHAN_BODY, "mummy/crawl.wav", 1, ATTN_NORM); distance = 2; } } ai_walk(distance); } void mummystand(void) { // self.nextthink = time + HX_FRAME_TIME; thinktime self : HX_FRAME_TIME; self.think = mummystand; self.frame = $mwalkA1; if (random() < .5) ai_stand(); } /*QUAKED monster_mummy (1 0.3 0) (-16 -16 0) (16 16 50) AMBUSH No, it's not Keith Richards or Bob Dylan. It's the mummy. -------------------------FIELDS------------------------- health : 150 experience : 500 -------------------------------------------------------- */ void monster_mummy (void) { if(deathmatch) { remove(self); return; } if(!self.flags2&FL_SUMMONED) { precache_model2("models/mummy.mdl"); precache_model2 ("models/larm.mdl"); precache_model2 ("models/leg.mdl"); precache_model2 ("models/mumshot.mdl"); precache_model2 ("models/muhead.mdl"); precache_sound2 ("mummy/sight.wav"); precache_sound2 ("mummy/die.wav"); precache_sound2 ("mummy/mislfire.wav"); precache_sound2 ("mummy/limbloss.wav"); precache_sound2 ("mummy/moan.wav"); precache_sound2 ("mummy/pain.wav"); precache_sound2 ("mummy/crawl.wav"); precache_sound2 ("mummy/slide.wav"); precache_sound2 ("mummy/step.wav"); precache_sound2 ("mummy/tap.wav"); precache_sound2 ("mummy/bite.wav"); } CreateEntityNew(self,ENT_MUMMY,"models/mummy.mdl",mummy_die); self.mintel = 3; self.monsterclass = CLASS_GRUNT; self.th_stand = mummystand; self.th_walk = mummywalk; self.th_run = mummyrun; self.th_melee = mummymelee; self.th_missile = mummymissile; self.th_pain = mummy_pain; self.parts_gone = MUMMY_NONE; self.skin = 0; self.flags (+) FL_MONSTER; self.yaw_speed = 10; self.health = 200; self.experience_value = 200; walkmonster_start(); } /*QUAKED monster_mummy_lord (1 0.3 0) (-16 -16 0) (16 16 50) AMBUSH STUCK JUMP PLAY_DEAD DORMANT He's big, he's bad, he's wrapped in moldy bandages - he's the mummy. -------------------------FIELDS------------------------- health : 500 experience : 300 -------------------------------------------------------- */ void monster_mummy_lord (void) { if(deathmatch) { remove(self); return; } if(!self.flags2&FL_SUMMONED) { precache_model2("models/mummy.mdl"); precache_model2("models/larm.mdl"); precache_model2("models/leg.mdl"); precache_model2("models/mumshot.mdl"); precache_model2("models/muhead.mdl"); precache_sound2 ("mummy/sight2.wav"); precache_sound2 ("mummy/die2.wav"); precache_sound2 ("mummy/mislfire.wav"); precache_sound2 ("mummy/limbloss.wav"); precache_sound2 ("mummy/moan2.wav"); precache_sound2 ("mummy/pain2.wav"); precache_sound2 ("mummy/crawl.wav"); precache_sound2 ("mummy/slide.wav"); precache_sound2 ("mummy/step.wav"); precache_sound2 ("mummy/tap.wav"); precache_sound2 ("mummy/bite.wav"); } CreateEntityNew(self,ENT_MUMMY,"models/mummy.mdl",mummy_die); self.mintel = 3; self.monsterclass = CLASS_GRUNT; self.th_stand = mummystand; self.th_walk = mummywalk; self.th_run = mummyrun; self.th_melee = mummymelee; self.th_missile = mummylordchoice; self.th_pain = mummy_pain; self.parts_gone = MUMMY_NONE; self.skin = 1; self.headmodel="models/muhead.mdl"; self.flags (+) FL_MONSTER; self.yaw_speed = 10; self.health = 400; self.experience_value = 300; walkmonster_start(); } gamecode/hc/h2/newplay.hc000066400000000000000000000570331444734033100155330ustar00rootroot00000000000000 /* ============================================================================== ALL PLAYERS FRAME CONTROL!!! MG ============================================================================== */ //================================================================= //PALADIN // $frame attgnt1 attgnt2 attgnt3 attgnt4 attgnt5 $frame attgnt6 attgnt7 attgnt8 attgnt9 attgnt10 $frame attgnt11 // $frame pattstf1 pattstf2 pattstf3 pattstf4 // $frame attswd1 attswd2 attswd3 attswd4 attswd5 $frame attswd6 attswd7 attswd8 attswd9 attswd10 $frame attswd11 attswd12 // $frame pcrouch1 pcrouch2 pcrouch3 pcrouch4 pcrouch5 $frame pcrouch6 pcrouch7 pcrouch8 pcrouch9 pcrouch10 $frame pcrouch11 pcrouch12 pcrouch13 pcrouch14 pcrouch15 $frame pcrouch16 pcrouch17 pcrouch18 pcrouch19 pcrouch20 // $frame pdeath1 pdeath2 pdeath3 pdeath4 pdeath5 $frame pdeath6 pdeath7 pdeath8 pdeath9 pdeath10 $frame pdeath11 pdeath12 pdeath13 pdeath14 pdeath15 $frame pdeath16 pdeath17 pdeath18 pdeath19 pdeath20 // $frame pdecap1 pdecap2 pdecap3 pdecap4 pdecap5 $frame pdecap6 pdecap7 pdecap8 pdecap9 pdecap10 $frame pdecap11 pdecap12 pdecap13 pdecap14 pdecap15 $frame pdecap16 pdecap17 pdecap18 pdecap19 pdecap20 $frame pdecap21 pdecap22 pdecap23 pdecap24 pdecap25 $frame pdecap26 pdecap27 pdecap28 // $frame flygnt1 flygnt2 flygnt3 flygnt4 flygnt5 $frame flygnt6 flygnt7 flygnt8 flygnt9 flygnt10 $frame flygnt11 flygnt12 flygnt13 flygnt14 flygnt15 // $frame pflystf1 pflystf2 pflystf3 pflystf4 pflystf5 $frame pflystf6 pflystf7 pflystf8 pflystf9 pflystf10 $frame pflystf11 pflystf12 pflystf13 pflystf14 pflystf15 // $frame flyswd1 flyswd2 flyswd3 flyswd4 flyswd5 $frame flyswd6 flyswd7 flyswd8 flyswd9 flyswd10 $frame flyswd11 flyswd12 flyswd13 flyswd14 flyswd15 // $frame pjump1 pjump2 pjump3 pjump4 pjump5 $frame pjump6 pjump7 pjump8 pjump9 pjump10 $frame pjump11 pjump12 // $frame paingnt1 paingnt2 paingnt3 paingnt4 paingnt5 $frame paingnt6 paingnt7 // $frame ppainstf1 ppainstf2 ppainstf3 ppainstf4 ppainstf5 $frame ppainstf6 ppainstf7 // $frame painswd1 painswd2 painswd3 painswd4 painswd5 $frame painswd6 painswd7 // $frame rungnt1 rungnt2 rungnt3 rungnt4 rungnt5 $frame rungnt6 rungnt7 rungnt8 rungnt9 rungnt10 $frame rungnt11 rungnt12 // $frame prunstf1 prunstf2 prunstf3 prunstf4 prunstf5 $frame prunstf6 prunstf7 prunstf8 prunstf9 prunstf10 $frame prunstf11 prunstf12 // $frame runswd1 runswd2 runswd3 runswd4 runswd5 $frame runswd6 runswd7 runswd8 runswd9 runswd10 $frame runswd11 runswd12 // $frame stdgnt1 stdgnt2 stdgnt3 stdgnt4 stdgnt5 $frame stdgnt6 stdgnt7 stdgnt8 stdgnt9 stdgnt10 $frame stdgnt11 stdgnt12 stdgnt13 // $frame pstdstf1 pstdstf2 pstdstf3 pstdstf4 pstdstf5 $frame pstdstf6 pstdstf7 pstdstf8 pstdstf9 pstdstf10 $frame pstdstf11 pstdstf12 pstdstf13 // $frame stdswd1 stdswd2 stdswd3 stdswd4 stdswd5 $frame stdswd6 stdswd7 stdswd8 stdswd9 stdswd10 $frame stdswd11 stdswd12 stdswd13 //================================================================= $framevalue 0 //CRUSADER // $frame ccrouch1 ccrouch2 ccrouch3 ccrouch4 ccrouch5 $frame ccrouch6 ccrouch7 ccrouch8 ccrouch9 ccrouch10 $frame ccrouch11 ccrouch12 ccrouch13 ccrouch14 ccrouch15 $frame ccrouch16 ccrouch17 ccrouch18 ccrouch19 ccrouch20 // $frame cdecap1 cdecap2 cdecap3 cdecap4 cdecap5 $frame cdecap6 cdecap7 cdecap8 cdecap9 cdecap10 $frame cdecap11 cdecap12 cdecap13 cdecap14 cdecap15 $frame cdecap16 cdecap17 cdecap18 cdecap19 cdecap20 $frame cdecap21 cdecap22 cdecap23 cdecap24 cdecap25 $frame cdecap26 cdecap27 cdecap28 // $frame cdeath1 cdeath2 cdeath3 cdeath4 cdeath5 $frame cdeath6 cdeath7 cdeath8 cdeath9 cdeath10 $frame cdeath11 cdeath12 cdeath13 cdeath14 cdeath15 $frame cdeath16 cdeath17 cdeath18 cdeath19 cdeath20 // $frame flyham1 flyham2 flyham3 flyham4 flyham5 $frame flyham6 flyham7 flyham8 flyham9 flyham10 $frame flyham11 flyham12 flyham13 flyham14 flyham15 // $frame attham1 attham2 attham3 attham4 attham5 $frame attham6 attham7 attham8 attham9 attham10 // $frame painham1 painham2 painham3 painham4 painham5 $frame painham6 painham7 painham8 // $frame stdham1 stdham2 stdham3 stdham4 stdham5 $frame stdham6 stdham7 stdham8 stdham9 stdham10 $frame stdham11 stdham12 stdham13 // $frame runham1 runham2 runham3 runham4 runham5 $frame runham6 runham7 runham8 runham9 runham10 $frame runham11 runham12 // $frame flyice1 flyice2 flyice3 flyice4 flyice5 $frame flyice6 flyice7 flyice8 flyice9 flyice10 $frame flyice11 flyice12 flyice13 flyice14 flyice15 // $frame painice1 painice2 painice3 painice4 painice5 $frame painice6 painice7 painice8 // $frame runice1 runice2 runice3 runice4 runice5 $frame runice6 runice7 runice8 runice9 runice10 $frame runice11 runice12 // $frame attice1 attice2 attice3 attice4 // $frame stdice1 stdice2 stdice3 stdice4 stdice5 $frame stdice6 stdice7 stdice8 stdice9 stdice10 $frame stdice11 stdice12 stdice13 // $frame cjump1 cjump2 cjump3 cjump4 cjump5 $frame cjump6 cjump7 cjump8 cjump9 cjump10 $frame cjump11 cjump12 cjump13 // $frame cflystf1 cflystf2 cflystf3 cflystf4 cflystf5 $frame cflystf6 cflystf7 cflystf8 cflystf9 cflystf10 $frame cflystf11 cflystf12 cflystf13 cflystf14 cflystf15 // $frame cpainstf1 cpainstf2 cpainstf3 cpainstf4 cpainstf5 $frame cpainstf6 cpainstf7 cpainstf8 // $frame crunstf1 crunstf2 crunstf3 crunstf4 crunstf5 $frame crunstf6 crunstf7 crunstf8 crunstf9 crunstf10 $frame crunstf11 crunstf12 // $frame cattstf1 cattstf2 cattstf3 cattstf4 cattstf5 // $frame cstdstf1 cstdstf2 cstdstf3 cstdstf4 cstdstf5 $frame cstdstf6 cstdstf7 cstdstf8 cstdstf9 cstdstf10 $frame cstdstf11 cstdstf12 cstdstf13 //================================================================= $framevalue 0 //NECROMANCER // $frame ncrouch1 ncrouch2 ncrouch3 ncrouch4 ncrouch5 $frame ncrouch6 ncrouch7 ncrouch8 ncrouch9 ncrouch10 $frame ncrouch11 ncrouch12 ncrouch13 ncrouch14 ncrouch15 $frame ncrouch16 ncrouch17 ncrouch18 ncrouch19 ncrouch20 // $frame ndeath1 ndeath2 ndeath3 ndeath4 ndeath5 $frame ndeath6 ndeath7 ndeath8 ndeath9 ndeath10 $frame ndeath11 ndeath12 ndeath13 ndeath14 ndeath15 $frame ndeath16 ndeath17 ndeath18 ndeath19 ndeath20 // $frame ndecap1 ndecap2 ndecap3 ndecap4 ndecap5 $frame ndecap6 ndecap7 ndecap8 ndecap9 ndecap10 $frame ndecap11 ndecap12 ndecap13 ndecap14 ndecap15 $frame ndecap16 ndecap17 ndecap18 ndecap19 ndecap20 // $frame atthan1 atthan2 atthan3 atthan4 atthan5 $frame atthan6 atthan7 atthan8 // $frame flyhan1 flyhan2 flyhan3 flyhan4 flyhan5 $frame flyhan6 flyhan7 flyhan8 flyhan9 flyhan10 $frame flyhan11 flyhan12 flyhan13 flyhan14 flyhan15 // $frame painhan1 painhan2 painhan3 painhan4 painhan5 $frame painhan6 painhan7 painhan8 // $frame runhan1 runhan2 runhan3 runhan4 runhan5 $frame runhan6 runhan7 runhan8 runhan9 runhan10 $frame runhan11 runhan12 // $frame stdhan1 stdhan2 stdhan3 stdhan4 stdhan5 $frame stdhan6 stdhan7 stdhan8 stdhan9 stdhan10 $frame stdhan11 stdhan12 // $frame attsic1 attsic2 attsic3 attsic4 attsic5 $frame attsic6 attsic7 attsic8 attsic9 attsic10 $frame attsic11 attsic12 // $frame flysic1 flysic2 flysic3 flysic4 flysic5 $frame flysic6 flysic7 flysic8 flysic9 flysic10 $frame flysic11 flysic12 flysic13 flysic14 flysic15 // $frame painsic1 painsic2 painsic3 painsic4 painsic5 $frame painsic6 painsic7 painsic8 // $frame runsic1 runsic2 runsic3 runsic4 runsic5 $frame runsic6 runsic7 runsic8 runsic9 runsic10 $frame runsic11 runsic12 // $frame stdsic1 stdsic2 stdsic3 stdsic4 stdsic5 $frame stdsic6 stdsic7 stdsic8 stdsic9 stdsic10 $frame stdsic11 stdsic12 // $frame nattstf1 nattstf2 nattstf3 nattstf4 nattstf5 $frame nattstf6 nattstf7 nattstf8 // $frame nflystf1 nflystf2 nflystf3 nflystf4 nflystf5 $frame nflystf6 nflystf7 nflystf8 nflystf9 nflystf10 $frame nflystf11 nflystf12 nflystf13 nflystf14 nflystf15 // $frame npainstf1 npainstf2 npainstf3 npainstf4 npainstf5 $frame npainstf6 npainstf7 npainstf8 // $frame nrunstf1 nrunstf2 nrunstf3 nrunstf4 nrunstf5 $frame nrunstf6 nrunstf7 nrunstf8 nrunstf9 nrunstf10 $frame nrunstf11 nrunstf12 // $frame nstdstf1 nstdstf2 nstdstf3 nstdstf4 nstdstf5 $frame nstdstf6 nstdstf7 nstdstf8 nstdstf9 nstdstf10 $frame nstdstf11 nstdstf12 //================================================================= $framevalue 0 //ASSASSIN // $frame attdag1 attdag2 attdag3 attdag4 attdag5 $frame attdag6 attdag7 attdag8 attdag9 attdag10 $frame attdag11 // $frame aattstf1 aattstf2 aattstf3 aattstf4 // $frame attxbw1 attxbw2 attxbw3 attxbw4 // $frame acrouch1 acrouch2 acrouch3 acrouch4 acrouch5 $frame acrouch6 acrouch7 acrouch8 acrouch9 acrouch10 $frame acrouch11 acrouch12 acrouch13 acrouch14 acrouch15 $frame acrouch16 acrouch17 acrouch18 acrouch19 acrouch20 // $frame adeath1 adeath2 adeath3 adeath4 adeath5 $frame adeath6 adeath7 adeath8 adeath9 adeath10 $frame adeath11 adeath12 adeath13 adeath14 adeath15 $frame adeath16 adeath17 adeath18 adeath19 adeath20 // $frame adecap1 adecap2 adecap3 adecap4 adecap5 $frame adecap6 adecap7 adecap8 adecap9 adecap10 $frame adecap11 adecap12 adecap13 adecap14 adecap15 $frame adecap16 adecap17 adecap18 adecap19 adecap20 $frame adecap21 adecap22 adecap23 adecap24 adecap25 $frame adecap26 adecap27 adecap28 // $frame flydag1 flydag2 flydag3 flydag4 flydag5 $frame flydag6 flydag7 flydag8 flydag9 flydag10 $frame flydag11 flydag12 flydag13 flydag14 flydag15 // $frame aflystf1 aflystf2 aflystf3 aflystf4 aflystf5 $frame aflystf6 aflystf7 aflystf8 aflystf9 aflystf10 $frame aflystf11 aflystf12 aflystf13 aflystf14 aflystf15 // $frame flyxbw1 flyxbw2 flyxbw3 flyxbw4 flyxbw5 $frame flyxbw6 flyxbw7 flyxbw8 flyxbw9 flyxbw10 $frame flyxbw11 flyxbw12 flyxbw13 flyxbw14 flyxbw15 // $frame ajump1 ajump2 ajump3 ajump4 ajump5 $frame ajump6 ajump7 ajump8 ajump9 ajump10 $frame ajump11 ajump12 // $frame paindag1 paindag2 paindag3 paindag4 paindag5 $frame paindag6 paindag7 // $frame apainstf1 apainstf2 apainstf3 apainstf4 apainstf5 $frame apainstf6 apainstf7 // $frame painxbw1 painxbw2 painxbw3 painxbw4 painxbw5 $frame painxbw6 painxbw7 // $frame rundag1 rundag2 rundag3 rundag4 rundag5 $frame rundag6 rundag7 rundag8 rundag9 rundag10 $frame rundag11 rundag12 // $frame arunstf1 arunstf2 arunstf3 arunstf4 arunstf5 $frame arunstf6 arunstf7 arunstf8 arunstf9 arunstf10 $frame arunstf11 arunstf12 // $frame runxbw1 runxbw2 runxbw3 runxbw4 runxbw5 $frame runxbw6 runxbw7 runxbw8 runxbw9 runxbw10 $frame runxbw11 runxbw12 // $frame stddag1 stddag2 stddag3 stddag4 stddag5 $frame stddag6 stddag7 stddag8 stddag9 stddag10 $frame stddag11 stddag12 stddag13 // $frame astdstf1 astdstf2 astdstf3 astdstf4 astdstf5 $frame astdstf6 astdstf7 astdstf8 astdstf9 astdstf10 $frame astdstf11 astdstf12 astdstf13 // $frame stdxbw1 stdxbw2 stdxbw3 stdxbw4 stdxbw5 $frame stdxbw6 stdxbw7 stdxbw8 stdxbw9 stdxbw10 $frame stdxbw11 stdxbw12 stdxbw13 /* ============================== CONSTANTS: (in constants.hc) ============================== float ACT_STAND = 0; float ACT_RUN = 1; float ACT_SWIM_FLY = 2; float ACT_ATTACK = 3; float ACT_PAIN = 4; float ACT_JUMP = 5;//Rest of these not weapon dep. (except nec jump) float ACT_CROUCH_STAND = 6; float ACT_CROUCH_MOVE = 7; float ACT_DEAD = 8; float ACT_DECAP = 9; */ void Pal_Change_Weapon (void) { if(self.weapon==IT_WEAPON1) self.th_missile=pal_gauntlet_fire; else if(self.weapon==IT_WEAPON4) self.th_missile=pal_purifier_fire; else if(self.weapon==IT_WEAPON2) self.th_missile=pal_vorpal_fire; else self.th_missile=pal_axe_fire; } void Cru_Change_Weapon (void) { if(self.weapon==IT_WEAPON1) self.th_missile=Cru_Wham_Fire; else if(self.weapon==IT_WEAPON2) self.th_missile=Cru_Ice_Fire; else if(self.weapon==IT_WEAPON3) self.th_missile=Cru_Met_Attack; else self.th_missile=Cru_Sun_Fire; } void Nec_Change_Weapon (void) { if(self.weapon==IT_WEAPON1) self.th_missile=sickle_decide_attack; else if(self.weapon==IT_WEAPON4) self.th_missile=setstaff_decide_attack; else if(self.weapon==IT_WEAPON2) self.th_missile=Nec_Mis_Attack; else self.th_missile=Nec_Bon_Attack; } void Ass_Change_Weapon (void) { if(self.weapon==IT_WEAPON4) self.th_missile=ass_setstaff_fire; else if(self.weapon==IT_WEAPON2) self.th_missile=crossbow_fire; if(self.weapon==IT_WEAPON3) self.th_missile=grenade_throw; else self.th_missile=Ass_Pdgr_Fire; } float player_start_frames[160] = { //Stand $stdgnt1 ,$stdswd1 ,$stdswd1 ,$pstdstf1 , //Paladin $stdham1 ,$stdice1 ,$cstdstf1 ,$cstdstf1 , //Crusader $stdsic1 ,$stdhan1 ,$stdhan1 ,$nstdstf1 , //Necromancer $stddag1 ,$stdxbw1 ,$stddag1 ,$astdstf1 , //Assassin //Run $rungnt1,$runswd1,$runswd1,$prunstf1, //Paladin $runham1,$runice1,$crunstf1,$crunstf1, //Crusader $runsic1,$runhan1,$runhan1,$nrunstf1, //Necromancer $rundag1,$runxbw1,$rundag1,$arunstf1, //Assassin //Swim/fly $flygnt1,$flyswd1,$flyswd1,$pflystf1, //Paladin $flyham1,$flyice1,$cflystf1,$cflystf1, //Crusader $flysic1,$flyhan1,$flyhan1,$nflystf1, //Necromancer $flydag1,$flyxbw1,$flydag1,$aflystf1, //Assassin //Attack $attgnt1,$attswd1,$attswd1,$pattstf1, //Paladin $attham1,$attice1,$cattstf1,$cattstf1, //Crusader $attsic1,$atthan1,$atthan1,$nattstf1, //Necromancer $attdag1,$attxbw1,$attdag1,$aattstf1, //Assassin //pain $paingnt1,$painswd1,$painswd1,$ppainstf1, //Paladin $painham1,$painice1,$cpainstf1,$cpainstf1, //Crusader $painsic1,$painhan1,$painhan1,$npainstf1, //Necromancer $paindag1,$painxbw1,$paindag1,$apainstf1, //Assassin //Jump $pjump1,0,0,0, //Paladin $cjump1,0,0,0, //Crusader $runsic1,$runhan1,$runhan1,$nrunstf1, //Necromancer $ajump1,0,0,0, //Assassin //Crouch_stand $pcrouch1,0,0,0, //Paladin $ccrouch1,0,0,0, //Crusader $ncrouch1,0,0,0, //Necromancer $acrouch1,0,0,0, //Assassin //Crouch_move $pcrouch1,0,0,0, //Paladin $ccrouch1,0,0,0, //Crusader $ncrouch1,0,0,0, //Necromancer $acrouch1,0,0,0, //Assassin //dead $pdeath1,0,0,0, //Paladin $cdeath1,0,0,0, //Crusader $ndeath1,0,0,0, //Necromancer $adeath1,0,0,0, //Assassin //decap $pdecap1,0,0,0, //Paladin $cdecap1,0,0,0, //Crusader $ndecap1,0,0,0, //Necromancer $adecap1,0,0,0 //Assassin }; float player_end_frames[160] = { //Stand $stdgnt13,$stdswd13,$stdswd13,$pstdstf13, //Paladin $stdham13,$stdice13,$cstdstf13,$cstdstf13, //Crusader $stdsic12,$stdhan12,$stdhan12,$nstdstf12, //Necromancer $stddag13,$stdxbw13,$stddag13,$astdstf13, //Assassin //Run $rungnt12,$runswd12,$runswd12,$prunstf12, //Paladin $runham12,$runice12,$crunstf12,$crunstf12, //Crusader $runsic12,$runhan12,$runhan12,$nrunstf12, //Necromancer $rundag12,$runxbw12,$rundag12,$arunstf12, //Assassin //Swim/fly $flygnt15,$flyswd15,$flyswd15,$pflystf15, //Paladin $flyham15,$flyice15,$cflystf14,$cflystf14, //Crusader $flysic14,$flyhan14,$flyhan14,$nflystf14, //Necromancer $flydag15,$flyxbw15,$flydag15,$aflystf15, //Assassin //Attack $attgnt11,$attswd12,$attswd12,$pattstf4, //Paladin $attham10,$attice4,$cattstf5,$cattstf5, //Crusader $attsic12,$atthan8,$atthan8,$nattstf8, //Necromancer $attdag11,$attxbw4,$attdag11,$aattstf4, //Assassin //pain $paingnt7,$painswd7,$painswd7,$ppainstf7, //Paladin $painham8,$painice8,$cpainstf8,$cpainstf8, //Crusader $painsic8,$painhan8,$painhan8,$npainstf8, //Necromancer $paindag7,$painxbw7,$paindag7,$apainstf7, //Assassin //Jump $pjump12,0,0,0, //Paladin $cjump13,0,0,0, //Crusader $runsic12,$runhan12,$runhan12,$nrunstf12, //Necromancer $ajump12,0,0,0, //Assassin //Crouch_stand $pcrouch20,0,0,0, //Paladin $ccrouch20,0,0,0, //Crusader $ncrouch20,0,0,0, //Necromancer $acrouch20,0,0,0, //Assassin //Crouch_move $pcrouch20,0,0,0, //Paladin $ccrouch20,0,0,0, //Crusader $ncrouch20,0,0,0, //Necromancer $acrouch20,0,0,0, //Assassin //dead $pdeath20,0,0,0, //Paladin $cdeath20,0,0,0, //Crusader $ndeath20,0,0,0, //Necromancer $adeath20,0,0,0, //Assassin //decap $pdecap28,0,0,0, //Paladin $cdecap28,0,0,0, //Crusader $ndecap20,0,0,0, //Necromancer $adecap28,0,0,0 //Assassin }; void player_frames () { float weapmod, startframe,endframe,framestate; if(self.deadflag) self.act_state=ACT_DEAD; if(self.act_stateendframe || self.frame= 8)//Drop view self.view_ofs_z-=2.5; if(framestate==AF_END) { self.view_ofs='0 0 8'; self.think=PlayerDead; thinktime self : 0; } } else if(self.waterlevel>2||self.movetype==MOVETYPE_FLY)//overrides all others self.act_state=ACT_SWIM_FLY; } void() player_frames_behead = {//Note: give playerclass! self.level=player_start_frames[ACT_DECAP * 16 + (self.playerclass - 1) * 4]; self.dmg=player_end_frames[ACT_DECAP * 16 + (self.playerclass - 1) * 4]; self.cnt=0; player_behead(); }; gamecode/hc/h2/object.hc000066400000000000000000001355351444734033100153260ustar00rootroot00000000000000float SPAWNFLAG_BALLISTA_TRACK = 1; /* * obj_push() -- Allows players to push objects when they walk toward them. */ void() Missile_Arc; void(float vol) sheep_sound; void() obj_barrel_roll; void()float; void()sheep_trot; void obj_fly_hurt (entity loser) {//MG //FIXME: Check for sky //dprint("hit\n"); float magnitude,my_mass; if(self.frozen>0&&other.classname=="snowball") return; if(self.classname=="player") my_mass=self.mass; else if(!self.mass) my_mass = 1; else if(self.mass<=10) my_mass=10; else my_mass = self.mass/10; magnitude=vlen(self.velocity)*my_mass/10; if(pointcontents(self.absmax)==CONTENT_WATER)//FIXME: or other watertypes magnitude/=3; //water absorbs 2/3 velocity if(self.classname=="barrel"&&self.aflag)//rolling barrels are made for impacts! magnitude*=3; if(self.frozen>0&&magnitude<300&&self.flags&FL_ONGROUND&&loser==world&&self.velocity_z<-20&&self.last_onground+0.3 "); dprint(loser.classname); dprint("\n"); dprint(vtos(self.velocity)); dprint("\n"); dprint("Magnitude: "); dprint(ftos(magnitude)); dprint("\n"); dprint("Air time: "); dprint(ftos(time-self.last_onground)); dprint("\n"); dprint("Time since last impact: "); dprint(ftos(time-self.last_impact)); dprint("\n"); */ if(self.last_onground+0.3=THINGTYPE_GLASS)) { vector dir1, dir2; float force,dot; if(loser.thingtype>=THINGTYPE_GLASS) magnitude*=2; if(magnitude>=100&&loser.takedamage&&loser.classname!="catapult"&&loser!=world) { dir1=normalize(self.velocity); if(loser.origin=='0 0 0') dir2=dir1; else dir2=normalize(loser.origin-self.origin); dot= dir1*dir2; if(dot >= 0.2) force=dot; else force=0; force*=magnitude/50; if(pointcontents(loser.absmax)==CONTENT_WATER||(self.classname=="barrel"&&self.aflag))//FIXME: or other watertypes force/=3; //water absorbs 2/3 velocity if(self.flags&FL_MONSTER&&loser==world) force/=2; if(self.frozen>0&&force>10) force=10; if((force>=1&&loser.classname!="player")||force>=10) { /* dprint("Damage other ("); dprint(loser.classname); dprint("): "); dprint(ftos(force)); dprint("\n"); */ T_Damage (loser, self, self, force); } } if(self.classname!="monster_mezzoman"&&self.netname!="spider")//Cats always land on their feet if((magnitude>=100+self.health&&self.classname!="player")||magnitude>=700)//health here is used to simulate structural integrity { if(self.classname=="player"&&self.flags&FL_ONGROUND&&magnitude<1000) { //allow for some lenience on high falls magnitude/=2; if(self.absorb_time>=time)//crouching on impact absorbs 1/2 the damage magnitude/=2; } magnitude/=40; magnitude=magnitude - force/2;//If damage other, subtract half of that damage off of own injury if(magnitude>=1) { //FIXME: Put in a thingtype impact sound function /* dprint("Damage self ("); dprint(self.classname); dprint("): "); dprint(ftos(magnitude)); dprint("\n"); */ if(self.classname=="player_sheep"&&self.flags&FL_ONGROUND&&self.velocity_z>-50) return; T_Damage(self,world,world,magnitude); } } self.last_impact=time; if(self.flags&FL_ONGROUND) self.last_onground=time; } } void obj_push() {//MG vector pushdir,pushangle; float ontop,pushed,inertia,force,walkforce; float content; if(other.solid==SOLID_PHASE||other.movetype==MOVETYPE_FLYMISSILE||other.movetype==MOVETYPE_BOUNCEMISSILE) return; if(self.classname=="barrel"&&pointcontents(self.origin)!=CONTENT_EMPTY&&(!self.spawnflags&BARREL_SINK)) { self.classname="barrel_floating"; self.think=float; self.nextthink=time; } if(self.classname=="player_sheep"&&other.classname=="catapult") self.spawnflags(+)1; //Sheep won't move once on catapult if(self.last_impact + 0.1<=time) obj_fly_hurt(other); // if (other.classname != "player"&&!(other.flags&FL_MONSTER)&&(!other.flags&FL_PUSH)&&other.movetype!=MOVETYPE_PUSHPULL) // return; if(other!=world&&other.absmin_z >= self.origin_z+self.maxs_z - 5&&other.velocity_z<1) { if(!other.frozen&& ( (!other.flags2&FL_ALIVE&&other.flags&FL_MONSTER)|| (self.flags&FL_MONSTER&&self.model!="models/spider.mdl"&&self.model!="models/scorpion.mdl") ) ) { makevectors(other.angles); v_forward_z=1; other.velocity=v_forward*300; other.flags(-)FL_ONGROUND; } if(other.flags&FL_CLIENT&&!other.frozen) ontop = FALSE; else { ontop=TRUE; // other.flags(+)FL_ONGROUND; } } if(self.flags&FL_MONSTER) { if(other!=world&&self.absmin_z >= other.absmax_z - 3&&self.velocity_z<1&&other.movetype!=MOVETYPE_FLYMISSILE&&other.movetype!=MOVETYPE_BOUNCE&&other.movetype!=MOVETYPE_BOUNCEMISSILE) self.flags(+)FL_ONGROUND; if(self.frozen<=0&&!self.artifact_active&ARTFLAG_STONED) return; } if(!other.velocity) return; if (self.impulse == 20) return; if(!self.mass) inertia=1; if(self.mass<=30) inertia=self.mass/3; else inertia=self.mass/33; if(other.strength) force=vlen(other.velocity)*(other.strength/40+0.5); else force=vlen(other.velocity); content=pointcontents(self.origin); if(content==CONTENT_WATER||content==CONTENT_SLIME||content==CONTENT_LAVA) force/=3; //FIXME: mass should determine how fast the object can be pushed // if (self.mass > 199*force) // Too heavy to move if (self.mass >= 1000) // Too heavy to move return; //So you can push frozen guys around before they melt if(self.frozen>0) { self.freeze_time=time+10; self.wait=time + 10; } if(ontop) return; walkforce=force/inertia/40;//20 is the frame time... if(self.classname=="barrel"&&self.aflag) { vector dir1, dir2; float dot_forward,dot_right; self.angles_z=0; self.v_angle_x=self.v_angle_z=0; makevectors(self.v_angle); if(ontop) dir1=normalize(other.velocity); else dir1=normalize(self.origin-other.origin); dir2=normalize(v_forward); dir1_z=dir2_z=0; dot_forward= dir1*dir2; self.enemy=other; if(dot_forward>=0.9) { self.movedir=dir2;//test self.movedir_z=0; self.speed += force/inertia; if(self.speed>other.strength*300) self.speed=other.strength*300; traceline(self.origin,self.origin+dir2*48,FALSE,self); if(trace_fraction==1.0) { self.velocity=self.movedir*self.speed; self.think=obj_barrel_roll; self.nextthink=time; } } else if(dot_forward<=-0.9) { self.movedir=dir2;//test self.movedir_z=0; self.speed += force/inertia*-1; if(self.speed0.2) { if(dot_forward >0.1) { self.angles_y-=walkforce*10; } else if(dot_forward<-0.1) { self.angles_y+=walkforce*10; } } else if(dot_right<-0.2) { if(dot_forward >0.1) { self.angles_y+=walkforce*10; } else if(dot_forward<-0.1) { self.angles_y-=walkforce*10; } } self.v_angle_y=self.angles_y; } } else { pushdir=normalize(other.velocity); pushangle=vectoangles(pushdir); pushed=FALSE; walkforce=force/inertia/20;//20 is the frame time... if(!walkmove(pushangle_y,walkforce,FALSE))//FIXME: check mass { if(other.absmax_z<=self.origin_z+self.mins_z*0.75) pushdir_z*=2; self.velocity=(pushdir*force*2*(1/inertia)+self.velocity)*0.5; if(self.flags&FL_ONGROUND) { if(self.velocity_z<0) self.velocity_z=0; self.flags(-)FL_ONGROUND; } if(self.velocity) pushed=TRUE; } else pushed=TRUE; if(pushed&&self.classname!="barrel_floating") { if(self.pain_finished<=time) { if(self.classname=="player_sheep") { sheep_sound(.75); if(!infront(other)&&random()<0.5) //FIXME- find current think and set transition to run self.think=sheep_trot; } else if(self.thingtype==THINGTYPE_WOOD) { sound(self,CHAN_VOICE,"misc/pushwood.wav",1,ATTN_NORM); self.pain_finished=time + 1.041; } else if ((self.thingtype==THINGTYPE_GREYSTONE) || (self.thingtype==THINGTYPE_BROWNSTONE)) { sound(self,CHAN_VOICE,"misc/pushston.wav",1,ATTN_NORM); self.pain_finished=time + .711; } else if(self.thingtype==THINGTYPE_METAL) { sound(self,CHAN_VOICE,"misc/pushmetl.wav",1,ATTN_NORM); self.pain_finished=time + .835; } } } } } /*QUAKED obj_chair (0.3 0.1 0.6) (-10 -10 -5) (10 10 40) A wooden chair. -------------------------FIELDS------------------------- none -------------------------------------------------------- */ void obj_chair() { precache_model("models/chair.mdl"); CreateEntityNew(self,ENT_CHAIR,"models/chair.mdl",chunk_death); self.touch = obj_push; self.flags (+) FL_PUSH; } /*QUAKED obj_barstool (0.3 0.1 0.6) (-10 -10 -5) (10 10 32) A bar stool - Drinks on the house! -------------------------FIELDS------------------------- none -------------------------------------------------------- */ void obj_barstool() { precache_model3("models/stool.mdl"); CreateEntityNew(self,ENT_BARSTOOL,"models/stool.mdl",chunk_death); self.touch = obj_push; self.flags (+) FL_PUSH; } /*QUAKED obj_tree (0.3 0.1 0.6) (-42 -42 0) (42 42 160) A tree that has no leaves -------------------------FIELDS------------------------- health : 1000 -------------------------------------------------------- */ void obj_tree() { precache_model2("models/tree.mdl"); CreateEntityNew(self,ENT_TREEDEAD,"models/tree.mdl",chunk_death); } void tree2_death (void) { self.owner.nextthink = time + .01; self.owner.think = chunk_death; chunk_death(); } /*QUAKED obj_tree2 (0.3 0.1 0.6) (-140 -140 -16) (140 140 220) A tree with a round top of leaves -------------------------FIELDS------------------------- health : 1000 -------------------------------------------------------- */ void obj_tree2() { entity top; precache_model("models/tree2.mdl"); CreateEntityNew(self,ENT_TREE,"models/tree2.mdl",tree2_death); top = spawn(); top.scale = self.scale; CreateEntityNew(top,ENT_TREETOP,top.model,tree2_death); top.origin = self.origin; if (self.scale) // Move top according to scale top.origin_z += top.scale * 104; else top.origin_z += 104; top.health = self.health; top.classname = "tree2top"; top.owner = self; self.owner = top; } /*QUAKED obj_bench (0.3 0.1 0.6) (-30 -30 0) (30 30 40) A wooden bench -------------------------FIELDS------------------------- none -------------------------------------------------------- */ void obj_bench() { precache_model3("models/bench.mdl"); CreateEntityNew(self,ENT_BENCH,"models/bench.mdl",chunk_death); self.touch = obj_push; } /*QUAKED obj_cart (0.3 0.1 0.6) (-36 -32 -10) (36 75 64) A cart -------------------------FIELDS------------------------- none -------------------------------------------------------- */ void obj_cart() { precache_model("models/cart.mdl"); CreateEntityNew(self,ENT_CART,"models/cart.mdl",chunk_death); self.hull=HULL_SCORPION; self.touch = obj_push; self.flags (+) FL_PUSH; } /*QUAKED obj_chest1 (0.3 0.1 0.6) (-16 -16 0) (16 16 32) A treasure chest -------------------------FIELDS------------------------- skin - 0 - generic texture (default) 1 - roman texture -------------------------------------------------------- */ void obj_chest1() { precache_model("models/chest1.mdl"); CreateEntityNew(self,ENT_CHEST1,"models/chest1.mdl",chunk_death); self.touch = obj_push; self.flags (+) FL_PUSH; } /*QUAKED obj_chest2 (0.3 0.1 0.6) (-16 -16 0) (16 16 32) A treasure chest on legs -------------------------FIELDS------------------------- skin - 0 - generic texture (default) 1 - meso texture 2 - egypt texture -------------------------------------------------------- */ void obj_chest2() { precache_model3("models/chest2.mdl"); CreateEntityNew(self,ENT_CHEST2,"models/chest2.mdl",chunk_death); self.touch = obj_push; self.flags (+) FL_PUSH; } /*QUAKED obj_chest3 (0.3 0.1 0.6) (-16 -16 0) (16 16 32) A treasure chest on legs -------------------------FIELDS------------------------- none -------------------------------------------------------- */ void obj_chest3() { precache_model2("models/chest3.mdl"); CreateEntityNew(self,ENT_CHEST3,"models/chest3.mdl",chunk_death); self.touch = obj_push; self.flags (+) FL_PUSH; } /* void boulder_fall (void) { if(!self.flags&FL_ONGROUND||self.frags=0) self.velocity_z-=10; } self.think=boulder_fall; self.nextthink=time+0.1; } void boulder_push() { if(self.velocity) self.avelocity=self.velocity*-1; obj_fly_hurt(other); if(other.movetype&&other.velocity!='0 0 0'&&other.solid!=SOLID_TRIGGER&&other.solid!=SOLID_PHASE&&other.solid) { if(!walkmove(other.angles_y,1,TRUE)) { dprint("can't walk\n"); self.velocity=(other.velocity+self.velocity)*0.5; self.frags=time+0.01; } if(self.flags&FL_ONGROUND) { self.movetype=MOVETYPE_BOUNCEMISSILE;//movetype_slide- no friction self.flags(-)FL_ONGROUND; } } } */ /*QUAK-ED obj_boulder (0.3 0.1 0.6) (-32 -32 -32) (32 32 32) A big freaking rock -------------------------FIELDS------------------------- health = 75 mass = 25 -------------------------------------------------------- */ /* void obj_boulder (void) { precache_model2("models/boulder.mdl"); CreateEntityNew(self,ENT_BOULDER,"models/boulder.mdl",chunk_death); self.flags (+) FL_PUSH; self.touch = obj_push; // self.think=boulder_fall; // self.nextthink=time+0.1; } */ /*QUAKED obj_sword (0.3 0.1 0.6) (-16 -16 -8) (16 16 8) A sword -------------------------FIELDS------------------------- health = 50 -------------------------------------------------------- */ void obj_sword (void) { precache_model("models/sword.mdl"); CreateEntityNew(self,ENT_SWORD,"models/sword.mdl",chunk_death); if(self.targetname) self.use=chunk_death; } /* void BalBoltStick (void) { vector dir; self.velocity='0 0 0'; self.movetype=MOVETYPE_NONE; self.solid=SOLID_BBOX; self.takedamage=DAMAGE_YES; if (!self.health) self.health=10; self.th_die=chunk_death; makevectors(self.angles); dir=normalize(v_forward); if(pointcontents(self.origin+dir*24)!=CONTENT_SOLID) remove(self); } */ void BalBoltTouch (void) { if(other.takedamage) { //FIXME: Sound vector dir; if(other!=self.goalentity&&self.velocity!='0 0 0') { self.goalentity=other; dir=normalize(self.velocity); traceline(self.origin-dir*25,self.origin+dir*25,FALSE,self); if(other.thingtype==THINGTYPE_FLESH) MeatChunks (trace_endpos,self.velocity*0.5+'0 0 200', 3,trace_ent); SpawnPuff (trace_endpos, self.velocity*0.5+'0 0 200', self.dmg,trace_ent); T_Damage(other,self,self.owner.enemy.enemy,self.dmg); } self.think=chunk_death; thinktime self : 0.2; if(other.solid==SOLID_BSP||normalize(self.velocity)!=self.movedir) chunk_death(); } else chunk_death(); /* self.touch=SUB_Null; self.think=BalBoltStick; self.nextthink=time+0.1; } else { //FIXME: Sound vector dir; makevectors(self.angles); dir=normalize(v_forward); self.dmg/=1.2; if(self.movetype!=MOVETYPE_BOUNCE) if(pointcontents(self.origin+dir*52)==CONTENT_SOLID) { setorigin(self,self.origin+dir*8); self.avelocity=self.velocity='0 0 0'; self.touch=SUB_Null; self.movetype=MOVETYPE_NONE; self.dmg=0; self.think=SUB_Remove; self.nextthink=time + 2; } else { self.movetype=MOVETYPE_BOUNCE; self.avelocity=RandomVector('360 360 360'); } } */ } void FireBalBolt (void) { vector org; newmis=spawn(); newmis.owner=self; makevectors(self.angles); org=self.origin+self.proj_ofs+v_forward*10; if(self.spawnflags&SPAWNFLAG_BALLISTA_TRACK) { newmis.velocity=normalize((self.enemy.absmax+self.enemy.absmin)*0.5-org)*1000; } else { newmis.velocity=normalize(self.view_ofs-org)*1000; traceline(org,org+newmis.velocity,FALSE,self); if(trace_ent!=self.goalentity&&self.goalentity.health) newmis.velocity=normalize((self.goalentity.absmin+self.goalentity.absmax)*0.5-org)*1000; /* if(deathmatch) { newmis.think=Missile_Arc; newmis.nextthink=time+0.2; } */ } newmis.movedir=normalize(newmis.velocity); newmis.movetype=MOVETYPE_FLYMISSILE; newmis.solid=SOLID_PHASE; newmis.thingtype=THINGTYPE_WOOD; newmis.touch=BalBoltTouch; newmis.angles=vectoangles(newmis.velocity); newmis.avelocity_z=500; newmis.dmg=self.dmg; newmis.goalentity=newmis; setmodel(newmis,"models/balbolt.mdl"); setsize(newmis,'0 0 0','0 0 0'); setorigin(newmis,org); } void()ballista_think; void() ballista_fire = [++ 1 .. 30 ] { if (self.frame == 2) sound (self, CHAN_WEAPON, "weapons/ballista.wav", 1, ATTN_NORM); else if (self.frame==4) FireBalBolt(); else if (self.frame == 15) sound (self, CHAN_WEAPON, "weapons/ballwind.wav", 1, ATTN_NORM); if(cycle_wrapped) { self.frame=0; self.last_attack=time; if(self.spawnflags&SPAWNFLAG_BALLISTA_TRACK) self.think=ballista_think; else self.think=SUB_Null; // return; // else if(self.oldthink) // { // self.think=self.oldthink; // self.nextthink=time+1; // } } }; void ballista_think() { entity targ; float pitchmod,bestdist,lastdist; vector my_pitch, ideal_pitch; if(!self.enemy||!visible(self.enemy)||!self.enemy.flags2&FL_ALIVE&&!self.enemy.artifact_active&ART_INVISIBILITY&&!self.enemy.artifact_active&ART_INVINCIBILITY) { // dprint("looking\n"); self.enemy=targ = world; bestdist=9999; loop { targ = find (targ, classname, "player"); if(targ==world) break; if(visible(targ)&&targ.flags2&FL_ALIVE&&!targ.artifact_active&ART_INVISIBILITY&&!targ.artifact_active&ART_INVINCIBILITY) { lastdist=vlen(targ.origin-self.origin); if(lastdistmy_pitch_z) { if(ideal_pitch_z-my_pitch_z>self.count) pitchmod=self.count; else pitchmod=ideal_pitch_z-my_pitch_z; self.angles_z+=pitchmod; } else if(ideal_pitch_zself.count) pitchmod=self.count; else pitchmod=my_pitch_z-ideal_pitch_z; self.angles_z-=pitchmod; } if(self.last_attack+self.speedself.friction) other.flags(-)FL_ONGROUND; } void ice_slab_melt (void) { if(self.scale>0.05) { self.scale-=0.05; setsize(self,self.mins*0.9,self.maxs*0.9); thinktime self : 0.1; } else remove(self); } void obj_ice (void) { //thingtype_ice, need ice chunks if(self.flags2&FL_SUMMONED) { //Make pushable, floating?! self.solid = SOLID_BBOX; self.movetype = MOVETYPE_NONE; self.drawflags(+)SCALE_TYPE_XYONLY; self.scale = 1; self.think = ice_slab_melt; thinktime self : 10; } else { self.solid = SOLID_BSP; self.movetype = MOVETYPE_PUSH; } self.takedamage=DAMAGE_YES; self.thingtype = THINGTYPE_ICE; setorigin (self, self.origin); setmodel (self, self.model); self.frozen=TRUE; self.classname="ice"; self.use = chunk_death; self.drawflags(+)MLS_ABSLIGHT; if(!self.abslight) self.abslight = 0.75; if(!self.spawnflags&1) self.drawflags(+)DRF_TRANSLUCENT; if(!self.health) self.health = 20; self.max_health = self.health; if(!self.friction) self.friction = 0.2; // self.touch = friction_change_touch; self.touch = ice_touch; self.th_die = chunk_death; } /*QUAKED obj_beefslab (0.3 0.1 0.6) (-16 -16 0) (16 16 40) A slab of beef. -------------------------FIELDS------------------------- health = 50 -------------------------------------------------------- */ void obj_beefslab (void) { precache_model3("models/beefslab.mdl"); CreateEntityNew(self,ENT_BEEFSLAB,"models/beefslab.mdl",chunk_death); } /*QUAKED obj_seaweed (0.3 0.1 0.6) (-8 -8 0) (8 8 32) An animate seaweed that sways from side to side. -------------------------FIELDS------------------------- health = 10 -------------------------------------------------------- */ void obj_seaweed (void) { precache_model("models/seaweed.mdl"); CreateEntityNew(self,ENT_SEAWEED,"models/seaweed.mdl",chunk_death); } /*QUAKED obj_statue_lion (0.3 0.1 0.6) (-56 -14 0) (56 14 60) Statue of a lion. -------------------------FIELDS------------------------- health = 200 -------------------------------------------------------- */ void obj_statue_lion(void) { precache_model2("models/lion.mdl"); CreateEntityNew(self,ENT_STATUE_LION,"models/lion.mdl",chunk_death); self.drawflags (+) SCALE_ORIGIN_BOTTOM; } /*QUAKED obj_statue_athena(0.3 0.1 0.6) (-30 -30 0) (30 30 90) Statue of a Athena -------------------------FIELDS------------------------- health = 200 -------------------------------------------------------- */ void obj_statue_athena (void) { precache_model2("models/athena.mdl"); CreateEntityNew(self,ENT_STATUE_ATHENA,"models/athena.mdl",chunk_death); self.drawflags (+) SCALE_ORIGIN_BOTTOM; } /*QUAKED obj_statue_neptune(0.3 0.1 0.6) (-30 -30 0) (30 30 100) Statue of Neptune (I think) -------------------------FIELDS------------------------- health = 200 -------------------------------------------------------- */ void obj_statue_neptune (void) { precache_model2("models/neptune.mdl"); CreateEntityNew(self,ENT_STATUE_NEPTUNE,"models/neptune.mdl",chunk_death); self.drawflags (+) SCALE_ORIGIN_BOTTOM; } /*QUAKED obj_bonepile(0.3 0.1 0.6) (-10 -10 0) (10 10 10) A pile of bones. Uses the model of the puzzle keep1 -------------------------FIELDS------------------------- health = -------------------------------------------------------- */ void obj_bonepile (void) { precache_model3("models/bonepile.mdl"); CreateEntityNew(self,ENT_BONEPILE,"models/bonepile.mdl",chunk_death); self.use = chunk_death; self.drawflags (+) SCALE_ORIGIN_BOTTOM; } /*QUAKED obj_statue_caesar(0.3 0.1 0.6) (-24 -24 0) (24 24 90) Statue of a Caesar Romero -------------------------FIELDS------------------------- health = 200 -------------------------------------------------------- */ void obj_statue_caesar (void) { precache_model2("models/caesar.mdl"); CreateEntityNew(self,ENT_STATUE_CAESAR,"models/caesar.mdl",chunk_death); self.drawflags (+) SCALE_ORIGIN_BOTTOM; } /*QUAKED obj_statue_snake_coil (0.3 0.1 0.6) (-44 -44 0) (44 44 90) Statue of a coiled snake (just like the one that comes to life) but this one doesn't come to life. -------------------------FIELDS------------------------- health = 200 -------------------------------------------------------- */ void obj_statue_snake_coil (void) { precache_model2 ("models/snake.mdl"); CreateEntityNew(self,ENT_STATUE_SNAKE_COIL,"models/snake.mdl",chunk_death); self.scale = .5; self.drawflags (+) SCALE_ORIGIN_BOTTOM; } /*QUAKED obj_skull (0.3 0.1 0.6) (-8 -8 0) (8 8 16) A skull, suitable for over the fireplace or perhaps a colorful holiday display -------------------------FIELDS------------------------- health = 10 -------------------------------------------------------- */ void obj_skull (void) { precache_model("models/skull.mdl"); CreateEntityNew(self,ENT_SKULL,"models/skull.mdl",chunk_death); } /*QUAKED obj_pew (0.3 0.1 0.6) (-16 -40 0) (16 40 50) A church pew - like you might find in a church. -------------------------FIELDS------------------------- health = 50 -------------------------------------------------------- */ void obj_pew (void) { precache_model("models/pew.mdl"); CreateEntityNew(self,ENT_PEW,"models/pew.mdl",chunk_death); } /*QUAKED obj_statue_olmec (0.3 0.1 0.6) (-40 -40 0) (40 40 130) A olmec statue, of course. What the heck is an olmec? -------------------------FIELDS------------------------- health = 50 -------------------------------------------------------- */ void obj_statue_olmec (void) { precache_model2("models/olmec1.mdl"); CreateEntityNew(self,ENT_STATUE_OLMEC,"models/olmec1.mdl",chunk_death); } /*QUAKED obj_statue_mars (0.3 0.1 0.6) (-30 -30 0) (30 30 80) A statue of Mars. -------------------------FIELDS------------------------- health = 200 -------------------------------------------------------- */ void obj_statue_mars (void) { precache_model2("models/mars.mdl"); CreateEntityNew(self,ENT_STATUE_MARS,"models/mars.mdl",chunk_death); } /*QUAKED obj_playerhead_paladin (0.3 0.1 0.6) (-8 -8 0) (8 8 16) The head of the paladin. -------------------------FIELDS------------------------- health = 20 -------------------------------------------------------- */ void obj_playerhead_paladin (void) { precache_model("models/h_pal.mdl"); CreateEntityNew(self,ENT_PLAYERHEAD,"models/h_pal.mdl",chunk_death); self.use=chunk_death; } /*QUAKED obj_playerhead_assassin (0.3 0.1 0.6) (-8 -8 0) (8 8 16) The head of the assassin. -------------------------FIELDS------------------------- health = 20 -------------------------------------------------------- */ void obj_playerhead_assassin (void) { precache_model("models/h_ass.mdl"); CreateEntityNew(self,ENT_PLAYERHEAD,"models/h_ass.mdl",chunk_death); self.use=chunk_death; } /*QUAKED obj_playerhead_necromancer (0.3 0.1 0.6) (-8 -8 0) (8 8 16) The head of the necromancer. -------------------------FIELDS------------------------- health = 20 -------------------------------------------------------- */ void obj_playerhead_necromancer (void) { precache_model ("models/h_nec.mdl"); CreateEntityNew(self,ENT_PLAYERHEAD,"models/h_nec.mdl",chunk_death); self.use=chunk_death; } /*QUAKED obj_playerhead_crusader (0.3 0.1 0.6) (-8 -8 0) (8 8 16) The head of the crusader. -------------------------FIELDS------------------------- health = 20 -------------------------------------------------------- */ void obj_playerhead_crusader (void) { precache_model ("models/h_cru.mdl"); CreateEntityNew(self,ENT_PLAYERHEAD,"models/h_cru.mdl",chunk_death); self.use=chunk_death; } /*QUAKED obj_statue_king (0.3 0.1 0.6) (-30 -30 0) (30 30 120) A statue of a king holding a sword in front of him. -------------------------FIELDS------------------------- health = 200 -------------------------------------------------------- */ void obj_statue_king (void) { precache_model3("models/king.mdl"); CreateEntityNew(self,ENT_STATUE_KING,"models/king.mdl",chunk_death); self.mins -= '0 0 80'; self.maxs -= '0 0 80'; setsize(self, self.mins, self.maxs); } /*QUAKED obj_plant_generic (0.3 0.1 0.6) (-10 -10 0) (10 10 20) A generic plant that should have some kind of pot placed below it. -------------------------FIELDS------------------------- health = 20 -------------------------------------------------------- */ void obj_plant_generic (void) { precache_model ("models/plantgen.mdl"); CreateEntityNew(self,ENT_PLANT_GENERIC,"models/plantgen.mdl",chunk_death); } /*QUAKED obj_plant_meso (0.3 0.1 0.6) (-10 -10 0) (10 10 40) A generic plant that should have some kind of pot placed below it. -------------------------FIELDS------------------------- health = 20 -------------------------------------------------------- */ void obj_plant_meso (void) { precache_model2("models/plantmez.mdl"); CreateEntityNew(self,ENT_PLANT_MESO,"models/plantmez.mdl",chunk_death); } /*QUAKED obj_plant_rome (0.3 0.1 0.6) (-24 -24 0) (24 24 90) A plant for the Rome area. -------------------------FIELDS------------------------- health = 20 -------------------------------------------------------- */ void obj_plant_rome (void) { precache_model2("models/plantrom.mdl"); CreateEntityNew(self,ENT_PLANT_ROME,"models/plantrom.mdl",chunk_death); } gamecode/hc/h2/path.hc000066400000000000000000000050071444734033100150020ustar00rootroot00000000000000/* ============= pathcorner_touch Something has bumped into a path_corner. If it is a monster change to the next target and continue. ============== */ void() pathcorner_touch = { local entity temp; if (other.pathentity != self) // This corner was not targeted by this monster return; if (other.enemy) return; // fighting, not following a path if(self.wait==-2&&other.flags&FL_MONSTER) remove(other); temp = self; self = other; other = temp; self.goalentity = self.pathentity = find (world, targetname, other.target); self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin); if (!self.pathentity) { self.pausetime = time + 999999; self.th_stand (); return; } }; /* ============================================================================== TARGET CODE The angle of the pathentity effects standing and bowing direction, but has no effect on movement, which always heads to the next target. targetname must be present. The name of this pathentity. target the next spot to move to. If not present, stop here for good. pausetime The number of seconds to spend standing or bowing for path_stand or path_bow ============================================================================== */ float SYNCH = 1; /*QUAKED path_corner (0.5 0.3 0) (-8 -8 -8) (8 8 8) SYNCH Monsters will continue walking towards the next target corner. FOR TRAINS: -------------------------FIELDS------------------------- SYNCH - Will make the train automatically calculate a new anglespeed based on the distance it's going and will finish the turn at the same time the move is done. "speed" - how fast the train should move to this path corner. This will change the train's default speed to whatever you specify. Nice for trains that should speed up and slow down. No speed will let the train move at whatever speed it was last set to. "angles" - how much to modify the train's angles by. So if you set it at '0 90 0', and the train was at an angle of '30 60 90', the train would rotate unitl it's angles equalled '30 150 90' "anglespeed" - how fast the train should rotate to the new angle. Again, this will change the train's default anglespeed. The defaults of all of these are 0. -------------------------------------------------------- As usual, any rotating brush needs an origin brush. */ void path_corner () { if (!self.targetname) objerror ("path_corner has no targetname"); if(!self.mangle) self.mangle=self.angles; self.solid = SOLID_TRIGGER; self.touch = pathcorner_touch; setsize (self, '-8 -8 -8', '8 8 8'); } gamecode/hc/h2/plaque.hc000066400000000000000000000041371444734033100153400ustar00rootroot00000000000000float PLAQUE_INVISIBLE = 1; float PLAQUE_ACTIVATE = 2; /* ================ plague_use Activate a plaque ================ */ void plaque_use (void) { if (self.spawnflags & PLAQUE_ACTIVATE) self.inactive = 0; } void plaque_touch (void) { vector spot1, spot2; if (self.inactive) return; if ((other.classname == "player") && (!other.plaqueflg)) { makevectors (other.v_angle); spot1 = other.origin + other.view_ofs; spot2 = spot1 + (v_forward*25); // Look just a little ahead traceline (spot1, spot2 , FALSE, other); if ((trace_fraction == 1.0) || (trace_ent.classname!="plaque")) { traceline (spot1, spot2 - (v_up * 30), FALSE, other); // 30 down if ((trace_fraction == 1.0) || (trace_ent.classname!="plaque")) { traceline (spot1, spot2 + v_up * 30, FALSE, other); // 30 up if ((trace_fraction == 1.0) || (trace_ent.classname!="plaque")) return; } } other.plaqueflg = 1; other.plaqueangle = other.v_angle; msg_entity = other; plaque_draw(MSG_ONE,self.message); if (other.noise1 != "") sound (other, CHAN_VOICE, self.noise1, 1, ATTN_NORM); else sound (other, CHAN_ITEM, self.noise, 1, ATTN_NORM); } } /*QUAKED plaque (.5 .5 .5) ? INVISIBLE deactivated A plaque on the wall a player can read -------------------------FIELDS------------------------- "message" the index of the string in the text file "noise1" the wav file activated when plaque is used deactivated - if this is on, the plaque will not be readable until a trigger has activated it. -------------------------------------------------------- */ void() plaque = { setsize (self, self.mins, self.maxs); setorigin (self, self.origin); setmodel (self, self.model); self.solid = SOLID_SLIDEBOX; if (deathmatch) // I don't do a remove because they might be a part of the architecture return; self.use = plaque_use; precache_sound("raven/use_plaq.wav"); self.noise = "raven/use_plaq.wav"; self.touch = plaque_touch; if (self.spawnflags & PLAQUE_INVISIBLE) self.effects (+) EF_NODRAW; if (self.spawnflags & PLAQUE_ACTIVATE) self.inactive = 1; else self.inactive = 0; }; gamecode/hc/h2/plats.hc000066400000000000000000000577651444734033100152130ustar00rootroot00000000000000void() newplat_center_touch; void() newplat_go_up; void() newplat_go_down; void() plat_center_touch; void() plat_outside_touch; void() plat_trigger_use; void() plat_go_up; void() plat_go_down; void() plat_crush; float PLAT_LOW_TRIGGER = 1; void() crusher_hit_bottom; void() crusher_hit_top; void() crusher_trigger_use; void() crusher_go_up; void() crusher_go_down; float CRUSH_MULT = 1; float CRUSH_SLIDE = 2; float CRUSH_START_OPEN = 4; float CRUSH_ENDPOS = 8; float START_BOTTOM = 1; float START_RTRN= 2; float CONTINUE= 4; void() train_wait; float TRAIN_GLOW = 1; float TRAIN_WAITTRIG = 2; float TRAIN_RETURN = 4; void() plat_spawn_inside_trigger = { local entity trigger; local vector tmin, tmax; //middle trigger trigger = spawn(); if (self.classname == "newplat") trigger.touch = newplat_center_touch; else trigger.touch = plat_center_touch; trigger.movetype = MOVETYPE_NONE; trigger.solid = SOLID_TRIGGER; trigger.enemy = self; tmin = self.mins + '25 25 0'; tmax = self.maxs - '25 25 -8'; tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8); if (self.spawnflags & PLAT_LOW_TRIGGER) tmax_z = tmin_z + 8; if (self.size_x <= 50) { tmin_x = (self.mins_x + self.maxs_x) / 2; tmax_x = tmin_x + 1; } if (self.size_y <= 50) { tmin_y = (self.mins_y + self.maxs_y) / 2; tmax_y = tmin_y + 1; } setsize (trigger, tmin, tmax); }; void() plat_hit_top = { sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); self.state = STATE_TOP; self.think = plat_go_down; self.nextthink = self.ltime + 3; }; void() plat_hit_bottom = { sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); self.state = STATE_BOTTOM; }; void() plat_go_down = { sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); self.state = STATE_DOWN; SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom); }; void() plat_go_up = { sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); self.state = STATE_UP; SUB_CalcMove (self.pos1, self.speed, plat_hit_top); }; void() plat_center_touch = { if (other.classname != "player"&&other.movetype!=MOVETYPE_PUSHPULL)//Monsters too? return; if (other.health <= 0) return; self = self.enemy; if (self.state == STATE_BOTTOM) plat_go_up (); else if (self.state == STATE_TOP) self.nextthink = self.ltime + 1; // delay going down }; void() plat_outside_touch = { if (other.classname != "player"&&other.movetype!=MOVETYPE_PUSHPULL) return; if (other.health <= 0) return; //dprint ("plat_outside_touch\n"); self = self.enemy; if (self.state == STATE_TOP) plat_go_down (); }; void() plat_trigger_use = { if (self.think) return; // already activated plat_go_down(); }; void() plat_crush = { T_Damage (other, self, self, 1); if (self.state == STATE_UP) plat_go_down (); else if (self.state == STATE_DOWN) plat_go_up (); else objerror ("plat_crush: bad self.state\n"); }; void() plat_use = { self.use = SUB_Null; if (self.state != STATE_UP) objerror ("plat_use: not in up state"); plat_go_down(); }; /*QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER speed default 150 Plats are always drawn in the extended position, so they will light correctly. If the plat is the target of another trigger or button, it will start out disabled in the extended position until it is trigger, when it will lower and become a normal plat. If the "height" key is set, that will determine the amount the plat moves, instead of being implicitly determined by the model's height. Set "soundtype" to one of the following: 1) pulley 2) chain */ void() func_plat = { if (!self.t_length) self.t_length = 80; if (!self.t_width) self.t_width = 10; if (self.soundtype == 0) self.soundtype = 2; // FIX THIS TO LOAD A GENERIC PLAT SOUND if (self.soundtype == 1) { precache_sound ("plats/pulyplt1.wav"); precache_sound ("plats/pulyplt2.wav"); self.noise = "plats/pulyplt1.wav"; self.noise1 = "plats/pulyplt2.wav"; } if (self.soundtype == 2) { precache_sound ("plats/chainplt1.wav"); precache_sound ("plats/chainplt2.wav"); self.noise = "plats/chainplt1.wav"; self.noise1 = "plats/chainplt2.wav"; } self.mangle = self.angles; self.angles = '0 0 0'; self.classname = "plat"; self.solid = SOLID_BSP; self.movetype = MOVETYPE_PUSH; setorigin (self, self.origin); setmodel (self, self.model); setsize (self, self.mins , self.maxs); self.blocked = plat_crush; if (!self.speed) self.speed = 150; // pos1 is the top position, pos2 is the bottom self.pos1 = self.origin; self.pos2 = self.origin; if (self.height) self.pos2_z = self.origin_z - self.height; else self.pos2_z = self.origin_z - self.size_z + 8; self.use = plat_trigger_use; plat_spawn_inside_trigger (); // the "start moving" trigger if (self.targetname) { self.state = STATE_UP; self.use = plat_use; } else { setorigin (self, self.pos2); self.state = STATE_BOTTOM; } }; //============================================================================ void() train_next; void() func_train_find; void() train_blocked = { if (time < self.attack_finished) return; self.attack_finished = time + 0.5; T_Damage (other, self, self, self.dmg); }; void() train_use = { if(self.wait==-1) self.use=SUB_Null; if (self.spawnflags & TRAIN_GLOW) { self.effects = EF_BRIGHTLIGHT; } if (self.decap != 1) { self.decap = 1; if (self.think != train_next) { self.think = func_train_find; train_next(); } } else { if (self.spawnflags & TRAIN_RETURN) self.decap = 0; else self.decap = 2; } }; void() train_wait = { //Check to make sure train is active if(self.decap!=2) { self.think = train_next; if(self.wait==-2) { if(self.th_die) { if(self.pausetime) { self.think=self.th_die; self.nextthink=self.ltime+self.pausetime; } else { self.th_die(); return; } } else { if(self.pausetime) { self.think=chunk_death; self.nextthink=self.ltime+self.pausetime; } else { chunk_death(); return; } } } else if(self.wait==-1) self.nextthink=-1; else if (self.wait) { self.nextthink = self.ltime + self.wait; sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); } else self.nextthink = self.ltime + 0.1; } if (self.decap == 0 || self.decap == 2) self.effects = 0; }; void() train_rotate = { local entity targ; local vector dir; targ = self.enemy; if (targ.mangle_x != 0 || targ.mangle_y != 0 || targ.mangle_z != 0) { dir = self.angles; dir += targ.mangle; SUB_CalcAngleMove (dir, self.speed, train_wait); } else train_wait(); }; void() train_next = { local entity targ; local vector dir; float targ_speed, targ_aspeed; targ = find (world, targetname, self.target); self.target = targ.target; if (!self.decap && self.spawnflags & TRAIN_RETURN) if (self.netname == targ.targetname) self.decap = 2; if (!self.target) objerror ("train_next: no next target"); if (targ.wait) self.wait = targ.wait; else self.wait = 0; self.enemy = targ; sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); if (targ.mangle_x != 0 || targ.mangle_y != 0 || targ.mangle_z != 0) { dir = self.angles; dir += targ.mangle; if(targ.speed) targ_speed=targ.speed; else targ_speed = self.speed; if(targ.anglespeed) targ_aspeed=targ.anglespeed; else targ_aspeed = self.anglespeed; if(targ.spawnflags&SYNCH) SUB_CalcMoveAndAngleInit (targ.origin - self.mins, targ_speed, dir, targ_aspeed, train_wait,TRUE); else SUB_CalcMoveAndAngleInit (targ.origin - self.mins, targ_speed, dir, targ_aspeed, train_wait,FALSE); } else SUB_CalcMove (targ.origin - self.mins, self.speed, train_rotate); if (self.spawnflags & TRAIN_WAITTRIG) self.decap = 2; }; void() func_train_find = { local entity targ; targ = find (world, targetname, self.target); self.target = targ.target; setorigin (self, targ.origin - self.mins); if (!self.targetname) { // not triggered, so start immediately self.decap = 1; self.nextthink = self.ltime + 0.1; self.think = train_next; } }; /*QUAKED func_train (0 .5 .8) ? GLOW TOGGLE RETURN TRANSLUCENT Trains are moving platforms that players can ride. The targets origin specifies the min point of the train at each corner. The train spawns at the first target it is pointing at. If the train is the target of a button or trigger, it will not begin moving until activated. speed default 100 dmg default 2 soundtype 1) ratchet metal if train is only moving to one spot "angle" - to tell it's direction "distance" - in pixels, how far to move "speed" - how fast it moves between spots (default=100) "anglespeed" - how fast it rotates to a new angle (default = 100) "wait" - -1 will make it stop forever, -2 will make it blow up (you can put the waits on the pathcorners and it will take the wait from there. "pausetime" - How long to wait after getting to the end of it's path before blowing up, default is 0 NOTE: If you give it a wait of -2, be sure to set the thingtype. thingtype - type of chunks and sprites it will generate 0 - glass 1 - grey stone (default for trains) 2 - wood 3 - metal 4 - flesh 5 - fire 6 - clay 7 - leaves 8 - hay 9 - brown stone 10 - cloth 11 - wood & leaf 12 - wood & metal 13 - wood stone 14 - metal stone 15 - metal cloth The train will modify it's angles by whatever angles it's next path point has, so if it heads towards a path corner with an angle of '0 90 0', the train will rotate '0 90 0' on it's way to the pathpoint. If you make the anglespeed the same as the angle, the turn should finish right as the train gets to the new spot. NOTE: A path_corner using spawnflag "SYNCH" will make the train automatically calculate a new anglespeed based on the distance it's going and will finish the turn at the same time the move is done. As usual, any rotating brush needs an origin brush. "abslight" - to set the absolute light level if TRAIN_GLOW is checked, changes to a light globe sprite and lights up an area */ void() func_train = { local entity targ; self.decap = 0; if (self.spawnflags & TRAIN_GLOW) { self.solid = SOLID_NOT; setmodel (self, "models/s_light.spr"); } else { self.solid = SOLID_BSP; setmodel (self, self.model); } if (!self.speed) self.speed = 100; if (!self.anglespeed) self.anglespeed = 100; if (!self.target) objerror ("func_train without a target"); if (!self.dmg) self.dmg = 2; if (self.soundtype == 1) { self.noise = ("plats/train2.wav"); precache_sound ("plats/train2.wav"); self.noise1 = ("plats/train1.wav"); precache_sound ("plats/train1.wav"); } else { self.noise = self.noise1 = "misc/null.wav"; precache_sound ("misc/null.wav"); } if(self.wait==-2) { if(!self.thingtype) self.thingtype=1; if(!self.th_die) self.th_die=chunk_death; } self.cnt = 1; self.movetype = MOVETYPE_PUSH; self.blocked = train_blocked; self.use = train_use; self.classname = "train"; setsize (self, self.mins , self.maxs); setorigin (self, self.origin); targ = find(world, target, self.target); self.netname = targ.target; if (self.abslight) self.drawflags(+)MLS_ABSLIGHT; if (self.spawnflags & 8) { self.drawflags(+)DRF_TRANSLUCENT; self.solid = SOLID_NOT; } // start trains on the second frame, to make sure their targets have had // a chance to spawn self.nextthink = self.ltime + 0.1; self.think = func_train_find; }; /*QUAK-ED misc_teleporttrain (0 .5 .8) (-8 -8 -8) (8 8 8) This is used for the final bos */ /* void() misc_teleporttrain = { if (!self.speed) self.speed = 100; if (!self.target) objerror ("func_train without a target"); self.cnt = 1; self.solid = SOLID_NOT; self.movetype = MOVETYPE_PUSH; self.blocked = train_blocked; self.use = train_use; self.avelocity = '100 200 300'; self.noise = ("misc/null.wav"); precache_sound ("misc/null.wav"); self.noise1 = ("misc/null.wav"); precache_sound ("misc/null.wav"); precache_model2 ("models/teleport.mdl"); setmodel (self, "models/teleport.mdl"); setsize (self, self.mins , self.maxs); setorigin (self, self.origin); // start trains on the second frame, to make sure their targets have had // a chance to spawn self.nextthink = self.ltime + 0.1; self.think = func_train_find; }; */ void() newplat_hit_bottom = { sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); self.state = STATE_BOTTOM; self.lifetime = time + self.wait; if (((self.spawnflags & START_RTRN) && !(self.spawnflags & START_BOTTOM)) || (self.spawnflags & CONTINUE)) { self.nextthink = self.ltime + self.wait; self.think=newplat_go_up; } setorigin (self.enemy, self.origin); }; void() newplat_hit_top = { sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); self.state = STATE_TOP; self.lifetime = time + self.wait; if (((self.spawnflags & START_RTRN) && (self.spawnflags & START_BOTTOM)) || (self.spawnflags & CONTINUE)) { self.nextthink = self.ltime + self.wait; self.think=newplat_go_down; } setorigin (self.enemy, self.origin); }; void() newplat_trigger_use = { if (self.think) return; // already activated if ((self.state==STATE_MOVING) || (self.lifetime > time)) return; if (self.state == STATE_BOTTOM) newplat_go_up (); else newplat_go_down (); }; void() newplat_calc_down = { self.state=STATE_MOVING; SUB_CalcMove (self.pos2, self.speed, newplat_hit_bottom); }; void() newplat_go_down = { sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); newplat_calc_down(); }; void() newplat_calc_up = { self.state=STATE_MOVING; SUB_CalcMove (self.pos1, self.speed, newplat_hit_top); }; void() newplat_go_up = { sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); newplat_calc_up(); }; void() newplat_crush = { T_Damage (other, self, self, 1); if (self.velocity_z < 0) newplat_calc_down (); else if (self.velocity_z > 0) newplat_calc_up(); else objerror ("newplat_crush: bad self.state\n"); }; void() newplat_center_touch = { if ((other.classname != "player"&&other.movetype!=MOVETYPE_PUSHPULL) || (other.health <= 0)) return; self = self.enemy; if ((self.state==STATE_MOVING) || (self.lifetime > time)) return; if (self.state == STATE_BOTTOM) newplat_go_up (); else newplat_go_down (); }; void() newplat_spawn_inside_trigger = { local entity trigger; //middle trigger trigger = spawn(); trigger.touch = newplat_center_touch; trigger.movetype = MOVETYPE_PUSH; trigger.solid = SOLID_TRIGGER; trigger.enemy = self; self.enemy = trigger; setsize (trigger, self.mins,self.maxs); }; /*QUAKED func_newplat (0 .5 .8) ? START_BOTTOM STRT_RTRN CONTINUE speed default 150 If the "height" key is set, that will determine the amount the plat moves, instead of being implicitly determined by the model's height. Set "soundtype" to one of the following: 1) base fast 2) chain slow START_BOTTOM - where plat starts at if checked plat starts at the bottom of it's movement START_RTRN - if check will return plat to start position. CONTINUE - plat will never stop moving height - distance plat moves up or down wait - amount of time plat waits before moving (default 3) */ void() func_newplat = { if (!self.t_length) self.t_length = 80; if (!self.t_width) self.t_width = 10; if (self.soundtype == 0) self.soundtype = 2; if (self.soundtype == 1) { precache_sound ("plats/pulyplt1.wav"); precache_sound ("plats/pulyplt2.wav"); self.noise = "plats/pulyplt1.wav"; self.noise1 = "plats/pulyplt2.wav"; } if (self.soundtype == 2) { precache_sound ("plats/chainplt1.wav"); precache_sound ("plats/chainplt2.wav"); self.noise = "plats/chainplt1.wav"; self.noise1 = "plats/chainplt2.wav"; } self.mangle = self.angles; self.angles = '0 0 0'; self.classname = "newplat"; self.solid = SOLID_BSP; self.movetype = MOVETYPE_PUSH; setorigin (self, self.origin); setmodel (self, self.model); setsize (self, self.mins , self.maxs); if (!self.speed) self.speed = 150; if (!self.wait) self.wait = 3; // pos1 is the top position, pos2 is the bottom self.pos1 = self.origin; self.pos2 = self.origin; if (self.spawnflags & START_BOTTOM) self.state=STATE_BOTTOM; else self.state=STATE_TOP; if (self.state==STATE_BOTTOM) { self.pos1_z = self.origin_z + self.height; self.pos2_z = self.origin_z; } else { self.pos1_z = self.origin_z; self.pos2_z = self.origin_z - self.height; } self.use = newplat_trigger_use; self.blocked = newplat_crush; newplat_spawn_inside_trigger (); //set the "start moving" trigger }; /* =============================================================================== FUNC_CRUSHER =============================================================================== */ void() crusher_slide_next = { local vector vdestdelta; local float len, tspeed; tspeed = self.speed; if (!tspeed) objerror("No speed defined!"); //Make sure we're not already at the destination if (self.finaldest == self.origin) { self.velocity = '0 0 0'; if (self.state == STATE_DOWN) self.think = crusher_hit_bottom; else if (self.state == STATE_UP) self.think = crusher_hit_top; self.nextthink = self.ltime + 0.1; return; } //Set destdelta to the vector needed to move vdestdelta = self.finaldest - self.origin; //Get the length of the vector len = vlen (vdestdelta); //If the length is this small, just stop if (len < 1) { if (self.state == STATE_DOWN) crusher_hit_bottom(); else if (self.state == STATE_UP) crusher_hit_top(); else dprint("NO STATE\n"); return; } self.nextthink = self.ltime + 0.1; self.think = crusher_slide_next; self.velocity = vdestdelta * ((len / (len / 3)) / (self.speed / 100)); }; void(vector tdest) crusher_slide = { self.finaldest = tdest; crusher_slide_next(); }; void() crusher_hit_top = { sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); self.state = STATE_TOP; if (self.spawnflags & CRUSH_MULT) return; if (!self.level) { self.think = crusher_go_down; self.nextthink = self.ltime + 1; } else self.nextthink = -1; }; void() crusher_hit_bottom = { sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); self.state = STATE_BOTTOM; if (self.level && self.spawnflags & CRUSH_ENDPOS) return; self.think = crusher_go_up; self.nextthink = self.ltime + 1; }; void() crusher_go_down = { sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); self.state = STATE_DOWN; if (self.spawnflags & CRUSH_SLIDE) crusher_slide(self.pos2); else SUB_CalcMove (self.pos2, self.speed, crusher_hit_bottom); }; void() crusher_go_up = { sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); self.state = STATE_UP; if (self.spawnflags & CRUSH_SLIDE) crusher_slide(self.pos1); else SUB_CalcMove (self.pos1, self.speed, crusher_hit_top); }; void() crusher_trigger_use = { if (!self.level) self.level = TRUE; else self.level = FALSE; //HCC doesn't like these together in one statement if (self.think) if (self.spawnflags & CRUSH_MULT) return; // already activated crusher_go_down(); }; void() crusher_crush = { //Crusher does not return like a plat, it just keeps on crushin' T_Damage (other, self, self, self.dmg); }; void() crusher_use = { if (!self.level) self.level = TRUE; else self.level = FALSE; crusher_go_down(); }; /*QUAKED func_crusher (0 .5 .8) ? multiple slide start_open end_open speed default 150 dmg default 10 If not targetname is given, crushers will start working immediatly Crushers are always drawn in the extended position, so they will light correctly. start_open = start in open position multiple = go once, return, and wait to be triggered again slide = slide move (like doors) end_open = stop in the position opposite what they were drawn in "lip" same as doors "speed" speed of the crusher "wait pause until going in the other direction "targetname" if set, no trigger is needed (use with multiple) "dmg" damage the crusher does to a victim Set "soundtype" to one of the following: 1) base fast 2) chain slow 3) Guillotine */ void() func_crusher = { SetMovedir(); if (self.soundtype == 0) self.soundtype = 2; if (self.soundtype == 1) { precache_sound ("plats/plat1.wav"); precache_sound ("plats/plat2.wav"); self.noise = "plats/plat1.wav"; self.noise1 = "plats/plat2.wav"; } else if (self.soundtype == 2) { precache_sound ("plats/medplat1.wav"); precache_sound ("plats/medplat2.wav"); self.noise = "plats/medplat1.wav"; self.noise1 = "plats/medplat2.wav"; } else if (self.soundtype == 3) { precache_sound3 ("plats/guiltin1.wav"); precache_sound3 ("plats/guiltin2.wav"); self.noise = "plats/guiltin1.wav"; self.noise1 = "plats/guiltin2.wav"; } self.mangle = self.angles; self.angles = '0 0 0'; self.classname = "crusher"; self.solid = SOLID_BSP; self.movetype = MOVETYPE_PUSH; setorigin (self, self.origin); setmodel (self, self.model); setsize (self, self.mins , self.maxs); self.level = TRUE; if (!self.dmg) self.dmg = 10; self.blocked = crusher_crush; if (!self.speed) self.speed = 150; self.pos1 = self.origin; self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip); if (self.spawnflags & CRUSH_START_OPEN) { setorigin (self, self.pos2); self.pos2 = self.pos1; self.pos1 = self.origin; } self.use = crusher_trigger_use; if (self.targetname) { self.state = STATE_UP; self.use = crusher_use; } else { setorigin (self, self.pos2); self.state = STATE_BOTTOM; self.nextthink = self.ltime + 0.1; self.think = crusher_use; } }; void reset_solid (void) { } void rot_mov_dmg (void) { if(other==world) return; if(other.classname=="player") { self.solid=SOLID_TRIGGER; self.think=reset_solid; thinktime self : 0.1; } if(other.takedamage) { if(self.noise1) sound(self,CHAN_VOICE,self.noise1,1,ATTN_NORM); self.pain_finished=time+self.wait; T_Damage(other,self,self.owner,self.dmg); } } void rot_mov_snd (void) { if(self.pain_finished<=time) { sound(self,CHAN_VOICE,self.noise,1,ATTN_NORM); self.pain_finished=time+self.wait; } self.think=rot_mov_snd; thinktime self : self.wait; } void rot_mov_activate (void) { if(self.dmg) self.touch=rot_mov_dmg; if(!self.avelocity) self.avelocity=self.o_angle; if(self.noise) if(!self.wait) objerror ("func_rotating_movechain: sound, but no delay time"); else { self.think=rot_mov_snd; thinktime self : 0; } } float NOANGLECHAIN = 1; /*QUAKED func_rotating_movechain (0 .5 .8) ? NOANGLECHAIN Only one other object in the world should have the same netname. If not, it will find and use only the first one it finds! If you're making multiple sawblades, for instance, label the mover and the rotater "sawblade1" for the first one, "sawblade2" for the second, and so on. If you give it a targetname, it will wait to be activated, this can be seperate from the object it's attached to. It will not do damage until it's been activated. NOANGLECHAIN = Setting this flag will stop it from modifying it's angles by the owner's angles, but will still movechain. dmg = How much damage it should do when it touches. noise = Noise it should make, if any, be sure to set the wait time noise1 = noise it should make when it hits something wait = Length of the sound so it knows when to loop it. avelocity = The direction and speed it should spin: pitch yaw roll (this is relative to it's own axis, not the world) netname = the name of the object it's linked to, that object must have a matching netname!!! Needs something to tell it to stop? A way to make it die at the end of a path or if triggered again? Maybe make movechain_angle optional spawnflag? What do YOU think? We'd like to know... */ void func_rotating_movechain (void) { if(!self.netname) objerror ("func_rotating_movechain has no netname"); self.owner=find(world,netname,self.netname); if(self.owner.classname=="world") objerror ("func_rotating_movechain has no owner!"); self.solid=SOLID_TRIGGER; self.movetype=MOVETYPE_NOCLIP; setmodel(self,self.model); setsize(self,self.mins,self.maxs); setorigin(self,self.origin); //dprint(vtos(self.avelocity)); self.owner.movechain=self; if(!self.spawnflags&NOANGLECHAIN) self.flags+=FL_MOVECHAIN_ANGLE; if(self.targetname) { self.use=rot_mov_activate; self.o_angle=self.avelocity; self.avelocity='0 0 0'; } else { self.think=rot_mov_activate; thinktime self : 3;//wait a few secs for board to start } } gamecode/hc/h2/precache.hc000066400000000000000000001202421444734033100156170ustar00rootroot00000000000000// called by worldspawn void() W_Precache = { precache_sound ("raven/kiltorch.wav"); // player torch dying precache_sound ("raven/littorch.wav"); // player torch being lit precache_sound ("raven/fire1.wav"); // player torch burning precache_sound ("rj/steve.wav"); precache_sound ("weapons/ric1.wav"); // ricochet (used in c code) precache_sound ("weapons/ric2.wav"); // ricochet (used in c code) precache_sound ("weapons/ric3.wav"); // ricochet (used in c code) precache_sound ("weapons/tink1.wav"); // ricochet (used in c code) precache_sound ("weapons/r_exp3.wav"); //Still being used? // precache_sound ("items/inv2.wav"); //Quake sounds! precache_model ("gfx/puff.spr"); precache_model ("models/ball.mdl"); precache_model ("models/star.mdl"); }; // // these are all of the lumps from the cached.ls files // void Precache_lmp (void) { precache_file ("gfx/palette.lmp"); precache_file ("gfx/colormap.lmp"); precache_file ("gfx/player.lmp"); precache_file ("gfx/tinttab.lmp"); precache_file ("gfx/tinttab2.lmp"); precache_file ("gfx/invpal.lmp"); precache_file ("gfx/skin100.lmp"); precache_file ("gfx/skin101.lmp"); precache_file ("strings.txt"); precache_file ("puzzles.txt"); precache_file ("maplist.txt"); precache_file ("gfx/menu/fontsize.lmp"); precache_file ("gfx/menu/backtile.lmp"); precache_file ("gfx/menu/bigfont.lmp"); precache_file ("gfx/menu/bigfont2.lmp"); precache_file ("gfx/menu/conback.lmp"); precache_file ("gfx/menu/conchars.lmp"); precache_file ("gfx/menu/fontsize.lmp"); precache_file ("gfx/menu/help01.lmp"); precache_file ("gfx/menu/help02.lmp"); precache_file ("gfx/menu/help03.lmp"); precache_file ("gfx/menu/help04.lmp"); precache_file ("gfx/menu/help05.lmp"); precache_file ("gfx/menu/hplaque.lmp"); precache_file ("gfx/menu/load.lmp"); precache_file ("gfx/menu/loading.lmp"); precache_file ("gfx/menu/menudot1.lmp"); precache_file ("gfx/menu/menudot2.lmp"); precache_file ("gfx/menu/menudot3.lmp"); precache_file ("gfx/menu/menudot4.lmp"); precache_file ("gfx/menu/menudot5.lmp"); precache_file ("gfx/menu/menudot6.lmp"); precache_file ("gfx/menu/menudot7.lmp"); precache_file ("gfx/menu/menudot8.lmp"); precache_file ("gfx/menu/paused.lmp"); precache_file ("gfx/menu/save.lmp"); precache_file ("gfx/menu/skull0.lmp"); precache_file ("gfx/menu/skull1.lmp"); precache_file ("gfx/menu/skull10.lmp"); precache_file ("gfx/menu/skull11.lmp"); precache_file ("gfx/menu/skull12.lmp"); precache_file ("gfx/menu/skull13.lmp"); precache_file ("gfx/menu/skull14.lmp"); precache_file ("gfx/menu/skull15.lmp"); precache_file ("gfx/menu/skull16.lmp"); precache_file ("gfx/menu/skull17.lmp"); precache_file ("gfx/menu/skull2.lmp"); precache_file ("gfx/menu/skull3.lmp"); precache_file ("gfx/menu/skull4.lmp"); precache_file ("gfx/menu/skull5.lmp"); precache_file ("gfx/menu/skull6.lmp"); precache_file ("gfx/menu/skull7.lmp"); precache_file ("gfx/menu/skull8.lmp"); precache_file ("gfx/menu/skull9.lmp"); precache_file ("gfx/menu/title0.lmp"); precache_file ("gfx/menu/title1.lmp"); precache_file ("gfx/menu/title2.lmp"); precache_file ("gfx/menu/title3.lmp"); precache_file ("gfx/menu/title4.lmp"); precache_file ("gfx/menu/title5.lmp"); precache_file ("gfx/menu/title6.lmp"); precache_file ("gfx/menu/title7.lmp"); precache_file ("gfx/menu/title8.lmp"); precache_file ("gfx/box_tl.lmp"); precache_file ("gfx/box_tm.lmp"); precache_file ("gfx/box_tr.lmp"); precache_file ("gfx/box_ml.lmp"); precache_file ("gfx/box_mm.lmp"); precache_file ("gfx/box_mm2.lmp"); precache_file ("gfx/box_mr.lmp"); precache_file ("gfx/box_bl.lmp"); precache_file ("gfx/box_bm.lmp"); precache_file ("gfx/box_br.lmp"); precache_file ("gfx/cport1.lmp"); precache_file ("gfx/cport2.lmp"); precache_file ("gfx/cport3.lmp"); precache_file ("gfx/cport4.lmp"); precache_file ("gfx/menu/netp1.lmp"); precache_file ("gfx/menu/netp2.lmp"); precache_file ("gfx/menu/netp3.lmp"); precache_file ("gfx/menu/netp4.lmp"); precache_file ("gfx/menu/frame.lmp"); // Interface graphics precache_file ("gfx/topbar1.lmp"); precache_file ("gfx/topbar2.lmp"); precache_file ("gfx/topbumpl.lmp"); precache_file ("gfx/topbumpm.lmp"); precache_file ("gfx/topbumpr.lmp"); precache_file ("gfx/bmana.lmp"); precache_file ("gfx/bmanacov.lmp"); precache_file ("gfx/gmana.lmp"); precache_file ("gfx/gmanacov.lmp"); precache_file ("gfx/hpchain.lmp"); precache_file ("gfx/hpgem.lmp"); precache_file ("gfx/chnlcov.lmp"); precache_file ("gfx/chnrcov.lmp"); precache_file ("gfx/bmmana.lmp"); precache_file ("gfx/gmmana.lmp"); precache_file ("gfx/btmbar1.lmp"); precache_file ("gfx/btmbar2.lmp"); precache_file ("gfx/armor1.lmp"); precache_file ("gfx/armor2.lmp"); precache_file ("gfx/armor3.lmp"); precache_file ("gfx/armor4.lmp"); precache_file ("gfx/ring_f.lmp"); precache_file ("gfx/ring_w.lmp"); precache_file ("gfx/ring_t.lmp"); precache_file ("gfx/ring_r.lmp"); precache_file ("gfx/artisel.lmp"); precache_file ("gfx/artinum0.lmp"); precache_file ("gfx/artinum1.lmp"); precache_file ("gfx/artinum2.lmp"); precache_file ("gfx/artinum3.lmp"); precache_file ("gfx/artinum4.lmp"); precache_file ("gfx/artinum5.lmp"); precache_file ("gfx/artinum6.lmp"); precache_file ("gfx/artinum7.lmp"); precache_file ("gfx/artinum8.lmp"); precache_file ("gfx/artinum9.lmp"); precache_file ("gfx/rngfly1.lmp"); precache_file ("gfx/rngfly2.lmp"); precache_file ("gfx/rngfly3.lmp"); precache_file ("gfx/rngfly4.lmp"); precache_file ("gfx/rngfly5.lmp"); precache_file ("gfx/rngfly6.lmp"); precache_file ("gfx/rngfly7.lmp"); precache_file ("gfx/rngfly8.lmp"); precache_file ("gfx/rngfly9.lmp"); precache_file ("gfx/rngfly10.lmp"); precache_file ("gfx/rngfly11.lmp"); precache_file ("gfx/rngfly12.lmp"); precache_file ("gfx/rngfly13.lmp"); precache_file ("gfx/rngfly14.lmp"); precache_file ("gfx/rngfly15.lmp"); precache_file ("gfx/rngfly16.lmp"); precache_file ("gfx/rngwtr1.lmp"); precache_file ("gfx/rngwtr2.lmp"); precache_file ("gfx/rngwtr3.lmp"); precache_file ("gfx/rngwtr4.lmp"); precache_file ("gfx/rngwtr5.lmp"); precache_file ("gfx/rngwtr6.lmp"); precache_file ("gfx/rngwtr7.lmp"); precache_file ("gfx/rngwtr8.lmp"); precache_file ("gfx/rngwtr9.lmp"); precache_file ("gfx/rngwtr10.lmp"); precache_file ("gfx/rngwtr11.lmp"); precache_file ("gfx/rngwtr12.lmp"); precache_file ("gfx/rngwtr13.lmp"); precache_file ("gfx/rngwtr14.lmp"); precache_file ("gfx/rngwtr15.lmp"); precache_file ("gfx/rngwtr16.lmp"); precache_file2 ("gfx/rngtrn1.lmp"); precache_file2 ("gfx/rngtrn2.lmp"); precache_file2 ("gfx/rngtrn3.lmp"); precache_file2 ("gfx/rngtrn4.lmp"); precache_file2 ("gfx/rngtrn5.lmp"); precache_file2 ("gfx/rngtrn6.lmp"); precache_file2 ("gfx/rngtrn7.lmp"); precache_file2 ("gfx/rngtrn8.lmp"); precache_file2 ("gfx/rngtrn9.lmp"); precache_file2 ("gfx/rngtrn10.lmp"); precache_file2 ("gfx/rngtrn11.lmp"); precache_file2 ("gfx/rngtrn12.lmp"); precache_file2 ("gfx/rngtrn13.lmp"); precache_file2 ("gfx/rngtrn14.lmp"); precache_file2 ("gfx/rngtrn15.lmp"); precache_file2 ("gfx/rngtrn16.lmp"); /* precache_file2 ("gfx/rngreg1.lmp"); precache_file2 ("gfx/rngreg2.lmp"); precache_file2 ("gfx/rngreg3.lmp"); precache_file2 ("gfx/rngreg4.lmp"); precache_file2 ("gfx/rngreg5.lmp"); precache_file2 ("gfx/rngreg6.lmp"); precache_file2 ("gfx/rngreg7.lmp"); precache_file2 ("gfx/rngreg8.lmp"); precache_file2 ("gfx/rngreg9.lmp"); precache_file2 ("gfx/rngreg10.lmp"); precache_file2 ("gfx/rngreg11.lmp"); precache_file2 ("gfx/rngreg12.lmp"); precache_file2 ("gfx/rngreg13.lmp"); precache_file2 ("gfx/rngreg14.lmp"); precache_file2 ("gfx/rngreg15.lmp"); precache_file2 ("gfx/rngreg16.lmp");*/ precache_file ("gfx/pwrbook1.lmp"); precache_file ("gfx/pwrbook2.lmp"); precache_file ("gfx/pwrbook3.lmp"); precache_file ("gfx/pwrbook4.lmp"); precache_file ("gfx/pwrbook5.lmp"); precache_file ("gfx/pwrbook6.lmp"); precache_file ("gfx/pwrbook7.lmp"); precache_file ("gfx/pwrbook8.lmp"); precache_file ("gfx/pwrbook9.lmp"); precache_file ("gfx/pwrbook10.lmp"); precache_file ("gfx/pwrbook11.lmp"); precache_file ("gfx/pwrbook12.lmp"); precache_file ("gfx/pwrbook13.lmp"); precache_file ("gfx/pwrbook14.lmp"); precache_file ("gfx/pwrbook15.lmp"); precache_file ("gfx/pwrbook16.lmp"); precache_file ("gfx/durhst1.lmp"); precache_file ("gfx/durhst2.lmp"); precache_file ("gfx/durhst3.lmp"); precache_file ("gfx/durhst4.lmp"); precache_file ("gfx/durhst5.lmp"); precache_file ("gfx/durhst6.lmp"); precache_file ("gfx/durhst7.lmp"); precache_file ("gfx/durhst8.lmp"); precache_file ("gfx/durhst9.lmp"); precache_file ("gfx/durhst10.lmp"); precache_file ("gfx/durhst11.lmp"); precache_file ("gfx/durhst12.lmp"); precache_file ("gfx/durhst13.lmp"); precache_file ("gfx/durhst14.lmp"); precache_file ("gfx/durhst15.lmp"); precache_file ("gfx/durhst16.lmp"); precache_file ("gfx/durshd1.lmp"); precache_file ("gfx/durshd2.lmp"); precache_file ("gfx/durshd3.lmp"); precache_file ("gfx/durshd4.lmp"); precache_file ("gfx/durshd5.lmp"); precache_file ("gfx/durshd6.lmp"); precache_file ("gfx/durshd7.lmp"); precache_file ("gfx/durshd8.lmp"); precache_file ("gfx/durshd9.lmp"); precache_file ("gfx/durshd10.lmp"); precache_file ("gfx/durshd11.lmp"); precache_file ("gfx/durshd12.lmp"); precache_file ("gfx/durshd13.lmp"); precache_file ("gfx/durshd14.lmp"); precache_file ("gfx/durshd15.lmp"); precache_file ("gfx/durshd16.lmp"); precache_file ("gfx/arti00.lmp"); precache_file ("gfx/arti01.lmp"); precache_file ("gfx/arti02.lmp"); precache_file ("gfx/arti03.lmp"); precache_file ("gfx/arti04.lmp"); precache_file ("gfx/arti05.lmp"); precache_file ("gfx/arti06.lmp"); precache_file ("gfx/arti07.lmp"); precache_file ("gfx/arti08.lmp"); precache_file ("gfx/arti09.lmp"); precache_file ("gfx/arti10.lmp"); precache_file ("gfx/arti11.lmp"); precache_file ("gfx/arti12.lmp"); precache_file ("gfx/arti13.lmp"); precache_file ("gfx/arti14.lmp"); precache_file ("gfx/ringhlth.lmp"); precache_file ("gfx/rhlthcvr.lmp"); precache_file2 ("gfx/rhlthcv2.lmp"); precache_file ("gfx/castle.lmp"); precache_file2 ("gfx/meso.lmp"); precache_file2 ("gfx/egypt.lmp"); precache_file2 ("gfx/roman.lmp"); precache_file2 ("gfx/end-1.lmp"); precache_file2 ("gfx/end-2.lmp"); precache_file2 ("gfx/end-3.lmp"); /* precache_file (""); precache_file (""); precache_file (""); precache_file (""); precache_file (""); precache_file (""); precache_file (""); */ // Puzzle Pieces precache_file2("models/puzzle/staff.mdl"); precache_file2("models/puzzle/e1.mdl"); precache_file2("models/puzzle/e2.mdl"); precache_file2("models/puzzle/e3.mdl"); precache_file2("models/puzzle/e4.mdl"); precache_file2("models/puzzle/e5.mdl"); precache_file2("models/puzzle/e6.mdl"); precache_file2("models/puzzle/m1.mdl"); precache_file2("models/puzzle/m2.mdl"); precache_file2("models/puzzle/m3.mdl"); precache_file2("models/puzzle/m4.mdl"); precache_file2("models/puzzle/m5.mdl"); precache_file2("models/puzzle/s1.mdl"); precache_file2("models/puzzle/s2.mdl"); precache_file2("models/puzzle/scepter.mdl"); precache_file("models/puzzle/keep1.mdl"); precache_file("models/puzzle/keep2.mdl"); precache_file("models/puzzle/keep3.mdl"); precache_file2("models/puzzle/ra1.mdl"); precache_file2("models/puzzle/r1.mdl"); precache_file2("models/puzzle/r2.mdl"); precache_file2("models/puzzle/r3.mdl"); precache_file2("models/puzzle/r4.mdl"); precache_file2("models/puzzle/r5.mdl"); precache_file2("models/puzzle/r6.mdl"); precache_file2("models/puzzle/r7.mdl"); precache_file2("models/puzzle/r8.mdl"); precache_file3("models/puzzle/trkey.mdl"); precache_file("models/puzzle/cskey.mdl"); precache_file3("models/puzzle/amult.mdl"); precache_file3("models/puzzle/glass.mdl"); precache_file3("models/puzzle/lens.mdl"); precache_file3("models/puzzle/shovl.mdl"); precache_file3("models/puzzle/scrol.mdl"); precache_file2("models/puzzle/e4.mdl"); precache_file2("models/puzzle/e5.mdl"); precache_file2("models/puzzle/e6.mdl"); precache_file3("models/puzzle/sand.mdl"); precache_file2("models/puzzle/air.mdl"); precache_file2("models/puzzle/earth.mdl"); precache_file2("models/puzzle/water.mdl"); precache_file2("models/puzzle/fire.mdl"); precache_file2("models/puzzle/scept.mdl"); precache_file2("models/puzzle/eyeh.mdl"); precache_file2("models/puzzle/clueb.mdl"); precache_file2("models/puzzle/lcrwn.mdl"); precache_file2("models/puzzle/ucrwn.mdl"); precache_file("models/puzzle/mithl.mdl"); precache_file3("models/puzzle/stkey.mdl"); precache_file3("models/puzzle/takey.mdl"); precache_file2("models/puzzle/ankey.mdl"); precache_file2("models/puzzle/stime.mdl"); precache_file2("models/puzzle/speye.mdl"); precache_file2("models/puzzle/rcjem.mdl"); precache_file2("models/puzzle/music.mdl"); precache_file2("models/puzzle/silver.mdl"); precache_file2("models/puzzle/cross.mdl"); precache_file2("models/puzzle/holycrss.mdl"); precache_file2("models/puzzle/gsphere.mdl"); precache_file2("models/puzzle/prybar.mdl"); precache_file2("models/puzzle/soul.mdl"); precache_file2("models/puzzle/mage.mdl"); precache_file2("models/puzzle/rakey.mdl"); precache_file2("models/puzzle/h-book.mdl"); precache_file2("gfx/puzzle/staff.lmp"); precache_file2("gfx/puzzle/e1.lmp"); precache_file2("gfx/puzzle/e2.lmp"); precache_file2("gfx/puzzle/e3.lmp"); precache_file2("gfx/puzzle/e4.lmp"); precache_file2("gfx/puzzle/e5.lmp"); precache_file2("gfx/puzzle/e6.lmp"); precache_file2("gfx/puzzle/m1.lmp"); precache_file2("gfx/puzzle/m2.lmp"); precache_file2("gfx/puzzle/m3.lmp"); precache_file2("gfx/puzzle/m4.lmp"); precache_file2("gfx/puzzle/m5.lmp"); precache_file2("gfx/puzzle/s1.lmp"); precache_file2("gfx/puzzle/s2.lmp"); precache_file2("gfx/puzzle/scepter.lmp"); precache_file("gfx/puzzle/keep1.lmp"); precache_file("gfx/puzzle/keep2.lmp"); precache_file("gfx/puzzle/keep3.lmp"); precache_file2("gfx/puzzle/ra1.lmp"); precache_file2("gfx/puzzle/r1.lmp"); precache_file2("gfx/puzzle/r2.lmp"); precache_file2("gfx/puzzle/r3.lmp"); precache_file2("gfx/puzzle/r4.lmp"); precache_file2("gfx/puzzle/r5.lmp"); precache_file2("gfx/puzzle/r6.lmp"); precache_file2("gfx/puzzle/r7.lmp"); precache_file2("gfx/puzzle/r8.lmp"); precache_file3("gfx/puzzle/trkey.lmp"); precache_file("gfx/puzzle/cskey.lmp"); precache_file3("gfx/puzzle/amult.lmp"); precache_file3("gfx/puzzle/glass.lmp"); precache_file3("gfx/puzzle/lens.lmp"); precache_file3("gfx/puzzle/shovl.lmp"); precache_file3("gfx/puzzle/scrol.lmp"); precache_file2("gfx/puzzle/e4.lmp"); precache_file2("gfx/puzzle/e5.lmp"); precache_file2("gfx/puzzle/e6.lmp"); precache_file3("gfx/puzzle/sand.lmp"); precache_file2("gfx/puzzle/air.lmp"); precache_file2("gfx/puzzle/earth.lmp"); precache_file2("gfx/puzzle/water.lmp"); precache_file2("gfx/puzzle/fire.lmp"); precache_file2("gfx/puzzle/scept.lmp"); precache_file2("gfx/puzzle/eyeh.lmp"); precache_file2("gfx/puzzle/clueb.lmp"); precache_file2("gfx/puzzle/lcrwn.lmp"); precache_file2("gfx/puzzle/ucrwn.lmp"); precache_file("gfx/puzzle/mithl.lmp"); precache_file3("gfx/puzzle/stkey.lmp"); precache_file3("gfx/puzzle/takey.lmp"); precache_file2("gfx/puzzle/ankey.lmp"); precache_file2("gfx/puzzle/stime.lmp"); precache_file2("gfx/puzzle/speye.lmp"); precache_file2("gfx/puzzle/rcjem.lmp"); precache_file2("gfx/puzzle/music.lmp"); precache_file2("gfx/puzzle/silver.lmp"); precache_file2("gfx/puzzle/cross.lmp"); precache_file2("gfx/puzzle/holycrss.lmp"); precache_file2("gfx/puzzle/gsphere.lmp"); precache_file2("gfx/puzzle/prybar.lmp"); precache_file2("gfx/puzzle/soul.lmp"); precache_file2("gfx/puzzle/mage.lmp"); precache_file2("gfx/puzzle/rakey.lmp"); precache_file2("gfx/puzzle/h-book.lmp"); //RIDER_INIT precache_file3 ("models/boss/shaft.mdl"); precache_file3 ("models/boss/circle.mdl"); precache_file3 ("models/boss/star.mdl"); precache_sound3 ("famine/flashdie.wav"); //EIDOLON precache_file2 ("models/boss/smaleido.mdl"); precache_file2 ("models/boss/bigeido.mdl"); precache_file2 ("models/eidoball.mdl"); precache_file2 ("models/eidoflam.spr"); precache_file2 ("models/glowball.mdl"); precache_file2 ("models/boss/chaosorb.mdl"); precache_sound3 ("eidolon/roar.wav"); precache_sound3 ("eidolon/pain.wav"); //Hurt precache_sound3 ("eidolon/death.wav"); //Dies- long and agonizing precache_sound3 ("eidolon/fakedie.wav");//1st death- fake precache_sound3 ("eidolon/spell.wav"); //Spell attack (tracking globes) precache_sound3 ("eidolon/stomp.wav"); //Hot-steppin' precache_sound3 ("eidolon/fireball.wav"); //Launching Nasty fireballs precache_sound3 ("eidolon/flamstrt.wav"); // precache_sound3 ("eidolon/flambrth.wav"); // precache_sound3 ("eidolon/flamend.wav"); // precache_sound3 ("eidolon/growl.wav"); // precache_sound3 ("eidolon/chrgstrt.wav"); //Orb starts recharging Eido precache_sound3 ("eidolon/orbhurt.wav"); //Orb gets hit precache_sound3 ("eidolon/orbxpld.wav"); //Orb gets destroyed precache_sound3 ("eidolon/orbpulse.wav"); //Orb pulsating //PESTILENCE precache_file2 ("models/boss/boar.mdl"); precache_file2 ("models/boss/pstrider.mdl"); precache_file2 ("models/boss/hive.mdl"); precache_file2 ("models/boss/swarm.mdl"); precache_file2 ("models/boss/shaft.mdl"); precache_file2 ("models/pestshot.mdl"); precache_file2 ("sound/pest/snort.wav"); precache_file2 ("sound/pest/clop1.wav"); precache_file2 ("sound/pest/clop2.wav"); precache_file2 ("sound/pest/clop3.wav"); precache_file2 ("sound/pest/gallop.wav"); precache_file2 ("sound/pest/sight.wav"); precache_file2 ("sound/pest/sting1.wav"); precache_file2 ("sound/pest/sting2.wav"); precache_file2 ("sound/pest/sting3.wav"); precache_file2 ("sound/pest/buzz.wav"); precache_file2 ("sound/pest/hivehit.wav"); precache_file2 ("sound/pest/xbowfire.wav"); precache_file2 ("sound/pest/xbowhit.wav"); precache_file2 ("sound/pest/die.wav"); precache_file2 ("sound/pest/charge.wav"); precache_file2 ("sound/pest/laugh.wav"); precache_file2 ("sound/pest/snort2.wav"); // Famine Rider precache_file3 ("models/boss/famhorse.mdl"); precache_file3 ("models/boss/famrider.mdl"); precache_file3 ("models/famshot.mdl"); precache_file3 ("sound/famine/die.wav"); precache_file3 ("sound/famine/laugh.wav"); precache_file3 ("sound/famine/whinny.wav"); precache_file3 ("sound/famine/pull.wav"); precache_file3 ("sound/famine/shot.wav"); precache_file3 ("sound/famine/snort.wav"); precache_file3 ("sound/famine/clop1.wav"); precache_file3 ("sound/famine/clop2.wav"); precache_file3 ("sound/famine/clop3.wav"); precache_file3 ("sound/misc/null.wav"); precache_file3 ("sound/raven/blast.wav"); precache_file3 ("sound/skullwiz/blinkout.wav"); precache_file3 ("sound/skullwiz/blinkin.wav"); // War Rider precache_file2 ("models/boss/warhorse.mdl"); precache_file2 ("models/boss/warrider.mdl"); precache_file2 ("models/boss/waraxe.mdl"); precache_file2 ("sound/war/fire_big.wav"); precache_file2 ("sound/war/die.wav"); precache_file2 ("sound/war/laugh.wav"); precache_file2 ("sound/war/laugh_sm.wav"); precache_file2 ("sound/war/fire.wav"); precache_file2 ("sound/war/whinny.wav"); precache_file2 ("sound/war/whinbig.wav"); precache_file2 ("sound/boss/wartrot1.wav"); precache_file2 ("sound/boss/wartrot2.wav"); precache_file2 ("sound/boss/wartrot3.wav"); // Death Rider precache_file2 ("models/boss/dthhorse.mdl"); precache_file2 ("models/boss/dthrider.mdl"); precache_file2 ("models/famshot.mdl"); precache_file2 ("models/boss/bone1.mdl"); precache_file2 ("models/boss/bone2.mdl"); precache_file2 ("models/boss/bone3.mdl"); precache_file2 ("models/boss/bone4.mdl"); precache_file2 ("models/boss/bone5.mdl"); precache_file2 ("models/boss/bone6.mdl"); precache_file2 ("models/mumshot.mdl"); precache_file2 ("models/booberry.mdl"); precache_file2 ("sound/mummy/mislfire.wav"); precache_file2 ("sound/eidolon/flamend.wav"); precache_file2 ("sound/misc/fburn_bg.wav"); precache_file2 ("sound/death/fout.wav"); precache_file2 ("sound/death/dthdie.wav"); precache_file2 ("sound/death/dthfire.wav"); precache_file2 ("sound/death/victory.wav"); precache_file2 ("sound/death/dthlaugh.wav"); precache_file2 ("sound/death/clop.wav"); precache_file2 ("sound/death/clop1.wav"); precache_file2 ("sound/death/clop2.wav"); precache_file2 ("sound/death/clop3.wav"); precache_file2 ("sound/death/shot.wav"); precache_file2 ("sound/ambience/moan1.wav"); precache_file2 ("sound/ambience/moan2.wav"); precache_file2 ("sound/ambience/moan3.wav"); } //********************************************** // *************** Raven sounds //********************************************** void Precache_wav (void) { //DEMO //Miscellaneous precache_sound ("misc/drip.wav"); //Ambience precache_sound ("misc/bshatter.wav"); //Burnt thing breaking precache_sound ("misc/sshatter.wav"); //Stone thing breaking precache_sound ("misc/pushmetl.wav"); //Pushing different materials- metal precache_sound ("misc/pushwood.wav"); //Wood precache_sound ("misc/pushston.wav"); //Stone precache_sound ("misc/fout.wav"); //Fire doused precache_sound ("misc/fburn_sm.wav"); //Small fire burning precache_sound ("misc/fburn_md.wav"); //Medium fire buring precache_sound ("misc/fburn_bg.wav"); //Big ol' blaze! precache_sound ("misc/decomp.wav"); //Decomposing sound- actually used for blood squirts precache_sound ("misc/camera.wav"); //View through camera precache_sound ("misc/hith2o.wav"); // thing landing in water precache_sound ("misc/lighthit.wav"); //Something hit by lightning precache_sound ("misc/teleprt1.wav"); // teleport sounds- teleport coin precache_sound ("misc/teleprt2.wav"); precache_sound ("misc/teleprt3.wav"); precache_sound ("misc/teleprt4.wav"); precache_sound ("misc/teleprt5.wav"); precache_sound ("misc/comm.wav"); // communication precache_sound ("raven/squawk.wav"); //Ambient raven precache_sound ("raven/blast.wav"); precache_sound ("doors/baddoor.wav"); // Bad attempt to open a door precache_sound ("buttons/switch04.wav"); // used by the trip mine //FX precache_sound ("fx/glassbrk.wav"); // Glass breaking precache_sound ("fx/woodbrk.wav"); // Wood breaking precache_sound ("fx/wallbrk.wav"); // Stone breaking precache_sound ("fx/metalbrk.wav"); // Metal breaking precache_sound ("fx/claybrk.wav"); // Clay pot breaking precache_sound ("fx/leafbrk.wav"); // Leaves breaking (bush or tree) precache_sound ("fx/clothbrk.wav"); // Cloth breaking (rug) precache_sound ("fx/thngland.wav"); // landing thud precache_sound ("misc/null.wav"); // null sound to stop other sounds from playing //ITEMS and ARTIFACTS precache_sound ("weapons/ammopkup.wav");// Backpack pick up precache_sound ("weapons/weappkup.wav");// weapon pick up precache_sound ("items/artpkup.wav"); //Artifact pick up precache_sound ("misc/sheep1.wav"); //In case someone uses Lambinator precache_sound ("misc/sheep2.wav"); precache_sound ("misc/sheep3.wav"); precache_sound ("misc/sheepfly.wav"); //Sheep launched from catapult!!! precache_sound ("spider/bite.wav"); //Sheep bite precache_sound ("items/itempkup.wav"); precache_sound ("items/itmspawn.wav"); // item respawn sound precache_sound ("items/ringpkup.wav"); // Picking up a ring precache_sound ("items/artpkup.wav"); precache_sound ("items/armrpkup.wav"); precache_sound ("misc/warning.wav"); //glyph about to explode //Summoning Stone precache_sound ("imp/upbig.wav"); precache_sound ("imp/diebig.wav"); precache_sound ("imp/swoophit.wav"); precache_sound ("imp/swoopbig.wav"); precache_sound ("imp/flybig.wav"); precache_sound ("imp/fireball.wav"); precache_sound ("imp/shard.wav"); //SHARED PLAYER SOUNDS //General body/physics sounds precache_sound ("raven/outwater.wav"); // leaving water sound precache_sound ("raven/inh2o.wav"); // player in water precache_sound ("raven/inlava.wav"); // player enter lava precache_sound ("player/h2ojmp.wav"); // player jumping into water precache_sound ("player/swim1.wav"); // swimming precache_sound ("player/swim2.wav"); // swimming precache_sound ("player/land.wav"); //Player pain/death sounds precache_sound ("player/leave.wav"); // leaving deathmatch precache_sound ("player/telefrag.wav"); // telefrag death precache_sound ("player/decap.wav"); // Decapitation sound precache_sound ("player/megagib.wav"); //Really nasty explosive death precache_sound ("player/gib1.wav"); // player gib sound precache_sound ("player/gib2.wav"); // player gib sound precache_sound ("player/slimbrn1.wav"); // player enter slime //General weapon sounds precache_sound ("misc/whoosh.wav"); //Throwing grenades, swing weapons, etc. precache_sound ("weapons/unsheath.wav"); //Unsheath bladed weapon precache_sound ("weapons/slash.wav"); //bladed weapon cuts flesh precache_sound ("weapons/met2flsh.wav"); //Metal impact sounds precache_sound ("weapons/met2wd.wav"); precache_sound ("weapons/met2stn.wav"); precache_sound ("weapons/met2met.wav"); precache_sound ("weapons/expsmall.wav"); //Small explosion precache_sound ("weapons/explode.wav"); //Normal explosion precache_sound ("weapons/exphuge.wav"); //SHA-BOOOOMMM!!! precache_sound ("weapons/hithurt2.wav"); // Damaging non-flesh with a melee weapon precache_sound ("weapons/hitwall.wav"); // Hitting (not damaging) a wall with a melee weapon //PALADIN //Body sounds precache_sound ("player/paljmp.wav"); // player jump precache_sound ("player/pallnd.wav"); // player hurt when landing precache_sound ("player/paldieh2.wav"); // player dying in water precache_sound ("player/paldie1.wav"); // player death 1 precache_sound ("player/paldie2.wav"); // player death 2 precache_sound ("player/palpain1.wav"); // player pain 1 precache_sound ("player/palpain2.wav"); // player pain 2 precache_sound ("player/palgasp1.wav"); // little air precache_sound ("player/palgasp2.wav"); // no air precache_sound ("player/paldrown.wav"); // he's drowning precache_sound ("paladin/devine.wav"); // Devine Intervention //Weapon sounds //Gauntlets precache_sound ("weapons/gaunt1.wav"); precache_sound ("weapons/gauntht1.wav"); precache_sound ("weapons/gauntht2.wav"); //Vorpal Sword precache_sound ("weapons/vorpswng.wav"); // Vorpal sword swinging precache_sound ("weapons/vorpht1.wav"); // Vorpal sword hitting something it can damage precache_sound ("weapons/vorpht2.wav"); // Vorpal sword hitting something it cannot da precache_sound ("weapons/vorpturn.wav"); // Vorpal Sword - weapon 2 precache_sound ("weapons/vorpblst.wav"); precache_sound ("weapons/vorppwr.wav"); //Axe precache_sound ("paladin/axric1.wav"); // Double Headed Axe - weapon 3 precache_sound ("paladin/axgen.wav"); precache_sound ("paladin/axgenpr.wav"); precache_sound ("paladin/axblade.wav"); //Purifier precache_sound ("paladin/purfire.wav"); precache_sound ("paladin/purfireb.wav"); //Glyph: delayed fireball precache_sound ("weapons/fbfire.wav"); //Delayed fireball explosion sound //ASSASSIN //Body sounds precache_sound ("player/assjmp.wav"); // player jump precache_sound ("player/asslnd.wav"); // player hurt when landing precache_sound ("player/assdieh2.wav"); // player dying in water precache_sound ("player/assdie1.wav"); // player death 1 precache_sound ("player/assdie2.wav"); // player death 2 precache_sound ("player/asspain1.wav"); // player pain 1 precache_sound ("player/asspain2.wav"); // player pain 2 precache_sound ("player/assgasp1.wav"); // little air precache_sound ("player/assgasp2.wav"); // no air precache_sound ("player/assdrown.wav"); // she's drowning //Weapon sounds //Crossbow precache_sound ("assassin/arrowfly.wav"); precache_sound ("assassin/arr2flsh.wav"); precache_sound ("assassin/arr2wood.wav"); precache_sound ("assassin/arrowbrk.wav"); precache_sound ("assassin/firefblt.wav"); precache_sound ("assassin/firebolt.wav"); //Grenades precache_sound ("assassin/gbounce.wav"); //Scarab Staff precache_sound ("assassin/build.wav"); precache_sound ("assassin/pincer.wav"); precache_sound ("assassin/chntear.wav"); precache_sound ("assassin/chn2flsh.wav"); precache_sound ("assassin/chain.wav"); // precache_sound ("assassin/clink.wav"); precache_sound ("assassin/scarab.wav"); precache_sound ("assassin/scrbfly.wav"); precache_sound ("assassin/spin.wav"); precache_sound ("assassin/core.wav"); precache_sound ("misc/pulse.wav"); //Fully charged staff //REGISTERED====================================================== //Shared sounds precache_sound ("raven/soul.wav"); // noise the soul sphere makes //FX precache_sound ("fx/quake.wav"); //CRUSADER //Warhammer precache_sound ("crusader/lghtn1.wav"); precache_sound ("crusader/lghtn2.wav"); precache_sound ("raven/lightng1.wav"); //Ice Staff precache_sound ("crusader/icehit.wav"); precache_sound ("crusader/icewall.wav"); precache_sound ("crusader/icefire.wav"); precache_sound ("misc/tink.wav"); //Ice shots bounce precache_sound ("crusader/blizfire.wav"); precache_sound ("crusader/blizzard.wav"); precache_sound ("crusader/frozen.wav"); precache_sound ("misc/icestatx.wav"); //Ice statue breaking //Meteor Staff precache_sound ("crusader/metfire.wav"); precache_sound ("misc/rubble.wav"); //Meteor bits fall, stoned player bits fall (from Medusa) precache_sound ("crusader/torngo.wav"); precache_sound ("crusader/tornado.wav"); //Sunstaff precache_sound ("crusader/sunstart.wav"); precache_sound ("crusader/sunhum.wav"); precache_sound ("crusader/sunhit.wav"); //NECROMANCER //Sickle precache_sound ("weapons/drain.wav"); //Magic Missiles precache_sound ("necro/mmfire.wav"); //Bone Shards precache_sound ("necro/bonefpow.wav"); precache_sound ("necro/bonefnrm.wav"); precache_sound ("necro/bonephit.wav"); precache_sound ("necro/bonenhit.wav"); precache_sound ("necro/bonenwal.wav"); //Raven Staff precache_sound ("raven/ravengo.wav"); precache_sound ("raven/squawk2.wav"); precache_sound ("raven/death.wav"); precache_sound ("raven/rfire1.wav"); precache_sound ("raven/rfire2.wav"); precache_sound ("raven/split.wav"); } //********************************************** // *************** Id models //********************************************** void Precache_Id_mdl (void) { //REMOVE!!!! precache_model ("models/s_light.spr"); // sphere light } //********************************************** // *************** Raven models //********************************************** void Precache_mdl (void) { //REMOVE!!! precache_model("models/akarrow.mdl");//Mummy, archer, pstboar precache_model("models/dthball.mdl");//Goes in dthfire.hc //MISC precache_model ("models/teleport.mdl"); //Teleport model precache_model("models/xhair.mdl"); //Ballista- REPLACE!!! precache_model ("models/spike.mdl"); //CHUNKS precache_model("models/shard1.mdl"); precache_model("models/shard2.mdl"); precache_model("models/shard3.mdl"); precache_model("models/shard4.mdl"); precache_model("models/shard5.mdl"); precache_model("models/splnter1.mdl"); precache_model("models/splnter2.mdl"); precache_model("models/splnter3.mdl"); precache_model("models/splnter4.mdl"); precache_model("models/metlchk1.mdl"); precache_model("models/metlchk2.mdl"); precache_model("models/metlchk3.mdl"); precache_model("models/metlchk4.mdl"); precache_model("models/leafchk1.mdl"); precache_model("models/leafchk2.mdl"); precache_model("models/leafchk3.mdl"); precache_model("models/clthchk1.mdl"); precache_model("models/clthchk2.mdl"); precache_model("models/clthchk3.mdl"); precache_model("models/flesh1.mdl"); precache_model("models/flesh2.mdl"); precache_model("models/flesh3.mdl"); precache_model("models/brains.mdl"); precache_model("models/clshard1.mdl"); precache_model("models/clshard2.mdl"); precache_model("models/clshard3.mdl"); precache_model("models/clshard4.mdl"); precache_model("models/hay1.mdl"); precache_model("models/hay2.mdl"); precache_model("models/hay3.mdl"); precache_model("models/shard.mdl"); //shard model for ice, rock, ashes //ARTIFACTS precache_model("models/a_shbost.mdl"); precache_model("models/a_hboost.mdl"); precache_model("models/a_torch.mdl"); precache_model("models/a_blast.mdl"); precache_model("models/a_mboost.mdl"); precache_model("models/a_telprt.mdl"); precache_model("models/a_tome.mdl"); precache_model("models/a_summon.mdl"); // precache_model("models/a_mine.mdl"); precache_model("models/a_glyph.mdl"); precache_model("models/a_haste.mdl"); precache_model("models/a_poly.mdl"); // precache_model("models/a_mirror.mdl"); precache_model("models/a_cube.mdl"); precache_model("models/a_invinc.mdl"); // precache_model("models/a_growth.mdl"); // precache_model("models/a_xray.mdl"); precache_model("models/a_invis.mdl"); precache_model("models/cube.mdl"); precache_model("models/ringft.mdl"); //Lambinator precache_model("models/sheep.mdl"); precache_model("models/snout.mdl"); //Summoning Stone precache_model ("models/imp.mdl"); precache_model ("models/h_imp.mdl");//empty for now precache_model ("models/shardice.mdl"); precache_model ("models/fireball.mdl"); //ITEMS precache_model("models/i_bracer.mdl"); // Armor precache_model("models/i_bplate.mdl"); precache_model("models/i_helmet.mdl"); precache_model("models/i_amulet.mdl"); precache_model ("models/i_gmana.mdl"); // Instant Mana precache_model ("models/i_bmana.mdl"); precache_model ("models/i_btmana.mdl"); precache_model ("models/i_hboost.mdl"); // Instant Health precache_model ("models/bag.mdl"); // Our version of a backpack //TE_STREAM models // TE_STREAM_SUNSTAFF1 / TE_STREAM_SUNSTAFF2 precache_model ("models/stsunsf1.mdl"); //Sunbeam and ball models precache_model ("models/stsunsf2.mdl"); precache_model ("models/stsunsf3.mdl"); precache_model ("models/stsunsf4.mdl"); precache_model ("models/stsunsf5.mdl"); // TE_STREAM_LIGHNING precache_model ("models/stlghtng.mdl"); //Lightning- also warhammer // TE_STREAM_CHAIN precache_model("models/stchain.mdl"); //Chain- also for Scarab staff // TE_STREAM_COLORBEAM precache_model ("models/stclrbm.mdl"); //Colored beams of light // TE_STREAM_ICECHUNKS precache_model("models/stice.mdl"); //For blizzard // TE_STREAM_GAZE precache_model("models/stmedgaz.mdl"); //Medusa's gaze // TE_STREAM_FAMINE precache_model ("models/fambeam.mdl"); //Famine's beam attack //GLYPHS precache_model("models/glyph.mdl"); //Non-artifact flagged glyph //Paladin precache_model ("models/blast.mdl"); //Delayed fireball //Assassin precache_model("models/glyphwir.mdl"); //Tripwire version of glyph precache_model ("models/twspike.mdl"); //Trip wire spike //PALADIN precache_model ("models/paladin.mdl"); precache_model ("models/h_pal.mdl"); //Gauntlets precache_model("models/gauntlet.mdl"); // Paladin Weapons //Axe precache_model("models/axe.mdl"); precache_model("models/axblade.mdl"); precache_model("models/axtail.mdl"); //Vorpal Sword precache_model("models/vorpal.mdl"); precache_model("models/vorpswip.mdl"); precache_model("models/vorpshot.mdl"); precache_model("models/vorpshok.mdl"); //Vorpal sword & lightning hit precache_model("models/vorpshk2.mdl"); //Purifier precache_model("models/purifier.mdl"); precache_model("models/purfir1.mdl"); //Purifier flame precache_model("models/drgnball.mdl"); //Purifier fireball, take 2 precache_model("models/ring.mdl"); //Smoke ring //ASSASSIN precache_model ("models/assassin.mdl"); precache_model ("models/h_ass.mdl"); precache_model ("models/h_fangel.mdl");//Temp head model for Assassin //Punch Dagger precache_model("models/punchdgr.mdl"); //Crossbow precache_model("models/crossbow.mdl"); precache_model ("models/arrow.mdl"); precache_model ("models/arrowhit.mdl"); precache_model ("models/flaming.mdl"); precache_model ("models/NFarrow.mdl"); //Grenades precache_model("models/v_assgr.mdl"); precache_model("models/assgren.mdl"); //Scarab Staff precache_model("models/scarabst.mdl"); precache_model("models/scrbstp1.mdl"); precache_model("models/scrbpbdy.mdl"); precache_model("models/scrbpwng.mdl"); //REGISTERED======================================================= //NECROMANCER precache_model ("models/necro.mdl"); precache_model ("models/h_nec.mdl"); //Sickle precache_model("models/sickle.mdl"); // Necromancer Weapons //Magic Missiles precache_model ("models/spllbook.mdl"); precache_model ("models/handfx.mdl"); //Bone Shards precache_model ("models/bonefx.mdl"); precache_model ("models/boneshot.mdl"); precache_model ("models/boneshrd.mdl"); precache_model ("models/bonelump.mdl"); //Raven Staff precache_model ("models/ravenstf.mdl"); precache_model ("models/vindsht1.mdl"); precache_model ("models/ravproj.mdl"); precache_model ("models/birdmsl2.mdl"); //CRUSADER precache_model ("models/crusader.mdl"); precache_model ("models/h_cru.mdl"); //Warhammer precache_model ("models/warhamer.mdl"); precache_model ("models/hamthrow.mdl"); //Ice Staff precache_model ("models/icestaff.mdl"); precache_model ("models/iceshot1.mdl"); precache_model ("models/iceshot2.mdl"); //Meteor Staff precache_model ("models/meteor.mdl"); precache_model ("models/tempmetr.mdl");//temp- meteor projectile precache_model ("models/tornato.mdl"); precache_model ("models/funnal.mdl"); //Sunstaff precache_model ("models/sunstaff.mdl"); //SPECIAL ABILITIES //Necromancer precache_model ("models/soulball.mdl");//Soul sphere precache_model ("models/soulskul.mdl"); //Crusader precache_model ("models/goodsphr.mdl");//Smiting Sphere precache_model ("models/cross.mdl"); precache_model ("models/birdmisl.mdl"); //Miscellaneous Shared precache_model ("models/fireball.mdl");//Fireball traps, imps precache_model ("models/test.mdl"); //For testing } //********************************************** // *************** Raven sprites //********************************************** void Precache_spr (void) {//ALPHABETIZED! //FIXME: Which of these aren't being used anymore? precache_model("models/bg_expld.spr"); precache_model("models/bldspot1.spr"); precache_model("models/bldspot2.spr"); precache_model("models/bldspot3.spr"); precache_model("models/bldspot4.spr"); precache_model("models/bluflash.spr"); precache_model("models/bonexpld.spr"); precache_model("models/bspark.spr"); precache_model("models/rcloud.spr"); precache_model ("models/eidoflam.spr"); precache_model("models/fcircle.spr"); precache_model("models/fl_expld.spr"); precache_model("models/gen_expl.spr"); precache_model ("models/ghost.spr"); precache_model ("gfx/glass.spr"); precache_model("models/grnsmk1.spr"); precache_model ("models/gryspt.spr"); precache_model("models/grysmk1.spr"); precache_model ("models/gspark.spr"); precache_model("models/icehit.spr"); //Ice mace shot impact precache_model ("models/medhit.spr"); precache_model ("models/mezzoref.spr"); precache_model ("models/mm_expld.spr"); precache_model ("models/null.spr"); //nothing there precache_model("models/polymrph.spr"); precache_model("models/redsmk1.spr"); precache_model ("models/redspt.spr"); precache_model("models/rspark.spr"); precache_model ("gfx/s_blast.spr"); precache_model ("models/s_bubble.spr"); // drowning bubbles precache_model ("models/s_explod.spr"); // Not sure, but it was in our directory precache_model ("models/s_light.spr"); precache_model("models/sm_blue.spr"); precache_model("models/sm_expld.spr"); precache_model("models/sm_white.spr"); precache_model("gfx/smoke1.spr"); precache_model("models/spark.spr"); precache_model("models/spark0.spr"); precache_model ("gfx/stone.spr"); precache_model("models/telesmk1.spr"); precache_model("models/telesmk2.spr"); precache_model("models/telesmk3.spr"); precache_model("models/whtsmk1.spr"); precache_model("models/wsplash.spr"); precache_model("models/xbowexpl.spr"); //Crossbow explosion precache_model("models/xplod29.spr"); //old Eidolon flames precache_model("models/xpspblue.spr"); precache_model("models/yr_flsh.spr"); precache_model3("models/rcloud.spr"); } void precache_archer() { precache_model("models/archer.mdl"); precache_model("models/archerhd.mdl"); precache_model("models/gspark.spr"); precache_sound ("archer/arrowg.wav"); precache_sound ("archer/arrowr.wav"); precache_sound ("archer/growl.wav"); precache_sound ("archer/pain.wav"); precache_sound ("archer/sight.wav"); precache_sound ("archer/death.wav"); precache_sound ("archer/draw.wav"); } void precache_spider () { precache_model("models/spider.mdl"); precache_model("models/sflesh1.mdl"); precache_model("models/sflesh2.mdl"); precache_model("models/sflesh3.mdl"); precache_sound("spider/bite.wav"); precache_sound("spider/pain.wav"); precache_sound("spider/death.wav"); precache_sound("spider/step1.wav"); precache_sound("spider/step2.wav"); precache_sound("spider/step3.wav"); } gamecode/hc/h2/progs.src000066400000000000000000000027361444734033100154030ustar00rootroot00000000000000progs.dat global.hc entity.hc constant.hc builtin.hc proto.hc math.hc strings.hc spawn.hc chunk.hc sound.hc fx.hc plaque.hc camera.hc subs.hc fight.hc path.hc ai.hc projbhvr.hc // Miscellaneous Projectile Behaviour(homing, spiral, spread, etc) ravenai.hc MG_AI.hc //New AI routines waypoint.hc altdeath.hc damage.hc combat.hc artifact.hc // Artifacts, items, and rings items.hc cube.hc invntory.hc rings.hc wp_art.hc // Objects object.hc barrel.hc light.hc lightning.hc breakabl.hc soul.hc explode.hc //explosions, weapons.hc was checked out gauntlet.hc // Paladin Weapons vorpal.hc axe.hc purifier.hc warhamer.hc // Crusader weapons icemace.hc meteor.hc sunstaff.hc sickle.hc // Necromancer weapons magicmis.hc boner.hc ravenstf.hc punchdgr.hc // Assassin weapons assgren.hc crossbow.hc setstaff.hc weapons.hc tripmine.hc // UQ tripmines impulse.hc weather.hc precache.hc world.hc head.hc corpse.hc allplay.hc specials.hc client.hc monsters.hc doors.hc buttons.hc triggers.hc cat2.hc quake.hc plats.hc misc.hc torch.hc //assassin.hc // Player models //paladin.hc //crusader.hc //necro.hc newplay.hc setmodth.hc fish.hc sheep.hc raven.hc rat.hc snake.hc fireball.hc // Imp stuff shardice.hc mummy.hc imp.hc spider.hc scorpion.hc golem.hc skullwiz.hc medusa.hc mezzoman.hc archer.hc spit.hc // Hydra stuff hydra.hc faspell.hc // Fallen angel stuff fablade.hc fangel.hc eric.hc //rider.hc //pstboar.hc //famhorse.hc //warhorse.hc //dthhorse.hc //eidolon.hc stats.hc gamecode/hc/h2/progs2.src000066400000000000000000000027541444734033100154650ustar00rootroot00000000000000progs2.dat global.hc entity.hc constant.hc builtin.hc proto.hc math.hc strings.hc spawn.hc chunk.hc sound.hc fx.hc plaque.hc camera.hc subs.hc fight.hc path.hc ai2.hc projbhvr.hc // Miscellaneous Projectile Behaviour(homing, spiral, spread, etc) ravenai.hc MG_AI.hc //New AI routines waypoint.hc altdeath.hc damage.hc combat.hc artifact.hc // Artifacts, items, and rings items.hc cube.hc invntory.hc rings.hc wp_art.hc // Objects object.hc barrel.hc light.hc lightning.hc breakabl.hc soul.hc explode.hc //explosions, weapons.hc was checked out gauntlet.hc // Paladin Weapons vorpal.hc axe.hc purifier.hc warhamer.hc // Crusader weapons icemace.hc meteor.hc sunstaff.hc sickle.hc // Necromancer weapons magicmis.hc boner.hc ravenstf.hc punchdgr.hc // Assassin weapons assgren.hc crossbow.hc setstaff.hc weapons.hc tripmine.hc // UQ tripmines impulse.hc weather.hc precache.hc world.hc head.hc corpse.hc allplay.hc specials.hc client.hc monsters.hc doors.hc buttons.hc triggers.hc cat2.hc quake.hc plats.hc misc.hc torch.hc //assassin.hc // Player models //paladin.hc //crusader.hc //necro.hc newplay.hc setmodth.hc fish.hc sheep.hc raven.hc rat.hc snake.hc fireball.hc // Imp stuff shardice.hc mummy.hc imp.hc //spider.hc //scorpion.hc //golem.hc //skullwiz.hc //medusa.hc //mezzoman.hc //archer.hc //spit.hc // Hydra stuff //hydra.hc //faspell.hc // Fallen angel stuff //fablade.hc //fangel.hc eric.hc rider.hc pstboar.hc famhorse.hc warhorse.hc dthhorse.hc eidolon.hc stats.hc gamecode/hc/h2/projbhvr.hc000066400000000000000000000247271444734033100157140ustar00rootroot00000000000000/* ==================================================================== PROJBHVR.HC Projectile Behaviours By Michael Gummelt Various routines for projectile behaviours. =================================================================== */ void(float num_bubbles) DeathBubbles; /* ==================================================== void Skip() MG Returns True if the projectile hit a surface at a slight enough angle to deflect. False if not (meaning the missile should be destroyed in the touch function this was called from). The missile must be a MOVETYPE_BOUNCEMISSILE for this to work, and it's o_angle must match it's last known velocity. For now the angle of deflection is anything below the 0.2 (using the dot product of the negative value of the o_angle and the trace_plane_normal of the surface it hit.) But this could be easily customized to use a parameter or entity field. Values Used: .o_angle (.movetype must be MOVETYPE_BOUNCEMISSILE) ===================================================== */ float Skip (void) { vector dir1,dir2; float dot; dir1 = normalize(self.velocity); traceline(self.origin-dir1*4,self.origin+dir1*16,TRUE,self); dir2=trace_plane_normal; dir1*=-1; dot=dir1*dir2; if(dot<=0.15&&trace_fraction<1) return TRUE; else return FALSE; } /* ==================================================== void Veer(float amount) MG This function will make a projectile wander from it's course in a random manner. It does not actually directly use the .veer value, you must send the veer amount value to the function as a parameter. But this allows it to be used in other ways (call it once, etc.) So you can call it by using Veer(self.veer) or Veer(random()*300) or Veer([any number]), etc. ===================================================== */ void Veer(float amount) { //useful code for making projectiles wander randomly to a specified degree vector veerdir; veerdir_x=veerdir_y=veerdir_z=amount; self.velocity+=RandomVector(veerdir); self.angles=vectoangles(self.velocity); } void VeerThink () { Veer(self.veer); if(self.think==Veer) thinktime self : 0.1; } /* ========================================================= float ahead (entity loser, entity from) MG Checks to see if "loser" is within the forward cone (based on facing, NOT velocity!) of "from". Cone size defaults to 0.2 unless crossbow arrows (they do a one-time narrow-cone aquisition of 0.8) NOTE: "accept" can be a value between -1 and 1, the higher the value, the smaller the cone. Returns TRUE if so, FALSE if not. ========================================================= */ float ahead (entity loser, entity from) { vector proj_dir, spot1, spot2, vec; float accept, dot; proj_dir=normalize(from.velocity); spot1 = from.origin; spot2 = (loser.absmin+loser.absmax)*0.5; if(from.classname=="flaming arrow"||from.classname=="bolt") accept=0.875; else accept=0.2; vec = normalize (spot2 - spot1); dot = vec * proj_dir; if ( dot > accept) return TRUE; return FALSE; } /* ========================================================= float heading (entity loser, entity from, float accept) MG Checks to see if "loser" is within the forward cone (based on velocity, NOT facing!) of "from". "accept" is the size of the cone (see "ahead"), which, if none is sent, defaults to 0.8 (rather narrow). Returns TRUE if so, FALSE if not. ========================================================= */ float heading (entity loser, entity from, float accept) { vector proj_dir, spot1, spot2, vec; float dot; proj_dir=normalize(from.velocity); spot1 = from.origin; spot2 = (loser.absmin+loser.absmax)*0.5; if(!accept) accept=0.8; vec = normalize (spot2 - spot1); dot = vec * proj_dir; if ( dot > accept) return TRUE; return FALSE; } void()HomeThink; /* ====================================== entity HomeFindTarget() MG Simply looks for and returns a target to go after. ====================================== */ entity HomeFindTarget() { entity loser; float dist, bestdist; if(self.think!=HomeThink)//one-time only acquisition bestdist=5000; else bestdist=1000; loser=findradius(self.origin,bestdist); bestdist+=1; while (loser) { if(loser.health&&loser.takedamage&&(loser.flags2&FL_ALIVE)&&visible(loser)&&loser!=self&&loser!=world&&loser!=self.owner&&!other.effects&EF_NODRAW)//&&!(loser.artifact_active&ARTFLAG_STONED) Why Not? if((!self.aflag||self.ideal_yaw)&&!ahead(loser,self)) //looks for someone in front first time dprint("");//not infront\n"); else if(teamplay&&loser.classname=="player"&&((loser.team==self.owner.team&&self.owner.classname=="player")||(loser.team==self.controller.team&&self.owner.classname=="player"))) dprint("");//targeting teammate\n"); else if(coop&&loser.classname=="player"&&(self.owner.classname=="player"||self.controller.classname=="player")) dprint("");//target coop player\n"); else if((self.classname=="flame arrow"||self.classname=="bolt")&&deathmatch&&vlen(loser.velocity)>300) dprint("");//DM: player moving too fast\n"); else { //make it wait for closest (by vlen) or just go for first found? dist=vlen(self.origin-loser.origin); if(dist=PB_STAGE_CHARGE&&self.monster_stage=PB_STAGE_CHARGE&&self.monster_stagetime) { if(!walkmove(self.angles_y,self.speed,TRUE)) { if (trace_ent!=world&&trace_ent.takedamage&&self.torchtime30) { self.monster_stage=PB_STAGE_BACKUP; self.aflag=0; self.turn_time=time+3;//Back up for 3 secs } if(self.aflag) { makevectors(self.angles); tracearea(self.origin,self.origin+v_forward*200,'-48 -48 0','48 48 100',TRUE,self); if(trace_plane_normal!='0 0 0') self.ideal_yaw=vectoyaw((trace_plane_normal+v_forward)*0.5); navigate(fabs(self.speed)); } ChangeYaw(); } if ((!self.enemy) || (self.enemy.health <= 0)) { if(self.enemy) sound(self,CHAN_VOICE,"pest/laugh.wav",1,ATTN_NONE); if (!FindTarget(TRUE)) { self.enemy = world; if(!self.monster_awake) self.monster_stage = self.rider_gallop_mode = PB_STAGE_STAND; } else sound(self,CHAN_VOICE,"pest/sight.wav",1,ATTN_NONE); self.goalentity = self.enemy; } else self.monster_awake=TRUE; enemy_vis=visible(self.enemy); if(enemy_vis) self.search_time=time+5-coop*2; if(self.rider_gallop_modeself.angles_y+89&&self.movechain.ideal_yawself.angles_y - 180) self.movechain.ideal_yaw=self.angles_y - 89; } else self.movechain.ideal_yaw=self.angles_y; oself=self; self=self.movechain; ChangeYaw(); self=oself; if(self.rider_gallop_mode==PB_STAGE_ATTACK||self.rider_gallop_mode==PB_STAGE_ATTACK2) self.movechain.frame+=1; else if(self.rider_gallop_mode= $Parrow6) if(self.monster_stage==PB_STAGE_BACKUP) self.rider_gallop_mode=PB_STAGE_NORMAL; else self.rider_gallop_mode=self.monster_stage; } else if(self.rider_gallop_mode == PB_STAGE_ATTACK2) { if (self.movechain.frame == $Phive33) throw_hive(); if (self.movechain.frame >= $Phive38) if(self.monster_stage==PB_STAGE_BACKUP) self.rider_gallop_mode=PB_STAGE_NORMAL; else self.rider_gallop_mode=self.monster_stage; } if(self.monster_stage >= PB_STAGE_CHARGE&&self.monster_stage=random(20,40)) { self.movechain.frame=$PtranD1; self.monster_stage=self.rider_gallop_mode=PB_STAGE_CHARGE_END; } } if(self.frame==$BtranD8) { self.lifespan=time + 5; self.monster_stage = self.rider_gallop_mode=PB_STAGE_NORMAL; self.yaw_speed=4; } if(self.rider_gallop_mode==PB_STAGE_CHARGE) { if(random(1000)<=5) { enemy_infront=infront_of_ent(self.enemy,self.movechain); if(enemy_vis&&(enemy_infront||random()<0.2)) if (random()<0.1&&self.cnt<=0) // Shoot { self.rider_gallop_mode = PB_STAGE_ATTACK2; self.movechain.frame = $Phive1; } else { self.rider_gallop_mode = PB_STAGE_ATTACK; self.movechain.frame = $Parrow1; } } } } if (fabs(pst_speed[self.monster_stage] - fabs(self.speed)) < 0.2) self.speed = pst_speed[self.monster_stage]; else if (pst_speed[self.monster_stage] > fabs(self.speed)) self.speed = fabs(self.speed)+0.2; else self.speed=fabs(self.speed) - 0.2; if(self.monster_stage==PB_STAGE_BACKUP) if(self.turn_timetime) chance/=3; if(random(100)<3) sound(self,CHAN_VOICE,"pest/laugh.wav",1,ATTN_NONE); if (chance < 3&&self.cnt<=0) // Shoot { self.rider_gallop_mode = PB_STAGE_ATTACK2; self.movechain.frame = $Phive1; } else if(chance < 5) // Shoot { self.rider_gallop_mode = PB_STAGE_ATTACK; self.movechain.frame = $Parrow1; } else if(chance < 8&&self.monster_stage5) { c_level = self.level; if (c_level > 10) c_level = 10; if (random(1,10)<=(c_level - 4)) { damage_base = WEAPON1_PWR_BASE_DAMAGE; damage_mod = WEAPON1_PWR_ADD_DAMAGE; CreateRedFlash(trace_endpos); centerprint(self,"Critical Hit Backstab!\n"); AwardExperience(self,trace_ent,10); damage_base*=random(2.5,4); } } else if (self.artifact_active & ART_TOMEOFPOWER) { damage_base = WEAPON1_PWR_BASE_DAMAGE; damage_mod = WEAPON1_PWR_ADD_DAMAGE; CreateWhiteFlash(org); if(trace_ent.mass<=10) inertia=1; else inertia=trace_ent.mass/10; if ((trace_ent.hull != HULL_BIG) && (inertia<1000) && (trace_ent.classname != "breakable_brush")) { if (trace_ent.mass < 1000) { dir = trace_ent.origin - self.origin; trace_ent.velocity = dir * WEAPON1_PUSH*(1/inertia); if(trace_ent.movetype==MOVETYPE_FLY) { if(trace_ent.flags&FL_ONGROUND) trace_ent.velocity_z=200/inertia; } else trace_ent.velocity_z = 200/inertia; trace_ent.flags(-)FL_ONGROUND; } } } else { damage_base = WEAPON1_BASE_DAMAGE; damage_mod = WEAPON1_ADD_DAMAGE; } damg = random(damage_mod + damage_base,damage_base); SpawnPuff (org, '0 0 0', damg,trace_ent); T_Damage (trace_ent, self, self, damg); if (!MetalHitSound(trace_ent.thingtype)) sound (self, CHAN_WEAPON, "weapons/slash.wav", 1, ATTN_NORM); } else { // hit wall sound (self, CHAN_WEAPON, "weapons/hitwall.wav", 1, ATTN_NORM); WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); WriteByte (MSG_BROADCAST, TE_GUNSHOT); WriteCoord (MSG_BROADCAST, org_x); WriteCoord (MSG_BROADCAST, org_y); WriteCoord (MSG_BROADCAST, org_z); if (self.artifact_active & ART_TOMEOFPOWER) CreateWhiteFlash(org); else { org = trace_endpos + (v_forward * -1) + (v_right * 15); org -= '0 0 26'; CreateSpark (org); } } } void punchdagger_idle(void) { self.th_weapon=punchdagger_idle; self.weaponframe=$rootdag; } void () punchdagger_d = { self.th_weapon=punchdagger_d; self.wfs = advanceweaponframe($attackd2,$attackd21); if (self.weaponframe == $attackd5) sound (self, CHAN_WEAPON, "weapons/gaunt1.wav", 1, ATTN_NORM); if (self.weaponframe == $attackd10) fire_punchdagger(); if(self.wfs==WF_CYCLE_WRAPPED) punchdagger_idle(); }; void () punchdagger_c = { self.th_weapon=punchdagger_c; self.wfs = advanceweaponframe($attackc3,$attackc20); if (self.weaponframe == $attackc7) sound (self, CHAN_WEAPON, "weapons/gaunt1.wav", 1, ATTN_NORM); if (self.weaponframe == $attackc11) fire_punchdagger(); if(self.wfs==WF_CYCLE_WRAPPED) punchdagger_idle(); }; void () punchdagger_b = { self.th_weapon=punchdagger_b; self.wfs = advanceweaponframe($attackb5,$attackb29); if (self.weaponframe == $attackb6) sound (self, CHAN_WEAPON, "weapons/gaunt1.wav", 1, ATTN_NORM); if (self.weaponframe == $attackb18) fire_punchdagger(); if(self.wfs==WF_CYCLE_WRAPPED) punchdagger_idle(); }; void () punchdagger_a = { self.th_weapon=punchdagger_a; self.wfs = advanceweaponframe($attacka1,$attacka19); if (self.weaponframe == $attacka7) sound (self, CHAN_WEAPON, "weapons/gaunt1.wav", 1, ATTN_NORM); if (self.weaponframe == $attacka13) fire_punchdagger(); if(self.wfs==WF_CYCLE_WRAPPED) punchdagger_idle(); }; float r2; void Ass_Pdgr_Fire (void) { self.attack_finished = time + .5; // Attack every .7 seconds // r2 = rint(random(1,4)); if (r2==1) punchdagger_a(); else if (r2==2) punchdagger_b(); else if (r2==3) punchdagger_c(); else punchdagger_d(); r2+=1; if (r2>4) r2=1; } void punchdagger_select (void) { self.th_weapon=punchdagger_select; self.wfs = advanceweaponframe($f8,$f1); self.weaponmodel = "models/punchdgr.mdl"; if(self.wfs==WF_CYCLE_STARTED) sound(self,CHAN_WEAPON,"weapons/unsheath.wav",1,ATTN_NORM); if(self.wfs==WF_CYCLE_WRAPPED) { self.attack_finished = time - 1; punchdagger_idle(); } } void punchdagger_deselect (void) { self.th_weapon=punchdagger_deselect; self.wfs = advanceweaponframe($f1,$f8); if(self.wfs==WF_CYCLE_WRAPPED) W_SetCurrentAmmo(); } gamecode/hc/h2/purifier.hc000066400000000000000000000237571444734033100157070ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\weapons\purifier\final\purifier.hc ============================================================================== */ // For building the model $cd Q:\art\models\weapons\purifier\final $origin 0 0 0 $base BASE skin $skin skin $flags 0 // $frame Rootpose // $frame 1Lshot1 1Lshot2 1Lshot3 // $frame 1Rshot1 1Rshot2 1Rshot3 // $frame 2Lshot1 2Lshot2 2Lshot3 // $frame 2Rshot1 2Rshot2 2Rshot3 // $frame 3Rshot1 3Rshot2 3Rshot3 // $frame bigshot1 bigshot2 bigshot3 bigshot4 bigshot5 $frame bigshot6 bigshot7 bigshot8 bigshot9 // $frame select1 select2 select3 select4 select5 $frame select6 select7 select8 select9 select10 $frame select11 select12 //================================================================== void purifier_ready (void); void pmissile_gone(void) { sound (self, CHAN_VOICE, "misc/null.wav", 1, ATTN_NORM); sound (self, CHAN_WEAPON, "misc/null.wav", 1, ATTN_NORM); remove(self); } /* ============ pmissile_touch - missile1 hit something. Hurt it ============ */ void pmissile_touch (void) { float damg; if (other == self.owner) return; // don't explode on owner if (pointcontents(self.origin) == CONTENT_SKY) { pmissile_gone(); return; } damg = random(15,25); if (other.health) T_Damage (other, self, self.owner, damg ); sound (self, CHAN_BODY, "weapons/expsmall.wav", 1, ATTN_NORM); self.origin = self.origin - 8*normalize(self.velocity); CreateFireCircle(self.origin - (v_forward * 8)); self.effects = EF_NODRAW; self.solid = SOLID_NOT; self.nextthink = time + .5; // So explosion sound can finish out self.think = pmissile_gone; } /* ============ smokering_run - the life and death of a smoke ring FIXME: this should be done client side ============ */ void smokering_run(void) { self.scale += 0.12; self.nextthink = time + HX_FRAME_TIME + random(HX_FRAME_TIME); self.think = smokering_run; if ((self.lifetime - time) < .30) self.skin = 4; else if ((self.lifetime - time) < .60) self.skin = 3; else if ((self.lifetime - time) < .90) self.skin = 2; else if ((self.lifetime - time) < 1.20) self.skin = 1; else self.skin = 0; if (self.lifetime < time) remove(self); } /* ============ pmissile2_touch - missile2 hit something. Hurt it bad ============ */ void pmissile2_touch (void) { float damg; if (other == self.owner) return; // don't explode on owner if (pointcontents(self.origin) == CONTENT_SKY) { pmissile_gone(); return; } // don't do radius damage to the other, because all the damage // was done in the impact damg = random(150,200); if (other.health) T_Damage (other, self, self.owner, damg ); damg = random(120,160); T_RadiusDamage (self, self.owner, damg, other); sound (self, CHAN_BODY, "weapons/exphuge.wav", 1, ATTN_NORM); self.origin = self.origin - 8*normalize(self.velocity); self.effects = EF_NODRAW; self.solid = SOLID_NOT; CreateExplosion29(self.origin - (v_forward * 8)); self.nextthink = time + .5; self.think = pmissile_gone; } /* ============ pmissile2_puff - create smoke ring behind missile ============ */ void pmissile2_puff(void) { entity smokering; // Not using it till we get the smoke animation in smokering = spawn (); smokering.owner = self; smokering.movetype = MOVETYPE_FLYMISSILE; smokering.solid = SOLID_BBOX; smokering.classname = "puffring"; // set missile speed smokering.angles = self.angles + '0 0 90'; // set missile duration setmodel (smokering, "models/ring.mdl"); setsize (smokering, '0 0 0', '0 0 0'); smokering.drawflags(+)DRF_TRANSLUCENT; smokering.origin = self.origin; smokering.velocity_z = 15; smokering.nextthink = time + .01; smokering.think = smokering_run; smokering.lifetime = time + 1.2; smokering.drawflags(+)SCALE_ORIGIN_CENTER; smokering.scale =1; smokering.owner = self; self.nextthink = time + .15; self.think = pmissile2_puff; if(time>self.lifetime - 1.7) //Don't start tracking until it's been in the world 1/3 of a second { HomeThink(); self.angles=vectoangles(self.velocity); } if (self.lifetime < time) // Kill missile if it's time is up pmissile_gone(); } /* ============ launch_pmissile2 - create and launch power up missile ============ */ void launch_pmissile2 (void) { local entity missile; missile = spawn (); missile.owner = self; missile.movetype = MOVETYPE_FLYMISSILE; missile.solid = SOLID_BBOX; missile.frags=TRUE; missile.classname = "purimissile"; // set missile speed makevectors (self.v_angle); missile.velocity = normalize(v_forward); missile.velocity = missile.velocity * 1000; missile.touch = pmissile2_touch; missile.angles = vectoangles(missile.velocity); sound (self, CHAN_VOICE, "paladin/purfireb.wav", 1, ATTN_NORM); setmodel (missile, "models/drgnball.mdl"); setsize (missile, '0 0 0', '0 0 0'); // setorigin (missile, self.origin + v_forward*10 + v_right * 1 + v_up * 40); setorigin (missile, self.origin + self.proj_ofs + v_forward*10); missile.effects=EF_BRIGHTLIGHT; missile.nextthink = time + .15; missile.think = pmissile2_puff; missile.lifetime = time + 2; //Homing stuff------------------- missile.veer=FALSE; //No random wandering missile.turn_time=3;//Lower the number, tighter the turn missile.speed=1000; //Speed missile.ideal_yaw=TRUE;//Only track things in front //End homing stuff------------------- self.greenmana -= 8; self.bluemana -= 8; } /* ============ purifier_tomefire - firing animation when in power up mode ============ */ void purifier_tomefire (void) { self.wfs = advanceweaponframe($bigshot1,$bigshot9); self.th_weapon=purifier_tomefire; if(self.weaponframe==$bigshot2) { self.punchangle_x= -4; launch_pmissile2 (); self.attack_finished = time + 0.5; } else if(self.wfs==WF_CYCLE_WRAPPED) purifier_ready(); } /* ============ launch_pmissile1 - create and launch normal missile ============ */ void launch_pmissile1 (void) { local entity missile; missile = spawn (); missile.owner = self; missile.movetype = MOVETYPE_FLYMISSILE; missile.solid = SOLID_BBOX; missile.classname = "purimissile"; // set missile speed makevectors (self.v_angle); missile.velocity = normalize(v_forward); missile.velocity = missile.velocity * 1000; missile.touch = pmissile_touch; missile.angles = vectoangles(missile.velocity); // set missile duration setmodel (missile, "models/purfir1.mdl"); setsize (missile, '0 0 0', '0 0 0'); if ((self.cnt==1) || (self.cnt==3)) setorigin (missile, self.origin + self.proj_ofs + v_forward*6 + v_right * 10 ); else if ((self.cnt==0) || (self.cnt==2)) setorigin (missile, self.origin +self.proj_ofs + v_forward*6 - v_right * 10); sound (self, CHAN_WEAPON, "paladin/purfire.wav", 1, ATTN_NORM); self.cnt += 1; if (self.cnt > 3) self.cnt =0; missile.drawflags=MLS_ABSLIGHT; missile.abslight=1; missile.nextthink = time + 2.5; missile.think = pmissile_gone; self.greenmana -= 1; self.bluemana -= 1; } /* ============ purifier_rapidfire? - different rapid fire animations ============ */ void purifier_rapidfire2R (void) { self.wfs = advanceweaponframe($2Rshot1,$2Rshot3); self.th_weapon=purifier_rapidfire2R; if (self.weaponframe == $2Rshot3) self.punchangle_x= random(-3); if (self.attack_finished <= time&&self.button0) launch_pmissile1(); if (self.wfs==WF_CYCLE_WRAPPED) purifier_ready(); } void purifier_rapidfire2L (void) { self.wfs = advanceweaponframe($2Lshot1,$2Lshot3); self.th_weapon=purifier_rapidfire2L; if (self.weaponframe == $1Lshot3) self.punchangle_x= random(-3); if (self.attack_finished <= time&&self.button0) launch_pmissile1(); if (self.wfs==WF_CYCLE_WRAPPED) purifier_ready(); } void purifier_rapidfire1R (void) { self.wfs = advanceweaponframe($1Rshot1,$1Rshot3); self.th_weapon=purifier_rapidfire1R; if (self.weaponframe == $1Rshot3) self.punchangle_x= random(-3); if (self.attack_finished <= time&&self.button0) launch_pmissile1(); if (self.wfs==WF_CYCLE_WRAPPED) purifier_ready(); } void purifier_rapidfire1L (void) { self.wfs = advanceweaponframe($1Lshot1,$1Lshot3); self.th_weapon=purifier_rapidfire1L; if (self.weaponframe == $1Lshot3) self.punchangle_x= random(-3); if (self.attack_finished <= time&&self.button0) launch_pmissile1(); if (self.wfs==WF_CYCLE_WRAPPED) purifier_ready(); } /* ============ purifier_rapidfire - choose which rapid fire animation to use ============ */ void purifier_rapidfire (void) { if (self.counter ==0) purifier_rapidfire1L(); else if (self.counter ==1) purifier_rapidfire1R(); else if (self.counter ==2) purifier_rapidfire2L(); else if (self.counter ==3) purifier_rapidfire2R(); self.counter += 1; self.attack_finished = time + .14; if (self.counter > 3) self.counter =0; } /* ============ purifier_fire - shoot purifier. ============ */ void() pal_purifier_fire = { if ((self.artifact_active & ART_TOMEOFPOWER) && (self.greenmana >= 8) && (self.bluemana >= 8)) purifier_tomefire(); else if ((self.greenmana >= 2) && (self.bluemana >= 2)) purifier_rapidfire(); self.nextthink=time; }; /* ============ purifier_ready - just sit there until fired ============ */ void purifier_ready (void) { self.weaponframe = $Rootpose; self.wfs = $Rootpose; self.th_weapon=purifier_ready; } /* ============ purifier_deselect - purifier was just unchosen. Remove from view ============ */ void purifier_deselect (void) { self.wfs = advanceweaponframe($Select12,$Select1); self.th_weapon=purifier_deselect; self.oldweapon = IT_WEAPON4; if (self.wfs == WF_LAST_FRAME) W_SetCurrentAmmo(); } /* ============ purifier_select - purifier was just chosen. Bring into view ============ */ void purifier_select (void) { self.wfs = advanceweaponframe($select1,$select12); self.weaponmodel = "models/purifier.mdl"; self.th_weapon=purifier_select; self.counter = 0; if (self.wfs==WF_CYCLE_WRAPPED) { self.attack_finished = time - 1; purifier_ready(); } } gamecode/hc/h2/quake.hc000066400000000000000000000070041444734033100151530ustar00rootroot00000000000000/* ========================================= QUAKE.HC MG EarthQuakes! ========================================= */ void() ShakeRattleAndRoll = { entity head; float inertia; float richter; float dist; float rand,boost; vector dir; head = findradius(self.origin, self.mass); richter=self.mass/100*(self.lifetime-time)/3; //FIXME: Add particle dust while (head) { if ((head.movetype==MOVETYPE_STEP||head.movetype==MOVETYPE_WALK||head.movetype==MOVETYPE_PUSHPULL)&&head.solid!=SOLID_BSP&&head.mass<500 && head != self.owner && ((head.classname != "monster_golem_bronze") &&(head.classname != "monster_golem_iron") && (head.classname != "monster_golem_stone")&& (head.classname != "monster_golem_crystal"))) { // bprint(head.classname); dist=vlen(self.origin-head.origin)/self.mass; if(!head.mass) inertia=1; else inertia=head.mass/10; if((!head.flags2&FL_ALIVE)&&head.takedamage&&head.health&&random()<0.2&&head!=self.owner) T_Damage(head,self,self.owner,richter*dist); if(head.flags&FL_ONGROUND) { if(head!=self.owner) { if(head.classname=="player") { head.punchangle_x=random()*10 - 5; head.punchangle_y=random()*8 - 4; head.punchangle_z=random()*8 - 4; } else if(head.flags2&FL_ALIVE) { head.angles_y+=random()*20 - 10; rand=random()*6 - 3;//FIXME: Remember old angles if(head.angles_x+rand>30||head.angles_x+rand<-30) head.angles_x-=rand; else head.angles_x+=rand; rand=random()*6 - 3; if(head.angles_z+rand>30||head.angles_z+rand<-30) head.angles_z-=rand; else head.angles_z+=rand; } else { if(head.movetype!=MOVETYPE_BOUNCE) { head.oldmovetype=head.movetype; head.movetype=MOVETYPE_BOUNCE; } else if(!head.oldmovetype) head.oldmovetype=MOVETYPE_BOUNCE; head.avelocity_z= random(1,10); head.avelocity_y= random()*720 - 360; head.avelocity_x= random()*720 - 360; } boost= (random(100)+25)/inertia*richter*dist; if(boost>100) boost=100; head.velocity_z+=boost; } if(self.owner.classname=="monster_golem_bronze") {//Make Bronze G make you drift towards him dir=normalize(self.owner.origin-head.origin); dir_z=0; head.velocity=head.velocity+dir*((random()*50 - 25)/inertia*richter*dist); } else { head.velocity_y+= (random()*50 - 25)/inertia*richter*dist; head.velocity_x+= (random()*50 - 25)/inertia*richter*dist; head.flags(-)FL_ONGROUND; } } if(self.lifetimetime) self.think = ShakeRattleAndRoll; else self.think=SUB_Remove; self.nextthink = time + 0.1; }; void (float richter) MonsterQuake = { newmis=spawn(); newmis.owner=self; newmis.solid=SOLID_NOT; newmis.movetype=MOVETYPE_NONE; newmis.classname="quake"; newmis.think=ShakeRattleAndRoll; newmis.nextthink=time; newmis.mass=fabs(richter); newmis.lifetime=time + 3; setorigin(newmis,self.origin); //FIXME: Replace explosion with some other quake-start sound sound(newmis,CHAN_AUTO,"weapons/explode.wav",1,ATTN_NORM); sound(newmis,CHAN_AUTO,"fx/quake.wav",1,ATTN_NORM); }; gamecode/hc/h2/rat.hc000066400000000000000000000132271444734033100146370ustar00rootroot00000000000000// Scuttle //$frame swalk1 swalk2 swalk3 swalk4 swalk5 //$frame swalk6 swalk7 swalk8 swalk9 swalk10 //$frame swalk11 swalk12 swalk13 swalk14 swalk15 //$frame swalk16 void() rat_wait; void() rat_noise; void rat_die_wait() { if(self.health<-80) { chunk_death(); return; } if(!self.velocity) { if(random()<0.5) self.angles_x=0; else self.angles_x=180; self.avelocity='0 0 0'; MakeSolidCorpse(); return; } else { self.think=rat_die_wait; thinktime self : 0.5; } } void rat_death (void) { if(self.health<-80) { chunk_death(); return; } vector bounce_vel; bounce_vel=normalize(self.origin-damage_attacker.origin); bounce_vel*=random(self.health*-30); self.velocity=bounce_vel; self.velocity_z=random(150,450); self.flags(-)FL_ONGROUND; self.movetype=MOVETYPE_BOUNCE; self.avelocity_x=random(self.health*10) - self.health; self.avelocity_y=random(self.health*10) - self.health; self.avelocity_z=random(self.health*10) - self.health; self.think=rat_die_wait; thinktime self : 0; } void ratrun(void) { entity player; float player_see_me; self.frame += 1; thinktime self : HX_FRAME_TIME; self.think = ratrun; AdvanceFrame(0,17); self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin); // dprint(ftos(self.ideal_yaw)); // dprint("\n"); ChangeYaw(); if(pointcontents(self.origin+'0 0 7')!=CONTENT_EMPTY) { if(pointcontents(self.origin)==CONTENT_LAVA) { // dprint("lava death\n"); chunk_death(); } else { self.velocity_z=1; self.flags(+)FL_SWIM; } } else self.velocity_z=-1; if((random()<0.1||self.origin==self.oldorigin)&&self.pain_finishedself.lifetime) { if(self.goalentity.classname=="tempgoal") remove(self.goalentity); remove(self); } if(self.frags>=10) { self.frags=0; makevectors(self.angles); newmis=spawn(); setorigin(newmis,v_forward*-1000+v_right*(random()*100 - 50)); remove(self.goalentity); self.goalentity=newmis; } } if(!walkmove(self.angles_y,random(3,7),FALSE)) { //TEMP remove(self); // self.frags+=1; } } void rat_wait () { if(vlen(self.goalentity.origin-self.origin)>56) self.think=ratrun; else self.think=rat_wait; thinktime self : 0.05; } void rat_noise (void) { sound(self,CHAN_VOICE,"misc/squeak.wav",1,ATTN_NORM); } void rat_touch (void) { } //========================================================================== // // monster_ratnest // //========================================================================== /*QUAKED monster_rat (1 0.3 0) (-3 -3 0) (3 3 7) If the rat is targeted to a path, the rats will follow that path ------- key / value ---------------------------------- ------- spawnflags ----------------------------------- AMBUSH */ void rat_make_goal (void) { vector goaldir; goaldir=self.angles; goaldir_y=self.ideal_yaw; makevectors(goaldir); newmis=spawn(); newmis.effects=EF_NODRAW; setmodel(newmis,"models/null.spr"); setorigin(newmis,self.origin+v_forward*1000); self.goalentity=newmis; newmis.classname="tempgoal"; thinktime newmis : 30; newmis.think=SUB_Remove; } void rat_stupor (void) { self.angles_y+=random(-2,2); self.frame=random(17); self.think=rat_stupor; thinktime self : random(0.3,1.3); } void rat_go (void) { self.th_stand = ratrun; self.th_walk = ratrun; self.think=ratrun; thinktime self : 0; } void monster_rat (void) { //FIXME: DAMN RAT JUMPS UP IN AIR AND GET STUCK IN EACH OTHER!!!!!!!!!!! if(!self.flags2&FL_SUMMONED) { precache_model("models/rat.mdl"); precache_sound("misc/squeak.wav"); self.th_stand = rat_stupor; self.th_walk = rat_stupor; self.use=rat_go; } else { self.th_stand = ratrun; self.th_walk = ratrun; } setmodel(self, "models/rat.mdl"); self.movetype = MOVETYPE_STEP; self.solid = SOLID_SLIDEBOX; if(pointcontents(self.origin)!=CONTENT_EMPTY) { self.flags(+)FL_SWIM; self.angles_x=10; } self.touch=rat_touch; self.classname="monster_rat"; setsize(self, '-3 -3 0', '3 3 7'); self.health = 3; self.thingtype=THINGTYPE_FLESH; self.th_run = ratrun; self.th_melee = ratrun; self.th_missile = ratrun; self.th_pain = rat_noise; self.th_die = rat_death; self.flags(+)FL_MONSTER; self.yaw_speed = 10; self.lifetime = time+10; if(!self.target) { self.ideal_yaw=random(360); // self.movetype=MOVETYPE_NOCLIP; // self.solid=SOLID_NOT; rat_make_goal(); } walkmonster_start(); } /*QUAKED monster_ratnest (1 0.3 0) (-20 -20 0) (20 20 10) A group of 3 to 6 rats that flee when triggered If the nest is targeted to a path, the rats will follow that path ------- key / value ---------------------------------- ------- spawnflags ----------------------------------- AMBUSH */ void monster_ratnest(void) { precache_model("models/rat.mdl"); precache_sound("misc/squeak.wav"); self.use=barrel_die; } gamecode/hc/h2/raven.hc000066400000000000000000000263001444734033100151600ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\monsters\RAVEN\FINAL\raven.hc ============================================================================== */ // For building the model $cd Q:\art\models\monsters\RAVEN\FINAL $origin 0 0 0 $base BASE skin $skin skin $flags 0 // $frame 1stpec1 1stpec2 1stpec3 1stpec4 1stpec5 $frame 1stpec6 1stpec7 1stpec8 1stpec9 1stpec10 $frame 1stpec11 1stpec12 1stpec13 1stpec14 1stpec15 $frame 1stpec16 1stpec17 1stpec18 1stpec19 1stpec20 $frame 1stpec21 1stpec22 1stpec23 1stpec24 // $frame 2ndpec1 2ndpec2 2ndpec3 2ndpec4 2ndpec5 $frame 2ndpec6 2ndpec7 2ndpec8 2ndpec9 2ndpec10 $frame 2ndpec11 2ndpec12 2ndpec13 // $frame bounce1 bounce2 bounce3 bounce4 bounce5 $frame bounce6 bounce7 bounce8 // $frame death1 // $frame down1 // $frame fastfly1 fastfly2 fastfly3 fastfly4 fastfly5 // $frame flaunt1 flaunt2 flaunt3 flaunt4 flaunt5 $frame flaunt6 flaunt7 flaunt8 // $frame fold1 fold2 fold3 fold4 fold5 $frame fold6 fold7 fold8 fold9 fold10 $frame fold11 fold12 fold13 fold14 fold15 // $frame grabing1 grabing2 grabing3 grabing4 grabing5 // $frame land1 land2 land3 land4 land5 // $frame lowlft1 // $frame lowrght1 // $frame POLY // $frame root1 // $frame shit1 shit2 shit3 shit4 shit5 $frame shit6 shit7 // $frame slowfly1 slowfly2 slowfly3 slowfly4 slowfly5 $frame slowfly6 slowfly7 slowfly8 slowfly9 // $frame soarght1 soarlft1 soar1 // $frame takeoff1 takeoff2 takeoff3 takeoff4 takeoff5 // $frame upleft1 // $frame uprght1 //NOTE: Need Raven chunks (feathers) // needs "Nevermore" sound, squawk sound, feather flapping sound void()raven_flap_fast; void()raven_glide; void()raven_glide_right; void()raven_glide_left; void()raven_hop_wings_out; void()raven_stand; void()raven_slowdown; void()raven_land; void()raven_fold_wings; void()raven_ruffle_end; void()raven_peck_up1; void()raven_peck_up_all; void()raven_peck_up2; void()raven_peck2; void()raven_peck_down; void()raven_peck_down1; void()raven_peck_down2; void drop_feathers() { vector ofs; newmis=spawn(); newmis.movetype=MOVETYPE_FLY; newmis.avelocity_y=random(-200,200); newmis.angles_x=random(-90,90); newmis.velocity_z=random(-100); ofs=RandomVector('32 32 32'); newmis.think=SUB_Remove; thinktime newmis : 3; setmodel(newmis,"models/fether.mdl"); setorigin(newmis,self.origin+ofs); } /* --------------- IN AIR --------------- */ void raven_takeoff (void) [++ $takeoff1 .. $takeoff5] { //after unfold, jumps in air, goes to fast flap ai_walk(3+self.frame-$takeoff1); if(cycle_wrapped) { self.flags(+)FL_FLY; self.movetype=MOVETYPE_FLY; self.think=raven_flap_fast; thinktime self : 0; } } void raven_flap_fast (void) [++ $fastfly1 .. $fastfly5] { //Taking off or flying fast ai_walk(self.speed+(self.frame-$fastfly1)*2); if(cycle_wrapped&&random()<0.5) { self.think=raven_glide; thinktime self : 0; } } void raven_flap_slow (void) [++ $slowfly1 .. $slowfly9] { //used every now and then when gliding ai_walk(self.speed+self.frame-$slowfly1); } void raven_glide_think () { ai_walk(self.speed); if(self.goalentity.origin_zself.angles_y) { self.think=raven_glide_right; thinktime self : 0; } else if(self.ideal_yaw>self.angles_y) { self.think=raven_glide_left; thinktime self : 0; } else { self.think=raven_glide; thinktime self : 0; } } void raven_glide (void) { self.frame=$soar1; if(self.angles_z>0) self.angles_z-=1; else if(self.angles_z<0) self.angles_z+=1; raven_glide_think(); } void raven_glide_right (void) { //Add some roll self.frame=$soarght1; if(self.angles_z>-15) self.angles_z-=1; raven_glide_think(); } void raven_glide_left (void) { //Add some roll self.frame=$soarlft1; if(self.angles_z<15) self.angles_z+=1; raven_glide_think(); } void raven_slowdown (void) [++ $grabing1 .. $grabing5] { //landing or pulling off a piece or slowing down to grab something ai_walk(self.speed); if(self.msg3=="landing") if(self.speed>1) self.speed-=0.1; if(self.flags&FL_ONGROUND) { self.angles_z=0; self.msg3=""; self.think=raven_land; thinktime self : 0; } if(self.angles_z>0) self.angles_z-=1; else if(self.angles_z<0) self.angles_z+=1; } void raven_land (void) [++ $land1 .. $land5] { //hitting the ground, comes from slowdown to flaunt ai_walk(1); if(cycle_wrapped) { self.flags(-)FL_FLY; self.movetype=MOVETYPE_STEP; self.think=raven_hop_wings_out; thinktime self : 0; } } /* --------------- ON GROUND --------------- */ void raven_hop_wings_out (void) [++ $flaunt1 .. $flaunt8] { //flaunting, also after landing to folding ai_walk(5); if(cycle_wrapped) { if(self.msg3!="threatening"||random()<0.3) { self.think=raven_fold_wings; thinktime self : 0; } } } void raven_fold_wings (void) [++ $fold1 .. $fold15] { //fold wings away after flaunting or landing if(cycle_wrapped) { self.think=raven_stand; thinktime self : 0; } } void raven_open_wings (void) [-- $fold15 .. $fold1] { //open wings to flaunt or take off, or just randomly to ruffle if(cycle_wrapped) { if(self.msg3=="taking off") self.think=raven_flap_fast; else if(self.msg3=="threatening") self.think=raven_hop_wings_out; thinktime self : 0; } else if(random()<0.3) { self.think=raven_fold_wings; thinktime self : 0; } } void raven_hop (void) [++ $bounce1 .. $bounce8] { //hopping around, no flying ai_walk(2); if(cycle_wrapped) { self.think=raven_stand; thinktime self : 0; } } void raven_shit (void) [++ $shit1 .. $shit7] { //hold and drop shit on $shit6 if(self.frame==$shit6) thinktime self : 0.77; else if(cycle_wrapped) { self.think=raven_stand; thinktime self : 0; } } void raven_ruffle_start (void) [++ $shit1 .. $shit3] { //frames 1-3 ping-pong for feather ruffle if(cycle_wrapped) { self.think=raven_ruffle_end; thinktime self : 0; } } void raven_ruffle_end (void) [-- $shit3 .. $shit1] { //frames 1-3 ping-pong for feather ruffle if(cycle_wrapped) { self.think=raven_stand; thinktime self : 0; } } void raven_choose_look (entity targ) { vector vec; //FIXME: If !targ choose random dir vec=normalize((targ.absmin+targ.absmax)*0.5-self.origin+self.view_ofs); makevectors(self.angles); } void raven_stand_think () { float r; if(!self.goalentity) self.goalentity=find(world,classname,"obj_corpse1"); if(random()<0.3) { r=rint(random(1,10)); if(r==1) self.think=raven_shit; else if(r==2) self.think=raven_ruffle_start; else if(r==3) { self.msg3="threatening"; self.think=raven_open_wings; } else if(r==4) self.think=raven_ruffle_start; else if(r==5) { self.msg3="taking off"; self.think=raven_open_wings; } else if(r==6) self.think=raven_peck_down; else self.think=raven_hop; thinktime self : 0; } else if(self.msg3=="looking") raven_choose_look(world); else thinktime self : 0.05; } void raven_stand (void) { self.frame=$root1; self.think=raven_stand_think; thinktime self : 0; } void raven_die (void) { drop_feathers(); if(self.health<-20) chunk_death(); else { self.frame=$death1; self.flags(-)FL_FLY; if(self.movetype!=MOVETYPE_BOUNCE) self.movetype=MOVETYPE_BOUNCE; self.angles_z=0; self.avelocity_y=random(200,400); MakeSolidCorpse(); } } /* --------------- LOOKING --------------- */ void raven_look_left_low (void) { self.frame=$lowlft1; self.th_stand(); } void raven_look_right_low (void) { self.frame=$lowrght1; self.th_stand(); } void raven_look_left_high (void) { self.frame=$upleft1; self.th_stand(); } void raven_look_right_high (void) { self.frame=$uprght1; self.th_stand(); } void raven_look_down (void) { self.frame=$down1; self.th_stand(); } /* --------------- EATING --------------- */ void peck_effect () { makevectors(self.angles); traceline(self.origin,self.origin+v_forward*16,FALSE,self); SpawnPuff(trace_endpos,'0 0 20' , random(3,10),trace_ent); } void raven_peck1 (void) [++ $1stpec8 .. $1stpec20] { if(random()<0.2) peck_effect(); if(cycle_wrapped) { if(random()<0.2) { self.think=raven_peck_up1; thinktime self : 0; } else if(random()<0.5) { self.think=raven_peck_up_all; thinktime self : 0; } } else if(random()<0.2) { self.think=raven_peck2; thinktime self : 0; } } void raven_peck2 (void) [++ $2ndpec1 .. $2ndpec10] { if(random()<0.2) peck_effect(); if(cycle_wrapped) { if(random()<0.2) { self.think=raven_peck_up2; thinktime self : 0; } else if(random()<0.5) { self.think=raven_peck_up_all; thinktime self : 0; } } else if(random()<0.2) { self.think=raven_peck1; thinktime self : 0; } } void raven_peck_down (void) [++ $1stpec1 .. $1stpec7] { if(cycle_wrapped) { self.think=raven_peck1; thinktime self : 0; } } void raven_peck_up_all (void) [-- $1stpec7 .. $1stpec1] { if(cycle_wrapped) { if(random()<0.7) self.think=raven_stand; else self.think=raven_peck_down; thinktime self : 0; } } void raven_peck_up1 (void) [++ $1stpec21 .. $1stpec24] { if(self.frame==$1stpec24) { self.think=raven_peck_down1; thinktime self : random(0.3,1.3); } } void raven_peck_up2 (void) [++ $2ndpec11 .. $2ndpec13] { if(self.frame==$2ndpec13) { self.think=raven_peck_down2; thinktime self : random(0.3,1.3); } } void raven_peck_down1 (void) [-- $1stpec24 .. $1stpec21] { if(cycle_wrapped) { if(random()<0.5) self.think=raven_peck1; else self.think=raven_peck2; thinktime self : 0; } } void raven_peck_down2 (void) [-- $2ndpec13 .. $2ndpec11] { if(cycle_wrapped) { if(random()<0.5) self.think=raven_peck1; else self.think=raven_peck2; thinktime self : 0; } } /*QUAKED monster_raven (1 0 0) (-16 -16 0) (16 16 56) Scavenger black bird of ill portent. "Nevermore!" */ void() monster_raven = { if (!self.flags2&FL_SUMMONED) { precache_model2 ("models/raven.mdl"); precache_model2 ("models/fether.mdl"); precache_sound2 ("raven/squawk.wav"); // precache_sound2 ("raven/nevermor.wav"); } self.solid = SOLID_SLIDEBOX; self.takedamage=DAMAGE_YES; self.thingtype=THINGTYPE_FLESH; self.movetype = MOVETYPE_STEP; self.view_ofs = '0 0 17'; self.speed=10; self.yaw_speed = 10; self.health = 10; self.mass = 1; self.mintel=5; self.th_stand=raven_stand; self.th_walk=raven_hop; self.th_run=raven_glide; // self.th_pain=raven_pain; self.th_melee=raven_peck_down; self.th_missile=raven_shit; self.th_jump=raven_takeoff; self.th_die=raven_die; setmodel (self, "models/raven.mdl"); setsize (self, '-7 -7 0', '7 7 20'); walkmonster_start(); }; gamecode/hc/h2/ravenai.hc000066400000000000000000000154331444734033100154770ustar00rootroot00000000000000float() LocateTarget = { return FindTarget(TRUE); }; float MA_SUCCESSFUL = 0; float MA_BLOCKED = -1; float MA_CROSSED = -2; float MA_NOWEAPON = -3; float MA_TOOSOON = -4; float MA_TOOFAR = -5; float MA_NOATTACK = -6; float MA_MELEE = 1; float MA_MISSILE = 2; float MA_BOTH = 3; float MA_FAR_MELEE = 4; float MA_SHORT_MISSILE = 8; // You must perform the following call sometime before calling this function: // enemy_range = range (self.enemy); float(float AttackType, float ChanceModifier) CheckMonsterAttack = { local vector spot1, spot2; local entity targ; local float chance; targ = self.enemy; if (self.classname == "monster_hydra") if (self.enemy.watertype != CONTENT_WATER) { if (self.search_time < time) { self.monster_stage = 0; self.enemy = world; return 0; } return 0; } else self.search_time = time + 5; // see if any entities are in the way of the shot spot1 = self.origin + self.view_ofs; spot2 = targ.origin + targ.view_ofs; traceline (spot1, spot2, FALSE, self); if(trace_ent.thingtype>=THINGTYPE_WEBS) traceline (trace_endpos, spot2, FALSE, trace_ent); if (trace_ent != targ) if(trace_ent.health>25||!trace_ent.takedamage||(trace_ent.flags&FL_MONSTER&&trace_ent.classname!="player_sheep")) return MA_BLOCKED; // don't have a clear shot if (trace_inopen && trace_inwater) return MA_CROSSED; // sight line crossed contents if (enemy_range == RANGE_MELEE) { // melee attack if (AttackType & MA_SHORT_MISSILE) { if (random() < 0.5) { self.th_missile (); return MA_SUCCESSFUL; } } if (self.th_melee) { if (AttackType & MA_MELEE) { self.th_melee (); return MA_SUCCESSFUL; } else return MA_NOWEAPON; } } // missile attack if (!self.th_missile || !(AttackType & (MA_MISSILE | MA_FAR_MELEE))) { return MA_NOWEAPON; } if (time < self.attack_finished) return MA_TOOSOON; if (enemy_range == RANGE_FAR) return MA_TOOFAR; if (enemy_range == RANGE_MELEE) { chance = 0.9; self.attack_finished = 0; } else if (enemy_range == RANGE_NEAR) { if (self.th_melee) chance = 0.2; else chance = 0.4; } else if (enemy_range == RANGE_MID) { if (self.th_melee) chance = 0.05; else chance = 0.1; } else chance = 0; chance = chance * ChanceModifier; if (chance > 0.95) chance = 0.95; if (random () < chance) { if (self.th_melee) { // quake c wouldn't allow me to put this in on if!!! if (AttackType & MA_FAR_MELEE) { self.th_melee (); } else { self.th_missile (); } } else { self.th_missile (); } // SUB_AttackFinished (random(2,0)); SUB_AttackFinished (random(8,0)); return MA_SUCCESSFUL; } return MA_NOATTACK; }; /* float (vector offset, vector to_where)monster_checkpos = { // This function will trace 2 lines - the first line will go from the origin to the offset from the origin. // If this could be done atleast half way, then a 2nd trace is done from the end point of the first trace // to the final destination. If this was mostly successful, then the function will return true, otherwise // false. You would use this function in a situation where you want to see if the enemy is visibile from // your right side, so you first see if you can go do the right side, then go forward from there to the enemy. local vector start; local float length; start = self.origin + offset; traceline (self.origin, start, FALSE, self); if (trace_fraction < 0.5) { // Couldn't get to the offset return FALSE; } length = vlen(self.origin-start) * trace_fraction; start = trace_endpos; traceline (start,to_where, FALSE, self); if (trace_ent == self.enemy || trace_fraction > 0.98) { // We found the enemy! length = length + vlen(start-self.enemy.origin) * trace_fraction; return length; } return FALSE; }; void (float l, float r, float u, float d, float last_move, vector where) find_path = { // This function will check to see if an enemy can be located from the top, bottom, left, and right sides. // The l, r, u, d parameters specify the order for which the search should be done. If it couldn't find // the enemy, then it will try the last seen position of the enemy. last_move indicates that the previous // move was successful (i.e. the monster could move forward). where is the position to check for. local float length; local float newyaw; local float newz; local float c; local float retval; // local vector a, b; makevectors (self.angles); length = 99999; newyaw = self.ideal_yaw; newz = self.velocity_z; c = 0; while(c<=4) { // We have 5 checks to do if (c == 0 && last_move) { // Try checking forward retval = monster_checkpos(v_forward*300,where); if (retval && retval < length) { //dprint("found you to the forward\n"); self.monster_duration = 18 + 5; length = retval; } } if (c == l) { // Try checking to the left retval = monster_checkpos(v_right*-200,where); if (retval && retval < length) { //dprint("found you to the left\n"); newyaw = self.angles_y + 90; self.monster_duration = 18 + 5; length = retval; } } else if (c == r) { // Try checking to the right retval = monster_checkpos(v_right*200,where); if (retval && retval < length) { //dprint("found you to the right\n"); newyaw = self.angles_y - 90; self.monster_duration = 18 + 5; length = retval; } } else if (c == u) { // Try checking to the top retval = monster_checkpos(v_up*200,where); if (retval && retval < length) { //dprint("found you to the up\n"); newz = 30; self.monster_duration = 18 + 5; length = retval; } } else if (c == d) { // Try checking to the bottom retval = monster_checkpos(v_up*-200,where); if (retval && retval < length) { //dprint("found you to the down\n"); newz = -30; self.monster_duration = 18 + 5; length = retval; } } c = c + 1; } if (length == 99999 && self.monster_last_seen != where) { // If we didn't find a direction, and we haven't done this, try looking where the enemy // was last seen find_path(l,r,u,d,0,self.monster_last_seen); //dprint("Using last seen\n"); } else { self.ideal_yaw = newyaw; self.velocity_z = newz; } }; float () FindDir = { // Monster couldn't go in the direction it is pointed to, so find one it can go to local vector a,b,c; local float inc,step; if (random() < 0.5) inc = 45; else inc = -45; c = '0 0 0'; c_y = c_y + inc; step = 0; while(step < 6) { // 7 directions to check (45 degrees each) makevectors (self.angles + c); a = self.origin + self.view_ofs; b = a + v_forward*100; traceline (a, b, FALSE, self); if (trace_fraction > 0.9) { // We can mostly go this direction return self.angles_y + c_y; //dprint(" found\n"); } c_y = c_y + inc; step = step + 1; } return self.angles_y; }; */ gamecode/hc/h2/ravenstf.hc000066400000000000000000000357401444734033100157050ustar00rootroot00000000000000// For building the model $cd Q:\art\models\weapons\newass $origin 0 0 0 $base BASE SKIN $skin SKIN $flags 0 // $frame rootpose // $frame fire1 fire2 fire3 fire4 // $frame select1 select2 select3 select4 select5 $frame select6 select7 select8 select9 select10 $frame select11 select12 void ravenstaff_fire (void); void ravenstaff_idle (void); void split (void); void raven_track(void); void raven_flap(void); void raven_touch (void); void raven_track_init(void); void ravenmissile_explode(void); void raven_spark (void) { if (pointcontents(self.origin) == CONTENT_SKY) { remove(self); return; } CreateWhiteSmoke(self.origin + '0 0 -10', '0 8 -10', HX_FRAME_TIME *3); CreateRedSmoke(self.origin + '0 0 -10', '0 0 -10', HX_FRAME_TIME *3); CreateWhiteSmoke(self.origin + '0 0 -10', '0 -8 -10', HX_FRAME_TIME *3); sound(self,CHAN_WEAPON,"raven/death.wav",1,ATTN_NORM); self.touch = SUB_Null; self.effects=EF_NODRAW; self.think=SUB_Remove; thinktime self : HX_FRAME_TIME; // thinktime self : HX_FRAME_TIME * 2; } void raven_death_init (void) { self.owner.raven_cnt -= 1; self.takedamage = DAMAGE_NO; traceline(self.origin,self.origin + '0 0 600',FALSE,self); if (trace_fraction < 1) { self.touch = raven_spark; self.nextthink = 0; } else { self.touch = raven_spark; self.think = raven_spark; thinktime self : 1; } self.velocity = normalize('0 0 600'); self.velocity = self.velocity * 400; self.angles = vectoangles(self.velocity); } void raven_bounce(void) { self.flags (-) FL_ONGROUND; self.angles = vectoangles(self.velocity); // Flip it around to match the velocity set by the BOUNCEMISSLE code self.angles_y += random(-90,90); // Change it's yaw a little self.angles_x = random(-20,20); // Change it's pitch a little makevectors (self.angles); self.velocity = normalize (v_forward); self.velocity = self.velocity * 600; self.think = raven_flap; self.nextthink = time + HX_FRAME_TIME; self.think1 = raven_track_init; self.next_action = time + HX_FRAME_TIME * random(2,4); self.touch = raven_touch; } // Bite the enemy void raven_touch (void) { if ((other == self.enemy) && (other.takedamage != DAMAGE_NO)) { if (other.monsterclass >= CLASS_BOSS) // Bosses only take half damage T_Damage(other,self,self.owner,10); else T_Damage(other,self,self.owner,20); self.damage_max += 20; SpawnPuff (self.origin, '0 0 -5', random(5,10),other); MeatChunks (self.origin,self.velocity*0.5+'0 0 20', 2,other); sound(self,CHAN_WEAPON,"weapons/gauntht1.wav",1,ATTN_NORM); } if (self.damage_max > 200) raven_death_init(); else { self.touch = SUB_Null; self.think = raven_bounce; self.nextthink = time + .05; // Need to wait a little before flipping model to match velocity } if ((self.lifetime < time) || (self.owner.raven_cnt > 6)) { raven_death_init(); return; } } // Search for an enemy void raven_search(void) { entity victim; self.nextthink = time + HX_FRAME_TIME; // Gotta flap self.think = raven_flap; victim = findradius( self.origin,1000); while(victim) { // the controller check is for the summoned imp if (((victim.flags & FL_MONSTER) || (victim.flags & FL_CLIENT)) && (victim.owner != self) && (victim.controller != self.owner) && (victim.health>0) && (victim!=self.owner)) { if (coop && self.enemy.team == self.team) victim = victim; // Do nothing if its a player on your team. else { traceline(self.origin,victim.origin,TRUE,self); if (trace_fraction == 1.0) { self.enemy = victim; self.think1 = raven_track; self.think1 = raven_track_init; self.next_action = time + .1; self.searchtime = 0; return; } } } victim = victim.chain; } self.think1 = raven_search; self.next_action = time + HX_FRAME_TIME * 3; if (self.searchtime == 0) // Done only on birth of raven { self.searchtime = time + .5; self.angles_y = random(0, 360); self.angles_x = 0; makevectors (self.angles); self.velocity = normalize (v_forward); self.velocity = self.velocity * 600; } if ((self.searchtime < time) || (self.lifetime < time) || (self.owner.raven_cnt > 6)) raven_death_init(); } // // Chase after the enemy // void raven_track (void) { vector delta; vector hold_spot; // dprint("\n trk:"); // dprint(self.enemy.classname); // The FL_MONSTER flag gets flipped when it becomes a head if ((self.enemy.health <= 0) || (self.enemy == world) || (!self.enemy.flags & FL_MONSTER)) raven_search(); else { traceline(self.origin,self.enemy.origin,TRUE,self); if (trace_fraction == 1) { hold_spot = self.enemy.origin; hold_spot_z += self.enemy.maxs_z; // Hit 'em on the head delta = hold_spot - self.origin; self.velocity = normalize(delta); self.velocity = self.velocity * 600; self.angles = vectoangles(self.velocity); self.think1 = raven_track; self.next_action = time + HX_FRAME_TIME * 3; self.think = raven_flap; self.nextthink = time + HX_FRAME_TIME; } else raven_search(); } if ((self.lifetime < time) || (self.owner.raven_cnt > 6)) { raven_death_init(); return; } } void raven_track_init (void) { vector delta; vector hold_spot; if ((self.enemy.health <= 0) || (self.enemy == world)) raven_search(); else { hold_spot = self.enemy.origin; hold_spot_z += self.enemy.maxs_z; delta = hold_spot - self.origin; self.velocity = normalize(delta); self.angles = vectoangles(self.velocity); self.idealpitch = self.angles_x; makevectors(self.angles); self.velocity = normalize(v_forward); self.velocity = self.velocity * 600; self.pitchdowntime = time + HX_FRAME_TIME *3; self.think = raven_track; self.nextthink = time; } } // Everything comes back to here void raven_flap(void) { AdvanceFrame(0,7); // Flapping wings if ((self.frame == 1) && (random() < .2)) { sound(self,CHAN_VOICE,"raven/squawk2.wav",1,ATTN_NORM); } if (self.next_action < time) { self.think = self.think1; self.nextthink = time; } else { // ChangeYaw(); self.think = raven_flap; self.nextthink = time + HX_FRAME_TIME; } if ((self.lifetime < time) || (self.owner.raven_cnt > 6)) { raven_death_init(); return; } } /*-------------------- Create one raven ----------------------*/ void create_raven(void) { entity missile; vector spot1, spot2; missile = spawn (); missile.frags=TRUE; missile.owner = self.owner; self.owner.raven_cnt += 1; missile.movetype = MOVETYPE_BOUNCEMISSILE; missile.solid = SOLID_BBOX; missile.takedamage = DAMAGE_NO; // set missile speed makevectors (self.v_angle); missile.velocity = normalize (v_forward); missile.velocity = missile.velocity * 600; missile.angles = vectoangles(missile.velocity); missile.searchtime = 0; missile.yaw_speed = 50; setmodel (missile, "models/ravproj.mdl"); setsize (missile, '-8 -8 8', '8 8 8'); setorigin (missile, self.origin + self.proj_ofs - v_forward * 14 + v_right * random(-8,8)); missile.touch = raven_touch; missile.lifetime = time + 5; missile.classname = "bird_missile"; sound(missile,CHAN_VOICE,"raven/ravengo.wav",1,ATTN_NORM); // Find an enemy makevectors(self.v_angle); spot1 = self.origin + self.proj_ofs; spot2 = spot1 + (v_forward*600); // Look ahead traceline(spot1,spot2,FALSE,self); missile.th_die=raven_death_init; // We have a victim in sights if ((trace_ent!=world) && (trace_ent.flags & FL_MONSTER) && (trace_ent.owner != self) && (trace_ent.health>0)) { missile.enemy = trace_ent; missile.nextthink = time + HX_FRAME_TIME; missile.think = raven_flap; missile.next_action = time + .01; missile.think1 = raven_track; missile.think1 = raven_track_init; } else { missile.nextthink = time + .01; missile.think = raven_search; } } void ravenmissile_explode (void) { create_raven(); create_raven(); create_raven(); CreateWhiteSmoke(self.origin + '0 0 0','0 0 8',HX_FRAME_TIME * 3); CreateWhiteSmoke(self.origin + '0 0 5','0 0 8',HX_FRAME_TIME * 3); CreateWhiteSmoke(self.origin + '0 0 10','0 0 8',HX_FRAME_TIME * 3); remove(self); } void ravenmissile_touch (void) { if (pointcontents(self.origin) == CONTENT_SKY) { remove(self); return; } if (other.health) { sound (self, CHAN_WEAPON, "weapons/explode.wav", 1, ATTN_NORM); starteffect(CE_SM_EXPLOSION , self.origin); self.enemy = other; T_Damage(other,self,self,10); } ravenmissile_explode(); } void ravenmissile_puff (void) { makevectors(self.angles); if (self.lifetime < time) ravenmissile_explode(); else { thinktime self : HX_FRAME_TIME * 3; self.think = ravenmissile_puff; } } /*-------------------- Launch all ravens ----------------------*/ void launch_superraven (void) { entity newmis; self.attack_finished = time + 0.5; makevectors(self.v_angle); newmis = spawn(); setmodel (newmis, "models/birdmsl2.mdl"); newmis.movetype = MOVETYPE_FLYMISSILE; newmis.solid = SOLID_BBOX; newmis.takedamage = DAMAGE_NO; newmis.owner = self; setsize (newmis, '0 0 0', '0 0 0'); newmis.velocity = normalize (v_forward); newmis.velocity = newmis.velocity * 600; newmis.angles = vectoangles(newmis.velocity); setorigin(newmis, self.origin + self.proj_ofs + v_forward*10); newmis.touch = ravenmissile_touch; newmis.lifetime = time + .5; newmis.avelocity_z = 1000; newmis.scale = .40; thinktime newmis : HX_FRAME_TIME * 3; newmis.think = ravenmissile_puff; self.punchangle_x= random(-3); } void ravenshot_touch (void) { if (pointcontents(self.origin) == CONTENT_SKY) { remove(self); return; } T_Damage (other, self, self.owner, 30 ); sound (self, CHAN_WEAPON, "weapons/explode.wav", 1, ATTN_NORM); starteffect(CE_SM_EXPLOSION , self.origin); remove(self); } void create_raven_shot2(vector location,float add_yaw,float nexttime,float rotate,void() nextfunc) { entity missile; vector holdangle; missile = spawn (); missile.owner = self.owner; missile.movetype = MOVETYPE_FLYMISSILE; missile.solid = SOLID_BBOX; // set missile speed missile.angles = self.angles; holdangle = self.angles; holdangle_z = 0; holdangle_x = 0 - holdangle_x; holdangle_y += add_yaw; makevectors (holdangle); missile.velocity = normalize (v_forward); missile.velocity = missile.velocity * 800; if (rotate) missile.avelocity_z = 1000; else missile.avelocity_z = -1000; missile.touch = ravenshot_touch; setmodel (missile, "models/vindsht1.mdl"); setsize (missile, '0 0 0', '0 0 0'); setorigin (missile, location); missile.classname = "set_missile"; thinktime missile : nexttime; missile.think = nextfunc; } void create_raven_shot1(vector location,float nexttime,void() nextfunc,vector fire_angle) { entity missile; missile = spawn (); missile.owner = self; missile.movetype = MOVETYPE_FLYMISSILE; missile.solid = SOLID_BBOX; // set missile speed makevectors (fire_angle); missile.velocity = normalize (v_forward); missile.velocity = missile.velocity * 800; missile.avelocity_z = 1000; missile.angles = vectoangles(missile.velocity); missile.dmg = 40; missile.touch = ravenshot_touch; setmodel (missile, "models/vindsht1.mdl"); setsize (missile, '0 0 0', '0 0 0'); setorigin (missile, location); missile.classname = "set_missile"; thinktime missile : nexttime; missile.think = nextfunc; } void missle_straight(void) { vector holdangles; holdangles = self.angles; holdangles_z = 0; holdangles_x = 0 - holdangles_x; makevectors (holdangles); self.velocity = normalize (v_forward); self.velocity = self.velocity * 800; } void missle_straight1(void) { vector holdangles; holdangles = self.angles; holdangles_z = 0; holdangles_x = 0 - holdangles_x; makevectors (holdangles); self.velocity = normalize (v_forward); self.velocity = self.velocity * 800; create_raven_shot2(self.origin,-10,.25,1,missle_straight); CreateLittleBlueFlash(self.origin); sound(self,CHAN_WEAPON,"raven/split.wav",1,ATTN_NORM); } void missle_straight2(void) { vector holdangles; holdangles = self.angles; holdangles_z = 0; holdangles_x = 0 - holdangles_x; makevectors (holdangles); self.velocity = normalize (v_forward); self.velocity = self.velocity * 800; create_raven_shot2(self.origin,10,.25,1,missle_straight); CreateLittleBlueFlash(self.origin); sound(self,CHAN_WEAPON,"raven/split.wav",1,ATTN_NORM); } void split (void) { vector holdangles; // RIGHT SIDE create_raven_shot2(self.origin,-6,.50,0,missle_straight1); // LEFT SIDE create_raven_shot2(self.origin,6,.50,0,missle_straight2); CreateLittleBlueFlash(self.origin); sound(self,CHAN_WEAPON,"raven/split.wav",1,ATTN_NORM); self.dmg = 20; holdangles = self.angles; holdangles_z = 0; holdangles_x = 0 - holdangles_x; makevectors (holdangles); self.velocity = normalize (v_forward); self.velocity = self.velocity * 800; } void launch_set (vector dir_mod) { self.attack_finished = time + 0.5; create_raven_shot1(self.origin + self.proj_ofs + v_forward*14,0.05,split,self.v_angle); } void ravenstaff_power (void) { self.wfs=advanceweaponframe($fire1,$fire4); self.th_weapon=ravenstaff_power; if (self.weaponframe==$fire1) { self.punchangle_x = -4; launch_superraven(); self.greenmana -= 16; self.bluemana -= 16; } else if(self.weaponframe == $fire4) { self.weaponframe = $fire4; self.th_weapon=ravenstaff_idle; } thinktime self : HX_FRAME_TIME; } void ravenstaff_normal (void) { self.wfs=advanceweaponframe($fire1,$fire4); self.th_weapon=ravenstaff_normal; if(self.weaponframe==$fire3) { self.punchangle_x = -2; launch_set('0 0 0'); self.greenmana -= 8; self.bluemana -= 8; } else if(self.wfs==WF_CYCLE_WRAPPED) { self.weaponframe = $rootpose; self.th_weapon=ravenstaff_idle; } thinktime self : HX_FRAME_TIME; } void ravenstaff_fire (void) { vector holdvelocity; if ((self.artifact_active & ART_TOMEOFPOWER) && (self.greenmana >= 16) && (self.bluemana >= 16)) { sound (self, CHAN_WEAPON, "raven/rfire2.wav", 1, ATTN_NORM); stuffcmd (self, "bf\n"); ravenstaff_power(); } else if ((self.greenmana >= 8) && (self.bluemana >= 8)) { stuffcmd (self, "bf\n"); makevectors(self.v_angle); holdvelocity = normalize(v_right); holdvelocity = holdvelocity * 10; starteffect(CE_TELESMK1, self.origin + self.proj_ofs + v_forward * 14,holdvelocity,HX_FRAME_TIME * 3); starteffect(CE_TELESMK1, self.origin + self.proj_ofs + v_forward * 14,holdvelocity * -1,HX_FRAME_TIME * 3); sound (self, CHAN_WEAPON, "raven/rfire1.wav", 1, ATTN_NORM); ravenstaff_normal(); } self.attack_finished = time + 0.5; } /* ============ ravenstaff_ready - just sit there until fired ============ */ void ravenstaff_idle (void) { self.weaponframe= $rootpose; self.th_weapon=ravenstaff_idle; } void ravenstaff_select (void) { self.wfs=advanceweaponframe($select1,$select12); self.weaponmodel=("models/ravenstf.mdl"); self.th_weapon=ravenstaff_select; if(self.weaponframe==$select12) { self.attack_finished = time - 1; ravenstaff_idle(); } } void ravenstaff_deselect (void) { self.wfs=advanceweaponframe($select12,$select1); self.th_weapon=ravenstaff_deselect; thinktime self : HX_FRAME_TIME; self.oldweapon = IT_WEAPON4; if(self.wfs==WF_CYCLE_WRAPPED) W_SetCurrentAmmo(); } gamecode/hc/h2/rider.hc000066400000000000000000000347031444734033100151600ustar00rootroot00000000000000/*QUAKED rider_path (0.5 0.3 0) (-8 -8 -8) (8 8 8) Specifies a path point for a rider. You can specify up to 4 other places that the rider can go after this point. -------------------------FIELDS------------------------- path_id: number (id) of this path point next_path_1: number (id) of a possible next point next_path_2: number (id) of a possible next point next_path_3: number (id) of a possible next point next_path_4: number (id) of a possible next point -------------------------------------------------------- */ void rider_path(void) { } void riderpath_init(void) { entity search,found; found = world; search = find(world, classname, "rider_path"); while(search != world && found == world) { if (search.path_id == 1) found = search; else search = find(search, classname, "rider_path"); } if (found==world) { dprint("No starting point for rider\n"); remove(self); return; } self.path_current=self.path_last = found; } void riderpath_findnext(void) { entity search,found; float next,num_points,position; num_points = 0; if (self.path_current.next_path_1) num_points += 1; if (self.path_current.next_path_2) num_points += 1; if (self.path_current.next_path_3) num_points += 1; if (self.path_current.next_path_4) num_points += 1; if (!num_points) { dprintf("rider path %s has no next points\n",self.path_id); remove(self); return; } position = random(num_points); // dprintf("Num_points is %s ",num_points); // dprintf("position is %s\n",position); num_points = next = 0; if (self.path_current.next_path_1) { num_points += 1; if (position <= num_points && !next) next = self.path_current.next_path_1; } if (self.path_current.next_path_2) { num_points += 1; if (position <= num_points && !next) next = self.path_current.next_path_2; } if (self.path_current.next_path_3) { num_points += 1; if (position <= num_points && !next) next = self.path_current.next_path_3; } if (self.path_current.next_path_4) { num_points += 1; if (position <= num_points && !next) next = self.path_current.next_path_4; } if (!next) { dprint("Internal error for rider path\n"); dprintf(" Next is %s\n",next); dprintf(" Num_points is %s\n",num_points); dprintf(" position is %s\n",position); return; } found = world; search = find(world, classname, "rider_path"); while(search != world && found == world) { if (search.path_id == next) found = search; else search = find(search, classname, "rider_path"); } if (!found) { dprintf("Could not find rider path %s\n",next); remove(self); return; } else self.path_current = found; } void rider_die(void); void riderpath_move(float move_speed) { float distance, altitude, temp; vector displace; entity save_ent; if(self.velocity!='0 0 0') self.velocity='0 0 0'; self.ideal_yaw = vectoyaw(self.path_current.origin - self.origin); self.rider_last_y_change = self.rider_y_change; self.rider_y_change = ChangeYaw(); //rj rider_die(); if (self.movetype == MOVETYPE_FLY) { distance = vhlen(self.origin - self.path_current.origin); altitude = self.path_current.origin_z - self.origin_z; if (distance < 400) { // altitude *= 0.06; temp = (distance - self.rider_path_distance) / (400-self.rider_path_distance); temp = 1-temp; temp = temp / 6; if (altitude > 30) { self.angles_x = temp * 200; self.rider_move_adjustment -= 0.1; } else if (altitude < -30) { self.angles_x = 0 - (temp * 200); self.rider_move_adjustment += 0.15; } if (altitude > 60) { self.rider_move_adjustment -= 0.1; } else if (altitude < -60) { self.rider_move_adjustment += 0.15; } altitude *= temp; } else { altitude = 0; self.angles_x -= self.angles_x / 10; self.rider_move_adjustment -= self.rider_move_adjustment / 15; } // dprintf("Move adjustment %s\n",self.rider_move_adjustment); self.origin_z += altitude; // altitude = self.path_current.origin_z - self.origin_z; // dprintf("Flying: distance %s",distance); // dprintf(" Alt is %s\n",altitude); } move_speed += self.rider_move_adjustment; if(!walkmove(self.angles_y, move_speed, TRUE)) { save_ent=self.goalentity; self.goalentity=self.path_current; movetogoal(move_speed); self.goalentity=save_ent; } if (self.classname != "rider_famine") // Famine doesn't do radius pain { if (trace_ent)//!=world&&trace_ent.classname!="ghost") { displace = normalize(trace_ent.origin - self.origin); if (infront(trace_ent)) { trace_ent.velocity += displace*random(1000,1600); T_Damage (trace_ent, self, self, random(25,35)); if(trace_ent.flags&FL_CLIENT) { trace_ent.punchangle_x = random(-9,-1); trace_ent.punchangle_y = random(-10,10); trace_ent.punchangle_z = random(-10,10); } } else { trace_ent.velocity += displace*random(700,900); T_Damage (trace_ent, self, self, random(15,20)); if(trace_ent.flags&FL_CLIENT) { trace_ent.punchangle_x = random(-3,2); trace_ent.punchangle_y = random(-5,5); trace_ent.punchangle_z = random(-5,5); } } } } distance = vhlen(self.origin - self.path_current.origin); if (distance < self.rider_path_distance) riderpath_findnext(); } void rider_multi_wait(void) { if (self.max_health) { self.health = self.max_health; self.takedamage = DAMAGE_NO_GRENADE; self.solid = SOLID_BBOX; } } void rider_multi_trigger(void) { if (self.nextthink > time) { return; // already been triggered } if (self.enemy.classname != "rider_war") { return; } if (random() <= self.rt_chance) { if (self.noise) sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); // don't trigger again until reset self.takedamage = DAMAGE_NO; activator = self.enemy; SUB_UseTargets(); } if (self.wait > 0) { self.think = rider_multi_wait; thinktime self : self.wait; } else { // we can't just remove (self) here, because this is a touch function // called while C code is looping through area links... self.touch = SUB_Null; thinktime self : 0.1; self.think = SUB_Remove; } } void rider_multi_use(void) { if (time < self.attack_finished) return; self.enemy = activator; rider_multi_trigger(); } void rider_multi_touch(void) { if (time < self.attack_finished) return; // if the trigger has an angles field, check player's facing direction if (self.movedir != '0 0 0') { makevectors (other.angles); if (v_forward * self.movedir < 0) return; // not facing the right way } self.enemy = other; rider_multi_trigger (); } /*QUAKED rider_trigger_multiple (0.5 0.15 0) ? -------------------------FIELDS------------------------- rt_chance: chance (0-1) that the trigger will be run -------------------------------------------------------- */ void rider_trigger_multiple(void) { if (!self.wait) self.wait = 0.2; self.use = rider_multi_use; InitTrigger (); if (self.health) { self.max_health = self.health; self.th_die = multi_killed; self.takedamage = DAMAGE_NO_GRENADE; self.solid = SOLID_BBOX; setorigin (self, self.origin); // make sure it links into the world } else { self.touch = rider_multi_touch; } } /*QUAKED rider_trigger_once (0.5 0.15 0) ? -------------------------FIELDS------------------------- rt_chance: chance (0-1) that the trigger will be run -------------------------------------------------------- */ void rider_trigger_once(void) { self.wait = -1; rider_trigger_multiple(); } void beam_move(void) { thinktime self : HX_FRAME_TIME; if (self.scale < self.beam_max_scale) self.scale += 0.6; else self.scale = self.beam_max_scale; if (self.beam_direction) { self.beam_angle_a += self.beam_speed; if (self.beam_angle_a >= 360) self.beam_angle_a -= 360; } else { self.beam_angle_a -= self.beam_speed; if (self.beam_angle_a < 0) self.beam_angle_a += 360; } self.angles_x = cos(self.beam_angle_a) * self.beam_angle_b; self.angles_z = sin(self.beam_angle_a) * self.beam_angle_b; } void star_think(void) { thinktime self : HX_FRAME_TIME; self.velocity = self.velocity * 1.05; if (self.scale < 2) self.scale *= 1.08; } void rider_star(void) { entity star; float angle; if (random() < 0.5) return; star = spawn(); setmodel(star,"models/boss/star.mdl"); setorigin(star,self.origin); setsize (star, '0 0 0', '0 0 0'); star.owner = self.owner; star.movetype = MOVETYPE_FLYMISSILE; star.solid = SOLID_BBOX; star.avelocity = randomv('40 40 40', '100 100 100'); star.scale = 0.1; angle = random(360); star.velocity_x = cos(angle)*300; star.velocity_y = sin(angle)*300; star.classname = "rider_temp"; star.think = star_think; star.touch = SUB_Remove; thinktime star : HX_FRAME_TIME; } void circle_think(void) { thinktime self : HX_FRAME_TIME; rider_star(); if (self.monster_stage == 0) { self.scale *= 1.0275; if (self.scale >= 2.5) { self.monster_stage = 1; self.scale = 2.5; } } else if (self.monster_stage == 1) { self.scale -= random(.1); if (self.scale < 1.5 || random() < 0.1) self.monster_stage = 2; } else if (self.monster_stage == 2) { self.scale += random(.1); if (self.scale >= 2.5) { self.monster_stage = 1; self.scale = 2.5; } else if (random() < 0.1) self.monster_stage = 1; } } void rider_eol(void) { entity search; if (self.count == 0) { self.count = 1; self.effects (-) EF_BRIGHTLIGHT; self.effects (+) EF_NODRAW; if(self.movechain!=world) self.movechain.effects (+) EF_NODRAW; thinktime self : 5; search = find(world, classname, "rider_temp"); while (search != world) { remove(search); search = find(search, classname, "rider_temp"); } return; } // return; nextmap = self.map; nextstartspot = self.target; //rj nextmap = "rick1"; intermission_running = 1; if (deathmatch) intermission_exittime = time + 5; else if (self.classname == "monster_eidolon") intermission_exittime = time + 99999; else intermission_exittime = time + 2; //Remove cross-level trigger server flags for next hub serverflags (-) SFL_CROSS_TRIGGERS; search=find(world,classname,"player"); while(search) {//Take away all their goodies search.puzzle_inv1 = string_null; search.puzzle_inv2 = string_null; search.puzzle_inv3 = string_null; search.puzzle_inv4 = string_null; search.puzzle_inv5 = string_null; search.puzzle_inv6 = string_null; search.puzzle_inv7 = string_null; search.puzzle_inv8 = string_null; search=find(search,classname,"player"); } WriteByte (MSG_ALL, SVC_INTERMISSION); if (!cvar("registered") && cvar("oem")) { WriteByte (MSG_ALL, 9); intermission_exittime = time + 99999; } else if (self.classname == "rider_famine") WriteByte (MSG_ALL, 1); else if (self.classname == "rider_death") WriteByte (MSG_ALL, 2); else if (self.classname == "rider_pestilence") WriteByte (MSG_ALL, 3); else if (self.classname == "rider_war") WriteByte (MSG_ALL, 4); else if (self.classname == "monster_eidolon") WriteByte (MSG_ALL, 6); else dprint("Invalid boss creature\n"); FreezeAllEntities(); } void rider_die(void) { entity beam; entity save; entity found; vector new_origin; if (self.think != rider_die) { SUB_UseTargets(); self.think = rider_die; self.count = 0; thinktime self : HX_FRAME_TIME; self.rider_death_speed = 0.2; // self.effects = EF_NODRAW; return; } if (self.count == 0) { sound (self, CHAN_AUTO, "famine/flashdie.wav", 1, ATTN_NONE); // Start of the death flash found=find(world,classname,"player"); while(found) {//Give them all the exp AwardExperience(found,self,self.experience_value); found=find(found,classname,"player"); } self.experience_value=FALSE; self.drawflags (+) MLS_ABSLIGHT; self.abslight = 3; if(self.noise) sound(self,CHAN_VOICE,self.noise,1,ATTN_NONE); self.movetype=MOVETYPE_NONE; self.velocity='0 0 0'; } thinktime self : self.rider_death_speed; self.rider_death_speed += 0.1; if (self.count >= 10) { if (self.count == 3) { beam = spawn(); new_origin = self.origin + '0 0 50'; setmodel(beam,"models/boss/circle.mdl"); setorigin(beam,new_origin); setsize (beam, '0 0 0', '0 0 0'); beam.owner = self; beam.movetype = MOVETYPE_FLYMISSILE; beam.solid = SOLID_NOT; beam.drawflags = SCALE_TYPE_UNIFORM; beam.scale = .1; beam.skin = 0; beam.avelocity = '0 0 300'; beam.think = circle_think; thinktime beam : HX_FRAME_TIME; self.count = 13; } self.count = 0; self.think = rider_eol; thinktime self : .5; return; } else { self.effects = EF_BRIGHTLIGHT; // self.effects = EF_NODRAW; if (self.count == 3) { beam = spawn(); new_origin = self.origin + '0 0 50'; setmodel(beam,"models/boss/circle.mdl"); setorigin(beam,new_origin); setsize (beam, '0 0 0', '0 0 0'); beam.owner = self; beam.movetype = MOVETYPE_FLYMISSILE; beam.solid = SOLID_NOT; beam.drawflags = SCALE_TYPE_UNIFORM; beam.scale = .1; beam.skin = 0; beam.avelocity = '0 0 300'; beam.classname = "rider_temp"; beam.think = circle_think; thinktime beam : HX_FRAME_TIME; } else if (self.count == 0) { starteffect(18, self.origin + '0 0 40'); } } self.count += 1; beam = spawn(); makevectors(self.angles); // new_origin = v_factorrange('-3 -25 45', '3 25 50') + self.origin; new_origin = self.origin + '0 0 50'; setmodel(beam,"models/boss/shaft.mdl"); setorigin(beam,new_origin); setsize (beam, '0 0 0', '0 0 0'); beam.owner = self; beam.drawflags = SCALE_ORIGIN_BOTTOM | SCALE_TYPE_XYONLY; beam.movetype = MOVETYPE_FLYMISSILE; beam.solid = SOLID_NOT; beam.think = beam_move; beam.angles = '0 0 0'; beam.angles_x = random(-50,50); beam.angles_z = random(-50,50); beam.beam_angle_a = random(360); beam.beam_angle_b = random(20,130); beam.scale = .1; beam.beam_max_scale = random(.5,1.5); beam.classname = "rider_temp"; if (random() > 0.5) beam.beam_direction = 1; beam.beam_speed = random(2,4.5); save = self; self = beam; beam_move(); self = save; } void rider_use(void) { thinktime self : 0.2; // wait for path points to spawn } void rider_init(void) { precache_model3 ("models/boss/shaft.mdl"); precache_model3 ("models/boss/circle.mdl"); precache_model3 ("models/boss/star.mdl"); precache_sound3 ("famine/flashdie.wav"); total_monsters += 1; self.takedamage = DAMAGE_YES; self.flags(+)FL_MONSTER; self.flags2(+)FL_ALIVE; self.thingtype=THINGTYPE_FLESH; if(self.classname!="monster_eidolon") self.monsterclass=CLASS_BOSS; self.th_die = rider_die; self.use = rider_use; if (!(self.spawnflags & 1)) thinktime self : 0.2; // wait for path points to spawn } gamecode/hc/h2/rings.hc000066400000000000000000000073151444734033100151740ustar00rootroot00000000000000void player_fly(void); void Ring_Init(string modelname,string name); void ring_touch (void) { float amount; entity holdself; if ((other.classname != "player") || (other.health <= 0)) return; // Was it thrown out by player just a frame of two ago????? if (self.owner == other && self.artifact_ignore_owner_time > time) return; if (self.artifact_ignore_time > time) return; amount = random(); if (amount < 0.5) { sprint (other, STR_YOUPOSSESS); sprint (other, self.netname); } else { sprint (other, STR_YOUHAVEACQUIRED); sprint (other, self.netname); } sprint (other,"\n"); if (deathmatch||(self.classname == "Ring_Flight"&&!self.owner)) { self.mdl = self.model; self.nextthink = time + 60; self.think = SUB_regen; } sound (other, CHAN_VOICE, "items/ringpkup.wav", 1, ATTN_NORM); stuffcmd (other, "bf\n"); self.solid = SOLID_NOT; self.model = string_null; // take appropriate action if ((self.classname == "Ring_Flight") && (deathmatch)) { other.cnt_flight += 1; } else if (self.classname == "Ring_Flight") { if(other.rings_active&RING_FLIGHT)//add time to current ring { other.ring_flight = 100; other.ring_flight_time = time + 1; } else { other.rings(+)RING_FLIGHT; other.ring_flight = 100; other.ring_flight_time = time + 1; holdself = self; self = other; player_fly(); self = holdself; other.rings_active (+) RING_FLIGHT; } other.rings_low (-) RING_FLIGHT; } else if (self.classname == "Ring_WaterBreathing") { other.rings (+) RING_WATER; other.ring_water = 100; other.ring_water_time = time + 1; if (other.rings_low & RING_WATER) other.rings_low (-) RING_WATER; } else if (self.classname == "Ring_Turning") { other.rings (+) RING_TURNING; other.ring_turning = 100; other.ring_turning_time = time + 1; other.rings_active (+) RING_TURNING; if (other.rings_low & RING_TURNING) other.rings_low (-) RING_TURNING; } else if (self.classname == "Ring_Regeneration") { other.rings (+) RING_REGENERATION; other.ring_regeneration = 100; other.rings_active (+) RING_REGENERATION; if (other.rings_low & RING_REGENERATION) other.rings_low (-) RING_REGENERATION; } activator = other; SUB_UseTargets(); // fire all targets / killtargets } /*QUAKED Ring_WaterBreathing (0 0 0) (-8 -8 -44) (8 8 20) FLOATING Ring of Water Breathing -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void Ring_WaterBreathing (void) { precache_model("models/ringwb.mdl"); Ring_Init("models/ringwb.mdl",STR_RINGWATERBREATHING); } /*QUAKED Ring_Flight (0 0 0) (-8 -8 -44) (8 8 20) FLOATING Ring of Flight -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void Ring_Flight (void) { precache_model("models/ringft.mdl"); Ring_Init("models/ringft.mdl",STR_RINGFLIGHT); } /*QUAKED Ring_Regeneration (0 0 0) (-8 -8 -44) (8 8 20) FLOATING Ring of Regeneration -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void Ring_Regeneration (void) { precache_model("models/ringre.mdl"); Ring_Init("models/ringre.mdl",STR_RINGREGENERATION); } /*QUAKED Ring_Turning (0 0 0) (-8 -8 -44) (8 8 20) FLOATING Ring of Turning -------------------------FIELDS------------------------- None -------------------------------------------------------- */ void Ring_Turning (void) { precache_model("models/ringtn.mdl"); Ring_Init("models/ringtn.mdl",STR_RINGTURNING); } void Ring_Init(string modelname,string name) { setmodel(self, modelname); self.netname = name; setsize(self,'0 0 0','0 0 0'); self.hull=HULL_POINT; self.touch = ring_touch; StartItem(); } gamecode/hc/h2/scorpion.hc000066400000000000000000000400511444734033100157000ustar00rootroot00000000000000//************************************************************************** //** scorpion.hc //** bgokey //************************************************************************** // FRAMES ------------------------------------------------------------------ // Attack 1 (claw and tail) $frame ScAttA1 ScAttA2 ScAttA3 ScAttA4 ScAttA5 $frame ScAttA6 ScAttA7 ScAttA8 ScAttA9 ScAttA10 $frame ScAttA11 ScAttA12 ScAttA13 ScAttA14 ScAttA15 $frame ScAttA16 ScAttA17 ScAttA18 ScAttA19 ScAttA20 $frame ScAttA21 ScAttA22 ScAttA23 ScAttA24 ScAttA25 // Attack 2 (claw only) $frame ScAttB1 ScAttB2 ScAttB3 ScAttB4 ScAttB5 $frame ScAttB6 ScAttB7 ScAttB8 ScAttB9 ScAttB10 $frame ScAttB11 ScAttB12 ScAttB13 ScAttB14 ScAttB15 $frame ScAttB16 ScAttB17 ScAttB18 ScAttB19 ScAttB20 $frame ScAttB21 ScAttB22 ScAttB23 ScAttB24 ScAttB25 $frame ScAttB26 ScAttB27 // Attack 3 (tail only) $frame ScAttC1 ScAttC2 ScAttC3 ScAttC4 ScAttC5 $frame ScAttC6 ScAttC7 ScAttC8 ScAttC9 ScAttC10 $frame ScAttC11 ScAttC12 ScAttC13 ScAttC14 ScAttC15 $frame ScAttC16 ScAttC17 ScAttC18 ScAttC19 ScAttC20 $frame ScAttC21 ScAttC22 // Death $frame SCDead1 SCDead2 SCDead3 SCDead4 SCDead5 $frame SCDead6 SCDead7 SCDead8 SCDead9 SCDead10 $frame SCDead11 SCDead12 SCDead13 SCDead14 SCDead15 $frame SCDead16 SCDead17 SCDead18 SCDead19 SCDead20 $frame SCDead21 // Pain $frame ScPain1 ScPain2 ScPain3 ScPain4 ScPain5 $frame ScPain6 ScPain7 ScPain8 ScPain9 ScPain10 // Transition from standing to attack pose $frame ScRedy1 ScRedy2 ScRedy3 ScRedy4 ScRedy5 $frame ScRedy6 ScRedy7 ScRedy8 ScRedy9 ScRedy10 $frame ScRedy11 // Transition from standing to walk $frame ScStep1 ScStep2 ScStep3 ScStep4 // Transition from walk to standing $frame ScStop1 ScStop2 ScStop3 ScStop4 ScStop5 $frame ScStop6 ScStop7 ScStop8 ScStop9 // Wake from resting (ScWake1 = resting) $frame ScWake1 ScWake2 ScWake3 ScWake4 ScWake5 $frame ScWake6 ScWake7 ScWake8 ScWake9 ScWake10 $frame ScWake11 ScWake12 ScWake13 ScWake14 ScWake15 $frame ScWake16 ScWake17 ScWake18 ScWake19 ScWake20 $frame ScWake21 ScWake22 ScWake23 ScWake24 ScWake25 $frame ScWake26 ScWake27 ScWake28 ScWake29 ScWake30 // Walk $frame ScWalk1 ScWalk2 ScWalk3 ScWalk4 ScWalk5 $frame ScWalk6 ScWalk7 ScWalk8 ScWalk9 ScWalk10 $frame ScWalk11 ScWalk12 ScWalk13 ScWalk14 ScWalk15 $frame ScWalk16 // CONSTANTS --------------------------------------------------------------- float SCORPION_YELLOW = 0; float SCORPION_BLACK = 1; // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- void ScorpionInit(float type); void ScorpionStand(void); void ScorpionWake(void); void ScorpionWalk(void); void ScorpionRun(void); void ScorpionRunBlack(void); void ScorpionPainDecide(void); void ScorpionPain(void); void ScorpionMeleeDecide(void); void ScorpionMelee1(void); void ScorpionMelee2(void); void ScorpionMelee3(void); void ScorpionMelee4(void); void ScorpionDie(void); void ScorpionDieInit(void); entity ScorpionLookProjectiles(void); float ScorpionCheckDefense(void); void ScorpionStrafeDefense(void); // PRIVATE DATA DEFINITIONS ------------------------------------------------ float ScorpionStandFrames[6] = { $scwake1, $scwake2, $scwake3, $scwake4, $scwake3, $scwake2 }; // CODE -------------------------------------------------------------------- //========================================================================== // // monster_scorpion_yellow // //========================================================================== /*QUAKED monster_scorpion_yellow (1 0.3 0) (-10 -10 0) (10 10 64) AMBUSH Yellow scorpion. ------- key / value ---------------------------- health = 100 experience_value = 60 ------- spawnflags ----------------------------- AMBUSH */ void monster_scorpion_yellow(void) { ScorpionInit(SCORPION_YELLOW); } //========================================================================== // // monster_scorpion_black // //========================================================================== /*QUAKED monster_scorpion_black (1 0.3 0) (-10 -10 0) (10 10 64) AMBUSH Black scorpion. ------- key / value ---------------------------- health = 200 experience_value = 150 ------- spawnflags ----------------------------- AMBUSH */ void monster_scorpion_black(void) { ScorpionInit(SCORPION_BLACK); } //========================================================================== // // ScorpionInit // //========================================================================== void ScorpionInit(float type) { if(deathmatch) { remove(self); return; } if not(self.flags2&FL_SUMMONED) { precache_model2("models/scorpion.mdl"); precache_sound2("scorpion/awaken.wav"); precache_sound2("scorpion/walk.wav"); precache_sound2("scorpion/clawsnap.wav"); precache_sound2("scorpion/tailwhip.wav"); precache_sound2("scorpion/pain.wav"); precache_sound2("scorpion/death.wav"); } setmodel(self, "models/scorpion.mdl"); self.solid = SOLID_SLIDEBOX; self.movetype = MOVETYPE_STEP; self.thingtype = THINGTYPE_FLESH; if (type == SCORPION_YELLOW) self.yaw_speed = 8; else self.yaw_speed = 10; self.mass = 9; self.mintel = 4; //self.touch = SUB_Null; //self.use = SUB_Null; setsize(self, '-16 -16 0', '16 16 64'); if(type == SCORPION_YELLOW) { self.health = 100; self.experience_value = 60; } else { self.health = 200; self.experience_value = 150; } self.takedamage = DAMAGE_YES; self.th_stand = ScorpionStand; self.th_walk = ScorpionWalk; if(type == SCORPION_BLACK) self.th_run = ScorpionRunBlack; else self.th_run = ScorpionRun; self.th_melee = ScorpionMeleeDecide; self.th_pain = ScorpionPainDecide; self.th_die = ScorpionDieInit; self.view_ofs = '0 0 12'; self.scorpionType = type; if(type == SCORPION_BLACK) { self.skin = 1; } walkmonster_start(); } //========================================================================== // // ScorpionStand // //========================================================================== void ScorpionStand(void) { self.think = ScorpionStand; self.nextthink = time + 0.2; self.scorpionRest += 2; if(self.scorpionRest < 0 || self.scorpionRest > 5) { self.scorpionRest = 0; } self.frame = ScorpionStandFrames[self.scorpionRest]; ai_stand(); if(self.think != ScorpionStand) { // Wake up self.th_save = self.think; self.think = ScorpionWake; sound(self, CHAN_VOICE, "scorpion/awaken.wav", 1, ATTN_NORM); } } //========================================================================== // // ScorpionWake // //========================================================================== void ScorpionWake(void) [++ $scwake1..$scwake30] { if(cycle_wrapped) { self.cnt = time; self.think = self.th_save; self.think(); } } //========================================================================== // // ScorpionWalk // //========================================================================== void ScorpionWalk(void) [++ $scwalk1..$scwalk16] { if(((self.scorpionWalkCount += 1)&3) == 0) { sound(self, CHAN_BODY, "scorpion/walk.wav", random(0.9, 1), ATTN_NORM); } ai_walk(2); if(random()<0.1) pitch_roll_for_slope('0 0 0'); } //========================================================================== // // ScorpionRunBlack // //========================================================================== void ScorpionRunBlack(void) [++ $scwalk1..$scwalk16] { float enemy_dist; if(((self.scorpionWalkCount += 1)&3) == 0) { sound(self, CHAN_BODY, "scorpion/walk.wav", random(0.9, 1), ATTN_NORM); } if ((self.enemy.last_attack > time - 1) && (fov(self, self.enemy, 45))) { if (ScorpionCheckDefense()) { ScorpionStrafeDefense(); return; } } if (self.attack_state == AS_SLIDING) if ((random() < 0.8) && (self.cnt <= time)) { self.cnt = time + random(0.5, 1.0); self.attack_state = AS_STRAIGHT; ScorpionMelee4(); return; } enemy_dist = vlen(self.enemy.origin - self.origin); if (enemy_dist < 120) { if ((random() < 0.33) && (infront(self.enemy)) && (self.cnt <= time)) { self.attack_state = AS_SLIDING; self.cnt = time + random(0.5, 1.0); self.lefty = random(0,1); if (self.lefty < 0.5) self.lefty = 0; else self.lefty = 1; ai_run(10); return; } else if (!infront(self.enemy)) self.attack_state = AS_STRAIGHT; } else self.attack_state = AS_STRAIGHT; ai_run(8); if(random()<0.1) pitch_roll_for_slope('0 0 0'); } //========================================================================== // // ScorpionRun // //========================================================================== void ScorpionRun(void) [++ $scwalk1..$scwalk16] { float enemy_dist; if(((self.scorpionWalkCount += 1)&3) == 0) { sound(self, CHAN_BODY, "scorpion/walk.wav", random(0.9, 1), ATTN_NORM); } if ((self.enemy.last_attack > time - 1) && (fov(self, self.enemy, 45))) { if (ScorpionCheckDefense()) { ScorpionStrafeDefense(); return; } } if (self.attack_state == AS_SLIDING) if ((random() < 0.5) && (self.cnt <= time)) { self.cnt = time + random(0.5, 1.0); self.attack_state = AS_STRAIGHT; ScorpionMelee4(); return; } enemy_dist = vlen(self.enemy.origin - self.origin); if (enemy_dist < 120) { if ((random() < 0.33) && (infront(self.enemy)) && (self.cnt <= time)) { self.attack_state = AS_SLIDING; self.cnt = time + random(0.5, 1.0); self.lefty = random(0,1); if (self.lefty < 0.5) self.lefty = 0; else self.lefty = 1; ai_run(10); return; } else if (!infront(self.enemy)) self.attack_state = AS_STRAIGHT; } else self.attack_state = AS_STRAIGHT; ai_run(6); if(random()<0.1) pitch_roll_for_slope('0 0 0'); } //========================================================================== // // ScorpionPainDecide // //========================================================================== void ScorpionPainDecide(void) { if(random() < 0.3) { ScorpionPain(); sound(self, CHAN_VOICE, "scorpion/pain.wav", 1, ATTN_NORM); } } //========================================================================== // // ScorpionPain // //========================================================================== void ScorpionPain(void) [++ $scpain1..$scpain10] { if(cycle_wrapped) { self.th_run(); } } //========================================================================== // // ScorpionMelee // //========================================================================== void ScorpionMelee(float damage) { vector source; makevectors (self.angles); source = self.origin; traceline (source, source + v_forward*60, FALSE, self); if (trace_ent != self.enemy) return; damage += random(0.1, 1); T_Damage (self.enemy, self, self, damage); } //========================================================================== // // ScorpionMeleeDecide // //========================================================================== void ScorpionMeleeDecide(void) { float r; r = random(); self.last_attack=time; if (self.classname == "monster_scorpion_black") { if(r < 0.2) { ScorpionMelee1(); } else if(r < 0.4) { ScorpionMelee2(); } else if (r < 0.6) { ScorpionMelee3(); } else ScorpionMelee4(); } else { if(r < 0.3) { ScorpionMelee1(); } else if(r < 0.6) { ScorpionMelee2(); } else if (r < 0.9) { ScorpionMelee3(); } else ScorpionMelee4(); } } //========================================================================== // // ScorpionMelee1 // //========================================================================== void ScorpionMelee1(void) [++ $scatta1..$scatta25] { if(self.frame == $scatta4 || self.frame == $scatta9) { sound(self, CHAN_VOICE, "scorpion/clawsnap.wav", 1, ATTN_NORM); ScorpionMelee(1); } else if(self.frame == $scatta14) { sound(self, CHAN_BODY, "scorpion/tailwhip.wav", 1, ATTN_NORM); ScorpionMelee(1); } if(self.frame > $scatta16 && self.frame < $scatta20) { ai_charge(4); if (self.classname == "monster_scorpion_yellow") ScorpionMelee(2); else ScorpionMelee(3); } else { ai_charge(2); } if(cycle_wrapped) self.think = self.th_run; } //========================================================================== // // ScorpionMelee2 // //========================================================================== void ScorpionMelee2(void) [++ $scattb1..$scattb27] { if(self.frame == $scattb4 || self.frame == $scattb8 || self.frame == $scattb13) { sound(self, CHAN_VOICE, "scorpion/clawsnap.wav", 1, ATTN_NORM); ScorpionMelee(1); } if(self.frame > $scattb16 && self.frame < $scattb20) { ai_charge(4); if (self.classname == "monster_scorpion_yellow") ScorpionMelee(2); else ScorpionMelee(3); } else { ai_charge(2); } if(cycle_wrapped) self.think = self.th_run; } //========================================================================== // // ScorpionMelee3 // //========================================================================== void ScorpionMelee3(void) [++ $scattc1..$scattc22] { if(self.frame == $scattc9) { sound(self, CHAN_BODY, "scorpion/tailwhip.wav", 1, ATTN_NORM); ScorpionMelee(1); } if(self.frame > $scattc16 && self.frame < $scattc20) { ai_charge(4); if (self.classname == "monster_scorpion_yellow") ScorpionMelee(2); else ScorpionMelee(3); } else { ai_charge(2); } if(cycle_wrapped) self.think = self.th_run; } //========================================================================== // // ScorpionMelee4 // //========================================================================== void ScorpionMelee4(void) [++ $scatta1..$scatta25] { if (self.frame == $scatta4 || self.frame == $scatta9) { sound(self, CHAN_VOICE, "scorpion/clawsnap.wav", 1, ATTN_NORM); ScorpionMelee(1); } else if(self.frame == $scatta14) { sound(self, CHAN_BODY, "scorpion/tailwhip.wav", 1, ATTN_NORM); if (self.classname == "monster_scorpion_yellow") ScorpionMelee(2); else ScorpionMelee(3); } if(self.frame > $scatta16 && self.frame < $scatta20) { ai_charge(16); if (self.classname == "monster_scorpion_yellow") ScorpionMelee(2); else ScorpionMelee(4); } else { ai_charge(12); } if(cycle_wrapped) self.think = self.th_run; } //========================================================================== // // ScorpionStrafeDefense // //========================================================================== void ScorpionStrafeDefense(void) [++ $scwalk1..$scwalk8] { float ofs; if (cycle_wrapped) { thinktime self : 0.1; self.think = self.th_run; return; } makevectors(self.v_angle); if (self.lefty == -1) ofs = 90; else ofs = -90; if (walkmove (self.ideal_yaw + ofs, 10, FALSE)) return; walkmove (self.ideal_yaw - ofs, 10, FALSE); if (self.lefty == -1) self.lefty = 1; else self.lefty = -1; } //========================================================================== // // ScorpionCheckDefense // //========================================================================== float ScorpionCheckDefense() { entity enemy_proj; float r; enemy_proj=ScorpionLookProjectiles(); if (!enemy_proj) return 0; r=range(enemy_proj); self.lefty=check_heading_left_or_right(enemy_proj); if(r==RANGE_NEAR) { if (random() < 0.2) return 1; } return 0; } //========================================================================== // // ScorpionLookProjectiles // //========================================================================== entity ScorpionLookProjectiles () { entity found, enemy_proj; found=findradius(self.origin,1000); while(found) { if(found.movetype==MOVETYPE_FLYMISSILE||found.movetype==MOVETYPE_BOUNCE||found.movetype==MOVETYPE_BOUNCEMISSILE) if(visible(found)) { if(heading(self,found,0.9)) enemy_proj=found; } found=found.chain; } if(enemy_proj) return enemy_proj; else return world; } //========================================================================== // // ScorpionDie // //========================================================================== void ScorpionDie(void) { if(self.frame == $scdead21) { MakeSolidCorpse(); return; } if(self.health < -30) { chunk_death(); } self.frame += 1; thinktime self : HX_FRAME_TIME; } void ScorpionDieInit(void) [$scdead1 ScorpionDie] { sound(self, CHAN_VOICE, "scorpion/death.wav", 1, ATTN_NORM); } gamecode/hc/h2/setmodth.hc000066400000000000000000000041631444734033100156770ustar00rootroot00000000000000//In progs.src, must come AFTER all player.hc's void() SetModelAndThinks = {//MG //Note: you'll also want to set a head model //for each. self.touch=PlayerTouch; self.th_die=PlayerDie; self.th_goredeath=player_frames_behead; self.th_pain=player_pain; if(self.playerclass==CLASS_ASSASSIN) { self.mass=6;//should be 15 Ass_Change_Weapon(); //sets other th_*'s based on weapon in hand setmodel (self, "models/assassin.mdl"); self.headmodel="models/h_ass.mdl"; if(self.weapon==IT_WEAPON4) self.th_weapon=setstaff_select; else if(self.weapon==IT_WEAPON2) self.th_weapon=crossbow_select; else if(self.weapon==IT_WEAPON3) self.th_weapon=grenade_select; else self.th_weapon=punchdagger_select; } else if(self.playerclass==CLASS_CRUSADER) { self.mass=7;//should be 10 setmodel (self, "models/crusader.mdl"); self.headmodel="models/h_cru.mdl"; Cru_Change_Weapon(); if(self.weapon==IT_WEAPON4) self.th_weapon=sunstaff_select; else if(self.weapon==IT_WEAPON3) self.th_weapon=meteor_select; else if(self.weapon==IT_WEAPON2) self.th_weapon=icestaff_select; else self.th_weapon=warhammer_select; } else if(self.playerclass==CLASS_PALADIN) { self.mass=8;//should be 15 Pal_Change_Weapon(); //sets other th_*'s based on weapon in hand setmodel (self, "models/paladin.mdl"); self.headmodel="models/h_pal.mdl"; if(self.weapon==IT_WEAPON4) self.th_weapon=purifier_select; else if(self.weapon==IT_WEAPON3) self.th_weapon=axe_select; else if(self.weapon==IT_WEAPON2) self.th_weapon=vorpal_select; else self.th_weapon=gauntlet_select; } else if(self.playerclass==CLASS_NECROMANCER) { self.mass=7;//should be 10 setmodel (self, "models/necro.mdl"); self.headmodel="models/h_nec.mdl"; Nec_Change_Weapon(); //sets other th_*'s based on weapon in hand if(self.weapon==IT_WEAPON4) self.th_weapon=ravenstaff_select; else if(self.weapon==IT_WEAPON1) self.th_weapon=sickle_select; else if(self.weapon==IT_WEAPON2) self.th_weapon=magicmis_select; else self.th_weapon=boneshard_select; } self.init_model=self.model; setsize(self,'-16 -16 0','16 16 56'); }; gamecode/hc/h2/setstaff.hc000066400000000000000000000370401444734033100156670ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\weapons\setstaff\newstff\scarabst.hc ============================================================================== */ // For building the model $cd Q:\art\models\weapons\setstaff\newstff $origin 0 0 0 $base BASE skin $skin skin $skin SKIN2 $flags 0 // $frame build1 build2 build3 build4 build5 $frame build6 build7 build8 build9 build10 $frame build11 build12 build13 build14 build15 // $frame chain1 chain2 chain3 chain4 chain5 $frame chain6 chain7 chain8 chain9 // $frame rootpose // $frame scarab1 scarab2 scarab3 scarab4 scarab5 $frame scarab6 scarab7 // $frame select1 select2 select3 select4 select5 $frame select6 select7 select8 select9 select10 $frame select11 select12 void setstaff_decide_attack (void); void setstaff_idle (void); void DrawLinks(void) { //These seem to lock up game if there are too many of them vector org, dir; org = self.origin; dir=normalize(self.view_ofs-org); org+=dir*15; WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); WriteByte (MSG_BROADCAST, TE_STREAM_CHAIN); WriteEntity (MSG_BROADCAST, self); WriteByte (MSG_BROADCAST, 1+STREAM_ATTACHED); WriteByte (MSG_BROADCAST, 1); WriteCoord (MSG_BROADCAST, org_x); WriteCoord (MSG_BROADCAST, org_y); WriteCoord (MSG_BROADCAST, org_z); WriteCoord (MSG_BROADCAST, self.view_ofs_x); WriteCoord (MSG_BROADCAST, self.view_ofs_y); WriteCoord (MSG_BROADCAST, self.view_ofs_z); } void() DeactivateGhook = { self.aflag=FALSE; CreateRedFlash(self.origin); sound (self, CHAN_BODY, "items/itmspawn.wav", 1, ATTN_NORM); remove(self); }; void PullBack (void) { if(self.enemy!=world) { self.enemy.flags2(-)FL_CHAINED; if(self.enemy.flags2&FL_ALIVE) { // dprintf("Restoring old movetype: %s\n",self.enemy.oldmovetype); self.enemy.movetype=self.enemy.oldmovetype; } else { // dprint("Setting movetype to bounce\n"); self.enemy.movetype=MOVETYPE_BOUNCE; } self.enemy=world; } // else // dprint("No enemy\n"); self.movetype=MOVETYPE_NOCLIP; self.solid=SOLID_NOT; self.velocity=normalize(self.view_ofs-self.origin)*350; self.flags(-)FL_ONGROUND; DrawLinks(); if(vlen(self.origin-self.view_ofs)<48||self.lifetime 100 ) dir = dir*300; if(self.enemy.flags&FL_ONGROUND) { self.enemy.flags(-)FL_ONGROUND; dir+='0 0 200'; } if(self.enemy.flags&FL_CLIENT) self.enemy.adjust_velocity=(self.enemy.velocity+dir)*0.5; else self.enemy.velocity = (self.enemy.velocity+dir)*0.5; } else self.enemy.velocity='0 0 0'; if(!self.enemy.health||!self.enemy.flags2&FL_ALIVE||!self.enemy.flags2&FL_CHAINED||self.attack_finished150||(other.flags&FL_MONSTER&&other.monsterclass>=CLASS_BOSS))//other.classname!="player") { self.lockentity=other; scarab_die(); } else { self.touch=SUB_Null; self.velocity='0 0 0'; self.enemy=other; ChainsOfLove(); } } else scarab_die(); } void scarab_think () { self.frame+=1; if(self.frame>15) self.frame=8; self.movechain.frame=self.frame; if(self.pain_finished<=time) { HomeThink(); self.angles=vectoangles(self.velocity); self.pain_finished=time+0.1; sound(self,CHAN_BODY,"assassin/scrbfly.wav",1,ATTN_NORM); } // particle4(self.origin,7,random(250,254),PARTICLETYPE_EXPLODE2,random(3,7)); if(self.lifetime300) DarkExplosion(); else if(other!=self.enemy) { if(other.takedamage) { self.enemy=other; makevectors(self.velocity); T_Damage(other,self,self.owner,self.dmg); if(self.dmg<10) { T_Damage(other,self,self.owner,10); DarkExplosion(); } else { SpawnPuff (self.origin, self.velocity, 10,other); SpawnPuff (self.origin+v_forward*36, self.velocity, 10,other); if(other.thingtype==THINGTYPE_FLESH) { sound(self,CHAN_VOICE,"assassin/core.wav",1,ATTN_NORM); MeatChunks(self.origin+v_forward*36, self.velocity*0.2+v_right*random(-30,150)+v_up*random(-30,150),5,other); } if(other.classname=="player") T_Damage(other,self,self.owner,(self.dmg+self.frags*10)/3); else T_Damage(other,self,self.owner,self.dmg+self.frags*10); self.frags+=1; self.dmg-=10; } } } } void pincer_think () { if(self.frame<7) self.frame+=1; if(self.pain_finished<=time) { self.pain_finished=time+1; sound(self,CHAN_BODY,"assassin/spin.wav",1,ATTN_NORM); } if(self.lifetime= HX_FRAME_TIME) { self.ltime = time; self.wfs = advanceweaponframe($scarab1,$scarab7); self.th_weapon = setstaff_powerfire; // Pa3PyX: we now have animation loop, so don't think thinktime self: 0; if (self.weaponframe== $scarab2) { TheOldBallAndChain(); self.greenmana -= 30; self.bluemana -= 30; } } if (self.wfs == WF_CYCLE_WRAPPED) setstaff_idle(); } void setstaff_settle () { self.wfs = advanceweaponframe($chain1,$chain9); self.th_weapon = setstaff_settle; if (self.wfs == WF_LAST_FRAME) setstaff_idle(); } void setstaff_readyfire (void) { // Pa3PyX: Added casting delay (0.5s). You can no // longer use set staff in place of a chaingun. if (self.attack_finished > time) { self.th_weapon = setstaff_readyfire; return; } if(self.weaponframe>$build15) self.weaponframe=$build1; if(self.weaponframe==$build1) sound(self,CHAN_WEAPON,"assassin/build.wav",1,ATTN_NORM); if (self.lifetime <= 0) self.lifetime = time; // Pa3PyX if (self.weaponframe >= $build1 && self.weaponframe < $build15) { self.weaponframe_cnt +=1; // Pa3PyX // if (self.weaponframe_cnt > 3) if (time - self.lifetime > HX_FRAME_TIME * 3) { self.wfs = advanceweaponframe($build1,$build15); self.weaponframe_cnt =0; self.lifetime = time; // Pa3PyX } else if(self.weaponframe_cnt==1) { if(self.greenmana>=1) self.greenmana-=1; if(self.bluemana>=1) self.bluemana-=1; } if(self.weaponframe==$build15) self.weaponframe_cnt=0; } else if(self.weaponframe_cnt=10) self.greenmana-=10; else self.button0=FALSE; if(self.bluemana>=10) self.bluemana-=10; else self.button0=FALSE; } self.th_weapon = setstaff_readyfire; if(!self.button0||self.greenmana<=0||self.bluemana<=0) { self.weaponframe_cnt=0; self.lifetime = 0; // Pa3PyX Drilla(14 - ($build15 - self.weaponframe)); self.attack_finished = time + 0.5; // Pa3PyX: casting delay setstaff_settle(); } } void() ass_setstaff_fire = { if (self.artifact_active & ART_TOMEOFPOWER) // Pause for firing in power up mode self.th_weapon=setstaff_powerfire; else self.th_weapon=setstaff_readyfire; thinktime self : 0; self.nextthink=time; }; void setstaff_idle (void) { self.weaponframe=$rootpose; self.th_weapon=setstaff_idle; } void setstaff_select (void) { self.wfs = advanceweaponframe($select12,$select1); self.weaponmodel = "models/scarabst.mdl"; self.th_weapon=setstaff_select; self.last_attack=time; if (self.wfs == WF_LAST_FRAME) { self.attack_finished = time - 1; self.ltime = -1; setstaff_idle(); } } void setstaff_deselect (void) { self.wfs = advanceweaponframe($select1,$select12); self.th_weapon=setstaff_deselect; if (self.wfs == WF_LAST_FRAME) W_SetCurrentAmmo(); } void setstaff_decide_attack (void) { self.attack_finished = time + 0.5; } gamecode/hc/h2/shardice.hc000066400000000000000000000041241444734033100156270ustar00rootroot00000000000000/* ============================================================================== ice shard (IMP) ============================================================================== */ // For building the model $cd q:/art/models/monsters/imp/final $base shrdbase 64 128 $skin shrdskin $frame shard void() shardTouch = { float damg; if (other == self.owner) return; // don't explode on owner if (pointcontents(self.origin) == CONTENT_SKY) { remove(self); return; } if(self.owner.classname=="monster_imp_lord") damg = random(33,77); else damg = random(5,10); if(other.health<=damg&&other.thingtype==THINGTYPE_FLESH&&random()<0.1) SnowJob(other,self); else if (other.health) { T_Damage (other, self, self.owner, damg ); sound (self, CHAN_BODY, "crusader/icehit.wav", 1, ATTN_NORM); if(other.classname=="player") { other.artifact_active(+)ARTFLAG_FROZEN; newmis=spawn(); newmis.enemy=other; newmis.artifact_active=ARTFLAG_FROZEN; newmis.think=remove_artflag; thinktime newmis : 0.1; } } remove(self); }; //============================================================================ void() shard_1 =[ $shard , shard_1 ] { }; //============================================================================ void(vector offset, float set_speed, vector dest_offset) do_shard = { entity missile; vector vec; missile = spawn (); missile.owner = self; missile.movetype = MOVETYPE_FLYMISSILE; missile.solid = SOLID_BBOX; missile.health = 10; if(self.classname=="monster_imp_lord") { set_speed*=2; missile.scale=2; } setmodel (missile, "models/shardice.mdl"); setsize (missile, '0 0 0', '0 0 0'); // set missile speed makevectors (self.angles); vec = self.origin + self.view_ofs + v_factor(offset); setorigin (missile, vec); vec = self.enemy.origin - missile.origin + self.enemy.proj_ofs + dest_offset; vec = normalize(vec); missile.velocity = (vec+aim_adjust(self.enemy))*set_speed; missile.angles = vectoangles(missile.velocity); missile.touch = shardTouch; missile.think = shard_1; missile.nextthink = time + HX_FRAME_TIME; }; gamecode/hc/h2/sheep.hc000066400000000000000000000415011444734033100151510ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\players\SHEEP\ambient\sheep.hc MG ============================================================================== */ // For building the model $cd Q:\art\models\players\SHEEP\ambient $origin 20 0 11 $base BASE SKIN $skin SKIN $flags 0 // $frame grazeA1 grazeA2 grazeA3 grazeA4 grazeA5 $frame grazeA6 grazeA7 grazeA8 grazeA9 grazeA10 $frame grazeA11 grazeA12 grazeA13 grazeA14 grazeA15 $frame grazeA16 grazeA17 grazeA18 grazeA19 grazeA20 $frame grazeA21 grazeA22 grazeA23 grazeA24 grazeA25 $frame grazeA26 grazeA27 grazeA28 grazeA29 grazeA30 $frame grazeA31 grazeA32 grazeA33 grazeA34 grazeA35 $frame grazeA36 grazeA37 grazeA38 grazeA39 grazeA40 // $frame grazeB1 grazeB2 grazeB3 grazeB4 grazeB5 $frame grazeB6 grazeB7 grazeB8 grazeB9 grazeB10 $frame grazeB11 grazeB12 grazeB13 grazeB14 grazeB15 $frame grazeB16 grazeB17 grazeB18 grazeB19 grazeB20 $frame grazeB21 grazeB22 grazeB23 grazeB24 grazeB25 $frame grazeB26 grazeB27 grazeB28 grazeB29 grazeB30 $frame grazeB31 grazeB32 grazeB33 grazeB34 grazeB35 $frame grazeB36 grazeB37 grazeB38 grazeB39 grazeB40 // $frame gstepA10 gstepA11 gstepA12 gstepA13 gstepA14 $frame gstepA15 gstepA16 gstepA17 gstepA18 gstepA19 $frame gstepA20 gstepA21 gstepA22 gstepA23 gstepA24 $frame gstepA25 gstepA26 gstepA27 gstepA28 gstepA29 $frame gstepA30 // $frame gstepB10 gstepB11 gstepB12 gstepB13 gstepB14 $frame gstepB15 gstepB16 gstepB17 gstepB18 gstepB19 $frame gstepB20 gstepB21 gstepB22 gstepB23 gstepB24 $frame gstepB25 gstepB26 gstepB27 gstepB28 gstepB29 $frame gstepB30 // $frame lookup1 lookup2 lookup3 lookup4 lookup5 $frame lookup6 lookup7 lookup8 lookup9 lookup10 $frame lookup11 lookup12 look1 look2 look3 $frame look4 look5 look6 look7 look8 $frame look9 look10 look11 look12 look13 $frame look14 look15 look16 look17 look18 $frame look19 look20 look21 look22 look23 $frame look24 look25 look26 look27 look28 $frame look29 look30 look31 look32 look33 $frame look34 look35 look36 look37 look38 $frame look39 look40 look41 look42 look43 $frame look44 look45 look46 look47 look48 $frame look49 look50 look51 look52 look53 $frame look54 look55 look56 look57 look58 $frame look59 look60 // $frame pain1 pain2 pain3 pain4 pain5 $frame pain6 pain7 pain8 // $frame tranA1 tranA2 tranA3 tranA4 tranA5 $frame tranA6 tranA7 tranA8 tranA9 tranA10 // $frame tranB1 tranB2 tranB3 tranB4 tranB5 $frame tranB6 tranB7 tranB8 tranB9 tranB10 $frame tranB11 tranB12 // $frame trot1 trot2 trot3 trot4 trot5 $frame trot6 trot7 trot8 trot9 trot10 // $frame wait1 wait2 wait3 wait4 wait5 $frame wait6 wait7 wait8 wait9 wait10 $frame wait11 wait12 wait13 wait14 wait15 $frame wait16 float stationary = 1; /*QUAKED player_sheep (0.3 0.1 0.6) (-8 -8 -0) (8 8 32) stationary STUCK JUMP PLAY_DEAD DORMANT NODROP A sheep player model -------------------------FIELDS------------------------- stationary- sheep will not wander around or change angles nodrop- won't drop to floor (and through other entities!) on spawn -------------------------------------------------------- */ void()sheep_tranA; void()sheep_tranB; void()sheep_graze_a; void()sheep_graze_b; void()sheep_gstep_a; void()sheep_gstep_b; void()sheep_trot; void()sheep_wait; void()sheep_pain; void()sheep_lookdown; void()sheep_look; void()sheep_lookup; void restore_monster () { //SOUND and MODEL newmis=spawn(); thinktime newmis : 0; newmis.think=self.th_spawn; newmis.skin=self.skin; newmis.health=self.max_health; remove(self); } void sheep_sound (float vol) { float r; string playsound; if(self.noise!=""&&self.classname!="player") playsound=self.noise; else { r=rint(random(1,3)); if(r==1) playsound="misc/sheep1.wav"; else if(r==2) playsound="misc/sheep2.wav"; else playsound="misc/sheep3.wav"; } sound(self,CHAN_VOICE,playsound,vol,ATTN_NORM); self.pain_finished=time + 1; } void sheep_turn (void) { if(random()<0.5) self.angles_y+=random()*self.yaw_speed+self.yaw_speed/2; else self.angles_y-=random()*self.yaw_speed+self.yaw_speed/2; } void sheep_move (float dist) { vector best_yaw; best_yaw=normalize(self.goalentity.origin-self.origin); best_yaw=vectoangles(best_yaw); self.ideal_yaw=best_yaw_y; ChangeYaw(); movetogoal(dist); } void sheep_think (void) { MonsterCheckContents(); if(!self.spawnflags&stationary&&(self.think==sheep_trot||self.think==sheep_gstep_a||self.think==sheep_gstep_b)&&random()<0.1) sheep_turn(); if(random()<0.1&&random()<0.2&&self.pain_finished25) self.health=25; } CreateEntityNew(self,ENT_SHEEP,"models/sheep.mdl",chunk_death); self.th_pain = sheep_pain; self.touch = obj_push; self.flags(+)FL_PUSH; self.flags2(+)FL_ALIVE; self.yaw_speed=2; //FIXME: Don't allow trans, trot, etc, if stationary flag on r=rint(random(1,3)); if(r==1) self.noise="misc/sheep1.wav"; else if(r==2) self.noise="misc/sheep2.wav"; else self.noise="misc/sheep3.wav"; if(self.enemy) self.think=monster_sheep_run; else { r=rint(random(1,11)); if(r==1) self.think=sheep_graze_a; else if(r==2) self.think=sheep_graze_b; else if(r==3&&!self.spawnflags&1) self.think=sheep_gstep_a; else if(r==4&&!self.spawnflags&1) self.think=sheep_gstep_b; else if(r==5) self.think=sheep_look; else if(r==6) self.think=sheep_lookup; else if(r==7&&!self.spawnflags&1) self.think=sheep_trot; else if(r==8) self.think=sheep_wait; else if(r==9) self.think=sheep_lookdown; else if(r==10&&!self.spawnflags&1) self.think=sheep_tranA; else if(!self.spawnflags&1) self.think=sheep_tranB; } if(!self.think) self.think=sheep_wait; self.th_walk=self.th_stand=self.think; walkmonster_start(); } /* ============================ PLAYER SHEEP ============================ */ void()player_sheep_run; void player_sheep_snout_slowpuff () { self.wfs = advanceweaponframe(22,31); self.th_weapon=player_sheep_snout_slowpuff; } void player_sheep_snout_fastpuff () { self.wfs = advanceweaponframe(17,21); self.th_weapon=player_sheep_snout_fastpuff; if(self.wfs==WF_CYCLE_WRAPPED) self.weaponframe_cnt+=1; if(self.weaponframe_cnt>5) { self.weaponframe_cnt=0; player_sheep_snout_slowpuff(); } } void player_sheep_snout_bite () { self.wfs = advanceweaponframe(8,16); self.th_weapon=player_sheep_snout_bite; if(self.wfs==WF_CYCLE_WRAPPED) player_sheep_snout_fastpuff(); } void player_sheep_snout_pain1 () { self.wfs = advanceweaponframe(0,3); self.th_weapon=player_sheep_snout_pain1; if(self.wfs==WF_CYCLE_WRAPPED) player_sheep_snout_fastpuff(); } void player_sheep_snout_pain2 () { self.wfs = advanceweaponframe(4,7); self.th_weapon=player_sheep_snout_pain2; if(self.wfs==WF_CYCLE_WRAPPED) player_sheep_snout_fastpuff(); } void() player_sheep_stand =[++$wait1..$wait16] { self.th_weapon(); if (self.velocity_x || self.velocity_y) self.think=player_sheep_run; }; void() player_sheep_run =[++$trot1..$trot10] { self.th_weapon(); if (!self.velocity_x && !self.velocity_y) self.think=player_sheep_stand; }; void player_sheep_baa () { self.th_weapon=player_sheep_snout_bite; self.th_weapon(); traceline(self.origin+self.view_ofs,self.origin+self.view_ofs+v_forward*36,FALSE,self); if(trace_ent.takedamage)//classname=="obj_catapult2") { sound(self,CHAN_VOICE,"spider/bite.wav",1,ATTN_NORM); SpawnPuff (trace_endpos, '0 0 0', 3,trace_ent); T_Damage(trace_ent,self,self,5); } else sheep_sound(1); self.attack_finished=time+0.5; if(!self.velocity_x && !self.velocity_y) self.think=player_sheep_stand; else self.think=player_sheep_run; thinktime self : 0; } void() player_sheep_pain=[++$pain1..$pain8] { if(self.pain_finished25) loser.health=25; loser.th_missile=player_sheep_baa; loser.th_melee=player_sheep_baa; loser.th_stand=player_sheep_stand; loser.th_run=player_sheep_run; loser.th_walk=player_sheep_run; loser.th_pain=player_sheep_pain; loser.th_jump=player_sheep_jump; setmodel (loser, "models/sheep.mdl"); setsize (loser,'-16 -16 0','16 16 28'); loser.model="models/sheep.mdl"; // loser.modelindex=modelindex_sheep; // loser.headmodel="models/h_sheep.mdl"; loser.th_weapon=player_sheep_snout_pain1; loser.hull=HULL_CROUCH; // if(!loser.flags2&FL_CAMERA_VIEW) loser.view_ofs = '0 0 24'; loser.proj_ofs='0 0 18'; loser.attack_finished=0; loser.weapon=FALSE; loser.weaponmodel="models/snout.mdl"; loser.weaponframe=0; loser.sheep_sound_time=FALSE; loser.think=player_sheep_stand; thinktime loser : 0; } else { //Stop all auto-looping sounds on removed monster sound(loser,CHAN_BODY,"misc/null.wav",1,ATTN_NONE); sound(loser,CHAN_WEAPON,"misc/null.wav",1,ATTN_NONE); sound(loser,CHAN_ITEM,"misc/null.wav",1,ATTN_NONE); newmis=spawn(); setorigin(newmis,loser.origin); //For restoring monster: newmis.th_spawn=loser.th_spawn; newmis.skin=0; newmis.oldskin=loser.skin; newmis.max_health=loser.health; if(!loser.enemy) newmis.enemy=self.owner; else newmis.enemy=loser.enemy; newmis.goalentity=newmis.enemy; newmis.angles=loser.angles; newmis.target=loser.target;//So it will still activate targets newmis.killtarget=loser.killtarget; remove(loser); newmis.flags2(+)FL_SUMMONED; newmis.spawnflags(+)NO_DROP; newmis.think=player_sheep; thinktime newmis : 0; } } void poly_touch () { if(other.monsterclass >= CLASS_BOSS&&other!=self.owner) { self.velocity=normalize((self.owner.absmin+self.owner.absmax)*0.5-self.origin)*700; self.owner=other; } else if(other.flags2&FL_ALIVE&&other.model!="models/sheep.mdl"&&other.classname!="monster_golem_crystal") { self.touch=SUB_Null; Polymorph(other); remove(self); } else if(other.movetype!=MOVETYPE_FLYMISSILE&&other.movetype!=MOVETYPE_BOUNCE&&other.movetype!=MOVETYPE_BOUNCEMISSILE) { self.touch=SUB_Null; particleexplosion(self.origin,random(144,159),self.absmax_z-self.absmin_z,10); remove(self); } } void polymorph_anim () [++ 0 .. 3] { particle4(self.origin,7,random(144,159),PARTICLETYPE_EXPLODE2,random(1,5)); } void FirePoly (float ofs) { makevectors(self.v_angle); newmis=spawn(); newmis.movetype=MOVETYPE_FLYMISSILE; newmis.solid=SOLID_BBOX; newmis.owner=self; newmis.touch=poly_touch; newmis.speed=700; newmis.velocity=v_forward*newmis.speed+v_right*ofs; newmis.drawflags=MLS_POWERMODE; newmis.think=polymorph_anim; thinktime newmis : 0; setmodel(newmis,"models/polymrph.spr"); setsize(newmis,'0 0 0','0 0 0'); setorigin(newmis,self.origin+self.proj_ofs+v_forward*10); } void Use_Polymorph () { FirePoly(-150); FirePoly(-75); FirePoly(0); FirePoly(75); FirePoly(150); self.cnt_polymorph -= 1; } gamecode/hc/h2/sickle.hc000066400000000000000000000145741444734033100153310ustar00rootroot00000000000000/* ============================================================================== Q:\art\models\weapons\sickle\final\sickle.hc ============================================================================== */ // For building the model $cd Q:\art\models\weapons\sickle\final $origin 0 0 0 $base BASE skin $skin skin $flags 0 // $frame rootpose // $frame 1swipe4 1swipe5 $frame 1swipe6 1swipe7 1swipe10 $frame 1swipe14 $frame 1swipe15 1swipe16 1swipe17 // frame 10 - $frame 2swipe1 2swipe2 2swipe3 $frame 2swipe6 2swipe7 2swipe8 2swipe9 $frame 2swipe12 2swipe13 2swipe14 // frame 20 $frame 3swipe1 3swipe5 $frame 3swipe7 3swipe8 3swipe9 3swipe10 $frame 3swipe11 3swipe12 3swipe13 3swipe14 // $frame select1 select2 select3 select4 select5 $frame select6 select7 select8 select9 select10 void()sickle_decide_attack; void sickle_fire () { vector source; vector org,dir; float damg, inertia; float damage_mod; float damage_base; float chance,point_chance,drain_ok; damage_mod = 10; makevectors (self.v_angle); source = self.origin + self.proj_ofs; traceline (source, source + v_forward*64, FALSE, self); if (trace_fraction == 1.0) { traceline (source, source + v_forward*64 - (v_up * 30), FALSE, self); // 30 down if (trace_fraction == 1.0) { traceline (source, source + v_forward*64 + v_up * 30, FALSE, self); // 30 up if (trace_fraction == 1.0) return; } } org = trace_endpos + (v_forward * 4); if (trace_ent.takedamage) { // Necromancer stands a chance of vampirically stealing health points if(teamplay && trace_ent.team == self.team) drain_ok=FALSE; else drain_ok=TRUE; if (drain_ok && (trace_ent.flags & FL_MONSTER || trace_ent.flags & FL_CLIENT) && (self.level >= 6)) { // msg_entity=self; // WriteByte (MSG_ONE, SVC_SET_VIEW_TINT); // WriteByte (MSG_ONE, 168); chance = (self.level - 5) * .04; if (chance > .20) chance = .2; if (random() < chance) { // point_chance = (self.level - 5) * 2; // if (point_chance > 10) // point_chance = 10; /* Pa3PyX -- This HAS TO change: One will be dead 3 times * before stealing their 10 hit points at clvl 10 in 5 hits * trying to hack away at a medusa or an archer lord. This * has to be at least 20 to make any practical use of this * level 6 ability, and at most that, since spiders can be * leeched from fairly easily (but they will still caw at * you at least twice before your hit actually drains). * Given that you cannot leech from dead bodies. */ point_chance = (self.level - 5) * 4; if (point_chance > 20) point_chance = 20; sound (self, CHAN_BODY, "weapons/drain.wav", 1, ATTN_NORM); // self.health += point_chance; // if (self.health>self.max_health) // self.health = self.max_health; // Pa3PyX: no longer cancel mystic urn effect if (self.health < self.max_health) { self.health += point_chance; if (self.health>self.max_health) self.health = self.max_health; } } } if (self.artifact_active & ART_TOMEOFPOWER) { damage_base = WEAPON1_PWR_BASE_DAMAGE; damage_mod = WEAPON1_PWR_ADD_DAMAGE; CreateWhiteFlash(org); if(trace_ent.mass<=10) inertia=1; else inertia=trace_ent.mass/10; if ((trace_ent.hull != HULL_BIG) && (inertia<1000) && (trace_ent.classname != "breakable_brush")) { if (trace_ent.mass < 1000) { dir = trace_ent.origin - self.origin; trace_ent.velocity = dir * WEAPON1_PUSH*(1/inertia); if(trace_ent.movetype==MOVETYPE_FLY) { if(trace_ent.flags&FL_ONGROUND) trace_ent.velocity_z=200/inertia; } else trace_ent.velocity_z = 200/inertia; trace_ent.flags(-)FL_ONGROUND; } } } else { damage_base = WEAPON1_BASE_DAMAGE; damage_mod = WEAPON1_ADD_DAMAGE; } damg = random(damage_mod + damage_base,damage_base); SpawnPuff (org, '0 0 0', damg,trace_ent); T_Damage (trace_ent, self, self, damg); if (!MetalHitSound(trace_ent.thingtype)) sound (self, CHAN_WEAPON, "weapons/slash.wav", 1, ATTN_NORM); } else { // hit wall sound (self, CHAN_WEAPON, "weapons/hitwall.wav", 1, ATTN_NORM); WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); WriteByte (MSG_BROADCAST, TE_GUNSHOT); WriteCoord (MSG_BROADCAST, org_x); WriteCoord (MSG_BROADCAST, org_y); WriteCoord (MSG_BROADCAST, org_z); if (self.artifact_active & ART_TOMEOFPOWER) CreateWhiteFlash(org); else { org = trace_endpos + (v_forward * -1) + (v_right * 15); org -= '0 0 26'; CreateSpark (org); } } } void sickle_ready (void) { self.th_weapon=sickle_ready; self.weaponframe = $rootpose; } void () sickle_c = { self.th_weapon=sickle_c; self.wfs = advanceweaponframe($3swipe1,$3swipe14); if (self.weaponframe==$3swipe1) sound (self, CHAN_WEAPON, "weapons/gaunt1.wav", 1, ATTN_NORM); else if (self.weaponframe == $3swipe7) sickle_fire(); if (self.wfs==WF_LAST_FRAME) sickle_ready(); }; void () sickle_b = { self.th_weapon=sickle_b; self.wfs = advanceweaponframe($2swipe1,$2swipe14); if (self.weaponframe==$2swipe1) sound (self, CHAN_WEAPON, "weapons/gaunt1.wav", 1, ATTN_NORM); else if (self.weaponframe == $2swipe3) sickle_fire(); else if (self.wfs == WF_LAST_FRAME) sickle_ready(); }; void () sickle_a = { self.th_weapon=sickle_a; self.wfs = advanceweaponframe($1swipe4,$1swipe17); if (self.weaponframe==$1swipe4) sound (self, CHAN_WEAPON, "weapons/gaunt1.wav", 1, ATTN_NORM); else if (self.weaponframe == $1swipe5) sickle_fire(); else if (self.wfs == WF_LAST_FRAME) sickle_ready(); }; void sickle_select (void) { //selection sound? self.th_weapon=sickle_select; self.wfs = advanceweaponframe($select10,$select1); self.weaponmodel = "models/sickle.mdl"; if(self.wfs==WF_CYCLE_STARTED) sound(self,CHAN_WEAPON,"weapons/unsheath.wav",1,ATTN_NORM); if (self.wfs==WF_CYCLE_WRAPPED) { self.attack_finished = time - 1; sickle_ready(); } } void sickle_deselect (void) { self.th_weapon=sickle_deselect; self.wfs = advanceweaponframe($select1,$select10); if (self.wfs==WF_CYCLE_WRAPPED) W_SetCurrentAmmo(); } void sickle_decide_attack (void) { if (self.attack_cnt < 1) sickle_a (); else { sickle_b (); self.attack_cnt = -1; } self.attack_cnt += 1; self.attack_finished = time + 0.5; } gamecode/hc/h2/skullwiz.hc000066400000000000000000000634101444734033100157340ustar00rootroot00000000000000// $frame skdeth1 skdeth2 skdeth3 skdeth4 skdeth5 $frame skdeth6 skdeth7 skdeth8 skdeth9 skdeth10 $frame skdeth11 skdeth12 skdeth13 skdeth14 skdeth15 // $frame skgate1 skgate2 skgate3 skgate4 skgate5 $frame skgate6 skgate7 skgate8 skgate9 skgate10 $frame skgate11 skgate12 skgate13 skgate14 skgate15 $frame skgate16 skgate17 skgate18 skgate19 skgate20 $frame skgate21 skgate22 skgate23 skgate24 skgate25 $frame skgate26 skgate27 skgate28 skgate29 skgate30 // Frame 46 - 57 $frame skpain1 skpain2 skpain3 skpain4 skpain5 $frame skpain6 skpain7 skpain8 skpain9 skpain10 $frame skpain11 skpain12 // Frame 58 - 69 $frame skredi1 skredi2 skredi3 skredi4 skredi5 $frame skredi6 skredi7 skredi8 skredi9 skredi10 $frame skredi11 skredi12 // //$frame skspel1 skspel2 skspel3 skspel4 skspel5 //$frame skspel6 skspel7 skspel8 skspel9 skspel10 //$frame skspel11 skspel12 skspel13 skspel14 skspel15 //$frame skspel16 skspel17 skspel18 skspel19 skspel20 //$frame skspel21 skspel22 skspel23 skspel24 skspel25 //$frame skspel26 skspel27 skspel28 skspel29 skspel30 //$frame skspel31 // Frame 70 - 84 $frame skspel2 skspel4 $frame skspel6 skspel8 skspel10 $frame skspel12 skspel14 $frame skspel16 skspel18 skspel20 $frame skspel22 skspel24 $frame skspel26 skspel28 skspel30 // //$frame sktele1 sktele2 sktele3 sktele4 sktele5 //$frame sktele6 sktele7 sktele8 sktele9 sktele10 //$frame sktele11 sktele12 sktele13 sktele14 sktele15 //$frame sktele16 sktele17 sktele18 sktele19 sktele20 //$frame sktele21 sktele22 sktele23 sktele24 sktele25 //$frame sktele26 sktele27 sktele28 sktele29 sktele30 //$frame sktele31 $frame sktele2 sktele4 $frame sktele6 sktele8 sktele10 $frame sktele12 sktele14 $frame sktele16 sktele18 sktele20 $frame sktele22 sktele24 $frame sktele26 sktele28 sktele30 // $frame sktran1 sktran2 sktran3 sktran4 sktran5 $frame sktran6 sktran7 // $frame skwait1 skwait2 skwait10 skwait11 skwait12 $frame skwait13 skwait14 skwait15 skwait16 skwait17 $frame skwait18 skwait19 skwait20 skwait21 skwait22 $frame skwait23 skwait24 skwait25 skwait26 // $frame skwalk1 skwalk2 skwalk3 skwalk4 skwalk5 $frame skwalk6 skwalk7 skwalk8 skwalk9 skwalk10 $frame skwalk11 skwalk12 skwalk13 skwalk14 skwalk15 $frame skwalk16 skwalk17 skwalk18 skwalk19 skwalk20 $frame skwalk21 skwalk22 skwalk23 skwalk24 void skullwiz_walk(void); void skullwiz_run(void); void skullwiz_melee(void); void skullwiz_blink(void); void skullwiz_push (void); void skullwiz_missile_init (void); float SKULLBOOK =0; float SKULLHEAD =1; float() SkullFacingIdeal = { local float delta; delta = anglemod(self.angles_y - self.ideal_yaw); if (delta > 25 && delta < 335) return FALSE; return TRUE; }; void phase_init (void) { vector spot1,newangle; float loop_cnt,forward; trace_fraction =0; loop_cnt = 0; do { newangle = self.angles; newangle_y = random(0,360); makevectors (newangle); forward = random(40,100); spot1 = self.origin + v_forward * forward; tracearea (spot1,spot1 + v_up * 80,'-32 -32 -10','32 32 46',FALSE,self); if ((trace_fraction == 1.0) && (!trace_allsolid)) // Check there is a floor at the new spot { traceline (spot1, spot1 + (v_up * -4) , FALSE, self); if (trace_fraction ==1) // Didn't hit anything? There was no floor trace_fraction = 0; // So it will loop else trace_fraction = 1; // So it will end loop } else trace_fraction = 0; loop_cnt += 1; if (loop_cnt > 500) // No endless loops { self.nextthink = time + 2; return; } } while (trace_fraction != 1.0); self.origin = spot1; } float check_defense_blink () { vector spot1,spot2,dangerous_dir; float dot; if(!self.enemy) return FALSE; if(!visible(self.enemy)) return FALSE; if(self.enemy.last_attack