2 REGISTER_NET_LINKED(ENT_CLIENT_LADDER)
4 void func_ladder_think(entity this)
7 // TODO: check if this is what is causing the glitchiness when switching between them
8 float dt = time - this.move_time;
10 if(dt <= 0) { return; }
13 // set myself as current ladders where possible
14 IL_EACH(g_ladderents, it.ladder_entity == this,
16 it.ladder_entity = NULL;
17 IL_REMOVE(g_ladderents, it);
20 FOREACH_ENTITY_RADIUS((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1, !it.ladder_entity && IS_PLAYER(it) && it.move_movetype != MOVETYPE_NOCLIP && !IS_DEAD(it),
22 vector emin = it.absmin;
23 vector emax = it.absmax;
24 if(this.solid == SOLID_BSP || (IS_CSQC && this.solid == SOLID_TRIGGER)) // CSQC doesn't expand properly
29 if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick
31 if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, it)) // accurate
34 IL_PUSH(g_ladderents, it);
35 it.ladder_entity = this;
41 this.nextthink = time;
46 bool func_ladder_send(entity this, entity to, int sf)
48 WriteHeader(MSG_ENTITY, ENT_CLIENT_LADDER);
50 WriteString(MSG_ENTITY, this.classname);
51 WriteByte(MSG_ENTITY, this.skin);
52 WriteCoord(MSG_ENTITY, this.speed);
54 trigger_common_write(this, false);
59 void func_ladder_link(entity this)
61 trigger_link(this, func_ladder_send);
62 //this.model = "null";
65 void func_ladder_init(entity this)
67 string m = this.model;
69 // restore the model string unset in WarpZoneLib_ExactTrigger_Init()
70 // see: https://gitlab.com/xonotic/xonotic-data.pk3dir/-/issues/2838
72 BITSET_ASSIGN(this.effects, EF_NODEPTHTEST);
73 func_ladder_link(this);
74 setthink(this, func_ladder_think);
75 this.nextthink = time;
77 if(min(this.absmax.x - this.absmin.x, this.absmax.y - this.absmin.y) > 100)
80 entity tracetest_ent = spawn();
81 setsize(tracetest_ent, PL_MIN_CONST, PL_MAX_CONST);
82 tracetest_ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
84 vector top_min = (this.absmin + this.absmax) / 2;
85 top_min.z = this.absmax.z;
86 vector top_max = top_min;
87 top_max.z += PL_MAX_CONST.z - PL_MIN_CONST.z;
88 tracebox(top_max + jumpstepheightvec, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent);
91 tracebox(top_max + stepheightvec, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent);
94 tracebox(top_max, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent);
97 if(this.absmax.x - this.absmin.x > PL_MAX_CONST.x - PL_MIN_CONST.x
98 && this.absmax.y - this.absmin.y < this.absmax.x - this.absmin.x)
100 // move top on one side
101 top_max.y = top_min.y = this.absmin.y + (PL_MAX_CONST.y - PL_MIN_CONST.y) * 0.75;
103 else if(this.absmax.y - this.absmin.y > PL_MAX_CONST.y - PL_MIN_CONST.y
104 && this.absmax.x - this.absmin.x < this.absmax.y - this.absmin.y)
106 // move top on one side
107 top_max.x = top_min.x = this.absmin.x + (PL_MAX_CONST.x - PL_MIN_CONST.x) * 0.75;
109 tracebox(top_max, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent);
112 if(this.absmax.x - this.absmin.x > PL_MAX_CONST.x - PL_MIN_CONST.x
113 && this.absmax.y - this.absmin.y < this.absmax.x - this.absmin.x)
115 // alternatively on the other side
116 top_max.y = top_min.y = this.absmax.y - (PL_MAX_CONST.y - PL_MIN_CONST.y) * 0.75;
118 else if(this.absmax.y - this.absmin.y > PL_MAX_CONST.y - PL_MIN_CONST.y
119 && this.absmax.x - this.absmin.x < this.absmax.y - this.absmin.y)
121 // alternatively on the other side
122 top_max.x = top_min.x = this.absmax.x - (PL_MAX_CONST.x - PL_MIN_CONST.x) * 0.75;
124 tracebox(top_max, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent);
129 delete(tracetest_ent);
130 if(trace_startsolid || trace_endpos.z < this.absmax.z)
135 this.bot_pickup = true; // allow bots to make use of this ladder
136 float cost = waypoint_getlinearcost(trace_endpos.z - this.absmin.z);
137 top_min = trace_endpos;
138 waypoint_spawnforteleporter_boxes(this, WAYPOINTFLAG_LADDER, this.absmin, this.absmax, top_min, top_min, cost);
141 spawnfunc(func_ladder)
143 IL_PUSH(g_ladders, this); // TODO: also func_water? bots currently loop through func_ladder only
145 func_ladder_init(this);
148 spawnfunc(func_water)
150 func_ladder_init(this);
156 void func_ladder_draw(entity this) { func_ladder_think(this); }
158 void func_ladder_remove(entity this)
160 IL_EACH(g_ladderents, it.ladder_entity == this,
162 it.ladder_entity = NULL;
163 IL_REMOVE(g_ladderents, it);
165 strfree(this.classname);
168 NET_HANDLE(ENT_CLIENT_LADDER, bool isnew)
170 this.classname = strzone(ReadString());
171 this.skin = ReadByte();
172 this.speed = ReadCoord();
173 this.solid = SOLID_TRIGGER;
175 trigger_common_read(this, false);
178 IL_PUSH(g_drawables, this);
179 this.draw = func_ladder_draw;
180 this.drawmask = MASK_NORMAL;
182 this.move_time = time;
183 this.entremove = func_ladder_remove;
185 // NOTE: CSQC's version of setorigin doesn't expand
186 this.absmin -= '1 1 1';
187 this.absmax += '1 1 1';